sumulige-claude 1.1.2 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. package/.claude/hooks/code-formatter.cjs +7 -2
  2. package/.claude/hooks/multi-session.cjs +9 -3
  3. package/.claude/hooks/pre-commit.cjs +0 -0
  4. package/.claude/hooks/pre-push.cjs +0 -0
  5. package/.claude/hooks/project-kickoff.cjs +22 -11
  6. package/.claude/hooks/rag-skill-loader.cjs +7 -0
  7. package/.claude/hooks/thinking-silent.cjs +9 -3
  8. package/.claude/hooks/todo-manager.cjs +19 -13
  9. package/.claude/hooks/verify-work.cjs +10 -4
  10. package/.claude/quality-gate.json +9 -3
  11. package/.claude/settings.local.json +16 -1
  12. package/.claude/templates/hooks/README.md +302 -0
  13. package/.claude/templates/hooks/hook.sh.template +94 -0
  14. package/.claude/templates/hooks/user-prompt-submit.cjs.template +116 -0
  15. package/.claude/templates/hooks/user-response-submit.cjs.template +94 -0
  16. package/.claude/templates/hooks/validate.js +173 -0
  17. package/.claude/workflow/document-scanner.js +426 -0
  18. package/.claude/workflow/knowledge-engine.js +941 -0
  19. package/.claude/workflow/notebooklm/browser.js +1028 -0
  20. package/.claude/workflow/phases/phase1-research.js +578 -0
  21. package/.claude/workflow/phases/phase1-research.ts +465 -0
  22. package/.claude/workflow/phases/phase2-approve.js +722 -0
  23. package/.claude/workflow/phases/phase3-plan.js +1200 -0
  24. package/.claude/workflow/phases/phase4-develop.js +894 -0
  25. package/.claude/workflow/search-cache.js +230 -0
  26. package/.claude/workflow/templates/approval.md +315 -0
  27. package/.claude/workflow/templates/development.md +377 -0
  28. package/.claude/workflow/templates/planning.md +328 -0
  29. package/.claude/workflow/templates/research.md +250 -0
  30. package/.claude/workflow/types.js +37 -0
  31. package/.claude/workflow/web-search.js +278 -0
  32. package/.claude-plugin/marketplace.json +2 -2
  33. package/AGENTS.md +176 -0
  34. package/CHANGELOG.md +7 -14
  35. package/cli.js +20 -0
  36. package/config/quality-gate.json +9 -3
  37. package/development/cache/web-search/search_1193d605f8eb364651fc2f2041b58a31.json +36 -0
  38. package/development/cache/web-search/search_3798bf06960edc125f744a1abb5b72c5.json +36 -0
  39. package/development/cache/web-search/search_37c7d4843a53f0d83f1122a6f908a2a3.json +36 -0
  40. package/development/cache/web-search/search_44166fa0153709ee168485a22aa0ab40.json +36 -0
  41. package/development/cache/web-search/search_4deaebb1f77e86a8ca066dc5a49c59fd.json +36 -0
  42. package/development/cache/web-search/search_94da91789466070a7f545612e73c7372.json +36 -0
  43. package/development/cache/web-search/search_dd5de8491b8b803a3cb01339cd210fb0.json +36 -0
  44. package/development/knowledge-base/.index.clean.json +0 -0
  45. package/development/knowledge-base/.index.json +486 -0
  46. package/development/knowledge-base/test-best-practices.md +29 -0
  47. package/development/projects/proj_mkh1pazz_ixmt1/phase1/feasibility-report.md +160 -0
  48. package/development/projects/proj_mkh4jvnb_z7rwf/phase1/feasibility-report.md +160 -0
  49. package/development/projects/proj_mkh4jxkd_ewz5a/phase1/feasibility-report.md +160 -0
  50. package/development/projects/proj_mkh4k84n_ni73k/phase1/feasibility-report.md +160 -0
  51. package/development/projects/proj_mkh4wfyd_u9w88/phase1/feasibility-report.md +160 -0
  52. package/development/projects/proj_mkh4wsbo_iahvf/development/projects/proj_mkh4xbpg_4na5w/phase1/feasibility-report.md +160 -0
  53. package/development/projects/proj_mkh4wsbo_iahvf/phase1/feasibility-report.md +160 -0
  54. package/development/projects/proj_mkh4xulg_1ka8x/phase1/feasibility-report.md +160 -0
  55. package/development/projects/proj_mkh4xwhj_gch8j/phase1/feasibility-report.md +160 -0
  56. package/development/projects/proj_mkh4y2qk_9lm8z/phase1/feasibility-report.md +160 -0
  57. package/development/projects/proj_mkh4y2qk_9lm8z/phase2/requirements.md +226 -0
  58. package/development/projects/proj_mkh4y2qk_9lm8z/phase3/PRD.md +345 -0
  59. package/development/projects/proj_mkh4y2qk_9lm8z/phase3/TASK_PLAN.md +284 -0
  60. package/development/projects/proj_mkh4y2qk_9lm8z/phase3/prototype/README.md +14 -0
  61. package/development/projects/proj_mkh4y2qk_9lm8z/phase4/DEVELOPMENT_LOG.md +35 -0
  62. package/development/projects/proj_mkh4y2qk_9lm8z/phase4/TASKS.md +34 -0
  63. package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/.env.example +5 -0
  64. package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/README.md +60 -0
  65. package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/package.json +25 -0
  66. package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/src/index.js +70 -0
  67. package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/src/routes/index.js +48 -0
  68. package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/tests/health.test.js +20 -0
  69. package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/tests/jest.config.js +21 -0
  70. package/development/projects/proj_mkh7veqg_3lypc/phase1/feasibility-report.md +160 -0
  71. package/development/projects/proj_mkh7veqg_3lypc/phase2/requirements.md +226 -0
  72. package/development/projects/proj_mkh7veqg_3lypc/phase3/PRD.md +345 -0
  73. package/development/projects/proj_mkh7veqg_3lypc/phase3/TASK_PLAN.md +284 -0
  74. package/development/projects/proj_mkh7veqg_3lypc/phase3/prototype/README.md +14 -0
  75. package/development/projects/proj_mkh8k8fo_rmqn5/phase1/feasibility-report.md +160 -0
  76. package/development/projects/proj_mkh8xyhy_1vshq/phase1/feasibility-report.md +178 -0
  77. package/development/projects/proj_mkh8zddd_dhamf/phase1/feasibility-report.md +377 -0
  78. package/development/projects/proj_mkh8zddd_dhamf/phase2/requirements.md +442 -0
  79. package/development/projects/proj_mkh8zddd_dhamf/phase3/api-design.md +800 -0
  80. package/development/projects/proj_mkh8zddd_dhamf/phase3/architecture.md +625 -0
  81. package/development/projects/proj_mkh8zddd_dhamf/phase3/data-model.md +830 -0
  82. package/development/projects/proj_mkh8zddd_dhamf/phase3/risks.md +957 -0
  83. package/development/projects/proj_mkh8zddd_dhamf/phase3/wbs.md +381 -0
  84. package/development/todos/.state.json +14 -1
  85. package/development/todos/INDEX.md +31 -73
  86. package/development/todos/completed/develop/local-knowledge-index.md +85 -0
  87. package/development/todos/{active → completed/develop}/todo-system.md +13 -3
  88. package/development/todos/completed/develop/web-search-integration.md +83 -0
  89. package/development/todos/completed/test/phase1-e2e-test.md +103 -0
  90. package/lib/commands.js +388 -0
  91. package/package.json +3 -2
  92. package/tests/config-manager.test.js +677 -0
  93. package/tests/config-validator.test.js +436 -0
  94. package/tests/errors.test.js +477 -0
  95. package/tests/manual/phase1-e2e.sh +389 -0
  96. package/tests/manual/phase2-test-cases.md +311 -0
  97. package/tests/manual/phase3-test-cases.md +309 -0
  98. package/tests/manual/phase4-test-cases.md +414 -0
  99. package/tests/manual/test-cases.md +417 -0
  100. package/tests/quality-gate.test.js +679 -0
  101. package/tests/quality-rules.test.js +619 -0
  102. package/tests/version-check.test.js +75 -0
