To block destructive commands before an agent executes them, configure a pre-execution hook script that intercepts shell calls out of band. Unlike natural language instructions in prompt files, a hook provides a deterministic control plane: it parses proposed tool inputs and halts execution immediately whenever a dangerous command pattern appears.
Before you start
- Claude Code CLI (v1.0.38 or later, check with
claude --version) or Cursor (v1.7 beta or later). jqinstalled on your system PATH to parse JSON payloads.- Basic shell scripting tools to create executable hook files.
How command interception works
flowchart LR
A[Agent proposes tool command] --> B[Pre-execution hook receives payload]
B --> C{Matches banned pattern?}
C -- Yes --> D[Exit code 2 with error reason]
D --> E[Runtime aborts execution]
C -- No --> F[Exit code 0]
F --> G[Command runs in shell]
Step 1: Write the guard script
Create a directory for your project hooks and add a shell script that inspects commands for dangerous operations, such as forced git pushes or recursive file removals.
mkdir -p .claude/hooks
Save the following logic to .claude/hooks/block-risky.sh:
#!/usr/bin/env bash
set -euo pipefail
PAYLOAD=$(cat)
COMMAND=$(echo "$PAYLOAD" | jq -r '.tool_input.command // empty')
if [[ "$COMMAND" =~ rm\ -rf ]] || [[ "$COMMAND" =~ git\ push\ .*\--force ]]; then
echo "Blocked: Dangerous command detected ('$COMMAND')." >&2
exit 2
fi
exit 0
Step 2: Make the script executable
Set execute permissions on the hook script so the agent runtime harness can run it directly.
chmod +x .claude/hooks/block-risky.sh
Step 3: Register the hook in your project configuration
Register the script under the pre-tool execution stage in your agent settings file (.claude/settings.json for Claude Code or your Cursor configuration).
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"command": ".claude/hooks/block-risky.sh"
}
]
}
}
When deciding how to structure your control layers across skills, commands, or hooks, review Skills, Commands, Subagents or Hooks: Which to Use.
Step 4 (optional): Return structured JSON decisions
Instead of halting with exit code 2, you can return exit code 0 alongside a structured JSON payload to escalate the decision to human approval or modify inputs.
#!/usr/bin/env bash
set -euo pipefail
PAYLOAD=$(cat)
COMMAND=$(echo "$PAYLOAD" | jq -r '.tool_input.command // empty')
if [[ "$COMMAND" =~ rm\ -rf ]]; then
echo '{"permissionDecision": "ask", "reason": "Requires manual user confirmation."}'
exit 0
fi
echo '{"permissionDecision": "allow"}'
exit 0
Step 5: Test the block
Run a command session and ask the agent to execute a prohibited command:
claude "Run rm -rf ./tmp"
The hook intercepts the tool call, sends the error output from stderr back to the model, and aborts the command before the shell executes it.
Common mistakes
- Missing executable permissions. Forgetting
chmod +xcauses the agent runtime to fail when triggering the hook. - Relying purely on prompt instructions. Prompts are probabilistic and can slip under long context windows. For why deterministic checks prevent failures, read AI Agent Oversight: Why Passive Approval Fails Systems.
- Missing
jqon PATH. If the hook script cannot parse the incoming JSON payload, it will fail open or crash the execution loop.
Frequently asked questions
What exit code stops a command in Claude Code?
Exit code 2 halts execution and sends whatever the script printed to stderr back to the agent as the reason for blocking. Exit code 0 permits execution to proceed.
Can I use static configuration instead of a hook script?
Yes. If your checks only involve static strings, declarative rules in permissions.deny provide zero-overhead blocking. Use executable hook scripts when you need pattern matching or dynamic state inspection.
Why do hooks work better than prompt reminders?
Prompt rules degrade as the context window fills with tool output. Hooks run in the execution runtime outside the LLM, enforcing deterministic boundaries regardless of context size.
Related
- Skills, Commands, Subagents or Hooks: Which to Use details when to reach for hooks over subagents and prompt files.
- AI Agent Oversight: Why Passive Approval Fails Systems analyzes why mechanical verification outperforms prompt instructions.
- Why AI agent checks Outperform Reminders in Mobile Dev demonstrates automated pre-commit and tool verification patterns.
- External reference: Pushary: Claude Code Hooks Explained.