通俗版 Claude Code 文档

结构照原 docs,内容改成真能照着做的人话版。

查看原始文档 目录顺序与官方保持一致

Automation

用钩子自动化

用钩子自动化 这一页讲的,就是 用钩子自动化 这件事在 Claude Code 里到底怎么用。

页面信息

对应原页

用钩子自动化

页面性质

第三方中文解释页

使用建议

先看人话解释,再对照原页命令和代码

这页不是官方原文,而是顺着官方文档结构做的中文解释版。命令、参数、配置名这些硬东西尽量保留,解释部分则尽量讲成人能照着做的话。

如果你碰到特别敏感的配置、权限或企业环境差异,最好顺手点上面的“查看原始文档”再核一遍。

这一页先讲明白

钩子是在特定时刻自动触发的动作。

你可以理解成“门口安个检查员”,进门前看一眼,出门后再看一眼。

钩子很适合做守门工作,比如改文件后自动格式化、提交前自动跑检查、会话开始时先提醒规则。

它不是替代 Claude,而是在关键关口补一道自动规则。

先把最稳的钩子加上,比如格式化、lint、提醒说明。

钩子别写太重,不然每次都卡很久,用户很快就不想用了。

预留广告位

正文中段响应式广告 等你后面真接 AdSense,这里再放正式广告。

Documentation Index

这里不是让你背"Documentation Index"这个词,而是让你看它真干活时怎么使。

这里还牵扯作用域,意思就是这条规则到底管当前项目、你个人,还是只管这一趟会话。

Set up your first hook

这段就是开工前的准备清单,先把地基打好。

看这段时要特别盯工具和权限边界,别为了省事一把全开。

改配置

Set up your first hook 1

想把这条规矩固定住,就把下面这块老老实实写进去。

{
  "hooks": {
    "Notification": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "osascript -e 'display notification \"Claude Code needs your attention\" with title \"Claude Code\"'"
          }
        ]
      }
    ]
  }
}
改配置

Set up your first hook 2

想把这条规矩固定住,就把下面这块老老实实写进去。

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [{ "type": "command", "command": "jq -r '.tool_input.file_path' | xargs npx prettier --write" }]
      }
    ],
    "Notification": [
      {
        "matcher": "",
        "hooks": [{ "type": "command", "command": "osascript -e 'display notification \"Claude Code needs your attention\" with title \"Claude Code\"'" }]
      }
    ]
  }
}

What you can automate

看到这里,就把"What you can automate"当成一件真要上手的活来看。

这里还牵扯作用域,意思就是这条规则到底管当前项目、你个人,还是只管这一趟会话。

Get notified when Claude needs input

这一块主要是在说"Get notified when Claude needs input"真到手上该怎么用,哪里最容易踩坑。

看这段时要特别盯工具和权限边界,别为了省事一把全开。

改配置

Get notified when Claude needs input 1

想把这条规矩固定住,就把下面这块老老实实写进去。

{
  "hooks": {
    "Notification": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "osascript -e 'display notification \"Claude Code needs your attention\" with title \"Claude Code\"'"
          }
        ]
      }
    ]
  }
}
改配置

Get notified when Claude needs input 2

想把这条规矩固定住,就把下面这块老老实实写进去。

{
  "hooks": {
    "Notification": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "notify-send 'Claude Code' 'Claude Code needs your attention'"
          }
        ]
      }
    ]
  }
}
改配置

Get notified when Claude needs input 3

想把这条规矩固定住,就把下面这块老老实实写进去。

{
  "hooks": {
    "Notification": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "powershell.exe -Command \"[System.Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms'); [System.Windows.Forms.MessageBox]::Show('Claude Code needs your attention', 'Claude Code')\""
          }
        ]
      }
    ]
  }
}

Auto-format code after edits

这一块主要是在说"Auto-format code after edits"真到手上该怎么用,哪里最容易踩坑。

这里还牵扯作用域,意思就是这条规则到底管当前项目、你个人,还是只管这一趟会话。

改配置

Auto-format code after edits

想把这条规矩固定住,就把下面这块老老实实写进去。

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r '.tool_input.file_path' | xargs npx prettier --write"
          }
        ]
      }
    ]
  }
}

Block edits to protected files

这一段主要是在把"Block edits to protected files"讲实,不是只摆个标题给你看。