@@ -15,6 +15,11 @@ const { spawn } = require('child_process');
15
15
 
16
16
  const PROJECT_DIR = process.env.CLAUDE_PROJECT_DIR || process.cwd();
17
17
 
18
+ // 如果不在 Claude Code 环境中运行,静默退出
19
+ if (!process.env.CLAUDE_PROJECT_DIR) {
20
+ process.exit(0);
21
+ }
22
+
18
23
  // 支持的格式化器配置
19
24
  const FORMATTERS = {
20
25
  // JavaScript/TypeScript
@@ -173,8 +178,8 @@ function main() {
173
178
 
174
179
  // 输出格式化结果(可选,用于调试)
175
180
  if (formatted.length > 0 && process.env.DEBUG_FORMATTER) {
176
- console.log(`\n🎨 [格式化] 已格式化 ${formatted.length} 个文件:`);
177
- formatted.forEach(f => console.log(` - ${f.file} (${f.formatter})`));
181
+ console.error(`\n🎨 [格式化] 已格式化 ${formatted.length} 个文件:`);
182
+ formatted.forEach(f => console.error(` - ${f.file} (${f.formatter})`));
178
183
  }
179
184
 
180
185
  } catch (e) {
@@ -16,6 +16,12 @@ const path = require('path');
16
16
  const os = require('os');
17
17
 
18
18
  const PROJECT_DIR = process.env.CLAUDE_PROJECT_DIR || process.cwd();
19
+
20
+ // 如果不在 Claude Code 环境中运行,静默退出
21
+ if (!process.env.CLAUDE_PROJECT_DIR) {
22
+ process.exit(0);
23
+ }
24
+
19
25
  const SESSIONS_DIR = path.join(PROJECT_DIR, '.claude', 'sessions');
20
26
  const ACTIVE_SESSIONS_FILE = path.join(SESSIONS_DIR, 'active-sessions.json');
21
27
  const SESSION_LOCK_FILE = path.join(SESSIONS_DIR, '.session-lock');
@@ -123,13 +129,13 @@ function displaySessionStatus() {
123
129
  console.log(` 主机: ${getSessionIdentifier()}`);
124
130
 
125
131
  if (sessions.length > 1) {
126
- console.log(`\n 其他活跃会话:`);
132
+ console.error(`\n 其他活跃会话:`);
127
133
  sessions
128
134
  .filter(s => s.id !== currentId)
129
135
  .forEach(s => {
130
136
  const number = s.number || '?';
131
137
  const duration = Math.round((Date.now() - s.startedAt) / 1000 / 60);
132
- console.log(` - 会话 #${number}: ${s.identifier} (${duration}分钟前启动)`);
138
+ console.error(` - 会话 #${number}: ${s.identifier} (${duration}分钟前启动)`);
133
139
  });
134
140
  }
135
141
 
@@ -150,7 +156,7 @@ function main() {
150
156
  if (eventType === 'UserPromptSubmit' && toolInput.length > 20) {
151
157
  // 只在较长的用户输入时显示,避免噪音
152
158
  if (sessions.length > 1 || process.env.SHOW_SESSION_STATUS) {
153
- console.log(`\n🔄 [会话 #${sessionNumber}] 活跃会话: ${sessions.length}\n`);
159
+ console.error(`\n🔄 [会话 #${sessionNumber}] 活跃会话: ${sessions.length}\n`);
154
160
  }
155
161
  }
156
162
 
File without changes
File without changes
@@ -15,6 +15,12 @@ const fs = require('fs');
15
15
  const path = require('path');
16
16
 
17
17
  const PROJECT_DIR = process.env.CLAUDE_PROJECT_DIR || process.cwd();
18
+
19
+ // 如果不在 Claude Code 环境中运行,静默退出
20
+ if (!process.env.CLAUDE_PROJECT_DIR) {
21
+ process.exit(0);
22
+ }
23
+
18
24
  const TEMPLATES_DIR = path.join(PROJECT_DIR, '.claude/templates');
19
25
  const KICKOFF_FILE = path.join(PROJECT_DIR, 'PROJECT_KICKOFF.md');
20
26
  const PLAN_FILE = path.join(PROJECT_DIR, 'TASK_PLAN.md');
@@ -95,19 +101,24 @@ function generateKickoffHint() {
95
101
 
96
102
  // 主函数
97
103
  function main() {
98
- // 如果项目已启动且不是强制模式,静默退出
99
- if (isProjectStarted() && !isForceKickoff()) {
100
- process.exit(0);
104
+ try {
105
+ // 如果项目已启动且不是强制模式,静默退出
106
+ if (isProjectStarted() && !isForceKickoff()) {
107
+ process.exit(0);
108
+ }
109
+
110
+ // 生成提示文件
111
+ const hint = generateKickoffHint();
112
+ fs.mkdirSync(path.dirname(HINT_FILE), { recursive: true });
113
+ fs.writeFileSync(HINT_FILE, hint);
114
+
115
+ // 同时输出到 stdout (供 AI 直接读取)
116
+ console.log(hint);
117
+
118
+ } catch (e) {
119
+ // 静默处理错误
101
120
  }
102
121
 
103
- // 生成提示文件
104
- const hint = generateKickoffHint();
105
- fs.mkdirSync(path.dirname(HINT_FILE), { recursive: true });
106
- fs.writeFileSync(HINT_FILE, hint);
107
-
108
- // 同时输出到 stdout (供 AI 直接读取)
109
- console.log(hint);
110
-
111
122
  process.exit(0);
112
123
  }
113
124
 
@@ -10,6 +10,12 @@ const fs = require('fs');
10
10
  const path = require('path');
11
11
 
12
12
  const PROJECT_DIR = process.env.CLAUDE_PROJECT_DIR || process.cwd();
13
+
14
+ // 如果不在 Claude Code 环境中运行,静默退出
15
+ if (!process.env.CLAUDE_PROJECT_DIR) {
16
+ process.exit(0);
17
+ }
18
+
13
19
  const RAG_DIR = path.join(PROJECT_DIR, '.claude/rag');
14
20
  const SKILL_INDEX_FILE = path.join(RAG_DIR, 'skill-index.json');
15
21
  const SKILLS_DIR = path.join(PROJECT_DIR, '.claude/skills');
@@ -147,6 +153,7 @@ function main() {
147
153
  // 将提示写入文件供 AI 阅读
148
154
  const hint = generateSkillLoadHint(matches);
149
155
  const hintFile = path.join(RAG_DIR, '.skill-hint.txt');
156
+ fs.mkdirSync(path.dirname(hintFile), { recursive: true });
150
157
  fs.writeFileSync(hintFile, hint + '\n');
151
158
 
152
159
  } catch (e) {
@@ -14,7 +14,13 @@ const fs = require('fs');
14
14
  const path = require('path');
15
15
  const { spawn } = require('child_process');
16
16
 
17
- const THINKING_DIR = path.join(process.env.CLAUDE_PROJECT_DIR, '.claude/thinking-routes');
17
+ // 环境变量检查 - 如果不是在项目环境中运行,静默退出
18
+ const PROJECT_DIR = process.env.CLAUDE_PROJECT_DIR || process.cwd();
19
+ if (!process.env.CLAUDE_PROJECT_DIR) {
20
+ process.exit(0);
21
+ }
22
+
23
+ const THINKING_DIR = path.join(PROJECT_DIR, '.claude/thinking-routes');
18
24
  const ACTIVITY_FILE = path.join(THINKING_DIR, '.conversation-flow.json');
19
25
  const SESSION_FILE = path.join(THINKING_DIR, '.current-session');
20
26
  const SYNC_MARKER_FILE = path.join(THINKING_DIR, '.last-sync');
@@ -31,7 +37,7 @@ function getSession() {
31
37
 
32
38
  if (!sessionId) {
33
39
  const date = new Date().toISOString().split('T')[0].replace(/-/g, '');
34
- const project = path.basename(process.env.CLAUDE_PROJECT_DIR).slice(0, 10);
40
+ const project = path.basename(PROJECT_DIR).slice(0, 10);
35
41
  sessionId = `s-${date}-${project}`;
36
42
  fs.writeFileSync(SESSION_FILE, sessionId);
37
43
  }
@@ -75,7 +81,7 @@ function markSynced(turnCount) {
75
81
 
76
82
  // 静默同步到 PROJECT_LOG.md
77
83
  function syncToLog() {
78
- const syncScript = path.join(process.env.CLAUDE_PROJECT_DIR, '.claude/hooks/sync-to-log.sh');
84
+ const syncScript = path.join(PROJECT_DIR, '.claude/hooks/sync-to-log.sh');
79
85
  if (fs.existsSync(syncScript)) {
80
86
  try {
81
87
  // 使用 spawn 静默执行,不输出任何内容
@@ -16,6 +16,12 @@ const fs = require('fs');
16
16
  const path = require('path');
17
17
 
18
18
  const PROJECT_DIR = process.env.CLAUDE_PROJECT_DIR || process.cwd();
19
+
20
+ // 如果不在 Claude Code 环境中运行,静默退出
21
+ if (!process.env.CLAUDE_PROJECT_DIR) {
22
+ process.exit(0);
23
+ }
24
+
19
25
  const TODOS_DIR = path.join(PROJECT_DIR, 'development', 'todos');
20
26
  const INDEX_FILE = path.join(TODOS_DIR, 'INDEX.md');
21
27
  const STATE_FILE = path.join(TODOS_DIR, '.state.json');
@@ -318,11 +324,11 @@ function main() {
318
324
 
319
325
  // 输出建议
320
326
  if (result.suggestions && result.suggestions.length > 0) {
321
- console.log('\n📋 [任务流转] 检测到 ' + result.suggestions.length + ' 个自动流转建议:\n');
327
+ console.error('\n📋 [任务流转] 检测到 ' + result.suggestions.length + ' 个自动流转建议:\n');
322
328
  result.suggestions.forEach((s, i) => {
323
- console.log(` ${i + 1}. ${s.message}`);
329
+ console.error(` ${i + 1}. ${s.message}`);
324
330
  });
325
- console.log('');
331
+ console.error('');
326
332
  }
327
333
 
328
334
  // 在 AgentStop 时输出摘要
@@ -334,10 +340,10 @@ function main() {
334
340
  const completedTotal = completed.research.length + completed.develop.length + completed.test.length;
335
341
 
336
342
  if (activeTotal > 0 || completedTotal > 0) {
337
- console.log(`\n📋 [任务追踪]`);
338
- console.log(` 进行中: ${activeTotal} 个 (📊 ${active.research.length} | 💻 ${active.develop.length} | 🧪 ${active.test.length})`);
339
- console.log(` 已完成: ${completedTotal} 个`);
340
- console.log(` 查看: ${path.relative(PROJECT_DIR, INDEX_FILE)}\n`);
343
+ console.error(`\n📋 [任务追踪]`);
344
+ console.error(` 进行中: ${activeTotal} 个 (📊 ${active.research.length} | 💻 ${active.develop.length} | 🧪 ${active.test.length})`);
345
+ console.error(` 已完成: ${completedTotal} 个`);
346
+ console.error(` 查看: ${path.relative(PROJECT_DIR, INDEX_FILE)}\n`);
341
347
  }
342
348
  }
343
349
 
@@ -348,18 +354,18 @@ function main() {
348
354
  if (require.main === module) {
349
355
  if (process.argv[2] === '--force') {
350
356
  updateIndex();
351
- console.log('✅ Task index updated');
357
+ console.error('✅ Task index updated');
352
358
  } else if (process.argv[2] === '--suggest') {
353
359
  const result = updateIndex();
354
360
  if (result.suggestions.length > 0) {
355
- console.log('\n💡 自动流转建议:\n');
361
+ console.error('\n💡 自动流转建议:\n');
356
362
  result.suggestions.forEach((s, i) => {
357
- console.log(`${i + 1}. ${s.message}`);
358
- console.log(` 开发任务: ${s.developTask.file}`);
359
- console.log(` 测试任务: ${s.testTask}`);
363
+ console.error(`${i + 1}. ${s.message}`);
364
+ console.error(` 开发任务: ${s.developTask.file}`);
365
+ console.error(` 测试任务: ${s.testTask}`);
360
366
  });
361
367
  } else {
362
- console.log('✅ 无待处理的流转建议');
368
+ console.error('✅ 无待处理的流转建议');
363
369
  }
364
370
  } else {
365
371
  main();
@@ -14,6 +14,12 @@ const fs = require('fs');
14
14
  const path = require('path');
15
15
 
16
16
  const PROJECT_DIR = process.env.CLAUDE_PROJECT_DIR || process.cwd();
17
+
18
+ // 如果不在 Claude Code 环境中运行,静默退出
19
+ if (!process.env.CLAUDE_PROJECT_DIR) {
20
+ process.exit(0);
21
+ }
22
+
17
23
  const VERIFY_DIR = path.join(PROJECT_DIR, '.claude/verify');
18
24
  const VERIFY_PENDING_FILE = path.join(VERIFY_DIR, '.pending-verify.json');
19
25
  const VERIFY_LOG_FILE = path.join(VERIFY_DIR, 'verify-log.md');
@@ -110,8 +116,8 @@ function main() {
110
116
 
111
117
  // 输出验证提示(仅在非 PostToolUse 事件,避免重复)
112
118
  if (eventType !== 'PostToolUse') {
113
- console.log(`\n🔍 [验证提醒] ${hint}`);
114
- console.log(`使用 /verify-work 查看待验证任务列表\n`);
119
+ console.error(`\n🔍 [验证提醒] ${hint}`);
120
+ console.error(`使用 /verify-work 查看待验证任务列表\n`);
115
121
  }
116
122
  }
117
123
 
@@ -119,8 +125,8 @@ function main() {
119
125
  if (eventType === 'AgentStop') {
120
126
  const pending = getPendingVerifies();
121
127
  if (pending.length > 0) {
122
- console.log(`\n🔍 [待验证任务] 还有 ${pending.length} 个任务待验证`);
123
- console.log(`使用 /verify-work 查看详情并标记完成\n`);
128
+ console.error(`\n🔍 [待验证任务] 还有 ${pending.length} 个任务待验证`);
129
+ console.error(`使用 /verify-work 查看详情并标记完成\n`);
124
130
  }
125
131
  }
126
132
 
@@ -1,13 +1,19 @@
1
1
  {
2
2
  "enabled": true,
3
- "severity": "warn",
3
+ "severity": "error",
4
4
  "rules": [
5
5
  {
6
6
  "id": "line-count-limit",
7
7
  "enabled": true,
8
- "severity": "error",
8
+ "severity": "warn",
9
9
  "config": {
10
- "maxLines": 800
10
+ "maxLines": 3000,
11
+ "exclude": [
12
+ "node_modules/**",
13
+ "package-lock.json",
14
+ ".claude/skills/**",
15
+ "lib/commands.js"
16
+ ]
11
17
  }
12
18
  },
13
19
  {
@@ -90,7 +90,22 @@
90
90
  "Bash(npm view:*)",
91
91
  "Bash(npm update:*)",
92
92
  "Bash(ln:*)",
93
- "Bash(git reset:*)"
93
+ "Bash(git reset:*)",
94
+ "Bash(echo:*)",
95
+ "Bash(bash:*)",
96
+ "Bash(find:*)",
97
+ "Bash(read:*)",
98
+ "WebFetch(domain:github.com)",
99
+ "Bash(chelper --help:*)",
100
+ "Bash(chelper enter --help:*)",
101
+ "Bash(chelper enter:*)",
102
+ "Bash(chelper doctor:*)",
103
+ "Bash(claude mcp list:*)",
104
+ "Bash(claude:*)",
105
+ "WebFetch(domain:docs.bigmodel.cn)",
106
+ "WebFetch(domain:docs.z.ai)",
107
+ "Bash(CLAUDE_PROJECT_DIR=/Users/sumulige/Documents/Antigravity/talos node:*)",
108
+ "Bash(npm run test:coverage:*)"
94
109
  ]
95
110
  },
96
111
  "hooks": {
@@ -0,0 +1,302 @@
1
+ # Claude Code Hooks 模板库
2
+
3
+ > 标准化 hook 开发,防止常见错误
4
+
5
+ ---
6
+
7
+ ## 📋 目录
8
+
9
+ - [快速开始](#快速开始)
10
+ - [Hook 类型](#hook-类型)
11
+ - [最佳实践](#最佳实践)
12
+ - [常见错误](#常见错误)
13
+ - [检查清单](#检查清单)
14
+
15
+ ---
16
+
17
+ ## 🚀 快速开始
18
+
19
+ ### 1. 选择模板
20
+
21
+ ```bash
22
+ # UserPromptSubmit hook (用户提交提示时触发)
23
+ cp user-prompt-submit.cjs.template my-hook.cjs
24
+
25
+ # UserResponseSubmit hook (AI 返回响应后触发)
26
+ cp user-response-submit.cjs.template my-hook.cjs
27
+
28
+ # Shell hook
29
+ cp hook.sh.template my-hook.sh
30
+ ```
31
+
32
+ ### 2. 修改配置
33
+
34
+ ```javascript
35
+ // 修改配置区域
36
+ const CONFIG = {
37
+ enabled: true,
38
+ verbose: false, // 开发时设为 true
39
+ };
40
+ ```
41
+
42
+ ### 3. 实现逻辑
43
+
44
+ 在标记区域添加你的代码:
45
+
46
+ ```javascript
47
+ // ===== 在这里实现你的 hook 逻辑 =====
48
+
49
+ // 你的代码
50
+
51
+ // ===== Hook 逻辑结束 =====
52
+ ```
53
+
54
+ ### 4. 测试
55
+
56
+ ```bash
57
+ # 添加执行权限
58
+ chmod +x my-hook.cjs
59
+
60
+ # 测试(模拟环境变量)
61
+ CLAUDE_PROJECT_DIR=/path/to/project node my-hook.cjs
62
+
63
+ # 开启调试模式
64
+ VERBOSE=true CLAUDE_PROJECT_DIR=/path/to/project node my-hook.cjs
65
+ ```
66
+
67
+ ---
68
+
69
+ ## 📦 Hook 类型
70
+
71
+ ### UserPromptSubmit
72
+
73
+ 用户每次提交提示时触发。
74
+
75
+ **可用环境变量:**
76
+ - `CLAUDE_PROJECT_DIR` - 项目根目录
77
+ - `CLAUDE_TOOL_NAME` - 当前调用的工具名
78
+ - `CLAUDE_TOOL_INPUT` - 工具输入内容
79
+
80
+ **使用场景:**
81
+ - 追踪用户操作
82
+ - 记录工具调用
83
+ - 触发自动化流程
84
+
85
+ ### UserResponseSubmit
86
+
87
+ AI 返回响应后触发。
88
+
89
+ **可用环境变量:**
90
+ - `CLAUDE_PROJECT_DIR` - 项目根目录
91
+ - `CLAUDE_RESPONSE_CONTENT` - 响应内容
92
+
93
+ **使用场景:**
94
+ - 分析响应内容
95
+ - 统计 token 使用
96
+ - 自动保存代码
97
+
98
+ ---
99
+
100
+ ## ✅ 最佳实践
101
+
102
+ ### 1. 环境变量处理
103
+
104
+ ```javascript
105
+ // ✅ 正确 - 提供 fallback
106
+ const PROJECT_DIR = process.env.CLAUDE_PROJECT_DIR || process.cwd();
107
+
108
+ // ❌ 错误 - 直接使用可能 undefined
109
+ const PROJECT_DIR = process.env.CLAUDE_PROJECT_DIR;
110
+ ```
111
+
112
+ ### 2. 静默退出
113
+
114
+ ```javascript
115
+ // ✅ 正确 - 非项目环境时静默退出
116
+ if (!process.env.CLAUDE_PROJECT_DIR) {
117
+ process.exit(0);
118
+ }
119
+
120
+ // ❌ 错误 - 会抛出异常
121
+ if (!process.env.CLAUDE_PROJECT_DIR) {
122
+ throw new Error('No project dir');
123
+ }
124
+ ```
125
+
126
+ ### 3. 错误处理
127
+
128
+ ```javascript
129
+ // ✅ 正确 - 捕获所有错误
130
+ try {
131
+ // 你的逻辑
132
+ } catch (error) {
133
+ log('Error:', error.message);
134
+ }
135
+
136
+ // ❌ 错误 - 未捕获错误会显示给用户
137
+ // 直接执行可能失败的操作
138
+ ```
139
+
140
+ ### 4. 文件操作
141
+
142
+ ```javascript
143
+ // ✅ 正确 - 安全的文件操作
144
+ function safeWriteFile(filePath, content) {
145
+ try {
146
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
147
+ fs.writeFileSync(filePath, content, 'utf-8');
148
+ return true;
149
+ } catch (e) {
150
+ return false; // 静默失败
151
+ }
152
+ }
153
+
154
+ // ❌ 错误 - 可能因为目录不存在而失败
155
+ fs.writeFileSync(filePath, content);
156
+ ```
157
+
158
+ ### 5. 输出控制
159
+
160
+ ```javascript
161
+ // ✅ 正确 - 使用 stderr 记录日志
162
+ function log(...args) {
163
+ console.error('[Hook]', ...args);
164
+ }
165
+
166
+ // ❌ 错误 - stdout 会干扰响应
167
+ console.log(...args);
168
+ ```
169
+
170
+ ---
171
+
172
+ ## 🐛 常见错误
173
+
174
+ ### 错误 1: 未检查环境变量
175
+
176
+ ```javascript
177
+ // ❌ 问题代码
178
+ const dir = path.join(process.env.CLAUDE_PROJECT_DIR, 'subdir');
179
+
180
+ // ✅ 修复
181
+ const PROJECT_DIR = process.env.CLAUDE_PROJECT_DIR || process.cwd();
182
+ if (!process.env.CLAUDE_PROJECT_DIR) {
183
+ process.exit(0);
184
+ }
185
+ const dir = path.join(PROJECT_DIR, 'subdir');
186
+ ```
187
+
188
+ ### 错误 2: 未捕获异常
189
+
190
+ ```javascript
191
+ // ❌ 问题代码
192
+ function main() {
193
+ const data = JSON.parse(fs.readFileSync('data.json')); // 文件不存在会崩溃
194
+ }
195
+
196
+ // ✅ 修复
197
+ function main() {
198
+ try {
199
+ const data = JSON.parse(fs.readFileSync('data.json') || '{}');
200
+ } catch (e) {
201
+ // 静默处理
202
+ }
203
+ }
204
+ ```
205
+
206
+ ### 错误 3: 输出到 stdout
207
+
208
+ ```javascript
209
+ // ❌ 问题代码
210
+ console.log('Processing...'); // 会出现在用户对话中
211
+
212
+ // ✅ 修复
213
+ function log(...args) {
214
+ if (CONFIG.verbose) {
215
+ console.error('[Hook]', ...args); // stderr 不影响对话
216
+ }
217
+ }
218
+ ```
219
+
220
+ ### 错误 4: 忘记退出码
221
+
222
+ ```javascript
223
+ // ❌ 问题代码
224
+ main(); // 可能返回非零退出码
225
+
226
+ // ✅ 修复
227
+ try {
228
+ main();
229
+ } catch (e) {
230
+ // ...
231
+ }
232
+ process.exit(0); // 始终返回成功
233
+ ```
234
+
235
+ ---
236
+
237
+ ## 📝 Hook 开发检查清单
238
+
239
+ 在提交新 hook 前,确保:
240
+
241
+ - [ ] **环境变量**: 使用 `|| process.cwd()` 提供 fallback
242
+ - [ ] **静默退出**: 非项目环境时 `process.exit(0)`
243
+ - [ ] **错误处理**: 所有可能失败的操作都在 try-catch 中
244
+ - [ ] **文件操作**: 使用安全函数,目录不存在时自动创建
245
+ - [ ] **输出控制**: 调试信息使用 stderr,生产环境静默
246
+ - [ ] **退出码**: 始终返回 0,不影响 Claude Code
247
+ - [ ] **执行权限**: Shell hooks 有 `chmod +x`
248
+ - [ ] **测试**: 在有/无 CLAUDE_PROJECT_DIR 环境下都测试过
249
+
250
+ ---
251
+
252
+ ## 🔧 调试技巧
253
+
254
+ ### 开启调试模式
255
+
256
+ ```bash
257
+ # 临时开启
258
+ VERBOSE=true CLAUDE_PROJECT_DIR=/path/to/project node my-hook.cjs
259
+
260
+ # 或修改配置
261
+ const CONFIG = {
262
+ verbose: true, // 开启调试
263
+ };
264
+ ```
265
+
266
+ ### 查看日志
267
+
268
+ ```bash
269
+ # 追踪执行
270
+ node my-hook.cjs 2>&1 | tee hook-debug.log
271
+
272
+ # 检查退出码
273
+ node my-hook.cjs; echo "Exit code: $?"
274
+ ```
275
+
276
+ ### 常用测试命令
277
+
278
+ ```bash
279
+ # 模拟完整环境
280
+ CLAUDE_PROJECT_DIR=$(pwd) \
281
+ CLAUDE_TOOL_NAME=Edit \
282
+ CLAUDE_TOOL_INPUT="test input" \
283
+ node my-hook.cjs
284
+
285
+ # 测试错误处理
286
+ CLAUDE_PROJECT_DIR="" node my-hook.cjs
287
+ ```
288
+
289
+ ---
290
+
291
+ ## 📚 现有 Hooks 参考
292
+
293
+ | Hook | 功能 | 关键技术 |
294
+ |------|------|----------|
295
+ | `todo-manager.cjs` | 任务管理 | 目录扫描、状态流转 |
296
+ | `thinking-silent.cjs` | 对话追踪 | 静默执行、定期同步 |
297
+ | `code-formatter.cjs` | 代码格式化 | 文件监听、外部调用 |
298
+ | `multi-session.cjs` | 多会话管理 | 会话追踪、状态恢复 |
299
+
300
+ ---
301
+
302
+ > **维护**: 添加新 hook 时,请基于模板创建并更新本文档