首页 › tutorials › Claude Code Hooks自动化:四类钩子构建生产工作流

Claude Code Hooks自动化实战:四类钩子构建生产级工作流

概述

本文系统讲解Claude Code的Hooks机制——一个被大多数开发者忽视的自动化层。你将学会使用PreToolUse、PostToolUse、Notification、SessionStart四类生命周期钩子,构建安全防护、自动格式化、质量门禁和上下文注入的完整工作流。适合已掌握Claude Code基础操作、希望将AI编码效率提升到团队级生产环境的中高级开发者。

前置条件

一、Hooks机制全景:为什么你需要它

Claude Code在每次工具调用前后都会触发生命周期事件。Hooks允许你在这些关键节点注入确定性逻辑——一段Shell脚本,在AI的决策链中充当"护栏"和"加速器"。

与Skill不同,Hooks不是给AI的新能力,而是对AI行为的约束与增强。它在AI执行操作的前后运行确定性代码,确保输出符合团队标准。

四类生命周期事件

flowchart LR
    A[用户消息] --> B[SessionStart]
    B --> C[AI思考]
    C --> D{工具调用?}
    D -->|是| E[PreToolUse]
    E --> F[工具执行]
    F --> G[PostToolUse]
    G --> H[AI继续]
    H --> D
    D -->|否| I[AI回复]
    I --> J[Notification]
    J --> A
钩子类型 触发时机 核心用途
SessionStart 会话开始时 注入上下文、加载项目规则
PreToolUse 工具执行前 安全拦截、参数校验
PostToolUse 工具执行后 自动格式化、质量检查
Notification 任务完成/需要关注时 桌面通知、日志记录

二、配置结构:Hooks的栖身之所

Hooks配置存储在.claude/settings.json中,支持项目级和用户级两个作用域。

2.1 配置文件位置

~/.claude/settings.json          # 用户级(所有项目生效)
.claude/settings.json            # 项目级(仅当前项目)

2.2 基本配置骨架

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "bash-script-here"
          }
        ]
      }
    ],
    "PostToolUse": [],
    "Notification": [],
    "SessionStart": []
  }
}

2.3 Matcher语法

Matcher是一个正则表达式,匹配工具名称:

// 匹配所有文件写入操作
"matcher": "Write|Edit"

// 匹配所有Bash命令
"matcher": "Bash"

// 匹配所有工具(通配)
"matcher": ".*"

// 匹配特定文件路径的写入(需要配合脚本内判断)
"matcher": "Write"

2.4 Hook输入输出协议

每个Hook通过stdin接收JSON输入,通过stdout返回JSON输出,通过exit code控制行为:

# 输入格式(stdin JSON)
{
  "tool_name": "Write",
  "tool_input": {
    "file_path": "/src/app.ts",
    "content": "..."
  },
  "session_id": "abc123"
}

# 输出格式(stdout JSON)— 可选
{
  "decision": "block",       // "block" | "approve"(默认)
  "reason": "不允许修改生产环境配置"
}

# Exit Code语义
# 0 = 允许继续(approve)
# 2 = 阻止操作(block)

三、PreToolUse:安全防护的第一道门

PreToolUse在工具执行前触发,最核心的用途是安全拦截。你可以阻止AI执行危险操作,或对参数做预校验。

3.1 阻止修改敏感文件

创建.claude/settings.json:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "cat | jq -r '.tool_input.file_path // empty' | grep -qE '(\\.env|credentials|secret|\\.pem)$' && echo '{\"decision\":\"block\",\"reason\":\"禁止修改敏感文件(.env/credentials/secret/证书)\"}' && exit 2 || exit 0"
          }
        ]
      }
    ]
  }
}

工作原理: 1. Claude Code通过stdin传入工具调用信息的JSON 2. jq提取目标文件路径 3. grep匹配敏感文件模式 4. 匹配到则返回decision: block并exit 2,阻止操作 5. 不匹配则exit 0,允许继续

