很多人用 Claude Code 时,都想实现一些自定义功能:自动记录所有执行的命令、写完代码自动格式化、拦截危险操作、报错自动发通知。

常规改源码、写插件的方式又麻烦、又容易失效。而 Hooks (钩子系统) 就是 Claude Code 官方的轻量化解决方案:不改一行源码,只需简单配置脚本,就能在AI运行的关键节点,插入自定义逻辑

本文用最简单直白的方式,讲懂 Hooks 的核心作用、运行逻辑、配置方法和实用案例。

一、Hooks 到底是什么?

Hooks 核心定义:Claude Code Hooks 是一套无侵入的轻量化扩展机制,本质是绑定指定AI事件的自定义脚本(Shell/Python 均可)。无需修改 Claude 源码,当AI执行命令、读写文件、提交输入等操作时,会自动触发预设脚本,实现个性化拓展能力。

通俗类比 Java AOP (核心精髓) :Hooks 的设计思想和 Java AOP 切面编程完全一致,都是不侵入核心业务,在关键节点横向扩展功能,精准对应关系如下:

  • PreToolUse 工具执行前 = @Before 前置切面
  • PostToolUse 工具执行后 = @After 后置切面
  • PostToolUseFailure 工具执行失败 = @AfterThrowing 异常切面

所有配置统一写在 ~/.claude/settings.json,支持跨语言编写、无需专属插件,核心实用场景覆盖:安全拦截高危操作、操作日志审计、代码自动格式化、执行异常告警、用户输入预处理。

日常最实用的场景全覆盖:

  • 安全防护:禁止 git push --forcerm -rf 等高危操作
  • 日志审计:自动记录所有命令、文件操作记录,方便溯源
  • 工程提效:代码保存后自动格式化、自动修复格式问题
  • 异常告警:命令执行失败,自动推送通知到 Slack 等工具
  • 输入管控:统一预处理、过滤用户的提问内容

二、核心事件:哪些节点可以自定义?

Claude Code 预留了二十多种触发事件,新手只需要掌握 4个高频核心事件,就能搞定90%的场景:

  • PreToolUse(工具执行前) :最常用!可以修改即将执行的命令、直接拦截危险操作、提前记录日志
  • PostToolUse(工具执行后) :工具运行完触发,适合做后置处理,比如写完代码自动格式化
  • PostToolUseFailure(工具执行失败) :专门捕获报错,用于异常通知、错误记录
  • UserPromptSubmit(用户输入提交) :用户发送指令时触发,可过滤、修改用户输入

除此之外,还有会话启停、文件变更、配置修改等事件,满足进阶定制需求。

三、极简运行流程:钩子是怎么工作的?

整个执行链路非常清晰,新手只需记住顺序:

AI生成操作 → 前置钩子(PreToolUse) → 权限校验 → 执行操作 → 后置钩子(PostToolUse) → 返回结果

两个关键细节:

  1. 前置钩子在权限检查之前运行,能提前拦截危险操作,效率更高
  2. 所有钩子统一 30秒超时,避免脚本卡死导致AI助手瘫痪

这里我们可以简单的看一个工具执行的流程:

在这里插入图片描述

四、匹配规则:精准控制钩子生效范围

我们可以精准设置,让钩子只在指定操作触发,支持三种匹配方式:

  1. 精准匹配:只针对单个工具,比如只对 Bash 命令生效
  2. 正则匹配:批量匹配多个工具,比如同时匹配「写文件、改文件」操作
  3. 条件匹配:精准过滤内容,比如「只拦截包含 rm -rf 的命令」

极简配置示例:仅拦截高危删除命令

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "if": "rm -rf",
        "hooks": ["./block-danger.sh"]
      }
    ]
  }
}

整体拦截流程如下:

在这里插入图片描述

五、通信协议与核心源码

很多新手看不懂 Hook 的本质,是因为没看过底层执行逻辑。Claude Code Hook 整套机制完全可以通过 源码 看懂,且非常像 AOP 的环绕通知。

Hook 整套通信规则只有一句话:

主程序通过 stdin (标准输入) 灌入 JSON 上下文 → 脚本处理 → stdout 标准输出 )输出 JSON 控制结果

这是 Hook 能够跨语言、通用、解耦的核心。

5.1 官方底层核心执行源码(精简真实逻辑)

PreToolUse 前置钩子的运行逻辑(等同于 AOP @Before),这是 Claude 真实源码删减版:

