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.
- package/.claude/hooks/code-formatter.cjs +7 -2
- package/.claude/hooks/multi-session.cjs +9 -3
- package/.claude/hooks/pre-commit.cjs +0 -0
- package/.claude/hooks/pre-push.cjs +0 -0
- package/.claude/hooks/project-kickoff.cjs +22 -11
- package/.claude/hooks/rag-skill-loader.cjs +7 -0
- package/.claude/hooks/thinking-silent.cjs +9 -3
- package/.claude/hooks/todo-manager.cjs +19 -13
- package/.claude/hooks/verify-work.cjs +10 -4
- package/.claude/quality-gate.json +9 -3
- package/.claude/settings.local.json +16 -1
- package/.claude/templates/hooks/README.md +302 -0
- package/.claude/templates/hooks/hook.sh.template +94 -0
- package/.claude/templates/hooks/user-prompt-submit.cjs.template +116 -0
- package/.claude/templates/hooks/user-response-submit.cjs.template +94 -0
- package/.claude/templates/hooks/validate.js +173 -0
- package/.claude/workflow/document-scanner.js +426 -0
- package/.claude/workflow/knowledge-engine.js +941 -0
- package/.claude/workflow/notebooklm/browser.js +1028 -0
- package/.claude/workflow/phases/phase1-research.js +578 -0
- package/.claude/workflow/phases/phase1-research.ts +465 -0
- package/.claude/workflow/phases/phase2-approve.js +722 -0
- package/.claude/workflow/phases/phase3-plan.js +1200 -0
- package/.claude/workflow/phases/phase4-develop.js +894 -0
- package/.claude/workflow/search-cache.js +230 -0
- package/.claude/workflow/templates/approval.md +315 -0
- package/.claude/workflow/templates/development.md +377 -0
- package/.claude/workflow/templates/planning.md +328 -0
- package/.claude/workflow/templates/research.md +250 -0
- package/.claude/workflow/types.js +37 -0
- package/.claude/workflow/web-search.js +278 -0
- package/.claude-plugin/marketplace.json +2 -2
- package/AGENTS.md +176 -0
- package/CHANGELOG.md +7 -14
- package/cli.js +20 -0
- package/config/quality-gate.json +9 -3
- package/development/cache/web-search/search_1193d605f8eb364651fc2f2041b58a31.json +36 -0
- package/development/cache/web-search/search_3798bf06960edc125f744a1abb5b72c5.json +36 -0
- package/development/cache/web-search/search_37c7d4843a53f0d83f1122a6f908a2a3.json +36 -0
- package/development/cache/web-search/search_44166fa0153709ee168485a22aa0ab40.json +36 -0
- package/development/cache/web-search/search_4deaebb1f77e86a8ca066dc5a49c59fd.json +36 -0
- package/development/cache/web-search/search_94da91789466070a7f545612e73c7372.json +36 -0
- package/development/cache/web-search/search_dd5de8491b8b803a3cb01339cd210fb0.json +36 -0
- package/development/knowledge-base/.index.clean.json +0 -0
- package/development/knowledge-base/.index.json +486 -0
- package/development/knowledge-base/test-best-practices.md +29 -0
- package/development/projects/proj_mkh1pazz_ixmt1/phase1/feasibility-report.md +160 -0
- package/development/projects/proj_mkh4jvnb_z7rwf/phase1/feasibility-report.md +160 -0
- package/development/projects/proj_mkh4jxkd_ewz5a/phase1/feasibility-report.md +160 -0
- package/development/projects/proj_mkh4k84n_ni73k/phase1/feasibility-report.md +160 -0
- package/development/projects/proj_mkh4wfyd_u9w88/phase1/feasibility-report.md +160 -0
- package/development/projects/proj_mkh4wsbo_iahvf/development/projects/proj_mkh4xbpg_4na5w/phase1/feasibility-report.md +160 -0
- package/development/projects/proj_mkh4wsbo_iahvf/phase1/feasibility-report.md +160 -0
- package/development/projects/proj_mkh4xulg_1ka8x/phase1/feasibility-report.md +160 -0
- package/development/projects/proj_mkh4xwhj_gch8j/phase1/feasibility-report.md +160 -0
- package/development/projects/proj_mkh4y2qk_9lm8z/phase1/feasibility-report.md +160 -0
- package/development/projects/proj_mkh4y2qk_9lm8z/phase2/requirements.md +226 -0
- package/development/projects/proj_mkh4y2qk_9lm8z/phase3/PRD.md +345 -0
- package/development/projects/proj_mkh4y2qk_9lm8z/phase3/TASK_PLAN.md +284 -0
- package/development/projects/proj_mkh4y2qk_9lm8z/phase3/prototype/README.md +14 -0
- package/development/projects/proj_mkh4y2qk_9lm8z/phase4/DEVELOPMENT_LOG.md +35 -0
- package/development/projects/proj_mkh4y2qk_9lm8z/phase4/TASKS.md +34 -0
- package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/.env.example +5 -0
- package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/README.md +60 -0
- package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/package.json +25 -0
- package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/src/index.js +70 -0
- package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/src/routes/index.js +48 -0
- package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/tests/health.test.js +20 -0
- package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/tests/jest.config.js +21 -0
- package/development/projects/proj_mkh7veqg_3lypc/phase1/feasibility-report.md +160 -0
- package/development/projects/proj_mkh7veqg_3lypc/phase2/requirements.md +226 -0
- package/development/projects/proj_mkh7veqg_3lypc/phase3/PRD.md +345 -0
- package/development/projects/proj_mkh7veqg_3lypc/phase3/TASK_PLAN.md +284 -0
- package/development/projects/proj_mkh7veqg_3lypc/phase3/prototype/README.md +14 -0
- package/development/projects/proj_mkh8k8fo_rmqn5/phase1/feasibility-report.md +160 -0
- package/development/projects/proj_mkh8xyhy_1vshq/phase1/feasibility-report.md +178 -0
- package/development/projects/proj_mkh8zddd_dhamf/phase1/feasibility-report.md +377 -0
- package/development/projects/proj_mkh8zddd_dhamf/phase2/requirements.md +442 -0
- package/development/projects/proj_mkh8zddd_dhamf/phase3/api-design.md +800 -0
- package/development/projects/proj_mkh8zddd_dhamf/phase3/architecture.md +625 -0
- package/development/projects/proj_mkh8zddd_dhamf/phase3/data-model.md +830 -0
- package/development/projects/proj_mkh8zddd_dhamf/phase3/risks.md +957 -0
- package/development/projects/proj_mkh8zddd_dhamf/phase3/wbs.md +381 -0
- package/development/todos/.state.json +14 -1
- package/development/todos/INDEX.md +31 -73
- package/development/todos/completed/develop/local-knowledge-index.md +85 -0
- package/development/todos/{active → completed/develop}/todo-system.md +13 -3
- package/development/todos/completed/develop/web-search-integration.md +83 -0
- package/development/todos/completed/test/phase1-e2e-test.md +103 -0
- package/lib/commands.js +388 -0
- package/package.json +3 -2
- package/tests/config-manager.test.js +677 -0
- package/tests/config-validator.test.js +436 -0
- package/tests/errors.test.js +477 -0
- package/tests/manual/phase1-e2e.sh +389 -0
- package/tests/manual/phase2-test-cases.md +311 -0
- package/tests/manual/phase3-test-cases.md +309 -0
- package/tests/manual/phase4-test-cases.md +414 -0
- package/tests/manual/test-cases.md +417 -0
- package/tests/quality-gate.test.js +679 -0
- package/tests/quality-rules.test.js +619 -0
- 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.
|
|
177
|
-
formatted.forEach(f => console.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
100
|
-
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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.
|
|
327
|
+
console.error('\n📋 [任务流转] 检测到 ' + result.suggestions.length + ' 个自动流转建议:\n');
|
|
322
328
|
result.suggestions.forEach((s, i) => {
|
|
323
|
-
console.
|
|
329
|
+
console.error(` ${i + 1}. ${s.message}`);
|
|
324
330
|
});
|
|
325
|
-
console.
|
|
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.
|
|
338
|
-
console.
|
|
339
|
-
console.
|
|
340
|
-
console.
|
|
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.
|
|
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.
|
|
361
|
+
console.error('\n💡 自动流转建议:\n');
|
|
356
362
|
result.suggestions.forEach((s, i) => {
|
|
357
|
-
console.
|
|
358
|
-
console.
|
|
359
|
-
console.
|
|
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.
|
|
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.
|
|
114
|
-
console.
|
|
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.
|
|
123
|
-
console.
|
|
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": "
|
|
3
|
+
"severity": "error",
|
|
4
4
|
"rules": [
|
|
5
5
|
{
|
|
6
6
|
"id": "line-count-limit",
|
|
7
7
|
"enabled": true,
|
|
8
|
-
"severity": "
|
|
8
|
+
"severity": "warn",
|
|
9
9
|
"config": {
|
|
10
|
-
"maxLines":
|
|
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 时,请基于模板创建并更新本文档
|