一文详解Claude Code中Hooks的使用

Written by

in

文章目录
  • Claude 要调用工具时,Claude Code 把当时的状态打包成 JSON,通过 stdin 传给你配置的脚本,等脚本退出再继续。 整个流程:
  • 事件 触发时机 能否阻断 SessionStart 会话开始 否 UserPromptSubmit 用户提交了一条消息 否 PreToolUse Claude 调用工具之前 是 PostToolUse 工具调用完成之后 否 Notification Claude 需要提醒用户时 否 Stop Claude 完成一轮回复,等待用户 否 SessionEnd 会话结束 否 七个事件覆盖一次完整会话的全过程: PreToolUse 是最关键的事件。退出码决定行为:0 继续执行,2 阻断工具调用,其他非 0 记录错误但不阻断。
  • 打开 ~/.claude/settings.json,加入 hooks 字段: {   “hooks”: {     “PreToolUse”: [       {         “hooks”: [           {             “type”: “command”,             “command”: “python3 /path/to/my_hook.py”           }         ]       }     ],     “Notification”: [       {         “hooks”: [           {             “type”: “command”,             “command”: “python3 /path/to/notify.py”           }         ]       }     ]   } } 每个事件可以配多个脚本,按顺序执行。脚本可以是任何可执行文件,Python、Shell、Node 都行。
  • Claude Code 把 JSON 写入脚本的 stdin,不同事件的字段不同。 PreToolUse 示例: {   “session_id”: “abc-123”, “hook_event_name”: “PreToolUse”, “tool_name”: “Bash”, “tool_input”: {     “command”: “rm -rf /tmp/test”   }, “cwd”: “/Users/you/myproject”, “tty”: “/dev/ttys001” } Notification 示例: {   “session_id”: “abc-123”,   “hook_event_name”: “Notification”,   “message”: “任务完成,等待你的下一步指令”,   “cwd”: “/Users/you/myproject” } Stop 示例: {   “session_id”: “abc-123”,   “hook_event_name”: “Stop”,   “cwd”: “/Users/you/myproject” }
  • stdin 是 Claude Code 传给脚本的数据,stdout 方向相反:脚本输出 JSON 到 stdout,Claude Code 读取并按内容决策。 PreToolUse:用 stdout 阻断或放行 脚本可以在 stdout 输出一个 JSON 对象,decision 字段控制行为: import json, sys data = json.load(sys.stdin) if data.get(“tool_name”) == “Bash”:     command = data.get(“tool_input”, {}).get(“command”, “”)     if”rm -rf”in command:         # 通过 stdout JSON 阻断         print(json.dumps({             “decision”: “block”,             “reason”: “禁止执行 rm -rf”         }))         sys.exit(0)  # stdout 已表达意图,退出码可以是 0 sys.exit(0) decision 值为 "block" 时阻断,"approve" 时强制放行(跳过权限提示)。 向 Claude 注入上下文 stdout JSON 还支持 additionalContext 字段,内容会直接注入到 Claude 的上下文里,Claude 读完再决定下一步。适合在工具调用前后补充环境信息: print(json.dumps({     “additionalContext”: “当前磁盘剩余空间 2GB,请谨慎执行大文件操作” })) sys.exit(0)
  • 写日志:把 Claude 的所有工具调用记下来,方便复盘 发通知:Claude 等待输入时,通过系统通知或微信、钉钉提醒你 阻断危险操作:检测到高风险命令,退出码 2 阻止执行 转发事件:把 JSON 发到本地 socket,驱动自定义 UI 或状态面板 记录耗时:PreToolUse 记开始时间,PostToolUse 记结束时间,统计每类工具的耗时分布
  • Hook 脚本除了做判断和记录,还可以把事件转发到本地 socket,让一个常驻进程处理所有状态变化。 转发脚本只需几行: #!/usr/bin/env python3 import json, socket, sys data = sys.stdin.read() sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) sock.connect(“/tmp/my_app.sock”) sock.sendall((data + “n”).encode()) response = sock.recv(1024).decode().strip() sock.close() # 常驻进程返回 “block” 时阻断工具调用(必须用退出码 2) sys.exit(2if response == “block”else0) 常驻进程在 socket 上监听,收到 PreToolUse 弹出确认窗口,用户点通过就回 ok,点拒绝就回 block,hook 脚本根据返回值决定退出码。这样就能在 Claude 执行过程中插入任意交互界面,不限于终端。
  • 创建你的脚本文件,给执行权限:chmod +x my_hook.py 编辑 ~/.claude/settings.json,加入 hooks 配置 启动一个新的 Claude Code 会话(已有会话需要重启才能加载新配置) 触发相关操作,看脚本是否被调用 调试时在脚本里加 print(json.dumps(data, indent=2), file=sys.stderr) 把收到的完整数据打出来,Claude Code 会把 stderr 输出显示在终端。 一个脚本、一个退出码,就能接入 Claude Code 的整个执行流程。你的脚本负责判断逻辑,Claude Code 负责触发和等待,各管各的。 到此这篇关于一文详解Claude Code中Hooks的使用的文章就介绍到这了,更多相关Claude Code Hooks使用内容请搜索风君子博客以前的文章或继续浏览下面的相关文章,希望大家以后多多支持风君子博客!
  • 目录
    • Hooks 是什么
    • 有哪些 Hook 事件
    • 怎么配置
    • 脚本收到什么数据
    • 脚本还能通过 stdout 影响 Claude
    • 动手写第一个 Hook
      • 示例 1:记录 Claude 调用过的所有命令
      • 示例 2:阻止删除操作
      • 示例 3:任务完成时发系统通知
      • 示例 4:针对特定工具设置 matcher
    • 脚本拿到数据后能做什么
      • 进阶:用 Hooks 驱动自定义 UI
        • 上手步骤

          Claude Code 每次调用工具、等待输入、结束会话,都会触发对应的生命周期事件。你可以在这些事件上挂脚本,拿到上下文 JSON,决定 Claude 要不要继续执行。

          Claude 要调用工具时,Claude Code 把当时的状态打包成 JSON,通过 stdin 传给你配置的脚本,等脚本退出再继续。

          整个流程:

          事件 触发时机 能否阻断
          SessionStart 会话开始
          UserPromptSubmit 用户提交了一条消息
          PreToolUse Claude 调用工具之前
          PostToolUse 工具调用完成之后
          Notification Claude 需要提醒用户时
          Stop Claude 完成一轮回复,等待用户
          SessionEnd 会话结束

          七个事件覆盖一次完整会话的全过程:

          PreToolUse 是最关键的事件。退出码决定行为:0 继续执行,2 阻断工具调用,其他非 0 记录错误但不阻断。

          打开 ~/.claude/settings.json,加入 hooks 字段:

          {
            "hooks": {
              "PreToolUse": [
                {
                  "hooks": [
                    {
                      "type": "command",
                      "command": "python3 /path/to/my_hook.py"
                    }
                  ]
                }
              ],
              "Notification": [
                {
                  "hooks": [
                    {
                      "type": "command",
                      "command": "python3 /path/to/notify.py"
                    }
                  ]
                }
              ]
            }
          }

          每个事件可以配多个脚本,按顺序执行。脚本可以是任何可执行文件,Python、Shell、Node 都行。

          Claude Code 把 JSON 写入脚本的 stdin,不同事件的字段不同。

          PreToolUse 示例:

          {
            "session_id": "abc-123",
          "hook_event_name": "PreToolUse",
          "tool_name": "Bash",
          "tool_input": {
              "command": "rm -rf /tmp/test"
            },
          "cwd": "/Users/you/myproject",
          "tty": "/dev/ttys001"
          }

          Notification 示例:

          {
            "session_id": "abc-123",
            "hook_event_name": "Notification",
            "message": "任务完成,等待你的下一步指令",
            "cwd": "/Users/you/myproject"
          }

          Stop 示例:

          {
            "session_id": "abc-123",
            "hook_event_name": "Stop",
            "cwd": "/Users/you/myproject"
          }

          stdin 是 Claude Code 传给脚本的数据,stdout 方向相反:脚本输出 JSON 到 stdout,Claude Code 读取并按内容决策。

          PreToolUse:用 stdout 阻断或放行

          脚本可以在 stdout 输出一个 JSON 对象,decision 字段控制行为:

          import json, sys
          
          data = json.load(sys.stdin)
          
          if data.get("tool_name") == "Bash":
              command = data.get("tool_input", {}).get("command", "")
              if"rm -rf"in command:
                  # 通过 stdout JSON 阻断
                  print(json.dumps({
                      "decision": "block",
                      "reason": "禁止执行 rm -rf"
                  }))
                  sys.exit(0)  # stdout 已表达意图,退出码可以是 0
          
          sys.exit(0)
          

          decision 值为 "block" 时阻断,"approve" 时强制放行(跳过权限提示)。

          向 Claude 注入上下文

          stdout JSON 还支持 additionalContext 字段,内容会直接注入到 Claude 的上下文里,Claude 读完再决定下一步。适合在工具调用前后补充环境信息:

          print(json.dumps({
              "additionalContext": "当前磁盘剩余空间 2GB,请谨慎执行大文件操作"
          }))
          sys.exit(0)
          

          #!/usr/bin/env python3
          import json
          import sys
          from datetime import datetime
          
          data = json.load(sys.stdin)
          
          if data.get("tool_name") == "Bash":
              command = data.get("tool_input", {}).get("command", "")
              with open("/tmp/claude_commands.log", "a") as f:
                  f.write(f"{datetime.now()} | {command}n")
          
          sys.exit(0)  # 退出码 0,Claude 继续执行
          

          挂在 PostToolUse 上,Claude 每跑完一条 Bash 命令,日志文件就多一行。

          #!/usr/bin/env python3
          import json
          import sys
          
          data = json.load(sys.stdin)
          
          if data.get("tool_name") == "Bash":
              command = data.get("tool_input", {}).get("command", "")
              if "rm -rf" in command:
                  print("拦截:禁止执行 rm -rf", file=sys.stderr)
                  sys.exit(2)  # 退出码 2,Claude 放弃这次工具调用
          
          sys.exit(0)
          

          退出码 2 是 PreToolUse 的阻断信号。Claude 读到 stderr,把它当作拒绝原因展示给用户,然后停止这次工具调用。退出码 1 或其他非 0 值只会记录错误,Claude 仍会继续执行。

          #!/usr/bin/env python3
          import json
          import sys
          import subprocess
          
          data = json.load(sys.stdin)
          
          subprocess.run([
              "osascript", "-e",
              f'display notification "Claude 完成了,去看看吧" with title "Claude Code"'
          ])
          
          sys.exit(0)
          

          把这个脚本挂在 Stop 或 Notification 上。Claude 停下来等你回复时,Mac 右上角就会弹出通知。

          如果只想监听某类工具,加 matcher 字段:

          {
            "hooks": {
              "PreToolUse": [
                {
                  "matcher": "Bash",
                  "hooks": [
                    {
                      "type": "command",
                      "command": "python3 /path/to/bash_guard.py"
                    }
                  ]
                }
              ]
            }
          }

          加了 matcher,只有 Bash 工具触发时脚本才跑,EditRead 等其他工具不走这里。

          写日志:把 Claude 的所有工具调用记下来,方便复盘

          发通知:Claude 等待输入时,通过系统通知或微信、钉钉提醒你

          阻断危险操作:检测到高风险命令,退出码 2 阻止执行

          转发事件:把 JSON 发到本地 socket,驱动自定义 UI 或状态面板

          记录耗时:PreToolUse 记开始时间,PostToolUse 记结束时间,统计每类工具的耗时分布

          Hook 脚本除了做判断和记录,还可以把事件转发到本地 socket,让一个常驻进程处理所有状态变化。

          转发脚本只需几行:

          #!/usr/bin/env python3
          import json, socket, sys
          data = sys.stdin.read()
          sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
          sock.connect("/tmp/my_app.sock")
          sock.sendall((data + "n").encode())
          response = sock.recv(1024).decode().strip()
          sock.close()
          # 常驻进程返回 "block" 时阻断工具调用(必须用退出码 2)
          sys.exit(2if response == "block"else0)

          常驻进程在 socket 上监听,收到 PreToolUse 弹出确认窗口,用户点通过就回 ok,点拒绝就回 block,hook 脚本根据返回值决定退出码。这样就能在 Claude 执行过程中插入任意交互界面,不限于终端。

          1. 创建你的脚本文件,给执行权限:chmod +x my_hook.py
          2. 编辑 ~/.claude/settings.json,加入 hooks 配置
          3. 启动一个新的 Claude Code 会话(已有会话需要重启才能加载新配置)
          4. 触发相关操作,看脚本是否被调用

          调试时在脚本里加 print(json.dumps(data, indent=2), file=sys.stderr) 把收到的完整数据打出来,Claude Code 会把 stderr 输出显示在终端。

          一个脚本、一个退出码,就能接入 Claude Code 的整个执行流程。你的脚本负责判断逻辑,Claude Code 负责触发和等待,各管各的。

          到此这篇关于一文详解Claude Code中Hooks的使用的文章就介绍到这了,更多相关Claude Code Hooks使用内容请搜索风君子博客以前的文章或继续浏览下面的相关文章,希望大家以后多多支持风君子博客!

          站内搜索