Tools
Tools: Hooks: How to Put Guardrails on Your AI Coding Assistant
2026-01-20
0 views
admin
Introduction ## The Problem ## The Solution ## How to Use It ## Pro Tips ## Real-World Use Case ## Conclusion Deterministic control over probabilistic AI — because sometimes you need absolute certainty. Here's the uncomfortable truth about AI coding assistants: they're probabilistic. They make educated guesses. And while those guesses are remarkably good most of the time, "most of the time" isn't good enough when you're about to run rm -rf on production data. Enter Hooks — Claude Code's answer to the age-old question: "How do I trust an AI with my terminal?" Hooks let you define shell commands that run at specific points in Claude's workflow, giving you deterministic control over an otherwise probabilistic system. Think of hooks as your personal security guard, notification system, and workflow automation layer — all rolled into one. They're the safety net that lets you push the boundaries of AI-assisted development while keeping the truly dangerous stuff firmly under your control. AI coding assistants are incredibly powerful, but that power comes with risk. Consider these scenarios: Without some form of intervention layer, you're left with two options: either micromanage every command Claude runs (defeating the purpose of an AI assistant), or cross your fingers and hope for the best. The traditional solution? Manual review of every action. But that's exhausting, error-prone, and doesn't scale. What you need is a way to codify your rules, your preferences, and your safety requirements — then let the system enforce them automatically. Hooks are shell commands that execute at predetermined lifecycle events in Claude Code. They're your programmable intervention layer between "Claude wants to do this" and "Claude actually does this." Getting started with hooks is straightforward. Run the /hooks command in Claude Code to see your options, or configure them directly in .claude/settings.json: Here's a practical example — a hook that blocks dangerous commands: The hook lifecycle events include: 🔒 Layer your defenses: Combine hooks with Claude's built-in permission system. Hooks handle the automated blocking; permissions handle the interactive approval. 📱 Mobile notifications: Use hooks to send push notifications via services like Pushover or ntfy when long-running tasks complete: 📊 Audit everything: Create a PostToolUse hook that logs all Claude actions to a file. Invaluable for debugging and compliance. 🎯 Use matchers wisely: The matcher field accepts regex patterns. Match specific tools (Bash), all tools (.*), or complex patterns (Write|Edit). Scenario: A fintech startup uses Claude Code for rapid development but has strict compliance requirements around data handling. Result: Developers get the full power of AI-assisted coding while the compliance team sleeps soundly knowing every action is validated and logged. Hooks transform Claude Code from a powerful-but-unpredictable assistant into a powerful-and-controlled team member. By injecting deterministic checkpoints into Claude's workflow, you get the best of both worlds: AI's creativity and speed, with your rules firmly in place. The key insight? You don't have to choose between trusting AI completely or not trusting it at all. Hooks let you define exactly where the boundaries are — and those boundaries can be as permissive or restrictive as your situation demands. Coming up on Day 17: We'll explore @ mentions — the fastest way to give Claude the context it needs. Because the only thing better than a safe AI assistant is a safe AI assistant that actually understands what you're working on. This article is part of the "31 Days of Claude Code Features" series. Follow along to discover one powerful feature each day. Templates let you quickly answer FAQs or store snippets for re-use. Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment's permalink. Hide child comments as well For further actions, you may consider blocking this person and/or reporting abuse CODE_BLOCK:
{ "hooks": { "PreToolUse": [ { "matcher": "Bash", "hooks": [ { "type": "command", "command": "python3 ~/.claude/scripts/validate-command.py \"$CLAUDE_TOOL_INPUT\"" } ] } ], "PostToolUse": [ { "matcher": ".*", "hooks": [ { "type": "command", "command": "~/.claude/scripts/notify-complete.sh" } ] } ] }
} Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
{ "hooks": { "PreToolUse": [ { "matcher": "Bash", "hooks": [ { "type": "command", "command": "python3 ~/.claude/scripts/validate-command.py \"$CLAUDE_TOOL_INPUT\"" } ] } ], "PostToolUse": [ { "matcher": ".*", "hooks": [ { "type": "command", "command": "~/.claude/scripts/notify-complete.sh" } ] } ] }
} CODE_BLOCK:
{ "hooks": { "PreToolUse": [ { "matcher": "Bash", "hooks": [ { "type": "command", "command": "python3 ~/.claude/scripts/validate-command.py \"$CLAUDE_TOOL_INPUT\"" } ] } ], "PostToolUse": [ { "matcher": ".*", "hooks": [ { "type": "command", "command": "~/.claude/scripts/notify-complete.sh" } ] } ] }
} COMMAND_BLOCK:
#!/bin/bash
# ~/.claude/scripts/validate-command.py import json
import sys DANGEROUS_PATTERNS = [ "rm -rf /", "DROP DATABASE", "git push --force origin main", "> /dev/sda"
] input_data = json.loads(sys.argv[1])
command = input_data.get("command", "") for pattern in DANGEROUS_PATTERNS: if pattern in command: print(f"BLOCKED: Command contains dangerous pattern '{pattern}'") sys.exit(1) sys.exit(0) Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
#!/bin/bash
# ~/.claude/scripts/validate-command.py import json
import sys DANGEROUS_PATTERNS = [ "rm -rf /", "DROP DATABASE", "git push --force origin main", "> /dev/sda"
] input_data = json.loads(sys.argv[1])
command = input_data.get("command", "") for pattern in DANGEROUS_PATTERNS: if pattern in command: print(f"BLOCKED: Command contains dangerous pattern '{pattern}'") sys.exit(1) sys.exit(0) COMMAND_BLOCK:
#!/bin/bash
# ~/.claude/scripts/validate-command.py import json
import sys DANGEROUS_PATTERNS = [ "rm -rf /", "DROP DATABASE", "git push --force origin main", "> /dev/sda"
] input_data = json.loads(sys.argv[1])
command = input_data.get("command", "") for pattern in DANGEROUS_PATTERNS: if pattern in command: print(f"BLOCKED: Command contains dangerous pattern '{pattern}'") sys.exit(1) sys.exit(0) COMMAND_BLOCK:
curl -s -o /dev/null "https://ntfy.sh/my-claude-notifications" -d "Claude completed: $CLAUDE_TOOL_NAME" Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
curl -s -o /dev/null "https://ntfy.sh/my-claude-notifications" -d "Claude completed: $CLAUDE_TOOL_NAME" COMMAND_BLOCK:
curl -s -o /dev/null "https://ntfy.sh/my-claude-notifications" -d "Claude completed: $CLAUDE_TOOL_NAME" - Claude suggests running a database migration... on your production database
- A seemingly innocent git push --force is about to overwrite your team's work
- You're deep in a debugging session and need to know when Claude completes a long-running task
- Company policy requires all AI-generated code to pass through a security scanner - PreToolUse: Before Claude executes any tool (perfect for validation)
- PostToolUse: After a tool completes (great for notifications or logging)
- Notification: When Claude wants to notify you of something
- Stop: When Claude completes a session - PreToolUse hook scans all Bash commands for database operations and blocks any that target production
- PreToolUse hook ensures all file writes go through the company's code signing process
- PostToolUse hook logs every Claude action to an audit trail with timestamps
- Stop hook generates a summary report of all actions taken during the session
how-totutorialguidedev.toaimlbashshellpythondatabasegit