claude_rust_tools/infrastructure/
exit_worktree_tool.rs1use claude_rust_errors::{AppError, AppResult};
2use claude_rust_types::{PermissionLevel, Tool};
3use serde_json::{Value, json};
4use tokio::process::Command;
5
6pub struct ExitWorktreeTool;
7
8impl ExitWorktreeTool {
9 pub fn new() -> Self {
10 Self
11 }
12}
13
14#[async_trait::async_trait]
15impl Tool for ExitWorktreeTool {
16 fn name(&self) -> &str {
17 "exit_worktree"
18 }
19
20 fn description(&self) -> &str {
21 "Exit and optionally remove a git worktree."
22 }
23
24 fn input_schema(&self) -> Value {
25 json!({
26 "type": "object",
27 "properties": {
28 "path": {
29 "type": "string",
30 "description": "Path of the worktree to exit/remove"
31 },
32 "remove": {
33 "type": "boolean",
34 "description": "Whether to remove the worktree (default false)"
35 }
36 },
37 "required": ["path"]
38 })
39 }
40
41 fn permission_level(&self) -> PermissionLevel {
42 PermissionLevel::Dangerous
43 }
44
45 async fn execute(&self, input: Value) -> AppResult<String> {
46 let path = input
47 .get("path")
48 .and_then(|v| v.as_str())
49 .ok_or_else(|| AppError::Tool("missing 'path' field".into()))?;
50
51 let remove = input
52 .get("remove")
53 .and_then(|v| v.as_bool())
54 .unwrap_or(false);
55
56 tracing::info!(path, remove, "exiting worktree");
57
58 if remove {
59 let output = Command::new("git")
60 .args(["worktree", "remove", path])
61 .output()
62 .await
63 .map_err(|e| AppError::Tool(format!("failed to run git: {e}")))?;
64
65 if output.status.success() {
66 Ok(format!("Removed worktree at '{path}'."))
67 } else {
68 let stderr = String::from_utf8_lossy(&output.stderr);
69 Err(AppError::Tool(format!("git worktree remove failed: {stderr}")))
70 }
71 } else {
72 Ok(format!("Exited worktree at '{path}'. Use remove=true to delete it."))
73 }
74 }
75}