kvb-git-branch-gc becomes kvb-git-branch-cleanup and kvb-git-orphans-find becomes kvb-git-worktree-orphans, which drops its own orphans.py for pkgmgr's output. New wrappers: kvb-git-gc, kvb-git-worktree-cleanup and kvb-git-worktree-up. kvb-cleanup, kvb-dream, AGENTS.md and README.md point at the new names. Merges only after the pkgmgr release that ships these commands. 🎯 Fix: 90 % 💥 Break: 3 % 💸 Cost: new ⏱️ Time: new 🛡️ Security: 100 % 🏆 Score: 87 🚦 Gate: cost new, time new: cleared by the operator's commit word for the Skills wrappers (2026-11-07) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit 18dade9a48a84761bf0c3a9b35584367ae0cb0e4)
3.7 KiB
name, description
| name | description |
|---|---|
| kvb-git-worktree-orphans | Find abandoned branches and worktrees. |
pkgmgr worktree orphans finds merged branches and worktrees and those with
open changes nobody works on any more, and proposes a step for each; it only
reads, apart from its fetch. Nothing goes without the operator's pick in this
run.
When
On "verwaiste branches", "alte worktrees", "was kann weg"; from step 8 of
kvb-dream; from kvb-cleanup with --sizes when disk runs short.
Step 1: find
Per repository in scope, from inside it (--all: every managed one):
- No fetch of your own: pkgmgr fetches without auto-maintenance (the
kvb-git-branch-cleanupreason). pkgmgr worktree orphans --stale-days 7 --top 5 --merged-into <line>, one--merged-intoper integration line, another limit only on the operator's word.--sizesorders by worktree size (slow,du), only when disk matters.--jsonlists every row, active ones included.
Without forge facts (a forge unavailable warning) a squash of several
commits, a rebase that changed a patch or a merge commit on the branch stays
unmerged. Say so above the summaries.
| Class | Means | Proposal |
|---|---|---|
merged-clean |
merged, nothing dirty | kvb-git-gc |
merged-dirty |
merged, dirty files | ask |
stale-open |
unmerged, dirty or unpushed (no remote counts), idle beyond the limit, not busy | ask |
stale |
worktree metadata whose checkout is missing | report only |
active |
anything else; always the main worktree, protected lines and archive/ branches |
keep |
pkgmgr knows no claims: a row whose branch or worktree a live claim in
council.py list --long names becomes keep (claimed). Never offer a keep (process: ...) or keep (lock: ...) row. host_pid_namespace: false leaves
busy and locks unknown: say so. Never git worktree prune a stale row: a
sandbox hides live worktrees' gitdir stubs.
Step 2: propose
Print each repository's summary, then:
- merged-clean: the rows go to the
kvb-git-gcskill; its dry run, claims step and pick decide. A row its dry run does not list stays. - merged-dirty, stale-open (the
askrows): first one question through thekvb-think-choiceskill: "review abandoned items now?". No ends here. Yes: rerun with--jsoninto/tmp/orphans-<repo>-<N>.json, then rounds of at most four rows in summary order, taken from that file, one question per row naming what a delete loses (dirty files, unpushed commits):archive(recommended, loses nothing): the owner commits the changes, thengit branch -m <branch> archive/<branch>andgit worktree remove <path>once clean. Owner gone: anoperator.pydecisionrow for the changes.keep: nothing changes.delete: the worktree and the branch go.
Step 3: act
Only on the operator's pick for that row, never widened:
- An agent never runs
git branch -D,git worktree remove(EBUSY in a sandbox) or anything outside the sandbox. Each such command becomes its ownoperator.py addrow (kvb-org-operator):--dir <repo>, the command, the reason,--check absent:<worktree path>for a worktree, and for a branchabsent:<git common dir>/refs/heads/<branch>while that loose ref file exists, elsemanual. Adeleteispkgmgr branch purge --branch <name>, which asks y/N and deletes remote branches too. - Never delete a peer's worktree without the operator's pick; afterwards
radio.py post --to <peer>(kvb-org-radio) names what went.
Step 4: report
Per repository: rows handed to gc, archived, kept and deleted, each with its
reason, then the operator.py run block.
Rules
- A pick from an earlier run does not carry over.
- Never route around a refusal through another command or a peer.