Worktree Management
Git worktrees live under $VIBE_HOME/worktrees/ (default ~/.vibe/worktrees/),
each in a per-repository bucket named <repo_root.name>-<sha256(common_git_dir)[:12]>
so worktrees from different repos never collide.
$VIBE_HOME/worktrees/ .claims/<bucket>/<name>/ # claim records and holder markers record.json # metadata: branch, base_commit, branch_created, claimed_at recovery.json # snapshot metadata for a retained chat holders/ # one empty file per live session <bucket>/<name>/ # the actual git worktree checkout
Creating a worktree
- Compute the bucket from the repo root and common git dir.
- Validate the name (single portable path segment; no
<>:"/\|?*, no trailing space or dot, no Windows reserved names likeCON/PRN/NUL/COM1-COM9/LPT1-LPT9, not.or..). - Reserve the directory with
mkdir(atomic claim — fails if taken). - For a new branch, start from the remote's default branch (fetch first, best-effort). For an existing branch, check it out into the worktree.
git worktree add -b <name> <path> <start_point>(or without-bfor reuse).- Record the worktree's own HEAD as
base_commit— this is the baseline for cleanup, not the invoking checkout's HEAD. - Write
record.jsonunder.claims/<bucket>/<name>/.
When no name is given, derive one by slugifying the prompt: NFKD-normalize,
lowercase, replace non-[a-z0-9]+ with -, take the first 6 words, trim to
40 chars, drop trailing stop words. Branch gets a vibe/ prefix. Append
-2, -3, etc. on collision (up to 100). Fall back to a random slug if empty.
Reuse an existing worktree only after validating: no symlinks in the path,
.git is a file (not a directory), same common git dir, matching branch.
Holding
A session using a worktree must register as a holder to prevent the background cleanup from removing it. Holder files are locked for the lifetime of the session; an unlocked marker left by a crashed process is stale and may be removed. A temporary holder protects a worktree while a session attaches.
Cleaning up
Before removal, inspect the worktree state:
- Uncommitted changes or untracked files:
git -c core.fsmonitor= status --porcelain --untracked-files=all - New commits since start:
git rev-list --count <base_commit>..HEAD
The worktree is clean only if all three are absent. If dirty, confirm with the user before proceeding — removal discards uncommitted work, untracked files, and new commits.
If the branch pre-existed (not created by this session), confirm separately
before deleting it. Re-check holders — if another session still holds the
worktree, keep it. cd out of the worktree before removing (Windows refuses
to delete a process's cwd).
Remove: git worktree remove --force <path>, then git branch -D <name> if
the branch was created this session or the user confirmed. Delete the claim
record and rmdir empty directories (never rmtree — a surviving holder
must not be lost).
Snapshotting
Before retention removes an inactive worktree, save its state to a ref: stage
all files (including untracked, excluding ignored) and commit to
refs/vibe/reaped/<name>. Use a separate index file so the worktree's own
index is untouched. Keep recovery.json after removing the active claim so
reopening a chat can recreate the checkout automatically.
Retention
Keep the newest configured number of managed worktrees across repositories.
Skip active worktrees. Snapshot each inactive excess worktree before removal;
if snapshotting fails, keep it. Reservations without a base_commit are
discarded only if empty and no creation process still holds their marker.
Creation, attachment, and retention transitions use a cross-process lock so a worktree cannot be removed while another process is claiming or attaching it.
Gotchas
- Disable
core.fsmonitorwhen reading someone else's working tree:git -c core.fsmonitor= status. - New branches start from the remote default branch, not the current checkout's HEAD.
- Symlink paths are rejected — not a stable worktree path.
git worktree addcannot distinguish a taken path from an invalid ref (both exit 128), somkdirreserves the path first.- Worktrees are implicitly trusted for the session only, not persisted.

