Summary
Claude Code hooks defined in .claude/settings.json are not triggered when the active working directory is a git worktree (created via git worktree add).
Environment
- Claude Code CLI
- Git worktree setup: main repo at
/path/to/repo, worktree at /path/to/repo/worktrees/feature-branch
Steps to reproduce
- Set up a hook in
.claude/settings.json at the main repo root, e.g. a PreToolUse hook on Bash that intercepts git commit
- Create a git worktree:
git worktree add worktrees/my-feature -b my-feature
- Open a Claude Code session with the worktree as the working directory
- Run
git commit from within the session
Expected: The PreToolUse hook fires and runs the configured command.
Actual: The hook is silently skipped. Claude Code does not find .claude/settings.json because it looks relative to the current working directory, not the git repository root.
Root cause
In a git worktree, the .git entry is a file (not a directory) that points back to the main repo. The .claude/ directory lives in the main repo root, not in the worktree directory. Claude Code appears to resolve .claude/settings.json relative to the process working directory rather than using git rev-parse --show-toplevel to locate the repository root and find .claude/ from there.
Impact
Any project that uses .claude/hooks/ for pre-commit checks, linters, or test runners will silently lose those guardrails whenever a developer works inside a git worktree. In our case, a PreToolUse hook runs cargo test inside Docker before every commit — this hook was bypassed in the worktree, causing tests to run directly on the host and inadvertently interact with the local tmux session.
Suggested fix
When resolving .claude/settings.json, fall back to git rev-parse --show-toplevel to find the repository root and look for .claude/ there, so hooks work consistently whether the session is started from the main worktree or any linked worktree.
Summary
Claude Code hooks defined in
.claude/settings.jsonare not triggered when the active working directory is a git worktree (created viagit worktree add).Environment
/path/to/repo, worktree at/path/to/repo/worktrees/feature-branchSteps to reproduce
.claude/settings.jsonat the main repo root, e.g. aPreToolUsehook onBashthat interceptsgit commitgit worktree add worktrees/my-feature -b my-featuregit commitfrom within the sessionExpected: The
PreToolUsehook fires and runs the configured command.Actual: The hook is silently skipped. Claude Code does not find
.claude/settings.jsonbecause it looks relative to the current working directory, not the git repository root.Root cause
In a git worktree, the
.gitentry is a file (not a directory) that points back to the main repo. The.claude/directory lives in the main repo root, not in the worktree directory. Claude Code appears to resolve.claude/settings.jsonrelative to the process working directory rather than usinggit rev-parse --show-toplevelto locate the repository root and find.claude/from there.Impact
Any project that uses
.claude/hooks/for pre-commit checks, linters, or test runners will silently lose those guardrails whenever a developer works inside a git worktree. In our case, aPreToolUsehook runscargo testinside Docker before every commit — this hook was bypassed in the worktree, causing tests to run directly on the host and inadvertently interact with the local tmux session.Suggested fix
When resolving
.claude/settings.json, fall back togit rev-parse --show-toplevelto find the repository root and look for.claude/there, so hooks work consistently whether the session is started from the main worktree or any linked worktree.