3.2 阻止危险Bash命令

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "INPUT=$(cat) && CMD=$(echo \"$INPUT\" | jq -r '.tool_input.command // empty') && echo \"$CMD\" | grep -qiE '(rm\\s+-rf\\s+/|DROP\\s+TABLE|truncate|mkfs|dd\\s+if=)' && echo '{\"decision\":\"block\",\"reason\":\"检测到危险命令,已拦截\"}' && exit 2 || exit 0"
          }
        ]
      }
    ]
  }
}

3.3 限制操作范围到项目目录

{
  "matcher": "Write|Edit|Bash",
  "hooks": [
    {
      "type": "command",
      "command": "INPUT=$(cat) && PROJECT_DIR=$(pwd) && FP=$(echo \"$INPUT\" | jq -r '.tool_input.file_path // .tool_input.command // empty') && echo \"$FP\" | grep -qv \"$PROJECT_DIR\" && echo \"{\\\"decision\\\":\\\"block\\\",\\\"reason\\\":\\\"操作超出项目目录范围\\\"}\" && exit 2 || exit 0"
    }
  ]
}

常见问题

Q: Hook脚本失败会怎样? A: 如果脚本exit非0且非2,Claude Code会显示错误但继续执行。只有exit 2明确阻止操作。

Q: 可以有多个PreToolUse hook吗? A: 可以。按数组顺序执行,任一hook返回block即阻止。

四、PostToolUse:自动格式化与质量门

PostToolUse在工具执行完成后触发,最适合做自动修复和质量检查。

4.1 自动格式化代码

这是最高频使用的Hook——每次AI写入文件后自动运行格式化工具:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "FILE=$(cat | jq -r '.tool_input.file_path // empty') && [ -n \"$FILE\" ] && [ -f \"$FILE\" ] && case \"$FILE\" in *.ts|*.tsx|*.js|*.jsx) npx prettier --write \"$FILE\" 2>/dev/null;; *.py) python -m black \"$FILE\" 2>/dev/null && python -m isort \"$FILE\" 2>/dev/null;; *.go) gofmt -w \"$FILE\" 2>/dev/null;; *.rs) rustfmt \"$FILE\" 2>/dev/null;; esac && exit 0"
          }
        ]
      }
    ]
  }
}

执行流程: 1. 从stdin JSON提取被修改的文件路径 2. 根据文件扩展名选择对应的格式化工具 3. 静默执行格式化(2>/dev/null抑制错误输出) 4. 始终exit 0(PostToolUse中block通常无意义)

4.2 自动运行Lint检查

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "FILE=$(cat | jq -r '.tool_input.file_path // empty') && [ -n \"$FILE\" ] && [ -f \"$FILE\" ] && case \"$FILE\" in *.ts|*.tsx|*.js|*.jsx) npx eslint --fix \"$FILE\" 2>/dev/null;; *.py) python -m ruff check --fix \"$FILE\" 2>/dev/null;; esac && exit 0"
          }
        ]
      }
    ]
  }
}

4.3 自动运行相关测试

对于修改了源码文件的情况,自动触发对应的测试:

{
  "matcher": "Write|Edit",
  "hooks": [
    {
      "type": "command",
      "command": "FILE=$(cat | jq -r '.tool_input.file_path // empty') && TEST_FILE=$(echo \"$FILE\" | sed 's/\\.ts$/.test.ts/;s/src\\//test\\//') && [ -f \"$TEST_FILE\" ] && npx vitest run \"$TEST_FILE\" --reporter=verbose 2>&1 | tail -5 || exit 0"
    }
  ]
}

4.4 PostToolUse注入修复提示

PostToolUse还可以通过stdout向AI返回额外信息,引导AI自我修复:

