speccore 8.3.313 → 8.3.332

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 (122) hide show
  1. package/dist/cli.js +3 -1
  2. package/dist/cli.js.map +1 -1
  3. package/dist/commands/analyze.js +2 -2
  4. package/dist/commands/analyze.js.map +1 -1
  5. package/dist/commands/doc2spec.d.ts +12 -0
  6. package/dist/commands/doc2spec.d.ts.map +1 -1
  7. package/dist/commands/doc2spec.js +64 -17
  8. package/dist/commands/doc2spec.js.map +1 -1
  9. package/dist/commands/done.js +4 -1
  10. package/dist/commands/done.js.map +1 -1
  11. package/dist/commands/execute.js +22 -19
  12. package/dist/commands/execute.js.map +1 -1
  13. package/dist/commands/help.js +1 -0
  14. package/dist/commands/help.js.map +1 -1
  15. package/dist/commands/init.d.ts.map +1 -1
  16. package/dist/commands/init.js +299 -113
  17. package/dist/commands/init.js.map +1 -1
  18. package/dist/commands/iteration/create.js +9 -6
  19. package/dist/commands/iteration/create.js.map +1 -1
  20. package/dist/commands/iteration/taskify.d.ts +8 -0
  21. package/dist/commands/iteration/taskify.d.ts.map +1 -1
  22. package/dist/commands/iteration/taskify.js +55 -38
  23. package/dist/commands/iteration/taskify.js.map +1 -1
  24. package/dist/commands/review.js +2 -7
  25. package/dist/commands/review.js.map +1 -1
  26. package/dist/commands/run.js +11 -2
  27. package/dist/commands/run.js.map +1 -1
  28. package/dist/commands/spec2doc.d.ts +7 -0
  29. package/dist/commands/spec2doc.d.ts.map +1 -1
  30. package/dist/commands/spec2doc.js +32 -18
  31. package/dist/commands/spec2doc.js.map +1 -1
  32. package/dist/commands/unitize.d.ts +2 -0
  33. package/dist/commands/unitize.d.ts.map +1 -1
  34. package/dist/commands/unitize.js +79 -0
  35. package/dist/commands/unitize.js.map +1 -1
  36. package/dist/commands/update.d.ts.map +1 -1
  37. package/dist/commands/update.js +6 -0
  38. package/dist/commands/update.js.map +1 -1
  39. package/dist/core/agents/engine/agent-loader.d.ts +2 -0
  40. package/dist/core/agents/engine/agent-loader.d.ts.map +1 -1
  41. package/dist/core/agents/engine/agent-loader.js +8 -2
  42. package/dist/core/agents/engine/agent-loader.js.map +1 -1
  43. package/dist/core/analyze-engine.d.ts +11 -0
  44. package/dist/core/analyze-engine.d.ts.map +1 -1
  45. package/dist/core/analyze-engine.js +136 -112
  46. package/dist/core/analyze-engine.js.map +1 -1
  47. package/dist/core/ask-engine.d.ts.map +1 -1
  48. package/dist/core/ask-engine.js +22 -2
  49. package/dist/core/ask-engine.js.map +1 -1
  50. package/dist/core/asset-stamp.d.ts +40 -0
  51. package/dist/core/asset-stamp.d.ts.map +1 -0
  52. package/dist/core/asset-stamp.js +77 -0
  53. package/dist/core/asset-stamp.js.map +1 -0
  54. package/dist/core/change-detection.d.ts +1 -1
  55. package/dist/core/change-detection.d.ts.map +1 -1
  56. package/dist/core/change-detection.js +28 -1
  57. package/dist/core/change-detection.js.map +1 -1
  58. package/dist/core/code-graph/incremental-build.d.ts.map +1 -1
  59. package/dist/core/code-graph/incremental-build.js +2 -2
  60. package/dist/core/code-graph/incremental-build.js.map +1 -1
  61. package/dist/core/code-graph/index.d.ts.map +1 -1
  62. package/dist/core/code-graph/index.js +3 -2
  63. package/dist/core/code-graph/index.js.map +1 -1
  64. package/dist/core/code-graph/requirement-linker.js +2 -2
  65. package/dist/core/code-graph/requirement-linker.js.map +1 -1
  66. package/dist/core/code-scanner.d.ts +11 -0
  67. package/dist/core/code-scanner.d.ts.map +1 -1
  68. package/dist/core/code-scanner.js +103 -1
  69. package/dist/core/code-scanner.js.map +1 -1
  70. package/dist/core/constitution-builder.d.ts +24 -0
  71. package/dist/core/constitution-builder.d.ts.map +1 -1
  72. package/dist/core/constitution-builder.js +110 -88
  73. package/dist/core/constitution-builder.js.map +1 -1
  74. package/dist/core/doc-validator.d.ts.map +1 -1
  75. package/dist/core/doc-validator.js +14 -4
  76. package/dist/core/doc-validator.js.map +1 -1
  77. package/dist/core/git-integration.d.ts +13 -8
  78. package/dist/core/git-integration.d.ts.map +1 -1
  79. package/dist/core/git-integration.js +191 -89
  80. package/dist/core/git-integration.js.map +1 -1
  81. package/dist/core/global-knowledge.js +1 -1
  82. package/dist/core/global-knowledge.js.map +1 -1
  83. package/dist/core/intent-recognition.d.ts.map +1 -1
  84. package/dist/core/intent-recognition.js +40 -0
  85. package/dist/core/intent-recognition.js.map +1 -1
  86. package/dist/core/platform-registry.d.ts +2 -2
  87. package/dist/core/platform-registry.d.ts.map +1 -1
  88. package/dist/core/platform-registry.js +25 -2
  89. package/dist/core/platform-registry.js.map +1 -1
  90. package/dist/core/prompt-builder.js +1 -1
  91. package/dist/core/prompt-builder.js.map +1 -1
  92. package/dist/core/rag-engine.d.ts +25 -0
  93. package/dist/core/rag-engine.d.ts.map +1 -1
  94. package/dist/core/rag-engine.js +88 -3
  95. package/dist/core/rag-engine.js.map +1 -1
  96. package/dist/core/refresh-scheduler.js +39 -17
  97. package/dist/core/refresh-scheduler.js.map +1 -1
  98. package/dist/core/requirement-clarifier.js +1 -1
  99. package/dist/core/requirement-clarifier.js.map +1 -1
  100. package/dist/core/task-paths.d.ts.map +1 -1
  101. package/dist/core/task-paths.js +4 -1
  102. package/dist/core/task-paths.js.map +1 -1
  103. package/dist/core/unified-config.d.ts +8 -0
  104. package/dist/core/unified-config.d.ts.map +1 -1
  105. package/dist/core/unified-config.js +3 -0
  106. package/dist/core/unified-config.js.map +1 -1
  107. package/dist/core/unit-context-assembler.d.ts.map +1 -1
  108. package/dist/core/unit-context-assembler.js +21 -45
  109. package/dist/core/unit-context-assembler.js.map +1 -1
  110. package/dist/core/unit-staffing.d.ts +28 -1
  111. package/dist/core/unit-staffing.d.ts.map +1 -1
  112. package/dist/core/unit-staffing.js +115 -20
  113. package/dist/core/unit-staffing.js.map +1 -1
  114. package/dist/utils/markdown-image.d.ts +12 -0
  115. package/dist/utils/markdown-image.d.ts.map +1 -0
  116. package/dist/utils/markdown-image.js +34 -0
  117. package/dist/utils/markdown-image.js.map +1 -0
  118. package/package.json +2 -1
  119. package/templates/html/speccore-ask-guide.html +1 -1
  120. package/templates/html/speccore-ask-pipeline.html +1 -1
  121. package/templates/html/speccore-ask-result.html +1 -1
  122. package/templates/html/speccore-help.html +2 -2
