Skip to content

/sdlc:project-cleanup

Generated from solutions/ontological/skills/project-cleanup/SKILL.md.

Sweep local branches, worktrees, remote branches, and SDLC task linkage to identify what can safely be cleaned up. The deterministic sdlc project cleanup scanner gathers facts and pre-classifies each item; this skill dispatches sub-agents in parallel to investigate ambiguous cases, then presents a single batched plan for one-shot approval before executing.

  • Read
  • Bash
  • Agent
  • AskUserQuestion

Usage:

  • /sdlc:project-cleanup — full scan + investigation + batched approval + execute.
  • /sdlc:project-cleanup --dry-run — same as above but never executes; reports the plan and stops.
  • /sdlc:project-cleanup --scope <list> — restrict to one or more of local-branches,worktrees,remote-branches,task-linkage.
  • /sdlc:project-cleanup --no-gh — skip GitHub PR lookups.
  • /sdlc:project-cleanup --no-investigate — skip sub-agent investigation; ambiguous items remain unclassified and are listed for the user to handle manually.

Project context (don’t re-derive every run):

  • The deterministic scanner is sdlc project cleanup. It only reads state — it never deletes branches, removes worktrees, or pushes anything.
  • The scanner pre-classifies every artifact into a safe-to-delete / safe-to-keep / needs-investigation bucket; Step 2 resolves the needs-investigation ones. See the op doc-comment (solutions/ontological/lib/services/project/ops/cleanup.ts) and sdlc project cleanup --help.
  • The skill never deletes anything until the user has approved the full plan in one prompt.

Run the deterministic scanner and capture its JSON:

${CLAUDE_PLUGIN_ROOT}cli/sdlc project cleanup --json <scope/flags>

Pass through any --scope / --no-gh flags from the user invocation. Always use --json here — the structured payload is what the rest of the skill consumes.