{
  "matcher": "Write|Edit",
  "hooks": [
    {
      "type": "command",
      "command": "FILE=$(cat | jq -r '.tool_input.file_path // empty') && [ -n \"$FILE\" ] && [ -f \"$FILE\" ] && LINT_OUT=$(npx eslint \"$FILE\" --format json 2>/dev/null) && ERRORS=$(echo \"$LINT_OUT\" | jq '[.[0].messages // [] | .[] | .message] | length') && [ \"$ERRORS\" -gt 0 ] && echo \"{\\\"decision\\\":\\\"block\\\",\\\"reason\\\":\\\"ESLint发现 $ERRORS 个错误,请修复:$(echo \\\"$LINT_OUT\\\" | jq -r '.[0].messages[:3] | .[] | .message')\\\"}\" && exit 2 || exit 0"
    }
  ]
}

这段Hook在格式化后运行ESLint检查,如果有错误,直接block并返回错误信息——AI会看到错误描述并自动尝试修复。

五、SessionStart:上下文注入引擎

SessionStart在每次新会话开始时触发,是注入项目规则和上下文的最佳时机。

5.1 注入项目规范

{
  "hooks": {
    "SessionStart": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "cat << 'CTX'\n本项目使用以下规范:\n- TypeScript严格模式,禁止any\n- 组件使用函数式组件+Hooks\n- 测试覆盖率≥80%\n- 提交信息遵循Conventional Commits\n- API响应统一使用{code, data, message}格式\nCTX"
          }
        ]
      }
    ]
  }
}

SessionStart hook的stdout内容会作为上下文注入到对话中,相当于自动在每次对话开头添加了一段系统提示。

5.2 动态加载CLAUDE.md规则

{
  "SessionStart": [
    {
      "hooks": [
        {
          "type": "command",
          "command": "[ -f CLAUDE.md ] && cat CLAUDE.md || echo '未找到项目级CLAUDE.md,使用默认规则'"
        }
      ]
    }
  ]
}

5.3 加载环境信息

{
  "SessionStart": [
    {
      "hooks": [
        {
          "type": "command",
          "command": "echo \"当前环境信息:\" && echo \"- Node: $(node -v)\" && echo \"- npm: $(npm -v)\" && echo \"- Git分支: $(git branch --show-current 2>/dev/null || echo '非Git仓库')\" && echo \"- 项目: $(basename $(pwd))\" && echo \"- 最近修改: $(git log --oneline -5 2>/dev/null || echo '无Git日志')\""
        }
      ]
    }
  ]
}

5.4 条件化上下文注入

根据项目类型加载不同的规则集:

#!/bin/bash
# .claude/hooks/session-start.sh

PROJECT_DIR=$(pwd)

if [ -f "$PROJECT_DIR/package.json" ]; then
  FRAMEWORK=$(jq -r '.dependencies | keys[] | select(. == "next" or . == "react" or . == "vue" or . == "svelte")' "$PROJECT_DIR/package.json" 2>/dev/null | head -1)

  case "$FRAMEWORK" in
    next)
      echo "检测到Next.js项目。规则:使用App Router,Server Components优先,API Routes使用RESTful风格。"
      ;;
    react)
      echo "检测到React项目。规则:使用函数式组件,状态管理使用zustand,样式使用CSS Modules。"
      ;;
    vue)
      echo "检测到Vue项目。规则:使用Composition API,组件命名PascalCase,状态管理使用Pinia。"
      ;;
    *)
      echo "检测到Node.js项目。规则:ES模块优先,错误处理使用自定义Error类。"
      ;;
  esac
fi

if [ -f "$PROJECT_DIR/requirements.txt" ] || [ -f "$PROJECT_DIR/pyproject.toml" ]; then
  echo "检测到Python项目。规则:类型注解必须,使用ruff格式化,FastAPI作为Web框架首选。"
fi

if [ -f "$PROJECT_DIR/go.mod" ]; then
  echo "检测到Go项目。规则:错误处理不使用panic,接口定义在消费方,测试使用table-driven。"
fi

在settings.json中引用:

{
  "SessionStart": [
    {
      "hooks": [
        {
          "type": "command",
          "command": "bash .claude/hooks/session-start.sh"
        }
      ]
    }
  ]
}

六、Notification:任务完成通知

Notification在Claude Code完成任务或需要用户关注时触发,用于发送通知到外部系统。

6.1 桌面通知(macOS)

{
  "Notification": [
    {
      "hooks": [
        {
          "type": "command",
          "command": "MSG=$(cat | jq -r '.message // empty') && osascript -e \"display notification \\\"$MSG\\\" with title \\\"Claude Code\\\"\" 2>/dev/null || exit 0"
        }
      ]
    }
  ]
}

6.2 桌面通知(Linux)

{
  "Notification": [
    {
      "hooks": [
        {
          "type": "command",
          "command": "MSG=$(cat | jq -r '.message // empty') && notify-send \"Claude Code\" \"$MSG\" 2>/dev/null || exit 0"
        }
      ]
    }
  ]
}

6.3 发送到Slack

{
  "Notification": [
    {
      "hooks": [
        {
          "type": "command",
          "command": "MSG=$(cat | jq -r '.message // \"任务完成\"') && curl -s -X POST \"$SLACK_WEBHOOK\" -H 'Content-type: application/json' -d \"{\\\"text\\\":\\\"[Claude Code] $MSG\\\"}\" > /dev/null 2>&1 || exit 0"
        }
      ]
    }
  ]
}

使用前设置环境变量:

export SLACK_WEBHOOK="https://hooks.slack.com/services/YOUR/WEBHOOK/URL"

6.4 记录操作日志

{
  "Notification": [
    {
      "hooks": [
        {
          "type": "command",
          "command": "MSG=$(cat | jq -r '.message // empty') && TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S') && echo \"[$TIMESTAMP] $MSG\" >> .claude/notifications.log || exit 0"
        }
      ]
    }
  ]
}

七、完整实战案例:团队级Hooks配置

下面是一个完整的、可直接用于团队项目的settings.json配置,整合了所有四类钩子。

7.1 项目结构

my-project/
├── .claude/
│   ├── settings.json          # Hooks配置
│   └── hooks/
│       ├── pre-tool.sh        # PreToolUse脚本
│       ├── post-tool.sh       # PostToolUse脚本
│       ├── session-start.sh   # SessionStart脚本
│       └── notify.sh          # Notification脚本
├── src/
├── tests/
└── package.json

7.2 完整settings.json

{
  "hooks": {
    "SessionStart": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "bash .claude/hooks/session-start.sh"
          }
        ]
      }
    ],
    "PreToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "bash .claude/hooks/pre-tool.sh"
          }
        ]
      },
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "bash .claude/hooks/pre-tool.sh"
          }
        ]
      }
    ],
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "bash .claude/hooks/post-tool.sh"
          }
        ]
      }
    ],
    "Notification": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "bash .claude/hooks/notify.sh"
          }
        ]
      }
    ]
  }
}

7.3 pre-tool.sh — 安全防护脚本

#!/bin/bash
set -euo pipefail

INPUT=$(cat)
TOOL=$(echo "$INPUT" | jq -r '.tool_name // empty')

