You are replacing git worktree add
When you run claude --worktree, spin up a subagent with isolation: "worktree", or start an isolated background session, Claude Code needs a fresh checkout to work in. A worktree is a second working copy of the repository in its own folder, and Claude Code normally creates one for you with git worktree add. A WorktreeCreate hook takes that job over. It runs while the worktree is being created, and it has to hand back the directory Claude Code should enter.
The payload carries name, a slug for the new worktree that you typed or Claude Code generated. There is no matcher, so every configured handler runs for every request. The hooks reference does not say how Claude Code resolves the case where more than one handler each returns a path.
The path is the last line of stdout
A command hook prints the directory as the last non-empty line of stdout and exits 0. It cannot also return JSON, because stdout is the path. An HTTP hook returns hookSpecificOutput with hookEventName: "WorktreeCreate" and worktreePath, and may use the shared side-effect fields. Both kinds discard systemMessage and continue.
The returned path is normalised, meaning rewritten into one standard form, and checked before Claude Code enters it. The check screens for absolute dot segments and for symlinks that resolve below the repository root. That screening applies from v2.1.216. Before that version it did not.
Any non-zero code fails, and so can zero
Most hook events treat exit 2 as the block signal. This one is different. Exit 1, exit 2, and every other non-zero code fail creation the same way. Exit 0 fails too when the last line of stdout is not a usable path. A zero exit is necessary for success here, but it is not sufficient.
Your script creates the checkout, prints the path, then prints done as a final status line, and exits 0.
Creation fails. done is the last non-empty line of stdout, and it is not a path. The checkout exists on disk and Claude Code never enters it.
Print the path last, and send status lines to stderr. The same goes for every failed git command that sets a non-zero exit: on this event there is no exit 1 that proceeds.
Module 08 covers isolation: worktree, and module 09 names this event as the exception to the exit-code rule.