看这段时要特别盯工具和权限边界,别为了省事一把全开。

改配置

Block edits to protected files 1

这会儿轮到改配置了,字段名和关键字别自己乱换。

#!/bin/bash
# protect-files.sh

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

PROTECTED_PATTERNS=(".env" "package-lock.json" ".git/")

for pattern in "${PROTECTED_PATTERNS[@]}"; do
  if [[ "$FILE_PATH" == *"$pattern"* ]]; then
    echo "Blocked: $FILE_PATH matches protected pattern '$pattern'" >&2
    exit 2
  fi
done

exit 0
终端里敲

Block edits to protected files 2

先别急着往下翻,下面这条命令跑完,心里才有底。

chmod +x .claude/hooks/protect-files.sh
改配置

Block edits to protected files 3

这会儿轮到改配置了,字段名和关键字别自己乱换。

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

Re-inject context after compaction

这一段更像在讲判断条件,什么时候该上,什么时候先别急。把触发条件看清,比背标题更重要。

这里还牵扯作用域,意思就是这条规则到底管当前项目、你个人,还是只管这一趟会话。

改配置

Re-inject context after compaction

想把这条规矩固定住,就把下面这块老老实实写进去。

{
  "hooks": {
    "SessionStart": [
      {
        "matcher": "compact",
        "hooks": [
          {
            "type": "command",
            "command": "echo 'Reminder: use Bun, not npm. Run bun test before committing. Current sprint: auth refactor.'"
          }
        ]
      }
    ]
  }
}

Audit configuration changes

这一段主要是在把"Audit configuration changes"讲实,不是只摆个标题给你看。

这里还牵扯作用域,意思就是这条规则到底管当前项目、你个人,还是只管这一趟会话。

改配置

Audit configuration changes

这会儿轮到改配置了,字段名和关键字别自己乱换。

{
  "hooks": {
    "ConfigChange": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "jq -c '{timestamp: now | todate, source: .source, file: .file_path}' >> ~/claude-config-audit.log"
          }
        ]
      }
    ]
  }
}

Reload environment when directory or files change

这一块主要是在说"Reload environment when directory or files change"真到手上该怎么用,哪里最容易踩坑。

这里还牵扯作用域,意思就是这条规则到底管当前项目、你个人,还是只管这一趟会话。

改配置

Reload environment when directory or files change 1

想把这条规矩固定住,就把下面这块老老实实写进去。

{
  "hooks": {
    "SessionStart": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "direnv export bash > \"$CLAUDE_ENV_FILE\""
          }
        ]
      }
    ],
    "CwdChanged": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "direnv export bash > \"$CLAUDE_ENV_FILE\""
          }
        ]
      }
    ]
  }
}
改配置

Reload environment when directory or files change 2

想把这条规矩固定住,就把下面这块老老实实写进去。

{
  "hooks": {
    "FileChanged": [
      {
        "matcher": ".envrc|.env",
        "hooks": [
          {
            "type": "command",
            "command": "direnv export bash > \"$CLAUDE_ENV_FILE\""
          }
        ]
      }
    ]
  }
}

Auto-approve specific permission prompts

看到这里,就把"Auto-approve specific permission prompts"当成一件真要上手的活来看。

这里还牵扯作用域,意思就是这条规则到底管当前项目、你个人,还是只管这一趟会话。

改配置

Auto-approve specific permission prompts 1

这一段说完,最后还得写到配置里才算真的生效。

{
  "hooks": {
    "PermissionRequest": [
      {
        "matcher": "ExitPlanMode",
        "hooks": [
          {
            "type": "command",
            "command": "echo '{\"hookSpecificOutput\": {\"hookEventName\": \"PermissionRequest\", \"decision\": {\"behavior\": \"allow\"}}}'"
          }
        ]
      }
    ]
  }
}
改配置

Auto-approve specific permission prompts 2

这一段说完,最后还得写到配置里才算真的生效。

{
  "hookSpecificOutput": {
    "hookEventName": "PermissionRequest",
    "decision": {
      "behavior": "allow",
      "updatedPermissions": [
        { "type": "setMode", "mode": "acceptEdits", "destination": "session" }
      ]
    }
  }
}

How hooks work

这一块主要是在说"How hooks work"真到手上该怎么用,哪里最容易踩坑。

看这段时要特别盯工具和权限边界,别为了省事一把全开。