case "$TOOL" in
  Write|Edit)
    FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')

    # 规则1:禁止修改敏感文件
    if echo "$FILE" | grep -qE '\.(env|secret|credentials|pem|key)$'; then
      echo '{"decision":"block","reason":"安全策略:禁止修改敏感文件"}'
      exit 2
    fi

    # 规则2:禁止修改.git目录
    if echo "$FILE" | grep -qE '\.git/'; then
      echo '{"decision":"block","reason":"安全策略:禁止修改Git内部文件"}'
      exit 2
    fi

    # 规则3:禁止修改CI/CD配置(需人工审批)
    if echo "$FILE" | grep -qE '\.(github|gitlab-ci|Jenkinsfile|docker-compose)'; then
      echo '{"decision":"block","reason":"CI/CD配置修改需要人工审批"}'
      exit 2
    fi
    ;;

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

    # 规则4:阻止破坏性命令
    if echo "$CMD" | grep -qiE '(rm\s+-rf\s+/|DROP\s+TABLE|truncate\s+table|dd\s+if=|mkfs|:(){ :\|:& };:)'; then
      echo '{"decision":"block","reason":"检测到破坏性命令,已拦截"}'
      exit 2
    fi

    # 规则5:阻止强制推送
    if echo "$CMD" | grep -qiE 'git\s+push.*--force'; then
      echo '{"decision":"block","reason":"禁止强制推送(git push --force)"}'
      exit 2
    fi

    # 规则6:阻止直接推送main分支
    if echo "$CMD" | grep -qiE 'git\s+push.*main|git\s+push.*master'; then
      echo '{"decision":"block","reason":"禁止直接推送到主分支,请使用PR"}'
      exit 2
    fi
    ;;
esac

exit 0

7.4 post-tool.sh — 自动修复脚本

#!/bin/bash
set -euo pipefail

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

[ -z "$FILE" ] || [ ! -f "$FILE" ] && exit 0

case "$FILE" in
  *.ts|*.tsx|*.js|*.jsx)
    # 格式化
    npx prettier --write "$FILE" 2>/dev/null || true
    # Lint修复
    npx eslint --fix "$FILE" 2>/dev/null || true
    # 检查剩余错误
    LINT_ERRORS=$(npx eslint "$FILE" --format json 2>/dev/null | jq '[.[0].messages // [] | .[] | .message] | length' 2>/dev/null || echo "0")
    if [ "$LINT_ERRORS" -gt 0 ]; then
      ERROR_MSGS=$(npx eslint "$FILE" --format json 2>/dev/null | jq -r '.[0].messages[:3] | .[] | "\(.line):\(.message)"' 2>/dev/null || true)
      echo "{\"decision\":\"block\",\"reason\":\"ESLint仍有${LINT_ERRORS}个错误:${ERROR_MSGS}\"}"
      exit 2
    fi
    ;;
  *.py)
    python -m ruff check --fix "$FILE" 2>/dev/null || true
    python -m ruff format "$FILE" 2>/dev/null || true
    ;;
  *.go)
    gofmt -w "$FILE" 2>/dev/null || true
    goimports -w "$FILE" 2>/dev/null || true
    ;;
  *.rs)
    rustfmt "$FILE" 2>/dev/null || true
    ;;
  *.sql)
    # SQL格式化
    npx sql-formatter --fix "$FILE" 2>/dev/null || true
    ;;
esac

exit 0

7.5 session-start.sh — 上下文注入脚本

#!/bin/bash
set -euo pipefail

PROJECT_DIR=$(pwd)
PROJECT_NAME=$(basename "$PROJECT_DIR")

echo "=== 项目:$PROJECT_NAME ==="

# Git信息
BRANCH=$(git branch --show-current 2>/dev/null || echo "detached")
echo "分支:$BRANCH"

# 最近提交
echo "最近活动:"
git log --oneline -3 2>/dev/null | while read -r line; do echo "  $line"; done

# 技术栈检测
if [ -f "package.json" ]; then
  echo "技术栈:"
  jq -r '.dependencies // {} | keys[]' package.json 2>/dev/null | head -5 | while read -r dep; do echo "  - $dep"; done
fi

# 项目规则
echo ""
echo "项目规则:"
echo "- 提交前必须通过所有测试"
echo "- 禁止使用any类型"
echo "- 新功能必须有对应测试"
echo "- API端点必须有JSDoc注释"

7.6 notify.sh — 通知脚本

#!/bin/bash
set -euo pipefail

INPUT=$(cat)
MSG=$(echo "$INPUT" | jq -r '.message // "任务完成"')
TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')

# 写入日志
echo "[$TIMESTAMP] $MSG" >> .claude/notifications.log

