Skip to main content

git_worktree_manager/operations/
setup_claude.rs

1/// Claude Code skill installation for worktree task delegation.
2use std::path::PathBuf;
3
4use console::style;
5
6use crate::constants::home_dir_or_fallback;
7use crate::error::Result;
8
9const SKILL_DIR: &str = "gw";
10const LEGACY_SKILL_DIR: &str = "gw-delegate";
11const SKILL_FILE: &str = "SKILL.md";
12const REFERENCE_FILE: &str = "gw-commands.md";
13
14/// Get the skill base directory.
15fn skill_dir() -> PathBuf {
16    home_dir_or_fallback()
17        .join(".claude")
18        .join("skills")
19        .join(SKILL_DIR)
20}
21
22/// Get the skill installation path.
23pub fn skill_path() -> PathBuf {
24    skill_dir().join(SKILL_FILE)
25}
26
27/// Get the reference file path.
28fn reference_path() -> PathBuf {
29    skill_dir().join("references").join(REFERENCE_FILE)
30}
31
32/// Check if the Claude Code skill is already installed.
33pub fn is_skill_installed() -> bool {
34    skill_path().exists()
35}
36
37/// Write a file if its content differs from the new content.
38/// Returns true if the file was written (created or updated).
39fn write_if_changed(
40    path: &PathBuf,
41    new_content: &str,
42) -> std::result::Result<bool, std::io::Error> {
43    if path.exists() {
44        let existing = std::fs::read_to_string(path).unwrap_or_default();
45        if existing == new_content {
46            return Ok(false);
47        }
48    }
49    if let Some(parent) = path.parent() {
50        std::fs::create_dir_all(parent)?;
51    }
52    std::fs::write(path, new_content)?;
53    Ok(true)
54}
55
56/// Remove the legacy gw-delegate skill directory if it exists.
57fn remove_legacy_skill() {
58    let legacy_dir = home_dir_or_fallback()
59        .join(".claude")
60        .join("skills")
61        .join(LEGACY_SKILL_DIR);
62    if legacy_dir.exists() {
63        let _ = std::fs::remove_dir_all(&legacy_dir);
64    }
65}
66
67/// Install or update the Claude Code skill for worktree task delegation.
68pub fn setup_claude() -> Result<()> {
69    remove_legacy_skill();
70
71    let skill = skill_path();
72    let reference = reference_path();
73
74    let skill_changed = write_if_changed(&skill, skill_content())?;
75    let ref_changed = write_if_changed(&reference, reference_content())?;
76
77    if !skill_changed && !ref_changed {
78        println!(
79            "{} Claude Code skill is already up to date.\n",
80            style("*").green()
81        );
82        println!("  Location: {}", style(skill_dir().display()).dim());
83        return Ok(());
84    }
85
86    let action = if !skill_changed && ref_changed {
87        "updated"
88    } else if skill.exists() && skill_changed {
89        // File existed before we wrote (we just overwrote it)
90        "updated"
91    } else {
92        "installed"
93    };
94
95    println!(
96        "{} Claude Code skill {} successfully!\n",
97        style("*").green().bold(),
98        action
99    );
100    println!("  Location: {}", style(skill_dir().display()).dim());
101    println!(
102        "  Use {} in Claude Code to delegate tasks to worktrees.",
103        style("/gw").cyan()
104    );
105    println!(
106        "  Or just ask Claude about {} — it will use gw automatically.\n",
107        style("worktree management").cyan()
108    );
109
110    Ok(())
111}
112
113fn skill_content() -> &'static str {
114    r#"---
115name: gw
116description: "Delegate coding tasks to isolated git worktrees. Invoke with: /gw <natural language task description>. Also handles worktree management: list, sync, clean, PR, merge, etc."
117allowed-tools: Bash
118---
119
120# git-worktree-manager (gw)
121
122CLI tool integrating git worktree with AI coding assistants. Single binary, ~3ms startup.
123
124## Natural Language Task Delegation
125
126When the user invokes `/gw <task description>` (e.g., `/gw fix the auth token expiration bug`), follow these steps:
127
128### Step 1: Parse the user's intent
129From the natural language input, determine:
130- **Task description** — the full task text (will be passed via `--prompt-file` in Step 2)
131- **Branch name** — generate a short, descriptive branch name from the task (e.g., `fix-auth-token-expiration`). Use conventional prefixes: `fix-`, `feat-`, `refactor-`, `docs-`, `test-`, `chore-`.
132- **Base branch** — use the default unless the user specifies otherwise
133
134### Step 2: Confirm and execute
135
136All three prompt ingestion modes (`--prompt`, `--prompt-file`, `--prompt-stdin`) are equally safe from shell-escaping issues. Use `--prompt-file` for convenience when managing multi-line prompts in an editor or passing skill-generated files.
137
138**Recommended (use this by default):**
139```bash
140# Write the full prompt to a temp file, then pass the path.
141# `trap` ensures the file is cleaned up even if `gw new` fails.
142trap 'rm -f /tmp/gw-prompt-$$.txt' EXIT
143cat > /tmp/gw-prompt-$$.txt <<'PROMPT'
144<task description — multi-line OK, quotes OK, no escaping needed>
145PROMPT
146gw new <branch-name> -T <terminal-method> --prompt-file /tmp/gw-prompt-$$.txt
147```
148
149**Short one-liner alternative:**
150```bash
151gw new <branch-name> -T <terminal-method> --prompt "<short task>"
152```
153
154**Piping from another command:**
155```bash
156generate-spec | gw new <branch-name> --prompt-stdin
157```
158
159Note: `--prompt-stdin` consumes the process's stdin. Avoid combining it with
160`-T <terminal-method>` — the spawned terminal may inherit a closed stdin and
161behave unpredictably. Use `--prompt-file` if you need to specify a terminal launcher alongside the prompt.
162
163Only one of `--prompt`, `--prompt-file`, `--prompt-stdin` may be given per invocation.
164
165### Branch name rules
166- Lowercase, hyphen-separated, max ~50 chars
167- Strip filler words (the, a, an, for, in, on, etc.)
168- Examples:
169  - "Fix the JWT token expiration check in auth" → `fix-jwt-token-expiration`
170  - "Add user avatar upload feature" → `feat-avatar-upload`
171  - "Refactor the database connection pool" → `refactor-db-connection-pool`
172
173### Terminal method selection
174- **Default: omit the `-T` flag** to use the system default (`gw config get launch.method`). Only add `-T` if the user explicitly requests a specific terminal method.
175- If the user explicitly asks for a specific method, use it. Common methods: `w-t` (WezTerm tab), `w-t-b` (WezTerm tab, background — no focus steal), `i-t` (iTerm2 tab), `t` (tmux session), `d` (detached/background)
176- Once the user specifies a method, remember it for subsequent calls in the same session.
177
178## Quick Reference
179
180| Command | Description |
181|---------|-------------|
182| `gw new <branch> [--prompt-file <path> \| --prompt "..." \| --prompt-stdin]` | Create worktree + optionally launch AI with task |
183| `gw delete <branch>` | Delete worktree and branch |
184| `gw list` | List all worktrees with status |
185| `gw status` | Show current worktree info |
186| `gw resume [branch]` | Resume AI session in worktree |
187| `gw pr [branch]` | Create GitHub Pull Request |
188| `gw merge [branch]` | Merge branch into base |
189| `gw sync [--all]` | Rebase worktree(s) onto base branch |
190| `gw clean [--merged]` | Batch cleanup of worktrees |
191| `gw diff <b1> <b2>` | Compare two branches |
192| `gw change-base <new> [branch]` | Change base branch |
193| `gw config <action>` | Configuration management |
194| `gw doctor` | Run diagnostics |
195| `gw tree` / `gw stats` | Visual hierarchy / statistics |
196| `gw backup <action>` | Backup and restore worktrees |
197| `gw stash <action>` | Worktree-aware stash management |
198| `gw shell [worktree]` | Open shell in worktree |
199
200## Delegate a task to a new worktree
201
202Three ways to supply the initial prompt (mutually exclusive):
203
204| Flag | When to use |
205|------|-------------|
206| `--prompt-file <path>` ⭐ | Convenient for multi-line prompts, editor-managed content, or skill-generated files. |
207| `--prompt "<text>"` | Short single-line prompts only. |
208| `--prompt-stdin` | Piping from another command (`cmd \| gw new ... --prompt-stdin`). |
209
210Example (recommended):
211```bash
212trap 'rm -f /tmp/gw-prompt-$$.txt' EXIT
213cat > /tmp/gw-prompt-$$.txt <<'PROMPT'
214Fix JWT token expiration check in src/auth.rs.
215Make sure to cover the "leeway" edge case and add a unit test.
216PROMPT
217gw new fix-auth -T w-t --prompt-file /tmp/gw-prompt-$$.txt
218```
219
220Example (short form):
221```bash
222gw new fix-auth -T w-t --prompt "Fix JWT token expiration check"
223```
224
225### Terminal methods (use with -T flag)
226- `w-t` — WezTerm new tab
227- `w-t-b` — WezTerm new tab (background, no focus steal)
228- `w-w` — WezTerm new window
229- `i-t` — iTerm2 new tab
230- `i-w` — iTerm2 new window
231- `t` — tmux new session
232- `t-w` — tmux new window
233- `d` — detached (background, no terminal)
234
235Use the method matching the user's terminal. If unsure, ask.
236
237## Common Workflows
238
239### Feature development
240```bash
241trap 'rm -f /tmp/gw-prompt-$$.txt' EXIT
242cat > /tmp/gw-prompt-$$.txt <<'PROMPT'
243Implement feature X
244PROMPT
245# `-T` omitted — uses the default launcher from `gw config get launch.method`.
246gw new feature-x --prompt-file /tmp/gw-prompt-$$.txt
247# ... work is done in the new worktree ...
248gw pr feature-x                    # create PR
249gw delete feature-x                # cleanup after merge
250```
251
252### Keep worktrees in sync
253```bash
254gw sync --all                      # rebase all worktrees onto their base
255gw sync --all --ai-merge           # use AI to resolve conflicts
256```
257
258### Batch cleanup
259```bash
260gw clean --merged                  # delete worktrees for merged branches
261gw clean --older-than 30d --dry-run  # preview old worktree cleanup
262```
263
264### Global mode (across repos)
265```bash
266gw -g list                         # list worktrees across all repos
267gw -g scan --dir ~/projects        # discover repositories
268```
269
270## Guidelines
271
272- Before running any `gw` subcommand that reads the current worktree (`gw status`, `gw list`, `gw delete` without an explicit target, etc.), make sure the shell's cwd still exists. If another session or an earlier `gw delete`/`gw clean` removed the worktree, the shell holds a stale pwd and commands behave unexpectedly. Quick guard:
273  ```bash
274  [ -d "$(pwd -P 2>/dev/null)" ] || { echo "FATAL: cwd missing (worktree likely deleted). Abort."; exit 1; }
275  ```
276  `gw new` does not need this guard (it creates a fresh worktree elsewhere); the guard matters for in-worktree commands.
277- Use descriptive branch names: `fix-auth`, `feat-login-page`, `refactor-api`
278- Specify base branch if not main/master:
279  ```bash
280  trap 'rm -f /tmp/gw-prompt-$$.txt' EXIT
281  cat > /tmp/gw-prompt-$$.txt <<'PROMPT'
282  <task description>
283  PROMPT
284  gw new fix-auth --base develop -T w-t --prompt-file /tmp/gw-prompt-$$.txt
285  ```
286- One focused task per worktree
287- The delegated Claude Code instance works independently in its own worktree directory
288- You can delegate multiple tasks in parallel to different worktrees
289- **Fire-and-forget**: Once a worktree task is spawned, you CANNOT stop it, send follow-up messages, or interact with it. The initial prompt is the ONLY instruction the delegated instance receives. Therefore:
290  - Make the prompt comprehensive — include all requirements, constraints, and acceptance criteria upfront
291  - Use `--prompt-file` for complex or skill-generated prompts to manage them conveniently
292  - If the user's request is vague or ambiguous, ask clarifying questions BEFORE spawning
293  - Do NOT spawn a task assuming you can "correct course later" — you cannot
294
295## Full command reference
296
297For detailed flags and options for all commands, see [gw-commands.md](references/gw-commands.md).
298"#
299}
300
301fn reference_content() -> &'static str {
302    r#"# gw Command Reference
303
304Complete reference for all gw (git-worktree-manager) commands.
305
306## Core Worktree Management
307
308### `gw new <branch> [OPTIONS]`
309Create new worktree for feature branch.
310- `-p, --path <PATH>` — Custom worktree path (default: `../<repo>-<branch>`)
311- `-b, --base <BASE>` — Base branch to create from (default: from config or auto-detect)
312- `--no-term` — Skip AI tool launch
313- `-T, --term <METHOD>` — Terminal launch method. Accepts canonical name (e.g., `tmux`, `wezterm-tab`) or alias (e.g., `t`, `w-t`). Supports `method:session-name` for tmux/zellij (e.g., `tmux:mywork`). See Terminal Launch Methods section below.
314- `--bg` — Launch AI tool in background
315- `--prompt <PROMPT>` — Initial prompt as a CLI string (single-line, best for short prompts)
316- `--prompt-file <PATH>` — Read initial prompt from a file (recommended for multi-line / quoted content)
317- `--prompt-stdin` — Read initial prompt from standard input (for piping). Avoid combining with `-T <terminal>` — the spawned terminal may inherit a closed stdin.
318
319Only one of `--prompt`, `--prompt-file`, `--prompt-stdin` may be used per invocation.
320
321### `gw delete [target] [OPTIONS]`
322Delete a worktree.
323- `-k, --keep-branch` — Keep the branch (only remove worktree directory)
324- `-r, --delete-remote` — Also delete the remote branch
325- `--no-force` — Don't use --force flag
326- `-w, --worktree` — Resolve target as worktree directory name
327- `-b, --branch` — Resolve target as branch name
328
329### `gw list`
330List all worktrees with status indicators (active, clean, modified, stale). Alias: `gw ls`.
331
332### `gw status`
333Show detailed info about the current worktree.
334
335### `gw resume [branch] [OPTIONS]`
336Resume AI work in a worktree. Auto-detects existing Claude sessions and uses `--continue`.
337- `-T, --term <METHOD>` — Terminal launch method (same format as `gw new`)
338- `--bg` — Launch AI tool in background
339- `-w, --worktree` — Resolve as worktree name
340- `-b, --by-branch` — Resolve as branch name
341
342### `gw shell [worktree] [COMMAND...]`
343Open interactive shell in a worktree, or execute a command.
344```bash
345gw shell feature-x           # interactive shell
346gw shell feature-x npm test  # run command
347```
348
349## Git Workflow
350
351### `gw pr [branch] [OPTIONS]`
352Create GitHub Pull Request from worktree.
353- `-t, --title <TITLE>` — PR title
354- `-B, --body <BODY>` — PR body
355- `-d, --draft` — Create as draft PR
356- `--no-push` — Skip pushing to remote
357- `-w, --worktree` / `-b, --by-branch` — Target resolution
358
359### `gw merge [branch] [OPTIONS]`
360Merge feature branch into base branch.
361- `-i, --interactive` — Interactive rebase
362- `--dry-run` — Show what would happen
363- `--push` — Push to remote after merge
364- `--ai-merge` — Use AI to resolve merge conflicts
365- `-w, --worktree` — Resolve as worktree name
366
367### `gw sync [branch] [OPTIONS]`
368Sync worktree with base branch (rebase).
369- `--all` — Sync all worktrees
370- `--fetch-only` — Only fetch without rebasing
371- `--ai-merge` — Use AI to resolve conflicts
372- `-w, --worktree` / `-b, --by-branch` — Target resolution
373
374### `gw change-base <new-base> [branch] [OPTIONS]`
375Change base branch for a worktree.
376- `--dry-run` — Show what would happen
377- `-i, --interactive` — Interactive rebase
378- `-w, --worktree` / `-b, --by-branch` — Target resolution
379
380### `gw diff <branch1> <branch2> [OPTIONS]`
381Compare two branches.
382- `-s, --summary` — Show statistics only
383- `-f, --files` — Show changed files only
384
385## Maintenance
386
387### `gw clean [OPTIONS]`
388Batch cleanup of worktrees.
389- `--merged` — Delete worktrees for branches already merged to base
390- `--older-than <DURATION>` — Delete worktrees older than duration (e.g., `7d`, `2w`, `1m`)
391- `-i, --interactive` — Interactive selection
392- `--dry-run` — Preview without deleting
393
394### `gw doctor`
395Run health check: git version, worktree accessibility, uncommitted changes, behind-base detection, merge conflicts, Claude Code integration.
396
397### `gw upgrade`
398Check for updates and install latest version from GitHub Releases.
399
400### `gw tree`
401Display worktree hierarchy as a visual tree.
402
403### `gw stats`
404Show worktree statistics (count, age, size).
405
406## Backup & Stash
407
408### `gw backup create [branch] [--all]`
409Create git bundle backup of worktree(s).
410
411### `gw backup list [branch] [--all]`
412List available backups.
413
414### `gw backup restore <branch> [--path <PATH>] [--id <ID>]`
415Restore worktree from backup.
416
417### `gw stash save [message]`
418Save changes to worktree-aware stash.
419
420### `gw stash list`
421List stashes organized by worktree/branch.
422
423### `gw stash apply <target-branch> [-s <stash-ref>]`
424Apply stash to a different worktree.
425
426## Configuration
427
428### `gw config show`
429Show current configuration.
430
431### `gw config list`
432List all configuration keys with descriptions.
433
434### `gw config get <KEY>`
435Get a config value. Keys use dot notation (see Key Config Keys section below).
436
437### `gw config set <KEY> <VALUE>`
438Set a config value. Key-specific valid values:
439- `ai_tool.command` — Preset name (`claude`, `claude-yolo`, `claude-remote`, `claude-yolo-remote`, `codex`, `codex-yolo`, `no-op`) or any command name
440- `launch.method` — Any terminal launch method name or alias (see Terminal Launch Methods)
441- `update.auto_check` — `true` or `false`
442
443### `gw config use-preset <NAME>`
444Use a predefined AI tool preset: `claude`, `claude-yolo`, `claude-remote`, `claude-yolo-remote`, `codex`, `codex-yolo`, `no-op`.
445
446### `gw config list-presets`
447List available presets.
448
449### `gw config reset`
450Reset configuration to defaults.
451
452## Hooks
453
454### `gw hook add <EVENT> <COMMAND> [--id <ID>] [-d <DESC>]`
455Add a lifecycle hook.
456
457### `gw hook remove <EVENT> <HOOK_ID>`
458Remove a hook.
459
460### `gw hook list [EVENT]`
461List hooks.
462
463### `gw hook enable/disable <EVENT> <HOOK_ID>`
464Toggle hook on/off.
465
466### `gw hook run <EVENT> [--dry-run]`
467Manually run hooks for an event.
468
469**Available events:** `worktree.pre_create`, `worktree.post_create`, `worktree.pre_delete`, `worktree.post_delete`, `merge.pre`, `merge.post`, `pr.pre`, `pr.post`, `resume.pre`, `resume.post`, `sync.pre`, `sync.post`
470
471## Export / Import
472
473### `gw export [-o <FILE>]`
474Export worktree configuration to JSON.
475
476### `gw import <FILE> [--apply]`
477Import configuration (preview by default, `--apply` to apply).
478
479## Global Mode
480
481Add `-g` or `--global` to any command to operate across all registered repositories.
482
483### `gw -g list`
484List worktrees across all registered repos.
485
486### `gw scan [--dir <DIR>]`
487Scan for and register git repositories.
488
489### `gw prune`
490Clean up stale registry entries.
491
492## Shell Integration
493
494### `gw shell-setup`
495Interactive setup for shell integration (gw-cd function).
496
497### `gw-cd [branch]`
498Shell function to navigate to worktree by branch name. Supports:
499- `gw-cd` — interactive selector
500- `gw-cd feature-x` — direct navigation
501- `gw-cd -g feature-x` — global (across repos)
502- `gw-cd repo:branch` — repo-scoped navigation
503
504## Terminal Launch Methods
505
506Used with `-T` flag on `gw new` and `gw resume`. Supports `method:session-name` for tmux/zellij (e.g., `tmux:mywork`, `z:task1`).
507
508| Method | Alias | Description |
509|--------|-------|-------------|
510| `foreground` | `fg` | Block in current terminal |
511| `detach` | `d` | Fully detached process |
512| `iterm-window` | `i-w` | iTerm2 new window |
513| `iterm-tab` | `i-t` | iTerm2 new tab |
514| `iterm-pane-h` | `i-p-h` | iTerm2 horizontal pane |
515| `iterm-pane-v` | `i-p-v` | iTerm2 vertical pane |
516| `tmux` | `t` | tmux new session |
517| `tmux-window` | `t-w` | tmux new window |
518| `tmux-pane-h` | `t-p-h` | tmux horizontal pane |
519| `tmux-pane-v` | `t-p-v` | tmux vertical pane |
520| `zellij` | `z` | Zellij new session |
521| `zellij-tab` | `z-t` | Zellij new tab |
522| `zellij-pane-h` | `z-p-h` | Zellij horizontal pane |
523| `zellij-pane-v` | `z-p-v` | Zellij vertical pane |
524| `wezterm-window` | `w-w` | WezTerm new window |
525| `wezterm-tab` | `w-t` | WezTerm new tab |
526| `wezterm-tab-bg` | `w-t-b` | WezTerm new tab (background, no focus steal) |
527| `wezterm-pane-h` | `w-p-h` | WezTerm horizontal pane |
528| `wezterm-pane-v` | `w-p-v` | WezTerm vertical pane |
529
530## Key Config Keys
531
532| Key | Description | Default |
533|-----|-------------|---------|
534| `ai_tool.command` | AI tool name or preset | `claude` |
535| `ai_tool.args` | Additional arguments | `[]` |
536| `launch.method` | Default terminal method | `foreground` |
537| `launch.tmux_session_prefix` | tmux session prefix | `gw` |
538| `launch.wezterm_ready_timeout` | WezTerm ready timeout (secs) | `5.0` |
539| `update.auto_check` | Auto-check for updates | `true` |
540
541## Helper Commands (for scripting and completion)
542
543These hidden commands output newline-separated values, useful for scripting:
544- `gw _config-keys` — List all config key names
545- `gw _term-values` — List all valid `--term` values (canonical + aliases)
546- `gw _preset-names` — List all AI tool preset names
547- `gw _hook-events` — List all valid hook event names
548- `gw _path --list-branches [-g]` — List worktree branch names
549"#
550}