Tidy up after shipped work — remove .minerva/worktrees/<date-slug>/ directories whose branches have been merged into the default branch, and prune the corresponding local branches. Idempotent: safe to run on a clean tree (reports zero items removed).
Usage
minerva:cleanup— sweep all merged worktrees + branches in the current repominerva:cleanup 005-add-payments— clean up only the named work unit (slug or path)minerva:cleanup --dry-run— list what would be removed and what reconciliation would do, changing nothing
Target resolution
Same pattern used by minerva:work, minerva:replan, minerva:promote, minerva:review, minerva:ship. Keep all six blocks in sync if you edit one. For minerva:cleanup specifically, the default mode (no argument) is "all merged worktrees" rather than a single target — but resolution rules apply when an argument is passed.
- Explicit argument (slug or path) → operate on just that work unit. Check both
.minerva/work/<date-slug>/and.minerva/worktrees/<date-slug>/. Required: the corresponding branch must be merged into default (see Merge detection). - No argument → scan all
.minerva/worktrees/*/directories and check each branch's merge state. Match both id forms —YYYY-MM-DD-<slug>and legacyNNN-<slug>. A glob anchored on digits-then-dash ([0-9][0-9][0-9]-*) does not match2026-08-09-slug, so a date-named worktree would be silently skipped and never cleaned up. - Non-git repo → report "not a git repo, nothing to clean up" and stop.
Pre-flight checks
Bail with a clear message on any failure:
- Git repo.
git rev-parse --is-inside-work-treereturns true. ghCLI available (optional but preferred). Falls back to local-only merge detection ifghis missing or unauthenticated.- Not currently inside a worktree being cleaned. If invoked from inside
.minerva/worktrees/<date-slug>/, that worktree cannot be removed while it's the current working tree. Report and ask the user tocdout (back to the main repo root) and re-run. Cleanup always operates from the parent repo — its job is to remove worktrees, so it must never be running inside one. (No lifecycle skill makes a worktree its working directory: the others address worktrees by.minerva/worktrees/<date-slug>/-prefixed paths, while cleanup removes them outright.)
Default-branch detection
Resolve once at the start:
git symbolic-ref refs/remotes/origin/HEAD→ parserefs/remotes/origin/<name>.- Fall back to
main, thenmaster.
Use the resolved value for all merge checks.
Merge detection per worktree
A worktree is safe to remove only once its branch has merged into the default branch. The full
detection protocol — including the squash-merge case a plain git branch --merged misses —
lives in references/merge-detection.md. Read it before removing anything.
On a phased unit teardown and reconciliation diverge — read references/phased-units.md before
tearing anything down. The rule itself is stated once, in references/merge-detection.md.
Orchestrated mode (--yes)
Mode argument: --yes
With an explicit single-unit argument --yes satisfies the confirmation gate below — how the
autonomous orchestrators invoke cleanup. Act on the argument, not on who you think is calling.
Confirmation gate
Before removing anything, present the list:
Will remove:
.minerva/worktrees/005-add-payments/ (branch 005-add-payments, merged via PR #42 on 2026-05-12)
.minerva/worktrees/006-add-ship-skill/ (branch 006-add-ship-skill, merged via PR #45 on 2026-05-15)
Will skip:
.minerva/worktrees/007-add-cleanup/ (branch 007-add-cleanup, NOT merged — unmerged work, leaving alone)
Ask:
"Remove these worktrees and prune the matching local branches? [y/N]"
Default is no — destructive operations require explicit yes. The user can also batch ("yes, all" / "just the first two" / "skip 006").
Skip this gate when --dry-run is set (nothing destructive happens) or when the user invokes with an explicit single-unit argument and says --yes (e.g. minerva:cleanup 005-add-payments --yes).
Removal
Conservative by design — each step prefers to refuse and surface rather than force, because
cleanup's failure mode is destroying unmerged work. Read references/removal.md before removing anything; it carries the worktree removal, the -d/-D branch-delete rule and its squash-merge rationale, the phased-unit branch prune, and the metadata prune.
Final report
Worktrees removed: N (<list>)
Branches pruned: N (<list>)
Skipped (unmerged): N (<list with branch names>)
Skipped (uncommitted): N (<list — needs manual review>)
Remaining worktrees: N (<list>)
If any worktrees were skipped due to uncommitted changes, recommend the user inspect each (cd .minerva/worktrees/<slug>; git status) and decide whether the changes are valuable.
Knowledge reconciliation
Because minerva:promote is add-only — it writes new knowledge entries on a work-unit branch and touches no aggregate — the index.md catalog lines, watermark, reciprocal ## Related links, supersession banners, and overview.md are all written here, on the default branch, where there is one writer at a time. This is what makes concurrent minerva PRs conflict-free.
Run it on every invocation, decoupled from worktree removal: a merge done through the GitHub UI leaves pending entries with no worktree to remove. It is cheap and silent when nothing is pending. Exception: a repo that reconciles in CI (Step 0).
The full protocol — the deterministic pending/un-synthesized signal, the at-most-one-open-PR rule, the throwaway worktree, the knowledge_fix + minerva:synthesize pass, and the auto-merging PR — lives in references/reconciliation.md. Read it before reconciling. Three rules bind even before you read it: never commit to the default branch directly (reconciliation always goes through its own PR); if gh pr merge --auto is rejected, report the PR URL and stop rather than merging another way; and never end a run leaving entries uncatalogued without naming them — if a reconciliation PR is already open, wait for it and reconcile what remains, and if it never merges, list every still-pending entry stem under Pending, NOT catalogued. A run that leaves entries invisible must not report itself clean.
Idempotency
Cleanup is stateless. Re-running on a fresh tree finds zero candidates and reports zero removed. Reconciliation is likewise idempotent — knowledge_fix is a byte-level no-op on an already-reconciled corpus, so a second run reports nothing pending. Running mid-CI for a branch with an auto-merge pending will correctly skip that worktree (PR is OPEN, not MERGED).
If a user manually removed a worktree directory without running git worktree remove, the next minerva:cleanup call will see stale worktree metadata; git worktree prune at the end of the run handles this.
Out of scope
- Removing the work-unit's
.minerva/work/<date-slug>/directory frommain. The docs already moved into the worktree atminerva:worktime and were merged intomainvia the PR; the canonical record lives at.minerva/work/<date-slug>/post-merge.cleanuponly removes the worktree, not the merged docs. - Removing knowledge files.
.minerva/knowledge/entries are permanent by design. - Force-removing unmerged work. Always requires explicit user override; cleanup is conservative by default.
- Pruning remote branches. GitHub usually auto-deletes the source branch after merge if the repo is configured for it. Local prune handles the local side; remote prune is
git fetch --pruneand not part of this skill.