Claude Code hooks execution policies provide a programmatic, deterministic security architecture that intercepts agent tool calls before subshell execution. Unlike natural language guidelines in CLAUDE.md that can drift under heavy token loads, PreToolUse hooks run as native operating system processes managed directly by the Claude Code harness. By inspecting structured JSON payloads passed via stdin, executing custom validation scripts, and returning exit code 2 on policy violations, developers can stop destructive commands, enforce branch protection, sanitize environment variables, and eliminate credential leaks across their codebase.
What Are Claude Code Pre-Tool Hooks and Shell Interceptors?
Claude Code pre-tool hooks are deterministic lifecycle interceptors that execute before a tool call runs inside the agent loop. When Claude Code attempts to run a terminal utility through the Bash tool, edit source code via Edit, write files via Write, or invoke an external tool via Model Context Protocol (MCP), the runtime halts execution and executes your designated hook command with full context payload on stdin.
Traditional AI developer workflows rely on instructions declared in repository documentation files like CLAUDE.md or AGENTS.md. However, LLM adherence to prompt-based instructions is probabilistic. When an agent enters complex multi-step reasoning trajectories spanning dozens of file inspections and dependency builds, prompt guidelines suffer from context dilution. As documented in the official Anthropic Claude Code documentation on agent lifecycle extensions:
“PreToolUse hooks execute immediately before a tool call runs in the agentic loop. Returning exit code 2 immediately blocks execution and returns stderr to Claude so the model can self-correct.”
In Claude Code, PreToolUse hooks intercept tool calls deterministically via stdin JSON before subshell invocation, whereas CLAUDE.md instructions are probabilistic and vulnerable to context dilution. While static permission systems such as autoApprove allowlists in Claude Code tool permissions evaluate simple command prefix strings, programmatic shell interceptors inspect complete argument flags, working directories, and shell redirections before any child process is spawned.
How Does the PreToolUse Lifecycle Intercept Shell Commands?
The PreToolUse lifecycle intercepts commands by executing a synchronous subshell process that evaluates tool intent and returns a binary authorization decision. When an action matches the configured hook pattern, Claude Code pauses agent execution, serializes the pending tool request into a JSON string, and pipes it directly to the hook process standard input.
Claude Code defines 5 distinct lifecycle hook events: PreToolUse, PostToolUse, UserPromptSubmit, Notification, and Stop. For execution policy enforcement, PreToolUse is the foundational control gate. The JSON payload delivered to your hook script contains comprehensive runtime telemetry:
{
"session_id": "sess_01JBCDEFGH890123456789ABCD",
"cwd": "/Users/developer/projects/payment-service",
"hook_event_name": "PreToolUse",
"tool_name": "Bash",
"tool_input": {
"command": "git push origin main --force"
},
"agent_id": "subagent_researcher_01"
}Once the hook process receives this payload, it enforces deterministic workspace rules and signals its verdict back to Claude Code using POSIX process exit codes:
- Exit Code 0 (Allow): The hook script approves the action. Claude Code proceeds immediately to execute the tool or prompts the developer for interactive confirmation if the tool is not in the
autoApprovelist. - Exit Code 1 (Warning / Non-Blocking Error): Treated by the Claude Code harness as an internal hook failure or advisory warning, logging the error to diagnostics while permitting the agent turn to continue.
- Exit Code 2 (Strict Block / Deny): Immediately halts tool invocation. Claude Code reads everything written by the interceptor script to
stderrand surfaces it directly to the model as a prompt error message.
Returning exit code 2 from a Claude Code PreToolUse hook halts tool execution immediately and pipes stderr back into Claude's prompt context for automated self-correction. Because the agent receives your precise explanation (e.g., “Direct force-pushes to origin/main are forbidden by company security policy”), Claude Code adapts in real time, creating a dedicated feature branch or choosing a non-destructive command without requiring human manual intervention.
Configuring PreToolUse Execution Policies in settings.json
PreToolUse execution policies are configured by declaring hook matchers inside the hooks dictionary of .claude/settings.json. You can target specific built-in tools like Bash, file modification tools like Edit and Write, or third-party MCP utilities by defining exact names or regular expression pattern matchers.
The following production configuration demonstrates how to wire a workspace security interceptor script into Claude Code, complete with execution timeouts and multi-tool matcher patterns:
{
"permissions": {
"autoApprove": [
"Bash(git status)",
"Bash(npm test)",
"Bash(cargo test)"
]
},
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": ""$CLAUDE_PROJECT_DIR"/.claude/hooks/enforce-workspace-policies.sh",
"timeout": 10
}
]
},
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": ""$CLAUDE_PROJECT_DIR"/.claude/hooks/enforce-file-boundaries.sh",
"timeout": 5
}
]
}
]
}
}Claude Code hooks configured in project-level .claude/settings.json automatically scope child subagents by passing the active subagent identifier in the agent_id payload field. The configuration above establishes a strict 10-second timeout safeguard. If a hook script hangs due to an unresponsive network request or locked database socket, Claude Code terminates the hook process and alerts the user, preventing infinite execution stalls.
Writing a Deterministic Bash Interceptor Script
A deterministic bash interceptor script reads the incoming JSON payload from standard input, extracts command parameters using utilities like jq, and validates execution against strict enterprise security constraints. Similar to traditional repository hooks defined in the official Git Hooks documentation, Claude Code hooks execute locally in the host environment, guaranteeing sub-50ms execution latency with 0-byte external package dependencies.
Below is an enterprise-grade bash interceptor script (.claude/hooks/enforce-workspace-policies.sh) designed to block destructive Git commands, protect sensitive environment variables, and prevent unauthorized file deletions:
#!/usr/bin/env bash
# .claude/hooks/enforce-workspace-policies.sh
# Deterministic PreToolUse Execution Interceptor for Claude Code
set -euo pipefail
# Read complete JSON payload from stdin
PAYLOAD=$(cat)
# Extract tool name and command string safely
TOOL_NAME=$(printf '%s' "$PAYLOAD" | jq -r '.tool_name // empty')
COMMAND=$(printf '%s' "$PAYLOAD" | jq -r '.tool_input.command // empty')
SUBAGENT_ID=$(printf '%s' "$PAYLOAD" | jq -r '.agent_id // "main"')
# Rule 1: Block destructive Git operations
if printf '%s' "$COMMAND" | grep -Eiq 'git[[:space:]]+push[[:space:]]+.*(--force|-f[[:space:]]|origin[[:space:]]+main|origin[[:space:]]+master)'; then
printf 'SECURITY POLICY VIOLATION: Direct force-pushes and pushes to protected main/master branches are blocked. Create a feature branch and submit a pull request.
' >&2
exit 2
fi
# Rule 2: Prevent credential and secret exfiltration
if printf '%s' "$COMMAND" | grep -Eiq '(\.env|aws_secret_access_key|id_rsa|npmrc|ghp_)'; then
# Allow viewing git status or listing directory, but block reading or printing contents
if printf '%s' "$COMMAND" | grep -Eiq '(cat|head|tail|grep|curl|wget|base64)[[:space:]]'; then
printf 'DATA LOSS PREVENTION: Commands reading, printing, or exfiltrating private credential files (.env, private keys, tokens) are forbidden.
' >&2
exit 2
fi
fi
# Rule 3: Restrict recursive file deletions outside build directories
if printf '%s' "$COMMAND" | grep -Eiq 'rm[[:space:]]+-[a-zA-Z]*r[a-zA-Z]*[[:space:]]+'; then
# Permit cleaning build artifacts, but block root or source code recursion
if ! printf '%s' "$COMMAND" | grep -Eiq 'rm[[:space:]]+-[a-zA-Z]*r[a-zA-Z]*[[:space:]]+(dist|build|target|\.next|node_modules)[/]?'; then
printf 'SAFETY GATE: Recursive rm commands are restricted to build directories (dist, build, target). Manual deletion required for source directories.
' >&2
exit 2
fi
fi
# Rule 4: Subagent privilege sandboxing
if [ "$SUBAGENT_ID" != "main" ] && [ "$SUBAGENT_ID" != "null" ]; then
if printf '%s' "$COMMAND" | grep -Eiq '(npm[[:space:]]+install|pip[[:space:]]+install|cargo[[:space:]]+install|brew[[:space:]]+install)'; then
printf 'SANDBOX RESTRICTION: Child subagents are restricted from installing global or project packages. Request dependency changes in the main conversation thread.
' >&2
exit 2
fi
fi
# All workspace security checks passed: permit tool execution
exit 0Notice the architecture of the script: every policy violation outputs a concise, actionable explanation to stderr and terminates with exit 2. When Claude Code executes this hook against an unauthorized command like git push origin main --force, the agent does not crash. Instead, Claude reads the stderr output and responds: “I noticed that direct force-pushes to main are restricted by repository policy. Let me switch to a new branch named feature/payment-webhook-fix and push there instead.”
How Do Claude Code Hooks Compare to Antigravity, Cursor, and Codex Policies?
Claude Code hooks compare favorably to competing agent architectures by offering deterministic programmatic execution rather than static prompt boundaries. While modern coding tools manage agent permissions through different layers of the operating system stack, Claude Code is unique in providing raw POSIX process lifecycle hooks with bidirectional feedback.
While Claude Code enforces runtime execution policies via stdin/stderr process hooks, Google Antigravity isolates tool execution through declarative sidecar modes (inherit, share, branch) and explicit tool capability flags. In Google Antigravity tool execution policies, administrators declare strict subagent tool access (EnableWriteTools, EnableMCPTools) within isolated Git worktrees. Cursor takes a visual, user-centric approach by relying on file globs inside .cursor/rules/*.mdc files combined with interactive terminal confirmation popups. OpenAI Codex CLI leverages native subshell sandbox isolation modes configured via command-line flags.
| Execution Dimension | Claude Code | Google Antigravity | Cursor IDE | OpenAI Codex CLI |
|---|---|---|---|---|
| Policy Enforcement Mechanism | Deterministic PreToolUse lifecycle process hooks via stdin JSON & exit codes | Declarative tool policies (EnableWriteTools, EnableMCPTools) & sidecars | Declarative .cursor/rules/*.mdc globs with interactive UI permission prompts | Process-level subshell sandbox flags (--sandbox, --ask-execute) |
| Inspection Granularity | Full JSON payload parsing (command, tool arguments, working directory, session ID) | Subagent permission boundaries and isolated Git worktree clones | File path globs, active file context, and manual user approval prompts | Command string prefix matching and subshell exit signal traps |
| Automated Blocking Signal | Process exit code 2 with stderr fed into model context for self-correction | Pre-flight subagent capability denial and workspace boundary enforcement | Terminal confirmation dialog prompt rejection by human operator | Subshell non-zero exit interruption and interactive terminal pause |
| Subagent Scoping Support | Automatic hook inheritance with agent_id passed in JSON context payload | First-class subagent isolation modes (branch, share, inherit) | Single-agent Composer sessions without nested subagent lifecycle delegation | Single-thread CLI execution runner without nested subagent pipelines |
| Execution Latency Overhead | Sub-50ms execution overhead for compiled binaries or native POSIX shell scripts | Zero runtime invocation overhead via declarative daemon capability gates | Zero automated overhead; variable latency dependent on human user interaction | Minimal subshell fork/exec overhead managed by native CLI runtime |
Developers working across polyglot agent environments frequently encounter integration gaps between these models. A team that implements strict regex interceptors in Claude Code must duplicate those policies as declarative rules in Cursor and container sandboxes in Codex. For comprehensive guidance on synchronizing configuration schemas across operating systems, see our systems architecture report on moving AI agent skills and rules across macOS, Linux, and Windows.
The Policy Drift Sprawl Moment: When Guardrails Stay in One Repo
The sprawl moment in workspace policy enforcement occurs when an engineer crafts a battle-tested PreToolUse bash interceptor script in one repository to prevent accidental credential exfiltration or destructive git pushes, only to switch to a second project or a remote development machine where that .claude/settings.json file does not exist. Believing the security guardrails are active globally, the engineer authorizes an autonomous multi-step refactoring session—only for the unconstrained agent in the new project to run an unchecked destructive command or push untracked secret keys to a remote repository because the policy script remained isolated in a local folder on another laptop.
Prompttly is a skill manager for AI agents — one library for your skills and prompts that syncs into Claude Code, Codex, ChatGPT, and Claude and is one hotkey away on your Mac, so your setup follows you across every machine, repo, and tool.
With Prompttly, your verified agent skills, custom instructions, and workspace guardrails do not remain trapped inside forgotten local directories. Instead of manually copying shell interceptors across dozens of project folders or reconstructing hook logic after switching laptops, Prompttly synchronizes your complete agent operating system into native directories across every workstation.
Prompttly's native Mac hotkey palette provides instant sub-200ms access to verified prompt skills and workspace policies across terminal agents, web browsers, and IDEs without manual copy-paste. Whether you are generating structured skills using our free Claude Skill Creator, managing corporate AI prompt libraries for developers, or establishing multi-repo governance, Prompttly ensures your security policies and workflow prompts travel with you everywhere you build.
Frequently Asked Questions About Claude Code Hooks
What is the difference between Claude Code tool autoApprove and PreToolUse hooks?
Claude Code autoApprove settings in settings.json perform static prefix matching against command strings to bypass human interactive prompts, but cannot inspect dynamic arguments, evaluate environment state, or validate file paths. In contrast, PreToolUse hooks execute a deterministic operating system process that receives the complete tool invocation as structured JSON via stdin, enabling programmatic policy validation and automated exit-code blocking before any tool executes.
Which exit code should a Claude Code hook use to block a dangerous shell command?
A Claude Code hook script must return exit code 2 to strictly block a tool call from executing. When a hook exits with status 2, Claude Code intercepts the failure, halts execution, and pipes all content written to stderr directly back into Claude's conversation context as an error message, allowing the model to understand why the command violated workspace policies and attempt an alternative, compliant strategy.
Where are Claude Code lifecycle hooks configured across project and global scopes?
Project-level lifecycle hooks are configured in .claude/settings.json at the root of a Git repository, while global user-wide hooks reside in ~/.claude/settings.json on macOS and Linux systems. Claude Code merges these configuration files hierarchically, allowing engineering teams to commit mandatory security policies in repository-shared settings while individual developers define local helper hooks in their home directory.
Do Claude Code PreToolUse hooks intercept subagent tool invocations automatically?
Yes. When Claude Code spawns child subagents using the Task tool to perform parallel codebase exploration or test execution, those child processes inherit all project-level PreToolUse hook configurations. Claude Code injects the active subagent identifier into the context payload under the agent_id field, allowing hook scripts to enforce stricter execution policies on autonomous subagents than on interactive user sessions.
How do you prevent PreToolUse hook scripts from drifting across multiple developer laptops?
To prevent hook script divergence across developer workstations, engineering organizations maintain unified policy and skill libraries using tools like Prompttly. Instead of manually copying shell interceptors between local .claude directories, Prompttly synchronizes validated hook templates, execution rules, and agent skills directly into Claude Code, Cursor, and Codex environments on macOS and Linux workstations.
Related Prompt Resources
Standardize Agent Execution Policies Across Every Machine
Stop copying shell interceptor scripts and settings across developer repos. Sync verified Claude Code hooks, Cursor rules, and agent skills across every machine with Prompttly.