ai newsFr., 6. Nov.
EilmeldungOpenAI is stepping back from NDAs for data center projects

Claude Code Hooks: Automating Your AI Coding Workflow

Claude Code Hooks: A Complete Guide to Automating Your AI Coding Workflow If you've been using Claude Code for more than a week, you've probably noticed a pattern: you keep telling it the same things. "Run prettier after editing." "Don't touch the .env file." "Run the tests before you stop." These

dev.toDonnerstag 19:41 · 05.11.8 Min. Lesezeit
Bild: dev.to

Posted on Nov 5 • Originally published at scotthavird.com

If you've been using Claude Code for more than a week, you've probably noticed a pattern: you keep telling it the same things. "Run prettier after editing." "Don't touch the.env file." "Run the tests before you stop." These aren't complex instructions — they're rules. And rules shouldn't depend on an LLM remembering to follow them.

That's exactly what Claude Code hooks solve. They're deterministic automation that runs at specific points in Claude Code's lifecycle, executed by the harness itself — not by Claude. If you configure a hook to format code after every edit, it will format code after every edit. No exceptions. No "I forgot."

Hooks are shell commands, HTTP endpoints, or LLM prompts that fire automatically when specific events happen in Claude Code. Think of them like git hooks, but for your AI coding assistant.

Hooks are configured in settings.json files at three scopes:

Claude Code exposes 27 lifecycle events. Here are the ones that matter most for day-to-day development:

Each event can be filtered with a matcher — a regex pattern that narrows which specific tools or sources trigger the hook.

Here's the basic structure in.claude/settings.json:

{ "hooks": { "EventName": [ { "matcher": "regex_pattern", "hooks": [ { "type": "command", "command": "your-script.sh", "timeout": 30 } ] } ] } } Enter fullscreen mode Exit fullscreen mode Hooks receive JSON context on stdin (tool name, arguments, file paths) and communicate back through exit codes:

I've been running hooks across my projects for months now. Here are the patterns that have stuck.

This is the single most impactful hook. Claude writes code, and it's immediately formatted to your project's standards. No more "can you run prettier on that?"

{ "hooks": { "PostToolUse": [ { "matcher": "Edit|Write", "hooks": [ { "type": "command", "command": "jq -r '.tool_input.file_path' | xargs npx prettier --write 2>/dev/null", "timeout": 30 } ] } ] } } Enter fullscreen mode Exit fullscreen mode The hook reads the file path from the tool's JSON input and runs prettier on it. Works with any formatter — swap in black for Python, gofmt for Go, rustfmt for Rust.

Some files should never be edited by an AI assistant. Period.

#!/bin/bash INPUT = $( cat ) FILE_PATH = $( echo " $INPUT " | jq -r '.tool_input.file_path // empty' ) PROTECTED =( ".env" ".env.local" "secrets.yml" "credentials.json" ) for pattern in " ${ PROTECTED [@] } "; do if [[ " $FILE_PATH " == * " $pattern " * ]]; then echo "Blocked: $FILE_PATH is a protected file" > &2 exit 2 fi done Enter fullscreen mode Exit fullscreen mode { "hooks": { "PreToolUse": [ { "matcher": "Edit|Write", "hooks": [ { "type": "command", "command": ".claude/hooks/protect-files.sh" } ] } ] } } Enter fullscreen mode Exit fullscreen mode Exit code 2 blocks the action and sends the stderr message back to Claude as feedback, so it knows why it was blocked and can adjust.

When Claude finishes a task or needs input, you want to know — especially if you've tabbed away to something else.

{ "hooks": { "Notification": [ { "matcher": "", "hooks": [ { "type": "command", "command": "osascript -e 'display notification \" Claude Code needs your attention \" with title \" Claude Code \" '" } ] } ] } } Enter fullscreen mode Exit fullscreen mode Linux:

{ "hooks": { "Notification": [ { "matcher": "", "hooks": [ { "type": "command", "command": "notify-send 'Claude Code' 'Claude needs your attention'" } ] } ] } } Enter fullscreen mode Exit fullscreen mode Pattern 4: Quality Gates Before Stopping This is where hooks get powerful. Instead of hoping Claude remembers to run tests, you can require it:

{ "hooks": { "Stop": [ { "hooks": [ { "type": "agent", "prompt": "Run the project's test suite and type checker. If any tests fail or type errors exist, respond with { \" ok \": false, \" reason \": \" description of failures \" }. If everything passes, respond with { \" ok \": true}.", "timeout": 120 } ] } ] } } Enter fullscreen mode Exit fullscreen mode The agent hook type spins up a subagent with full tool access. If it returns ok: false, Claude continues working to fix the issues instead of stopping. This is a game-changer for code quality.

Important: Check the stop_hook_active field in the Stop event input to prevent infinite loops — if your Stop hook triggers Claude to do more work, which triggers another Stop, you'd loop forever.

Long sessions hit the context window limit, and Claude compacts (summarizes) the conversation. Critical context can get lost. Fix it:

{ "hooks": { "SessionStart": [ { "matcher": "compact", "hooks": [ { "type": "command", "command": "cat.claude/critical-context.md" } ] } ] } } Enter fullscreen mode Exit fullscreen mode Whatever your hook outputs to stdout gets injected into Claude's context. Keep a.claude/critical-context.md file with must-remember information, and it survives compaction automatically.

