Automation
用钩子自动化
用钩子自动化 这一页讲的,就是 用钩子自动化 这件事在 Claude Code 里到底怎么用。
页面信息
这页不是官方原文,而是顺着官方文档结构做的中文解释版。命令、参数、配置名这些硬东西尽量保留,解释部分则尽量讲成人能照着做的话。
如果你碰到特别敏感的配置、权限或企业环境差异,最好顺手点上面的“查看原始文档”再核一遍。
这一页先讲明白
钩子是在特定时刻自动触发的动作。
你可以理解成“门口安个检查员”,进门前看一眼,出门后再看一眼。
钩子很适合做守门工作,比如改文件后自动格式化、提交前自动跑检查、会话开始时先提醒规则。
它不是替代 Claude,而是在关键关口补一道自动规则。
先把最稳的钩子加上,比如格式化、lint、提醒说明。
钩子别写太重,不然每次都卡很久,用户很快就不想用了。
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 的导航和内容变化继续重生成,原站加页、删页、改页时,这里会跟着更新。
人话解释会尽量顺着原页往下讲,但命令、参数名、配置名这些硬东西还是保留原样,免得你抄过去跑不起来。