debug-hooks
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseDebug Hooks
调试Hook
Systematic workflow for debugging Claude Code hooks.
用于调试Claude Code Hook的系统化工作流。
When to Use
适用场景
- "Hook isn't firing"
- "Hook produces wrong output"
- "SessionEnd not working"
- "PostToolUse hook not triggering"
- "Why didn't my hook run?"
- Hook未触发
- Hook输出错误结果
- SessionEnd无法正常工作
- PostToolUse Hook未触发
- 我的Hook为什么没有运行?
Workflow
工作流
1. Check Outputs First (Observe Before Editing)
1. 先检查输出(编辑前先观察)
bash
undefinedbash
undefinedCheck project cache
检查项目缓存
ls -la $CLAUDE_PROJECT_DIR/.claude/cache/
ls -la $CLAUDE_PROJECT_DIR/.claude/cache/
Check specific outputs
检查特定输出
ls -la $CLAUDE_PROJECT_DIR/.claude/cache/learnings/
ls -la $CLAUDE_PROJECT_DIR/.claude/cache/learnings/
Check for debug logs
检查调试日志
tail $CLAUDE_PROJECT_DIR/.claude/cache/*.log 2>/dev/null
tail $CLAUDE_PROJECT_DIR/.claude/cache/*.log 2>/dev/null
Also check global (common mistake: wrong path)
同时检查全局配置(常见错误:路径错误)
ls -la ~/.claude/cache/ 2>/dev/null
undefinedls -la ~/.claude/cache/ 2>/dev/null
undefined2. Verify Hook Registration
2. 验证Hook注册情况
bash
undefinedbash
undefinedProject settings
项目设置
cat $CLAUDE_PROJECT_DIR/.claude/settings.json | grep -A 20 '"SessionEnd"|"PostToolUse"|"UserPromptSubmit"'
cat $CLAUDE_PROJECT_DIR/.claude/settings.json | grep -A 20 '"SessionEnd"|"PostToolUse"|"UserPromptSubmit"'
Global settings (hooks merge from both)
全局设置(Hook会从两处合并)
cat ~/.claude/settings.json | grep -A 20 '"SessionEnd"|"PostToolUse"|"UserPromptSubmit"'
undefinedcat ~/.claude/settings.json | grep -A 20 '"SessionEnd"|"PostToolUse"|"UserPromptSubmit"'
undefined3. Check Hook Files Exist
3. 检查Hook文件是否存在
bash
undefinedbash
undefinedShell wrappers
Shell包装器
ls -la $CLAUDE_PROJECT_DIR/.claude/hooks/*.sh
ls -la $CLAUDE_PROJECT_DIR/.claude/hooks/*.sh
Compiled bundles (if using TypeScript)
编译后的打包文件(如果使用TypeScript)
ls -la $CLAUDE_PROJECT_DIR/.claude/hooks/dist/*.mjs
undefinedls -la $CLAUDE_PROJECT_DIR/.claude/hooks/dist/*.mjs
undefined4. Test Hook Manually
4. 手动测试Hook
bash
undefinedbash
undefinedSessionEnd hook
SessionEnd Hook
echo '{"session_id": "test-123", "reason": "clear", "transcript_path": "/tmp/test"}' |
$CLAUDE_PROJECT_DIR/.claude/hooks/session-end-cleanup.sh
$CLAUDE_PROJECT_DIR/.claude/hooks/session-end-cleanup.sh
echo '{"session_id": "test-123", "reason": "clear", "transcript_path": "/tmp/test"}' |
$CLAUDE_PROJECT_DIR/.claude/hooks/session-end-cleanup.sh
$CLAUDE_PROJECT_DIR/.claude/hooks/session-end-cleanup.sh
PostToolUse hook (Write tool example)
PostToolUse Hook(Write工具示例)
echo '{"tool_name": "Write", "tool_input": {"file_path": "test.md"}, "session_id": "test-123"}' |
$CLAUDE_PROJECT_DIR/.claude/hooks/handoff-index.sh
$CLAUDE_PROJECT_DIR/.claude/hooks/handoff-index.sh
undefinedecho '{"tool_name": "Write", "tool_input": {"file_path": "test.md"}, "session_id": "test-123"}' |
$CLAUDE_PROJECT_DIR/.claude/hooks/handoff-index.sh
$CLAUDE_PROJECT_DIR/.claude/hooks/handoff-index.sh
undefined5. Check for Silent Failures
5. 检查静默失败情况
If using detached spawn with :
stdio: 'ignore'typescript
// This pattern hides errors!
spawn(cmd, args, { detached: true, stdio: 'ignore' })Fix: Add temporary logging:
typescript
const logFile = fs.openSync('.claude/cache/debug.log', 'a');
spawn(cmd, args, {
detached: true,
stdio: ['ignore', logFile, logFile] // capture stdout/stderr
});如果使用了带有的分离式spawn:
stdio: 'ignore'typescript
// 这种模式会隐藏错误!
spawn(cmd, args, { detached: true, stdio: 'ignore' })修复方案: 添加临时日志记录:
typescript
const logFile = fs.openSync('.claude/cache/debug.log', 'a');
spawn(cmd, args, {
detached: true,
stdio: ['ignore', logFile, logFile] // 捕获stdout/stderr
});6. Rebuild After Edits
6. 编辑后重新构建
If you edited TypeScript source, you MUST rebuild:
bash
cd $CLAUDE_PROJECT_DIR/.claude/hooks
npx esbuild src/session-end-cleanup.ts \
--bundle --platform=node --format=esm \
--outfile=dist/session-end-cleanup.mjsSource edits alone don't take effect - the shell wrapper runs the bundled .
.mjs如果你编辑了TypeScript源码,必须重新构建:
bash
cd $CLAUDE_PROJECT_DIR/.claude/hooks
npx esbuild src/session-end-cleanup.ts \
--bundle --platform=node --format=esm \
--outfile=dist/session-end-cleanup.mjs仅编辑源码不会生效——Shell包装器运行的是打包后的文件。
.mjsCommon Issues
常见问题
| Symptom | Likely Cause | Fix |
|---|---|---|
| Hook never runs | Not registered in settings.json | Add to correct event in settings |
| Hook runs but no output | Detached spawn hiding errors | Add logging, check manually |
| Wrong session ID | Using "most recent" query | Pass ID explicitly |
| Works locally, not in CI | Missing dependencies | Check npx/node availability |
| Runs twice | Registered in both global + project | Remove duplicate |
| 症状 | 可能原因 | 修复方案 |
|---|---|---|
| Hook从未运行 | 未在settings.json中注册 | 在设置中添加到对应事件 |
| Hook运行但无输出 | 分离式spawn隐藏了错误 | 添加日志记录,手动检查 |
| 会话ID错误 | 使用了“最近的”查询 | 显式传递ID |
| 本地可用,CI环境不可用 | 缺少依赖项 | 检查npx/node是否可用 |
| 运行两次 | 同时在全局和项目中注册 | 移除重复项 |
Debug Checklist
调试检查清单
- Outputs exist? ()
ls -la .claude/cache/ - Registered? ()
grep -A10 '"hooks"' .claude/settings.json - Files exist? ()
ls .claude/hooks/*.sh - Bundle current? ()
ls -la .claude/hooks/dist/ - Manual test works? ()
echo '{}' | ./hook.sh - No silent failures? (check for )
stdio: 'ignore'
- 输出文件是否存在?()
ls -la .claude/cache/ - 是否已注册?()
grep -A10 '"hooks"' .claude/settings.json - 文件是否存在?()
ls .claude/hooks/*.sh - 打包文件是否为最新版本?()
ls -la .claude/hooks/dist/ - 手动测试是否可行?()
echo '{}' | ./hook.sh - 无静默失败?(检查是否使用了)
stdio: 'ignore'
Source Sessions
参考会话
Derived from 10 sessions (83% of all learnings):
- a541f08a, 1c21e6c8, 6a9f2d7a, a8bd5cea, 2ca1a178, 657ce0b2, 3998f3a2, 2a829f12, 0b46cfd7, 862f6e2c
源自10个会话(占所有经验的83%):
- a541f08a, 1c21e6c8, 6a9f2d7a, a8bd5cea, 2ca1a178, 657ce0b2, 3998f3a2, 2a829f12, 0b46cfd7, 862f6e2c