TimZander / TimZander/claude

New skill: /worktree-cleanup — cautious bulk removal of abandoned .claude/worktrees

Open
#196 0 comments 0 reactions 0 assignees View on GitHub

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]
  1. Enumerate worktrees via git worktree list --porcelain (never by globbing the directory — respects git's own registry and prunes stale admin entries).
  2. 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).
  3. For each remaining worktree, classify against the safety gates below and bucket it: Safe to remove, Keep (in use / unsafe), or Review (borderline).
  4. 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).
  5. On confirmation, remove in the correct order (per #168): git worktree remove <path> from the main checkout; optionally git branch -d <branch> (only if merged; never -D automatically); leave remote branches alone unless explicitly asked.
  6. git worktree prune at the end to clear stale admin records.
  7. Report what was removed, what was kept, and why.

Safety gates (a worktree is removable only when ALL hold)

  • Clean working treegit -C <wt> status --porcelain empty (no uncommitted or untracked changes). Never force-remove a dirty worktree.
  • No local-only commits — its branch is fully merged into origin/main OR 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 / /proc on 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 filesystemgit worktree list --porcelain is 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 repo CLAUDE.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-cleanup registered 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-place repo.

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-place opt-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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.