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 /// Terminal launch method for THIS invocation. Overrides config
53 /// and CW_LAUNCH_METHOD. Accepts canonical names (e.g.,
54 /// `wezterm-tab`) or aliases (e.g., `w-t`, `w-t-b`). Supports
55 /// `method:session-name` for tmux/zellij. Mutually exclusive
56 /// with `--no-term`.
57 #[arg(
58 short = 'T',
59 long = "term",
60 value_name = "METHOD",
61 conflicts_with = "no_term"
62 )]
63 term: Option<String>,
64
65 /// Initial prompt to pass to the AI tool (starts interactive session with task)
66 #[arg(long)]
67 prompt: Option<String>,
68
69 /// Read the initial prompt from a file (recommended for multi-line prompts)
70 #[arg(long = "prompt-file", value_hint = ValueHint::FilePath)]
71 prompt_file: Option<PathBuf>,
72
73 /// Read the initial prompt from standard input
74 #[arg(long = "prompt-stdin")]
75 prompt_stdin: bool,
76 },
77
78 /// Resume AI work in a worktree
79 Resume {
80 /// Branch name, worktree name, or path to resume (default: current worktree)
81 branch: Option<String>,
82
83 /// Terminal launch method for THIS invocation. Overrides config
84 /// and CW_LAUNCH_METHOD. Accepts canonical names or aliases.
85 /// Supports `method:session-name` for tmux/zellij.
86 #[arg(short = 'T', long = "term", value_name = "METHOD")]
87 term: Option<String>,
88 },
89
90 /// Launch AI tool in an existing worktree (default: current).
91 #[command(group(
92 clap::ArgGroup::new("prompt_source")
93 .args(["prompt", "prompt_file", "prompt_stdin"])
94 .multiple(false)
95 .required(false)
96 ))]
97 Spawn {
98 /// Worktree target — exact worktree name, branch name, or path.
99 /// Default: current worktree.
100 target: Option<String>,
101
102 /// Terminal launch method for THIS invocation. Overrides config
103 /// and CW_LAUNCH_METHOD. Accepts canonical names or aliases.
104 /// Supports `method:session-name` for tmux/zellij.
105 #[arg(short = 'T', long = "term", value_name = "METHOD")]
106 term: Option<String>,
107
108 /// Initial prompt to pass to the AI tool.
109 #[arg(long)]
110 prompt: Option<String>,
111
112 /// Read the initial prompt from a file.
113 #[arg(long = "prompt-file", value_hint = ValueHint::FilePath)]
114 prompt_file: Option<PathBuf>,
115
116 /// Read the initial prompt from standard input.
117 #[arg(long = "prompt-stdin")]
118 prompt_stdin: bool,
119 },
120
121 /// Remove one or more worktrees.
122 ///
123 /// With no arguments: removes the current worktree (must be inside one).
124 /// With one or more positional targets: removes each of them; flags apply
125 /// to every target.
126 /// With `-i`: opens a multi-select UI.
127 ///
128 /// Exits 0 on full success, 1 if the user cancelled at the confirmation
129 /// prompt or in the interactive UI, 2 if any target could not be removed
130 /// (not found, busy, or an error).
131 Rm {
132 /// Branch names or paths of worktrees to remove.
133 /// If empty and --interactive is not set, removes the current worktree.
134 #[arg(conflicts_with = "interactive")]
135 targets: Vec<String>,
136
137 /// Interactive multi-select UI (mutually exclusive with positional targets)
138 #[arg(short, long, conflicts_with = "targets")]
139 interactive: bool,
140
141 /// Show what would be removed without removing
142 #[arg(long)]
143 dry_run: bool,
144
145 /// Keep the branch (only remove worktree)
146 #[arg(short = 'k', long)]
147 keep_branch: bool,
148
149 /// Also delete the remote branch
150 #[arg(short = 'r', long)]
151 delete_remote: bool,
152
153 /// Force remove: also bypasses the busy-detection gate (skips the
154 /// "worktree is in use" check and removes anyway)
155 #[arg(short, long, conflicts_with = "no_force")]
156 force: bool,
157
158 /// Don't use --force flag
159 #[arg(long)]
160 no_force: bool,
161 },
162
163 /// List all worktrees (rich, human-readable)
164 List,
165
166 /// Print all worktrees as TSV (for scripts)
167 ///
168 /// Columns: worktree_id, branch, status, age, repo_root, path
169 Ls,
170
171 /// Run diagnostics
172 Doctor {
173 /// Hook-friendly mode: emit a single-line summary and exit 0.
174 #[arg(long)]
175 session_start: bool,
176 /// Suppress informational chatter; keep only the summary.
177 #[arg(long)]
178 quiet: bool,
179 },
180
181 /// Check for updates / upgrade
182 Upgrade {
183 /// Skip the confirmation prompt; required for non-TTY environments.
184 #[arg(short, long)]
185 yes: bool,
186 },
187
188 /// Install Claude Code skill for worktree task delegation
189 #[command(name = "setup-claude")]
190 SetupClaude,
191
192 /// Interactive shell integration setup
193 ShellSetup,
194
195 /// Run cmd in one specific worktree.
196 Exec {
197 /// Worktree name, branch name, or path.
198 target: String,
199
200 /// Command and args.
201 #[arg(trailing_var_arg = true, allow_hyphen_values = true, required = true)]
202 cmd: Vec<String>,
203 },
204
205 /// Run cmd in every worktree in scope.
206 Run {
207 /// Glob filter on branch name or worktree directory basename
208 /// (e.g. 'feat-*'). Matches if either side does.
209 #[arg(long)]
210 only: Option<String>,
211 /// Skip the main worktree.
212 #[arg(long = "no-main")]
213 no_main: bool,
214 /// Parallel worktrees.
215 #[arg(short = 'j', long, default_value = "1")]
216 jobs: usize,
217 /// Continue past per-worktree failures.
218 #[arg(long)]
219 continue_on_error: bool,
220 /// Command and args.
221 #[arg(trailing_var_arg = true, allow_hyphen_values = true, required = true)]
222 cmd: Vec<String>,
223 },
224
225 /// Hook helper: read a Claude Code hook payload from stdin (or a file)
226 /// and decide whether to allow or block the inbound tool use. Exits 0
227 /// to allow; non-zero with stderr message to block.
228 Guard {
229 /// Path to read the hook payload from, or "-" for stdin.
230 #[arg(long, value_name = "PATH")]
231 tool_input: String,
232 },
233
234 /// [Internal] Get worktree path for a branch
235 #[command(name = "_path", hide = true)]
236 Path {
237 /// Branch name
238 branch: Option<String>,
239
240 /// List branch names (for tab completion)
241 #[arg(long)]
242 list_branches: bool,
243
244 /// Interactive worktree selection
245 #[arg(short, long)]
246 interactive: bool,
247 },
248
249 /// Generate shell function for gw-cd / cw-cd
250 #[command(name = "_shell-function", hide = true)]
251 ShellFunction {
252 /// Shell type: bash, zsh, fish, or powershell
253 shell: String,
254 },
255
256 /// Refresh update cache (background process)
257 #[command(name = "_update-cache", hide = true)]
258 UpdateCache,
259
260 /// [Internal] Print completion targets for the current repo (one per line)
261 #[command(name = "_complete-targets", hide = true)]
262 CompleteTargets,
263
264 /// [Internal] Execute an AI tool spawn spec file
265 #[command(name = "_spawn-ai", hide = true)]
266 SpawnAi {
267 /// Path to the JSON spawn spec. If omitted, resolves the most recent
268 /// spec for the current worktree from `<git-dir>/gw-spawn-last.json`.
269 #[arg(value_hint = ValueHint::FilePath)]
270 spec: Option<PathBuf>,
271 },
272}