skills/git-worktree/SKILL.md
Manage Git worktrees for isolated parallel development. Use when creating, listing, switching, or cleaning up git worktrees, or when needing isolated branches for concurrent reviews or feature work.
npx skillsauth add iliaal/ai-skills git-worktreeInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Never call git worktree add directly -- always use the worktree-manager.sh script.
The script handles critical setup that raw git commands don't:
.env, .env.local, .env.test, etc. from main repo.worktrees is in .gitignorepackage.json → npm install, composer.json → composer install, pyproject.toml → pip install -e ., go.mod → go mod downloadBefore creating a worktree, verify the worktree directory is gitignored:
# Verify .worktrees is ignored (should output ".worktrees")
git check-ignore .worktrees || echo "WARNING: .worktrees not in .gitignore"
If not ignored, add it to .gitignore before proceeding. The manager script handles this, but verify when troubleshooting.
This subsection documents the safety logic worktree-manager.sh create implements when branching from the default branch — it is not a manual sequence to run. The script handles the steps; the rationale lives here so users can reason about the behavior.
When creating a worktree's branch from the default branch (main/master), the local base may be ahead of origin/<base> due to another session, worktree, or background task. Branching from local HEAD silently carries those unrelated commits into the new feature branch and the eventual PR — and an unconditional git reset --hard origin/<base> would silently drop the user's intentional unpushed work.
The script's safe sequence:
git fetch --no-tags origin <base>
unpushed=$(git log "origin/$base..$base" --oneline)
if [ -n "$unpushed" ]; then
# Surface the commit list and prompt: carry forward (base_ref=$base) or leave on local <base> (base_ref=origin/$base)
...
fi
git worktree add .worktrees/<name> -b <branch> "$base_ref"
Two failure modes the prompt distinguishes:
<base> past origin/<base> with unrelated commits. The user wants origin/<base> as the new branch's base; the unpushed commits stay on local <base> for separate handling.<base> intending them for a feature branch. The user wants HEAD as the new branch's base so the commits carry forward.Local git state alone cannot distinguish these (author email is unreliable in multi-session setups), so the script surfaces the choice rather than guessing. Default fallback when the user can't be reached: branch from origin/<base> and report the unpushed commits in the change summary so the user resolves them deliberately. If the script does not implement this yet, that is a known gap — open an issue rather than working around with raw git worktree add.
After creating a worktree, run the project's test suite to establish a clean baseline. Pre-existing failures in the worktree should be caught before starting new work -- not discovered mid-implementation.
# CORRECT - Always use the script
bash ${CLAUDE_PLUGIN_ROOT}/skills/ia-git-worktree/scripts/worktree-manager.sh create feature-name
# WRONG - Never do this directly
git worktree add .worktrees/feature-name -b feature-name main
| Command | Description | Example |
|---------|-------------|---------|
| create <branch> [from] | Create worktree + branch (default: from main) | ...worktree-manager.sh create feature-login |
| list / ls | List all worktrees with status | ...worktree-manager.sh list |
| switch <name> / go | Switch to existing worktree | ...worktree-manager.sh switch feature-login |
| copy-env <name> | Copy .env files to existing worktree | ...worktree-manager.sh copy-env feature-login |
| cleanup / clean | Interactively remove inactive worktrees | ...worktree-manager.sh cleanup |
After cleanup, run git worktree prune to remove any orphaned worktree metadata from manually deleted directories.
All commands use: bash ${CLAUDE_PLUGIN_ROOT}/skills/ia-git-worktree/scripts/worktree-manager.sh <command>
Before creating worktrees, detect the execution context:
git rev-parse --show-toplevel against git worktree list. If the current directory is already a linked worktree, skip creation -- work directly in the existing worktree.$CODEX_SANDBOX is set or the repo is at a non-standard path (e.g., /tmp/, /workspace/), worktrees may not be supported. Fall back to regular branch switching.git rev-parse --is-bare-repository returns true, worktrees are the only way to have a working directory. Adjust paths accordingly.Adapt the workflow to the detected context rather than failing with a generic error.
/ia-review/ia-workAlways offer choice:
When work in a worktree is done, verify tests pass, then present exactly 4 options:
gh pr create, keep worktree until mergedClean up the worktree directory only for options 1 and 4. For option 2, the worktree stays until the PR merges.
When completing work in a worktree (before merge or PR), output a structured summary:
CHANGES MADE:
- src/routes/tasks.ts: Added validation middleware
THINGS I DIDN'T TOUCH (intentionally):
- src/routes/auth.ts: Has similar validation gap but out of scope
POTENTIAL CONCERNS:
- The Zod schema is strict -- rejects extra fields. Confirm this is desired.
The "DIDN'T TOUCH" section prevents reviewers from wondering whether adjacent issues were missed or intentionally deferred.
Installing hooks into .git/hooks/ silently fails on any repo that uses Husky. Husky sets core.hooksPath (typically to .husky/_) and git ignores .git/hooks/ entirely when that config is non-empty. The hook file lands on disk, is executable, is correct, and is dead. Invisible failure until someone asks why the post-merge behavior isn't running.
hooks_path=$(git -C "$repo" config --get core.hooksPath)
$(git rev-parse --git-common-dir)/hooks/<name> as usual..husky/_ (or any path containing husky.sh / h trampoline): Husky v9 setup. Write to .husky/<name>; do NOT include the v8-era . "$(dirname "$0")/_/husky.sh" line (v9 prints a deprecation warning if you do)..git/hooks/ lives in the common git dir and runs for every worktree of the clone. A hook installed once fires across all trees. Two rules to stay safe:
git rev-parse --show-toplevel, not a hardcoded path. Hardcoding means the last install-hooks invocation wins for every worktree.#!/bin/sh
root="$(git rev-parse --show-toplevel 2>/dev/null)" || exit 0
[ -d "$root/.my-tool" ] || exit 0
( cd "$root" && my-tool index ) >>"$root/.my-tool/hook.log" 2>&1 &
exit 0
Redirect to a log file inside the tool's state dir, not /dev/null — silent failures produce stale state you only notice hours later.
Tooling artifacts (local index dirs, hook helpers, per-developer scratch files) belong in .git/info/exclude, NOT in the tracked .gitignore.
.gitignore is content — tracked, shared with the team, reviewed in PRs. Adding a personal tooling rule there pollutes a shared file. On foreign repos (upstream projects, third-party clones) the rule either rides into a PR by accident or sits as a dirty working tree forever..git/info/exclude is local — untracked, lives in the common git dir, shared across every worktree of the clone. Same syntax and semantics as .gitignore without the leakage.Don't hardcode $repo/.git/info/exclude. In a worktree, .git is a file (gitlink), not a directory. Use git itself:
exclude=$(git -C "$repo" rev-parse --git-path info/exclude)
line="/.my-tool/"
mkdir -p "$(dirname "$exclude")"
grep -qxF "$line" "$exclude" 2>/dev/null || printf '\n# my-tool\n%s\n' "$line" >> "$exclude"
grep -qxF matches the exact line with no regex surprises.
Only when the artifact is genuinely team-shared and belongs in the repo (build outputs used in CI, generated files, vendored dependencies). If in doubt, ask: "would another contributor benefit from this rule?" If no, exclude locally.
git worktree list shows the new entry.worktrees directory confirmed in .gitignoredevelopment
Generic test writing discipline: test quality, real assertions, anti-patterns, and rationalization resistance. Use when writing tests, adding test coverage, or fixing failing tests for any language or framework. Complements language-specific skills.
testing
Enforces fresh verification evidence before any completion claim. Use when about to claim "tests pass", "bug fixed", "done", "ready to merge", or handing off work.
tools
Tailwind CSS v4 patterns: CSS-first config, utility classes, component variants, v3 migration. Use when styling with Tailwind, configuring @theme tokens, using tailwind-variants/CVA, migrating v3 to v4, or fixing Tailwind styles and dark mode.
development
Simplifies, polishes, and declutters code without changing behavior. Use when asked to simplify, clean up, refactor, declutter, remove dead code or AI slop, or improve readability. For analysis-only reports without code changes, use code-simplicity-reviewer agent.