batch
Dispatch a night's batch of Todo tickets — one worktree and one pull request each — and leave a morning summary. Run explicitly; never inferred.
Clean up after a merged pull request — return to the base branch, remove the worktree, delete the branch, sweep other stale branches and worktrees, and report tracker state. Use after a merge or whenever asked what is left over.
$ npx -y skills add mikestankavich/claude-ship-workflow --skill cleanup --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/cleanupContext preview
The summary Claude sees to decide when to auto-load this skill.
Clean up after a merged pull request — return to the base branch, remove the worktree, delete the branch, sweep other stale branches and worktrees, and report tracker state. Use after a merge or whenever asked what is left over.
name: cleanup description: Clean up after a merged pull request — return to the base branch, remove the worktree, delete the branch, sweep other stale branches and worktrees, and report tracker state. Use after a merge or whenever asked what is left over. when_to_use: "clean up the worktree and merged branches", "any remaining worktrees?", "delete merged branches", after csw:merge lands a PR
**Announce at start:** "Using csw:cleanup to close out <ticket>."
Once the merge is confirmed, branch and worktree cleanup happens without asking. Closing a ticket always asks. Those two rules are not symmetric and the asymmetry is deliberate. Confirming the merge is itself the one precondition cleanup checks before it removes anything — that check is not optional, even though most of the time it passes instantly.
Before anything is removed, establish that the PR for this branch is actually merged:
gh pr view --json state,mergedAt
If this run was chained straight from **csw:merge**, the merge is already confirmed there — say so, and this check simply passes; the chained path stays frictionless.
The only thing that authorises deletion is `gh pr view` reporting `state: MERGED`. Anything else is a stop-and-ask: a non-merged state, no PR for this branch, or **the command failing for any reason** — no auth, no configured remote, a network error, or anything else. Command failure is not "no signal, treat it as merged" — it is exactly the same stop as an explicit non-merged state. No substitute may be used to establish the merge instead: not `git log --merges`, not `git branch --merged`, not reading the PR page. Only `gh pr view` reporting `state: MERGED` counts.
If the check does not clear, **stop**. Name the branch and what you found — including the raw error if the command itself failed — and ask whether to clean up anyway. This is the one case where branch and worktree cleanup asks: `git branch -d` refuses an unmerged branch on its own, but nothing stops `git worktree remove` from deleting the checkout that holds someone's unlanded work, and the worktree carries no such protection of its own.
Once the merge is confirmed:
git rev-parse --show-toplevel # this worktree's path git branch --show-current # the branch about to be deleted csw-config get baseBranch
Capture all three now. Step 2 changes directory and Step 3 needs these values.
csw-services stop "<path from Step 1>"
**This never asks**, for exactly the reason branch cleanup never asks. Removing the worktree is bookkeeping, and so is stopping the things that lived in it. Every argument for deleting the worktree unprompted applies unchanged to the processes inside it — and asking is how it gets forgotten, while a forgotten one goes on answering `:5432` for the next ten hours.
**Cleanup is what manufactures these orphans.** It does not merely fail to tidy up: removing the worktree is the moment those processes stop having a valid home and become indistinguishable from the live session's. The cost is not the wasted process — it is a test run going green against a backend, frontend or **database** belonging to a different branch, with every precondition check passing. And a healthy orphan does not get ignored, it gets **adopted**: the next stack finds it present, healthy and on the expected port, attaches rather than starting its own, and then correctly disclaims something it did not start. Every subsequent session reaches the same conclusion, which is what makes an orphan permanent.
**It runs before the worktree is removed, and that ordering is load-bearing three times over.** A graceful teardown often has to run from inside the directory; once the directory is gone the compose file goes with it; and a container that bind-mounts the worktree can leave root-owned files behind that a later non-root removal cannot delete. Step 2 is where `ExitWorktree` removes the directory, so this is the last moment any of that is still possible.
`csw-services` names everything **before** it signals anything, so an unprompted destructive step is still reviewable. **Pass that on, including the empty case** — `nothing running from <path>` is an answer, and silence is indistinguishable from not having checked. If it names something that looks like a test harness mid-run, say so in the report: losing one costs a measurement, and the human should learn that from you rather than from a result that never arrives.
**Report the `holding files open` section separately, and never treat it as done.** Those processes are *not* stopped, deliberately — an editor, an LSP or a `tail -f` from another terminal did not come from this worktree, and killing one is a blast radius this must not have. But removal still **breaks** them, and silently: the descriptor goes on pointing at a deleted inode, and an inotify watch on a deleted directory never fires again, so the editor holds a buffer it can no longer save and the watcher stops rebuilding with no error anywhere. That is a different failure from an orphan — an orphan survives and gets wrongly adopted, a holder survives and stops working. Name them and let the human decide; do not go looking for them to kill.
Its scope is one path. It stops what came from *this* worktree and nothing else. **It is not a zombie reaper**, and there is no machine-wide mode to reach for. That boundary makes acting without asking safe: what it might touch is answerable in one sentence. A supervised `systemctl --user` unit, a buildkit container, an editor's MCP server and a database started from the main checkout are all out of scope for the same reason rather than four different ones.
If `csw-services` itself fails — not "found nothing", but exited non-zero — report that and carry on with the cleanup. Removal is still correct; what is unknown i
CSW takes a ticket from your tracker to a merged pull request, then cleans up after itself. Superpowers owns how the work gets done. CSW owns *how it gets shipped and closed out*. This is one person's idiosyncratic workflow, and it is probably not yours.
Dispatch a night's batch of Todo tickets — one worktree and one pull request each — and leave a morning summary. Run explicitly; never inferred.
Merge the reviewed pull request for the current branch, then hand straight off to cleanup. A sentence of editorial direction alongside the green light is read…
Spec a ticket before it is dispatched. Brainstorms it, decides everything it can recommend an answer for, asks a present human about the little that survives,…
Dispatch a tracker ticket into an isolated worktree and drive it autonomously to an open pull request, then stop for review. Pass `interactive` to brainstorm…