Read input and return output

这一段主要是在把"Read input and return output"讲实,不是只摆个标题给你看。

这里还牵扯作用域,意思就是这条规则到底管当前项目、你个人,还是只管这一趟会话。

改配置

Read input and return output 1

这会儿轮到改配置了,字段名和关键字别自己乱换。

{
  "session_id": "abc123",          // unique ID for this session
  "cwd": "/Users/sarah/myproject", // working directory when the event fired
  "hook_event_name": "PreToolUse", // which event triggered this hook
  "tool_name": "Bash",             // the tool Claude is about to use
  "tool_input": {                  // the arguments Claude passed to the tool
    "command": "npm test"          // for Bash, this is the shell command
  }
}
终端里敲

Read input and return output 2

先别急着往下翻,下面这条命令跑完,心里才有底。

#!/bin/bash
INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command')

if echo "$COMMAND" | grep -q "drop table"; then
  echo "Blocked: dropping tables is not allowed" >&2  # stderr becomes Claude's feedback
  exit 2                                               # exit 2 = block the action
fi

exit 0  # exit 0 = let it proceed
改配置

Read input and return output 3

这会儿轮到改配置了,字段名和关键字别自己乱换。

{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "deny",
    "permissionDecisionReason": "Use rg instead of grep for better performance"
  }
}

Filter hooks with matchers

这一块主要是在说"Filter hooks with matchers"真到手上该怎么用,哪里最容易踩坑。

这里还牵扯作用域,意思就是这条规则到底管当前项目、你个人,还是只管这一趟会话。

改配置

Filter hooks with matchers 1

想把这条规矩固定住,就把下面这块老老实实写进去。

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          { "type": "command", "command": "prettier --write ..." }
        ]
      }
    ]
  }
}
改配置

Filter hooks with matchers 2

想把这条规矩固定住,就把下面这块老老实实写进去。

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r '.tool_input.command' >> ~/.claude/command-log.txt"
          }
        ]
      }
    ]
  }
}
改配置

Filter hooks with matchers 3

想把这条规矩固定住,就把下面这块老老实实写进去。

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "mcp__github__.*",
        "hooks": [
          {
            "type": "command",
            "command": "echo \"GitHub tool called: $(jq -r '.tool_name')\" >&2"
          }
        ]
      }
    ]
  }
}
改配置

Filter hooks with matchers 4

想把这条规矩固定住,就把下面这块老老实实写进去。

{
  "hooks": {
    "SessionEnd": [
      {
        "matcher": "clear",
        "hooks": [
          {
            "type": "command",
            "command": "rm -f /tmp/claude-scratch-*.txt"
          }
        ]
      }
    ]
  }
}

Configure hook location

这里讲怎么把某个开关拧对。重点不是概念,而是你该在哪儿改、改完怎么确认生效。

这里还牵扯作用域,意思就是这条规则到底管当前项目、你个人,还是只管这一趟会话。

Prompt-based hooks

这里不是让你背"Prompt-based hooks"这个词,而是让你看它真干活时怎么使。

这里还牵扯作用域,意思就是这条规则到底管当前项目、你个人,还是只管这一趟会话。

改配置

Prompt-based hooks

光知道意思还不够,这里得把规矩落进配置里,下面这块照着填。

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "prompt",
            "prompt": "Check if all tasks are complete. If not, respond with {\"ok\": false, \"reason\": \"what remains to be done\"}."
          }
        ]
      }
    ]
  }
}

Agent-based hooks

看到这里,就把"Agent-based hooks"当成一件真要上手的活来看。

看这段时要特别盯工具和权限边界,别为了省事一把全开。

改配置

Agent-based hooks

这一段说完,最后还得写到配置里才算真的生效。

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "agent",
            "prompt": "Verify that all unit tests pass. Run the test suite and check the results. $ARGUMENTS",
            "timeout": 120
          }
        ]
      }
    ]
  }
}

HTTP hooks

这一段是在说怎么用 type: "http" hooks to POST event data 去做 an HTTP endpoint instead of running a shell command. The endpoint receives the same JSON that a command hook would receive on stdin, and returns results through the HTTP response body using the same JSON format.。看这种内容,光知道名字没用,还是得落到手上。

看这段时要特别盯工具和权限边界,别为了省事一把全开。

改配置

HTTP hooks

