[DOCS] WorktreeCreate/WorktreeRemove hooks: ownership rule, clean-exit removal, agent_id example, bypassed defaults, cwd per caller
Documentation Type
Unclear/confusing documentation
Documentation Location
https://code.claude.com/docs/en/hooks, https://code.claude.com/docs/en/worktrees, https://code.claude.com/docs/en/sub-agents
Section/Topic
WorktreeCreate and WorktreeRemove hooks; SubagentStart and SubagentStop input
Current Documentation
hooks.md, WorktreeRemove triggers:
* you exit a --worktree session and choose to remove it
hooks.md, WorktreeRemove:
For git-based worktrees, Claude Code handles cleanup automatically with git worktree remove. If you configured a WorktreeCreate hook, pair it with a WorktreeRemove hook to control cleanup of the worktrees it creates
hooks.md, SubagentStart and SubagentStop input examples:
"agent_id": "agent-abc123","agent_id": "def456",
hooks.md, common input fields:
cwd | Current working directory when the hook is invoked
worktrees.md, Choose the base branch:
New worktrees branch from the repository's default branch, so most sessions don't need this setting.
What's Wrong or Missing?
Investigated and reproduced with an empty CLAUDE_CONFIG_DIR (no user hooks, plugins or CLAUDE.md) by Claude Code 2.1.288 using Opus 5.5.
All observed on 2.1.288 with logging hooks registered through --settings. Details marked "source" come from a reading of the 2.1.288 bundle, not from a run.
- The WorktreeRemove trigger list gives "you exit a
--worktreesession and choose to remove it", but a clean exit also calls WorktreeRemove, without a prompt. The docs also never say in one place that the hook decides whether removal is safe: that follows from several sections, and from a non-zero exit keeping the worktree.
- The ownership rule is only implied. WorktreeRemove fires only for worktrees a WorktreeCreate hook created; a worktree Claude Code creates with git is removed by git and never runs the hook, even when a WorktreeRemove hook is configured. A maintainer confirmed this in #74708 and wrote that the docs should say so plainly. No page states it.
- The SubagentStart example
agent_idis wrong. Both events receive the bare id, for exampleaaa40a63f12bc5cae. Theagent-prefix appears only in the subagent transcript filename (agent-<id>.jsonl) and in the name passed to WorktreeCreate for an isolated subagent (agent-<id>). A hook matching a subagent to its worktree or transcript needs to know which form arrives.
- Worktree defaults are described without saying a WorktreeCreate hook bypasses them. worktrees.md says
.worktreeincludeis not processed with a hook, but the base branch andworktree.baseRef(Choose the base branch), the pull request fetch for--worktree '#N'(Branch from a pull request), and theisolationrow in sub-agents.md describe git-path behaviour with no qualifier. With a hook, the hook receives onlyname(pr-<N>for a pull request), and Claude Code creates noworktree-<name>branch, resolves no base ref, and fetches nothing.
- WorktreeCreate
cwddepends on the caller, and isn't the obvious directory for--worktree:
| Caller | cwd |
| ------------------------------------------- | ------------------------------- |
| --worktree from the main checkout | the launch directory |
| --worktree from a subdirectory | the launch directory |
| --worktree from a linked worktree | the main checkout |
| --worktree from a worktree of a bare repo | the bare repository directory |
| subagent with isolation: worktree | the session's current directory |
A hook that resolves paths from cwd, or runs git there expecting a work tree, breaks in the last two --worktree cases.
Suggested Improvement
- In the WorktreeRemove trigger list, add that a clean
--worktreesession exit calls WorktreeRemove without a prompt, and say that the hook decides whether removal is safe, keeping the worktree by exiting non-zero. - Add near the top of the WorktreeRemove section: "WorktreeRemove runs only for worktrees a WorktreeCreate hook created. Worktrees Claude Code creates with git are removed by git and never run the hook."
- Change the SubagentStart example to a bare id such as
"agent_id": "a1b2c3d4e5f6a7b8c", use the same shape in the SubagentStop example, and note where theagent-prefix appears. - In worktrees.md (Choose the base branch, Branch from a pull request) and the sub-agents.md
isolationrow, note that with a WorktreeCreate hook these don't apply and the hook receives onlyname. - In the WorktreeCreate input section, document
cwdper caller, as in the table above.
Impact
Medium - Makes feature difficult to understand
Additional Context
Related: #99270 (WorktreeRemove never fires for hook-created subagent worktrees, contrary to the trigger list), #74708 (ownership rule), #92425 (WorktreeRemove contract for adopted worktrees).