Sweep leftover worktrees under worktree_dir (.claude/worktrees/ for
Untype) and the branches they reference. Counterpart to /finalize-worktree:
finalize handles the happy path (merge + remove one worktree), this handles
the accumulated debris from parallel-agent sessions that were abandoned,
crashed, or whose work already landed via a different path.
Project configuration
The manifest is this skill's configuration. If
$(git rev-parse --show-toplevel)/.claude/lifecycle-manifest.md does not exist, the
project has not adopted the kit: say so, offer to create it, and stop. Never infer
the keys and run anyway. The kit ships the template as
lifecycle-manifest.template.md; locate the installed copy with
find ~/.claude/plugins -path '*lifecycle-kit*' -name lifecycle-manifest.template.md | head -1.
The marketplace's ADOPTING.md carries a repo-inspecting setup prompt.
Read .claude/lifecycle-manifest.md first (resolve via
$(git rev-parse --show-toplevel)/.claude/lifecycle-manifest.md). Where a
step names a manifest key in code font (worktree_dir, worktree_ignore,
orphan_branch_globs, scratch_paths), use that key's value. The containment
commands write the ref as <integration_ref> and the remote as <remote>;
replace each with the manifest value before running. The surrounding prose still
names origin/main with Untype's value for readability. The ahead=0
containment proof and the -D-vs--d seam are the skill's logic, not
configuration.
Safety rule
A worktree/branch is safe to delete only if every commit on its branch
is already contained in origin/main, the authoritative integration ref —
i.e. git rev-list --count <integration_ref>..<branch> is 0 after a fresh
git fetch <remote> --prune. Measuring against local main would miss a
branch already merged on GitHub but not yet fast-forwarded locally, which
is the most common real debris this skill exists to sweep. Dirty working
trees do not count as unique work if they only touch the manifest's
scratch_paths (e.g. .claude/settings.local.json) or other ignored/noise files.
Anything with ahead > 0 is never auto-deleted by this skill. Report it
and ask the user.
Process
Phase 1 — Enumerate
git worktree list— list every worktree- For each non-primary worktree, record: path, branch (or detached HEAD), ref
git worktree list --porcelainif you need the machine-readable form
Phase 2 — Classify each worktree
First git fetch <remote> --prune so containment is measured against the
real merged state. Then, for each non-primary worktree branch $B
(measured against origin/main, never local main):
ahead=$(git rev-list --count <integration_ref>..$B 2>/dev/null)
behind=$(git rev-list --count $B..<integration_ref> 2>/dev/null)
dirty=$(git -C <worktree-path> status --porcelain)
If $ahead comes back empty (a bad or missing ref, with stderr swallowed
by 2>/dev/null), treat the branch as unique-work and report it.
Never classify an unparseable count as safe.
Classify as:
- safe —
ahead=0and (dirtyis empty, ORdirtyonly touches the manifest'sscratch_paths(for Untype:.claude/settings.local.json, gitignored, plustools/__pycache__/andtools/test_output.md, untracked build/test scratch not actually in.gitignore), or other genuinely ignored files) - unique-work —
ahead > 0. Has commits not in main. - dirty-real —
ahead=0butdirtycontains real uncommitted edits to tracked source files. Could be in-progress work. - detached — worktree is on a detached HEAD. Treat as
unique-workunless the HEAD SHA is an ancestor oforigin/main(git merge-base --is-ancestor $SHA <integration_ref>), in which case safe.
Also list any orphan local branches matching orphan_branch_globs
(feature/session-*, worktree-agent-*, fix/* for Untype) that are not
checked out in any worktree: the same ahead=0-against-origin/main check
applies, so those are safe to delete even without a worktree attached. The
session-branch glob (feature/session-* for Untype) is the dominant
convention /session-start and /session-end use, and a session whose PR
merged on GitHub leaves exactly such an orphan; omitting it was this skill's
main blind spot.
Phase 3 — Report, then confirm
Show the user a table:
worktree branch class notes
agent-xxxxxxx worktree-agent-xxxxxxx safe ahead=0, dirty only in settings.local.json
agent-yyyyyyy feature/foo unique-work ahead=3 — will NOT touch
agent-zzzzzzz fix/bar dirty-real ahead=0 but 2 tracked files modified
Then list what will be removed if the user confirms:
- N worktrees to
git worktree remove --force - M branches to
git branch -D
Always get explicit confirmation before deleting. Use AskUserQuestion if
the situation is non-trivial, otherwise a direct prompt. Never auto-delete
anything classified as unique-work or dirty-real.
Phase 4 — Execute
On confirmation:
git worktree remove --force <path>for each safe worktree (--forceis needed because of noise dirty state; we've already verified there's no real work to lose)git branch -D <branch>for each safe branch (including orphans). Use-Dnot-d: some ancestor-only branches appear "not merged" to git's merge detector even withahead=0. This is the deliberate inverse of/finalize-worktree, which forbids-Dand relies on a self-verifying-d. The two are not in conflict; the difference is the proof of containment.-Dis safe here only because Phase 2 already provedahead=0(every commit on the branch is inmain) before this line runs. Finalize has no such precomputed proof in its flow, so it must let-dverify. Never reach for-Dwithout an independent containment proof first.git worktree prune -vto clear any stale admin state- Leave the
worktree_dirdirectory itself in place — it's ignored viaworktree_ignore(.git/info/excludefor Untype, a machine-local exclude that does not survive a fresh clone, so do not assume.gitignorecovers it) and is reused by future agent runs
Phase 5 — Verify
git worktree list # only the primary worktree should remain (unless some were kept)
git branch # only surviving branches
ls .claude/worktrees/ # empty (or only kept worktrees)
Report a one-line summary: Removed N worktrees and M branches. P kept (reason).
Out of scope
This skill only touches worktrees and their branches. If the primary
repo has other untracked cruft (e.g. index.js, tools/test_output.md,
scratch files), flag them in the final report but do not delete — those
need a separate review.
Arguments
- No arguments: sweep everything under
worktree_dirplus orphan branches matchingorphan_branch_globs(worktree-agent-*/fix/*and the session glob for Untype). $ARGUMENTS=--dry-run: run phases 1–3 only, report, do not delete.