agents-hooks

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Claude Code Hooks — Meta Reference

Claude Code 钩子 — 元参考文档

This skill provides the definitive reference for creating Claude Code hooks. Use this when building automation that triggers on Claude Code events.

本技能提供了创建Claude Code钩子的权威参考。在构建基于Claude Code事件触发的自动化时可使用本文档。

When to Use This Skill

何时使用本技能

  • Building event-driven automation for Claude Code
  • Creating PreToolUse guards to block dangerous commands
  • Implementing PostToolUse formatters, linters, or auditors
  • Adding Stop hooks for testing or notifications
  • Setting up SessionStart/SessionEnd for environment management
  • Integrating Claude Code with CI/CD pipelines (headless mode)

  • 为Claude Code构建事件驱动型自动化
  • 创建PreToolUse防护机制以阻止危险命令
  • 实现PostToolUse格式化工具、代码检查器或审计器
  • 添加用于测试或通知的Stop钩子
  • 设置SessionStart/SessionEnd以进行环境管理
  • 将Claude Code与CI/CD流水线集成(无头模式)

Quick Reference

快速参考

EventTriggerUse Case
SessionStart
Session begins/resumesInitialize environment
UserPromptSubmit
User submits promptPreprocess/validate input
PreToolUse
Before tool executionValidate, block dangerous commands
PermissionRequest
Permission dialog shownAuto-allow/deny permissions
PostToolUse
After tool succeedsFormat, audit, notify
PostToolUseFailure
After tool failsCapture failures, add guidance
SubagentStart
Subagent spawnsInspect subagent metadata
Stop
When Claude finishesRun tests, summarize
SubagentStop
Subagent finishesVerify subagent completion
Notification
On notificationsAlert integrations
PreCompact
Before context compactionPreserve critical context
Setup
--init
/
--maintenance
Initialize repo/env
SessionEnd
Session endsCleanup, save state
事件触发时机适用场景
SessionStart
会话开始/恢复初始化环境
UserPromptSubmit
用户提交提示词预处理/验证输入
PreToolUse
工具执行前验证、阻止危险命令
PermissionRequest
显示权限对话框时自动允许/拒绝权限
PostToolUse
工具执行成功后格式化、审计、通知
PostToolUseFailure
工具执行失败后捕获失败、添加指导
SubagentStart
子Agent生成时检查子Agent元数据
Stop
Claude完成任务时运行测试、生成总结
SubagentStop
子Agent完成任务时验证子Agent执行结果
Notification
收到通知时触发集成告警
PreCompact
上下文压缩前保留关键上下文
Setup
执行
--init
/
--maintenance
初始化仓库/环境
SessionEnd
会话结束时清理、保存状态

Hook Structure

钩子结构

text
.claude/hooks/
├── pre-tool-validate.sh
├── post-tool-format.sh
├── post-tool-audit.sh
├── stop-run-tests.sh
└── session-start-init.sh

text
.claude/hooks/
├── pre-tool-validate.sh
├── post-tool-format.sh
├── post-tool-audit.sh
├── stop-run-tests.sh
└── session-start-init.sh

Configuration

配置

settings.json

settings.json

json
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/post-tool-format.sh"
          }
        ]
      }
    ],
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/pre-tool-validate.sh"
          }
        ]
      }
    ],
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/stop-run-tests.sh"
          }
        ]
      }
    ]
  }
}

json
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/post-tool-format.sh"
          }
        ]
      }
    ],
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/pre-tool-validate.sh"
          }
        ]
      }
    ],
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/stop-run-tests.sh"
          }
        ]
      }
    ]
  }
}

Execution Model (Jan 2026)

执行模型(2026年1月)

  • Hooks receive a JSON payload via stdin (treat it as untrusted input) and run with your user permissions (outside the Bash tool sandbox).
  • Default timeout is 60s per hook command; all matching hooks run in parallel; identical commands are deduplicated.
  • 钩子通过stdin接收JSON负载(需将其视为不可信输入),并以你的用户权限运行(在Bash工具沙箱之外)。
  • 默认超时时间为每个钩子命令60秒;所有匹配的钩子并行运行;重复命令会被自动去重。

