Skip to main content

stynx_code_tools/infrastructure/
enter_worktree_tool.rs

1use stynx_code_errors::{AppError, AppResult};
2use stynx_code_types::{PermissionLevel, Tool};
3use serde_json::{Value, json};
4use tokio::process::Command;
5
6pub struct EnterWorktreeTool;
7
8impl EnterWorktreeTool {
9    pub fn new() -> Self {
10        Self
11    }
12}
13
14#[async_trait::async_trait]
15impl Tool for EnterWorktreeTool {
16    fn name(&self) -> &str {
17        "enter_worktree"
18    }
19
20    fn description(&self) -> &str {
21        "Create a git worktree for the specified branch."
22    }
23
24    fn input_schema(&self) -> Value {
25        json!({
26            "type": "object",
27            "properties": {
28                "branch": {
29                    "type": "string",
30                    "description": "Branch name to create the worktree for"
31                },
32                "path": {
33                    "type": "string",
34                    "description": "Path for the worktree directory (defaults to ../branch-name)"
35                }
36            },
37            "required": ["branch"]
38        })
39    }
40
41    fn permission_level(&self) -> PermissionLevel {
42        PermissionLevel::Dangerous
43    }
44
45    fn is_destructive(&self, _input: &Value) -> bool { true }
46
47    async fn execute(&self, input: Value) -> AppResult<String> {
48        let branch = input
49            .get("branch")
50            .and_then(|v| v.as_str())
51            .ok_or_else(|| AppError::Tool("missing 'branch' field".into()))?;
52
53        let default_path = format!("../{branch}");
54        let path = input
55            .get("path")
56            .and_then(|v| v.as_str())
57            .unwrap_or(&default_path);
58
59        tracing::info!(branch, path, "creating worktree");
60
61        let output = Command::new("git")
62            .args(["worktree", "add", path, "-b", branch])
63            .output()
64            .await
65            .map_err(|e| AppError::Tool(format!("failed to run git: {e}")))?;
66
67        if output.status.success() {
68            return Ok(format!("Created worktree at '{path}' on new branch '{branch}'."));
69        }
70
71        let output = Command::new("git")
72            .args(["worktree", "add", path, branch])
73            .output()
74            .await
75            .map_err(|e| AppError::Tool(format!("failed to run git: {e}")))?;
76
77        if output.status.success() {
78            Ok(format!("Created worktree at '{path}' on existing branch '{branch}'."))
79        } else {
80            let stderr = String::from_utf8_lossy(&output.stderr);
81            Err(AppError::Tool(format!("git worktree add failed: {stderr}")))
82        }
83    }
84}