speckit-config-gc
Garbage-collect the harness: skills and subagents nothing invokes, unregistered hook scripts, duplicate permissions, retired instincts, stale telemetry ledgers, dead worktrees and expired trash. Propo
Files1
View on GitHubSKILL.md.claude/skills/speckit-config-gc/SKILL.md
GitHubUser Input
$ARGUMENTS
Why
Configuration is append-only in practice. Skills, hooks, permission entries and memory files only ever get added, and nothing ever reviews them — so a setup accumulates weight that costs context on every session and confuses every gate audit. This is the periodic sweep, borrowed from ECC's config-gc, with its rule kept intact: collection requires a human in the loop; never delete autonomously.
Phase 1 — Scan
node .claude/scripts/gc-scan.mjs
node .claude/scripts/gc-scan.mjs --jev # same candidates, least likely to be missed first
Channels it covers, and the signal each one uses:
| Channel | Candidate when |
|---|---|
| skills, agents | never invoked across the recorded sessions (.specify/telemetry/) |
| hooks | a script on disk that no registry.json entry names |
| permissions | duplicate entries, or an entry another rule already covers |
| instincts | retired, or faded below the injection floor |
| telemetry | a session ledger untouched for 90+ days |
| worktrees | a .worktrees/ directory whose branch is gone |
| trash | something in _gc_trash/ past its 30-day undo window |
With no telemetry recorded, the skills and agents channels stay silent on purpose: "never invoked" and "never measured" are different claims.
Phase 2 — Judge, then ask
Present candidates in small batches, grouped by channel, each with the
evidence and what you propose. Then take them one at a time — a per-item
y / n / skip. There is no "yes to all" here; a batch approval is how a setup
loses something it needed.
Before proposing any item, apply judgement the scan cannot:
- A gate that fires rarely is not dead. A release-time hook, a PreCompact flush, a reviewer subagent invoked once a feature — low counts are expected. Never propose deleting a gate; propose reviewing it.
- Never propose anything a constitution principle or CLAUDE.md names. If a documented thing looks unused, the finding is that the docs and reality disagree — report that instead.
- Anything under
specs/is project history, not garbage. Out of scope. - A skill with a real description and no invocations may simply be new. Check its git age before proposing it.
Phase 3 — Collect what was approved
Soft delete, in this order, never a straight rm:
Move the file or directory to
.specify/_gc_trash/<ISO date>-<name>.For a permission entry, remove the line from
.claude/settings.jsonand record the exact string in the log so it can be pasted back.For a hook, remove its
registry.jsonentry too, thennode .claude/scripts/doctor.mjs --bless-hooksand re-runnode .claude/scripts/doctor.mjs.Append to
.specify/gc-log.md:## <ISO date> - trashed `.claude/skills/foo` — not invoked in 12 sessions — undo: `git mv .specify/_gc_trash/2026-09-12-foo .claude/skills/foo`
Finish by running npm test && npm run lint && node .claude/scripts/doctor.mjs and
reporting what moved, what was kept, and where the undo lives.
Rules
- Nothing leaves the repo.
_gc_trash/is tracked like anything else, so an over-eager sweep is onegit mvaway from undone. - Never empty the trash in the same run that filled it.
- Never treat this command as a way to silence a failing gate — deleting the
hook that blocks you is the failure mode it is most able to cause. If a gate
is wrong, fix or retire it deliberately through
/speckit-evolve.