claude-code-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.
  • 钩子通过标准输入接收JSON负载(需将其视为不可信输入),并以你的用户权限运行(在Bash工具沙箱之外)。
  • 默认超时时间为每个钩子命令60秒;所有匹配的钩子并行运行;相同的命令会被去重。

Hook Input (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
阻塞性错误
stderr
内容会作为提示信息;标准输出中的JSON会被忽略
其他非阻塞性错误执行会继续;
stderr
内容仅在详细模式下可见
标准输出注入说明:对于
UserPromptSubmit
SessionStart
Setup
事件,非JSON格式的标准输出(退出码0)会被注入到Claude的上下文中;大多数其他事件仅在详细模式下显示标准输出。

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
{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          { "type": "command", "command": ".claude/hooks/quick-check.sh" },
          { "type": "prompt", "prompt": "Verify code quality meets standards" }
        ]
      }
    ]
  }
}

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
{
  "hooks": {
    "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

相关技能

  • ../claude-code-commands/SKILL.md — Command creation
  • ../claude-code-agents/SKILL.md — Agent creation
  • ../ops-devops-platform/SKILL.md — CI/CD integration
  • ../claude-code-commands/SKILL.md — 命令创建
  • ../claude-code-agents/SKILL.md — Agent创建
  • ../ops-devops-platform/SKILL.md — CI/CD集成