Clean up
π§ One batter, every repo β reusable AI agent & skill definitions rendered into harness-native files (.claude/, .codex/, .agents/)
npx -y skills add dustinkeeton/wafflestack --skill clean-upAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Tidy up after a finished process: delete local git branches and worktrees whose pull request has already merged, prune stale remote-tracking refs, and spin down background tasks and agents that have completed their work. Use this whenever one or more PRs have just merged, after a /delegate run wraps up, or when the user says things like "clean up", "clean up the merged branches", "remove stale worktrees", "tidy up my branches", or "we're done, spin down the agents". This is git + agent housekeeping β NOT source-code cleanup or refactoring.
SKILL.md
10.9 KB, as published. Nobody here has run it
Clean Up
Housekeeping after work has landed. Two independent domains:
- Git β local branches and worktrees whose PR is merged, plus a
git fetch --prune. - Harness β background tasks and agents that have finished their work.
The guiding principle is that cleanup should only ever remove things whose work is already safe elsewhere β a branch whose PR merged, a task that completed. Anything still in flight, or holding the only copy of some work, is left alone. Because the destructive half (force-deleting branches, stopping agents) can't be undone, the default is to show the full plan and wait for a yes before touching anything.
Arguments
| Arg | Effect |
|---|---|
| (none) | Full sweep β git and harness β previewed, then confirmed. |
git / branches / worktrees | Git scope only. |
agents / tasks | Harness scope only. |
--yes / auto | Skip the confirmation prompt. Intended for an agent calling this right after it merges a PR β not for interactive use unless the user explicitly says "no need to confirm". |
--yes combines with a scope (e.g. git --yes).
Post-merge convention
git --yes is the built-in path for the merging agent: right after it merges a PR, it runs
clean-up git --yes
to delete the just-merged local branch and worktree and prune stale remote-tracking refs β no
confirmation prompt, because the agent already knows the PR merged. This is step 3 of the
git-workflow "After a PR merges" flow; its sibling steps (verify the
linked issues closed, reconcile their board Status to Done) are a local-agent job too,
handled there via the github-project-management skill or the project-manager agent β none of
it is wired into CI. The one remote-side action that can be a CI job β deleting the merged head
branch on the remote β is the optional waffle-post-merge-hook workflow; this skill still owns
everything local.
The workflow
- Parse scope from the argument. Default to the full sweep.
- Build the git plan (if git is in scope) β run the bundled scanner in dry-run mode:
It prints which branches and worktrees are stale, which it deliberately skipped, and whether it will fast-forward the default branch. It deletes nothing in this mode.bash .claude/skills/clean-up/scripts/clean_up.sh - Build the harness plan (if harness is in scope) β see Harness scope below.
- Present the combined plan to the user using the report format.
- Gate on confirmation. Unless
--yeswas passed, ask "Proceed?" and wait. If the user vetoes specific items (e.g. "keep the dependabot worktree"), honor that β drop them from the plan and proceed with the rest. - Execute the confirmed plan (Git scope + Harness scope).
- Report what was actually removed.
Git scope
All git logic lives in scripts/clean_up.sh so the dry-run is provably read-only and the
execute path is identical every time. Always go through the script β don't hand-roll
git branch -D loops, because the staleness check is subtle:
git branch --merged mainis not a reliable signal for which branches have merged. Depending on the merge method, a merged branch's commits may never land onmainunder their original SHAs β squash and rebase merges rewrite them β sogit branch --merged maincan report nothing and miss merged branches. The script instead asks GitHub for merged PR state viagh, which is the authoritative signal regardless of how the PR was merged.
Run it dry-run to get the plan, then with --execute once confirmed:
bash .claude/skills/clean-up/scripts/clean_up.sh # dry run β the plan
bash .claude/skills/clean-up/scripts/clean_up.sh --execute # remove worktrees, delete branches, prune
What the script guarantees, so you don't have to re-check it:
- Only branches whose PR is merged are in scope (open / closed-without-merge are left alone).
- The default branch and the branch/worktree you're currently on are never switched
away from or deleted. On
--execute, if you're currently on the default branch and the working tree is clean, it is fast-forwarded to the freshly-fetched remote tip (fast-forward only β never a merge commit, never a branch switch). - A merged branch with un-pushed commits (ahead of its upstream) is skipped and listed under "needs a human look" β force-deleting it could lose those commits.
- Worktrees are removed with plain
git worktree remove(no--force); a dirty worktree is skipped and reported rather than discarded, and its branch is then left in place too. --executerunsgit fetch --pruneto droporigin/*refs for branches that no longer exist on the remote.- After the prune,
--executefast-forwards the local default branch to the just-fetchedorigin/<default>β but only when you're currently on it and the working tree is clean, and only as a fast-forward (never a merge commit, never a branch switch). It reports one of:fast-forwarded to <sha>,skipped: not on <default>, orskipped: diverged or dirty.
Out of scope by design: deleting the remote branch on GitHub (merges usually auto-delete it, and we don't want to touch the remote), and closed-but-unmerged branches (that work never landed β removing it is a judgment call the user should make deliberately, not a sweep).
Harness scope
Use the task and agent tools to find work that has wrapped up. The aim mirrors the git side: stop things that are done, never things still running.
There are no teams to hunt for: the session has a single implicit team, and TeamCreate /
TeamDelete no longer exist. What outlives its work is an agent, and an agent is stopped by
name.
-
Enumerate the tasks with
TaskList(it takes no arguments and lists every task in the session). Note each task'ssubject,status, andowner. -
Finished background tasks β for any task that is
completed(or is plainly idle/abandoned with no further use), stop it:TaskStop(task_id: "<id>")Never stop a task that is
in_progressβ that would kill live work. -
Finished agents. An agent is stopped by name β so first you need the names, and this is the step's real problem: the harness has no agent enumeration. There is no
AgentList;TaskListlists tasks, not agents, and it will not show you an agent that never held one (autopilot's gate agents, for instance, are spawned without a task). A task'sownernames an agent when something set it β but nothing in this toolkit's flows does, so it is usually empty. Do not treat an emptyTaskListas proof that no agent is running.The names therefore come from the run that spawned them, not from a discovery call. That is exactly why each orchestrator here names its spawns deterministically:
Run Agent names Where the record is /delegateissue-<N>-<agent-type>the run's checkpoint β execution[]carries each issue'snumberandagent/auditthe fixed six-agent chain: architecture-pass,security-pass1, the compliance agent,docs-agent,docs-human,security-finalthe skill's roster /autopilotqa-pr<N>,respond-qa-pr<N>,review-pr<N>,respond-rev-pr<N>keyed to the PR number So: reconstruct the candidate names from whichever run you are cleaning up after (and ask the user if you are cleaning up after something else β an agent nobody recorded is invisible to this skill). Then, for each agent that has completed its work or is plainly idle and abandoned, ask it to wind down and confirm the kill:
SendMessage(to: "<agent-name>", message: {type: "shutdown_request", reason: "Cleanup: work complete"}) TaskStop(task_id: "<agent-name>")shutdown_requestalone is not reliable β an agent can go idle but stay alive.TaskStopis what actually terminates it, and it is safe on an agent that has already exited (so a name you are unsure about costs nothing to try). Never stop an agent that is still doing live work.Report what you could not see. If you have no record of what was spawned, say so β "no run record available; agents not swept" β rather than reporting
Agents stopped: (none). The two read identically to a user and mean opposite things, and the second one is how a leaked agent goes unnoticed.
Crons are out of scope. Scheduled jobs (CronList) are almost always intentional recurring
work, not leftover state, so cleanup never deletes them. If you suspect a cron is genuinely
obsolete, surface it to the user as a note β don't remove it.
Report format
Keep the preview and the final report in the same shape so the user can diff "planned" against "done" at a glance:
clean-up β <scope> (<dry run / executed>)
Branches (PR merged):
<branch> -> PR #<n> MERGED
...
Worktrees:
<path> (<branch>)
Skipped (needs a human look):
<branch> <reason>
Default branch: fast-forwarded to <sha> (or: skipped (not on <default> / diverged / dirty))
Tasks stopped:
<task id / subject>
Agents stopped:
<agent-name> β <what it finished>
Left untouched: current branch, open/closed-unmerged PRs, crons.
In dry-run/preview, end with "Proceed? (y/N)". After executing, end with a one-line summary (e.g. "Removed 4 branches, 1 worktree; stopped 2 tasks and 1 agent.").
Edge cases
- Nothing stale β say so plainly ("Nothing to clean up β no merged branches, no finished agents.") and stop. Don't invent work.
ghmissing or not authenticated β the script exits non-zero with a clear message. Relay it; do not fall back togit branch --merged(it can miss merged branches depending on the merge method) or guess.- A worktree the user actively reuses (e.g. a long-lived dependabot scratch worktree) may be flagged because its setup PR merged. That's exactly what the confirm step is for β if the user says keep it, drop it from the plan.
- Detached HEAD β the script handles it (no current branch to protect); proceed normally.