Parse the JSON. For the full output shape see the op doc-comment (solutions/ontological/lib/services/project/ops/cleanup.ts) and sdlc project cleanup --help. The keys this skill drives directly:

  • markdownReport: a full pre-rendered markdown report (repo header, a per-category ## Summary, then the items grouped by classification) — print this verbatim to the user. Do not re-derive or reformat it; the op owns the wording so successive runs read consistently.
  • summary: per-category totals (safe-to-delete / safe-to-keep / needs-investigation) — drive the empty-backlog check below off this.
  • items: each item has category, id, classification, action, reason, facts, commands, and optionally investigation_prompt — Step 2 reads these.

If summary shows zero safe-to-delete AND zero needs-investigation across all categories, print Nothing to clean up. and exit.

2. Investigate ambiguous items in parallel

Section titled “2. Investigate ambiguous items in parallel”

For every item where classification == "needs-investigation", dispatch one sub-agent per item in a single message so they run concurrently.

Skip this step entirely if --no-investigate was passed. In that case, treat every needs-investigation item as if the agent had returned verdict: defer (see below) — they pass through to Step 3 as “leave alone, surface to user.”

Each sub-agent prompt must include:

  • Repo root (repo.root from the JSON).
  • The single item this agent owns (its id, category, facts, and investigation_prompt).
  • The verdict contract (below).
  • Permission to read files and run read-only git/gh commands. Sub-agents must not delete, push, or otherwise mutate state.

The agent does, in this order:

  1. Read the item’s facts and investigation_prompt.

  2. Run the full required probe set for the item’s category — every probe listed, not a subset, so the verdict rests on all available evidence and stays consistent across runs.

    local_branch (always run):

    • git log <branch> --oneline -10 — what’s on the branch?
    • git log <main>..<branch> --oneline — what’s unique vs main?
    • git diff <main>...<branch> --stat — divergence size.
    • If facts.pr is present: gh pr view <pr.number> --json state,reviewDecision,mergedAt,closedAt,comments.
    • If facts.task is present: read the task file end-to-end (status, completion_note, blocked sections).

    worktree (always run):

    • git -C <path> status --porcelain (re-verify dirty state at investigation time — the script’s count may be stale).
    • git -C <path> diff --stat — what’s actually been changed.
    • git -C <path> log <compare_against>..HEAD --oneline where compare_against is facts.compare_against.
    • ls <path> — recognise abandoned scaffolding vs real work.
    • If facts.pr is present: gh pr view <pr.number> --json state,reviewDecision,mergedAt,closedAt,comments.
    • If facts.task is present: read the task file end-to-end.

    remote_branch (always run):

    • git log <remote>/<main>..<remote>/<branch> --oneline — unique commits on the remote branch.
    • git diff <remote>/<main>...<remote>/<branch> --stat.
    • If facts.pr is present: gh pr view <pr.number> --json state,reviewDecision,mergedAt,closedAt,comments.
    • If facts.task is present: read the task file end-to-end.

    task_linkage (always run):

    • Read the task file end-to-end (status, completion_note, body — especially any <blocked> section).
    • git log --all --oneline -- <relative path of task file> — has the task itself been touched recently?
    • git log --all --oneline --grep '<task.slug>' — any commits referencing the slug?
    • For each candidate ref (task/<basename>, task/<slug>, <basename>): git rev-parse --verify --quiet refs/heads/<ref> and git ls-remote --heads <remote> <ref> to double-check the script’s branch-existence inference.
  3. Form a verdict from this menu (one only):

    • delete — safe to delete; the apparent disposability outweighs any remaining ambiguity. Must include commands (literal shell). Be explicit if force-delete (-D / --force) is required.
    • keep — worth preserving. Often “open PR pending”, “uncommitted work that should be pushed”, “task in-progress with a real direction”.
    • escalate — surface to the user with a specific question. Use when the choice depends on user intent the agent can’t infer (e.g. “this worktree has 4 uncommitted files; user may still want them”). Include a one-line question.
    • defer — leave it alone for this run; not worth either deleting or asking. Use sparingly.
  4. Return a strict JSON object (no surrounding prose) so the orchestrator can aggregate cleanly:

{
"id": "<the item id verbatim>",
"category": "<the item category verbatim>",
"verdict": "delete | keep | escalate | defer",
"reason": "<one sentence>",
"commands": ["<shell line>", "..."],
"question": "<only when verdict=escalate, else omit>",
"evidence": ["<bullet>", "..."]
}

Keep each agent’s response under 250 words. Use general-purpose for the subagent type unless the item is clearly code-review-shaped.

Send all Agent tool calls in a single message.

When every sub-agent returns, merge their verdicts back into the items list:

  • safe-to-delete items are unchanged.
  • safe-to-keep items are unchanged.
  • needs-investigation items adopt their agent’s verdict (delete / keep / escalate / defer). Replace reason and commands from the verdict.
  • If any sub-agent failed to return parseable JSON, treat that item as defer and note the failure in the plan.

Render a single markdown plan for the user. Group strictly by action so review is easy:

# Cleanup plan
## Will delete (<N>)
For each `delete` item across all categories:
- `<id>` — <reason>
- <command 1>
- <command 2>
## Needs your decision (<N>)
For each `escalate` item:
- `<id>` — <reason>
- Question: <question>
## Leaving alone (<N>)
For each `keep` and `defer` item, one line each — no commands.
## Final command list (will run on approval)
A fenced bash block containing every command from the "Will delete" section, in order.

Note any item where the proposed command is -D / --force explicitly — e.g. (force delete; branch not merged) — so the user can see the risk before approving.

Show the plan above, then ask the user one AskUserQuestion with these options:

  • Run the plan as shown (Recommended if the plan looks right) — executes every command in the final list.
  • Run only the safe-to-delete subset — executes only commands that came from the script’s deterministic safe-to-delete classification (skip sub-agent-promoted ones).
  • Cancel — don’t run anything — exit without mutating state.

If there are any escalate items, append a fourth option:

  • Walk escalations one-by-one first — drives a follow-up AskUserQuestion per escalation, then re-builds the plan with those answers folded in.

If --dry-run was passed, skip this prompt entirely and exit after Step 3.

Run each approved command via Bash, in order, from the repo root (repo.root from the JSON, not the worktree the skill is running in — git worktree remove etc. operate against the primary repo).

If a command fails:

  • Do NOT silently continue. Print the failure, the command, and the remaining queue.
  • Ask the user via AskUserQuestion whether to continue with the rest, retry, or stop. Default: stop.

When complete, re-run the scanner (--json again) and report the new summary so the user can see what shifted. If anything that was supposed to be deleted is still present, surface it.

6. Escalation walkthrough (only if the user chose that option in Step 4)

Section titled “6. Escalation walkthrough (only if the user chose that option in Step 4)”

For each escalate item, in order:

  1. Show the item’s id, reason, and the agent’s question.
  2. AskUserQuestion with three options: delete (run the proposed commands), keep (skip), or provide guidance (free-text via “Other”).
  3. Update the item’s verdict accordingly.

After walking all escalations, go back to Step 3 (rebuild the plan) and Step 4 (single approval).

  • Read-only by default everywhere except Step 5. Until the user approves the plan, no branch is deleted, no worktree is removed, no remote ref is touched.
  • Force-deletes are explicit. When the scanner’s proposed command uses -D / --force, the plan always labels it so the user can override (see the op doc-comment in solutions/ontological/lib/services/project/ops/cleanup.ts).
  • The current worktree is always kept. If you’re running the skill from inside .sdlc/worktrees/<x>/, that worktree is classified as safe-to-keep — it can be cleaned in a future run from main.
  • Sub-agents are read-only. They investigate and return a verdict; the orchestrator runs every mutating command.
  • Parallelism comes from one message. Step 2 dispatches every Agent call in a single response.
  • When in doubt, ask via AskUserQuestion rather than guessing — mis-deleting a branch is worse than asking one extra question, and the user can override any verdict.
  • Committing model-generated messages. If any approved command constructs a commit message, follow ${CLAUDE_PLUGIN_ROOT}conventions/commit-messages.md for the authoring mechanism.
  • Branch naming. When the scanner reports merged task/, docs/, and chore/ branches for cleanup, see ${CLAUDE_PLUGIN_ROOT}conventions/branch-naming.md for what each prefix means — a docs/<basename> spec branch may still have a task/<basename> implementation branch in flight.
  • Orphaned worktrees from a closed task. This skill is the recovery path for a worktree/feat branch left behind after its task was already marked closed/done. /sdlc:task-close-out no-ops on an already-closed task (it emits ALREADY-CLOSED and tears nothing down), so the leftover scaffolding lands here: the worktree scan classifies a closed/done-backed worktree as safe-to-delete and proposes its teardown for approval. Use --scope worktrees to target just that case.