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