Hooks and automation
Use Claude Code hooks to run linters, tests, and guards automatically on agent events — making the agent's environment opinionated and safe.
- Explain what Claude Code hooks are and the four event points where they can fire
- Write a PostToolUse hook that runs pytest after any Python file is written
- Describe the pattern of using hooks to enforce standards the agent must meet
- Identify appropriate and inappropriate uses of hooks in an agentic workflow
The previous lesson established phased orchestration: you review the output of each phase before the next one starts. That is a strong pattern, but it relies entirely on you to be the checkpoint. Every review costs your time and attention.
Hooks give you a different lever: automate the checkpoints that are always the same.
If you always want the tests to run after the agent writes a Python file, you should not have to remember to run them. A hook can run them for you — and feed the result back to the agent automatically, so it can fix failures without waiting for you to copy-paste them.
What hooks are
Claude Code hooks are shell commands that run automatically when specific events occur during an agent session. You configure them in your Claude Code settings, and they execute in the background as the agent works.
The four event points:
| Hook type | When it fires |
|---|---|
PreToolUse | Before the agent calls a tool (e.g., before writing a file) |
PostToolUse | After the agent calls a tool (e.g., after writing a file) |
Notification | When the agent sends a notification |
Stop | When the agent session ends |
The most useful for coding workflows is PostToolUse — it fires after the agent
has written or modified a file, which is exactly when you want to run your checks.
Configuring hooks
Hooks are defined in the Claude Code settings file, which lives at:
~/.claude/settings.jsonHere is a minimal configuration that runs pytest after any Python file is
written:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write",
"hooks": [
{
"type": "command",
"command": "cd /path/to/your/project && pytest tests/ -v 2>&1 | tail -20"
}
]
}
]
}
}The matcher field specifies which tool call triggers this hook. Write matches
any file-write tool call. You can also match on Edit, Bash, or use .* to
match all tool calls.
The command's stdout is fed back into the agent's context. The agent sees the pytest output as if you had run it yourself and pasted the result — and it can act on failures without waiting for you.
You can be more selective with the matcher. To only trigger on Python files:
use a matcher that checks the tool input, or filter in the shell command with
a conditional: if echo "$CLAUDE_TOOL_INPUT_FILE_PATH" | grep -q "\.py$"; then pytest ...; fi.
The exact syntax depends on the Claude Code version — check the docs for your
version.
What you can build with hooks
Auto-run linter after edits. After every file write, run ruff check or
eslint. The agent sees lint errors immediately and fixes them in the same
session, rather than you discovering them during review.
{
"type": "command",
"command": "ruff check . --output-format=text 2>&1 | head -30"
}Log all tool calls. Write a hook that appends every tool call to a log file. Useful for auditing what the agent did in a long session.
{
"type": "command",
"command": "echo \"$(date): $CLAUDE_TOOL_NAME $CLAUDE_TOOL_INPUT_FILE_PATH\" >> ~/.claude/session.log"
}Block dangerous operations. A PreToolUse hook on Bash can inspect the
command about to be run and exit with a non-zero status to block it:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "if echo \"$CLAUDE_TOOL_INPUT_COMMAND\" | grep -qE 'rm -rf|DROP TABLE|git push'; then echo 'BLOCKED: dangerous operation'; exit 2; fi"
}
]
}
]
}
}A hook that exits with code 2 signals Claude Code to block the tool call
entirely. An exit code of 0 allows it. Anything else is treated as a warning.
Be careful with blocking hooks on Bash — if your command is too broad, it
will block legitimate operations. Start with logging, then add blocking once
you have validated the pattern.
Run tests after any Python file change. The most universally useful hook for Python projects:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "pytest tests/ --tb=short -q 2>&1 | tail -15"
}
]
}
]
}
}With this hook active, the agent writes a file, sees the test output, and can immediately fix failures — all within the same session, without you having to intervene.
The pattern: opinionated environments
The principle behind hooks is making the agent's environment opinionated.
Without hooks, the agent operates in a neutral environment — it does not know your standards are being enforced until you tell it they were violated. With hooks, the environment enforces standards automatically. Lint errors appear before the agent moves on. Test failures are visible immediately. Dangerous operations are blocked before they execute.
This shifts the work from "catch mistakes in review" to "prevent mistakes during execution." Review is still necessary — hooks do not catch all categories of problems. But a well-configured hook setup means the diff you review has already passed a basic automated check.
What hooks are not for
Not a substitute for diff review. Hooks catch mechanical errors — syntax, lint, test failures. They do not catch deleted error handling, hardcoded values, or scope creep. Human review still covers those.
Not a place for complex business logic. Hooks run shell commands. If your "hook" would need to be a Python script with complex logic to determine whether to block an operation, you are building a more complex safety system than hooks are designed for. Keep hooks simple.
Not always appropriate for every project. A hook that runs the full test suite after every file write makes sense for a project with fast tests (under a few seconds). For a project where the test suite takes five minutes, that hook will slow every agent session to a crawl. Tune hooks to the project.
Hooks and automation
- 1.A PostToolUse hook that runs pytest after every file write feeds the test output back to the agent. What is the primary benefit of this pattern?
- 2.Which of the following are appropriate uses of Claude Code hooks? Select all that apply.
- 3.A PreToolUse hook that exits with code 2 will block the corresponding tool call from executing.
Where to go next
Hooks automate the enforcement of your project's standards. The next lesson expands the agent's capabilities in a different direction: MCP servers give the agent access to external tools and data sources — databases, APIs, and more — without you having to build that access from scratch.
Multi-step agent workflows
Orchestrate complex tasks across many files and phases by treating the agent as an engineer you manage, not a tool you run once.
MCP servers and agent tools
Understand the Model Context Protocol — what it is, how it expands agent capabilities, and how to add MCP servers to Claude Code safely.