Claude Code hooks silently break when you start the session from a subdirectory
Problem
Our PreToolUse hook, a lint gate wired to ./scripts/check.sh, worked perfectly for weeks. Then a teammate ran claude from inside packages/web/ and every edit produced:
PreToolUse:Bash hook error: /bin/sh: ./scripts/check.sh: No such file or directory (exit code 127)No blocking, no hard failure. The hook just stopped doing its job, quietly, for that session. Which is the worst possible failure mode for a guard rail.
Root cause
Hook commands execute with the current working directory of the session, which is wherever claude was started, not the project root. A relative path in a hook command is a bet on who launches the agent and from where. There is a second, sneakier variant of the same bug: the hooks configuration is captured when the session starts, so if you edit .claude/settings.json mid-session, the new hooks do not apply until you restart.
{
"hooks": {
"PreToolUse": [
… 12 more lines in the fix🔒 the fix — including 1 code block — is members-only. $1/mo unlocks everything.