Claude Code Hooks 完全指南:让 AI 自动执行你的工作流
| 入门指南

Claude Code Hooks 完全指南:让 AI 自动执行你的工作流

Hooks 是 Claude Code 的自动化利器:在特定事件发生时,自动执行你的脚本

比如:

  • 每次写文件后自动运行 ESLint
  • 提交代码前自动检查敏感信息
  • 会话开始时自动加载项目配置

快速上手

配置文件位置

~/.claude/settings.json          # 用户全局设置
.claude/settings.json            # 项目设置(提交到 Git)
.claude/settings.local.json      # 本地项目设置(不提交)

基本结构

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "npx eslint --fix \"$CLAUDE_PROJECT_DIR/src\"",
            "timeout": 30
          }
        ]
      }
    ]
  }
}
  • matcher:匹配工具名称,支持正则(Write|Edit)或 * 匹配所有
  • command:要执行的 bash 命令
  • timeout:超时时间(秒),默认 60 秒

Hook 事件类型

PreToolUse - 工具执行前

在 Claude 调用工具之前触发,可以阻止或修改工具调用。

常见匹配器:

  • Bash - Shell 命令
  • Write - 写文件
  • Edit - 编辑文件
  • Read - 读文件
  • Task - 子代理任务

场景:阻止写入敏感目录

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/check-path.sh"
          }
        ]
      }
    ]
  }
}

PostToolUse - 工具执行后

工具成功执行后触发,用于后处理

场景:写文件后自动格式化

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "npx prettier --write \"$CLAUDE_PROJECT_DIR/src/**/*.{js,ts}\""
          }
        ]
      }
    ]
  }
}

UserPromptSubmit - 用户提交提示时

用户发送消息后、Claude 处理前触发,可以验证或增强提示

场景:自动注入项目上下文

{
  "hooks": {
    "UserPromptSubmit": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "cat \"$CLAUDE_PROJECT_DIR\"/.claude/context.md"
          }
        ]
      }
    ]
  }
}

stdout 输出会被添加到 Claude 的上下文中。

SessionStart - 会话开始时

新会话启动或恢复时触发。

匹配器:

  • startup - 新启动
  • resume - 恢复会话
  • clear - /clear 命令
  • compact - 压缩操作

场景:自动设置环境变量

#!/bin/bash
# .claude/hooks/setup-env.sh

if [ -n "$CLAUDE_ENV_FILE" ]; then
  echo 'export NODE_ENV=development' >> "$CLAUDE_ENV_FILE"
  echo 'export API_URL=http://localhost:3000' >> "$CLAUDE_ENV_FILE"
fi
exit 0
{
  "hooks": {
    "SessionStart": [
      {
        "matcher": "startup",
        "hooks": [
          {
            "type": "command",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/setup-env.sh"
          }
        ]
      }
    ]
  }
}

Stop - Claude 完成响应时

Claude 完成回复后触发,可以阻止停止让 Claude 继续

场景:自动运行测试

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/run-tests.sh"
          }
        ]
      }
    ]
  }
}

Notification - 通知时

Claude 发送通知时触发(等待权限、空闲超时等)。

SessionEnd - 会话结束时

会话结束时触发,用于清理任务。

PreCompact - 压缩前

上下文压缩前触发。

SubagentStop - 子代理完成时

Task 工具调用完成时触发。

Hook 输入输出

输入格式

Hooks 通过 stdin 接收 JSON:

{
  "session_id": "abc123",
  "transcript_path": "/path/to/transcript.jsonl",
  "cwd": "/current/directory",
  "permission_mode": "default",
  "hook_event_name": "PostToolUse",
  "tool_name": "Write",
  "tool_input": {
    "file_path": "/path/to/file.txt",
    "content": "file content"
  },
  "tool_response": {
    "success": true
  }
}

输出控制

简单方式:退出码

  • exit 0:成功,stdout 显示给用户
  • exit 2:阻止操作,stderr 反馈给 Claude
  • 其他:非阻止错误,stderr 显示给用户

高级方式:JSON 输出

{
  "continue": true,
  "stopReason": "停止原因",
  "suppressOutput": false,
  "systemMessage": "警告信息"
}

PreToolUse 可以控制权限:

{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "allow",
    "permissionDecisionReason": "自动批准的原因",
    "updatedInput": {
      "file_path": "/modified/path.txt"
    }
  }
}
  • allow:绕过权限确认
  • deny:阻止执行
  • ask:要求用户确认

环境变量

  • CLAUDE_PROJECT_DIR:项目根目录
  • CLAUDE_ENV_FILE:环境变量持久化文件(仅 SessionStart)
  • CLAUDE_CODE_REMOTE:是否远程环境

MCP 工具匹配

MCP 工具命名格式:mcp__<server>__<tool>

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "mcp__memory__.*",
        "hooks": [
          {
            "type": "command",
            "command": "echo 'Memory operation' >> ~/mcp.log"
          }
        ]
      }
    ]
  }
}

实用示例

代码提交前检查敏感信息

#!/bin/bash
# .claude/hooks/check-secrets.sh

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

# 检查敏感模式
if echo "$content" | grep -qE '(api[_-]?key|secret|password|token)\s*[:=]'; then
  echo "检测到可能的敏感信息,请检查后再提交" >&2
  exit 2
fi

exit 0

自动添加文件头注释

#!/bin/bash
# .claude/hooks/add-header.sh

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

# 只处理新建的 JS/TS 文件
if [[ "$file_path" =~ \.(js|ts)$ ]]; then
  content=$(echo "$input" | jq -r '.tool_input.content // empty')

  # 检查是否已有头注释
  if ! echo "$content" | head -1 | grep -q "^/\*\*"; then
    header="/**\n * @file $(basename "$file_path")\n * @created $(date +%Y-%m-%d)\n */\n\n"

    # 输出修改后的内容
    echo "{\"hookSpecificOutput\":{\"hookEventName\":\"PreToolUse\",\"updatedInput\":{\"content\":\"${header}${content}\"}}}"
    exit 0
  fi
fi

exit 0

会话开始时加载 nvm

#!/bin/bash
# .claude/hooks/load-nvm.sh

ENV_BEFORE=$(export -p | sort)

source ~/.nvm/nvm.sh
nvm use 20 > /dev/null 2>&1

if [ -n "$CLAUDE_ENV_FILE" ]; then
  ENV_AFTER=$(export -p | sort)
  comm -13 <(echo "$ENV_BEFORE") <(echo "$ENV_AFTER") >> "$CLAUDE_ENV_FILE"
fi

exit 0

调试技巧

使用 claude --debug 查看 hook 执行详情:

[DEBUG] Executing hooks for PostToolUse:Write
[DEBUG] Found 1 hook commands to execute
[DEBUG] Hook command completed with status 0

安全注意事项

  1. 验证输入:永远不要盲目信任 hook 输入
  2. 引用变量:使用 "$VAR" 而不是 $VAR
  3. 检查路径:防止路径遍历攻击
  4. 跳过敏感文件:避免处理 .env、密钥文件
  5. 测试充分:在安全环境中测试后再用于生产

Hooks 配置修改后需要重启 Claude Code 或在 /hooks 菜单中确认才能生效。