Skip to main content
Bridge

October 25, 2026

How to Block Risky Commands in Claude Code and Cursor

Learn how to set up deterministic agent hooks in Claude Code and Cursor to intercept and block dangerous terminal commands before execution. You will write executable scripts that parse tool payloads, ensuring your local environment remains safe from accidental destructive actions.

Hooks and automated checksClaude CodeCursorAgent HooksPlaybookHow-to
HyunKi Lee··4 min read

Verified on October 25, 2026

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).
  • jq installed 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 +x causes 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 jq on 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

Want to try Bridge?

Bridge ships November 1, 2026 on Android. Become a founding member and lock in founders pricing.

Become a founding member