Skip to content
open tomato/ rafa
GitHub
rafa pages
  1. rafa
  2. rafa cleanup

rafa cleanup

List merged, stale and unpushed branches, idle worktrees and old run records, and remove the ones you tick.

rafa cleanup fetches, lists what has piled up in groups, and removes only what you tick. The branch checked out, the base and anything a cleanup.keep glob matches are never listed, and a worktree that is dirty, locked or running a loop session cannot be ticked.

Spends: nothing. Changes: deletes the branches, worktrees and run records you tick; --dry-run lists and removes nothing.

NOTE

In the target layout this code lives in the private package rafa-run, bundled into @open-tomato/rafa (Repository layout). In 0.47.0 it is already a package, @open-tomato/rafa-run in packages/rafa-run, but only as a facade: it re-exports this code from the root package's src/, where the files still live until their move. You install and type the same either way. [verify: the release the move lands in]

Reference: rafa cleanup

List merged, stale and unpushed branches, unmerged branches only on origin, leftover worktrees and old run records; remove the ones ticked.

rafa cleanup [--dry-run]

Runs git fetch --prune, then lists the local branches, worktrees and run records that have piled up, and the unmerged branches that exist only on origin, in six groups, each row its name, its date and why it is listed: Merged (reachable from the base, its pull request merged per the provider, or its upstream gone), Stale (an upstream, not merged, no commit in cleanup.staleDays), Not pushed (no upstream, or commits ahead of it), Worktrees (under .claude/worktrees/, ~/.rafa/worktrees/ and loop.worktreeDir), Run records (the session records under .rafa/runs/, each named by its plan and the day it started, with its events file; a running or paused run and the newest run of each plan are never listed) and Only on origin (a branch origin holds and this clone does not, which the base does not reach; its row says how many of its commits have no patch-equal copy on the base, as git cherry counts them, or that every commit has a copy; never the head of an open pull request, a stretch/ branch or a claim branch another store holds, and with no pull request provider, or one that cannot be read, the group lists nothing and a note says why). The current branch, the base and every name a cleanup.keep glob matches are never listed. With a terminal the groups are one checklist: space ticks a row, a ticks a whole group, and Merged rows, clean worktrees on a Merged branch and run records start ticked, an Only on origin row never; a worktree that is dirty, locked, the current one, running a loop session or modified within cleanup.worktreeIdleDays cannot be ticked, and says why, and a Merged, Stale or Not-pushed branch such a worktree holds starts unticked, naming it, and is not deleted even when ticked: it is printed as not removed: with that worktree. A Merged branch listed only because its upstream is gone also starts unticked, saying the base does not reach its tip or, when its tip is past a merged pull request's head, <n> commits past #<pr>'s head: <subjects>. A ticked Not-pushed branch asks again, naming the commits deleting it loses, only when it would be deleted: one a worktree that cannot be ticked holds, or a ticked worktree holds, is asked nothing and printed as not removed: with why, under --dry-run too. Enter then asks Delete <n> local branches and <o> branches on origin, remove <m> worktrees and remove <r> run records? [y/N], the deletes on origin counted apart, and a yes runs git worktree remove for each worktree and git branch -d for each Merged branch, -D for a squash-merged one, for one whose commits past its pull request's head are release fragments the base already holds (any other such branch is not deleted, its commits named), and for a Stale or Not-pushed branch ticked and confirmed, then git push origin --delete <name> for each ticked Only on origin branch (one whose name opens with - is not deleted, and is printed as not removed: with the line to run by hand), then removes each run record and its events file. Nothing runs with --force, and that delete of a ticked Only on origin branch is the one thing removed on a remote: a Stale row names the git push origin --delete <b> to run by hand. It exits 1 when a step did not run clean. Without a terminal, or with --output=json, it prints the six groups, asks nothing and removes nothing, exiting 0; with --output=json they are the data of the terminal result event, the branches only on origin its remoteOnly. Starts no session.

FlagType and defaultWhat it does
--dry-runbooleanShow the checklist and ask as usual, then print the commands the answer would run instead of running them. Removes nothing.

Examples:

  • rafa cleanup: Lists the six groups and removes what is ticked, once the final question is answered yes.
  • rafa cleanup --dry-run: Prints the git and rm commands the ticked rows would run, a ticked Only on origin branch as its git push origin --delete <name>, running none of them.
  • rafa cleanup --output=json: Gives the six groups as data, asking nothing and removing nothing.

Output formats: text, json.