agentsclimarketplace

Worktree branching

Skill yoelgal/better-dev/skills/worktree-branching

Portable dev practices, packaged as skills - that run inside the coding agent you already use (Claude Code, Codex, pi, hermes). Not an agent, framework, or provider layer.

Install
npx -y skills add yoelgal/better-dev --skill worktree-branching

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 0 stars0 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.

What its author says it does

Copied from the file, not written here

Use when a feature, fix, or chore work-item is about to start and needs its own isolated branch off the integration branch - before planning, diagnosis, or the autonomous loop runs - or when detecting whether such a worktree already exists. For removing one after merge, see this skill's teardown notes; for moving a half-finished item to a colleague or another machine, its handoff notes.

SKILL.md

14.1 KB, ~3.4k tokens by cl100k_base, as published. Nobody here has run it

Worktree branching

One job: give a work-item exactly one isolated git worktree on its own feature/, fix/, or chore/ branch off the integration branch, so work runs in parallel without touching the primary checkout. Detect an existing one before making a new one; never make two.

Native git worktree is the whole mechanism - no wrapper is added where git already does the job.

Before anything: read the overrides

A project may already have opinions here - a different branch prefix (feat/ not feature/), a different integration branch (develop not staging), a different placement. Read them first and let them win:

.better-dev/bin/bd-mem read overrides 2>/dev/null
.better-dev/bin/bd-mem recall "branch prefix integration worktree" 2>/dev/null

Detect the layout, don't impose one. What the repo already does is the default.

You are not the only actor here

This repo runs parallel worktrees, and other agents or the operator may be working in it while you are. If you notice changes in the working tree or index you did not make, they are not yours to undo - do not revert, stash, or "clean up" after another actor. Keep going if the changes don't touch your files; if they do, or you cannot tell whose they are, surface exactly what you see and ask before proceeding. git checkout --, git reset --hard, and git clean aimed at work you didn't author are how a parallel model loses someone else's data.

Step 0 - are you already isolated?

Before creating anything, check whether this session is already inside a linked worktree:

GIT_DIR=$(cd "$(git rev-parse --git-dir)" 2>/dev/null && pwd -P)
GIT_COMMON=$(cd "$(git rev-parse --git-common-dir)" 2>/dev/null && pwd -P)
BRANCH=$(git branch --show-current)

If GIT_DIR != GIT_COMMON you are already in a worktree (or a submodule - the two look alike; edge-cases.md has the one-line submodule guard). When the branch already matches this work-item, this is a re-run: report the existing worktree and stop. Nothing to create. Hand straight to the next skill.

If GIT_DIR == GIT_COMMON you are in the primary checkout - the place features branch from, not into. Continue to Step 1.

A worktree is per work-item, never per change. A same-work-item follow-up - a review fix, an operator tweak to an open item - rides the existing worktree and branch; only a new work-item earns a new one. And a live operator instruction ("just push it to the PR branch") is honored immediately per /overrides, never out-ritualed with fresh ceremony.

Step 1 - resolve the branch and its base

A work-item is a feature, a fix, or a chore. The prefix decides the base branch:

Work-itemBranchBase (unless overridden)
featurefeature/<slug>integration (staging)
fixfix/<slug>integration (staging)
chorechore/<slug>integration (staging)
hotfixhotfix/<slug>main

Apply any override from the read above (e.g. prefix feat/, integration develop) before building the name. Derive the slug from the work-item title - lowercase, non-alphanumerics to single dashes, trimmed:

slug=$(printf '%s' "$title" | tr 'A-Z' 'a-z' | tr -cs 'a-z0-9' '-' | sed 's/^-//;s/-$//')
branch="feature/$slug"        # or fix/ · chore/ · hotfix/ - honor the override prefix
base="staging"                # hotfix → main; honor the integration override

Confirm the base branch actually exists in git before branching off it - a base named in prose isn't real until git shows it (git rev-parse --verify "$base" or origin/$base). A missing integration branch is an onboarding gap, not something to invent here, and a recorded name git contradicts is re-verified and rewritten (/overrides), never obeyed.

