NEW: Claude Code Security — research preview

Hooks

Deterministic automation at Claude Code lifecycle events — enforce rules regardless of what Claude decides

NEWRead time: 8 min

title: "Hooks" description: "Deterministic automation at Claude Code lifecycle events — enforce rules regardless of what Claude decides" section: "Core" readTime: "8 min" badge: "NEW"

Hooks

Hooks execute shell commands at fixed lifecycle events in Claude Code. Unlike CLAUDE.md instructions (which Claude reads and tries to follow), hooks are deterministic — they run regardless of what Claude decides to do.

Use hooks when you need guaranteed behavior: run a formatter after every file edit, block dangerous commands before they execute, post to Slack when a session ends.


Hook Events

EventWhen it fires
PreToolUseBefore any tool call executes
PostToolUseAfter a tool call completes
InstructionsLoadedAfter CLAUDE.md and rules files are loaded into context
NotificationWhen Claude sends a user-facing notification
PreCommitBefore a git commit
PreFileDeletionBefore a file is deleted

Configuration

Hooks live in .claude/settings.json (project) or ~/.claude/settings.json (personal):

{
  "hooks": [
    {
      "event": "PostToolUse",
      "matcher": {
        "tool": "Write",
        "pattern": ".*\\.ts$"
      },
      "command": "npx prettier --write $CLAUDE_TOOL_OUTPUT_PATH"
    },
    {
      "event": "PreToolUse",
      "matcher": {
        "tool": "Bash",
        "pattern": "rm -rf"
      },
      "action": "ask"
    },
    {
      "event": "PreToolUse",
      "matcher": {
        "tool": "Bash",
        "pattern": "curl.*prod"
      },
      "action": "block",
      "message": "Direct curl to prod is not allowed. Use the staging environment."
    }
  ]
}

Hook Actions

ActionBehavior
run (default)Execute the shell command
askPause and ask the user for confirmation
blockCancel the tool call; show optional message
notifySend a desktop notification

Available Variables

Hooks receive context via environment variables:

VariableValue
$CLAUDE_TOOL_NAMEName of the tool being called
$CLAUDE_TOOL_INPUTJSON input to the tool
$CLAUDE_TOOL_OUTPUTTool output (PostToolUse only)
$CLAUDE_TOOL_OUTPUT_PATHFile path written (for Write tool)
$CLAUDE_SESSION_IDCurrent session UUID
$CLAUDE_HOOK_EVENTEvent name

Common Patterns

Auto-format on write

{
  "event": "PostToolUse",
  "matcher": { "tool": "Write", "pattern": ".*\\.(ts|tsx|js|jsx)$" },
  "command": "npx eslint --fix $CLAUDE_TOOL_OUTPUT_PATH && npx prettier --write $CLAUDE_TOOL_OUTPUT_PATH"
}

Block secrets from being committed

{
  "event": "PreCommit",
  "command": "git diff --cached | grep -qE '(sk-|AKIA|password=)' && echo 'Possible secret detected' && exit 1 || exit 0"
}

Log which instruction files were loaded (debugging)

{
  "event": "InstructionsLoaded",
  "command": "echo \"[$(date)] Loaded: $CLAUDE_INSTRUCTIONS_FILES\" >> ~/.claude/instruction-log.txt"
}

The InstructionsLoaded event is particularly useful for debugging why a CLAUDE.md rule or path-scoped rule isn't being applied — you can see exactly which files were discovered and when.

Slack notification on session end

{
  "event": "Notification",
  "matcher": { "pattern": "session_end" },
  "command": "curl -s -X POST $SLACK_WEBHOOK -d '{\"text\":\"Claude session $CLAUDE_SESSION_ID finished\"}'"
}

Hooks in Skills

Hooks can be scoped to a skill's lifecycle using the hooks frontmatter field. These hooks only fire when the skill is active:

---
name: deploy
description: Deploy to production
disable-model-invocation: true
hooks:
  - event: PreToolUse
    matcher:
      tool: Bash
      pattern: ".*production.*"
    action: ask
---
 
Deploy the application to production...

Hooks vs CLAUDE.md Instructions

CLAUDE.mdHooks
EnforcementClaude reads and tries to followExecuted by the runtime, always
Best forStyle guidance, conventionsSecurity policies, formatters, notifications
Can be overridden by ClaudeYes (it's context)No
Runs shell commandsNoYes

If an instruction must run at a specific lifecycle point — before every commit, after every file write — use a hook. If it's guidance Claude should follow while coding, use CLAUDE.md.


Security Considerations

  • Hooks in project .claude/settings.json take effect after you accept the workspace trust dialog
  • Review project hooks before trusting a repository — they can run arbitrary shell commands
  • Managed hooks (deployed by IT/DevOps) cannot be overridden by individual settings