// 工具执行前切面逻辑
async function runPreToolUseHooks(tool, input, context) {
  // 1. 匹配当前工具对应的所有钩子
  const hooks = getMatchingHooks('PreToolUse', tool.name)

  for (const hook of hooks) {
    // 2. 组装切面上下文(传给脚本的 stdin 数据)
    const hookInput = {
      event: 'PreToolUse',
      tool_name: tool.name,
      tool_input: input,
      session_id: context.sessionId,
      cwd: context.cwd
    }

    // 3. 执行外部脚本,30秒超时保护
    const result = await execHook(hook.command, hookInput, { timeout: 30000 })

    // 4. 读取脚本 stdout 返回的 JSON,实现切面控制
    if (result.stdout) {
      const output = JSON.parse(result.stdout)
      
      // 切面能力1:修改入参(AOP 修改参数)
      if (output.updatedInput) input = { ...input, ...output.updatedInput }
      
      // 切面能力2:拦截本次执行(AOP 阻止放行)
      if (output.block) {
        return { blocked: true, message: output.message }
      }
    }
  }
  return { updatedInput: input }
}

5.2 标准通信协议图解(stdin / stdout)

在这里插入图片描述

每次触发 Hook,主程序和脚本的完整数据交互模型如下:

1)Claude Code 发给脚本的 stdin 输入数据

{
  "event": "PreToolUse",
  "tool_name": "Bash",
  "tool_input": {
    "command": "npm test"
  },
  "session_id": "xxx",
  "cwd": "/当前项目路径"
}

2)脚本处理后返回给 Claude 的 stdout 输出数据

{
  "block": false,
  "message": "",
  "updatedInput": null,
  "notification": ""
}

5.3 可使用的全部返回字段(Hook 能力清单)

和 AOP 切面能力一一对应:

  • block: true拦截工具执行(切面终止流程)
  • message:拦截提示文案
  • updatedInput修改工具入参(前置切面改参)
  • updatedResult修改工具返回值(后置切面改结果)
  • notification:弹窗通知

其输出的格式如下:

interface HookOutput {
  // 阻止执行
  block?: boolean
  message?: string        // 阻止原因

  // 修改输入 (PreToolUse)
  updatedInput?: Record<string, unknown>

  // 修改结果 (PostToolUse)
  updatedResult?: unknown

  // 发送通知
  notification?: string

  // 静默 (不显示给用户)
  silent?: boolean
}

六、4个开箱即用的实战脚本

分享4个最常用、可直接复制使用的钩子脚本,覆盖日常绝大多数场景。

1. 自动操作审计日志

记录所有终端命令,留存操作记录,方便溯源。

#!/bin/bash
INPUT=$(cat)
TOOL=$(echo "$INPUT" | jq -r '.tool_name')
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')
echo "$(date) | $TOOL | $COMMAND" >> ~/.claude/audit.log
echo '{}'

2. 代码自动格式化

文件写入完成后,自动用Prettier格式化TS/TSX代码。

#!/bin/bash
INPUT=$(cat)
FILE=$(echo "$INPUT" | jq -r '.tool_result.data.filePath // empty')
if [[ "$FILE" == *.ts ]] || [[ "$FILE" == *.tsx ]]; then
  npx prettier --write "$FILE" 2>/dev/null
fi
echo '{}'

3. 拦截危险Git强推命令

禁止 git push --force,避免代码仓库被覆盖。

#!/bin/bash
INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')
if echo "$COMMAND" | grep -q "git push.*--force"; then
  echo '{"block": true, "message": "禁止执行git强推命令,操作已拦截"}'
else
  echo '{}'
fi

4. 执行失败自动通知

命令报错后,自动推送信息到Slack,及时感知异常。

#!/bin/bash
INPUT=$(cat)
ERROR=$(echo "$INPUT" | jq -r '.tool_result.error // empty')
SLACK_WEBHOOK="你的webhook地址"
if [ -n "$ERROR" ]; then
  curl -X POST "$SLACK_WEBHOOK" -d "{"text": "Claude Code执行异常:$ERROR"}"
fi
echo '{}'

七、核心设计优势

很多人疑惑:为什么Claude Code要用「Shell脚本+JSON」的钩子模式?

  • 不限语言:Shell、Python、Go都能写脚本,不用学专属插件语法
  • 稳定不崩溃:钩子是独立进程,脚本出错不会影响主程序运行
  • 数据完整:用JSON传结构化数据,比环境变量更稳定、无长度限制
  • 安全高效:前置拦截危险操作,减少无效执行,30秒超时杜绝

感兴趣的宝子可以关注一波,后续会更新更多有用的知识!!!
在这里插入图片描述

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