Hook Input (stdin)

钩子输入(stdin)

json
{
  "hook_event_name": "PreToolUse",
  "tool_name": "Bash",
  "tool_input": {
    "command": "ls -la"
  }
}
json
{
  "hook_event_name": "PreToolUse",
  "tool_name": "Bash",
  "tool_input": {
    "command": "ls -la"
  }
}

Environment Variables (shell)

环境变量(Shell)

VariableDescription
CLAUDE_PROJECT_DIR
Absolute project root where Claude Code started
CLAUDE_PLUGIN_ROOT
Plugin root (plugin hooks only)
CLAUDE_CODE_REMOTE
"true"
in remote/web environments; empty/local otherwise
CLAUDE_ENV_FILE
File path to persist
export ...
lines (available in SessionStart; check docs for Setup support)

变量描述
CLAUDE_PROJECT_DIR
Claude Code启动时的项目根目录绝对路径
CLAUDE_PLUGIN_ROOT
插件根目录(仅适用于插件钩子)
CLAUDE_CODE_REMOTE
在远程/网页环境中为
"true"
;本地环境为空
CLAUDE_ENV_FILE
用于持久化
export ...
语句的文件路径(SessionStart时可用;请查阅文档了解Setup的支持情况)

Exit Codes

退出码

CodeMeaningNotes
0
SuccessJSON written to stdout is parsed for structured control
2
Blocking error
stderr
becomes the message; JSON in stdout is ignored
OtherNon-blocking errorExecution continues;
stderr
is visible in verbose mode
Stdout injection note: for
UserPromptSubmit
,
SessionStart
, and
Setup
, non-JSON stdout (exit 0) is injected into Claude’s context; most other events show stdout only in verbose mode.

代码含义说明
0
成功标准输出中的JSON会被解析以用于结构化控制
2
阻塞性错误标准错误输出会作为提示信息;标准输出中的JSON会被忽略
其他非阻塞性错误执行会继续;标准错误输出仅在 verbose 模式下可见
标准输出注入说明:对于
UserPromptSubmit
SessionStart
Setup
事件,非JSON格式的标准输出(退出码0)会被注入到Claude的上下文中;大多数其他事件仅在verbose模式下显示标准输出。

Decision Control + Input Modification (v2.0.10+)

决策控制 + 输入修改(v2.0.10+)

PreToolUse hooks can allow/deny/ask and optionally modify the tool input via
updatedInput
.
PreToolUse钩子可通过
updatedInput
字段允许/拒绝/询问权限,还可选择性修改工具输入。

Hook Output Schema

钩子输出 Schema

json
{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "allow",
    "permissionDecisionReason": "Reason shown to user (and to Claude on deny)",
    "updatedInput": { "command": "echo 'modified'" },
    "additionalContext": "Extra context added before tool runs"
  }
}
Note: older
decision
/
reason
fields are deprecated; prefer the
hookSpecificOutput.*
fields.
json
{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "allow",
    "permissionDecisionReason": "显示给用户的理由(拒绝时也会显示给Claude)",
    "updatedInput": { "command": "echo 'modified'" },
    "additionalContext": "工具运行前添加的额外上下文"
  }
}
注意:旧版的
decision
/
reason
字段已被弃用;建议使用
hookSpecificOutput.*
字段。

Example: Redirect Sensitive File Edits

示例:重定向敏感文件编辑

bash
#!/bin/bash
set -euo pipefail

INPUT="$(cat)"
FILE_PATH="$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')"
bash
#!/bin/bash
set -euo pipefail

INPUT="$(cat)"
FILE_PATH="$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')"

Redirect package-lock.json edits to /dev/null

Redirect package-lock.json edits to /dev/null

