Skip to main content

git-worktree

This skill should be used when managing Git worktrees for isolated parallel development. It handles creating, listing, switching, and cleaning up worktrees with a simple interactive interface.

Source facts

Repository
jikig-ai/soleur
Last source activity
September 30, 2026 at 18:01
Detected SKILL.md language
English
Stars
16
Forks
5

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

File Explorer
9 files

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
git-worktree
description
This skill should be used when managing Git worktrees for isolated parallel development. It handles creating, listing, switching, and cleaning up worktrees with a simple interactive interface.
<!-- grok-harness-invoke:start --> **Grok Build (`plugins/soleur/lib/harness.ts` `invokeSkill()`):** Read this SKILL.md in this process and run it to completion. A one-segment `soleur:<name>` in this document names a SKILL — on Grok Build, Read `plugins/soleur/skills/<name>/SKILL.md` in this process; it is not a nested tool_use. A multi-segment id such as `soleur:<domain>:<name>` names an AGENT: spawn it, never Read it, and on Grok Build spawn_subagent takes the id with its colons replaced by hyphens (`agentIdToGrokSubagentType`). **Claude Code:** Skill tool for a skill (`soleur:<name>`), Task tool with `subagent_type` for an agent. Forbidden is executing a subset, not the Read. <!-- grok-harness-invoke:end --> # Git Worktree Manager This skill provides a unified interface for managing Git worktrees across your development workflow. Whether you're reviewing PRs in isolation or working on features in parallel, this skill handles all the complexity. ## What This Skill Does - **Create worktrees** from main branch with clear branch names - **List worktrees** with current status - **Switch between worktrees** for parallel work - **Clean up completed worktrees** automatically - **Interactive confirmations** at each step - **Automatic .gitignore management** for worktree directory - **Automatic .env file copying** from main repo to new worktrees - **Write guard enforcement** via PreToolUse hook (`.claude/hooks/worktree-write-guard.sh`) -- blocks Write/Edit to main checkout when worktrees exist ## CRITICAL: Always Use the Manager Script **NEVER call `git worktree add` directly.** Always use the `worktree-manager.sh` script. The script handles critical setup that raw git commands don't: 1. Copies `.env`, `.env.local`, `.env.test`, etc. from main repo 2. Ensures `.worktrees` is in `.gitignore` 3. Creates consistent directory structure 4. Detects bare repos (`core.bare = true`) and derives `GIT_ROOT` via `--absolute-git-dir` instead of `--show-toplevel` 5. Sources the shared `plugins/soleur/scripts/resolve-git-root.sh` helper -- all scripts that need `GIT_ROOT` should source this helper instead of inlining their own detection logic **After creating a worktree**, run `npm install` if the project has a `package.json` — worktrees do not share `node_modules/` with the main working tree, and build commands (`npx @11ty/eleventy`, etc.) will silently hang instead of erroring. ```bash # ✅ CORRECT - Always use the script bash "${CLAUDE_PLUGIN_ROOT}/skills/git-worktree/scripts/worktree-manager.sh" create feature-name # ❌ WRONG - Never do this directly git worktree add .worktrees/feature-name -b feature-name main ``` ## When to Use This Skill Use this skill in these scenarios: 1. **Code Review (`soleur:review`)**: If NOT already on the target branch (PR branch or requested branch), offer worktree for isolated review 2. **Feature Work (`soleur:work`)**: Always ask if user wants parallel worktree or live branch work 3. **Parallel Development**: When working on multiple features simultaneously 4. **Cleanup**: After completing work in a worktree ## How to Use ### In Claude Code Workflows The skill is automatically called from the `soleur:review` and `soleur:work` skills: ``` # For review: offers worktree if not on PR branch # For work: always asks - new branch or worktree? ``` ### Manual Usage You can also invoke the skill directly from bash: ```bash # Create a new worktree (copies .env files automatically) bash "${CLAUDE_PLUGIN_ROOT}/skills/git-worktree/scripts/worktree-manager.sh" create feature-login # List all worktrees bash "${CLAUDE_PLUGIN_ROOT}/skills/git-worktree/scripts/worktree-manager.sh" list # Switch to a worktree bash "${CLAUDE_PLUGIN_ROOT}/skills/git-worktree/scripts/worktree-manager.sh" switch feature-login # Copy .env files to an existing worktree (if they weren't copied) bash "${CLAUDE_PLUGIN_ROOT}/skills/git-worktree/scripts/worktree-manager.sh" copy-env feature-login # Clean up completed worktrees bash "${CLAUDE_PLUGIN_ROOT}/skills/git-worktree/scripts/worktree-manager.sh" cleanup ``` ## Commands ### `create <branch-name> [from-branch]` Creates a new worktree with the given branch name. **Options:** - `branch-name` (required): The name for the new branch. The worktree DIRECTORY is the slug of it — every `/` becomes `-`, so `ci/rule-metrics` creates branch `ci/rule-metrics` in `.worktrees/ci-rule-metrics`. Identical for any name without a slash. Read the path the script prints; do not construct `.worktrees/<branch-name>` yourself. `create` refuses if the target directory already holds a different branch (`SOLEUR_WORKTREE_SLUG_COLLISION`) — the transform is many-to-one, so `ci/foo` and `ci-foo` would otherwise share one directory. - `from-branch` (optional): Base branch to create from (defaults to `main`) **Example:** ```bash bash "${CLAUDE_PLUGIN_ROOT}/skills/git-worktree/scripts/worktree-manager.sh" create feature-login ``` **What happens:** 1. Checks if worktree already exists 2. Fetches `refs/remotes/origin/<from-branch>` (the local `<from-branch>` ref is NOT touched — this lets `create` succeed even when a sibling worktree has `<from-branch>` checked out; see #3741) 3. Creates the new worktree from `origin/<from-branch>` with `--no-track` (preserves the pre-fix upstream-unset state so downstream `git push -u origin <branch>` flows are unchanged) 4. **Copies all .env files from main repo** (.env, .env.local, .env.test, etc.) 5. Shows path for cd-ing to the worktree **Opt-in: also update local `<from-branch>`** Pass `--update-local-main` (as a global flag, before `create`) to additionally fast-forward the local `<from-branch>` ref. Default behavior leaves the local ref untouched. ```bash bash "${CLAUDE_PLUGIN_ROOT}/skills/git-worktree/scripts/worktree-manager.sh" --update-local-main create feature-login ``` **Dependency install (bounded, skippable):** After copying `.env` files, `create`/`feature` install dependencies at the root and for each `apps/*/` package (bun/npm/yarn by lockfile). Two guards keep this from hanging a pipeline on a restricted host: - **Opt-out:** `--no-install` (global flag) or `SOLEUR_WORKTREE_SKIP_INSTALL=1` skips every install arm and prints `SOLEUR_WORKTREE_INSTALL_SKIPPED reason=opt-out`. The worktree is still created and usable; run installs inside it later. - **Reachability preflight + timeout:** each arm probes its resolved registry endpoint (`curl`, bounded, on the scheme the registry actually uses — `http://` private registries included) and skips fast with `SOLEUR_WORKTREE_INSTALL_SKIPPED reason=registry-unreachable host=<host> endpoint=<endpoint>` when unreachable; a stalled install is killed after `SOLEUR_WORKTREE_INSTALL_TIMEOUT_SECS` (default 300) and reported as `reason=timeout`. Probe bounds: `SOLEUR_WORKTREE_REGISTRY_PROBE_SECS` (connect, default 5) / `SOLEUR_WORKTREE_REGISTRY_PROBE_MAX_SECS` (total, default 8). - **Other non-success arms also mark stdout:** `reason=failed rc=<n>` (ordinary nonzero install), `reason=tool-missing runtime=<rt>` (lockfile present but package manager absent), `reason=no-lockfile` (package.json with no recognized lockfile). When the host lacks `timeout`/`gtimeout`, installs run unbounded and `SOLEUR_WORKTREE_INSTALL_UNBOUNDED` is printed once. All skips and failures warn-and-continue: worktree creation exits 0 and the worktree stays on disk. ### `list` or `ls` Lists all available worktrees with their branches and current status. **Example:** ```bash bash "${CLAUDE_PLUGIN_ROOT}/skills/git-worktree/scripts/worktree-manager.sh" list ``` **Output shows:** - Worktree name - Branch name - Which is current (marked with ✓) - Main repo status ### `switch <name>` or `go <name>` Switches to an existing worktree and cd's into it. **Example:** ```bash bash "${CLAUDE_PLUGIN_ROOT}/skills/git-worktree/scripts/worktree-manager.sh" switch feature-login ``` **Optional:** - If name not provided, lists available worktrees and prompts for selection ### `cleanup` or `clean` Interactively cleans up inactive worktrees with confirmation. **Example:** ```bash bash "${CLAUDE_PLUGIN_ROOT}/skills/git-worktree/scripts/worktree-manager.sh" cleanup ``` **What happens:** 1. Lists all inactive worktrees 2. Asks for confirmation 3. Removes selected worktrees 4. Cleans up empty directories ### `sync-bare-files` or `sync` Syncs stale on-disk files from git HEAD in a bare repo. Only needed when the repo uses `core.bare=true` — on-disk files at the bare root become stale after merges since git never updates them. Auto-called after `cleanup-merged` cleans branches in bare repo context. **Example:** ```bash bash "${CLAUDE_PLUGIN_ROOT}/skills/git-worktree/scripts/worktree-manager.sh" sync-bare-files ``` **What it syncs: EVERY tracked file at HEAD — there is no allowlist.** The implementation builds a throwaway index from `HEAD` and runs `git checkout-index -a -f` against the bare root, so every path git tracks is materialized. That pass is **additive** — it never removes anything — so a separate prune step afterwards deletes on-disk leftovers for paths that were tracked once and are absent from HEAD. The two together are what make the bare root equal HEAD exactly. It then re-applies execute bits for the scripts and hooks the plugin loader and SessionStart hooks exec. > **Corrected 2026-08-10 (#7409).** This section previously enumerated a > seven-entry list (`AGENTS.md`, `plugins/soleur/hooks/*`, `.claude/hooks/*.sh`, > `resolve-git-root.sh`, …) and told readers that "any file Claude Code executes > at runtime from the bare repo root **must be added to the sync list**". Both > halves were wrong: the enumeration described a whitelist the code has not > had, and the instruction sent readers to register files in a list that does > not exist. Worth stating precisely rather than deleting, because the > difference is load-bearing — when #7409 moved the session-state lock/lease > library to `plugins/soleur/scripts/lib/`, a genuine whitelist would have left > the bare root's repointed `.claude/hooks/*` sources pointing at a file the > mirror never copied, and they degrade **silently** (`|| true`). The full > mirror is what makes that a non-event. **Still important:** run `sync-bare-files` after any merge that moves or adds a file executed from the bare root. The mirror is complete, but it is not automatic — on-disk files at a `core.bare=true` root are never updated by git itself, so a stale copy survives until this runs. ## Workflow Examples ### Code Review with Worktree ```bash # Claude Code recognizes you're not on the PR branch # Offers: "Use worktree for isolated review? (y/n)" # You respond: yes # Script runs (copies .env files automatically): bash "${CLAUDE_PLUGIN_ROOT}/skills/git-worktree/scripts/worktree-manager.sh" create pr-123-feature-name # You're now in isolated worktree for review with all env vars cd .worktrees/pr-123-feature-name # After review, return to main: cd ../.. bash "${CLAUDE_PLUGIN_ROOT}/skills/git-worktree/scripts/worktree-manager.sh" cleanup ``` ### Parallel Feature Development ```bash # For first feature (copies .env files): bash "${CLAUDE_PLUGIN_ROOT}/skills/git-worktree/scripts/worktree-manager.sh" create feature-login # Later, start second feature (also copies .env files): bash "${CLAUDE_PLUGIN_ROOT}/skills/git-worktree/scripts/worktree-manager.sh" create feature-notifications # List what you have: bash "${CLAUDE_PLUGIN_ROOT}/skills/git-worktree/scripts/worktree-manager.sh" list # Switch between them as needed: bash "${CLAUDE_PLUGIN_ROOT}/skills/git-worktree/scripts/worktree-manager.sh" switch feature-login # Return to main and cleanup when done: cd . bash "${CLAUDE_PLUGIN_ROOT}/skills/git-worktree/scripts/worktree-manager.sh" cleanup ``` ## Key Design Principles ### KISS (Keep It Simple, Stupid) - **One manager script** handles all worktree operations - **Simple commands** with sensible defaults - **Interactive prompts** prevent accidental operations - **Clear naming** using branch names directly ### Opinionated Defaults - Worktrees always created from **main** (unless specified) - Worktrees stored in **.worktrees/** directory - Branch name becomes worktree name - **.gitignore** automatically managed ### Safety First - **Confirms before creating** worktrees - **Confirms before cleanup** to prevent accidental removal - **Won't remove current worktree** - **Clear error messages** for issues ## Integration with Workflows ### `soleur:review` Instead of always creating a worktree: ``` 1. Check current branch 2. If ALREADY on target branch (PR branch or requested branch) → stay there, no worktree needed 3. If DIFFERENT branch than the review target → offer worktree: "Use worktree for isolated review? (y/n)" - yes → call git-worktree skill - no → proceed with PR diff on current branch ``` ### `soleur:work` Always offer choice: ``` 1. Ask: "How do you want to work? 1. New branch on current worktree (live work) 2. Worktree (parallel work)" 2. If choice 1 → create new branch normally 3. If choice 2 → call git-worktree skill to create from main ``` ## Troubleshooting ### "Worktree already exists" If you see this, the script will ask if you want to switch to it instead. ### "Cannot remove worktree: it is the current worktree" Switch out of the worktree first (to main repo), then cleanup: Navigate to the repository root directory, then run: ```bash bash "${CLAUDE_PLUGIN_ROOT}/skills/git-worktree/scripts/worktree-manager.sh" cleanup ``` ### Lost in a worktree? See where you are: ```bash bash "${CLAUDE_PLUGIN_ROOT}/skills/git-worktree/scripts/worktree-manager.sh" list ``` ### .env files missing in worktree? If a worktree was created without .env files (e.g., via raw `git worktree add`), copy them: ```bash bash "${CLAUDE_PLUGIN_ROOT}/skills/git-worktree/scripts/worktree-manager.sh" copy-env feature-name ``` Navigate back to the repository root directory. ### node_modules missing in worktree? A worktree created without an install step (raw `git worktree add`, a harness-created agent worktree, or a `create` whose dep install warned and continued) carries no `node_modules`. Since #8580 the pre-commit lint hook resolves its pinned binary from a sibling checkout's `node_modules` automatically — accepted only when the sibling's package manifests report CLI+engine versions equal to this checkout's pins (verified by file reads, so an unchecked binary never runs) — so docs commits work without any install. Everything else (vitest, tsx-driven suites, `npm run` scripts) still needs the real install inside the worktree — run both from the worktree root: ```bash npm ci --ignore-scripts npm ci --ignore-scripts --prefix apps/web-platform # needed by the webplat arm of scripts/test-all.sh
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub