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 # registered worktrees
git status --porcelain --ignored | head -50 # ignored bulk
du -sh <big hits from status> 2>/dev/null | sort -rh
git branch -vv | grep ': gone' # branches whose upstream vanished
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 removethengit worktree prune. - Local branches whose upstream is gone AND that hold no unique commits.
git worktree pruneandgit remote prune origin(the non-dry runs).node_modulesinside 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 --onelinenon-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:
.gitinternals, 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 # empty
git -C <dir> log --branches --not --remotes --oneline # empty — no unpushed commits
git worktree list | grep -F <dir> # empty — unregistered, or user confirmed removal
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 --remotesrun 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
.gitis 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 -rfon the verified path; no parallel deletion storms on a warm machine. - After worktree deletions run
git worktree prunein 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.