if [[ "$FILE_PATH" == *"package-lock.json" ]]; then UPDATED_INPUT="$(echo "$INPUT" | jq -c '.tool_input | .file_path = "/dev/null"')" jq -cn --argjson updatedInput "$UPDATED_INPUT" '{ hookSpecificOutput: { hookEventName: "PreToolUse", permissionDecision: "allow", permissionDecisionReason: "Redirected write to /dev/null", updatedInput: $updatedInput } }' exit 0 fi
echo '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"allow"}}'
undefined
if [[ "$FILE_PATH" == *"package-lock.json" ]]; then UPDATED_INPUT="$(echo "$INPUT" | jq -c '.tool_input | .file_path = "/dev/null"')" jq -cn --argjson updatedInput "$UPDATED_INPUT" '{ hookSpecificOutput: { hookEventName: "PreToolUse", permissionDecision: "allow", permissionDecisionReason: "Redirected write to /dev/null", updatedInput: $updatedInput } }' exit 0 fi
echo '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"allow"}}'
undefined

Example: Strip Sensitive Files from Git Add

示例:从Git Add中移除敏感文件

bash
#!/bin/bash
set -euo pipefail

INPUT="$(cat)"
TOOL_NAME="$(echo "$INPUT" | jq -r '.tool_name')"
CMD="$(echo "$INPUT" | jq -r '.tool_input.command // empty')"

if [[ "$TOOL_NAME" == "Bash" && "$CMD" =~ ^git[[:space:]]+add ]]; then
  # Remove .env files from staging
  SAFE_CMD="$(echo "$CMD" | sed 's/\.env[^ ]*//g')"
  if [[ "$SAFE_CMD" != "$CMD" ]]; then
    echo '{}' | jq -cn --arg cmd "$SAFE_CMD" '{
      hookSpecificOutput: {
        hookEventName: "PreToolUse",
        permissionDecision: "allow",
        permissionDecisionReason: "Removed .env from git add",
        updatedInput: { command: $cmd }
      }
    }'
    exit 0
  fi
fi

echo '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"allow"}}'

bash
#!/bin/bash
set -euo pipefail

INPUT="$(cat)"
TOOL_NAME="$(echo "$INPUT" | jq -r '.tool_name')"
CMD="$(echo "$INPUT" | jq -r '.tool_input.command // empty')"

if [[ "$TOOL_NAME" == "Bash" && "$CMD" =~ ^git[[:space:]]+add ]]; then
  # Remove .env files from staging
  SAFE_CMD="$(echo "$CMD" | sed 's/\.env[^ ]*//g')"
  if [[ "$SAFE_CMD" != "$CMD" ]]; then
    echo '{}' | jq -cn --arg cmd "$SAFE_CMD" '{
      hookSpecificOutput: {
        hookEventName: "PreToolUse",
        permissionDecision: "allow",
        permissionDecisionReason: "Removed .env from git add",
        updatedInput: { command: $cmd }
      }
    }'
    exit 0
  fi
fi

echo '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"allow"}}'

Prompt-Based Hooks

基于提示词的钩子

For complex decisions, use LLM-evaluated hooks (
type: "prompt"
) instead of bash scripts. They are most useful for
Stop
and
SubagentStop
decisions.
对于复杂决策,可使用LLM评估的钩子(
type: "prompt"
)替代Bash脚本。它们在
Stop
SubagentStop
决策场景中最为实用。

Configuration

配置

json
{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "prompt",
            "prompt": "Evaluate whether Claude should stop. Context JSON: $ARGUMENTS. Return {\"ok\": true} if all tasks are complete, otherwise {\"ok\": false, \"reason\": \"what remains\"}.",
            "timeout": 30
          }
        ]
      }
    ]
  }
}
json
{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "prompt",
            "prompt": "Evaluate whether Claude should stop. Context JSON: $ARGUMENTS. Return {\"ok\": true} if all tasks are complete, otherwise {\"ok\": false, \"reason\": \"what remains\"}.",
            "timeout": 30
          }
        ]
      }
    ]
  }
}