Tired of clicking "Allow" for read-only operations? Auto-approve them:

{ "hooks": { "PermissionRequest": [ { "matcher": "Read|Glob|Grep", "hooks": [ { "type": "command", "command": "echo '{ \" hookSpecificOutput \": { \" hookEventName \": \" PermissionRequest \", \" decision \": { \" behavior \": \" allow \" }}}'" } ] } ] } } Enter fullscreen mode Exit fullscreen mode Pattern 7: Environment Management with direnv If your project uses direnv for environment variables, hooks keep Claude in sync:

{ "hooks": { "CwdChanged": [ { "hooks": [ { "type": "command", "command": "direnv export bash >> \" $CLAUDE_ENV_FILE \" " } ] } ], "FileChanged": [ { "matcher": ".envrc|.env", "hooks": [ { "type": "command", "command": "direnv export bash >> \" $CLAUDE_ENV_FILE \" " } ] } ] } } Enter fullscreen mode Exit fullscreen mode The $CLAUDE_ENV_FILE variable points to a file where you can persist environment variables across the session. Writing to it with direnv export keeps Claude's environment aligned with your shell.

Claude Code supports four hook types, each suited to different needs:

command handles 90% of use cases. Use agent for Stop hooks that need to run commands and reason about results. Use http for integrating with external services like Slack or custom dashboards. Use prompt for lightweight LLM-based decisions.

Here's how I recommend structuring hooks for a team:

.claude/settings.json (committed to git):

{ "hooks": { "PostToolUse": [ { "matcher": "Edit|Write", "hooks": [ { "type": "command", "command": "jq -r '.tool_input.file_path' | xargs npx prettier --write 2>/dev/null" } ] } ], "PreToolUse": [ { "matcher": "Edit|Write", "hooks": [ { "type": "command", "command": ".claude/hooks/protect-files.sh" } ] } ] } } Enter fullscreen mode Exit fullscreen mode These are your team's rules — formatting standards, file protection, security gates. Everyone gets them automatically.

.claude/settings.local.json (gitignored):

{ "hooks": { "Notification": [ { "matcher": "", "hooks": [ { "type": "command", "command": "osascript -e 'display notification \" Claude needs you \" with title \" Claude Code \" '" } ] } ] } } Enter fullscreen mode Exit fullscreen mode Personal preferences like notifications and auto-approvals stay local.

echo '{"tool_name":"Edit","tool_input":{"file_path":"src/app.ts"}}' |.claude/hooks/protect-files.sh echo $? Enter fullscreen mode Exit fullscreen mode Check permissions: Ensure hook scripts are executable ( chmod +x ) Full debug mode: Run claude --debug for complete execution traces Common Pitfalls Shell profile noise: If your ~/.zshrc or ~/.bashrc prints output (welcome messages, fortune quotes), it can interfere with hook JSON parsing. Guard with [[ $- == *i* ]] checks.

Stop hook loops: A Stop hook that causes Claude to do more work will trigger another Stop event. Always check stop_hook_active in the input and return ok: true if it's already active.

Matcher case sensitivity: Tool names are case-sensitive. It's Bash, not bash. It's Edit, not edit.

Parallel hook conflicts: Multiple hooks on the same event run in parallel. If two PostToolUse hooks both modify the same file, only the last one to finish wins. Keep hooks independent.

Claude Code hooks are user-defined shell commands, HTTP endpoints, or LLM prompts that execute automatically at specific points in Claude Code's lifecycle. Unlike CLAUDE.md instructions which Claude may or may not follow, hooks are deterministic — they're executed by the harness, not by the AI, so they always run when triggered.

CLAUDE.md provides instructions that Claude should follow but might forget during long sessions or after compaction. Hooks are deterministic automation that the system will execute every time. Use CLAUDE.md for guidelines and context; use hooks for rules that must never be skipped (formatting, file protection, quality gates).

Yes. PreToolUse hooks can inspect any command before it runs and return exit code 2 to block it. The stderr message is sent back to Claude as feedback, so it understands why the action was blocked and can adjust its approach.

Configure shared hooks in.claude/settings.json (committed to git) and personal hooks in.claude/settings.local.json (gitignored). Team rules like formatting and file protection go in the shared config; personal preferences like notifications stay local.

Claude Code supports 27 lifecycle events including PreToolUse, PostToolUse, PermissionRequest, Stop, Notification, SessionStart, CwdChanged, FileChanged, and more. The most commonly used are PreToolUse (security), PostToolUse (formatting), and Stop (quality gates).

Hooks are one piece of the Claude Code customization puzzle. Combined with CLAUDE.md patterns, MCP server integrations, and custom slash commands, you can build a development environment where AI assistance is deeply integrated with your team's specific workflow.

The teams I've seen get the most out of AI coding tools aren't the ones with the best prompts — they're the ones who've automated the repetitive parts so they can focus on the creative work. Hooks are the foundation for that.

Originally published at scotthavird.com. Follow me on X and GitHub.

Quelle: dev.to (dev.to). Original öffnen