Tidy scratch — date-aware GC for the scratch notebook
A read → propose → execute sweep. This is the only ccx skill that proposes destructive actions; save-state stays a pure read-only dashboard. The sweep is dry-run by default — it prints a plan and touches nothing until you confirm. If the scratch dir is not version-controlled (the usual setup), a delete is permanent. When in doubt: archive (move), don't delete.
Live data (injected at load)
Project + config:
!bun "${CLAUDE_PLUGIN_ROOT}/scripts/where.ts"
Now: !date "+%Y-%m-%d %H:%M %Z"
Folder scan — date-aware, stalest first (newest-file age per folder; STATE=NO = no handoff doc):
!bun "${CLAUDE_PLUGIN_ROOT}/scripts/scan.ts"
Open PRs (a matching open PR keeps a thread alive):
!gh pr list --limit 30 2>/dev/null || echo "(gh unavailable / not a GitHub repo)"
Then do this
Classify every folder from the scan. With
ticket_system: nonethere is no external "closed" oracle — only age + PR signals exist, so stay patient and conservative.age= the scan'sage_days; thresholds are defaults you may adjust per item when presenting the plan:- KEEP (active) —
age < 3(protects an active spike), OR an open PR that matches the thread's topic/branch. - ARCHIVE (likely done) — has a STATE doc AND
age > 30AND no open PR. (Longer threshold than a ticket-oracle setup would use: age alone must be more patient.) - ADOPT (unfiled work) —
STATE = NOANDfiles > 3— too much accumulated work to be throwaway; propose creating a STATE doc for it (offer /ccx:start-thread), never delete in this pass. - DELETE (throwaway) —
STATE = NOANDage > 14ANDfiles ≤ 3(no handoff value + cold + tiny). - DELETE (empty) —
files = 0. - FLAG → default KEEP (unsure) — anything that fits no rule above, and every
kind: hubreference note. Never auto-propose deleting a folder that has a STATE doc.
- KEEP (active) —
Print the PLAN — execute NOTHING yet. One table, stalest first:
item class proposed action why last edit (age) End with a one-line tally:
N delete · M archive · K adopt · rest keep.Confirm. Ask: apply all, a subset (named), or none. Wait for the answer.
Execute only what was approved, echoing each action (paths from the injected config block):
- archive:
mkdir -p <scratch_root>/<archive_dir> && mv <scratch_root>/<slug> <scratch_root>/<archive_dir>/, then date-stamp:printf '\n_Archived %s by ccx tidy-scratch._\n' "$(date +%Y-%m-%d)" >> <scratch_root>/<archive_dir>/<slug>/<state_basename> - delete:
rm -rf <scratch_root>/<slug>— permanent if the notebook isn't version-controlled.
- archive:
Reindex. Run /ccx:save-state so the INDEX reflects the cleanup. Then a ≤3-line summary: deleted / archived counts and the new active-thread count.
Safety rails (non-negotiable)
- Dry-run is mandatory — never delete, move, or prune before the step-3 confirmation.
- Hard
rmis only ever proposed for folders with no STATE doc AND ≤3 files. Anything carrying a STATE — or carrying real bulk (>3 files) — is archived or adopted, never deleted; its work survives. - When unsure, archive, don't delete — without a ticket system there is no authoritative "this is finished" signal, only age.
- Reads the clock every run (
scan.ts+date) — no date is ever hardcoded, not even the model's notion of "today". Run it next month and the ages come out next-month-correct. - Archived threads drop off the save-state dashboard (scans skip the archive dir). A graph view may still show them until the archive dir is excluded from its filter — one-time setup, out of scope for this per-run GC.