From ab0f16a3b5f4e31718409ea80bcfed37f0c5c9c9 Mon Sep 17 00:00:00 2001 From: Kent Overstreet Date: Sun, 12 Apr 2026 15:49:46 -0400 Subject: [PATCH] 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 --- src/agent/tools/cd.rs | 39 +++++++++++++++++++++++++++++++++++++++ src/agent/tools/mod.rs | 7 ++++--- 2 files changed, 43 insertions(+), 3 deletions(-) create mode 100644 src/agent/tools/cd.rs diff --git a/src/agent/tools/cd.rs b/src/agent/tools/cd.rs new file mode 100644 index 0000000..b1f9c30 --- /dev/null +++ b/src/agent/tools/cd.rs @@ -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) + })), + } +} diff --git a/src/agent/tools/mod.rs b/src/agent/tools/mod.rs index b873a11..7dcccd1 100644 --- a/src/agent/tools/mod.rs +++ b/src/agent/tools/mod.rs @@ -6,13 +6,14 @@ // Core tools mod ast_grep; -pub mod lsp; -pub mod mcp_client; mod bash; +mod cd; pub mod channels; mod edit; mod glob; mod grep; +pub mod lsp; +pub mod mcp_client; pub mod memory; mod read; mod web; @@ -177,7 +178,7 @@ pub async fn dispatch_with_agent( pub fn tools() -> Vec { let mut all = vec![ 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(), ]; all.extend(web::tools());