| name | clean-stale |
| description | Find and delete stale worktrees, build artifacts, caches, and dead branches safely, with an inventory-first protocol and freed-space report. Use when asked to clean up, free disk space, remove stale worktrees or artifacts, when a repo or machine "is huge", or when leftover agent worktrees and generated test output accumulate. |
Clean stale data
Inventory first, classify, then delete. Deletion without a prior inventory line is a bug. Report freed space per item and in total.
Inventory
Run per repo (or per directory the user names):
git worktree list
git status --porcelain --ignored | head -50
du -sh <big hits from status> 2>/dev/null | sort -rh
git branch -vv | grep ': gone'
git worktree prune --dry-run; git remote prune origin --dry-run
find . -maxdepth 3 -name node_modules -prune -o -type d -name ".claude" -print 2>/dev/null
Look especially for the recurring offenders: orphaned agent worktrees under .claude/worktrees/ and /private/tmp/<repo>-*, generated test/scaffold output, .smoke-output-style ignored artifact dirs, committed generated reports.
Classification
Safe — delete without per-item approval, after the checks below pass:
- Git-ignored build artifacts and caches (
dist, .turbo, coverage, playwright-report, smoke output).
- Worktree directories NOT in
git worktree list, once verified clean (below).
- Registered worktrees the user calls stale, once verified clean, via
git worktree remove then git worktree prune.
- Local branches whose upstream is gone AND that hold no unique commits.
git worktree prune and git remote prune origin (the non-dry runs).
node_modules inside a directory already classified safe to delete.
Confirm with the user first — list with sizes, wait for a yes:
- Any tracked file (deletion is a commit; one-shot authorization rules apply).
- Branches or worktrees holding unique commits (
git log <ref> --not --remotes --oneline non-empty) or uncommitted changes.
- Remote branch deletion, and anything not authored by the user.
- Caches that are expensive to rebuild (downloaded browsers, models, dependency stores shared across repos).
- Session transcripts, logs, and histories (
~/.codex/sessions, ~/.claude/projects) — these are the user's records.
Never:
.git internals, git history rewrites, reflog expiry.
- Env files, credentials, keychains.
- Live application state dirs (
~/.<app> userdata patterns) — real databases live there.
- Anything inside a worktree with a dirty
git status, beyond that worktree's ignored artifacts.
Worktree safety check
A worktree directory is safe to delete only when ALL hold:
git -C <dir> status --porcelain
git -C <dir> log --branches --not --remotes --oneline
git worktree list | grep -F <dir>
Any check fails → report the directory with the failing evidence instead of deleting.
Two verified gotchas:
- A stale clone's own remote-tracking refs freeze at clone time, so
git log --branches --not --remotes run INSIDE it reports false "unpushed" commits. Resolve each branch SHA and test it against the PARENT repo's live remote instead: git -C <parent> merge-base --is-ancestor <sha> origin/<main>.
- A directory whose
.git is a pointer file is a worktree of some other clone, not of the main repo. Deleting that owner clone first strands the worktree. Delete owner and its worktrees together, after salvaging.
For a dirty clone or worktree whose committed history is fully merged, salvage the uncommitted edits as a patch (git -C <dir> diff > <name>.patch into a directory outside the repo), verify the patch is non-empty, then the directory joins the safe class.
Execution
- Delete sequentially with
rm -rf on the verified path; no parallel deletion storms on a warm machine.
- After worktree deletions run
git worktree prune in the parent repo.
- Machine-wide sweeps ("clean my machine") iterate the user's project directories, applying the same protocol per repo; also check the system temp dir for
<repo>-* scratch checkouts.
Report
One line per deleted item: path, size, class. One line per skipped item: path and the evidence that blocked it. End with total freed. Nothing else.