speccore 8.3.312 → 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.
- package/dist/cli.js +4 -6
- package/dist/cli.js.map +1 -1
- package/dist/commands/analyze.js +2 -2
- package/dist/commands/analyze.js.map +1 -1
- package/dist/commands/doc2spec.d.ts +12 -0
- package/dist/commands/doc2spec.d.ts.map +1 -1
- package/dist/commands/doc2spec.js +64 -17
- package/dist/commands/doc2spec.js.map +1 -1
- package/dist/commands/done.js +4 -1
- package/dist/commands/done.js.map +1 -1
- package/dist/commands/execute.d.ts.map +1 -1
- package/dist/commands/execute.js +37 -76
- package/dist/commands/execute.js.map +1 -1
- package/dist/commands/help.js +1 -0
- package/dist/commands/help.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +299 -183
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/iteration/create.js +9 -6
- package/dist/commands/iteration/create.js.map +1 -1
- package/dist/commands/iteration/taskify.d.ts +8 -0
- package/dist/commands/iteration/taskify.d.ts.map +1 -1
- package/dist/commands/iteration/taskify.js +56 -41
- package/dist/commands/iteration/taskify.js.map +1 -1
- package/dist/commands/review.js +2 -7
- package/dist/commands/review.js.map +1 -1
- package/dist/commands/run.js +11 -2
- package/dist/commands/run.js.map +1 -1
- package/dist/commands/spec2doc.d.ts +7 -0
- package/dist/commands/spec2doc.d.ts.map +1 -1
- package/dist/commands/spec2doc.js +32 -18
- package/dist/commands/spec2doc.js.map +1 -1
- package/dist/commands/task/new.d.ts +0 -9
- package/dist/commands/task/new.d.ts.map +1 -1
- package/dist/commands/task/new.js +6 -195
- package/dist/commands/task/new.js.map +1 -1
- package/dist/commands/unitize.d.ts +2 -0
- package/dist/commands/unitize.d.ts.map +1 -1
- package/dist/commands/unitize.js +79 -0
- package/dist/commands/unitize.js.map +1 -1
- package/dist/commands/update.d.ts.map +1 -1
- package/dist/commands/update.js +6 -0
- package/dist/commands/update.js.map +1 -1
- package/dist/core/agents/engine/agent-loader.d.ts +2 -0
- package/dist/core/agents/engine/agent-loader.d.ts.map +1 -1
- package/dist/core/agents/engine/agent-loader.js +8 -2
- package/dist/core/agents/engine/agent-loader.js.map +1 -1
- package/dist/core/analyze-engine.d.ts +11 -0
- package/dist/core/analyze-engine.d.ts.map +1 -1
- package/dist/core/analyze-engine.js +136 -112
- package/dist/core/analyze-engine.js.map +1 -1
- package/dist/core/ask-engine.d.ts.map +1 -1
- package/dist/core/ask-engine.js +22 -2
- package/dist/core/ask-engine.js.map +1 -1
- package/dist/core/asset-stamp.d.ts +40 -0
- package/dist/core/asset-stamp.d.ts.map +1 -0
- package/dist/core/asset-stamp.js +77 -0
- package/dist/core/asset-stamp.js.map +1 -0
- package/dist/core/change-detection.d.ts +1 -1
- package/dist/core/change-detection.d.ts.map +1 -1
- package/dist/core/change-detection.js +28 -1
- package/dist/core/change-detection.js.map +1 -1
- package/dist/core/code-graph/incremental-build.d.ts.map +1 -1
- package/dist/core/code-graph/incremental-build.js +2 -2
- package/dist/core/code-graph/incremental-build.js.map +1 -1
- package/dist/core/code-graph/index.d.ts.map +1 -1
- package/dist/core/code-graph/index.js +3 -2
- package/dist/core/code-graph/index.js.map +1 -1
- package/dist/core/code-graph/requirement-linker.js +2 -2
- package/dist/core/code-graph/requirement-linker.js.map +1 -1
- package/dist/core/code-scanner.d.ts +11 -0
- package/dist/core/code-scanner.d.ts.map +1 -1
- package/dist/core/code-scanner.js +103 -1
- package/dist/core/code-scanner.js.map +1 -1
- package/dist/core/constitution-builder.d.ts +24 -0
- package/dist/core/constitution-builder.d.ts.map +1 -1
- package/dist/core/constitution-builder.js +110 -88
- package/dist/core/constitution-builder.js.map +1 -1
- package/dist/core/doc-validator.d.ts.map +1 -1
- package/dist/core/doc-validator.js +14 -4
- package/dist/core/doc-validator.js.map +1 -1
- package/dist/core/git-integration.d.ts +14 -9
- package/dist/core/git-integration.d.ts.map +1 -1
- package/dist/core/git-integration.js +192 -93
- package/dist/core/git-integration.js.map +1 -1
- package/dist/core/global-knowledge.js +1 -1
- package/dist/core/global-knowledge.js.map +1 -1
- package/dist/core/intent-recognition.d.ts.map +1 -1
- package/dist/core/intent-recognition.js +40 -0
- package/dist/core/intent-recognition.js.map +1 -1
- package/dist/core/platform-registry.d.ts +2 -2
- package/dist/core/platform-registry.d.ts.map +1 -1
- package/dist/core/platform-registry.js +25 -2
- package/dist/core/platform-registry.js.map +1 -1
- package/dist/core/prompt-builder.js +1 -1
- package/dist/core/prompt-builder.js.map +1 -1
- package/dist/core/rag-engine.d.ts +25 -0
- package/dist/core/rag-engine.d.ts.map +1 -1
- package/dist/core/rag-engine.js +88 -3
- package/dist/core/rag-engine.js.map +1 -1
- package/dist/core/refresh-scheduler.js +39 -17
- package/dist/core/refresh-scheduler.js.map +1 -1
- package/dist/core/requirement-clarifier.js +1 -1
- package/dist/core/requirement-clarifier.js.map +1 -1
- package/dist/core/skeleton-residue.d.ts +25 -0
- package/dist/core/skeleton-residue.d.ts.map +1 -0
- package/dist/core/skeleton-residue.js +98 -0
- package/dist/core/skeleton-residue.js.map +1 -0
- package/dist/core/task-paths.d.ts +1 -1
- package/dist/core/task-paths.d.ts.map +1 -1
- package/dist/core/task-paths.js +5 -4
- package/dist/core/task-paths.js.map +1 -1
- package/dist/core/unified-config.d.ts +8 -0
- package/dist/core/unified-config.d.ts.map +1 -1
- package/dist/core/unified-config.js +3 -0
- package/dist/core/unified-config.js.map +1 -1
- package/dist/core/unit-context-assembler.d.ts.map +1 -1
- package/dist/core/unit-context-assembler.js +21 -45
- package/dist/core/unit-context-assembler.js.map +1 -1
- package/dist/core/unit-staffing.d.ts +28 -1
- package/dist/core/unit-staffing.d.ts.map +1 -1
- package/dist/core/unit-staffing.js +115 -20
- package/dist/core/unit-staffing.js.map +1 -1
- package/dist/utils/markdown-image.d.ts +12 -0
- package/dist/utils/markdown-image.d.ts.map +1 -0
- package/dist/utils/markdown-image.js +34 -0
- package/dist/utils/markdown-image.js.map +1 -0
- package/package.json +2 -1
- package/templates/html/speccore-ask-guide.html +1 -1
- package/templates/html/speccore-ask-pipeline.html +1 -1
- package/templates/html/speccore-ask-result.html +1 -1
- 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
|
-
// ──
|
|
193
|
-
//
|
|
194
|
-
|
|
195
|
-
let
|
|
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
|
-
|
|
203
|
-
|
|
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
|
-
|
|
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,
|
|
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,
|
|
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 +
|
|
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
|
-
|
|
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
|
-
|
|
366
|
-
|
|
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(` 📖
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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
|
-
// ── 代码关联分析(
|
|
832
|
-
//
|
|
833
|
-
if (
|
|
834
|
-
r += `\n---\n\n## 3.5
|
|
835
|
-
r +=
|
|
836
|
-
|
|
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,
|
|
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
|
-
// ── 代码关联分析(
|
|
873
|
-
if (
|
|
874
|
-
r += `\n##
|
|
875
|
-
r +=
|
|
876
|
-
|
|
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,
|
|
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 (
|
|
1095
|
-
for (const
|
|
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 =
|
|
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 += `### ✅
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
r +=
|
|
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 (
|
|
1178
|
-
r += `\n###
|
|
1179
|
-
|
|
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({
|