2026-07-12 11:28:39 +07:00
//! `cd` tool: verify and resolve a workspace-relative directory path.
2026-07-11 20:44:15 +07:00
use super ::super ::Tool ;
use super ::super ::ToolCtx ;
2026-07-17 06:44:31 +07:00
use anyhow ::Result ;
2026-07-16 07:42:03 +07:00
use serde_json ::{ json , Value };
2026-07-11 20:44:15 +07:00
2026-07-12 11:28:39 +07:00
/// Tool that resolves a workspace-relative path and reports whether it exists and is a dir.
2026-07-11 20:44:15 +07:00
pub struct Cd ;
impl Tool for Cd {
fn name ( & self ) -> & 'static str {
"cd"
}
fn description ( & self ) -> & 'static str {
"Check if a directory exists within the workspace and print its resolved path. Use this to verify a directory path before running other commands there."
}
fn parameters ( & self ) -> Value {
json! ({
"type" : "object" ,
"properties" : {
"path" : {
"type" : "string" ,
"description" : "Directory path (relative to workspace root)"
}
},
"required" : [ "path" ]
})
}
2026-07-12 11:28:39 +07:00
/// Resolve `path` against the workspace roots and report its status.
///
/// Flow: extract `path` → `resolve_path` (sandboxed to `ctx.workspaces`) →
/// check `exists()` and `is_dir()` → canonicalize → return canonical path.
///
/// Why: the agent has no persistent cwd between tool calls; "cd" here is purely a
/// verification + canonicalization helper rather than a state change.
///
/// Return: canonical path on success; explicit "does not exist" / "not a directory"
/// message (still `Ok`) so the model can react without treating it as an error.
2026-07-11 20:44:15 +07:00
fn run ( & self , ctx : & ToolCtx , args : & Value ) -> Result < String > {
2026-07-17 06:44:31 +07:00
let rel = crate ::tool ::arg_str ( args , "path" ) ? ;
2026-07-11 20:44:15 +07:00
2026-07-17 06:44:31 +07:00
let path = super ::super ::resolve_path ( & ctx . workspaces , & rel ) ? ;
2026-07-11 20:44:15 +07:00
if ! path . exists () {
2026-07-16 07:42:03 +07:00
return Ok ( format! (
"path ' {} ' does not exist (resolved to {} )" ,
rel ,
path . display ()
));
2026-07-11 20:44:15 +07:00
}
if ! path . is_dir () {
2026-07-16 07:42:03 +07:00
return Ok ( format! (
"path ' {} ' is not a directory (resolved to {} )" ,
rel ,
path . display ()
));
2026-07-11 20:44:15 +07:00
}
let canon = path . canonicalize (). unwrap_or ( path );
Ok ( format! ( " {} " , canon . display ()))
}
}