@@ -59,8 +59,6 @@ const logger_1 = require("../utils/logger");
59
59
  const task_utils_1 = require("../utils/task-utils");
60
60
  const feature_source_1 = require("./feature-source");
61
61
  const code_scanner_1 = require("./code-scanner");
62
- // v8.3.183+: tree-sitter 结构化摘要,替代原文 slice(0,N) 硬切
63
- const multi_lang_parser_1 = require("./multi-lang-parser");
64
62
  // v8.3.125+: 同义词扩展,解决中英文/缩写匹配失败问题
65
63
  const synonyms_1 = require("../utils/synonyms");
66
64
  const ai_context_generator_1 = require("./ai-context-generator");
@@ -121,6 +119,22 @@ async function runAnalysis(input) {
121
119
  const hasReqs = effectiveInput.requirements.length > 0;
122
120
  const hasSrc = effectiveInput.sources.length > 0;
123
121
  const type = hasReqs && hasSrc ? 'combined' : hasReqs ? 'req' : 'code';
122
+ // ── 统一刷新知识底座(RAG 索引 + 代码索引 + 知识图谱)──
123
+ // v8.3.184+: 收拢到 refresh-scheduler 统一调度(按 scope 分文件 + stale 判定)
124
+ // v8.3.328: 前移到分析之前——RAG 兑底召回依赖索引新鲜,首次分析即生效
125
+ try {
126
+ const { refreshKnowledgeBase } = await Promise.resolve().then(() => __importStar(require('./refresh-scheduler')));
127
+ await refreshKnowledgeBase({
128
+ cwd: process.cwd(),
129
+ iteration: effectiveInput.iteration,
130
+ scope: effectiveInput.scope,
131
+ taskId: effectiveInput.taskId,
132
+ force: true, // 分析命令强制刷新,保证上下文最新
133
+ });
134
+ }
135
+ catch (e) {
136
+ logger_1.logger.debug('知识底座刷新失败(非关键):', e);
137
+ }
124
138
  let result;
125
139
  switch (type) {
126
140
  case 'req':
@@ -148,21 +162,6 @@ async function runAnalysis(input) {
148
162
  }
149
163
  catch { } // 非关键,静默失败
150
164
  }
151
- // ── 统一刷新知识底座(RAG 索引 + 代码索引 + 知识图谱)──
152
- // v8.3.184+: 收拢到 refresh-scheduler 统一调度(按 scope 分文件 + stale 判定)
153
- try {
154
- const { refreshKnowledgeBase } = await Promise.resolve().then(() => __importStar(require('./refresh-scheduler')));
155
- await refreshKnowledgeBase({
156
- cwd: process.cwd(),
157
- iteration: effectiveInput.iteration,
158
- scope: effectiveInput.scope,
159
- taskId: effectiveInput.taskId,
160
- force: true, // 分析命令强制刷新,保证上下文最新
161
- });
162
- }
163
- catch (e) {
164
- logger_1.logger.debug('知识底座刷新失败(非关键):', e);
165
- }
166
165
  return result;
167
166
  }
