In this categoryClaude Code ยท 45
- How to Install Claude Code on macOSStart
- How to Install Claude Code on Windows Natively (No WSL)
- How to Install Claude Code on Windows with WSL
- How to Start Your First Project with Claude Code
- How to Create a CLAUDE.md File for Your Project
- How to Configure Claude Code with settings.json
- How to Layer Global, Project, and Local CLAUDE.md Files
- How to Set Up a Permissions Allowlist in Claude Code
- How to Use Claude Code Inside VS Code
- How to Add a Custom Slash Command in Claude Code
- How to Create a Custom Slash Command in Claude Code
- How to Pass Arguments to a Claude Code Slash Command
- How to Use Claude Code Slash Commands (Full List + Examples)
- How to Add Custom Slash Commands to Claude Code (Arguments + Frontmatter)
- How to Fix command not found After Installing Claude Code
- How to Add Any MCP Server to Claude Code
- How to Connect an MCP Server to Claude Code
- Best MCP Servers for Coding in 2026
- How to Add a Filesystem MCP Server to Claude Code
- How to Connect the GitHub MCP Server to Claude Code
- How to Create Your First Subagent in Claude Code
- Context7 Alternatives: MCP Servers for Up-to-Date Docs
- How to Manage MCP Server Scopes in Claude Code
- How to Restrict the Tools a Subagent Can Use
- How to Debug a Failing MCP Server in Claude Code
- How to Generate Unit Tests for a Function with Claude Code
- How to Refactor a Long Function Safely with Claude Code
- How to Debug a Failing Test with Claude Code
- How to Review Your Own Diff Before Committing with Claude Code
- How to Rename a Symbol Across the Codebase with Claude Code
- How to Review a Teammate's Pull Request with Claude Code
- How to Raise Test Coverage on a Specific File with Claude Code
- How to Debug a Runtime Error from a Stack Trace with Claude Code
- How to Add an Integration Test for an API Route with Claude Code
- How to Teach Claude Code Your Test Conventions with CLAUDE.md
- How to Find Which Commit Broke a Test with Claude Code
- How to Set Up Hooks for Automation in Claude Code
- Claude Code Hooks Explained: Events, Config, and Blocking
- How to Block Risky Commands with a Claude Code Hook
- How to Auto-Format Files After Edits with a PostToolUse Hook
- How to Get a Notification When Claude Code Finishes a Task
- How to Give a Coding Agent a Clear Stop Condition
- How to Make an Agent Plan Before It Edits
- How to Keep an Agent Session Focused by Clearing Context
- How to Pin a Specific Model for One Project
How to Block Risky Commands with a Claude Code Hook
Use a PreToolUse hook to inspect Bash commands before they run and deny dangerous ones automatically.
Hooks are shell commands that Claude Code runs at defined points in its lifecycle. A PreToolUse hook fires before a tool executes and can approve or deny the call. This is the cleanest way to enforce a guardrail like never run rm -rf on this machine, because the decision is made by your code, not by trusting the model. This guide builds a hook that blocks destructive Bash commands.
- Claude Code with hooks support
- Comfort editing JSON and a small shell or Python script
- A place to keep the script, for example .claude/hooks
Step 1: Understand the hook contract
A PreToolUse hook receives a JSON payload on stdin describing the tool call. To deny the action it returns a JSON object on stdout with a permission decision of deny and a reason. Exit code 0 with that JSON is how you veto cleanly.
#!/usr/bin/env python3
import json, sys, re
data = json.load(sys.stdin)
cmd = data.get("tool_input", {}).get("command", "")
BLOCKED = [r"rm\s+-rf\s+/", r":\(\)\{", r"mkfs", r"dd\s+if="]
for pattern in BLOCKED:
if re.search(pattern, cmd):
print(json.dumps({
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": f"Blocked dangerous command: {cmd}"
}
}))
sys.exit(0)
sys.exit(0)Step 2: Make the script executable
Step 3: Register the hook in settings
Hooks are configured in settings.json. The matcher selects which tool triggers the hook. Use Bash to target only shell commands so other tools are not slowed down.
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/guard-bash.py"
}
]
}
]
}
}Step 4: Test that the block fires
Ask Claude Code to run something that matches a blocked pattern. The hook should veto the call and the model should report that it was denied, then move on without running it.
Result: any Bash command matching your blocklist is refused before it ever reaches the shell, giving you a deterministic safety net independent of the model's judgment.
Watch related tutorials
5:42
16:30
1:42:18
28:14
41:09
9:47New guides in your inbox
Fresh step-by-step how-to guides as we publish them. One email a week, no more.