New skill: /worktree-cleanup — cautious bulk removal of abandoned .claude/worktrees
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 0
- Forks
- 1
- Avg merge
- 1d 3h
- Merged PRs (30d)
- 7
Description
Summary
Companion to #195. That issue makes per-agent worktrees the default (main checkout pinned to origin/main; every other branch lives in its own worktree) and requires agents to clean up their own worktrees when done. This skill handles the other half: a semi-regular bulk sweep that removes worktrees left behind under .claude/worktrees/ — because with 6–7 agents cycling through, some worktrees will always get orphaned (crashed agent, abandoned analysis, forgotten teardown).
The whole point is that it is cautious: a bulk remover that's even slightly too aggressive can tear out a worktree an agent still needs, or one holding un-pushed commits — i.e. cause data loss. This skill must be safe by construction.
Why a skill (not documentation, not inline markdown)
This is deterministic logic with a genuine data-loss hazard and a subtle "is this still needed?" judgment in the middle — exactly the shape this repo's CLAUDE.md says belongs in a committed, shell-testable script under plugins/<plugin>/scripts/, with the command markdown acting as a thin handler. A prose rule can't safely encode "remove only when clean AND merged/pushed AND idle, dry-run first."
Proposed behavior
/worktree-cleanup [--repo <path>] [--older-than <days>] [--yes] [--json]
- Enumerate worktrees via
git worktree list --porcelain(never by globbing the directory — respects git's own registry and prunes stale admin entries). - Skip the main checkout (the one on
main) and skip the worktree that is the current CWD (you can't remove the one you're standing in — see #168). - For each remaining worktree, classify against the safety gates below and bucket it: Safe to remove, Keep (in use / unsafe), or Review (borderline).
- Dry-run by default — print the table of what would be removed and why (bucket + evidence per worktree). Nothing is removed without
--yes, and even with--yes, automatic removal is restricted to the safest tier (merged/closed PR + clean + fully pushed). - On confirmation, remove in the correct order (per #168):
git worktree remove <path>from the main checkout; optionallygit branch -d <branch>(only if merged; never-Dautomatically); leave remote branches alone unless explicitly asked. git worktree pruneat the end to clear stale admin records.- Report what was removed, what was kept, and why.
Safety gates (a worktree is removable only when ALL hold)
- Clean working tree —
git -C <wt> status --porcelainempty (no uncommitted or untracked changes). Never force-remove a dirty worktree. - No local-only commits — its branch is fully merged into
origin/mainOR fully pushed to origin. Nothing exists only in the local worktree. This is the data-loss guard. - Idle — not actively in use, past an inactivity threshold (see open question below).
- (Strong done signal, when resolvable) — its PR is merged or closed.
Anything failing a gate goes to Keep or Review, never auto-removed. Bias: when in doubt, leave it.
Idle detection (open question — needs a decision)
How the sweep decides a worktree isn't still needed. Candidates:
- mtime / inactivity threshold (
--older-than, default e.g. 7 days) — simple, portable, no cooperation needed. Risk: a long-running-but-quiet agent could look idle. Recommended as the gate — but layered on top of the clean + merged/pushed gates, so a false "idle" reading can only remove an already-safe tree, never lose work. - Live-process-CWD scan — accurate ("is any process cwd'd inside this worktree?") but OS-specific (
lsof//procon Linux, different on Windows). - Heartbeat/lock file each active agent refreshes — most reliable, but requires agents to cooperate (write/refresh a marker), which is a bigger change.
Leaning: mtime threshold as the idle gate, stacked on clean + merged/pushed, so the worst-case failure is "removed a stale but already-safe worktree," not data loss. Open to a process scan as an optional stricter check.
Design considerations
- Enumerate via git, not the filesystem —
git worktree list --porcelainis the source of truth. - Never remove the CWD worktree or the main checkout.
- Dry-run first, always. Removal is opt-in (
--yes), and even then auto-removal is limited to the safest tier. - Deterministic logic in a committed, smoke-tested script under
plugins/worktree-cleanup/scripts/; the command markdown is a thin handler (per repoCLAUDE.md). Smoke test covers: dirty tree kept, unpushed/unmerged branch kept, CWD worktree skipped, clean+merged+idle removed, main checkout skipped. - Honor
.allow-in-place— in an opt-out repo (#195) there are no managed worktrees to sweep; the skill should no-op cleanly. - Removal order and Windows CWD-lock per #168 — remove from outside the target; don't try to delete a live CWD.
- Cross-platform — the idle check and any process scan must degrade gracefully on Windows.
Acceptance criteria
- New skill
worktree-cleanupregistered in the marketplace and installable. - Enumerates worktrees via
git worktree list --porcelain; skips the main checkout and the current CWD worktree. - Applies all safety gates (clean + merged/pushed + idle); never removes a dirty or local-only-commit worktree.
- Dry-run by default; removal requires
--yes; auto-removal limited to the safest tier. - Correct removal order per #168; runs
git worktree prune; does not force-delete branches or touch remote branches by default. - Idle-detection method chosen and implemented (see open question).
- Deterministic logic in a tested script; smoke test included covering the keep/remove/skip cases above.
- No-ops cleanly in an
.allow-in-placerepo.
Relationships
- Depends on / follows #195 (the worktree discipline this cleans up after).
- Builds on #168 (worktree-removal mechanics: can't remove the CWD; Windows CWD-lock; correct removal order).
- Interacts with the
.allow-in-placeopt-out defined in #195.
Out of scope
- Deleting remote branches (leave to the PR-merge flow).
- Force-removing dirty or unmerged worktrees.
- The per-agent self-cleanup rule itself (that's a directive in #195).
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Read the repository CLAUDE.md and related issues #195 and #168 before defining the skill. Implement the deterministic logic in plugins/worktree-cleanup/scripts/ and connect it to the command handler, using git worktree list --porcelain as the entry point. Resolve the idle-detection choice and add smoke coverage for the listed keep, remove, skip, dry-run, and .allow-in-place cases.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- git, shell
- Domain
- cli, tooling
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100