Skip to main content

git_worktree_manager/operations/
shell.rs

1/// Shell command — open interactive shell or execute command in a worktree.
2///
3/// Mirrors shell_worktree from ai_tools.py.
4use std::path::PathBuf;
5
6use console::style;
7
8use crate::error::{CwError, Result};
9use crate::git;
10
11/// Open interactive shell or execute command in a worktree.
12pub fn shell_worktree(worktree: Option<&str>, command: Option<Vec<String>>) -> Result<()> {
13    let target_path: PathBuf;
14
15    if let Some(wt) = worktree {
16        let repo = git::get_repo_root(None)?;
17        let path = git::find_worktree_by_branch(&repo, wt)?
18            .or(git::find_worktree_by_branch(
19                &repo,
20                &format!("refs/heads/{}", git::normalize_branch_name(wt)),
21            )?)
22            .ok_or_else(|| {
23                CwError::WorktreeNotFound(format!("No worktree found for branch '{}'", wt))
24            })?;
25        target_path = path;
26    } else {
27        target_path = std::env::current_dir()?;
28        // Verify we're in a git repo
29        let _ = git::get_current_branch(Some(&target_path))?;
30    }
31
32    if !target_path.exists() {
33        return Err(CwError::WorktreeNotFound(format!(
34            "Worktree directory does not exist: {}",
35            target_path.display()
36        )));
37    }
38
39    if let Some(cmd) = command {
40        if cmd.is_empty() {
41            return open_shell(&target_path, worktree);
42        }
43        // Execute command
44        println!(
45            "{} {}\n",
46            style(format!("Executing in {}:", target_path.display())).cyan(),
47            cmd.join(" ")
48        );
49
50        let status = std::process::Command::new(&cmd[0])
51            .args(&cmd[1..])
52            .current_dir(&target_path)
53            .status()?;
54
55        std::process::exit(status.code().unwrap_or(1));
56    } else {
57        open_shell(&target_path, worktree)?;
58    }
59
60    Ok(())
61}
62
63fn open_shell(path: &std::path::Path, branch: Option<&str>) -> Result<()> {
64    let branch_name = branch
65        .map(|b| b.to_string())
66        .or_else(|| git::get_current_branch(Some(path)).ok())
67        .unwrap_or_else(|| "unknown".to_string());
68
69    println!(
70        "{} {}\n{}\n{}\n",
71        style("Opening shell in worktree:").cyan().bold(),
72        branch_name,
73        style(format!("Path: {}", path.display())).dim(),
74        style("Type 'exit' to return").dim(),
75    );
76
77    let shell = std::env::var("SHELL").unwrap_or_else(|_| "/bin/bash".to_string());
78
79    let _ = std::process::Command::new(&shell)
80        .current_dir(path)
81        .status();
82
83    Ok(())
84}