Playbook: Stale Agent-Tool Worktree Cleanup
Created: 2026-07-31
Trigger for this writeup: VSCode/Pylance “large number of source files” warning — root cause
was 53 leftover worktree checkouts under .claude/worktrees/, 1.5 GB / ~100,700 files, dating back
to 2026-07-06.
Trigger
The Agent(..., isolation: "worktree") option creates its own isolated checkout under
.claude/worktrees/agent-<id> — this is separate from, and not covered by, the manual
tmp/wt-{issue} convention documented in CLAUDE.md §Agent Workflow — Worktree Branch Isolation.
Claude Code auto-removes an isolation: "worktree" checkout only when the agent made no
changes; any agent that actually committed work leaves its worktree checkout on disk indefinitely.
Over weeks of background-agent usage these accumulate silently — nothing in the CLAUDE.md
“clean up immediately after merge” rule applies to this mechanism because it isn’t a tmp/wt-*
path a human/orchestrating session created and is tracking.
Symptoms
- Pylance/VSCode: “found a large number of source files in this workspace”
.claude/worktrees/is large or has manyagent-*subdirectories- General editor/IDE sluggishness, slow
git statusin the primary checkout (unrelated worktrees don’t directly slowgit statusin the main tree, but do slow file-watcher-based tooling)
Confirm
# Size + file count of .claude/worktrees
(Get-ChildItem .claude\worktrees -Recurse -File -Force -ErrorAction SilentlyContinue | Measure-Object -Property Length -Sum).Sum /1MB
(Get-ChildItem .claude\worktrees -Recurse -File -Force -ErrorAction SilentlyContinue | Measure-Object).Count
# List every registered worktree — real git worktrees show up here even if nested oddly
git worktree listBefore removing anything, verify none are dirty (uncommitted changes) or hold work you still need:
$worktrees = git worktree list --porcelain | Select-String "^worktree (.*\.claude.*)" | ForEach-Object { $_.Matches[0].Groups[1].Value }
foreach ($wt in $worktrees) {
$dirty = git -C $wt status --porcelain 2>$null
[PSCustomObject]@{ Path=$wt; Branch=(git -C $wt rev-parse --abbrev-ref HEAD 2>$null); Dirty=[bool]$dirty }
}Any Dirty=True entry needs manual review (rescue the branch / stash) before removal — see
docs/playbooks/shared-checkout-worktree-isolation.md for the “don’t discard, rescue to a branch
first” pattern if something looks like real unmerged work.
Fix — remove stale worktree checkouts (branches/commits are NOT affected)
git worktree remove only deletes the working-tree checkout. The branch and every commit on it
stay fully intact in git regardless of merge status — safe to remove even for unmerged branches;
the branch can be re-checked-out or re-worktreed later if needed.
$worktrees = git worktree list --porcelain | Select-String "^worktree (.*\.claude.*)" | ForEach-Object { $_.Matches[0].Groups[1].Value }
foreach ($wt in $worktrees) { git worktree remove --force $wt }
git worktree prune -v
# Remove any leftover empty directories git didn't clean up (non-registered stragglers)
Get-ChildItem .claude\worktrees -Force -ErrorAction SilentlyContinue
Remove-Item .claude\worktrees -Recurse -Force -Confirm:$false -ErrorAction SilentlyContinueWhy the leftover dirs matter (not just disk bloat) — #6006. A leftover unregistered worktree dir is worse than clutter: git commands run from inside it walk up and silently operate on the parent repo’s current branch. Always pair
git worktree removewith an explicitRemove-Item/rm -rfof the same path (as above) so no unregistered dir survives, and have every worktree-scoped agent run the pre-writegit rev-parse --show-toplevelguard. Full writeup:worktree-remove-fallthrough-guard.md.
.claude/worktrees/ is already in .gitignore, so none of this was ever a git-tracked/committed
concern — it’s pure local disk bloat.
Prevention
.vscode/settings.json(gitignored, per-workstation) should exclude.claude/worktrees/**,tmp/**,node_modules,.next,.wrangler,.playwright-mcpfrompython.analysis.exclude,search.exclude, andfiles.watcherExclude— this stops Pylance/VSCode re-scanning them even if they build back up before the next manual sweep.- No automated cleanup exists yet for
.claude/worktrees/agent-*specifically (unliketmp/wt-*, which a human/orchestrating session creates and is expected togit worktree removeright after merge). Until an automated sweep exists, periodically re-run the Confirm + Fix commands above — e.g. as part of an occasional/sr-adjacent housekeeping pass.
Escalation
None needed — this is local workstation disk hygiene, not a production/infra issue. If
.claude/worktrees reappears at large scale again shortly after a cleanup, consider filing an
issue to add automated pruning (e.g. a nightly local task, or asking upstream Claude Code tooling
whether isolation: "worktree" can auto-remove on completion even when the agent made commits).