@mycodemap/mycodemap 0.1.0 → 0.1.1

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 (64) hide show
  1. package/CHANGELOG.md +86 -6
  2. package/README.md +172 -80
  3. package/dist/cli/commands/cycles.d.ts.map +1 -1
  4. package/dist/cli/commands/cycles.js +2 -0
  5. package/dist/cli/commands/cycles.js.map +1 -1
  6. package/dist/cli/commands/init.d.ts.map +1 -1
  7. package/dist/cli/commands/init.js +3 -1
  8. package/dist/cli/commands/init.js.map +1 -1
  9. package/dist/cli/commands/logs.d.ts +5 -0
  10. package/dist/cli/commands/logs.d.ts.map +1 -0
  11. package/dist/cli/commands/logs.js +189 -0
  12. package/dist/cli/commands/logs.js.map +1 -0
  13. package/dist/cli/commands/report.d.ts +12 -0
  14. package/dist/cli/commands/report.d.ts.map +1 -0
  15. package/dist/cli/commands/report.js +158 -0
  16. package/dist/cli/commands/report.js.map +1 -0
  17. package/dist/cli/commands/watch-foreground.d.ts.map +1 -1
  18. package/dist/cli/commands/watch-foreground.js +2 -0
  19. package/dist/cli/commands/watch-foreground.js.map +1 -1
  20. package/dist/cli/commands/watch.d.ts.map +1 -1
  21. package/dist/cli/commands/watch.js +2 -0
  22. package/dist/cli/commands/watch.js.map +1 -1
  23. package/dist/cli/first-run-guide.d.ts +23 -0
  24. package/dist/cli/first-run-guide.d.ts.map +1 -0
  25. package/dist/cli/first-run-guide.js +83 -0
  26. package/dist/cli/first-run-guide.js.map +1 -0
  27. package/dist/cli/index.js +63 -0
  28. package/dist/cli/index.js.map +1 -1
  29. package/dist/cli/platform-check.d.ts +21 -0
  30. package/dist/cli/platform-check.d.ts.map +1 -0
  31. package/dist/cli/platform-check.js +94 -0
  32. package/dist/cli/platform-check.js.map +1 -0
  33. package/dist/cli/tree-sitter-check.d.ts +35 -0
  34. package/dist/cli/tree-sitter-check.d.ts.map +1 -0
  35. package/dist/cli/tree-sitter-check.js +133 -0
  36. package/dist/cli/tree-sitter-check.js.map +1 -0
  37. package/dist/cli/utils/sanitize.d.ts +54 -0
  38. package/dist/cli/utils/sanitize.d.ts.map +1 -0
  39. package/dist/cli/utils/sanitize.js +131 -0
  40. package/dist/cli/utils/sanitize.js.map +1 -0
  41. package/docs/AI_ASSISTANT_SETUP.md +743 -0
  42. package/docs/CI_GATEWAY_DESIGN.md +784 -0
  43. package/docs/OMC_TEAM_DEBUG_REPORT.md +285 -0
  44. package/docs/PUBLISH_NPM_DESIGN_FINAL.md +485 -0
  45. package/docs/REFACTOR_ARCHITECTURE_OVERVIEW.md +552 -0
  46. package/docs/REFACTOR_CONFIDENCE_DESIGN.md +244 -0
  47. package/docs/REFACTOR_GIT_ANALYZER_DESIGN.md +785 -0
  48. package/docs/REFACTOR_ORCHESTRATOR_DESIGN.md +1065 -0
  49. package/docs/REFACTOR_REQUIREMENTS.md +970 -0
  50. package/docs/REFACTOR_RESULT_FUSION_DESIGN.md +315 -0
  51. package/docs/REFACTOR_TEST_LINKER_DESIGN.md +311 -0
  52. package/docs/SETUP_GUIDE.md +407 -0
  53. package/docs/archive/AI_INTEGRATION_GUIDE_ARCHIVED.md +385 -0
  54. package/docs/archive/PUBLISH_NPM_DESIGN_V1.md +1693 -0
  55. package/docs/archive/PUBLISH_NPM_DESIGN_V2.md +390 -0
  56. package/docs/archive/TASK_DESIGN_COVERAGE_REPORT.md +314 -0
  57. package/docs/plans/POST_TASK_PLAN.md +129 -0
  58. package/docs/plans/archive/2026-03-03-deps-path-extension-fix.md +186 -0
  59. package/examples/README.md +61 -0
  60. package/examples/claude/codemap-skill.md +94 -0
  61. package/examples/codex/codemap-agent.md +66 -0
  62. package/examples/copilot/copilot-instructions.md +24 -0
  63. package/examples/kimi/codemap-skill.md +92 -0
  64. package/package.json +5 -3