Response Schema

响应 Schema

  • Allow:
    {"ok": true}
  • Block:
    {"ok": false, "reason": "Explanation shown to Claude"}
  • 允许:
    {"ok": true}
  • 阻止:
    {"ok": false, "reason": "显示给Claude的解释"}

Combining Command and Prompt Hooks

组合命令型与提示词型钩子

Use command hooks for fast, deterministic checks. Use prompt hooks for nuanced decisions:
json
{
  "Stop": [
    {
      "hooks": [
        { "type": "command", "command": ".claude/hooks/quick-check.sh" },
        { "type": "prompt", "prompt": "Verify code quality meets standards" }
      ]
    }
  ]
}

使用命令型钩子进行快速、确定性检查。使用提示词型钩子进行精细化决策:
json
{
  "Stop": [
    {
      "hooks": [
        { "type": "command", "command": ".claude/hooks/quick-check.sh" },
        { "type": "prompt", "prompt": "Verify code quality meets standards" }
      ]
    }
  ]
}

Permission Prompt Fatigue Reduction Pattern

权限提示疲劳缓解模式

Use this when frequent approval dialogs slow down repeated safe workflows.
当频繁的审批对话框拖慢重复的安全工作流时,可使用此模式。

Strategy

策略

  1. Identify repetitive, low-risk command prefixes (for example: test runners, read-only diagnostics).
  2. Approve narrow prefixes instead of full commands.
  3. Keep destructive or broad shells (
    rm
    ,
    git reset --hard
    , generic interpreters with arbitrary input) out of auto-approval rules.
  4. Re-check approved prefixes periodically; remove stale ones.
  1. 识别重复出现的低风险命令前缀(例如:测试运行器、只读诊断工具)。
  2. 仅批准范围狭窄的前缀,而非完整命令。
  3. 禁止将破坏性或宽泛的Shell命令(
    rm
    git reset --hard
    、可处理任意输入的通用解释器)加入自动批准规则。
  4. 定期重新检查已批准的前缀;移除不再使用的规则。

Practical Guardrails

实用防护措施

  • Allow only task-scoped prefixes (example:
    npm run test:e2e
    ), not unrestricted executors.
  • Keep separate policy for write-outside-workspace actions.
  • Pair allow-rules with deny-rules for dangerous patterns.
  • 仅允许任务范围明确的前缀(例如:
    npm run test:e2e
    ),而非无限制的执行器。
  • 为工作区外的写入操作设置单独的策略。
  • 将允许规则与针对危险模式的拒绝规则配对使用。

Outcome

效果

This reduces repeated permission interruptions while preserving high-safety boundaries.
此模式可减少重复的权限中断,同时保留高安全边界。

Hook Templates

钩子模板

Pre-Tool Validation

工具执行前验证

bash
#!/bin/bash
set -euo pipefail

INPUT="$(cat)"
TOOL_NAME="$(echo "$INPUT" | jq -r '.tool_name')"
CMD="$(echo "$INPUT" | jq -r '.tool_input.command // empty')"

if [[ "$TOOL_NAME" == "Bash" ]]; then
  # Block rm -rf /
  if echo "$CMD" | grep -qE 'rm\s+-rf\s+/'; then
    echo '{}' | jq -cn '{
      hookSpecificOutput: {
        hookEventName: "PreToolUse",
        permissionDecision: "deny",
        permissionDecisionReason: "Dangerous rm command detected"
      }
    }'
    exit 0
  fi

  # Block force push to main
  if echo "$CMD" | grep -qE 'git\s+push.*--force.*(main|master)'; then
    echo '{}' | jq -cn '{
      hookSpecificOutput: {
        hookEventName: "PreToolUse",
        permissionDecision: "deny",
        permissionDecisionReason: "Force push to main/master not allowed"
      }
    }'
    exit 0
  fi

  # Soft-warning: possible credential exposure
  if echo "$CMD" | grep -qE '(password|secret|api_key)\s*='; then
    echo '{}' | jq -cn '{
      hookSpecificOutput: {
        hookEventName: "PreToolUse",
        permissionDecision: "ask",
        permissionDecisionReason: "Possible credential exposure in command",
        additionalContext: "Command may include a secret. Confirm intent and avoid committing secrets."
      }
    }'
    exit 0
  fi
