tools: add cd tool for changing working directory

Uses std::env::set_current_dir() syscall so the change affects
all subsequent tool invocations. Supports absolute paths, relative
paths, and ~ expansion.

Co-Authored-By: Proof of Concept <poc@bcachefs.org>
This commit is contained in:
Kent Overstreet 2026-04-12 15:49:46 -04:00
parent 0612e1bc41
commit ab0f16a3b5
2 changed files with 43 additions and 3 deletions

39
src/agent/tools/cd.rs Normal file
View file

@ -0,0 +1,39 @@
use std::sync::Arc;
use std::path::PathBuf;
// tools/cd.rs — Change working directory
//
// Uses the chdir syscall so it affects all tools.
pub fn tool() -> super::Tool {
super::Tool {
name: "cd",
description: "Change the current working directory.",
parameters_json: r#"{"type":"object","properties":{"path":{"type":"string","description":"The directory to change to (absolute or relative)"}},"required":["path"]}"#,
handler: Arc::new(|_agent, v| Box::pin(async move {
let path = v.get("path").and_then(|v| v.as_str())
.ok_or_else(|| anyhow::anyhow!("'path' parameter is required"))?;
if path.is_empty() { anyhow::bail!("'path' parameter cannot be empty"); }
// Resolve ~ to home directory
let resolved = if path.starts_with('~') {
let home = dirs::home_dir()
.ok_or_else(|| anyhow::anyhow!("could not determine home directory"))?;
home.join(path.strip_prefix("~/").unwrap_or(path))
} else {
PathBuf::from(path)
};
// Change directory (this is the actual chdir syscall)
std::env::set_current_dir(&resolved)
.map_err(|e| anyhow::anyhow!("cd: {}: {}", path, e))?;
// Return the canonical path
let canonical = std::env::current_dir()
.map(|p| p.display().to_string())
.unwrap_or_else(|_| resolved.display().to_string());
Ok(canonical)
})),
}
}

View file

@ -6,13 +6,14 @@
// Core tools // Core tools
mod ast_grep; mod ast_grep;
pub mod lsp;
pub mod mcp_client;
mod bash; mod bash;
mod cd;
pub mod channels; pub mod channels;
mod edit; mod edit;
mod glob; mod glob;
mod grep; mod grep;
pub mod lsp;
pub mod mcp_client;
pub mod memory; pub mod memory;
mod read; mod read;
mod web; mod web;
@ -177,7 +178,7 @@ pub async fn dispatch_with_agent(
pub fn tools() -> Vec<Tool> { pub fn tools() -> Vec<Tool> {
let mut all = vec![ let mut all = vec![
read::tool(), write::tool(), edit::tool(), read::tool(), write::tool(), edit::tool(),
grep::tool(), glob::tool(), bash::tool(), grep::tool(), glob::tool(), bash::tool(), cd::tool(),
ast_grep::tool(), vision::tool(), ast_grep::tool(), vision::tool(),
]; ];
all.extend(web::tools()); all.extend(web::tools());