| name | tidying-worktrees |
| description | Sweeps every fleet repo and removes spent git worktrees — clean trees whose branch is fully merged into the remote base, or gone from the remote with nothing unpushed. Conservative and no-prompt: a dirty worktree, or one carrying unpushed commits, is always kept (it may be a parallel session's live work). Use for periodic low-friction care of the fleet's worktree clutter, or before a cascade wave to clear interrupted-wave leftovers. Defaults to dry-run; pass --fix to act. |
tidying-worktrees
Interrupted cascade waves and finished tasks leave spent worktrees scattered
across the fleet (chore/wheelhouse-<sha> leftovers, merged feature branches,
abandoned ci-cascade-layer trees). Cleaning each by hand is friction. This
skill is the fleet-wide, conservative, no-prompt sweep — safe to run unattended.
It is the fleet-wide sibling of managing-worktrees (which prunes the current
repo). Both share one removability predicate (decideWorktree in
lib/tidy-worktrees.mts); this engine iterates the canonical roster.
When to use
- Periodic care. Run on a
/loop so worktree clutter never accumulates.
- Before a cascade wave. Clear interrupted-wave leftovers so a fresh wave
starts from a clean fleet.
- After a batch of merges. Reclaim the merged-but-not-deleted branches.
Run it
node .claude/skills/fleet/tidying-worktrees/lib/tidy-worktrees.mts
node .claude/skills/fleet/tidying-worktrees/lib/tidy-worktrees.mts --fix
node .claude/skills/fleet/tidying-worktrees/lib/tidy-worktrees.mts --fix --repo socket-cli
The engine reads the canonical roster from
cascading-fleet/lib/fleet-repos.txt (1 path, 1 reference — never a second
roster) and resolves sibling repos under $PROJECTS (default ~/projects).
Periodic, no-prompt operation
For background care, drive it with /loop:
/loop 6h /fleet:tidying-worktrees --fix
Every 6 hours it sweeps the fleet and removes only provably-spent worktrees.
Nothing to remove → it says so and exits. It never prompts: the conservative
predicate means an unattended --fix can only ever remove worktrees with no
work to lose.
Removability contract (conservative by construction)
A non-primary worktree is removed ONLY when its tree is clean AND it has
nothing left to land, where "nothing to land" means EITHER:
- its branch is fully merged into
origin/<base> (every commit is already
an ancestor — spent), OR
- its branch is gone from the remote AND the worktree is not ahead of
the base (a never-shared local branch with no unpushed commits).
Everything else is kept:
- dirty → may be live work, never auto-removed;
- ahead of base → carries unpushed commits (this guard is load-bearing: a
workflow's local-only isolation worktree reads as "branch gone from remote"
yet may hold unpushed work — removing it would lose that work);
- on remote with unlanded commits → a real open branch.
Gotchas the engine handles
- Submodule worktrees.
git worktree remove refuses a worktree containing
submodules even when clean. The engine passes --force only after the
clean-tree check, so it clears the submodule guard without discarding work.
- Relink after removal. A
git worktree remove can dangle the primary
checkout's node_modules symlinks. After a --fix that removed anything, run
pnpm i in each affected repo's primary checkout (the engine names them).
- Default branch fallback. Base resolves via
git symbolic-ref refs/remotes/origin/HEAD → main → master. Never
hard-coded.
Safety contract
- Parallel Claude sessions / Don't leave the worktree dirty: never removes
a dirty or ahead-of-base worktree — only provably-spent ones.
- Default branch fallback: every base lookup follows
main → master.
- 1 path, 1 reference: the roster + the removability predicate each live in
exactly one place;
managing-worktrees and this skill share them.