Rig worktree
Manage isolated git worktrees: create one wired up for dev (fetch, branch from base, symlink env, install deps), list them with PR state, or safely remove merged ones. A shared bootstrap that implement-style flows can call, also usable directly. Triggers on: 'worktree', 'set up a worktree', 'new worktree', 'list worktrees', 'remove worktree', 'isolate this in a worktree', 'symlink env into worktree'.From its SKILL.md
npx -y skills add agent-rig/rig --skill rig-worktreeAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 2 stars2 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
7.7 KB, ~2.0k tokens by cl100k_base, as published. Nobody here has run it
Worktree — isolated dev checkout lifecycle
Create / list / remove isolated git worktrees under .claude/rig-worktrees/.
Creation produces a worktree that's actually ready to run: fetched from
origin, branched from the right base, env files symlinked, dependencies
installed. This is a shared bootstrap you can call directly (spikes,
ad-hoc branches, parallel experiments) and that implement-style flows and
prune/rig-tidy steps can delegate to.
The logic lives in two scripts so the invariants live in one place:
.claude/scripts/setup-worktree.sh (create) and
.claude/scripts/remove-worktree.sh (teardown). Creation encodes
three invariants that bite if skipped — fetch-before-branch (stale
local refs silently start you on a pre-merge tip), env symlinks (missing
secrets read as flaky/timeout test failures, not "config not found"),
and a per-worktree dependency install (installed deps don't carry across
worktrees → Cannot find package '...'). Don't reimplement these
inline; call the scripts.
Configuration
Reads .rig/config.json (missing keys → defaults):
| Key | Default | Used for |
|---|---|---|
vcs.baseRef | origin/main | Default --base the worktree branches from. |
vcs.branchConvention | {user}/{ticket}-{slug} | Template when inferring a branch name from a ticket ID. |
vcs.defaultBranch | main | The trunk (informational; the main worktree). |
runtime.installCommand | derived from packageManager | Passed to the setup script as --install-cmd. |
runtime.packageManager | npm | Derives the install command when installCommand is unset (bun→bun install, pnpm→pnpm install, yarn→yarn, npm→npm install, none→skip install). |
tracker.ticketPrefix | — | Recognizing a ticket ID (e.g. ABC-521) to slot into the branch template. |
If .rig/config.json is absent, run unconfigured: base origin/main,
install npm install, branch template {user}/{ticket}-{slug}, and say so.
Subcommands
$ARGUMENTS routes to one of:
- (default — a branch name) create/reuse a worktree. See Create below.
list— show all worktrees with their branch + PR state. Read-only.rm <branch|path>— safely tear a worktree down (skip if dirty).
Create — arguments
$ARGUMENTS is the branch name plus optional flags. The branch is the
only required argument.
<branch>— branch to create/check out, e.g.alice/feat-521-renameorfeat-679-observability. The naming convention for ticket work isvcs.branchConvention(default{user}/{ticket}-{slug}).--base <ref>— base to branch from. Defaultvcs.baseRef. For a stacked/integration child, passorigin/<integration-branch>.--path <path>— worktree location. Default.claude/rig-worktrees/<last segment of branch>(soalice/feat-521-foo→.claude/rig-worktrees/feat-521-foo).--reuse— if the worktree/branch already exists, fetch and hard-reset it to<base>instead of failing. Use this for a "reuse a child worktree" path.--no-install— skip the dependency install (rarely wanted; only for a read-only checkout).--name <session-name>— background-job/session display name. Delegates toset-session-name.sh(ships with the kit). Claude-Code-only — it renames the FleetView background-job entry so the job list reads as the work (the ticket/epic) instead of the raw prompt; a clean no-op on any other agent.--skip-if-prefix <p>— passed through toset-session-name.sh; leave an existing name in place if it starts with<p>(so anEPIC:label set byrig-epicwins over a per-childFEAT:/CHORE:label).
Create — procedure
-
Read
.rig/config.jsonforvcs.baseRef,vcs.branchConvention, and the install command. Derive the install command fromruntime.installCommand, else fromruntime.packageManager. -
Infer the branch if not given. If
$ARGUMENTSis empty:- If the current branch matches the ticket-work convention, offer to reuse / recreate it. Otherwise ask the user for a branch name or ticket ID. Don't guess a branch out of thin air.
- If given a ticket ID (e.g.
<ticketPrefix>521) with no slug, build the branch fromvcs.branchConvention, substituting{user}(git user),{ticket}(the lowercased ID), and{slug}(a short kebab from the ticket title — fetch it if you have a tracker, else ask).
-
Run the script from the repo root, passing the resolved base and install command:
"$(git rev-parse --show-toplevel)/.claude/scripts/setup-worktree.sh" \ <branch> [--base <baseRef>] [--install-cmd "<install cmd>"] [--reuse]The script prints progress to stderr and the absolute worktree path as the last line of stdout, so capture it:
WT=$("$(git rev-parse --show-toplevel)/.claude/scripts/setup-worktree.sh" \ alice/feat-521-rename --base origin/main --install-cmd "bun install" | tail -1)(The script also honors
WORKTREE_BASE_REFandWORKTREE_INSTALL_CMDenv vars, so you can export those from config instead of passing flags.) -
Report back the worktree path, the branch, and the base it was cut from. If the caller is a human running this directly, remind them they can
cd "$WT"to work there. If the script failed (e.g. path exists without--reuse), surface the error verbatim and suggest--reuse.
list
Show every worktree with the branch it has checked out and, where one exists, the state of its PR. Read-only — never removes anything.
git worktree list --porcelain
For each non-main worktree, resolve its branch and look up the PR state so the user can see what's safe to prune:
gh pr list --head <branch> --state all --json number,state,title -q '.[0] // empty'
Report a table: worktree path · branch · PR # · PR state (OPEN /
MERGED / CLOSED / none) · dirty? (git -C <path> status --porcelain).
End with a one-line hint, e.g. "2 worktrees on MERGED branches —
/rig-worktree rm <branch> to reclaim them."
rm <branch|path>
Safely tear a worktree down via the shared script. Dirty worktrees are
skipped, not removed (a skip is a normal outcome, not an error) —
pass --force-dirty to override.
"$(git rev-parse --show-toplevel)/.claude/scripts/remove-worktree.sh" \
<branch|path> [--force-dirty] [--keep-branch]
The first stdout token is the outcome: removed <path>,
skipped <path> (dirty), or absent <arg> (no such worktree). The
script deletes the branch too (force -D, since a squash/rebase-merged
PR leaves the local branch unmerged); pass --keep-branch to retain it.
Report what was removed, skipped (with the dirty reason), or absent.
Notes
- The scripts resolve the main worktree (first entry of
git worktree list) as the source for env files / ignores and as the place to rungit worktreecommands, so they're safe to invoke from inside another worktree.remove-worktree.shrefuses to remove the main worktree. - Symlinks (not copies) — editing an env file in the main repo
propagates to every worktree.
ln -sfnis idempotent, so re-running create with--reusere-links cleanly.
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.