.cursor/skills/nv-worktree-cleanup/SKILL.md
End state: stale, merged, and missing worktrees are removed; their local branches are deleted; git worktree prune has run; the user has signed off on every deletion.
--force remove a worktree without explicit user confirmation. Dirty worktrees stay until the user says otherwise.git rev-parse --show-toplevel of the primary checkout) or the worktree the agent is currently sitting in. Detect the agent's worktree by comparing pwd (or git rev-parse --show-toplevel from the current directory) against each path from git worktree list. If they match, refuse deletion and tell the user to move first via move_agent_to_root.AskQuestion with allow_multiple: true. Don't delete one-by-one without a single approval step.git branch -d (safe delete) by default. Only use -D for branches the user explicitly flags as okay to force-delete.git worktree list --porcelain
Parse into entries: path, branch, HEAD. Skip the primary worktree.
Check in order:
git symbolic-ref refs/remotes/origin/HEAD (gives e.g. refs/remotes/origin/main)main, then next, then masterUse origin/<base> for merge checks so local staleness doesn't matter. Run git fetch --prune origin first to make checks accurate.
For each non-primary worktree, compute one of:
| Status | Detection | Default action |
|---|---|---|
| missing | path does not exist on disk | prune only |
| merged-direct | git merge-base --is-ancestor <branch> origin/<base> succeeds | remove + delete branch |
| merged-squash | gh pr list --state merged --head <branch> --json number returns a PR (requires GitHub CLI — if gh is missing, skip this classification and flag the branch as active with a note to install gh or verify merge manually) | remove + delete branch (with -D after confirm, since -d will refuse) |
| dirty | git -C <path> status --porcelain is non-empty | flag, do not remove |
| unpushed | branch has commits not on origin/<branch> and no merged PR | flag, do not remove |
| active | none of the above | leave alone |
Also flag any worktree that is the current agent root — compare pwd / git rev-parse --show-toplevel against each worktree path, mark matches as such, and exclude them from the removable set.
Use AskQuestion with allow_multiple: true. Group by category in the prompt so the user can scan it. For each removable entry, the option label should include path, branch, and reason. Example option labels:
merged: ../more-dcr-oauths (branch more-dcr-oauths)missing: ../old-experiment (branch old-experiment)dirty: ../wip-bar (branch wip/bar) — has uncommitted changesFor dirty / unpushed entries, surface them as separate options so the user can opt in to force-removing if they really want to.
For each confirmed worktree:
git worktree remove <path> # safe path
git worktree remove --force <path> # only if user opted in for dirty
git branch -d <branch> # safe delete (refuses if unmerged)
git branch -D <branch> # only if user opted in (squash-merged or force); squash-merges leave the branch ahead/unmerged in topology so `-d` refuses — confirm merge via `gh pr list` first
For missing entries, skip remove and rely on prune in the next step.
git worktree prune
git worktree list # show the user the new state
Report:
git worktree remove --force over every entry "to be safe". Force is for explicit user opt-in only.git branch -d will fail with "checked out at ...". Order is: remove worktree, then delete branch.gh pr list --state merged --head <branch> for that case.git worktree prune before listing — you'll lose the metadata you need to identify missing entries.git fetch --prune origin first; otherwise "merged into origin/main" checks can be wrong.User: "clean up my worktrees"
Agent flow:
git fetch --prune origingit worktree list --porcelain → 5 worktrees (1 primary + 4 others)../nv-100-x → merged-direct../nv-200-y → merged-squash (PR #4321 merged)../nv-300-z → dirty (3 modified files)../old-experiment → missing on diskAskQuestion: which of these to remove? (multi-select, dirty entry pre-flagged with a warning label)git worktree remove ../nv-100-x && git branch -d nv-100/xgit worktree remove ../nv-200-y && git branch -D nv-200/y (squash-merge needed -D, user confirmed)git worktree prune (cleans up ../old-experiment)git worktree list.