fi

exit 0
bash
#!/bin/bash
set -euo pipefail

INPUT="$(cat)"
TOOL_NAME="$(echo "$INPUT" | jq -r '.tool_name')"
CMD="$(echo "$INPUT" | jq -r '.tool_input.command // empty')"

if [[ "$TOOL_NAME" == "Bash" ]]; then
  # Block rm -rf /
  if echo "$CMD" | grep -qE 'rm\s+-rf\s+/'; then
    echo '{}' | jq -cn '{
      hookSpecificOutput: {
        hookEventName: "PreToolUse",
        permissionDecision: "deny",
        permissionDecisionReason: "Dangerous rm command detected"
      }
    }'
    exit 0
  fi

  # Block force push to main
  if echo "$CMD" | grep -qE 'git\s+push.*--force.*(main|master)'; then
    echo '{}' | jq -cn '{
      hookSpecificOutput: {
        hookEventName: "PreToolUse",
        permissionDecision: "deny",
        permissionDecisionReason: "Force push to main/master not allowed"
      }
    }'
    exit 0
  fi

  # Soft-warning: possible credential exposure
  if echo "$CMD" | grep -qE '(password|secret|api_key)\s*='; then
    echo '{}' | jq -cn '{
      hookSpecificOutput: {
        hookEventName: "PreToolUse",
        permissionDecision: "ask",
        permissionDecisionReason: "Possible credential exposure in command",
        additionalContext: "Command may include a secret. Confirm intent and avoid committing secrets."
      }
    }'
    exit 0
  fi
fi

exit 0

Post-Tool Formatting

工具执行后格式化

bash
#!/bin/bash
set -euo pipefail

INPUT="$(cat)"
TOOL_NAME="$(echo "$INPUT" | jq -r '.tool_name')"
FILE_PATH="$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')"

if [[ "$TOOL_NAME" =~ ^(Edit|Write)$ && -n "$FILE_PATH" && -f "$FILE_PATH" ]]; then
  case "$FILE_PATH" in
    *.js|*.ts|*.jsx|*.tsx|*.json|*.md)
      npx prettier --write "$FILE_PATH" 2>/dev/null || true
      ;;
    *.py)
      ruff format "$FILE_PATH" 2>/dev/null || true
      ;;
    *.go)
      gofmt -w "$FILE_PATH" 2>/dev/null || true
      ;;
    *.rs)
      rustfmt "$FILE_PATH" 2>/dev/null || true
      ;;
  esac
fi

exit 0
bash
#!/bin/bash
set -euo pipefail

INPUT="$(cat)"
TOOL_NAME="$(echo "$INPUT" | jq -r '.tool_name')"
FILE_PATH="$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')"

if [[ "$TOOL_NAME" =~ ^(Edit|Write)$ && -n "$FILE_PATH" && -f "$FILE_PATH" ]]; then
  case "$FILE_PATH" in
    *.js|*.ts|*.jsx|*.tsx|*.json|*.md)
      npx prettier --write "$FILE_PATH" 2>/dev/null || true
      ;;
    *.py)
      ruff format "$FILE_PATH" 2>/dev/null || true
      ;;
    *.go)
      gofmt -w "$FILE_PATH" 2>/dev/null || true
      ;;
    *.rs)
      rustfmt "$FILE_PATH" 2>/dev/null || true
      ;;
  esac
fi

exit 0

Post-Tool Security Audit

