1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
use anyhow::Result;
use clap::{CommandFactory, Parser, Subcommand};
use clap_complete::{Shell, generate};
use std::env;
use std::io;
/// A simple Git worktree manager
#[derive(Parser)]
#[command(author, version, about, long_about = None)]
#[command(arg_required_else_help = true)]
struct Cli {
#[command(subcommand)]
command: Option<Commands>,
}
#[derive(Subcommand)]
enum Commands {
/// Add a new worktree
Add {
/// Name of the worktree
name: String,
},
/// List all worktrees
List,
/// Remove a worktree
Remove {
/// Name of the worktree to remove
name: String,
},
/// Show status of all worktrees
Status,
/// Pull changes in worktrees
Pull {
/// Pull all worktrees
#[arg(long, short)]
all: bool,
/// Specific worktree name to pull (optional)
name: Option<String>,
},
/// Prune merged worktrees
Prune {
/// Show what would be pruned without actually removing
#[arg(long)]
dry_run: bool,
/// Skip confirmation prompt
#[arg(long, short)]
force: bool,
},
/// Add or show a note for a worktree
Note {
/// Name of the worktree
name: String,
/// Note text to save
text: Option<String>,
/// External reference (e.g. JIRA-123, GitHub issue URL)
#[arg(long)]
ref_: Option<String>,
},
/// Show context of worktrees for AI
Context {
/// Name of the worktree (optional, shows all if omitted)
name: Option<String>,
/// Save context to a file (.gwtr-context.md)
#[arg(long)]
save: bool,
},
/// Audit dependencies for supply chain attacks
Audit {
/// Only run typosquatting detection (skips cargo audit)
#[arg(long)]
typo_only: bool,
/// Run audit on all worktrees
#[arg(long, short)]
all: bool,
},
/// Generate shell completion script
Completions {
/// Shell to generate completions for (zsh, bash, fish, powershell, elvish)
shell: Shell,
},
}
fn main() {
if let Err(e) = run() {
eprintln!("Error: {}", e);
std::process::exit(1);
}
}
fn run() -> Result<()> {
let cli = Cli::parse();
match &cli.command {
Some(Commands::Add { name }) => {
// Validate git repository
let current_dir = env::current_dir()?;
let repo = gwtr::ensure_git_repository(¤t_dir)?;
// Create worktree
gwtr::create_worktree(&repo, name)?;
}
Some(Commands::List) => {
// Validate git repository
let current_dir = env::current_dir()?;
let repo = gwtr::ensure_git_repository(¤t_dir)?;
// List worktrees
gwtr::list_worktrees(&repo)?;
}
Some(Commands::Remove { name }) => {
// Validate git repository
let current_dir = env::current_dir()?;
let repo = gwtr::ensure_git_repository(¤t_dir)?;
// Remove worktree
gwtr::remove_worktree(&repo, name)?;
}
Some(Commands::Status) => {
// Validate git repository
let current_dir = env::current_dir()?;
let repo = gwtr::ensure_git_repository(¤t_dir)?;
// Show worktrees status
gwtr::show_worktrees_status(&repo)?;
}
Some(Commands::Pull { all, name }) => {
// Validate git repository
let current_dir = env::current_dir()?;
let repo = gwtr::ensure_git_repository(¤t_dir)?;
// Pull worktrees
if *all {
gwtr::pull_all_worktrees(&repo)?;
} else if let Some(worktree_name) = name {
gwtr::pull_worktree(&repo, worktree_name)?;
} else {
// Pull current worktree
gwtr::pull_current_worktree(&repo)?;
}
}
Some(Commands::Prune { dry_run, force }) => {
// Validate git repository
let current_dir = env::current_dir()?;
let repo = gwtr::ensure_git_repository(¤t_dir)?;
// Prune merged worktrees
gwtr::prune_merged_worktrees(&repo, *dry_run, *force)?;
}
Some(Commands::Note { name, text, ref_ }) => {
let current_dir = env::current_dir()?;
let repo = gwtr::ensure_git_repository(¤t_dir)?;
gwtr::manage_note(&repo, name, text.as_deref(), ref_.as_deref())?;
}
Some(Commands::Context { name, save }) => {
let current_dir = env::current_dir()?;
let repo = gwtr::ensure_git_repository(¤t_dir)?;
gwtr::show_context(&repo, name.as_deref(), *save)?;
}
Some(Commands::Audit { typo_only, all }) => {
let current_dir = env::current_dir()?;
gwtr::run_audit(¤t_dir, *typo_only, *all)?;
}
Some(Commands::Completions { shell }) => {
let mut cmd = Cli::command();
let bin_name = cmd.get_name().to_string();
generate(*shell, &mut cmd, bin_name, &mut io::stdout());
}
None => {
// This shouldn't happen with arg_required_else_help
}
}
Ok(())
}