Claude Code Hooks: Deterministic Automation Around Tools
What it is: Hooks are a built-in Claude Code feature that run a shell command you define at specific points around the model's tool calls — for example before or after a tool executes. Unlike asking Claude nicely to "always run the linter first," a hook runs deterministically every time, regardless of what the model decides to do.
Quick answer: Add a hooks section to .claude/settings.json mapping an event to the shell command you want run. Use it for things that must happen every time — validating a query before it runs, formatting a file after it's written, blocking a command outright — rather than relying on the model to remember instructions from a prompt.
Why it matters for data work
Some guardrails shouldn't depend on the model remembering them. A hook that runs before any command execution can block obviously destructive SQL, or one that runs after a file write can auto-run a schema validator on any changed migration file — deterministically, every single time, instead of hoping the system prompt's instructions stick across a long session.
Install & configure
Hooks are configured in your project's .claude/settings.json (or the user-level ~/.claude/settings.json for hooks you want everywhere, or .claude/settings.local.json for ones you don't want committed), mapping a lifecycle event to a shell command Claude Code invokes at that point. The two most common events are PreToolUse (fires before a tool call executes, and can block it) and PostToolUse (fires after a tool call succeeds); Claude Code exposes a longer list of session, prompt, and subagent lifecycle events too — check Anthropic's Claude Code docs for the current full list rather than guessing, since it's grown over time.
Which event should a hook run on?
Run before a tool executes when you need to validate or block — stopping a dangerous command before it happens is only possible pre-execution. Run after a tool executes when you need to react to a result that already happened — formatting a file that was just written, or kicking off a test suite after an edit. If you're not sure which you need, ask: "does this have to prevent something, or just respond to something?" Prevention is a pre-hook; reaction is a post-hook.
How do I keep it safe?
A hook runs an arbitrary shell command with your own permissions, automatically, every time its event fires — treat the hook script itself with the same scrutiny you'd give any code that runs unattended in your environment. Keep hook scripts version-controlled and reviewed like any other project code, avoid hooks that call out to the network with data you wouldn't want leaving your machine, and remember that a hook is a good way to enforce a safety boundary (like blocking a destructive command) but is itself a new piece of trusted, automatically-executed code that deserves the same review as the thing it's guarding against.
Troubleshooting
Most hook issues are configuration or exit-code related rather than Claude Code itself misbehaving.
- Hook doesn't fire: double-check the event name and the settings file's JSON is valid — a malformed config is typically ignored rather than erroring loudly.
- Hook fires but doesn't block anything: confirm the hook script's exit code actually signals failure; a script that prints an error but exits 0 won't stop execution.
- Hook works locally but not for teammates: project-level hooks in
.claude/settings.jsonshould be committed to the repo; a user-level hook in~/.claude/settings.jsonwon't travel with the project. - Hook seems to run twice or on the wrong tool: check for both a project and a user-level hook registered on the same event.
Author & links
Author: Anthropic
Docs: docs.claude.com/en/docs/claude-code/hooks
License: Proprietary — bundled with Claude Code
Related skills
Combine hooks with subagents for automation around delegated work, or with slash commands for a reusable trigger plus a deterministic guardrail.
← Back to Claude Code Skills