# macOS桌面通知
if command -v osascript &>/dev/null; then
  osascript -e "display notification \"$MSG\" with title \"Claude Code\"" 2>/dev/null || true
fi

# Linux桌面通知
if command -v notify-send &>/dev/null; then
  notify-send "Claude Code" "$MSG" 2>/dev/null || true
fi

exit 0

7.7 执行流程全景

sequenceDiagram
    participant U as 用户
    participant CC as Claude Code
    participant SS as SessionStart
    participant PTU as PreToolUse
    participant Tool as 工具执行
    participant PostT as PostToolUse
    participant N as Notification

    U->>CC: 发送消息
    CC->>SS: 会话开始
    SS-->>CC: 注入项目上下文+规则
    CC->>CC: AI分析+规划
    CC->>PTU: 准备调用Write工具
    PTU->>PTU: 检查敏感文件/危险操作
    PTU-->>CC: approve/block
    alt approved
        CC->>Tool: 执行Write
        Tool-->>CC: 写入完成
        CC->>PostT: 文件已修改
        PostT->>PostT: prettier→eslint→检查错误
        alt 有lint错误
            PostT-->>CC: block+错误详情
            CC->>CC: 自动修复
        else 无错误
            PostT-->>CC: approve
        end
    else blocked
        CC->>CC: 调整方案重试
    end
    CC->>N: 任务完成
    N->>N: 桌面通知+日志
    N-->>CC: done
    CC-->>U: 返回结果

八、高级模式与最佳实践

8.1 多Hook串联

同一事件可以注册多个Hook,按顺序执行:

{
  "PreToolUse": [
    {
      "matcher": "Write|Edit",
      "hooks": [
        {"type": "command", "command": "bash .claude/hooks/check-sensitive.sh"},
        {"type": "command", "command": "bash .claude/hooks/check-scope.sh"},
        {"type": "command", "command": "bash .claude/hooks/check-lock.sh"}
      ]
    }
  ]
}

执行规则: - 顺序执行,任一返回block即停止后续 - 某个Hook失败(exit 1)会跳过,继续执行下一个 - 只有exit 2才真正阻止操作

8.2 项目级与用户级协作

~/.claude/settings.json          # 个人偏好(格式化工具版本等)
.claude/settings.json            # 团队规则(安全策略、质量门禁)

两者会合并执行,项目级优先。

8.3 调试Hooks

当Hook行为不符合预期时,可以添加日志:

#!/bin/bash
INPUT=$(cat)
echo "[$(date)] INPUT: $INPUT" >> .claude/hooks/debug.log

# ... 正常逻辑 ...

echo "[$(date)] RESULT: $RESULT" >> .claude/hooks/debug.log

也可以使用环境变量开启调试模式:

{
  "type": "command",
  "command": "DEBUG=1 bash .claude/hooks/pre-tool.sh"
}

8.4 性能考量

Hooks在每个工具调用时都会执行,性能直接影响AI响应速度:

优化策略 说明
保持脚本轻量 避免在Hook中运行耗时操作
使用内置命令 优先使用grep/jq而非外部工具
条件短路 快速判断是否需要执行完整逻辑
缓存结果 对重复计算使用临时文件缓存
异步通知 Notification hook中用&后台发送
# 反模式:每次调用都安装依赖
npx -y prettier --write "$FILE"  # ❌ 首次需要下载

# 正模式:确保工具已安装
prettier --write "$FILE" 2>/dev/null || npx prettier --write "$FILE"

8.5 团队共享方案

将Hooks配置纳入版本控制,确保全团队一致:

# .claude/settings.json → Git跟踪
# .claude/hooks/ → Git跟踪
# .claude/hooks/local.sh → .gitignore(个人自定义)
# .gitignore
.claude/hooks/local.*
.claude/notifications.log
.claude/hooks/debug.log

九、从零搭建:5分钟快速上手

以下是一个最简可用的Hooks配置,适合立即开始:

第一步:创建目录

mkdir -p .claude/hooks