工具执行后安全审计

bash
#!/bin/bash
set -euo pipefail

INPUT="$(cat)"
TOOL_NAME="$(echo "$INPUT" | jq -r '.tool_name')"
FILE_PATH="$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')"

if [[ "$TOOL_NAME" =~ ^(Edit|Write)$ && -n "$FILE_PATH" && -f "$FILE_PATH" ]]; then
  # Check for hardcoded secrets
  if grep -qE '(password|secret|api_key|token)\s*[:=]\s*["\x27][^"\x27]+["\x27]' "$FILE_PATH"; then
    echo "WARNING: Possible hardcoded secret in $FILE_PATH" >&2
  fi

  # Check for console.log in production code
  if [[ "$FILE_PATH" =~ \.(ts|js|tsx|jsx)$ ]] && grep -q 'console.log' "$FILE_PATH"; then
    echo "NOTE: console.log found in $FILE_PATH" >&2
  fi
fi

exit 0
bash
#!/bin/bash
set -euo pipefail

INPUT="$(cat)"
TOOL_NAME="$(echo "$INPUT" | jq -r '.tool_name')"
FILE_PATH="$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')"

if [[ "$TOOL_NAME" =~ ^(Edit|Write)$ && -n "$FILE_PATH" && -f "$FILE_PATH" ]]; then
  # Check for hardcoded secrets
  if grep -qE '(password|secret|api_key|token)\s*[:=]\s*["\x27][^"\x27]+["\x27]' "$FILE_PATH"; then
    echo "WARNING: Possible hardcoded secret in $FILE_PATH" >&2
  fi

  # Check for console.log in production code
  if [[ "$FILE_PATH" =~ \.(ts|js|tsx|jsx)$ ]] && grep -q 'console.log' "$FILE_PATH"; then
    echo "NOTE: console.log found in $FILE_PATH" >&2
  fi
fi

exit 0

Stop Hook (Run Tests)

Stop钩子(运行测试)

bash
#!/bin/bash
set -euo pipefail
bash
#!/bin/bash
set -euo pipefail

Run tests after Claude finishes

Run tests after Claude finishes

cd "$CLAUDE_PROJECT_DIR"
cd "$CLAUDE_PROJECT_DIR"

Detect test framework

Detect test framework

if [[ -f "package.json" ]]; then if grep -q '"vitest"' package.json; then npm run test 2>&1 | head -50 elif grep -q '"jest"' package.json; then npm test 2>&1 | head -50 fi elif [[ -f "pytest.ini" ]] || [[ -f "pyproject.toml" ]]; then pytest --tb=short 2>&1 | head -50 fi
exit 0
undefined
if [[ -f "package.json" ]]; then if grep -q '"vitest"' package.json; then npm run test 2>&1 | head -50 elif grep -q '"jest"' package.json; then npm test 2>&1 | head -50 fi elif [[ -f "pytest.ini" ]] || [[ -f "pyproject.toml" ]]; then pytest --tb=short 2>&1 | head -50 fi
exit 0
undefined

Session Start

会话开始

bash
#!/bin/bash
set -euo pipefail

cd "$CLAUDE_PROJECT_DIR"
bash
#!/bin/bash
set -euo pipefail

cd "$CLAUDE_PROJECT_DIR"

Check git status

Check git status

echo "=== Git Status ===" git status --short
echo "=== Git Status ===" git status --short

Check for uncommitted changes

Check for uncommitted changes

if ! git diff --quiet; then echo "WARNING: Uncommitted changes detected" fi
if ! git diff --quiet; then echo "WARNING: Uncommitted changes detected" fi

Verify dependencies

Verify dependencies

if [[ -f "package.json" ]]; then if [[ ! -d "node_modules" ]]; then echo "NOTE: node_modules missing, run npm install" fi fi
exit 0

---
if [[ -f "package.json" ]]; then if [[ ! -d "node_modules" ]]; then echo "NOTE: node_modules missing, run npm install" fi fi
exit 0