168
167
  // ================================================================
@@ -189,46 +188,41 @@ async function analyzeRequirements(input) {
189
188
  const fullContent = allContent.join('\n\n---\n\n');
190
189
  const issues = scanCompleteness(fullContent);
191
190
  const archImpact = await analyzeArchitectureImpact(fullContent);
192
- // ── 需求分析时默认读取关联代码(避免分析不接地气 + 减少重复 token 消耗) ──
193
- // v8.3.122+: 增加文件数和字节配额,确保深入阅读业务逻辑
194
- let sourceContents = {};
195
- let sourceSummaries = {};
191
+ // ── 需求分析默认关联代码(v8.3.329:阅读清单模式,不拼源码原文) ──
192
+ // findRelevantCode 相关性导航 → 报告输出「源码阅读清单」(路径+优先级+符号行号),
193
+ // 全文由宿主 AI 用 Read / Grep / LSP 工具按需获取。
194
+ let sourceMatches = [];
196
195
  const shouldReadSource = input.readSource !== false;
197
196
  if (shouldReadSource && (input.iteration || input.taskId)) {
198
197
  // v8.3.3xx+: 消费前兜底——索引缺失/过期时自动重建 + 无索引告警
199
198
  const codeIndexScope = input.sourceScope || await (0, code_scanner_1.resolveIterationScope)(input.iteration);
200
199
  await (0, code_scanner_1.ensureCodeIndexFresh)(codeIndexScope);
201
200
  const limit = input.depth === 'deep' ? 25 : (input.depth === 'quick' ? 5 : 12);
202
- const maxBytes = input.depth === 'deep' ? 150000 : (input.depth === 'quick' ? 30000 : 70000);
203
- const rawMatches = await (0, code_scanner_1.findRelevantCode)(fullContent, limit, input.sourceScope, input.iteration, input.taskId);
204
- sourceContents = await (0, code_scanner_1.readRelevantSource)(rawMatches, maxBytes, limit);
205
- if (Object.keys(sourceContents).length > 0) {
201
+ sourceMatches = await (0, code_scanner_1.findRelevantCode)(fullContent, limit, input.sourceScope, input.iteration, input.taskId);
202
+ if (sourceMatches.length > 0) {
206
203
  const scopeHint = input.sourceScope ? ` (范围: ${input.sourceScope})` : '';
207
- const totalChars = Object.values(sourceContents).reduce((a, c) => a + c.length, 0);
208
- logger_1.logger.info(` 📖 需求分析已关联 ${Object.keys(sourceContents).length} 个源码文件 (${Math.round(totalChars / 1000)}K 字符)${scopeHint}`);
209
- // tree-sitter 结构化摘要(替代原文硬切,失败静默降级原文截断)
210
- try {
211
- sourceSummaries = await (0, multi_lang_parser_1.summarizeSources)(sourceContents);
212
- }
213
- catch { }
204
+ logger_1.logger.info(` 📖 需求分析已关联 ${sourceMatches.length} 个源码文件(阅读清单模式,原文按需深读)${scopeHint}`);
214
205
  }
215
206
  }
207
+ const sourceReadingList = sourceMatches.length > 0 ? await (0, code_scanner_1.formatSourceReadingList)(sourceMatches) : '';
216
208
  let outputPath;
217
209
  let report;
210
+ // v8.3.328: L2 规范注入 + RAG 兑底召回(此前需求分析链路未接入知识库)
211
+ const kctx = await collectKnowledgeContext(input, sourceMatches.length, fullContent);
218
212
  if (input.scope === 'global') {
219
213
  outputPath = (0, path_1.join)((0, task_utils_1.getProjectRoot)(), '.speccore', 'GLOBAL', input.output || 'REQ_CONSISTENCY.md');
220
- report = buildReqConsistencyReport(input, issues, archImpact);
214
+ report = buildReqConsistencyReport(input, issues, archImpact, kctx);
221
215
  }
222
216
  else if (input.scope === 'task') {
223
217
  // task scope — 已在入口校验
224
218
  outputPath = (0, path_1.join)((0, task_utils_1.getProjectRoot)(), `Iteration-${input.iteration}`, '030-tasks', input.taskId, '00-specs', input.output || 'ANALYSIS.md');
225
- report = buildTaskReqReport(input, issues, archImpact, sourceContents, sourceSummaries);
219
+ report = buildTaskReqReport(input, issues, archImpact, sourceReadingList, kctx);
226
220
  }
227
221
  else {
228
222
  // iteration (default)
229
223
  const iterDir = `Iteration-${input.iteration || 'current'}`;
230
224
  outputPath = (0, path_1.join)(iterDir, '020-specs', input.output || 'ANALYSIS.md');
231
- report = buildIterationReqReport(input, issues, archImpact, sourceContents, sourceSummaries);
225
+ report = buildIterationReqReport(input, issues, archImpact, sourceReadingList, kctx);
232
226
  // 按端分目录输出
233
227
  await writePerPlatform(iterDir, report, input.output || 'ANALYSIS.md');
234
228
  // 按功能模块输出到 020-specs/features/
@@ -262,7 +256,7 @@ async function analyzeRequirements(input) {
262
256
  summary: {
263
257
  issues: issues.length,
264
258
  blockers: issues.filter(i => i.severity === 'blocker').length,
265
- filesAnalyzed: input.requirements.length + Object.keys(sourceContents).length,
259
+ filesAnalyzed: input.requirements.length + sourceMatches.length,
266
260
  apisFound: archImpact.apis?.length || 0,
267
261
  risks: archImpact.risks?.length || 0,
268
262
  },
@@ -355,35 +349,33 @@ async function analyzeCombined(input) {
355
349
  logger_1.logger.info(` 🤖 AI 上下文已生成 → ${aiContext.promptPath}`);
356
350
  logger_1.logger.info(` 📁 源码文件: ${aiContext.totalFiles} 个`);
357
351
  logger_1.logger.info(` 🔗 API: ${aiContext.totalApis} 个`);
358
- // 默认读取相关源码内容注入分析(除非明确关闭)
352
+ // 默认关联相关代码(v8.3.329:阅读清单模式,不拼源码原文)
359
353
  // 解耦设计:从完整索引中按 sourceScope 筛选,不是按 scope 建索引
360
- // v8.3.122+: 增加配额,确保深入阅读业务逻辑和公共模块
361
- let sourceContents = {};
354
+ let sourceMatches = [];
362
355
  const shouldReadSource = input.readSource !== false;
363
356
  if (shouldReadSource) {
364
357
  const limit = input.depth === 'deep' ? 30 : (input.depth === 'quick' ? 8 : 15);
365
- const maxBytes = input.depth === 'deep' ? 200000 : (input.depth === 'quick' ? 40000 : 100000);
366
- const rawMatches = await (0, code_scanner_1.findRelevantCode)(fullReqContent, limit, input.sourceScope, input.iteration, input.taskId);
367
- sourceContents = await (0, code_scanner_1.readRelevantSource)(rawMatches, maxBytes, limit);
368
- if (Object.keys(sourceContents).length > 0) {
358
+ sourceMatches = await (0, code_scanner_1.findRelevantCode)(fullReqContent, limit, input.sourceScope, input.iteration, input.taskId);
359
+ if (sourceMatches.length > 0) {
369
360
  const scopeHint = input.sourceScope ? ` (范围: ${input.sourceScope})` : '';
370
- logger_1.logger.info(` 📖 已读取 ${Object.keys(sourceContents).length} 个源码文件${scopeHint} (${Object.values(sourceContents).reduce((a, c) => a + c.length, 0)} 字符)`);
361
+ logger_1.logger.info(` 📖 已关联 ${sourceMatches.length} 个源码文件${scopeHint}(阅读清单模式,原文按需深读)`);
371
362
  }
372
363
  }
364
+ const sourceReadingList = sourceMatches.length > 0 ? await (0, code_scanner_1.formatSourceReadingList)(sourceMatches) : '';
373
365
  let outputPath;
374
366
  let report;
375
367
  if (input.scope === 'global') {
376
368
  outputPath = (0, path_1.join)('.speccore', 'GLOBAL', input.output || 'ARCH_IMPACT.md');
377
- report = await buildAIEnhancedReport(input, 'global', { issues, archImpact, fileStats, apiInventory, aiContext, sourceContents });
369
+ report = await buildAIEnhancedReport(input, 'global', { issues, archImpact, fileStats, apiInventory, aiContext, sourceMatches, sourceReadingList, reqContent: fullReqContent });
378
370
  }
379
371
  else if (input.scope === 'task') {
380
372
  outputPath = (0, path_1.join)((0, task_utils_1.getProjectRoot)(), `Iteration-${input.iteration}`, '030-tasks', input.taskId, '00-specs', input.output || 'ANALYSIS.md');
381
- report = await buildAIEnhancedReport(input, 'task', { issues, archImpact, fileStats, apiInventory, aiContext, sourceContents });
373
+ report = await buildAIEnhancedReport(input, 'task', { issues, archImpact, fileStats, apiInventory, aiContext, sourceMatches, sourceReadingList, reqContent: fullReqContent });
382
374
  }
383
375
  else {
384
376
  const iterDir = `Iteration-${input.iteration || 'current'}`;
385
377
  outputPath = (0, path_1.join)(iterDir, '020-specs', input.output || 'ANALYSIS.md');
386
- report = await buildAIEnhancedReport(input, 'iteration', { issues, archImpact, fileStats, apiInventory, aiContext, sourceContents });
378
+ report = await buildAIEnhancedReport(input, 'iteration', { issues, archImpact, fileStats, apiInventory, aiContext, sourceMatches, sourceReadingList, reqContent: fullReqContent });
387
379
  await writePerPlatform(iterDir, report, input.output || 'ANALYSIS.md');
388
380
  await writePerFeature(iterDir, report, input.output || 'ANALYSIS.md');
389
381
  }
@@ -423,7 +415,7 @@ async function scanSourceDirs(sources, depth) {
423
415
  if (!(await (0, fs_extra_1.pathExists)(srcDir)))
424
416
  continue;
425
417
  await walkDir(srcDir, srcDir, async (filePath) => {
426
- const relPath = (0, path_1.relative)(process.cwd(), filePath);
418
+ const relPath = (0, path_1.relative)(process.cwd(), filePath).replace(/\\/g, '/');
427
419
  const ext = filePath.split('.').pop()?.toLowerCase() || '';
428
420
  const lang = LANG_MAP[ext];
429
421
  if (!lang)
@@ -464,7 +456,7 @@ async function walkDir(root, dir, callback, maxDepth = 5, visited = new Set()) {
464
456
  continue;
465
457
  if (entry.isDirectory()) {
466
458
  // 使用 path.relative 计算深度,避免尾部斜杠不一致问题
467
- const relPath = (0, path_1.relative)(root, fullPath);
459
+ const relPath = (0, path_1.relative)(root, fullPath).replace(/\\/g, '/');
468
460
  const curDepth = relPath.split('/').filter(Boolean).length;
469
461
  if (curDepth < maxDepth)
470
462
  await walkDir(root, fullPath, callback, maxDepth, visited);
@@ -501,7 +493,7 @@ async function buildApiInventory(sources) {
501
493
  return;
502
494
  try {
503
495
  const content = await (0, fs_extra_1.readFile)(filePath, 'utf-8');
504
- const relPath = (0, path_1.relative)(process.cwd(), filePath);
496
+ const relPath = (0, path_1.relative)(process.cwd(), filePath).replace(/\\/g, '/');
505
497
  // Java: @XXXMapping("/api/...")
506
498
  const javaApis = content.match(/@\w*Mapping\s*\(\s*"(\/[^"]+)"/g) || [];
507
499
  for (const m of javaApis) {
@@ -553,7 +545,7 @@ async function detectHotspots(sources) {
553
545
  try {
554
546
  const content = await (0, fs_extra_1.readFile)(filePath, 'utf-8');
555
547
  const lines = content.split('\n');
556
- const relPath = (0, path_1.relative)(process.cwd(), filePath);
548
+ const relPath = (0, path_1.relative)(process.cwd(), filePath).replace(/\\/g, '/');
557
549
  if (lines.length > 800) {
558
550
  hotspots.push({ file: relPath, severity: 'high', reason: `超大文件 (${lines.length} 行)`, lines: lines.length });
559
551
  }
@@ -600,7 +592,7 @@ async function analyzeDependencies(sources) {
600
592
  return;
601
593
  try {
602
594
  const content = await (0, fs_extra_1.readFile)(filePath, 'utf-8');
603
- const relPath = (0, path_1.relative)(process.cwd(), filePath);
595
+ const relPath = (0, path_1.relative)(process.cwd(), filePath).replace(/\\/g, '/');
604
596
  allFiles.push(relPath);
605
597
  // TS/JS imports — 只匹配项目内部引用 (相对路径)
606
598
  const importMatches = content.match(/import\s+.*?\s+from\s+['"]([^'"]+)['"]/g) || [];
@@ -762,11 +754,14 @@ async function analyzeArchitectureImpact(content) {
762
754
  // ================================================================
763
755
  // 报告生成: 纯需求
764
756
  // ================================================================
765
- function buildReqConsistencyReport(input, issues, archImpact) {
757
+ function buildReqConsistencyReport(input, issues, archImpact, kctx) {
766
758
  const now = new Date().toISOString().split('T')[0];
767
759
  let r = `# 全局需求一致性分析\n\n`;
768
760
  r += `> 分析时间: ${now} | 需求文档: ${input.requirements.length} 个 | 模式: 全局\n\n`;
769
761
  r += `---\n\n`;
762
+ // v8.3.328: 全局知识库(L2 注入)+ RAG 召回结果
763
+ if (kctx)
764
+ r += renderKnowledgeSections(kctx);
770
765
  r += `## 1. 需求覆盖\n\n`;
771
766
  r += `| # | 文档 |\n| :--- | :--- |\n`;
772
767
  for (let i = 0; i < input.requirements.length; i++) {
@@ -786,13 +781,16 @@ function buildReqConsistencyReport(input, issues, archImpact) {
786
781
  r += `- ⚠️ ${risk}\n`;
787
782
  return r;
788
783
  }
789
- function buildIterationReqReport(input, issues, archImpact, sourceContents, sourceSummaries) {
784
+ function buildIterationReqReport(input, issues, archImpact, sourceReadingList, kctx) {
790
785
  const now = new Date().toISOString().split('T')[0];
791
786
  const iter = input.iteration || 'current';
792
787
  const blockerCount = issues.filter(i => i.severity === 'blocker').length;
793
788
  let r = `# 需求分析报告\n\n`;
794
789
  r += `> 迭代: ${iter} | 分析时间: ${now} | 状态: ${blockerCount > 0 ? '🔴 有阻断' : '🟢 可拆分'}\n\n`;
795
790
  r += `---\n\n`;
791
+ // v8.3.328: 全局知识库(L2 注入)+ RAG 召回结果
792
+ if (kctx)
793
+ r += renderKnowledgeSections(kctx);
796
794
  r += `## 1. 需求完整性检查\n\n`;
797
795
  r += `| 严重度 | 分类 | 问题 |\n| :--- | :--- | :--- |\n`;
798
796
  for (const issue of issues) {
@@ -828,23 +826,12 @@ function buildIterationReqReport(input, issues, archImpact, sourceContents, sour
828
826
  r += `- [ ] ${rk}\n`;
829
827
  if (archImpact.newDependencies.length > 0)
830
828
  r += `- [ ] 确认新增依赖的引入方案和排期\n`;
831
- // ── 代码关联分析(v6.8.0 新增:需求分析默认关联代码) ──
832
- // v8.3.122+: 按深度动态调整代码展示配额,确保业务逻辑可见
833
- if (sourceContents && Object.keys(sourceContents).length > 0) {
834
- r += `\n---\n\n## 3.5 关联代码现状\n\n`;
835
- r += `> 以下源码文件与当前需求相关,供技术方案参考\n\n`;
836
- const previewLimit = input.depth === 'deep' ? 3000 : (input.depth === 'quick' ? 800 : 1500);
837
- for (const [path, content] of Object.entries(sourceContents)) {
838
- const summary = sourceSummaries?.[path];
839
- if (summary) {
840
- // 结构化摘要:签名 + 符号 + 接口,避免原文被按字符腰斩
841
- r += `### \`${path}\`\n\n\`\`\`\n${summary}\n\`\`\`\n\n`;
842
- }
843
- else {
844
- const preview = content.slice(0, previewLimit).replace(/\n/g, '\n ');
845
- r += `### \`${path}\`\n\n\`\`\`${path.split('.').pop() || 'ts'}\n${preview}${content.length > previewLimit ? '\n // ... 截断 ...' : ''}\n\`\`\`\n\n`;
846
- }
847
- }
829
+ // ── 代码关联分析(v8.3.329:源码阅读清单,不拼原文) ──
830
+ // CLI 负责导航(路径+优先级+符号行号),全文由宿主 AI 按需深读
831
+ if (sourceReadingList) {
832
+ r += `\n---\n\n## 3.5 关联代码导航\n\n`;
833
+ r += sourceReadingList;
834
+ r += `\n`;
848
835
  }
849
836
  r += `\n---\n\n## 4. 技术方案(待填写)\n\n`;
850
837
  r += `| 模块 | 技术方案 | 负责人 | 预计工时 |\n| :--- | :--- | :--- | :--- |\n| | | | |\n\n`;
@@ -854,10 +841,13 @@ function buildIterationReqReport(input, issues, archImpact, sourceContents, sour
854
841
  r += `- [ ] 需求确认无遗漏\n- [ ] 技术方案评审通过\n- [ ] 工时评估合理\n- [ ] 可以开始拆分任务\n`;
855
842
  return r;
856
843
  }
857
- function buildTaskReqReport(input, issues, archImpact, sourceContents, sourceSummaries) {
844
+ function buildTaskReqReport(input, issues, archImpact, sourceReadingList, kctx) {
858
845
  const now = new Date().toISOString().split('T')[0];
859
846
  let r = `# 任务需求分析\n\n`;
860
847
  r += `> 迭代: ${input.iteration} | 任务: ${input.taskId} | 分析: ${now}\n\n---\n\n`;
848
+ // v8.3.328: 全局知识库(L2 注入)+ RAG 召回结果
849
+ if (kctx)
850
+ r += renderKnowledgeSections(kctx);
861
851
  r += `## 问题清单\n`;
862
852
  if (issues.length === 0)
863
853
  r += `> ✅ 无问题\n`;
@@ -869,20 +859,11 @@ function buildTaskReqReport(input, issues, archImpact, sourceContents, sourceSum
869
859
  for (const rk of archImpact.risks)
870
860
  r += `- ⚠️ ${rk}\n`;
871
861
  }
872
- // ── 代码关联分析(v6.8.0 新增) ──
873
- if (sourceContents && Object.keys(sourceContents).length > 0) {
874
- r += `\n## 关联代码\n\n`;
875
- r += `> 以下源码与当前任务相关\n\n`;
876
- for (const [path, content] of Object.entries(sourceContents)) {
877
- const summary = sourceSummaries?.[path];
878
- if (summary) {
879
- r += `- **\`${path}\`**\n\n \`\`\`\n ${summary}\n \`\`\`\n\n`;
880
- }
881
- else {
882
- const preview = content.slice(0, 400).replace(/\n/g, '\n ');
883
- r += `- **\`${path}\`**\n\n \`\`\`${path.split('.').pop() || 'ts'}\n ${preview}${content.length > 400 ? '\n // ... 截断 ...' : ''}\n \`\`\`\n\n`;
884
- }
885
- }
862
+ // ── 代码关联分析(v8.3.329:源码阅读清单,不拼原文) ──
863
+ if (sourceReadingList) {
864
+ r += `\n## 关联代码导航\n\n`;
865
+ r += sourceReadingList;
866
+ r += `\n`;
886
867
  }
887
868
  return r;
888
869
  }
@@ -1022,11 +1003,59 @@ function buildTaskCodeReport(input, stats, apis, hotspots) {
1022
1003
  r += `- [ ] API 文档已更新 (${apis.length} 个接口)\n`;
1023
1004
  return r;
1024
1005
  }
1006
+ /**
1007
+ * 收集知识上下文:L2 规范注入(loadGlobalContext)+ RAG 兑底召回(searchRagIndex)
1008
+ *
1009
+ * 此前 analyze 链路对 RAG 索引只刷新不检索(索引只被体检从未被使用),
1010
+ * L2 规范也只注入 taskify/execute 的 prompt,analyze 链路完全未接入。
1011
+ */
1012
+ async function collectKnowledgeContext(input, sourceMatchesCount, queryText) {
1013
+ let globalContextStr = '';
1014
+ let ragHits = [];
1015
+ try {
1016
+ const { loadGlobalContext, formatGlobalContext } = await Promise.resolve().then(() => __importStar(require('./prompt-builder')));
1017
+ const gctx = await loadGlobalContext(process.cwd(), 'analyze', undefined, undefined);
1018
+ // 仅当知识库有实质内容才注入(formatGlobalContext 对空 ctx 也输出标题行)
1019
+ const hasContent = !!(gctx.indexSummary ||
1020
+ (gctx.keyFileSummaries && gctx.keyFileSummaries.length > 0) ||
1021
+ (gctx.toc && gctx.toc.length > 0));
1022
+ globalContextStr = hasContent ? formatGlobalContext(gctx) : '';
1023
+ if (sourceMatchesCount === 0) {
1024
+ const { searchRagIndex } = await Promise.resolve().then(() => __importStar(require('./rag-engine')));
1025
+ ragHits = await searchRagIndex(process.cwd(), queryText, {
1026
+ iteration: input.iteration,
1027
+ });
1028
+ if (ragHits.length > 0) {
1029
+ logger_1.logger.info(` 🔎 RAG 语义召回 ${ragHits.length} 条规范文档(源码索引零命中兑底)`);
1030
+ }
1031
+ }
1032
+ }
1033
+ catch { /* 非关键,静默失败 */ }
1034
+ return { globalContextStr, ragHits };
1035
+ }
1036
+ /** 渲染知识库注入小节(全局知识库 + RAG 召回命中) */
1037
+ function renderKnowledgeSections(kctx) {
1038
+ let out = '';
1039
+ if (kctx.globalContextStr) {
1040
+ out += `${kctx.globalContextStr}\n`;
1041
+ }
1042
+ if (kctx.ragHits.length > 0) {
1043
+ out += `## 🔎 相关规范与模式(RAG 召回)\n\n`;
1044
+ out += `> 源码索引零命中,以下为语义检索命中的规范/模式文档,请结合其内容完成分析。\n\n`;
1045
+ for (const hit of kctx.ragHits) {
1046
+ out += `- **${hit.title}**(\`${hit.filePath}\`): ${hit.summary.slice(0, 120)}\n`;
1047
+ }
1048
+ out += `\n`;
1049
+ }
1050
+ return out;
1051
+ }
1025
1052
  async function buildAIEnhancedReport(input, scope, params) {
1026
- const { issues, archImpact, fileStats, apiInventory, aiContext, sourceContents } = params;
1053
+ const { issues, archImpact, fileStats, apiInventory, aiContext, sourceMatches, sourceReadingList } = params;
1027
1054
  const now = new Date().toISOString().split('T')[0];
1028
1055
  const iter = input.iteration || 'current';
1029
1056
  const blockerCount = issues.filter(i => i.severity === 'blocker').length;
1057
+ // v8.3.328: L2 规范注入 + RAG 兑底召回(此前 analyze 链路未接入;RAG 索引只被体检从未被使用)
1058
+ const kctx = await collectKnowledgeContext(input, sourceMatches.length, params.reqContent || input.requirements.join('\n'));
1030
1059
  let r = `# ${scope === 'global' ? '全局架构影响分析' : scope === 'task' ? '任务综合分析' : '需求分析报告'}\n\n`;
1031
1060
  r += `> 迭代: ${iter} | 时间: ${now} | 状态: ${blockerCount > 0 ? '🔴 有阻断' : '🟢 可拆分'}\n`;
1032
1061
  r += `> 需求: ${input.requirements.length} 个 | 源码文件: ${fileStats.totalFiles} 个 | API: ${apiInventory.length} 个\n`;
@@ -1041,7 +1070,11 @@ async function buildAIEnhancedReport(input, scope, params) {
1041
1070
  r += `| 阻断问题 | ${blockerCount} |\n`;
1042
1071
  r += `| API 接口 | ${apiInventory.length} |\n`;
1043
1072
  r += `| 架构风险 | ${archImpact.risks.length} |\n`;
1073
+ r += `| 全局知识库 | ${kctx.globalContextStr ? '已注入' : '未发现'} |\n`;
1074
+ r += `| RAG 召回 | ${kctx.ragHits.length} 条 |\n`;
1044
1075
  r += `\n`;
1076
+ // v8.3.328: 全局知识库(L2 注入)+ RAG 召回结果
1077
+ r += renderKnowledgeSections(kctx);
1045
1078
  // 2. 问题清单 (基础规则检查)
1046
1079
  r += `## 🔍 自动检查结果\n\n`;
1047
1080
  if (issues.length > 0) {
@@ -1091,35 +1124,29 @@ async function buildAIEnhancedReport(input, scope, params) {
1091
1124
  // 6. 文件级变更预测
1092
1125
  r += `## 📄 预计变更文件\n\n`;
1093
1126
  r += `| 文件 | 变更类型 | 影响评估 | 风险等级 |\n| :--- | :--- | :--- | :--- |\n`;
1094
- if (Object.keys(sourceContents).length > 0) {
1095
- for (const [file] of Object.entries(sourceContents).slice(0, 8)) {
1096
- r += `| \`${file}\` | _AI 分析_ | _AI 分析_ | — |\n`;
1127
+ if (sourceMatches.length > 0) {
1128
+ for (const m of sourceMatches.slice(0, 8)) {
1129
+ r += `| \`${m.file}\` | _AI 分析_ | _AI 分析_ | — |\n`;
1097
1130
  }
1098
1131
  }
1099
1132
  r += `| _其他_ | _待 AI 分析_ | — | — |\n\n`;
1100
1133
  r += `> 📌 请 AI 读取上下文文件完成文件级对标\n\n`;
1101
- // 6.5 源码分析清单(已读 + 未覆盖,方便用户发现遗漏并补充)
1134
+ // 6.5 源码关联清单(已关联 + 未覆盖,方便用户发现遗漏并补充)
1102
1135
  {
1103
- const readFiles = Object.keys(sourceContents);
1136
+ const readFiles = sourceMatches.map((m) => m.file);
1104
1137
  const fullIndex = await (0, code_scanner_1.loadFullIndex)();
1105
1138
  const allIndexedFiles = fullIndex?.files?.map(f => f.path) || [];
1106
1139
  const uncoveredFiles = allIndexedFiles.filter(f => !readFiles.includes(f));
1107
1140
  if (readFiles.length > 0 || uncoveredFiles.length > 0) {
1108
1141
  r += `## 📚 源码分析清单\n\n`;
1109
- // 已分析文件
1110
- // v8.3.122+: 增加代码内容预览,确保 AI 能看到业务逻辑
1142
+ // 已关联文件(阅读清单模式,不拼原文)
1111
1143
  if (readFiles.length > 0) {
1112
- r += `### ✅ 已分析(${readFiles.length} 个文件)\n\n`;
1113
- const previewLimit = input.depth === 'deep' ? 3000 : (input.depth === 'quick' ? 800 : 1500);
1114
- for (const [file, content] of Object.entries(sourceContents)) {
1115
- const lines = content.split('\n').length;
1116
- const size = (content.length / 1024).toFixed(1);
1117
- r += `- \`${file}\` (${lines} 行, ${size}KB)\n`;
1118
- // 展示代码预览
1119
- if (content.length > 0) {
1120
- const preview = content.slice(0, previewLimit).replace(/\n/g, '\n ');
1121
- r += `\n \`\`\`${file.split('.').pop() || 'ts'}\n ${preview}${content.length > previewLimit ? '\n // ... 截断 ...' : ''}\n \`\`\`\n\n`;
1122
- }
1144
+ r += `### ✅ 已关联(${readFiles.length} 个文件,阅读清单模式)\n\n`;
1145
+ for (const file of readFiles) {
1146
+ r += `- \`${file}\`\n`;
1147
+ }
1148
+ if (sourceReadingList) {
1149
+ r += `\n${sourceReadingList}\n`;
1123
1150
  }
1124
1151
  }
1125
1152
  // 未覆盖文件(按目录分组)
@@ -1174,12 +1201,9 @@ async function buildAIEnhancedReport(input, scope, params) {
1174
1201
  r += `- [ ] **任务拆分建议**: 推荐任务粒度和依赖关系\n`;
1175
1202
  r += `- [ ] **技术方案推荐**: 架构方案、DB 设计、API 设计建议\n`;
1176
1203
  r += `- [ ] **工时评估**: 基于改动范围的工时估算\n`;
1177
- if (Object.keys(sourceContents).length > 0) {
1178
- r += `\n### 相关源码摘要 (${Object.keys(sourceContents).length} 个文件)\n\n`;
1179
- for (const [file, content] of Object.entries(sourceContents).slice(0, 5)) {
1180
- const lines = content.split('\n').slice(0, 12).join('\n');
1181
- r += `<details><summary><code>${file}</code></summary>\n\n\`\`\`\n${lines}\n\`\`\`\n\n</details>\n\n`;
1182
- }
1204
+ if (sourceMatches.length > 0) {
1205
+ r += `\n### 相关源码导航 (${sourceMatches.length} 个文件)\n\n`;
1206
+ r += `> 详情见上文「源码阅读清单」,全文请用 Read / Grep / LSP 按需深读。\n\n`;
1183
1207
  }
1184
1208
  // 8. 确认清单
1185
1209
  r += `\n---\n\n## ✅ 确认清单\n\n`;
@@ -2015,7 +2039,7 @@ async function generateSpecsFromRequirements(reqPaths, iteration, specDir) {
2015
2039
  const archImpactForAnalysis = await analyzeArchitectureImpact(fullContent);
2016
2040
  files.push({
2017
2041
  filename: 'ANALYSIS.md',
2018
- content: buildIterationReqReport({}, issues, archImpactForAnalysis, {}),
2042
+ content: buildIterationReqReport({}, issues, archImpactForAnalysis, undefined),
2019
2043
  });
2020
2044
  // DEPS.md — 依赖清单(全局公共依赖)
2021
2045
  files.push({