Skip to main content

git_worktree_manager/
cli.rs

1/// CLI definitions using clap derive.
2///
3/// Mirrors the Typer-based CLI in src/git_worktree_manager/cli.py.
4pub mod completions;
5
6use clap::{Parser, Subcommand, ValueHint};
7use std::path::PathBuf;
8
9/// Git worktree manager CLI.
10#[derive(Parser, Debug)]
11#[command(
12    name = "gw",
13    version,
14    about = "git worktree manager — AI coding assistant integration",
15    long_about = None,
16    arg_required_else_help = true,
17)]
18pub struct Cli {
19    /// Generate shell completions for the given shell
20    #[arg(long, value_name = "SHELL", value_parser = clap::builder::PossibleValuesParser::new(["bash", "zsh", "fish", "powershell", "elvish"]))]
21    pub generate_completion: Option<String>,
22
23    #[command(subcommand)]
24    pub command: Option<Commands>,
25}
26
27#[derive(Subcommand, Debug)]
28pub enum Commands {
29    /// Create new worktree for feature branch
30    #[command(group(
31        clap::ArgGroup::new("prompt_source")
32            .args(["prompt", "prompt_file", "prompt_stdin"])
33            .multiple(false)
34            .required(false)
35    ))]
36    New {
37        /// Branch name for the new worktree
38        name: String,
39
40        /// Custom worktree path (default: ../<repo>-<branch>)
41        #[arg(short, long, value_hint = ValueHint::DirPath)]
42        path: Option<String>,
43
44        /// Base branch to create from (default: from config)
45        #[arg(short = 'b', long = "base")]
46        base: Option<String>,
47
48        /// Skip AI tool launch
49        #[arg(long = "no-term")]
50        no_term: bool,
51
52        /// Initial prompt to pass to the AI tool (starts interactive session with task)
53        #[arg(long)]
54        prompt: Option<String>,
55
56        /// Read the initial prompt from a file (recommended for multi-line prompts)
57        #[arg(long = "prompt-file", value_hint = ValueHint::FilePath)]
58        prompt_file: Option<PathBuf>,
59
60        /// Read the initial prompt from standard input
61        #[arg(long = "prompt-stdin")]
62        prompt_stdin: bool,
63    },
64
65    /// Resume AI work in a worktree
66    Resume {
67        /// Branch name, worktree name, or path to resume (default: current worktree)
68        branch: Option<String>,
69    },
70
71    /// Launch AI tool in an existing worktree (default: current).
72    #[command(group(
73        clap::ArgGroup::new("prompt_source")
74            .args(["prompt", "prompt_file", "prompt_stdin"])
75            .multiple(false)
76            .required(false)
77    ))]
78    Spawn {
79        /// Worktree target — exact worktree name, branch name, or path.
80        /// Default: current worktree.
81        target: Option<String>,
82
83        /// Initial prompt to pass to the AI tool.
84        #[arg(long)]
85        prompt: Option<String>,
86
87        /// Read the initial prompt from a file.
88        #[arg(long = "prompt-file", value_hint = ValueHint::FilePath)]
89        prompt_file: Option<PathBuf>,
90
91        /// Read the initial prompt from standard input.
92        #[arg(long = "prompt-stdin")]
93        prompt_stdin: bool,
94    },
95
96    /// Remove one or more worktrees.
97    ///
98    /// With no arguments: removes the current worktree (must be inside one).
99    /// With one or more positional targets: removes each of them; flags apply
100    /// to every target.
101    /// With `-i`: opens a multi-select UI.
102    ///
103    /// Exits 0 on full success, 1 if the user cancelled at the confirmation
104    /// prompt or in the interactive UI, 2 if any target could not be removed
105    /// (not found, busy, or an error).
106    Rm {
107        /// Branch names or paths of worktrees to remove.
108        /// If empty and --interactive is not set, removes the current worktree.
109        #[arg(conflicts_with = "interactive")]
110        targets: Vec<String>,
111
112        /// Interactive multi-select UI (mutually exclusive with positional targets)
113        #[arg(short, long, conflicts_with = "targets")]
114        interactive: bool,
115
116        /// Show what would be removed without removing
117        #[arg(long)]
118        dry_run: bool,
119
120        /// Keep the branch (only remove worktree)
121        #[arg(short = 'k', long)]
122        keep_branch: bool,
123
124        /// Also delete the remote branch
125        #[arg(short = 'r', long)]
126        delete_remote: bool,
127
128        /// Force remove: also bypasses the busy-detection gate (skips the
129        /// "worktree is in use" check and removes anyway)
130        #[arg(short, long, conflicts_with = "no_force")]
131        force: bool,
132
133        /// Don't use --force flag
134        #[arg(long)]
135        no_force: bool,
136    },
137
138    /// List all worktrees (rich, human-readable)
139    List,
140
141    /// Print all worktrees as TSV (for scripts)
142    ///
143    /// Columns: worktree_id, branch, status, age, repo_root, path
144    Ls,
145
146    /// Run diagnostics
147    Doctor {
148        /// Hook-friendly mode: emit a single-line summary and exit 0.
149        #[arg(long)]
150        session_start: bool,
151        /// Suppress informational chatter; keep only the summary.
152        #[arg(long)]
153        quiet: bool,
154    },
155
156    /// Check for updates / upgrade
157    Upgrade {
158        /// Skip the confirmation prompt; required for non-TTY environments.
159        #[arg(short, long)]
160        yes: bool,
161    },
162
163    /// Install Claude Code skill for worktree task delegation
164    #[command(name = "setup-claude")]
165    SetupClaude,
166
167    /// Interactive shell integration setup
168    ShellSetup,
169
170    /// Run cmd in one specific worktree.
171    Exec {
172        /// Worktree name, branch name, or path.
173        target: String,
174
175        /// Command and args.
176        #[arg(trailing_var_arg = true, allow_hyphen_values = true, required = true)]
177        cmd: Vec<String>,
178    },
179
180    /// Run cmd in every worktree in scope.
181    Run {
182        /// Glob filter on branch name or worktree directory basename
183        /// (e.g. 'feat-*'). Matches if either side does.
184        #[arg(long)]
185        only: Option<String>,
186        /// Skip the main worktree.
187        #[arg(long = "no-main")]
188        no_main: bool,
189        /// Parallel worktrees.
190        #[arg(short = 'j', long, default_value = "1")]
191        jobs: usize,
192        /// Continue past per-worktree failures.
193        #[arg(long)]
194        continue_on_error: bool,
195        /// Command and args.
196        #[arg(trailing_var_arg = true, allow_hyphen_values = true, required = true)]
197        cmd: Vec<String>,
198    },
199
200    /// Hook helper: read a Claude Code hook payload from stdin (or a file)
201    /// and decide whether to allow or block the inbound tool use. Exits 0
202    /// to allow; non-zero with stderr message to block.
203    Guard {
204        /// Path to read the hook payload from, or "-" for stdin.
205        #[arg(long, value_name = "PATH")]
206        tool_input: String,
207    },
208
209    /// [Internal] Get worktree path for a branch
210    #[command(name = "_path", hide = true)]
211    Path {
212        /// Branch name
213        branch: Option<String>,
214
215        /// List branch names (for tab completion)
216        #[arg(long)]
217        list_branches: bool,
218
219        /// Interactive worktree selection
220        #[arg(short, long)]
221        interactive: bool,
222    },
223
224    /// Generate shell function for gw-cd / cw-cd
225    #[command(name = "_shell-function", hide = true)]
226    ShellFunction {
227        /// Shell type: bash, zsh, fish, or powershell
228        shell: String,
229    },
230
231    /// Refresh update cache (background process)
232    #[command(name = "_update-cache", hide = true)]
233    UpdateCache,
234
235    /// [Internal] Print completion targets for the current repo (one per line)
236    #[command(name = "_complete-targets", hide = true)]
237    CompleteTargets,
238
239    /// [Internal] Execute an AI tool spawn spec file
240    #[command(name = "_spawn-ai", hide = true)]
241    SpawnAi {
242        /// Path to the JSON spawn spec. If omitted, resolves the most recent
243        /// spec for the current worktree from `<git-dir>/gw-spawn-last.json`.
244        #[arg(value_hint = ValueHint::FilePath)]
245        spec: Option<PathBuf>,
246    },
247}