housekeep
Skill94 starsChanged 3 months agoApache-2.0View on GitHub
Local repo maintenance — clean stale worktrees, remove dirt files, sync with main, update codegraph, prune branches, and verify repo health
- Deletes or force-pushes
- Installs packages
2 other projects use a version of this.
Adopted by
Most recently taken up by
- Dammyjay93/housekeeptheir own version, first seen Sep 26, 2026
- kiloloop/ianthatheir own version, first seen Jun 23, 2026
Other versions2 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 2
- kiloloop/iantha★ 1same name
- Dammyjay93/housekeep★ 0same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository22 other files it gives its agents.
- CLAUDE.mdCLAUDE.md
- architectSkill
- bench-checkSkill
- create-skillSkill
- deps-auditSkill
- dogfoodSkill
- fixerSkill
- overseeSkill
- paritySkill
- releaseSkill
- resolveSkill
- sweepSkill
- test-healthSkill
- titan-closeSkill
- titan-forgeSkill
- titan-gateSkill
- titan-gauntletSkill
- titan-grindSkill
- titan-reconSkill
- titan-resetSkill
- titan-runSkill
- titan-syncSkill
The file
Show its rules and text
We split this skill into the rules it gives an agent
20 rules. About 0 are common, 14 are distinctive, and 6 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈14
Distinctive · estimate
This author's own choices. Worth a look.
6
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 18
No arguments — full cleanup
- Very specific: line 38
Always print this, even on --dry-run.
- Distinctive: line 52
If the total exceeds 5GB, raise it as a finding in the report regardless of whether any individual worktree is stale.
- Very specific: line 131
Before offering removal, run git -C <path> status --short to check for uncommitted changes:
- Very specific: line 174
Ask the user whether to clean build artifacts only (keep the source) — these regenerate on the next npm install / cargo build / codegraph …
- Distinctive: line 175
If confirmed, for each selected worktree:
- Distinctive: line 195
Remove temporary and generated files that accumulate over time.
- Distinctive: line 200
Run both commands and union the results to get the full set of candidate dirt files.
- Distinctive: line 204
Search for and remove files found by the two discovery commands above (never touch tracked files):
- Distinctive: line 268
Remove known dirt patterns automatically
- Distinctive: line 289
If main has new commits:
- Distinctive: line 290
If on main: git pull --no-rebase origin main
- Distinctive: line 291
If on a feature branch: inform the user how many commits behind main they are
- Distinctive: line 333
Not the current branch
- Distinctive: line 334
Not a worktree branch (check git worktree list)
- Distinctive: line 352
If confirmed, delete the branch:
- Very specific: line 360
Exit condition: every fully-merged, non-protected branch has had a confirmed delete decision (or been listed under DRY_RUN).
- Distinctive: line 381
If the graph is stale (built from a different commit), rebuild:
- Very specific: line 392
If issues found: npm install to fix.
- Very specific: line 444
Bloated-but-active worktrees: only delete the four regeneratable artifact paths (node_modules/, target/, dist/, .codegraph/graph.db*).
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
/housekeep — Local Repository Maintenance
Clean up the local repo: remove stale worktrees, delete dirt/temp files, sync with main, update codegraph to latest, prune merged branches, and verify repo health. The "spring cleaning" routine.
Arguments
$ARGUMENTSmay contain:--full— run all phases (default behavior)--dry-run— show what would be cleaned without actually doing it--skip-update— skip the codegraph npm update phase- No arguments — full cleanup
Phase 0 — Pre-flight
- Confirm we're in the codegraph repo root (check
package.jsonwith"name": "@optave/codegraph") - Parse
$ARGUMENTS:DRY_RUN=trueif--dry-runSKIP_UPDATE=trueif--skip-update
- Record current branch:
git branch --show-current - Record current git status:
git status --short - Warn the user if there are uncommitted changes — housekeeping works best from a clean state
Exit condition: repo identity confirmed (@optave/codegraph); DRY_RUN/SKIP_UPDATE are known; current branch and status recorded.
Phase 1 — Audit & Clean Worktrees
Always report disk usage first. Worktree bloat (per-worktree
node_modules/,target/,dist/) is the single largest source of disk waste in this repo — a fresh worktree withnpm install+ a Rust build is ~3GB. Even when no worktree is technically "stale" by branch criteria, the disk footprint must be surfaced so the user can decide what to keep.
1a. Total worktree disk usage
Always print this, even on --dry-run. Use du -sk (kilobytes) so the pipeline is portable across BSD (macOS) and GNU (Linux) — sort -h is a GNU coreutils extension and is rejected by stock macOS sort.
# 2>/dev/null suppress: .claude/worktrees/ may not exist yet on a fresh repo — zero worktrees, not an error
du -sh .claude/worktrees 2>/dev/null
# Portable per-worktree sort: kilobytes through sort -n, then format back to human-readable.
du -sk .claude/worktrees/*/ 2>/dev/null | sort -n | awk '{
k=$1; $1=""; sub(/^ /, "");
if (k >= 1048576) printf "%.1fG\t%s\n", k/1048576, $0;
else if (k >= 1024) printf "%.1fM\t%s\n", k/1024, $0;
else printf "%dK\t%s\n", k, $0;
}'If the total exceeds 5GB, raise it as a finding in the report regardless of whether any individual worktree is stale.
1b. List git-tracked worktrees
git worktree listCross-reference against .claude/worktrees/* on disk — directories there that aren't in git worktree list are orphaned (prunable). Worktrees in the list whose directory is missing are also prunable.
1c. Identify stale worktrees
Staleness is decided by .claude/scripts/gc-worktrees.sh, which is the single source for this rule. Run it in dry-run to get the triage list:
bash .claude/scripts/gc-worktrees.sh --dry-runIt reports a worktree as collectable only when all of: its path is under <repo-root>/.claude/worktrees/ (the scope guard, enforced in code); its branch belongs to a settled PR — merged or closed — with no open PR on that branch; its tree has no uncommitted changes; and it is not git worktree locked. Directories that vanished are handled by git worktree prune inside the script.
Do not hand-roll this check with
git branch --merged origin/main. In a repo that squash-merges, a landed branch's tip is never an ancestor of the default branch, so--mergedlists almost nothing — the check looks right, passes review, and silently reports every finished worktree as still in flight. The script resolves merged-ness from GitHub PR state for exactly this reason, and detects the default branch rather than assumingmain.
The script also reports an orphan bucket separately: worktrees whose branch has no PR at all. It never removes these (the work cannot be proven landed), but it names them, because they pin their branch names indefinitely and are usually the residue of an agent that improvised a local branch name after a checkout collision. Surface that list to the user.
To remove what it found, after confirming with the user:
bash .claude/scripts/gc-worktrees.sh --prune-branches--prune-branches also deletes each freed branch with git branch -d (safe delete — git refuses an unmerged branch); drop the flag to keep the branches. If DRY_RUN: stop after the --dry-run call above. The script never force-removes a worktree with uncommitted changes — it prints those as "has uncommitted work"; report that list rather than overriding it.
1d. Identify bloated worktrees (NEW)
A worktree is bloated if it is not stale (so we can't just remove it) but contains regeneratable build artifacts taking significant disk space. Check each non-stale worktree for:
node_modules/(typically ~1.8GB)target/(Rust build cache, typically ~1.4GB)dist/(compiled TS output).codegraph/graph.db*(rebuildable viacodegraph build) — measure only thegraph.dbandgraph.db-journalfiles, not the whole.codegraph/directory, because cleanup in §1e only removes those files. Measuring the whole directory would overstate the freed space.
For each worktree, sum the artifact sizes and emit a per-worktree subtotal so the 500MB threshold can be evaluated without manually regrouping flat output. Uses du -sk (kilobytes) with sort -n for portability — sort -h is GNU-only and breaks on stock macOS.
for wt in .claude/worktrees/*/; do
total_kb=0
breakdown=""
for sub in node_modules target dist; do
if [ -d "$wt$sub" ]; then
# 2>/dev/null suppress: the dir can vanish between the glob and this du (e.g. concurrent cleanup) — skip it, don't fail the scan
sz=$(du -sk "$wt$sub" 2>/dev/null | awk '{print $1}')
[ -n "$sz" ] && total_kb=$((total_kb + sz)) && breakdown="$breakdown $sub=${sz}K"
fi
done
# .codegraph: only measure the two files we will actually remove
for f in "$wt.codegraph/graph.db" "$wt.codegraph/graph.db-journal"; do
if [ -f "$f" ]; then
# 2>/dev/null suppress: same rationale as the node_modules/target/dist loop above — a vanished file mid-scan is skipped, not fatal
sz=$(du -sk "$f" 2>/dev/null | awk '{print $1}')
[ -n "$sz" ] && total_kb=$((total_kb + sz)) && breakdown="$breakdown $(basename "$f")=${sz}K"
fi
done
[ "$total_kb" -gt 0 ] && printf "%d\t%s\t%s\n" "$total_kb" "$wt" "$breakdown"
done | sort -n | awk -F'\t' '{
k=$1;
if (k >= 1048576) printf "%.1fG\t%s%s\n", k/1048576, $2, $3;
else if (k >= 1024) printf "%.1fM\t%s%s\n", k/1024, $2, $3;
else printf "%dK\t%s%s\n", k, $2, $3;
}'Flag any worktree whose combined build artifact size exceeds 500MB (512000 kilobytes).
1e. Clean up
For orphaned directories (on disk but not in git worktree list):
Critical: orphaned directories may still contain uncommitted work. A worktree's git registration can be dropped (failed
git worktree add, manualgit worktree prune, etc.) while the user's source edits remain on disk.rm -rfon such a directory is permanent data loss.
Before offering removal, run git -C <path> status --short to check for uncommitted changes:
# NOTE: iterate with `while IFS= read -r dir`, never `for dir in $ORPHANED_DIRS` —
# the Bash tool in Claude Code runs under zsh, which does NOT word-split an
# unquoted multi-line variable the way bash does. `for dir in $ORPHANED_DIRS`
# would silently collapse every orphaned directory into a single iteration
# whose $dir is the whole newline-joined blob, breaking `git -C "$dir"` the
# moment more than one orphaned directory exists.
printf '%s\n' "$ORPHANED_DIRS" | while IFS= read -r dir; do
[ -z "$dir" ] && continue
if [ -d "$dir/.git" ] || [ -f "$dir/.git" ]; then
# 2>/dev/null suppress: best-effort only — a failed git check on a non-worktree directory is expected and harmless here
changes=$(git -C "$dir" status --short 2>/dev/null)
if [ -n "$changes" ]; then
echo "SKIP $dir — has uncommitted changes:"
echo "$changes" | sed 's/^/ /'
continue
fi
fi
# Safe to offer removal — confirm with user first
echo "ORPHANED (clean): $dir"
doneOnly after confirming the directory is clean (no uncommitted changes) AND the user has explicitly approved removal, run rm -rf <path>. Then run git worktree prune to clear any dangling refs. Apply the same "Never force-remove a worktree with uncommitted changes" rule that protects stale worktrees in git worktree list — orphaned dirs get the same guardrail.
For prunable worktrees (in list but directory missing):
git worktree pruneFor stale worktrees with merged branches:
- List them with their disk size and always ask the user for confirmation before removing, regardless of
--full - If confirmed:
git worktree remove "<path>" git branch -D "<branch>"-D, not-d: this worktree was already classified "stale" via Phase 1c's GitHub-PR-state check, not git ancestry, so-d's own ancestry test would wrongly refuse a genuinely squash/rebase-merged branch here — see Phase 4d's identical fix for why-dis the wrong tool once a more authoritative source already confirmed the merge (issue #2309).gc-worktrees.sh's ownclassify()(which Phase 1c delegates to) now requires the same name-and-current-tip-SHA match against a MERGED PR that Phase 4a uses before treating a branch as-D-safe, closing the identical reused-branch-name gap here too (issue #2310).
For bloated (non-stale) worktrees:
- List them with a per-artifact size breakdown
- Ask the user whether to clean build artifacts only (keep the source) — these regenerate on the next
npm install/cargo build/codegraph build - If confirmed, for each selected worktree:
rm -rf "<worktree>/node_modules" rm -rf "<worktree>/target" rm -rf "<worktree>/dist" rm -f "<worktree>/.codegraph/graph.db" "<worktree>/.codegraph/graph.db-journal" - Never run
npm install/cargo cleaninside the target worktree — it may be in use by another Claude Code session
Follow-on risk this creates: stripped worktrees now silently fall back to the root repo's
node_modules. Node's module resolution walks up the directory tree, so once<worktree>/node_modulesis gone, anything run from inside that worktree resolves packages from the root repo'snode_modulesinstead — until that worktree's ownnode_modulesis reinstalled. This matters beyond this phase: if any later work in the same session manually copies, links, or patches something intonode_modulesfor local testing (a rebuilt native addon,npm link, a manually patched dependency, a locally built package tarball), never target the root repo'snode_modules— only the specific worktree that needs it, and only if that worktree still has (or is being given back) its ownnode_modules. Overwriting the root's shared copy silently changes behavior for every worktree currently falling back to it, including ones this exact cleanup just stripped and any other session using them. If this happens, restore the original immediately (e.g.npm install <package>@<original-version> --no-savefor an npm-distributed package, or reinstall/relink whatever else was overwritten) and verifygit status --shortin the root repo shows no unexpected changes.
If DRY_RUN: List everything that would be removed with sizes, don't do it.
Never force-remove a worktree with uncommitted changes. List it as "has uncommitted work" and skip — but still report its disk size so the user knows what it's costing. Never delete source files in a bloated worktree — only delete the four regeneratable artifact paths above.
Exit condition: worktree disk usage has been reported; every worktree is classified (orphaned / stale / bloated / active) with confirmed removals and artifact cleanups completed outside DRY_RUN.
Phase 2 — Delete Dirt Files
Remove temporary and generated files that accumulate over time. There are two distinct categories of dirt that require different discovery commands:
- Gitignored dirt (files matching
.gitignorepatterns — e.g.coverage/,.DS_Store,*.log,.codegraph/graph.db-journal): usegit clean -fdX --dry-runto list them.git ls-files --others --exclude-standardsilently omits these because--exclude-standardsuppresses gitignored entries. - Untracked non-ignored files (stray files not in
.gitignore— e.g.*.tmp.*,*.bak,*.orig): usegit ls-files --others --exclude-standardto list them.
Run both commands and union the results to get the full set of candidate dirt files.
2a. Known dirt patterns
Search for and remove files found by the two discovery commands above (never touch tracked files):
*.tmp.*,*.bak,*.origfiles in the repo (but NOT innode_modules/).DS_Storefiles*.logfiles in repo root (not innode_modules/)- Empty directories (except
.codegraph/,.claude/,node_modules/) coverage/directory (regenerated bynpm run test:coverage).codegraph/graph.db-journal(SQLite WAL leftovers)
Stale lock files (.codegraph/*.lock older than 1 hour): Before removing, first check if lsof is available (command -v lsof). If lsof is not installed (common in Docker/CI minimal containers where it exits 127), skip lock file removal entirely and print a warning: "lsof not available — skipping lock file cleanup (cannot verify no process holds the file)". When lsof IS available, use lsof "$f" to verify no process holds the file. If the file is held, skip it and warn — concurrent Claude Code sessions may hold legitimate long-lived locks.
# > /dev/null 2>&1: suppress command path on success and shell's "not found" on failure — the else branch reports the skip explicitly
if ! command -v lsof > /dev/null 2>&1; then
echo "lsof not available — skipping lock file cleanup (cannot verify no process holds the file)"
else
for f in .codegraph/*.lock; do
[ -f "$f" ] || continue
# 2>/dev/null suppress: BSD vs GNU stat take different flags — try GNU syntax first, fall back to BSD
age=$(( $(date +%s) - $(stat --format='%Y' "$f" 2>/dev/null || stat -f '%m' "$f" 2>/dev/null) ))
[ -z "$age" ] && continue
# > /dev/null 2>&1: suppress lsof's own output — only its exit code (held vs free) matters here
if [ "$age" -gt 3600 ] && ! lsof "$f" > /dev/null 2>&1; then
if [ "$DRY_RUN" = "true" ]; then
echo "[DRY RUN] Would remove stale lock: $f"
else
echo "Removing stale lock: $f"
rm "$f"
fi
elif [ "$age" -gt 3600 ]; then
echo "Lock file $f is old but still held by a process — ask user before removing"
fi
done
fi2b. Large untracked files
Find untracked files (both gitignored and non-ignored) larger than 1MB. Use both discovery commands and union the paths:
# Non-ignored untracked files
git ls-files --others --exclude-standard | while read f; do
# 2>/dev/null suppress: BSD vs GNU stat take different flags — try GNU syntax first, fall back to BSD; a genuine failure just yields empty $size and is skipped below
size=$(stat --format='%s' "$f" 2>/dev/null || stat -f '%z' "$f" 2>/dev/null)
[ -z "$size" ] && continue
if [ "$size" -gt 1048576 ]; then echo "$f ($size bytes)"; fi
done
# Gitignored files (strip the leading "Would remove " prefix from dry-run output)
git clean -fdX --dry-run | sed 's/^Would remove //' | while read f; do
# Skip directory entries — stat returns inode size, not content size
[ -d "$f" ] && continue
# 2>/dev/null suppress: BSD vs GNU stat fallback, same rationale as above
size=$(stat --format='%s' "$f" 2>/dev/null || stat -f '%z' "$f" 2>/dev/null)
[ -z "$size" ] && continue
if [ "$size" -gt 1048576 ]; then echo "$f ($size bytes) [gitignored]"; fi
doneFlag these for user review — they might be accidentally untracked binaries.
2c. Clean up
If DRY_RUN: List all files that would be removed with their sizes.
Otherwise:
- Remove known dirt patterns automatically
- For large untracked files: list and ask the user
Never delete files that are tracked by git. Only clean untracked/ignored files.
Exit condition: known dirt patterns have been removed (or listed, under DRY_RUN); large untracked files have been flagged for the user, never deleted automatically.
Phase 3 — Sync with Main
3a. Fetch latest
git fetch origin3b. Check main branch status
git log HEAD..origin/main --onelineIf main has new commits:
- If on main:
git pull --no-rebase origin main - If on a feature branch: inform the user how many commits behind main they are
- Suggest:
git merge origin/main(never rebase — per project rules)
- Suggest:
3c. Check for diverged branches
List local branches that have diverged from their remote tracking branch:
git for-each-ref --format='%(refname:short) %(upstream:track)' refs/heads/Flag any branches marked [ahead N, behind M] — these may need attention.
Exit condition: the local branch's relationship to origin/main has been reported; main has been pulled only if it was already checked out and new commits exist; diverged branches have been flagged, not acted on.
Phase 4 — Prune Merged Branches
4a. Find merged branches
Do not hand-roll this with
git branch --merged origin/main— same reason as Phase 1c: in a repo that squash-merges (confirmed live in this repo, issue #2309), a landed branch's tip is never an ancestor of the default branch, so--mergedreports almost nothing as merged even right after a run that squash-merged a dozen PRs. Resolve merged-ness from GitHub PR state instead, exactly like Phase 1c/gc-worktrees.shalready do:
# name + current tip SHA, not name alone: a branch name can be reused after its earlier
# PR merged (a later /fixer-style run, or a human, creating a new branch of the same
# name for unrelated work) — matching by name only would then classify that NEW, never-
# merged work as "confirmed merged" and hand it straight to 4d's force-delete. Requiring
# the local branch's CURRENT tip to equal the exact SHA GitHub recorded as the merged
# PR's head closes that gap: any commit added since (a rename, a reused name, a new
# unpushed commit) changes the tip SHA, so the pair no longer matches and 4d leaves the
# branch alone instead of guessing (Greptile review, PR #2311).
git branch --format='%(refname:short) %(objectname)' | grep -v '^main ' | sort > /tmp/housekeep-local-branches.txt
gh pr list --state merged --limit 1000 --json headRefName,headRefOid \
--jq '.[] | "\(.headRefName) \(.headRefOid)"' | sort > /tmp/housekeep-merged-heads.txt
comm -12 /tmp/housekeep-local-branches.txt /tmp/housekeep-merged-heads.txtThis lists local branches whose name and current commit match a PR GitHub reports as merged — true regardless of merge strategy (merge commit, squash, or rebase), unlike ancestry, and immune to name reuse since it's pinned to the exact commit GitHub recorded at merge time.
4b. Safe to delete
Branches that are:
- Confirmed merged via 4a's name-and-SHA check against
gh pr list(not the ancestry test, and not a name-only match) - Not
mainitself - Not the current branch
- Not a worktree branch (check
git worktree list)
4c. Prune remote tracking refs
git remote prune originThis removes local refs to branches that no longer exist on the remote.
4d. Clean up
If DRY_RUN: List branches that would be deleted.
Otherwise: For each branch 4a confirmed merged, ask the user for confirmation before deleting:
Delete merged branch '<branch>'? (y/n)If confirmed, delete the branch:
git branch -D "<branch>"Use
-Dhere, not-d. This is not a blanket force-delete: 4a already established via authoritative GitHub PR state — not git's own ancestry heuristic — that this exact branch is merged.-d's ancestry check is precisely the thing that's unreliable under squash/rebase merges (verified directly: it refuses a genuinely squash-merged branch once its stale remote-tracking ref has been pruned, e.g. by 4c), so requiring it to also agree before deleting is not an extra safety margin — it's reintroducing the exact bug 4a exists to fix. The safety boundary here is 4a's confirmed-merged check plus the per-branch confirmation prompt below, not-d's ancestry test. Always confirm before deleting — consistent with worktree removal in Phase 1c.
Exit condition: every fully-merged, non-protected branch has had a confirmed delete decision (or been listed under DRY_RUN); stale remote-tracking refs have been pruned.
Phase 5 — Update Codegraph
Skip if SKIP_UPDATE is set.
Source-repo guard: This phase is only meaningful when codegraph is installed as a dependency of a consumer project. Because the pre-flight confirms we are inside the codegraph source repo (
"name": "@optave/codegraph"), comparing the dev version to the published release and runningnpm installwould be a no-op — codegraph is not one of its own dependencies. Skip this entire phase when running inside the source repo and print:Codegraph: skipped (running inside source repo — update via git pull / branch sync instead)
Exit condition: either the phase was skipped (source-repo guard or SKIP_UPDATE) and that is printed, or codegraph's version status has been reported.
Phase 6 — Verify Repo Health
Quick health checks to catch issues:
6a. Graph integrity
npx codegraph statsIf the graph is stale (built from a different commit), rebuild:
npx codegraph build6b. Node modules integrity
npm ls --depth=0 2>&1 | grep -cE "missing|invalid|WARN"If issues found: npm install to fix.
6c. Git integrity
git fsck --no-dangling 2>&1 | head -20Flag any errors (rare but important).
Exit condition: graph integrity, node-modules integrity, and git integrity have each produced a pass/fail result.
Phase 7 — Report
Print a summary to the console (no file needed — this is a local maintenance task):
=== Housekeeping Report ===
Worktrees: total .claude/worktrees/ size 57G (32 worktrees)
removed 2 stale (4.2G freed), 1 has uncommitted work (skipped)
cleaned build artifacts in 3 active worktrees (9.6G freed)
Dirt files: cleaned 5 temp files (12KB), 1 large untracked flagged
Branches: pruned 3 merged branches, 2 remote refs
Main sync: up to date (or: 4 commits behind — merge suggested)
Codegraph: v3.1.2 → v3.1.3 updated (or: already latest)
Graph: rebuilt (was stale) (or: fresh)
Node mods: OK (or: fixed 2 missing deps)
Git: OK
Status: CLEAN ✓Always include the worktree total at the top of the Worktrees line, even when no worktrees were removed. This is the metric that surfaces hidden disk bloat — without it, multi-GB worktree accumulations go invisible to the user.
If DRY_RUN: prefix with [DRY RUN] and show what would happen without doing it.
Exit condition: the report above has been printed; nothing further runs after it.
Examples
/housekeep— full local cleanup: prune stale/bloated worktrees, remove regeneratable dirt, sync with main, prune merged branches, verify health./housekeep --dry-run— preview everything above with zero changes./housekeep --skip-update— run the cleanup phases but skip the codegraph-version-check phase.
Rules
- Never delete a branch without first confirming its current tip commit matches a merged PR's recorded head SHA via
gh pr list— notgit branch --merged/-d's ancestry test (unreliable under squash/rebase merges, issue #2309), and not a branch-name match alone (a reused name with new, never-merged commits would wrongly qualify, issue #2311).-Dis correct, not reckless, once that name-and-SHA check confirms the exact commit was merged (see Phase 1c and Phase 4) - Never rebase — sync with main via merge only (per project rules)
- Never delete tracked files — only clean untracked/ignored dirt
- Never delete worktrees with uncommitted changes — warn and skip
- Always report worktree disk usage — even when nothing is removed, the total must appear in the report. Worktree bloat is the #1 source of disk waste in this repo
- Bloated-but-active worktrees: only delete the four regeneratable artifact paths (
node_modules/,target/,dist/,.codegraph/graph.db*). Never touch source files in a worktree you don't own - Never target the root repo's shared
node_moduleswhen manually copying/linking something in for local testing (a rebuilt native addon,npm link, a patched dependency) — after stripping a worktree's ownnode_modulesin this phase, that worktree silently falls back to the root's copy, so touching the root affects every worktree currently depending on that fallback, not just the one you're working on - Ask before deleting large untracked files — they might be intentional
- This is a local-only operation — no pushes, no remote modifications, no PR creation
- Idempotent — running twice should be safe (second run finds nothing to clean)
--dry-runis sacred — it must NEVER modify anything, only report
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

