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}