这会儿轮到改配置了,字段名和关键字别自己乱换。

{
  "hooks": {
    "PostToolUse": [
      {
        "hooks": [
          {
            "type": "http",
            "url": "http://localhost:8080/hooks/tool-use",
            "headers": {
              "Authorization": "Bearer $MY_TOKEN"
            },
            "allowedEnvVars": ["MY_TOKEN"]
          }
        ]
      }
    ]
  }
}

Limitations and troubleshooting

遇到这种内容,别急着大拆大改,先按它给的路子把问题缩小。

Limitations

这一块主要是在说"Limitations"真到手上该怎么用,哪里最容易踩坑。

这里还牵扯作用域,意思就是这条规则到底管当前项目、你个人,还是只管这一趟会话。

Hooks and permission modes

这一段主要是在把"Hooks and permission modes"讲实,不是只摆个标题给你看。

这里还牵扯作用域,意思就是这条规则到底管当前项目、你个人,还是只管这一趟会话。

Hook not firing

这一段不只是挂个标题,它是在说明"Hook not firing"这一块到底负责什么。

看这段时要特别盯工具和权限边界,别为了省事一把全开。

Hook error in output

看到这里,就把"Hook error in output"当成一件真要上手的活来看。

这里还牵扯作用域,意思就是这条规则到底管当前项目、你个人,还是只管这一趟会话。

关键片段

Hook error in output

先看下面这块原始片段,等会儿再回头看解释会顺得多。

echo '{"tool_name":"Bash","tool_input":{"command":"ls"}}' | ./my-hook.sh
echo $?  # Check the exit code

/hooks shows no hooks configured

这里不是让你背"/hooks shows no hooks configured"这个词,而是让你看它真干活时怎么使。

这里还牵扯作用域,意思就是这条规则到底管当前项目、你个人,还是只管这一趟会话。

Stop hook runs forever

这一段主要是在把"Stop hook runs forever"讲实,不是只摆个标题给你看。

看这段时要特别盯工具和权限边界,别为了省事一把全开。

终端里敲

Stop hook runs forever

先别急着往下翻,下面这条命令跑完,心里才有底。

#!/bin/bash
INPUT=$(cat)
if [ "$(echo "$INPUT" | jq -r '.stop_hook_active')" = "true" ]; then
  exit 0  # Allow Claude to stop
fi
# ... rest of your hook logic

JSON validation failed

这里不是让你背"JSON validation failed"这个词,而是让你看它真干活时怎么使。

如果你打算把外接能力往里挂,这里提到的 hooks、MCP、skills、memory 都要分清各自负责哪一摊。

关键片段

JSON validation failed 1

下面这块是这一段最值钱的原文样板,先对着看一眼。

Shell ready on arm64
{"decision": "block", "reason": "Not allowed"}
关键片段

JSON validation failed 2

下面这块是这一段最值钱的原文样板,先对着看一眼。

# In ~/.zshrc or ~/.bashrc
if [[ $- == *i* ]]; then
  echo "Shell ready"
fi

Debug techniques

这段就是排错时拿来照着查的,先一项项看清楚,再动手改。

这里还牵扯作用域,意思就是这条规则到底管当前项目、你个人,还是只管这一趟会话。

Learn more

这一块主要是在说"Learn more"真到手上该怎么用,哪里最容易踩坑。

看这段时要特别盯工具和权限边界,别为了省事一把全开。

照着做一遍

这页属于“用钩子自动化”这类活,最稳的办法还是一小步一小步来。

下面这三步不一定华丽,但通常最不容易绕晕。

第 1 步:先起步

先把最稳的钩子加上,比如格式化、lint、提醒说明。

第 2 步:边做边看

钩子别写太重,不然每次都卡很久,用户很快就不想用了。

一眼看懂这一页

这页的作用,就是把原本偏专业的话题,拆成能直接照着走的明白话。

用钩子自动化
   |
   v
用钩子自动化 这一页讲的,就是 用钩子自动化 这件事在 Claude Code 里到底怎么用。
   |
   v
照着步骤去做

文末提醒

这站会按官方 docs 的导航和内容变化继续重生成,原站加页、删页、改页时,这里会跟着更新。

人话解释会尽量顺着原页往下讲,但命令、参数名、配置名这些硬东西还是保留原样,免得你抄过去跑不起来。