# Worktree Cleanup

> Tear down a finished task's git worktree and branch once its PR is merged or closed. Use when a task is done, a PR was merged/closed, or the user asks to clean up / remove / prune worktrees or stale branches. Companion to the `worktree-new` skill; applies to whatever repository Claude is currently in.

- Skill: `enverge-labs/worktree-cleanup` (Agent Skill)
- Install (CLI): `npx skillmds@latest add enverge-labs/worktree-cleanup`
- Raw SKILL.md: https://api.skillmd.com/api/skills/enverge-labs/worktree-cleanup/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Productivity
- Author: Enverge-Labs (https://skillmd.com/u/enverge-labs)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/enverge-labs/worktree-cleanup

---


# Worktree cleanup

Remove worktrees and branches whose PR is **merged or closed**. Destructive — never discard uncommitted or unpushed work.

## Find the repo

```bash
ROOT="$(git rev-parse --show-toplevel)" || { echo "not a git repo"; exit 1; }
```

**If `$ROOT/WORKTREES.md` exists, follow its naming/location** — the repo's doc is the source of truth. Defaults below: worktrees under `.claude/worktrees/<slug>/`, branches `worktree-<slug>`.

## Decide what's done

List worktrees and check each branch's PR state (`MERGED` / `CLOSED` / `OPEN`, or no PR):

```bash
git -C "$ROOT" fetch --prune origin
git -C "$ROOT" worktree list --porcelain          # path + branch per worktree

# per candidate branch:
gh pr view "$BRANCH" --json state,url --jq '"\(.state)\t\(.url)"'   # no PR ⇒ non-zero exit
```

A worktree is a **cleanup candidate** only if its PR is `MERGED` or `CLOSED`. Leave `OPEN` and no-PR worktrees alone.

## Verify it's safe (per candidate)

Before removing, confirm nothing would be lost — skip and report any that fail:

```bash
git -C "$WORKTREE" status --porcelain            # must be empty (no uncommitted/untracked)
git -C "$WORKTREE" log --oneline "@{u}.." 2>/dev/null   # must be empty (no unpushed commits)
```

- Dirty or unpushed → **stop, show the user, do not `--force`.**
- Present the full candidate list and get a go-ahead before deleting.

## Remove

Run from `$ROOT` (never from inside the worktree you're removing):

```bash
git -C "$ROOT" worktree remove ".claude/worktrees/${SLUG}"   # refuses if dirty — that guard is intentional
git -C "$ROOT" branch -d "worktree-${SLUG}" \
  || git -C "$ROOT" branch -D "worktree-${SLUG}"             # -D only after PR confirmed MERGED/CLOSED (squash-merge leaves -d "not fully merged")
git -C "$ROOT" worktree prune
```

## Notes

- Squash/rebase merges make `git branch -d` report "not fully merged"; the `-D` fallback is safe *because the PR state is already confirmed merged/closed* — not as a blanket habit.
- Never delete the main checkout or its default branch.
- Unreachable ≠ deletable: a failed `gh` call (network/auth) is not a closed PR. Confirm state before removing.