@@ -0,0 +1,784 @@
1
+ # CI 门禁护栏详细设计
2
+
3
+ > 版本: 1.3
4
+ > 所属模块: CI/CD - Git 工作流门禁
5
+ > 日期: 2026-03-04
6
+
7
+ ---
8
+
9
+ ## 1. 设计目标
10
+
11
+ 建立双层次 CI 门禁护栏,确保代码质量:
12
+
13
+ 1. **本地门禁** (pre-commit hook):快速反馈,提交前检查
14
+ 2. **服务端门禁** (GitHub Actions):最终把关,PR 合并前检查
15
+
16
+ **极简原则**:
17
+ - 总代码量 < 150 行
18
+ - 纯文本输出,无 emoji、无颜色(AI 可解析)
19
+ - 强制标签化 Commit,回答苏格拉底问题
20
+
21
+ ---
22
+
23
+ ## 2. 门禁检查项
24
+
25
+ ### 2.1 检查项清单
26
+
27
+ | 层级 | 检查项 | 失败处理 | 实现方式 |
28
+ |------|--------|----------|----------|
29
+ | **本地** | 测试通过 | ❌ 阻止提交 | `npm test` |
30
+ | **本地** | Commit 格式 | ❌ 阻止提交 | commit-msg hook |
31
+ | **本地** | 文件头注释 | ❌ 阻止提交 | pre-commit hook |
32
+ | **本地** | Commit 文件数量 | ❌ 阻止提交 | commit-msg hook / pre-commit hook |
33
+ | **本地** | 生成代码地图 | ⚠️ 警告 | `codemap generate` |
34
+ | **服务端** | 测试通过 | ❌ 阻止合并 | `npm test` |
35
+ | **服务端** | Commit 格式 | ❌ 阻止合并 | `codemap ci check-commits` |
36
+ | **服务端** | Commit 文件数量 | ❌ 阻止合并 | `codemap ci check-commit-size` |
37
+ | **服务端** | 文件头注释 | ❌ 阻止合并 | `codemap ci check-headers` |
38
+ | **服务端** | 代码地图同步 | ❌ 阻止合并 | `git diff --exit-code` |
39
+ | **服务端** | 危险置信度 | ❌ 阻止合并 | `codemap ci assess-risk` |
40
+ | **服务端** | 输出契约校验 | ❌ 阻止合并 | `codemap ci check-output-contract` |
41
+
42
+ ---
43
+
44
+ ## 3. 本地门禁实现
45
+
46
+ ### 3.1 commit-msg Hook
47
+
48
+ **文件**: `.git/hooks/commit-msg`
49
+
50
+ ```bash
51
+ #!/bin/sh
52
+ # Commit-msg hook: 验证 Commit 消息格式和文件数量
53
+
54
+ MSG_FILE=$1
55
+ MSG=$(head -1 "$MSG_FILE")
56
+
57
+ VALID_TAGS="BUGFIX FEATURE REFACTOR CONFIG DOCS DELETE"
58
+
59
+ # 1. 检查 commit 格式
60
+ if ! echo "$MSG" | grep -qE '^\[(BUGFIX|FEATURE|REFACTOR|CONFIG|DOCS|DELETE)\]'; then
61
+ echo "ERROR: Commit message must start with an uppercase tag."
62
+ echo "Format: [TAG] scope: message"
63
+ echo "Valid tags: $VALID_TAGS"
64
+ exit 1
65
+ fi
66
+
67
+ if ! echo "$MSG" | grep -qE '^\[(BUGFIX|FEATURE|REFACTOR|CONFIG|DOCS|DELETE)\]\s+[^:]+:\s+.+'; then
68
+ echo "ERROR: scope and message are required."
69
+ echo "Format: [TAG] scope: message"
70
+ echo "Example: [FEATURE] cli: add new command"
71
+ exit 1
72
+ fi
73
+
74
+ # 2. 检查 commit 文件数量(初始化 commit 除外)
75
+ MAX_FILES_PER_COMMIT=10
76
+ COMMIT_FILE_COUNT=$(git diff-tree --no-commit-id --name-only -r HEAD 2>/dev/null | wc -l)
77
+
78
+ IS_INITIAL_COMMIT=false
79
+ if [ -z "$(git rev-parse --verify HEAD 2>/dev/null)" ]; then
80
+ IS_INITIAL_COMMIT=true
81
+ fi
82
+
83
+ if [ "$COMMIT_FILE_COUNT" -gt "$MAX_FILES_PER_COMMIT" ] && [ "$IS_INITIAL_COMMIT" = "false" ]; then
84
+ echo "ERROR: Commit contains $COMMIT_FILE_COUNT files, exceeding limit of $MAX_FILES_PER_COMMIT"
85
+ echo "Please split your changes into smaller, focused commits."
86
+ exit 1
87
+ fi
88
+
89
+ echo "Commit message validated"
90
+ exit 0
91
+ ```
92
+
93
+ ### 3.2 Commit 文件数量检查
94
+
95
+ **规则**: 单 commit 文件数量不能超过 **10 个**,超过需要合理理由。
96
+
97
+ **豁免情况**:
98
+ - **初始化 commit**: 仓库的第一个 commit 可以超过 10 个文件
99
+ - **特殊场景**: 需要提供充分的理由说明(在 commit body 中解释)
100
+
101
+ **本地检查**:
102
+ - pre-commit hook 检查 staged 文件数量
103
+ - commit-msg hook 检查最终 commit 的文件数量
104
+
105
+ **服务端检查**:
106
+ - `codemap ci check-commit-size` 检查范围内的所有 commit
107
+
108
+ **有效例外场景**:
109
+ 1. 批量依赖更新(包含 lock 文件变更)
110
+ 2. 自动化代码生成结果
111
+ 3. 大规模重构(无逻辑变更)
112
+
113
+ **拆分大型 commit 的方法**:
114
+ ```bash
115
+ # 撤销最后一次 commit(保留改动)
116
+ git reset HEAD~1
117
+
118
+ # 选择性添加文件
119
+ git add -p
120
+
121
+ # 分批次提交
122
+ git commit -m "[FEATURE] module-a: add feature X"
123
+ git add <more-files>
124
+ git commit -m "[FEATURE] module-b: add feature Y"
125
+ ```
126
+
127
+ ### 3.4 pre-commit Hook
128
+
129
+ **文件**: `.git/hooks/pre-commit`
130
+
131
+ ```bash
132
+ #!/bin/sh
133
+ # Pre-commit hook: 运行测试和文件头检查
134
+
135
+ echo "Running pre-commit checks..."
136
+
137
+ # 1. 运行与变更相关的测试(失败即阻断)
138
+ echo "Running tests for changed files..."
139
+ npx vitest run --changed
140
+ if [ $? -ne 0 ]; then
141
+ echo "ERROR: Tests failed, commit rejected"
142
+ exit 1
143
+ fi
144
+
145
+ echo "Tests passed"
146
+
147
+ # 2. 检查 staged 文件数量(初始化 commit 除外)
148
+ MAX_FILES_PER_COMMIT=10
149
+ STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM | wc -l)
150
+
151
+ IS_INITIAL_COMMIT=false
152
+ git rev-parse --verify HEAD >/dev/null 2>&1
153
+ if [ $? -ne 0 ]; then
154
+ IS_INITIAL_COMMIT=true
155
+ fi
156
+
157
+ if [ "$STAGED_FILES" -gt "$MAX_FILES_PER_COMMIT" ] && [ "$IS_INITIAL_COMMIT" = "false" ]; then
158
+ echo "WARNING: Staged files count ($STAGED_FILES) exceeds limit ($MAX_FILES_PER_COMMIT)"
159
+ echo "Single commit should not contain more than $MAX_FILES_PER_COMMIT files."
160
+ echo "Please consider splitting your changes into smaller, focused commits."
161
+ exit 1
162
+ fi
163
+
164
+ # 3. 检查文件头注释(只检查 staged 的 TS 源文件)
165
+ echo "Checking file headers..."
166
+ STAGED_TS_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep '\.ts$' | grep -v '\.test\.ts$' | grep -v '\.d\.ts$')
167
+
168
+ if [ -n "$STAGED_TS_FILES" ]; then
169
+ MISSING_HEADERS=0
170
+ for file in $STAGED_TS_FILES; do
171
+ if [ ! -f "$file" ]; then
172
+ continue
173
+ fi
174
+ HEAD_CONTENT=$(head -10 "$file")
175
+ if ! echo "$HEAD_CONTENT" | grep -q '\[META\]'; then
176
+ echo "ERROR: $file missing [META] comment"
177
+ MISSING_HEADERS=$((MISSING_HEADERS + 1))
178
+ fi
179
+ if ! echo "$HEAD_CONTENT" | grep -q '\[WHY\]'; then
180
+ echo "ERROR: $file missing [WHY] comment"
181
+ MISSING_HEADERS=$((MISSING_HEADERS + 1))
182
+ fi
183
+ done
184
+
185
+ if [ $MISSING_HEADERS -gt 0 ]; then
186
+ echo "Add header comments at file top:"
187
+ echo "// [META] since:YYYY-MM | owner:team | stable:false"
188
+ echo "// [WHY] Explain why this file exists"
189
+ exit 1
190
+ fi
191
+ fi
192
+
193
+ echo "File headers passed"
194
+
195
+ # 4. 生成 AI 饲料(警告级,不阻断)
196
+ echo "Generating AI feed..."
197
+ npx mycodemap generate --quiet >/dev/null 2>&1 &
198
+
199
+ echo "Pre-commit checks passed"
200
+ exit 0
201
+ ```
202
+
203
+ ### 3.3 Husky 集成(推荐)
204
+
205
+ **安装**: `npm install husky --save-dev`
206
+
207
+ **package.json**:
208
+ ```json
209
+ {
210
+ "scripts": {
211
+ "prepare": "husky install",
212
+ "test": "vitest run"
213
+ },
214
+ "devDependencies": {
215
+ "husky": "^8.0.0"
216
+ }
217
+ }
218
+ ```
219
+
220
+ **初始化**:
221
+ ```bash
222
+ npx husky install
223
+ npx husky add .husky/commit-msg 'node scripts/verify-commit.js "$1"'
224
+ npx husky add .husky/pre-commit 'npm test && node scripts/check-headers.js'
225
+ ```
226
+
227
+ ---
228
+
229
+ ## 4. 服务端门禁实现
230
+
231
+ ### 4.1 GitHub Actions Workflow
232
+
233
+ **文件**: `.github/workflows/ci-gateway.yml`
234
+
235
+ ```yaml
236
+ name: CI Gateway
237
+
238
+ on:
239
+ push:
240
+ branches: [ main, develop ]
241
+ pull_request:
242
+ branches: [ main, develop ]
243
+
244
+ jobs:
245
+ ci-gateway:
246
+ runs-on: ubuntu-latest
247
+
248
+ steps:
249
+ - name: Checkout
250
+ uses: actions/checkout@v4
251
+ with:
252
+ fetch-depth: 0 # 需要完整历史检查 commit
253
+
254
+ - name: Setup Node.js
255
+ uses: actions/setup-node@v4
256
+ with:
257
+ node-version: '20'
258
+ cache: 'npm'
259
+
260
+ - name: Install dependencies
261
+ run: npm ci
262
+
263
+ # 1. 运行测试
264
+ - name: Run tests
265
+ run: npm test
266
+
267
+ # 2. 检查 Commit 格式
268
+ - name: Check commit format
269
+ run: npx codemap ci check-commits
270
+
271
+ # 3. 检查 Commit 文件数量
272
+ - name: Check commit size
273
+ run: npx codemap ci check-commit-size
274
+
275
+ # 4. 检查文件头注释
276
+ - name: Check file headers
277
+ run: npx codemap ci check-headers
278
+
279
+ # 5. 生成代码地图并检查同步
280
+ - name: Generate code map
281
+ run: |
282
+ npx codemap generate
283
+ git diff --exit-code .mycodemap/ || (echo "Code map is out of sync. Run 'codemap generate' and commit the changes." && exit 1)
284
+
285
+ # 6. 评估危险置信度
286
+ - name: Assess risk
287
+ run: npx codemap ci assess-risk --threshold=0.7
288
+
289
+ # 6. 输出契约检查(machine/json)
290
+ - name: Check output contract
291
+ run: npx codemap ci check-output-contract --schema-version v1.0.0 --top-k 8 --max-tokens 160
292
+ ```
293
+
294
+ ### 4.2 CI 子命令实现
295
+
296
+ **文件**: `src/cli/commands/ci.ts`
297
+
298
+ ```typescript
299
+ // src/cli/commands/ci.ts
300
+ // 极简实现: ~80 行
301
+
302
+ import { Command } from 'commander';
303
+ import { execSync } from 'child_process';
304
+ import { GitAnalyzer } from '../../orchestrator/git-analyzer.js';
305
+ import { FileHeaderScanner } from '../../orchestrator/file-header-scanner.js';
306
+ import { globby } from 'globby';
307
+
308
+ const ci = new Command('ci').description('CI gateway commands');
309
+
310
+ // 检查 Commit 格式
311
+ ci.command('check-commits')
312
+ .description('Validate commit message format')
313
+ .action(() => {
314
+ const analyzer = new GitAnalyzer();
315
+ const commits = execSync('git log --format=%s origin/main..HEAD', { encoding: 'utf-8' })
316
+ .split('\n')
317
+ .filter(Boolean);
318
+
319
+ let errors = 0;
320
+ for (const msg of commits) {
321
+ const tag = analyzer.parseCommitTag(msg);
322
+ if (!tag) {
323
+ console.error(`ERROR: Invalid commit format: "${msg}"`);
324
+ errors++;
325
+ }
326
+ }
327
+
328
+ if (errors > 0) {
329
+ console.error(`\n${errors} commits failed validation`);
330
+ process.exit(1);
331
+ }
332
+
333
+ console.log(`✓ ${commits.length} commits validated`);
334
+ });
335
+
336
+ // 检查文件头注释
337
+ ci.command('check-headers')
338
+ .description('Validate file header comments')
339
+ .action(async () => {
340
+ const scanner = new FileHeaderScanner();
341
+ const files = await globby(['src/**/*.ts', '!src/**/*.test.ts']);
342
+
343
+ let errors = 0;
344
+ for (const file of files) {
345
+ const result = scanner.validate(file);
346
+ if (!result.valid) {
347
+ console.error(`ERROR: ${file} missing: ${result.missing.join(', ')}`);
348
+ errors++;
349
+ }
350
+ }
351
+
352
+ if (errors > 0) {
353
+ console.error(`\n${errors} files missing required headers`);
354
+ process.exit(1);
355
+ }
356
+
357
+ console.log(`✓ ${files.length} files validated`);
358
+ });
359
+
360
+ // 评估危险置信度(简化版,不依赖 AI Feed)
361
+ ci.command('assess-risk')
362
+ .description('Assess risk level of changes')
363
+ .option('-t, --threshold <n>', 'Risk threshold (0-1)', '0.7')
364
+ .action(async (options) => {
365
+ const changedFiles = execSync('git diff --name-only origin/main...HEAD', { encoding: 'utf-8' })
366
+ .split('\n')
367
+ .filter(f => f.endsWith('.ts') && !f.endsWith('.test.ts'));
368
+
369
+ if (changedFiles.length === 0) {
370
+ console.log('✓ No changed TypeScript files');
371
+ return;
372
+ }
373
+
374
+ // 简化版风险评估:基于文件数量和变更范围
375
+ let riskScore = 0.3; // 基础风险
376
+
377
+ // 文件数量风险
378
+ if (changedFiles.length > 10) {
379
+ riskScore += 0.2;
380
+ }
381
+ if (changedFiles.length > 20) {
382
+ riskScore += 0.2;
383
+ }
384
+
385
+ // 核心文件变更风险
386
+ const coreFiles = changedFiles.filter(f =>
387
+ f.includes('/core/') ||
388
+ f.includes('/orchestrator/') ||
389
+ f.endsWith('/index.ts')
390
+ );
391
+ if (coreFiles.length > 0) {
392
+ riskScore += 0.15;
393
+ }
394
+
395
+ riskScore = Math.min(1, riskScore);
396
+
397
+ console.log(`Risk assessment: score=${riskScore.toFixed(2)}, threshold=${options.threshold}`);
398
+
399
+ if (riskScore > parseFloat(options.threshold)) {
400
+ console.log('ERROR: Risk score exceeds threshold');
401
+ console.log(`Changed files: ${changedFiles.length}`);
402
+ console.log('Risk mitigation notes required. Add explanation to commit body.');
403
+ process.exit(1);
404
+ } else {
405
+ console.log('✓ Risk assessment passed');
406
+ }
407
+ });
408
+
409
+ // 检查 Commit 文件数量
410
+ ci.command('check-commit-size')
411
+ .description('Check if commit file count exceeds limit (init commit exempt)')
412
+ .option('-r, --range <range>', 'Git log range to check', 'origin/main..HEAD')
413
+ .option('-m, --max-files <number>', 'Max files per commit', '10')
414
+ .action(async (options) => {
415
+ const maxFiles = parseInt(options.maxFiles, 10);
416
+ const { execSync } = require('child_process');
417
+
418
+ const range = options.range || 'origin/main..HEAD';
419
+ const commits = execSync(`git log --format=%H ${range}`, { encoding: 'utf-8' })
420
+ .split('\n')
421
+ .filter(Boolean);
422
+
423
+ let hasErrors = false;
424
+
425
+ for (const hash of commits) {
426
+ const message = execSync(`git log -1 --format=%s ${hash}`, { encoding: 'utf-8' }).trim();
427
+ const fileCount = parseInt(
428
+ execSync(`git diff-tree --no-commit-id --name-only -r ${hash} | wc -l`, { encoding: 'utf-8' }).trim(),
429
+ 10
430
+ );
431
+
432
+ if (fileCount > maxFiles) {
433
+ console.error(`ERROR: Commit ${hash.substring(0, 7)} has ${fileCount} files (limit: ${maxFiles})`);
434
+ console.error(` Message: ${message}`);
435
+ hasErrors = true;
436
+ }
437
+ }
438
+
439
+ if (hasErrors) {
440
+ console.error('\nLarge commits detected. Please split your changes.');
441
+ process.exit(1);
442
+ }
443
+
444
+ console.log(`All ${commits.length} commits pass file count check`);
445
+ });
446
+
447
+ // 输出契约校验 (P1-1 新增)
448
+ ci.command('check-output-contract')
449
+ .description('Validate output contract (schemaVersion, Top-K, token limit)')
450
+ .option('-s, --schema-version <version>', 'Expected schema version', 'v1.0.0')
451
+ .option('-k, --top-k <number>', 'Expected Top-K limit', '8')
452
+ .option('-t, --max-tokens <number>', 'Max tokens per result', '160')
453
+ .action(async (options) => {
454
+ const schemaVersion = options.schemaVersion || 'v1.0.0';
455
+ const topK = parseInt(options.topK) || 8;
456
+ const maxTokens = parseInt(options.maxTokens) || 160;
457
+
458
+ // 运行 analyze 命令获取输出(machine 模式必须为纯 JSON)
459
+ const { execSync } = require('child_process');
460
+ let output;
461
+ try {
462
+ output = execSync('npx codemap analyze --intent search --keywords test --output-mode machine --json', {
463
+ encoding: 'utf-8',
464
+ timeout: 30000
465
+ });
466
+ } catch (error) {
467
+ // 如果命令失败,尝试其他方式获取输出
468
+ console.error('ERROR: Failed to run analyze command');
469
+ process.exit(1);
470
+ }
471
+
472
+ let parsed;
473
+ try {
474
+ parsed = JSON.parse(output);
475
+ } catch {
476
+ console.error('ERROR: Invalid JSON output');
477
+ process.exit(1);
478
+ }
479
+
480
+ let errors = 0;
481
+
482
+ // 1. 校验 schemaVersion
483
+ if (!parsed.schemaVersion) {
484
+ console.error('ERROR: Missing schemaVersion in output');
485
+ errors++;
486
+ } else if (parsed.schemaVersion !== schemaVersion) {
487
+ console.error(`ERROR: schemaVersion mismatch: expected ${schemaVersion}, got ${parsed.schemaVersion}`);
488
+ errors++;
489
+ }
490
+
491
+ // 2. 校验 Top-K
492
+ const resultCount = parsed.results?.length || 0;
493
+ if (resultCount > topK) {
494
+ console.error(`ERROR: Result count ${resultCount} exceeds Top-K limit ${topK}`);
495
+ errors++;
496
+ }
497
+
498
+ // 3. 校验 token 限制 (简单估算)
499
+ if (parsed.results) {
500
+ for (const result of parsed.results) {
501
+ const tokenEstimate = result.content?.split(/[\s\u4e00-\u9fa5]/).filter(Boolean).length || 0;
502
+ if (tokenEstimate > maxTokens) {
503
+ console.error(`ERROR: Result token count ${tokenEstimate} exceeds limit ${maxTokens}`);
504
+ errors++;
505
+ break; // 只报告一次
506
+ }
507
+ }
508
+ }
509
+
510
+ if (errors > 0) {
511
+ console.error(`\n${errors} output contract violations detected`);
512
+ process.exit(1);
513
+ }
514
+
515
+ console.log(`✓ Output contract validated (schema: ${schemaVersion}, topK: ≤${topK}, tokens: ≤${maxTokens})`);
516
+ });
517
+
518
+ export default ci;
519
+ ```
520
+
521
+ ---
522
+
523
+ ## 5. 文件头注释模板
524
+
525
+ ### 5.1 自动添加脚本
526
+
527
+ **文件**: `scripts/add-headers.js`
528
+
529
+ ```javascript
530
+ #!/usr/bin/env node
531
+ // 自动添加文件头注释模板
532
+
533
+ import fs from 'fs';
534
+ import { globby } from 'globby';
535
+
536
+ const TEMPLATE = `// [META] since:2024-03 | owner:backend-team | stable:false
537
+ // [WHY] TODO: 回答为什么存在这个文件
538
+
539
+ `;
540
+
541
+ async function main() {
542
+ const files = await globby(['src/**/*.ts', '!src/**/*.test.ts']);
543
+
544
+ for (const file of files) {
545
+ const content = fs.readFileSync(file, 'utf-8');
546
+
547
+ if (!content.includes('[META]')) {
548
+ const newContent = TEMPLATE + content;
549
+ fs.writeFileSync(file, newContent);
550
+ console.log(`Updated: ${file}`);
551
+ }
552
+ }
553
+
554
+ console.log(`\nProcessed ${files.length} files`);
555
+ console.log('Please edit [WHY] comments to explain file purpose');
556
+ }
557
+
558
+ main();
559
+ ```
560
+
561
+ **使用**:
562
+ ```bash
563
+ node scripts/add-headers.js
564
+ ```
565
+
566
+ ---
567
+
568
+ ## 6. 迁移指南
569
+
570
+ ### 6.1 现有项目迁移步骤
571
+
572
+ 1. **安装依赖**:
573
+ ```bash
574
+ npm install husky --save-dev
575
+ npm install
576
+ ```
577
+
578
+ 2. **初始化 hooks**:
579
+ ```bash
580
+ npx husky install
581
+ npx husky add .husky/commit-msg 'node scripts/verify-commit.js "$1"'
582
+ npx husky add .husky/pre-commit 'npm test && node scripts/check-headers.js'
583
+ ```
584
+
585
+ 3. **批量添加文件头**:
586
+ ```bash
587
+ node scripts/add-headers.js
588
+ # 然后手动编辑 [WHY] 注释
589
+ ```
590
+
591
+ 4. **更新现有 Commit**(可选):
592
+ ```bash
593
+ # 修改最后一次 commit
594
+ git commit --amend -m "[REFACTOR] project: add CI gateway and file headers"
595
+ ```
596
+
597
+ ### 6.2 Commit 格式转换示例
598
+
599
+ | 旧格式 | 新格式 |
600
+ |--------|--------|
601
+ | `fix: bug in parser` | `[BUGFIX] parser: fix token handling bug` |
602
+ | `add new feature` | `[FEATURE] cli: add analyze command` |
603
+ | `update docs` | `[DOCS] readme: update installation guide` |
604
+ | `refactor cache` | `[REFACTOR] cache: simplify LRU implementation` |
605
+
606
+ ---
607
+
608
+ ## 7. 失败场景处理
609
+
610
+ ### 7.1 本地提交失败
611
+
612
+ ```bash
613
+ $ git commit -m "fix bug"
614
+ ERROR: 提交信息必须以大写标签开头
615
+
616
+ 格式: [TAG] scope: message
617
+
618
+ 允许的 TAG:
619
+ [BUGFIX] - 修复问题
620
+ [FEATURE] - 新功能
621
+ [REFACTOR] - 重构
622
+ [CONFIG] - 配置变更
623
+ [DOCS] - 文档
624
+ [DELETE] - 删除代码
625
+
626
+ 示例:
627
+ [BUGFIX] git-analyzer: fix risk score calculation
628
+ ```
629
+
630
+ ### 7.2 服务端 PR 失败
631
+
632
+ GitHub Actions 会显示:
633
+ - ❌ Tests failed
634
+ - ❌ Invalid commit format
635
+ - ❌ Missing file headers
636
+ - ⚠️ High risk files detected
637
+
638
+ ---
639
+
640
+ ## 8. 模块依赖
641
+
642
+ ```
643
+ CI Gateway
644
+
645
+ ├── Git Hooks
646
+ │ ├── commit-msg (验证格式)
647
+ │ └── pre-commit (测试+文件头)
648
+
649
+ ├── GitHub Actions
650
+ │ └── .github/workflows/ci-gateway.yml
651
+
652
+ ├── CLI Commands (src/cli/commands/ci.ts)
653
+ │ ├── check-commits
654
+ │ ├── check-commit-size (v1.3 新增)
655
+ │ ├── check-headers
656
+ │ ├── assess-risk (简化版)
657
+ │ └── check-output-contract (P1-1 新增)
658
+
659
+ └── Dependencies
660
+ ├── GitAnalyzer (解析 commit tag)
661
+ └── FileHeaderScanner (验证注释)
662
+ ```
663
+
664
+ ---
665
+
666
+ ## 9. 工作流集成 (v2.5 规划)
667
+
668
+ ### 9.1 工作流阶段的 CI 验证
669
+
670
+ 在编排层工作流中,CI 门禁作为最后一个阶段自动执行:
671
+
672
+ ```typescript
673
+ // 工作流阶段的 CI 集成
674
+
675
+ const PHASE_CI_CONFIG: Record<WorkflowPhase, CIConfig> = {
676
+ reference: {
677
+ preChecks: [],
678
+ postChecks: [],
679
+ required: false
680
+ },
681
+ impact: {
682
+ preChecks: [],
683
+ postChecks: [],
684
+ required: false
685
+ },
686
+ risk: {
687
+ preChecks: ['assess-risk'],
688
+ postChecks: [],
689
+ required: true
690
+ },
691
+ implementation: {
692
+ preChecks: ['check-headers'],
693
+ postChecks: [],
694
+ required: true
695
+ },
696
+ commit: {
697
+ preChecks: ['check-commits', 'check-headers'],
698
+ postChecks: [],
699
+ required: true
700
+ },
701
+ ci: {
702
+ preChecks: ['check-commits', 'check-headers', 'assess-risk', 'check-output-contract'],
703
+ postChecks: ['npm test'],
704
+ required: true
705
+ }
706
+ };
707
+ ```
708
+
709
+ ### 9.2 工作流 CI 执行器
710
+
711
+ ```typescript
712
+ class WorkflowCIExecutor {
713
+ private ciCommands: Map<string, () => Promise<CICheckResult>>;
714
+
715
+ /**
716
+ * 在指定阶段执行 CI 检查
717
+ */
718
+ async executePhaseChecks(phase: WorkflowPhase): Promise<CIExecutionResult> {
719
+ const config = PHASE_CI_CONFIG[phase];
720
+ const results: CICheckResult[] = [];
721
+
722
+ // 执行预检查
723
+ for (const check of config.preChecks) {
724
+ const executor = this.ciCommands.get(check);
725
+ if (executor) {
726
+ const result = await executor();
727
+ results.push(result);
728
+
729
+ if (!result.passed && config.required) {
730
+ return {
731
+ phase,
732
+ passed: false,
733
+ results,
734
+ failedCheck: check
735
+ };
736
+ }
737
+ }
738
+ }
739
+
740
+ return {
741
+ phase,
742
+ passed: results.every(r => r.passed),
743
+ results
744
+ };
745
+ }
746
+ }
747
+ ```
748
+
749
+ ---
750
+
751
+ ## 10. 检查项汇总
752
+
753
+ | 检查项 | 本地 | 服务端 | 工具 |
754
+ |--------|------|--------|------|
755
+ | 测试通过 | ✅ | ✅ | `npm test` |
756
+ | Commit 格式 `[TAG]` | ✅ | ✅ | `commit-msg hook` / `codemap ci check-commits` |
757
+ | Commit 文件数量 (≤10) | ✅ | ✅ | `commit-msg hook` / `codemap ci check-commit-size` |
758
+ | 文件头 `[META]` | ✅ | ✅ | `pre-commit hook` / `codemap ci check-headers` |
759
+ | 文件头 `[WHY]` | ✅ | ✅ | `pre-commit hook` / `codemap ci check-headers` |
760
+ | 代码地图生成 | ⚠️ | ✅ | `codemap generate` |
761
+ | 危险置信度评估 | ❌ | ✅ (P0-2) | `codemap ci assess-risk` |
762
+ | 输出契约校验 | ❌ | ✅ (P1-1) | `codemap ci check-output-contract` |
763
+
764
+ ---
765
+
766
+ ## 11. 极简实现统计
767
+
768
+ | 组件 | 代码量 | 文件 |
769
+ |------|--------|------|
770
+ | commit-msg hook | ~50 行 | `.git/hooks/commit-msg` |
771
+ | pre-commit hook | ~60 行 | `.git/hooks/pre-commit` |
772
+ | CI 子命令 | ~100 行 | `src/cli/commands/ci.ts` |
773
+ | **总计** | **~210 行** | **符合极简原则** |
774
+
775
+ ---
776
+
777
+ ## 附录:版本历史
778
+
779
+ | 版本 | 日期 | 变更 |
780
+ |------|------|------|
781
+ | 1.0 | 2026-02-28 | 初始版本 |
782
+ | 1.1 | 2026-03-01 | 添加输出契约校验 |
783
+ | 1.2 | 2026-03-03 | 移除 AI 饲料相关功能,简化风险评估 |
784
+ | 1.3 | 2026-03-04 | 添加 commit 文件数量限制检查(≤10 个)|