Before opening a second parallel worktree, read the live lanes: git worktree list, then per live branch git diff --name-only <base>...<branch> - a path two lanes both touch makes the work sequential, not parallel. So does a recorded shared-runtime: serialize (.better-dev/bin/bd-mem recall "shared-runtime"): lanes coupled through one mutable datastore collide in data, not files - Step 2's datastore note records that key.

Step 2 - create the worktree

Prefer a native tool. If the harness offers one - a tool named like EnterWorktree / WorktreeCreate, a /worktree command, or a --worktree flag - use it, skip the git commands, and let it place the worktree in its own default directory. Name it with the resolved branch name directly, <prefix>/<slug> (Claude Code's EnterWorktree name parameter accepts /-separated segments, so feat/my-slug is a legal name). Never feed it the .worktrees/ path below: that is the git fallback's default, not an argument for the native tool, and a model-supplied path outside the harness default buys a needless permission prompt.

Native creation typically branches off the repo's default branch, not the base from Step 1 - honor the base afterward, in the fresh clean tree the native tool just created:

git fetch origin "$base"
git checkout -B "$branch" "origin/$base" 2>/dev/null || git checkout -B "$branch" "$base"

Do not use git reset --hard here - bd-guard flags it as a working-tree discard, and it is the wrong tool anyway. checkout -B moves the branch pointer and re-checks-out the tree, and it refuses rather than discards if the tree unexpectedly carries changes; a fresh worktree is clean, so that refusal never fires in the normal case - a safety property of the command, not a workaround for a problem this flow has. No host knob, no settings write, no create-with-git-then-enter-natively hybrid, no relocation prompt. Running git worktree add alongside a native creation leaves phantom state the harness can't see - only the fallback below reaches for it. The git fallback remains only for a host with no native worktree tool at all.

Git fallback. Place worktrees under .worktrees/ at the repo root (gitignored, discoverable); a sibling ../<repo>-<slug> layout is an override some repos prefer - see edge-cases.md. Guard the placement, then branch off the base, not off HEAD:

git check-ignore -q .worktrees || { printf '.worktrees/\n' >> .gitignore && git add .gitignore; }
path=".worktrees/$slug"
git worktree prune   # clear a stale registration if $path was removed but still listed
git fetch origin "$base" 2>/dev/null || true
git worktree add -b "$branch" "$path" "origin/$base" 2>/dev/null \
  || git worktree add -b "$branch" "$path" "$base"

git worktree prune only clears entries whose directory is gone, so it's safe alongside a live concurrent run - a worktree still in use keeps its directory and survives the prune.

If $path already exists or the branch is already checked out somewhere, this is a re-detect: point at the existing worktree rather than forcing a duplicate. If git worktree add fails on a sandbox permission error, say so and work in place - edge-cases.md covers that fallback.

A fresh worktree also has none of the primary checkout's gitignored local state - the runtime config the app needs (.env* files and the like). Copy that class from the primary checkout at creation - copy, never symlink: build tools reject symlinks and a symlink turns teardown into a two-step dance - so the first dev-server run doesn't die mid-task on missing env. It is personal, gitignored state, so the copies never enter a PR. The copy happens at creation, not lazily on first failure: a fresh worktree whose first bd-mem or dev-server call dies on missing gitignored state (a missing bin bridge, an absent .env) is the tell this step was skipped. Settings-class files - a .claude/settings.local.json allowlist, or the host's equivalent - are operator-owned and never copied here: the agent never writes them, so there is nothing of that class for this step to carry forward. Where a noisier fresh worktree keeps re-prompting on actions the operator already approved elsewhere, /guardrails-install's grant step hands the operator a paste-ready copy command instead.

That copied runtime config points every lane at the same mutable datastore - one DATABASE_URL, one Redis, one object store - so when git worktree list shows another live lane, this lane's dev server and checks write the data that lane reads, even with zero file overlap between the branches. Where the stack supports it, namespace the copy per lane: suffix the schema or database name with the slug, or point the copy at a separate ephemeral store (a per-lane SQLite file, a second local database), so one lane's writes and resets never surface in another lane's verify. Where the stack offers no per-lane split, record the coupling once - .better-dev/bin/bd-mem remember "shared-runtime: serialize" - so /orchestrating-agents' live-lanes check treats data-coupled lanes as sequential rather than parallel; unrecorded, a failure born in another lane's data reads as flake, and no file diff explains it.

A fresh worktree has no installed deps, so run the project's setup and one baseline check here - that way the loop's first verify measures your work, not a missing node_modules misread as a failure. Prefer the project's own named setup entry point (a documented setup or bootstrap script or task) over an ad-hoc install command composed here; a fixed, idempotent entry point is what a later session or a restart re-runs without guessing which command you used. If none exists, record it as a groundwork gap rather than papering over it with a one-off a future session can't find. The recorded dev-run key (.better-dev/bin/bd-mem recall "dev-run") is the command that stands this tree's app up when a check needs it live - recorded once by /guardrails-install, recalled here instead of re-discovered per worktree. If this skill hands off immediately (the interlock in Step 3), /autonomous-loop's ground-truth gate covers the same baseline at the other end.

A fresh worktree shares git history but not installed deps or build output. An install-or-build step needed only to make the tree runnable is expected setup, not a scope violation or a deviation; a review that re-runs the done-criteria in a fresh worktree does not flag it as one.

Step 3 - record it, then hand off

Write the worktree's location into the ledger in the primary checkout (shared across worktrees, so a later session or a restart can find it):

printf 'branch: %s\nbase: %s\nworktree: %s\n' "$branch" "$base" "$(cd "$path" && pwd -P)" \
  | .better-dev/bin/bd-mem ledger put "$slug" worktree.md -

bd-mem resolves the primary checkout's ledger for you (the same path from any worktree), so there's no hand-rolled git-common-dir math to get subtly wrong.

Then set the mechanical edit boundary. This skill is the boundary's one writer - it scopes the new worktree at creation, and the loop only verifies the boundary is set, never re-sets it:

.better-dev/bin/bd-guard scope "$(cd "$path" && pwd -P)" --ttl 0

The state lives in the worktree's own git dir (.git/worktrees/<name>/bd-scope), which is what makes the auto-activation survive the handoff interlock below: the boundary is per-target-worktree, not per-session, so the session that picks up the handoff starts already bounded to its own tree, and parallel worktrees never share a boundary. Where no enforcement hook is wired (.better-dev/bin/bd-mem recall "safety-enforcement" says prose) the state is inert - write it anyway, so a later hook install starts enforcing without a re-setup. If the boundary ever blocks legitimate work, .better-dev/bin/bd-guard off lifts it in one command.

Now the interlock. When this skill created a new worktree, the work lives there. If the host can switch the session into it natively (the same worktree tool entering by path, or creation already moved the session), take that and continue as handoff: no. Otherwise the agent cannot move its own harness session into that directory, so it hands off and stops rather than pretending to continue:

handoff: yes
→ start a session rooted in <path> (branch <branch>), then run /plan-grill, /diagnose, or /autonomous-loop

A consumer that reads handoff: yes does not cross the worktree boundary on its own. When Step 0 found the session already in the right worktree, emit handoff: no instead and let the next skill continue in place. The hand-off targets are /plan-grill (feature, or a chore on its contract-lite path) or /diagnose (fix) for the typed front-end, then /autonomous-loop to drive the work.

An isolated worktree means an isolated running app: a dev server started here serves this tree on its own port, and the operator's habitual server keeps showing the old code until merge. When verifying against a live surface, name the exact URL/port that shows the fix in the same message, and give operator feedback that contradicts your verified state a stale-surface check first ("which port/URL are you looking at?") before any re-diagnosis.

Finishing up

Removing a worktree after its PR merges is a destructive operation with a strict safe order and a fail-closed ownership check. Read teardown.md when the work-item is done - don't improvise a git worktree remove --force or rm -rf from memory.

Handing a half-finished work-item to a colleague or another machine is its own procedure - the ledger's consent gates are per-machine and gitignored, so a bundle travels on the branch and the receiving side re-pins consent. Read handoff.md before handing one off or picking one up.

For the trickier detection cases - submodules, detached HEAD, native-tool phantom state, the sibling placement override, the trunk-based branch profile, sandbox denial - read edge-cases.md.

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.