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 ///
31 /// Trailing positional args are forwarded verbatim to the underlying AI
32 /// tool (claude/codex/gemini). Use `--` to disambiguate when forwarded
33 /// args start with a hyphen, e.g. `gw new feat-x -- --model opus`.
34 #[command(group(
35 clap::ArgGroup::new("prompt_source")
36 .args(["prompt", "prompt_file"])
37 .multiple(false)
38 .required(false)
39 ))]
40 New {
41 /// Branch name for the new worktree
42 name: String,
43
44 /// Custom worktree path (default: ../<repo>-<branch>)
45 #[arg(long, value_hint = ValueHint::DirPath)]
46 path: Option<String>,
47
48 /// Base branch to create from (default: from config)
49 #[arg(long = "base")]
50 base: Option<String>,
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. Use `noop`, `none`, or
56 /// `skip` to skip the AI tool launch entirely.
57 #[arg(short = 'T', long = "term", value_name = "METHOD")]
58 term: Option<String>,
59
60 /// Initial prompt to pass to the AI tool. Use `-` to read the
61 /// prompt from standard input (e.g., `--prompt -` with a heredoc
62 /// or pipe).
63 #[arg(long)]
64 prompt: Option<String>,
65
66 /// Read the initial prompt from a file (recommended for multi-line prompts)
67 #[arg(long = "prompt-file", value_hint = ValueHint::FilePath)]
68 prompt_file: Option<PathBuf>,
69
70 /// Disable auto-forwarding of `<TOOL>_*` environment variables
71 /// (e.g. `CLAUDE_*` when ai-tool is claude). Without this flag, gw
72 /// snapshots the parent shell's matching env at invocation time
73 /// and re-injects it inside the spawned terminal so launchers like
74 /// wezterm/iterm/tmux/zellij behave like a normal child process.
75 #[arg(long = "no-env-forward")]
76 no_env_forward: bool,
77
78 /// Extra arguments forwarded verbatim to the AI tool (claude/codex/
79 /// gemini). Mutually exclusive with `--prompt`/`--prompt-file`
80 /// because both ultimately set the AI tool's prompt — use one or
81 /// the other.
82 #[arg(
83 trailing_var_arg = true,
84 allow_hyphen_values = true,
85 value_name = "AI_TOOL_ARGS"
86 )]
87 forward_args: Vec<String>,
88 },
89
90 /// Resume AI work in a worktree.
91 ///
92 /// Trailing positional args are forwarded verbatim to the AI tool. The
93 /// tool's own resume flag (`--continue`/`--resume`) is always injected
94 /// alongside, so `gw resume foo -- --model opus` resumes with opus.
95 Resume {
96 /// Branch name, worktree name, or path to resume (default: current worktree)
97 branch: Option<String>,
98
99 /// Terminal launch method for THIS invocation. Overrides config
100 /// and CW_LAUNCH_METHOD. Accepts canonical names or aliases.
101 /// Supports `method:session-name` for tmux/zellij. Use `noop`,
102 /// `none`, or `skip` to skip the AI tool launch.
103 #[arg(short = 'T', long = "term", value_name = "METHOD")]
104 term: Option<String>,
105
106 /// Disable auto-forwarding of `<TOOL>_*` environment variables.
107 #[arg(long = "no-env-forward")]
108 no_env_forward: bool,
109
110 /// Extra arguments forwarded verbatim to the AI tool (claude/codex/
111 /// gemini). The tool's resume flag is still injected automatically.
112 #[arg(
113 trailing_var_arg = true,
114 allow_hyphen_values = true,
115 value_name = "AI_TOOL_ARGS"
116 )]
117 forward_args: Vec<String>,
118 },
119
120 /// Launch AI tool in an existing worktree (default: current).
121 ///
122 /// Trailing positional args are forwarded verbatim to the AI tool.
123 #[command(group(
124 clap::ArgGroup::new("prompt_source")
125 .args(["prompt", "prompt_file"])
126 .multiple(false)
127 .required(false)
128 ))]
129 Spawn {
130 /// Worktree target — exact worktree name, branch name, or path.
131 /// Default: current worktree.
132 target: Option<String>,
133
134 /// Terminal launch method for THIS invocation. Overrides config
135 /// and CW_LAUNCH_METHOD. Accepts canonical names or aliases.
136 /// Supports `method:session-name` for tmux/zellij. Use `noop`,
137 /// `none`, or `skip` to skip the AI tool launch.
138 #[arg(short = 'T', long = "term", value_name = "METHOD")]
139 term: Option<String>,
140
141 /// Initial prompt to pass to the AI tool. Use `-` to read from stdin.
142 #[arg(long)]
143 prompt: Option<String>,
144
145 /// Read the initial prompt from a file.
146 #[arg(long = "prompt-file", value_hint = ValueHint::FilePath)]
147 prompt_file: Option<PathBuf>,
148
149 /// Disable auto-forwarding of `<TOOL>_*` environment variables.
150 #[arg(long = "no-env-forward")]
151 no_env_forward: bool,
152
153 /// Extra arguments forwarded verbatim to the AI tool.
154 #[arg(
155 trailing_var_arg = true,
156 allow_hyphen_values = true,
157 value_name = "AI_TOOL_ARGS"
158 )]
159 forward_args: Vec<String>,
160 },
161
162 /// Remove one or more worktrees.
163 ///
164 /// With no arguments: removes the current worktree (must be inside one).
165 /// With one or more positional targets: removes each of them; flags apply
166 /// to every target.
167 /// With `-i`: opens a multi-select UI.
168 ///
169 /// Exits 0 on full success, 1 if the user cancelled at the confirmation
170 /// prompt or in the interactive UI, 2 if any target could not be removed
171 /// (not found, busy, or an error).
172 Rm {
173 /// Branch names or paths of worktrees to remove.
174 /// If empty and --interactive is not set, removes the current worktree.
175 #[arg(conflicts_with = "interactive")]
176 targets: Vec<String>,
177
178 /// Interactive multi-select UI (mutually exclusive with positional targets)
179 #[arg(short, long, conflicts_with = "targets")]
180 interactive: bool,
181
182 /// Show what would be removed without removing
183 #[arg(long)]
184 dry_run: bool,
185
186 /// Keep the branch (only remove worktree)
187 #[arg(short = 'k', long)]
188 keep_branch: bool,
189
190 /// Also delete the remote branch
191 #[arg(short = 'r', long)]
192 delete_remote: bool,
193
194 /// Force remove: also bypasses the busy-detection gate (skips the
195 /// "worktree is in use" check and removes anyway)
196 #[arg(short, long, conflicts_with = "no_force")]
197 force: bool,
198
199 /// Don't use --force flag
200 #[arg(long)]
201 no_force: bool,
202 },
203
204 /// List all worktrees (rich, human-readable)
205 List,
206
207 /// Print all worktrees as TSV (for scripts)
208 ///
209 /// Columns: worktree_id, branch, status, age, repo_root, path
210 Ls,
211
212 /// Run diagnostics
213 Doctor {
214 /// Hook-friendly mode: emit a single-line summary and exit 0.
215 #[arg(long)]
216 session_start: bool,
217 /// Suppress informational chatter; keep only the summary.
218 #[arg(long)]
219 quiet: bool,
220 },
221
222 /// Check for updates / upgrade
223 Upgrade {
224 /// Skip the confirmation prompt; required for non-TTY environments.
225 #[arg(short, long)]
226 yes: bool,
227 },
228
229 /// Install Claude Code skill for worktree task delegation
230 #[command(name = "setup-claude")]
231 SetupClaude,
232
233 /// Interactive shell integration setup
234 ShellSetup,
235
236 /// Run cmd in one specific worktree.
237 Exec {
238 /// Worktree name, branch name, or path.
239 target: String,
240
241 /// Command and args.
242 #[arg(trailing_var_arg = true, allow_hyphen_values = true, required = true)]
243 cmd: Vec<String>,
244 },
245
246 /// Run cmd in every worktree in scope.
247 Run {
248 /// Glob filter on branch name or worktree directory basename
249 /// (e.g. 'feat-*'). Matches if either side does.
250 #[arg(long)]
251 only: Option<String>,
252 /// Skip the main worktree.
253 #[arg(long = "no-main")]
254 no_main: bool,
255 /// Parallel worktrees.
256 #[arg(short = 'j', long, default_value = "1")]
257 jobs: usize,
258 /// Continue past per-worktree failures.
259 #[arg(long)]
260 continue_on_error: bool,
261 /// Command and args.
262 #[arg(trailing_var_arg = true, allow_hyphen_values = true, required = true)]
263 cmd: Vec<String>,
264 },
265
266 /// Hook helper: read a Claude Code hook payload from stdin (or a file)
267 /// and decide whether to allow or block the inbound tool use. Exits 0
268 /// to allow; non-zero with stderr message to block.
269 Guard {
270 /// Path to read the hook payload from, or "-" for stdin.
271 #[arg(long, value_name = "PATH")]
272 tool_input: String,
273 },
274
275 /// [Internal] Get worktree path for a branch
276 #[command(name = "_path", hide = true)]
277 Path {
278 /// Branch name
279 branch: Option<String>,
280
281 /// List branch names (for tab completion)
282 #[arg(long)]
283 list_branches: bool,
284
285 /// Interactive worktree selection
286 #[arg(short, long)]
287 interactive: bool,
288 },
289
290 /// Generate shell function for gw-cd
291 #[command(name = "_shell-function", hide = true)]
292 ShellFunction {
293 /// Shell type: bash, zsh, fish, or powershell
294 shell: String,
295 },
296
297 /// Refresh update cache (background process)
298 #[command(name = "_update-cache", hide = true)]
299 UpdateCache,
300
301 /// [Internal] Print completion targets for the current repo (one per line)
302 #[command(name = "_complete-targets", hide = true)]
303 CompleteTargets,
304
305 /// [Internal] Execute an AI tool spawn spec file
306 #[command(name = "_spawn-ai", hide = true)]
307 SpawnAi {
308 /// Path to the JSON spawn spec. If omitted, resolves the most recent
309 /// spec for the current worktree from `<git-dir>/gw-spawn-last.json`.
310 #[arg(value_hint = ValueHint::FilePath)]
311 spec: Option<PathBuf>,
312 },
313}