第二步:创建.claude/settings.json

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "FILE=$(cat | jq -r '.tool_input.file_path // empty') && echo \"$FILE\" | grep -qE '\\.(env|secret|credentials)$' && echo '{\"decision\":\"block\",\"reason\":\"禁止修改敏感文件\"}' && exit 2 || exit 0"
          }
        ]
      }
    ],
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "FILE=$(cat | jq -r '.tool_input.file_path // empty') && [ -f \"$FILE\" ] && npx prettier --write \"$FILE\" 2>/dev/null; exit 0"
          }
        ]
      }
    ]
  }
}

第三步:验证

# 启动Claude Code
claude

# 在对话中让AI创建一个测试文件
> 创建一个 hello.ts 文件,内容是打印hello world

观察输出:AI写入文件后,prettier自动格式化。

第四步:测试安全拦截

> 修改 .env 文件,添加 DATABASE_URL

# 预期:AI会报告操作被拦截

十、常见问题排查手册

问题 原因 解决方案
Hook不触发 matcher不匹配 检查正则是否正确,用.*测试
Hook触发但无效果 脚本报错 添加debug日志,检查stderr
格式化不执行 工具未安装 确保prettier/eslint在项目依赖中
阻止了不该阻止的操作 matcher太宽泛 缩小匹配范围或添加白名单
通知不发送 环境变量缺失 检查SLACK_WEBHOOK等变量
性能变慢 Hook太重 减少Hook中的外部调用
flowchart TD
    A[Hook不工作] --> B{触发了吗?}
    B -->|否| C[检查matcher正则]
    B -->|是| D{执行了吗?}
    D -->|否| E[检查脚本权限 chmod +x]
    D -->|是| F{返回正确?}
    F -->|否| G[检查jq语法和JSON输出]
    F -->|是| H[检查exit code逻辑]
    C --> I[修复matcher]
    E --> J[添加执行权限]
    G --> K[测试JSON输出]
    H --> L[确认0=通过 2=阻止]

延伸阅读

训练营适用性评估

维度 评估
适合训练营类型 企业AI开发内训、团队Claude Code推广
学员基础要求 会用Claude Code基本操作,懂基础bash
预计教学时长 2-3小时(含实操练习)
实操难度 中等(配置调试需要一定Shell能力)
交付物 一套可立即用于团队项目的Hooks配置

参考来源

  1. Automate workflows with hooks - Claude Code Docs — Anthropic官方Hooks指南,涵盖全部四类钩子配置
  2. Hooks reference - Claude Code Docs — Hooks完整参考文档,含JSON输入输出Schema和Exit Code语义
  3. Claude Code Hooks: The Automation Layer Nobody Knows About — 深度分析Hooks架构设计与生产级模式
  4. meet-deshani/claude-code-hooks-cookbook — 社区维护的实战Hook模式集合,包含安全防护、质量门禁等20+模式
  5. Claude Code Hooks: 6 Production Patterns — 六种生产级Hook模式详解

[!tip] 51学通信知识星球 星主:爱卫生(微信:gprshome201101)——公众号「51学通信」创办者,19 年通信行业运维与讲师经验,著有《GPRS网络信令实例详解》《EPC原理与实践》《5G核心网原理与实践》三本纸质书(各大电商平台有售)。 星球定位:深耕 5G 核心网、IMS、NFV/AI、云原生等 ICT 领域,主打运维岗位与信令分析。 当前规模:5200 篇主题 / 990 个回答,198 元/年。 加入福利:加入星球后请加爱卫生微信,拉您进知识星球年度陪伴与答疑群——不限量回复您的各种问题,另有群内分享。 AI 加持:51学通信知识星球已作为 skill 接入 WorkBuddy,使用 deepseek-4-flash 在海量星球帖子中精准找贴、找视频、找 FAQ 和案例,做总结、找文档和 PPT 等场景全覆盖。

51学通信知识星球海报-扫码加入