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;
5pub mod global;
6
7use clap::{Parser, Subcommand, ValueHint};
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 /// Run in global mode (across all registered repositories)
20 #[arg(short = 'g', long = "global", global = true)]
21 pub global: bool,
22
23 /// Generate shell completions for the given shell
24 #[arg(long, value_name = "SHELL")]
25 pub generate_completion: Option<String>,
26
27 #[command(subcommand)]
28 pub command: Option<Commands>,
29}
30
31#[derive(Subcommand, Debug)]
32pub enum Commands {
33 /// Create new worktree for feature branch
34 New {
35 /// Branch name for the new worktree
36 name: String,
37
38 /// Custom worktree path (default: ../<repo>-<branch>)
39 #[arg(short, long, value_hint = ValueHint::DirPath)]
40 path: Option<String>,
41
42 /// Base branch to create from (default: from config)
43 #[arg(short = 'b', long = "base")]
44 base: Option<String>,
45
46 /// Skip AI tool launch
47 #[arg(long = "no-term")]
48 no_term: bool,
49
50 /// Terminal launch method (e.g., tmux, iterm-tab, zellij)
51 #[arg(short = 'T', long)]
52 term: Option<String>,
53
54 /// Launch AI tool in background
55 #[arg(long)]
56 bg: bool,
57 },
58
59 /// Create GitHub Pull Request from worktree
60 Pr {
61 /// Branch name (default: current worktree branch)
62 branch: Option<String>,
63
64 /// PR title
65 #[arg(short, long)]
66 title: Option<String>,
67
68 /// PR body
69 #[arg(short = 'B', long)]
70 body: Option<String>,
71
72 /// Create as draft PR
73 #[arg(short, long)]
74 draft: bool,
75
76 /// Skip pushing to remote
77 #[arg(long)]
78 no_push: bool,
79
80 /// Resolve target as worktree name (instead of branch)
81 #[arg(short, long)]
82 worktree: bool,
83
84 /// Resolve target as branch name (instead of worktree)
85 #[arg(short = 'b', long = "by-branch", conflicts_with = "worktree")]
86 by_branch: bool,
87 },
88
89 /// Merge feature branch into base branch
90 Merge {
91 /// Branch name (default: current worktree branch)
92 branch: Option<String>,
93
94 /// Interactive rebase
95 #[arg(short, long)]
96 interactive: bool,
97
98 /// Dry run (show what would happen)
99 #[arg(long)]
100 dry_run: bool,
101
102 /// Push to remote after merge
103 #[arg(long)]
104 push: bool,
105
106 /// Use AI to resolve merge conflicts
107 #[arg(long)]
108 ai_merge: bool,
109
110 /// Resolve target as worktree name (instead of branch)
111 #[arg(short, long)]
112 worktree: bool,
113 },
114
115 /// Resume AI work in a worktree
116 Resume {
117 /// Branch name to resume (default: current worktree)
118 branch: Option<String>,
119
120 /// Terminal launch method
121 #[arg(short = 'T', long)]
122 term: Option<String>,
123
124 /// Launch AI tool in background
125 #[arg(long)]
126 bg: bool,
127
128 /// Resolve target as worktree name (instead of branch)
129 #[arg(short, long)]
130 worktree: bool,
131
132 /// Resolve target as branch name (instead of worktree)
133 #[arg(short, long, conflicts_with = "worktree")]
134 by_branch: bool,
135 },
136
137 /// Open interactive shell or execute command in a worktree
138 Shell {
139 /// Worktree branch to shell into
140 worktree: Option<String>,
141
142 /// Command and arguments to execute
143 #[arg(trailing_var_arg = true, allow_hyphen_values = true)]
144 args: Vec<String>,
145 },
146
147 /// Show current worktree status
148 Status,
149
150 /// Delete a worktree
151 Delete {
152 /// Branch name or path of worktree to delete (default: current worktree)
153 target: Option<String>,
154
155 /// Keep the branch (only remove worktree)
156 #[arg(short = 'k', long)]
157 keep_branch: bool,
158
159 /// Also delete the remote branch
160 #[arg(short = 'r', long)]
161 delete_remote: bool,
162
163 /// Don't use --force flag
164 #[arg(long)]
165 no_force: bool,
166
167 /// Resolve target as worktree name (instead of branch)
168 #[arg(short, long)]
169 worktree: bool,
170
171 /// Resolve target as branch name (instead of worktree)
172 #[arg(short, long, conflicts_with = "worktree")]
173 branch: bool,
174 },
175
176 /// List all worktrees
177 #[command(alias = "ls")]
178 List,
179
180 /// Batch cleanup of worktrees
181 Clean {
182 /// Delete worktrees for branches already merged to base
183 #[arg(long)]
184 merged: bool,
185
186 /// Delete worktrees older than N days
187 #[arg(long, value_name = "DAYS")]
188 older_than: Option<u64>,
189
190 /// Interactive selection UI
191 #[arg(short, long)]
192 interactive: bool,
193
194 /// Show what would be deleted without deleting
195 #[arg(long)]
196 dry_run: bool,
197 },
198
199 /// Display worktree hierarchy as a tree
200 Tree,
201
202 /// Show worktree statistics
203 Stats,
204
205 /// Compare two branches
206 Diff {
207 /// First branch
208 branch1: String,
209 /// Second branch
210 branch2: String,
211 /// Show statistics only
212 #[arg(short, long)]
213 summary: bool,
214 /// Show changed files only
215 #[arg(short, long)]
216 files: bool,
217 },
218
219 /// Sync worktree with base branch
220 Sync {
221 /// Branch name (default: current worktree)
222 branch: Option<String>,
223
224 /// Sync all worktrees
225 #[arg(long)]
226 all: bool,
227
228 /// Only fetch updates without rebasing
229 #[arg(long)]
230 fetch_only: bool,
231
232 /// Use AI to resolve merge conflicts
233 #[arg(long)]
234 ai_merge: bool,
235
236 /// Resolve target as worktree name (instead of branch)
237 #[arg(short, long)]
238 worktree: bool,
239
240 /// Resolve target as branch name (instead of worktree)
241 #[arg(short, long, conflicts_with = "worktree")]
242 by_branch: bool,
243 },
244
245 /// Change base branch for a worktree
246 ChangeBase {
247 /// New base branch
248 new_base: String,
249 /// Branch name (default: current worktree)
250 branch: Option<String>,
251
252 /// Dry run (show what would happen)
253 #[arg(long)]
254 dry_run: bool,
255
256 /// Interactive rebase
257 #[arg(short, long)]
258 interactive: bool,
259
260 /// Resolve target as worktree name (instead of branch)
261 #[arg(short, long)]
262 worktree: bool,
263
264 /// Resolve target as branch name (instead of worktree)
265 #[arg(short, long, conflicts_with = "worktree")]
266 by_branch: bool,
267 },
268
269 /// Configuration management
270 Config {
271 #[command(subcommand)]
272 action: ConfigAction,
273 },
274
275 /// Backup and restore worktrees
276 Backup {
277 #[command(subcommand)]
278 action: BackupAction,
279 },
280
281 /// Stash management (worktree-aware)
282 Stash {
283 #[command(subcommand)]
284 action: StashAction,
285 },
286
287 /// Manage lifecycle hooks
288 Hook {
289 #[command(subcommand)]
290 action: HookAction,
291 },
292
293 /// Export worktree configuration to a file
294 Export {
295 /// Output file path
296 #[arg(short, long)]
297 output: Option<String>,
298 },
299
300 /// Import worktree configuration from a file
301 Import {
302 /// Path to the configuration file to import
303 import_file: String,
304
305 /// Apply the imported configuration (default: preview only)
306 #[arg(long)]
307 apply: bool,
308 },
309
310 /// Scan for repositories (global mode)
311 Scan {
312 /// Base directory to scan (default: home directory)
313 #[arg(short, long, value_hint = ValueHint::DirPath)]
314 dir: Option<std::path::PathBuf>,
315 },
316
317 /// Clean up stale registry entries (global mode)
318 Prune,
319
320 /// Run diagnostics
321 Doctor,
322
323 /// Check for updates / upgrade
324 Upgrade,
325
326 /// Interactive shell integration setup
327 ShellSetup,
328
329 /// [Internal] Get worktree path for a branch
330 #[command(name = "_path", hide = true)]
331 Path {
332 /// Branch name
333 branch: Option<String>,
334
335 /// List branch names (for tab completion)
336 #[arg(long)]
337 list_branches: bool,
338
339 /// Interactive worktree selection
340 #[arg(short, long)]
341 interactive: bool,
342 },
343
344 /// Generate shell function for gw-cd / cw-cd
345 #[command(name = "_shell-function", hide = true)]
346 ShellFunction {
347 /// Shell type: bash, zsh, fish, or powershell
348 shell: String,
349 },
350}
351
352#[derive(Subcommand, Debug)]
353pub enum ConfigAction {
354 /// Show current configuration
355 Show,
356 /// Set a configuration value
357 Set {
358 /// Dot-separated config key (e.g., git.default_base_branch)
359 key: String,
360 /// Value to set
361 value: String,
362 },
363 /// Use a predefined AI tool preset
364 UsePreset {
365 /// Preset name (e.g., claude, codex, no-op)
366 name: String,
367 },
368 /// List available presets
369 ListPresets,
370 /// Reset configuration to defaults
371 Reset,
372}
373
374#[derive(Subcommand, Debug)]
375pub enum BackupAction {
376 /// Create backup of worktree(s) using git bundle
377 Create {
378 /// Branch name to backup (default: current worktree)
379 branch: Option<String>,
380
381 /// Backup all worktrees
382 #[arg(long)]
383 all: bool,
384
385 /// Output directory for backups
386 #[arg(short, long)]
387 output: Option<String>,
388 },
389 /// List available backups
390 List {
391 /// Filter by branch name
392 branch: Option<String>,
393 },
394 /// Restore worktree from backup
395 Restore {
396 /// Branch name to restore
397 branch: String,
398
399 /// Custom path for restored worktree
400 #[arg(short, long)]
401 path: Option<String>,
402
403 /// Backup ID (timestamp) to restore (default: latest)
404 #[arg(long)]
405 id: Option<String>,
406 },
407}
408
409#[derive(Subcommand, Debug)]
410pub enum StashAction {
411 /// Save changes in current worktree to stash
412 Save {
413 /// Optional message to describe the stash
414 message: Option<String>,
415 },
416 /// List all stashes organized by worktree/branch
417 List,
418 /// Apply a stash to a different worktree
419 Apply {
420 /// Branch name of worktree to apply stash to
421 target_branch: String,
422
423 /// Stash reference (default: stash@{0})
424 #[arg(short, long, default_value = "stash@{0}")]
425 stash: String,
426 },
427}
428
429#[derive(Subcommand, Debug)]
430pub enum HookAction {
431 /// Add a new hook for an event
432 Add {
433 /// Hook event (e.g., worktree.post_create, merge.pre)
434 event: String,
435 /// Shell command to execute
436 command: String,
437 /// Custom hook identifier
438 #[arg(long)]
439 id: Option<String>,
440 /// Human-readable description
441 #[arg(short, long)]
442 description: Option<String>,
443 },
444 /// Remove a hook
445 Remove {
446 /// Hook event
447 event: String,
448 /// Hook identifier to remove
449 hook_id: String,
450 },
451 /// List all hooks
452 List {
453 /// Filter by event
454 event: Option<String>,
455 },
456 /// Enable a disabled hook
457 Enable {
458 /// Hook event
459 event: String,
460 /// Hook identifier
461 hook_id: String,
462 },
463 /// Disable a hook without removing it
464 Disable {
465 /// Hook event
466 event: String,
467 /// Hook identifier
468 hook_id: String,
469 },
470 /// Manually run all hooks for an event
471 Run {
472 /// Hook event to run
473 event: String,
474 /// Show what would be executed without running
475 #[arg(long)]
476 dry_run: bool,
477 },
478}