use claude_rust_errors::{AppError, AppResult};
use claude_rust_types::{PermissionLevel, Tool};
use serde_json::{Value, json};
use tokio::process::Command;
pub struct EnterWorktreeTool;
impl EnterWorktreeTool {
pub fn new() -> Self {
Self
}
}
#[async_trait::async_trait]
impl Tool for EnterWorktreeTool {
fn name(&self) -> &str {
"enter_worktree"
}
fn description(&self) -> &str {
"Create a git worktree for the specified branch."
}
fn input_schema(&self) -> Value {
json!({
"type": "object",
"properties": {
"branch": {
"type": "string",
"description": "Branch name to create the worktree for"
},
"path": {
"type": "string",
"description": "Path for the worktree directory (defaults to ../branch-name)"
}
},
"required": ["branch"]
})
}
fn permission_level(&self) -> PermissionLevel {
PermissionLevel::Dangerous
}
fn is_destructive(&self, _input: &Value) -> bool { true }
async fn execute(&self, input: Value) -> AppResult<String> {
let branch = input
.get("branch")
.and_then(|v| v.as_str())
.ok_or_else(|| AppError::Tool("missing 'branch' field".into()))?;
let default_path = format!("../{branch}");
let path = input
.get("path")
.and_then(|v| v.as_str())
.unwrap_or(&default_path);
tracing::info!(branch, path, "creating worktree");
let output = Command::new("git")
.args(["worktree", "add", path, "-b", branch])
.output()
.await
.map_err(|e| AppError::Tool(format!("failed to run git: {e}")))?;
if output.status.success() {
return Ok(format!("Created worktree at '{path}' on new branch '{branch}'."));
}
let output = Command::new("git")
.args(["worktree", "add", path, branch])
.output()
.await
.map_err(|e| AppError::Tool(format!("failed to run git: {e}")))?;
if output.status.success() {
Ok(format!("Created worktree at '{path}' on existing branch '{branch}'."))
} else {
let stderr = String::from_utf8_lossy(&output.stderr);
Err(AppError::Tool(format!("git worktree add failed: {stderr}")))
}
}
}