Summary
When the Agent tool is invoked with isolation: "worktree" (or --worktree CLI flag) and the agent makes any changes, Claude Code intentionally preserves the worktree (per documented contract: "automatically cleaned up if the agent makes no changes; otherwise the path and branch are returned"). This is correct for review purposes, but in practice the leftover worktrees accumulate without any session-level visibility — users discover them weeks later. On Windows the situation is worse: even when the user manually attempts cleanup, external processes (Search Indexer, Defender, etc.) hold file handles on the now-empty directory, and git worktree remove / Remove-Item -Recurse -Force / cmd rd /s /q / .NET Directory.Delete all fail with the same IOException: process cannot access the file because it is being used by another process.
Current behavior (verified, Claude Code 2.1.92, Windows, Git Bash)
- Session uses
Agent({ isolation: "worktree", ... }) or --worktree. Worktree is created at .claude/worktrees/<random> with branch claude/<random>.
- Agent makes any change (even unintentional, e.g. updating
.claude/settings.local.json). Auto-cleanup is skipped.
WorktreeRemove hook fires with no decision control (observability only, per docs). Hook cannot retry, force-delete, or escalate.
- If user manually runs
git worktree remove, Windows file lock causes silent fail in many cases. Empty directory persists indefinitely.
- No SessionStart-level surfacing of accumulated leftovers — user has to look at
.claude/worktrees/ directly.
Proposed improvements
-
Leftover visibility on session start — when entering the main worktree of a repo with N+ registered auto-isolation worktrees (or orphan directories under .claude/worktrees/), surface a one-line notice ("3 leftover worktrees from previous sessions — review with git worktree list or /cleanup-worktrees"). Configurable threshold (default: 1).
-
WorktreeRemove hook decision control — let the hook return decision: "retry" / decision: "force" / decision: "skip" instead of being purely observability-only. Enables hooks to recover from Windows locks (e.g., wait + retry, fall back to PowerShell forced delete, defer to next session).
-
Explicit user notification on auto-cleanup failure — when Claude Code's automatic worktree removal hits an OS error (Windows file lock, permission denied), surface a systemMessage to the user instead of silent debug-only logging. Include the exact path and a copy-paste manual cleanup command.
-
Built-in /cleanup-worktrees command — one-command audit + best-effort cleanup of leftover auto-isolation worktrees from the main worktree, with explicit reporting of what was cleaned vs. what is locked vs. what was preserved due to uncommitted changes.
-
Optional: worktree.autoCleanupOnChanges setting — for users who prefer auto-cleanup even when changes exist (with a warning message preserving the diff in a stash or patch file). Opt-in to retain current default safety.
Why this matters
The Agent tool's isolation: "worktree" is genuinely useful for parallel/exploratory work. The current contract is reasonable. But the missing pieces — visibility + Windows recovery + hook decision power — turn a single forgotten leftover into months of accumulated cruft, especially for users who heavily use subagents on Windows.
Workaround in use
Custom ~/.claude/hooks/worktree-leftover-notice.sh (SessionStart user matcher) that scans for orphan directories, registered auto-isolation worktrees, and reflog leftovers under .git/logs/refs/heads/claude/, then injects an additionalContext notice. Effective as visibility, but it cannot recover the Windows-locked empty directories — that requires (2) or (3) above.
Environment
- Claude Code: 2.1.92
- OS: Windows 10/11
- Shell: Git Bash (MSYS)
- superpowers plugin: 5.0.7 (no
isolation: "worktree" frontmatter found in any plugin skill — confirms isolation trigger is Agent tool option, not plugin behavior)
Related issues
This proposal touches on themes in several existing issues. They each address a slice; this one bundles the visibility + Windows recovery + hook decision control angle.
Summary
When the
Agenttool is invoked withisolation: "worktree"(or--worktreeCLI flag) and the agent makes any changes, Claude Code intentionally preserves the worktree (per documented contract: "automatically cleaned up if the agent makes no changes; otherwise the path and branch are returned"). This is correct for review purposes, but in practice the leftover worktrees accumulate without any session-level visibility — users discover them weeks later. On Windows the situation is worse: even when the user manually attempts cleanup, external processes (Search Indexer, Defender, etc.) hold file handles on the now-empty directory, andgit worktree remove/Remove-Item -Recurse -Force/cmd rd /s /q/ .NETDirectory.Deleteall fail with the sameIOException: process cannot access the file because it is being used by another process.Current behavior (verified, Claude Code 2.1.92, Windows, Git Bash)
Agent({ isolation: "worktree", ... })or--worktree. Worktree is created at.claude/worktrees/<random>with branchclaude/<random>..claude/settings.local.json). Auto-cleanup is skipped.WorktreeRemovehook fires with no decision control (observability only, per docs). Hook cannot retry, force-delete, or escalate.git worktree remove, Windows file lock causes silent fail in many cases. Empty directory persists indefinitely..claude/worktrees/directly.Proposed improvements
Leftover visibility on session start — when entering the main worktree of a repo with N+ registered auto-isolation worktrees (or orphan directories under
.claude/worktrees/), surface a one-line notice ("3 leftover worktrees from previous sessions — review withgit worktree listor/cleanup-worktrees"). Configurable threshold (default: 1).WorktreeRemovehook decision control — let the hook returndecision: "retry"/decision: "force"/decision: "skip"instead of being purely observability-only. Enables hooks to recover from Windows locks (e.g., wait + retry, fall back to PowerShell forced delete, defer to next session).Explicit user notification on auto-cleanup failure — when Claude Code's automatic worktree removal hits an OS error (Windows file lock, permission denied), surface a
systemMessageto the user instead of silent debug-only logging. Include the exact path and a copy-paste manual cleanup command.Built-in
/cleanup-worktreescommand — one-command audit + best-effort cleanup of leftover auto-isolation worktrees from the main worktree, with explicit reporting of what was cleaned vs. what is locked vs. what was preserved due to uncommitted changes.Optional:
worktree.autoCleanupOnChangessetting — for users who prefer auto-cleanup even when changes exist (with a warning message preserving the diff in a stash or patch file). Opt-in to retain current default safety.Why this matters
The
Agenttool'sisolation: "worktree"is genuinely useful for parallel/exploratory work. The current contract is reasonable. But the missing pieces — visibility + Windows recovery + hook decision power — turn a single forgotten leftover into months of accumulated cruft, especially for users who heavily use subagents on Windows.Workaround in use
Custom
~/.claude/hooks/worktree-leftover-notice.sh(SessionStart user matcher) that scans for orphan directories, registered auto-isolation worktrees, and reflog leftovers under.git/logs/refs/heads/claude/, then injects anadditionalContextnotice. Effective as visibility, but it cannot recover the Windows-locked empty directories — that requires (2) or (3) above.Environment
isolation: "worktree"frontmatter found in any plugin skill — confirms isolation trigger isAgenttool option, not plugin behavior)Related issues
This proposal touches on themes in several existing issues. They each address a slice; this one bundles the visibility + Windows recovery + hook decision control angle.
isolation: "worktree"auto-reap when subagent returns without changes, breaking SendMessage resume #50889 "Worktrees fromisolation: \"worktree\"auto-reap when subagent returns without changes, breaking SendMessage resume" — current "no changes → reap" contract has its own failure mode