You set up a hook, and nothing happens. This is the most common hook frustration, and it is almost always one of a handful of causes. Here is how to track it down.
Check the config is loaded
Start with /hooks in your session. It shows the hooks Claude Code has actually loaded, which tells you whether your settings.json was read at all. If your hook is missing here, the problem is the file, not the command.
Remember settings precedence: enterprise, then command line, then .claude/settings.local.json, then .claude/settings.json, then ~/.claude/settings.json. Make sure you edited the file you think you did, and that valid JSON parses, since a stray comma will drop the whole block.
Check the event and matcher
Confirm the event name is right and spelled exactly, like PostToolUse, not postToolUse. Then check the matcher. A hook meant for edits needs "Edit|Write"; a matcher of "Bash" will never fire on a file edit. If in doubt, widen it to "*" temporarily to prove the wiring works.
Check the command runs
A hook that fires but seems to do nothing may be failing silently. Test the command on its own in a terminal, feeding it sample JSON:
echo '{"tool_input":{"command":"ls"}}' | ./.claude/guard.sh
echo "exit: $?"
Watch the exit code. Remember 0 is success, 2 blocks and feeds stderr back, and other non-zero values are non-blocking, so a crashing script may just be ignored rather than stopping anything.
Check the small stuff
Make the script executable with chmod +x. Use absolute paths or paths from the project root, since the working directory may not be what you expect. Print debugging output to a log file so you can see the event JSON your hook actually received. Work through these in order and the silent hook almost always gives itself up.
Comments
Be the first to comment.