---

Matchers

匹配器

Matchers filter which tool triggers the hook:
  • Exact match:
    Write
    matches only the Write tool
  • Regex:
    Edit|Write
    or
    Notebook.*
  • Match all:
    *
    (also works with
    ""
    or omitted matcher)

匹配器用于筛选触发钩子的工具:
  • 精确匹配:
    Write
    仅匹配Write工具
  • 正则匹配:
    Edit|Write
    Notebook.*
  • 匹配所有:
    *
    ""
    或省略matcher字段也可实现)

Security Best Practices

安全最佳实践

text
HOOK SECURITY CHECKLIST

[ ] Validate all inputs with regex
[ ] Quote all variables: "$VAR" not $VAR
[ ] Use absolute paths
[ ] No eval with untrusted input
[ ] Set -euo pipefail at top
[ ] Keep hooks fast (<1 second)
[ ] Log actions for audit
[ ] Test manually before deploying

text
HOOK SECURITY CHECKLIST

[ ] Validate all inputs with regex
[ ] Quote all variables: "$VAR" not $VAR
[ ] Use absolute paths
[ ] No eval with untrusted input
[ ] Set -euo pipefail at top
[ ] Keep hooks fast (<1 second)
[ ] Log actions for audit
[ ] Test manually before deploying

Hook Composition

钩子组合

Multiple Hooks on Same Event

同一事件绑定多个钩子

json
{
  "PostToolUse": [
    {
      "matcher": "Edit|Write",
      "hooks": [
        { "type": "command", "command": ".claude/hooks/format.sh" },
        { "type": "command", "command": ".claude/hooks/audit.sh" },
        { "type": "command", "command": ".claude/hooks/notify.sh" }
      ]
    }
  ]
}
All matching hooks run in parallel. If you need strict ordering (format → lint → test), make one wrapper script that runs them sequentially.

json
{
  "PostToolUse": [
    {
      "matcher": "Edit|Write",
      "hooks": [
        { "type": "command", "command": ".claude/hooks/format.sh" },
        { "type": "command", "command": ".claude/hooks/audit.sh" },
        { "type": "command", "command": ".claude/hooks/notify.sh" }
      ]
    }
  ]
}
所有匹配的钩子会并行运行。如果需要严格的执行顺序(格式化 → 代码检查 → 测试),可编写一个包装脚本按顺序执行它们。

Debugging Hooks

调试钩子

bash
undefined
bash
undefined

Test a PostToolUse hook manually (stdin JSON)

Test a PostToolUse hook manually (stdin JSON)

export CLAUDE_PROJECT_DIR="$(pwd)" echo '{"hook_event_name":"PostToolUse","tool_name":"Edit","tool_input":{"file_path":"'"$(pwd)"'/src/app.ts"}}'
| bash .claude/hooks/post-tool-format.sh
export CLAUDE_PROJECT_DIR="$(pwd)" echo '{"hook_event_name":"PostToolUse","tool_name":"Edit","tool_input":{"file_path":"'"$(pwd)"'/src/app.ts"}}'
| bash .claude/hooks/post-tool-format.sh

Check exit code

Check exit code

echo $?

---
echo $?

---

Navigation

导航

Resources

资源

  • references/hook-patterns.md — Common patterns
  • references/hook-security.md — Security guide
  • data/sources.json — Documentation links
  • references/hook-patterns.md — 常见模式
  • references/hook-security.md — 安全指南
  • data/sources.json — 文档链接

Related Skills

相关技能

  • ../agents-subagents/SKILL.md — Agent creation
  • ../agents-skills/SKILL.md — Skill creation
  • ../ops-devops-platform/SKILL.md — CI/CD integration
  • ../agents-subagents/SKILL.md — Agent创建
  • ../agents-skills/SKILL.md — 技能创建
  • ../ops-devops-platform/SKILL.md — CI/CD集成