speccore 8.3.235 → 8.3.239
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 +5 -5
- package/dist/cli.js.map +1 -1
- package/dist/commands/analyze.d.ts.map +1 -1
- package/dist/commands/analyze.js +327 -162
- package/dist/commands/analyze.js.map +1 -1
- package/dist/commands/execute.js +2 -1
- package/dist/commands/execute.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +35 -101
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/iteration/create.js +2 -2
- package/dist/commands/iteration/split.d.ts.map +1 -1
- package/dist/commands/iteration/split.js +96 -47
- package/dist/commands/iteration/split.js.map +1 -1
- package/dist/core/ask-engine.js +7 -7
- package/dist/core/ask-engine.js.map +1 -1
- package/dist/core/feature-source.js +1 -1
- package/dist/core/requirement-clarifier.d.ts +1 -1
- package/dist/core/requirement-clarifier.d.ts.map +1 -1
- package/dist/core/requirement-clarifier.js +5 -1
- package/dist/core/requirement-clarifier.js.map +1 -1
- package/dist/core/spec-paths.d.ts.map +1 -1
- package/dist/core/spec-paths.js +4 -1
- package/dist/core/spec-paths.js.map +1 -1
- package/dist/core/spec-skeleton.d.ts +6 -0
- package/dist/core/spec-skeleton.d.ts.map +1 -1
- package/dist/core/spec-skeleton.js +117 -5
- package/dist/core/spec-skeleton.js.map +1 -1
- package/package.json +1 -1
package/dist/commands/analyze.js
CHANGED
|
@@ -206,8 +206,17 @@ async function collectUnitClarifyDocs(specDir) {
|
|
|
206
206
|
if (!e.isDirectory() || e.name === 'overview' || e.name === 'requirements')
|
|
207
207
|
continue;
|
|
208
208
|
const clarify = (0, path_1.join)(specDir, e.name, 'overview', 'CLARIFY.md');
|
|
209
|
-
if (await (0, fs_extra_1.pathExists)(clarify))
|
|
210
|
-
|
|
209
|
+
if (!(await (0, fs_extra_1.pathExists)(clarify)))
|
|
210
|
+
continue;
|
|
211
|
+
// v8.3.236+: 骨架 CLARIFY.md(内容未生成)不算已澄清产物——
|
|
212
|
+
// 修复「空骨架被附加进 requirements 导致专业度检测误判、澄清被跳过」
|
|
213
|
+
try {
|
|
214
|
+
const c = await (0, fs_extra_2.readFile)(clarify, 'utf-8');
|
|
215
|
+
if (/<!--\s*SPEC-SKELETON\s*-->/.test(c))
|
|
216
|
+
continue;
|
|
217
|
+
}
|
|
218
|
+
catch { /* 读取失败按存在处理 */ }
|
|
219
|
+
docs.push(clarify);
|
|
211
220
|
}
|
|
212
221
|
}
|
|
213
222
|
catch { }
|
|
@@ -249,11 +258,14 @@ async function applyDocContent(content, iter) {
|
|
|
249
258
|
docs[name] = c;
|
|
250
259
|
}
|
|
251
260
|
let applied = 0;
|
|
261
|
+
// v8.3.237+: 与 --apply 主通道一致——有功能模块的项目才拦截(内容类归 {feature}/overview/,
|
|
262
|
+
// 全局 overview/REQUIREMENT.md 由 split 聚合),无功能模块的老项目放行
|
|
263
|
+
const featureCount = (await (0, spec_paths_1.parseFeatureList)(iterDir)).length;
|
|
252
264
|
for (const [filename, docContent] of Object.entries(docs)) {
|
|
253
265
|
if (typeof docContent !== 'string' || !docContent.trim())
|
|
254
266
|
continue;
|
|
255
267
|
// 迭代级需求文档不在此写(原始需求在 010-requirements)
|
|
256
|
-
if (filename === 'REQUIREMENT.md' || filename === 'overview/REQUIREMENT.md')
|
|
268
|
+
if (featureCount > 0 && (filename === 'REQUIREMENT.md' || filename === 'overview/REQUIREMENT.md'))
|
|
257
269
|
continue;
|
|
258
270
|
const fp = (0, path_1.join)(specDir, filename);
|
|
259
271
|
await (0, fs_extra_1.ensureDir)((0, path_1.dirname)(fp));
|
|
@@ -976,8 +988,15 @@ async function analyzeCommand(options) {
|
|
|
976
988
|
if (await (0, fs_extra_1.pathExists)(reqRoot))
|
|
977
989
|
requirements.push(reqRoot);
|
|
978
990
|
// 收集 features/*/SOURCE.md(v8.3.226+ 语义名,兼容旧 README.md)
|
|
991
|
+
// v8.3.236+: converted/ 非空时跳过切片收集——切片源自 converted 切分,
|
|
992
|
+
// 内容重复会污染澄清清单(会议项目实测 78 份「源文档」大部分是切片元数据)
|
|
979
993
|
const featuresDir = (0, path_1.join)(reqDir, 'features');
|
|
980
|
-
|
|
994
|
+
let hasConvertedDocs = false;
|
|
995
|
+
try {
|
|
996
|
+
hasConvertedDocs = (await (0, fs_extra_2.readdir)(convDir)).some((f) => f.endsWith('.md'));
|
|
997
|
+
}
|
|
998
|
+
catch { }
|
|
999
|
+
if (await (0, fs_extra_1.pathExists)(featuresDir) && !hasConvertedDocs) {
|
|
981
1000
|
try {
|
|
982
1001
|
const featureEntries = await (0, fs_extra_2.readdir)(featuresDir, { withFileTypes: true });
|
|
983
1002
|
for (const fe of featureEntries) {
|
|
@@ -985,11 +1004,6 @@ async function analyzeCommand(options) {
|
|
|
985
1004
|
const readmePath = await (0, feature_source_1.resolveUnitSourcePath)((0, path_1.join)(featuresDir, fe.name));
|
|
986
1005
|
if (await (0, fs_extra_1.pathExists)(readmePath))
|
|
987
1006
|
requirements.push(readmePath);
|
|
988
|
-
// v8.3.228+: 端覆盖矩阵(type/platforms/dependencies/apis)紧随切片注入,
|
|
989
|
-
// 保证功能单元的外链元数据(类型/依赖/API 契约)一定被 AI 读到
|
|
990
|
-
const matrixPath = (0, path_1.join)(featuresDir, fe.name, '_matrix.md');
|
|
991
|
-
if (await (0, fs_extra_1.pathExists)(matrixPath))
|
|
992
|
-
requirements.push(matrixPath);
|
|
993
1007
|
}
|
|
994
1008
|
}
|
|
995
1009
|
}
|
|
@@ -1023,8 +1037,17 @@ async function analyzeCommand(options) {
|
|
|
1023
1037
|
// 修复「用两级澄清产出的项目,黄金目录为空 → 回退原始需求 → 澄清白做」的问题
|
|
1024
1038
|
const clarifyDocs = await collectUnitClarifyDocs(specDir);
|
|
1025
1039
|
const overviewClarify = (0, path_1.join)(specDir, 'overview', 'CLARIFY.md');
|
|
1026
|
-
|
|
1027
|
-
|
|
1040
|
+
// v8.3.236+: 全局 CLARIFY.md 同样过滤骨架(与 collectUnitClarifyDocs 一致)
|
|
1041
|
+
if (await (0, fs_extra_1.pathExists)(overviewClarify)) {
|
|
1042
|
+
try {
|
|
1043
|
+
const oc = await (0, fs_extra_2.readFile)(overviewClarify, 'utf-8');
|
|
1044
|
+
if (!/<!--\s*SPEC-SKELETON\s*-->/.test(oc))
|
|
1045
|
+
clarifyDocs.unshift(overviewClarify);
|
|
1046
|
+
}
|
|
1047
|
+
catch {
|
|
1048
|
+
clarifyDocs.unshift(overviewClarify);
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1028
1051
|
if (clarifyDocs.length > 0) {
|
|
1029
1052
|
requirements.push(...clarifyDocs);
|
|
1030
1053
|
logger_1.logger.info(` 📝 黄金需求目录为空,已附加新路径澄清文档 ${clarifyDocs.length} 个(020-specs/*/overview/CLARIFY.md)`);
|
|
@@ -1193,7 +1216,12 @@ async function analyzeCommand(options) {
|
|
|
1193
1216
|
steps = result.steps;
|
|
1194
1217
|
pipelineKey = iter;
|
|
1195
1218
|
// v8.3.160+: clarify 拆分为 product → interaction → security 三个子步骤
|
|
1196
|
-
|
|
1219
|
+
// v8.3.236+: confirm-check 与 clarify 解耦——auto 模式下已有澄清产物时跳过 clarify
|
|
1220
|
+
// 子步骤但仍执行 confirm-check(确认检查独立于澄清,读 CLARIFY_REPORT 验证产物完整性);
|
|
1221
|
+
// off 模式才完全跳过(用户明确关闭澄清)
|
|
1222
|
+
initStep = skipClarifyStep
|
|
1223
|
+
? (clarifyMode === 'auto' ? 'confirm-check' : 'phase1-prompt')
|
|
1224
|
+
: 'clarify-product';
|
|
1197
1225
|
}
|
|
1198
1226
|
// 检查是否有活跃的 Pipeline(恢复模式)
|
|
1199
1227
|
const hasActive = await pipeline_engine_1.PipelineEngine.hasActivePipeline(_projectRoot, pipelineKey);
|
|
@@ -1457,6 +1485,213 @@ ${singlePrompt}`);
|
|
|
1457
1485
|
return;
|
|
1458
1486
|
}
|
|
1459
1487
|
}
|
|
1488
|
+
// v8.3.237+: Pipeline 推进逻辑抽取为共享函数——JSON / [DOC:] 多文档通道写入后
|
|
1489
|
+
// 提前 return,此前会跳过尾部的 Pipeline 推进(bug: 只有单文件模式能推进),
|
|
1490
|
+
// 现将「RAG 刷新 + Pipeline 推进」抽为共享函数,三个写入通道成功后都调用。
|
|
1491
|
+
// 返回 true 表示存在活跃 Pipeline 并已处理(调用点应直接 return,不再输出后续提示)。
|
|
1492
|
+
const advancePipelineAfterApply = async () => {
|
|
1493
|
+
if (isTaskLevel)
|
|
1494
|
+
return false;
|
|
1495
|
+
// v8.3.238+: 步骤完成检查——计算当前步骤尚未填充的骨架清单(相对 020-specs/ 的路径)。
|
|
1496
|
+
// 单文档模式一次 --apply 通常只写 1~2 个文档,但一个步骤(如 phase1-prompt)有多个
|
|
1497
|
+
// 骨架待填充;此前写 1 个就推进,剩余骨架会错位到下一步骤。
|
|
1498
|
+
// 返回空数组 = 步骤已完成,可推进;非空 = 仍有骨架未填充,不推进。
|
|
1499
|
+
const getPendingStepSkeletons = async (stepId) => {
|
|
1500
|
+
if (isGlobalScope)
|
|
1501
|
+
return []; // 全局层 Pipeline 无骨架清单,保持原行为
|
|
1502
|
+
const iterDirForCheck = await (0, context_1.getIterationDir)(options.iteration);
|
|
1503
|
+
if (!iterDirForCheck)
|
|
1504
|
+
return [];
|
|
1505
|
+
const specDirForCheck = (0, path_1.join)(iterDirForCheck, '020-specs');
|
|
1506
|
+
let checkDocs = [];
|
|
1507
|
+
let baseRel = '';
|
|
1508
|
+
if (stepId === 'phase1-prompt') {
|
|
1509
|
+
// 有功能模块的项目只生成全局索引;老项目生成全部全局文档。
|
|
1510
|
+
// CLARIFY.md 由 clarify 阶段产出,不在本步骤检查范围(skip clarify 时不会卡住)。
|
|
1511
|
+
const featureCount = (await (0, spec_paths_1.parseFeatureList)(iterDirForCheck)).length;
|
|
1512
|
+
checkDocs = (featureCount > 0 ? spec_skeleton_1.PHASE1_INDEX_DOC_NAMES : spec_skeleton_1.PHASE1_DOC_NAMES)
|
|
1513
|
+
.filter((n) => n !== 'CLARIFY.md');
|
|
1514
|
+
baseRel = spec_paths_1.GLOBAL_SPECS_DIR;
|
|
1515
|
+
}
|
|
1516
|
+
else {
|
|
1517
|
+
const m = stepId.match(/^feature-(.+)-prompt$/);
|
|
1518
|
+
if (!m)
|
|
1519
|
+
return []; // 其他步骤(clarify / confirm / contract / platform)不做骨架完成检查
|
|
1520
|
+
baseRel = (0, path_1.join)(m[1], 'overview');
|
|
1521
|
+
checkDocs = spec_skeleton_1.FEATURE_OVERVIEW_DOC_NAMES.filter((n) => n !== 'CLARIFY.md');
|
|
1522
|
+
}
|
|
1523
|
+
const pending = [];
|
|
1524
|
+
for (const doc of checkDocs) {
|
|
1525
|
+
const fp = (0, path_1.join)(specDirForCheck, baseRel, doc);
|
|
1526
|
+
if (!(await (0, fs_extra_1.pathExists)(fp))) {
|
|
1527
|
+
pending.push((0, path_1.join)(baseRel, doc));
|
|
1528
|
+
continue;
|
|
1529
|
+
}
|
|
1530
|
+
try {
|
|
1531
|
+
const content = await (0, fs_extra_2.readFile)(fp, 'utf-8');
|
|
1532
|
+
if (!content.trim() || content.includes(spec_skeleton_1.SKELETON_MARKER))
|
|
1533
|
+
pending.push((0, path_1.join)(baseRel, doc));
|
|
1534
|
+
}
|
|
1535
|
+
catch {
|
|
1536
|
+
pending.push((0, path_1.join)(baseRel, doc));
|
|
1537
|
+
}
|
|
1538
|
+
}
|
|
1539
|
+
return pending;
|
|
1540
|
+
};
|
|
1541
|
+
try {
|
|
1542
|
+
// 自动刷新知识图谱和 RAG 索引(v8.3.67+)
|
|
1543
|
+
if (options.iteration) {
|
|
1544
|
+
try {
|
|
1545
|
+
const { refreshKnowledgeGraph } = await Promise.resolve().then(() => __importStar(require('../core/knowledge-graph')));
|
|
1546
|
+
await refreshKnowledgeGraph(_projectRoot, options.iteration);
|
|
1547
|
+
logger_1.logger.info('🧠 知识图谱已刷新');
|
|
1548
|
+
}
|
|
1549
|
+
catch { }
|
|
1550
|
+
try {
|
|
1551
|
+
const { indexDirectoryDocuments } = await Promise.resolve().then(() => __importStar(require('../core/rag-engine')));
|
|
1552
|
+
const iterDir = `Iteration-${options.iteration}`;
|
|
1553
|
+
const specsDir = (0, path_1.join)(iterDir, '020-specs');
|
|
1554
|
+
const reqDir = (0, path_1.join)(iterDir, '010-requirements');
|
|
1555
|
+
const dirs = [];
|
|
1556
|
+
if (await (0, fs_extra_1.pathExists)(specsDir))
|
|
1557
|
+
dirs.push(specsDir);
|
|
1558
|
+
if (await (0, fs_extra_1.pathExists)(reqDir))
|
|
1559
|
+
dirs.push(reqDir);
|
|
1560
|
+
if (dirs.length > 0) {
|
|
1561
|
+
await indexDirectoryDocuments(_projectRoot, dirs, `${options.iteration}_iteration_all`, `rag-index-${options.iteration}.json`);
|
|
1562
|
+
logger_1.logger.info('🔍 迭代 RAG 索引已刷新');
|
|
1563
|
+
}
|
|
1564
|
+
}
|
|
1565
|
+
catch { }
|
|
1566
|
+
}
|
|
1567
|
+
// ── v6.68.0+: Pipeline 自动推进 ──
|
|
1568
|
+
const pipelineKey = isGlobalScope ? 'GLOBAL' : options.iteration;
|
|
1569
|
+
if (!pipelineKey)
|
|
1570
|
+
return false;
|
|
1571
|
+
const hasPipeline = await pipeline_engine_1.PipelineEngine.hasActivePipeline(_projectRoot, pipelineKey);
|
|
1572
|
+
if (hasPipeline) {
|
|
1573
|
+
const { createAnalyzePipeline, createGlobalAnalyzePipeline } = await Promise.resolve().then(() => __importStar(require('../core/pipeline-engine')));
|
|
1574
|
+
// v6.69.0+: 推进时保持与初始化时相同的过滤和排序条件
|
|
1575
|
+
let engine;
|
|
1576
|
+
if (isGlobalScope) {
|
|
1577
|
+
engine = (await createGlobalAnalyzePipeline()).engine;
|
|
1578
|
+
}
|
|
1579
|
+
else {
|
|
1580
|
+
const { detectAffectedPlatforms, detectPlatformPriorityOrder } = await Promise.resolve().then(() => __importStar(require('../core/change-detection')));
|
|
1581
|
+
const affectedPlatforms = await detectAffectedPlatforms(_projectRoot);
|
|
1582
|
+
const platformOrder = await detectPlatformPriorityOrder(options.iteration);
|
|
1583
|
+
// v8.3.225+: 与初始化时保持一致的功能模块分批与澄清开关(否则步骤链不一致会跳错步骤)
|
|
1584
|
+
let features;
|
|
1585
|
+
try {
|
|
1586
|
+
const iterDirForFeatures = await (0, context_1.getIterationDir)(options.iteration);
|
|
1587
|
+
if (iterDirForFeatures) {
|
|
1588
|
+
const { parseFeatureList } = await Promise.resolve().then(() => __importStar(require('../core/spec-paths')));
|
|
1589
|
+
const featureList = await parseFeatureList(iterDirForFeatures);
|
|
1590
|
+
if (featureList.length > 2)
|
|
1591
|
+
features = featureList;
|
|
1592
|
+
}
|
|
1593
|
+
}
|
|
1594
|
+
catch { /* 忽略功能模块检测失败 */ }
|
|
1595
|
+
const skipClarifyStep = clarifyMode === 'off' || (clarifyMode === 'auto' && !needsClarify);
|
|
1596
|
+
engine = (await createAnalyzePipeline(options.iteration, _projectRoot, {
|
|
1597
|
+
affectedPlatforms: affectedPlatforms.length > 0 ? affectedPlatforms : undefined,
|
|
1598
|
+
platformOrder: platformOrder.length > 0 ? platformOrder : undefined,
|
|
1599
|
+
skipClarify: skipClarifyStep,
|
|
1600
|
+
features,
|
|
1601
|
+
})).engine;
|
|
1602
|
+
}
|
|
1603
|
+
// v8.3.238+: 步骤完成检查(推进前)——本步骤的骨架未全部填充时不推进,
|
|
1604
|
+
// 输出剩余骨架清单 + 当前步骤 Prompt(同一会话内继续填充,无需重跑命令)。
|
|
1605
|
+
const stateBefore = await engine.getState();
|
|
1606
|
+
if (stateBefore?.currentStep) {
|
|
1607
|
+
const pendingSkeletons = await getPendingStepSkeletons(stateBefore.currentStep);
|
|
1608
|
+
if (pendingSkeletons.length > 0) {
|
|
1609
|
+
logger_1.logger.warn(`⏸️ 步骤 "${stateBefore.currentStep}" 尚未完成: ${pendingSkeletons.length} 个骨架未填充,Pipeline 不推进`);
|
|
1610
|
+
logger_1.logger.info(` 剩余待填充: ${pendingSkeletons.slice(0, 8).join(', ')}${pendingSkeletons.length > 8 ? ' …' : ''}`);
|
|
1611
|
+
try {
|
|
1612
|
+
const stepPrompt = await buildPipelineStepPrompt({
|
|
1613
|
+
currentStep: stateBefore.currentStep,
|
|
1614
|
+
isGlobalScope,
|
|
1615
|
+
iter: options.iteration || 'GLOBAL',
|
|
1616
|
+
stepIds: stateBefore.steps || [stateBefore.currentStep],
|
|
1617
|
+
options,
|
|
1618
|
+
});
|
|
1619
|
+
const scopeFlag = isGlobalScope ? '--scope global' : `-I ${options.iteration}`;
|
|
1620
|
+
const nextCmd = `speccore analyze --prompt --pipeline ${scopeFlag}`;
|
|
1621
|
+
const output = [
|
|
1622
|
+
`[SPECCORE_NEXT_STEP]`,
|
|
1623
|
+
`当前步骤 "${stateBefore.currentStep}" 尚未完成,请继续填充剩余骨架后再次 --apply。`,
|
|
1624
|
+
`如需重新获取本步骤 Prompt: ${nextCmd}`,
|
|
1625
|
+
``,
|
|
1626
|
+
`--- 当前步骤 Prompt ---`,
|
|
1627
|
+
stepPrompt.prompt,
|
|
1628
|
+
].join('\n');
|
|
1629
|
+
process.stdout.write(output);
|
|
1630
|
+
}
|
|
1631
|
+
catch (e) {
|
|
1632
|
+
logger_1.logger.debug(`步骤 Prompt 生成失败(非关键): ${e.message}`);
|
|
1633
|
+
}
|
|
1634
|
+
process.exitCode = 10;
|
|
1635
|
+
return true;
|
|
1636
|
+
}
|
|
1637
|
+
}
|
|
1638
|
+
// v8.3.160+: 默认步骤隔离模式
|
|
1639
|
+
const stepResult = await advanceToWorkStep(engine);
|
|
1640
|
+
const state = await engine.getState();
|
|
1641
|
+
if (state?.currentStep === 'done') {
|
|
1642
|
+
logger_1.logger.success('🎉 Pipeline 完成!');
|
|
1643
|
+
// v6.69.1: 记录分析快照,支持下次增量分析
|
|
1644
|
+
const snapshotScope = isGlobalScope ? 'global' : (options.iteration ? `Iteration-${options.iteration}` : 'unknown');
|
|
1645
|
+
await (0, change_detection_1.recordAnalysisSnapshot)(snapshotScope);
|
|
1646
|
+
await engine.reset();
|
|
1647
|
+
}
|
|
1648
|
+
else if (state?.currentStep) {
|
|
1649
|
+
logger_1.logger.info('');
|
|
1650
|
+
logger_1.logger.info(`🔄 Pipeline 推进: ${state.currentStep}`);
|
|
1651
|
+
logger_1.logger.info('');
|
|
1652
|
+
// v8.3.225+: 生成下一步 prompt(复用统一函数,与 --prompt --pipeline 路径保持一致)
|
|
1653
|
+
const stepPrompt = await buildPipelineStepPrompt({
|
|
1654
|
+
currentStep: state.currentStep,
|
|
1655
|
+
isGlobalScope,
|
|
1656
|
+
iter: options.iteration || 'GLOBAL',
|
|
1657
|
+
stepIds: state.steps || [state.currentStep],
|
|
1658
|
+
options,
|
|
1659
|
+
});
|
|
1660
|
+
// v8.3.225+: 方式 Y 输出标记 — 宿主会话内继续执行即可,不再强制新对话
|
|
1661
|
+
const scopeFlag = isGlobalScope ? '--scope global' : `-I ${options.iteration}`;
|
|
1662
|
+
const nextCmd = `speccore analyze --prompt --pipeline ${scopeFlag}`;
|
|
1663
|
+
const output = [
|
|
1664
|
+
`[SPECCORE_STEP_DONE]`,
|
|
1665
|
+
`步骤 "${state.currentStep}" 已完成。`,
|
|
1666
|
+
``,
|
|
1667
|
+
`[SPECCORE_NEXT_STEP]`,
|
|
1668
|
+
`下一步: ${state.currentStep}`,
|
|
1669
|
+
`请在同一会话继续执行: ${nextCmd}`,
|
|
1670
|
+
``,
|
|
1671
|
+
`[SPECCORE_CONTEXT_SNAPSHOT]`,
|
|
1672
|
+
JSON.stringify(stepResult.contextSnapshot || {}),
|
|
1673
|
+
``,
|
|
1674
|
+
// v8.3.160+: 子 Agent 激活标记
|
|
1675
|
+
...(stepResult.subagent ? [
|
|
1676
|
+
`[SPECCORE_SESSION_AGENT: ${stepResult.subagent}]`,
|
|
1677
|
+
`[SPECCORE_CONTEXT_BUDGET: ${stepResult.contextBudget || 12000}]`,
|
|
1678
|
+
`[SPECCORE_CONTEXT_TYPE: ${stepResult.contextType || 'full'}]`,
|
|
1679
|
+
``,
|
|
1680
|
+
] : []),
|
|
1681
|
+
`--- 下一步 Prompt ---`,
|
|
1682
|
+
stepPrompt.prompt,
|
|
1683
|
+
].join('\n');
|
|
1684
|
+
process.stdout.write(output);
|
|
1685
|
+
process.exitCode = 10;
|
|
1686
|
+
}
|
|
1687
|
+
}
|
|
1688
|
+
return hasPipeline;
|
|
1689
|
+
}
|
|
1690
|
+
catch (e) {
|
|
1691
|
+
logger_1.logger.debug(`Pipeline 推进失败(非关键): ${e.message}`);
|
|
1692
|
+
return false;
|
|
1693
|
+
}
|
|
1694
|
+
};
|
|
1460
1695
|
if (options.skipClarify && !process.stdout.isTTY) {
|
|
1461
1696
|
logger_1.logger.warn('⚠️ AI 上下文检测到 --skip-clarify,此行为不推荐');
|
|
1462
1697
|
logger_1.logger.info(' 需求澄清是质量门禁,应由人类用户确认后使用');
|
|
@@ -1547,12 +1782,19 @@ ${singlePrompt}`);
|
|
|
1547
1782
|
return;
|
|
1548
1783
|
}
|
|
1549
1784
|
if (!isGlobalScope) {
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1785
|
+
// v8.3.237+: 拦截条件化为「有功能模块的项目」——内容类归 {feature}/overview/,
|
|
1786
|
+
// 全局 overview/REQUIREMENT.md 由 split 阶段从功能模块聚合生成,AI 不应直接写。
|
|
1787
|
+
// 无功能模块的老项目放行(内容类唯一容器就是 overview/)。
|
|
1788
|
+
const featureCount = iterDir ? (await (0, spec_paths_1.parseFeatureList)(iterDir)).length : 0;
|
|
1789
|
+
if (featureCount > 0) {
|
|
1790
|
+
for (const filename of Object.keys(docs)) {
|
|
1791
|
+
if (filename === 'overview/REQUIREMENT.md' || filename === 'REQUIREMENT.md') {
|
|
1792
|
+
logger_1.logger.error(`❌ 写入路径拦截: analyze --apply 不允许直接写入迭代级的 ${filename}`);
|
|
1793
|
+
logger_1.logger.info(' 原始需求文档应在 010-requirements/ 中维护');
|
|
1794
|
+
logger_1.logger.info(' 本迭代有功能模块,模块需求写 {功能模块}/overview/REQUIREMENT.md,');
|
|
1795
|
+
logger_1.logger.info(' 全局 overview/REQUIREMENT.md 由 split 阶段自动聚合生成');
|
|
1796
|
+
return;
|
|
1797
|
+
}
|
|
1556
1798
|
}
|
|
1557
1799
|
}
|
|
1558
1800
|
}
|
|
@@ -2006,6 +2248,7 @@ ${singlePrompt}`);
|
|
|
2006
2248
|
}
|
|
2007
2249
|
}
|
|
2008
2250
|
printBackupSummary();
|
|
2251
|
+
await advancePipelineAfterApply();
|
|
2009
2252
|
return;
|
|
2010
2253
|
}
|
|
2011
2254
|
catch {
|
|
@@ -2029,17 +2272,23 @@ ${singlePrompt}`);
|
|
|
2029
2272
|
return;
|
|
2030
2273
|
}
|
|
2031
2274
|
if (!isGlobalScope) {
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2275
|
+
// v8.3.237+: 与 JSON 通道一致——有功能模块的项目拦截,无功能模块的老项目放行
|
|
2276
|
+
const featureCount = iterDir ? (await (0, spec_paths_1.parseFeatureList)(iterDir)).length : 0;
|
|
2277
|
+
if (featureCount > 0) {
|
|
2278
|
+
for (const docName of docBlocks.keys()) {
|
|
2279
|
+
if (docName === 'overview/REQUIREMENT.md' || docName === 'REQUIREMENT.md') {
|
|
2280
|
+
logger_1.logger.error(`❌ 写入路径拦截: analyze --apply 不允许直接写入迭代级的 ${docName}`);
|
|
2281
|
+
logger_1.logger.info(' 原始需求文档应在 010-requirements/ 中维护');
|
|
2282
|
+
logger_1.logger.info(' 本迭代有功能模块,模块需求写 {功能模块}/overview/REQUIREMENT.md,');
|
|
2283
|
+
logger_1.logger.info(' 全局 overview/REQUIREMENT.md 由 split 阶段自动聚合生成');
|
|
2284
|
+
return;
|
|
2285
|
+
}
|
|
2039
2286
|
}
|
|
2040
2287
|
}
|
|
2041
2288
|
}
|
|
2042
|
-
|
|
2289
|
+
// v8.3.238+: size===1 也走多文档写入——单个 [DOC:xxx] 文档此前被错误落入
|
|
2290
|
+
// 单文件模式,把含 [DOC:] 标记的原文整体写入 ANALYSIS.md(目标文档反而没写)。
|
|
2291
|
+
if (docBlocks.size > 0) {
|
|
2043
2292
|
// 将 [DOC:xxx] 格式转换为 JSON 格式复用现有写入逻辑
|
|
2044
2293
|
const docs = {};
|
|
2045
2294
|
for (const [docName, content] of docBlocks) {
|
|
@@ -2268,14 +2517,19 @@ ${singlePrompt}`);
|
|
|
2268
2517
|
}
|
|
2269
2518
|
}
|
|
2270
2519
|
printBackupSummary();
|
|
2520
|
+
await advancePipelineAfterApply();
|
|
2271
2521
|
return;
|
|
2272
2522
|
}
|
|
2273
2523
|
catch (e) {
|
|
2274
2524
|
logger_1.logger.warn('⚠️ [DOC:xxx] 解析后写入失败,回退到单文件模式');
|
|
2275
2525
|
}
|
|
2276
2526
|
}
|
|
2527
|
+
// v8.3.236+: [CLARIFY:] 标记块已单独写入(1591 行起),apply 原文(含标记)不得再当
|
|
2528
|
+
// ANALYSIS.md 写入——修复「澄清 payload 污染 020-specs/overview/ANALYSIS.md(带时间戳备份)」bug。
|
|
2529
|
+
// 注意:不能提前 return,否则会跳过后续的 Pipeline 推进逻辑(--apply 靠它推进 confirm-check)
|
|
2530
|
+
const hasClarifyMarkers = /\[CLARIFY:[^\]]+\]/.test(options.apply);
|
|
2277
2531
|
// 单文件模式
|
|
2278
|
-
if (isTaskLevel && taskDir) {
|
|
2532
|
+
if (!hasClarifyMarkers && isTaskLevel && taskDir) {
|
|
2279
2533
|
// 任务级:写 Task/00-specs/(v6.44.0+)
|
|
2280
2534
|
const targetSubDir = '00-specs';
|
|
2281
2535
|
const taskSpecDir = (0, path_1.join)(taskDir, targetSubDir);
|
|
@@ -2295,7 +2549,7 @@ ${singlePrompt}`);
|
|
|
2295
2549
|
logger_1.logger.info(` ⏭️ 用户取消覆盖`);
|
|
2296
2550
|
}
|
|
2297
2551
|
}
|
|
2298
|
-
else if (isGlobalScope) {
|
|
2552
|
+
else if (!hasClarifyMarkers && isGlobalScope) {
|
|
2299
2553
|
// 全局级:写 .speccore/GLOBAL/ANALYSIS.md
|
|
2300
2554
|
const globalDir = (0, path_1.join)(_projectRoot, '.speccore', 'GLOBAL');
|
|
2301
2555
|
await (0, fs_extra_1.ensureDir)(globalDir);
|
|
@@ -2313,7 +2567,7 @@ ${singlePrompt}`);
|
|
|
2313
2567
|
logger_1.logger.info(` ⏭️ 用户取消覆盖`);
|
|
2314
2568
|
}
|
|
2315
2569
|
}
|
|
2316
|
-
else {
|
|
2570
|
+
else if (!hasClarifyMarkers) {
|
|
2317
2571
|
// 迭代级:写 020-specs/overview/(迭代综合文档,v6.78.0+)
|
|
2318
2572
|
const specDir = (0, path_1.join)(iterDir, '020-specs');
|
|
2319
2573
|
const globalDir = (0, path_1.join)(specDir, spec_paths_1.GLOBAL_SPECS_DIR);
|
|
@@ -2353,122 +2607,11 @@ ${singlePrompt}`);
|
|
|
2353
2607
|
}
|
|
2354
2608
|
}
|
|
2355
2609
|
printBackupSummary();
|
|
2356
|
-
//
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
logger_1.logger.info('🧠 知识图谱已刷新');
|
|
2362
|
-
}
|
|
2363
|
-
catch { }
|
|
2364
|
-
try {
|
|
2365
|
-
const { indexDirectoryDocuments } = await Promise.resolve().then(() => __importStar(require('../core/rag-engine')));
|
|
2366
|
-
const iterDir = `Iteration-${options.iteration}`;
|
|
2367
|
-
const specsDir = (0, path_1.join)(iterDir, '020-specs');
|
|
2368
|
-
const reqDir = (0, path_1.join)(iterDir, '010-requirements');
|
|
2369
|
-
const dirs = [];
|
|
2370
|
-
if (await (0, fs_extra_1.pathExists)(specsDir))
|
|
2371
|
-
dirs.push(specsDir);
|
|
2372
|
-
if (await (0, fs_extra_1.pathExists)(reqDir))
|
|
2373
|
-
dirs.push(reqDir);
|
|
2374
|
-
if (dirs.length > 0) {
|
|
2375
|
-
await indexDirectoryDocuments(_projectRoot, dirs, `${options.iteration}_iteration_all`, `rag-index-${options.iteration}.json`);
|
|
2376
|
-
logger_1.logger.info('🔍 迭代 RAG 索引已刷新');
|
|
2377
|
-
}
|
|
2378
|
-
}
|
|
2379
|
-
catch { }
|
|
2380
|
-
}
|
|
2381
|
-
// ── v6.68.0+: Pipeline 自动推进 ──
|
|
2382
|
-
// v6.69.0+: 支持全局 Pipeline 推进(增强策略二)
|
|
2383
|
-
if (!isTaskLevel) {
|
|
2384
|
-
const isGlobalScope = options.scope === 'global';
|
|
2385
|
-
const pipelineKey = isGlobalScope ? 'GLOBAL' : options.iteration;
|
|
2386
|
-
if (!pipelineKey)
|
|
2387
|
-
return;
|
|
2388
|
-
const hasPipeline = await pipeline_engine_1.PipelineEngine.hasActivePipeline(_projectRoot, pipelineKey);
|
|
2389
|
-
if (hasPipeline) {
|
|
2390
|
-
const { createAnalyzePipeline, createGlobalAnalyzePipeline } = await Promise.resolve().then(() => __importStar(require('../core/pipeline-engine')));
|
|
2391
|
-
// v6.69.0+: 推进时保持与初始化时相同的过滤和排序条件
|
|
2392
|
-
let engine;
|
|
2393
|
-
if (isGlobalScope) {
|
|
2394
|
-
engine = (await createGlobalAnalyzePipeline()).engine;
|
|
2395
|
-
}
|
|
2396
|
-
else {
|
|
2397
|
-
const { detectAffectedPlatforms, detectPlatformPriorityOrder } = await Promise.resolve().then(() => __importStar(require('../core/change-detection')));
|
|
2398
|
-
const affectedPlatforms = await detectAffectedPlatforms(_projectRoot);
|
|
2399
|
-
const platformOrder = await detectPlatformPriorityOrder(options.iteration);
|
|
2400
|
-
// v8.3.225+: 与初始化时保持一致的功能模块分批与澄清开关(否则步骤链不一致会跳错步骤)
|
|
2401
|
-
let features;
|
|
2402
|
-
try {
|
|
2403
|
-
const iterDirForFeatures = await (0, context_1.getIterationDir)(options.iteration);
|
|
2404
|
-
if (iterDirForFeatures) {
|
|
2405
|
-
const { parseFeatureList } = await Promise.resolve().then(() => __importStar(require('../core/spec-paths')));
|
|
2406
|
-
const featureList = await parseFeatureList(iterDirForFeatures);
|
|
2407
|
-
if (featureList.length > 2)
|
|
2408
|
-
features = featureList;
|
|
2409
|
-
}
|
|
2410
|
-
}
|
|
2411
|
-
catch { /* 忽略功能模块检测失败 */ }
|
|
2412
|
-
const skipClarifyStep = clarifyMode === 'off' || (clarifyMode === 'auto' && !needsClarify);
|
|
2413
|
-
engine = (await createAnalyzePipeline(options.iteration, _projectRoot, {
|
|
2414
|
-
affectedPlatforms: affectedPlatforms.length > 0 ? affectedPlatforms : undefined,
|
|
2415
|
-
platformOrder: platformOrder.length > 0 ? platformOrder : undefined,
|
|
2416
|
-
skipClarify: skipClarifyStep,
|
|
2417
|
-
features,
|
|
2418
|
-
})).engine;
|
|
2419
|
-
}
|
|
2420
|
-
// v8.3.160+: 默认步骤隔离模式
|
|
2421
|
-
const stepResult = await advanceToWorkStep(engine);
|
|
2422
|
-
const state = await engine.getState();
|
|
2423
|
-
if (state?.currentStep === 'done') {
|
|
2424
|
-
logger_1.logger.success('🎉 Pipeline 完成!');
|
|
2425
|
-
// v6.69.1: 记录分析快照,支持下次增量分析
|
|
2426
|
-
const snapshotScope = isGlobalScope ? 'global' : (options.iteration ? `Iteration-${options.iteration}` : 'unknown');
|
|
2427
|
-
await (0, change_detection_1.recordAnalysisSnapshot)(snapshotScope);
|
|
2428
|
-
await engine.reset();
|
|
2429
|
-
}
|
|
2430
|
-
else if (state?.currentStep) {
|
|
2431
|
-
logger_1.logger.info('');
|
|
2432
|
-
logger_1.logger.info(`🔄 Pipeline 推进: ${state.currentStep}`);
|
|
2433
|
-
logger_1.logger.info('');
|
|
2434
|
-
// v8.3.225+: 生成下一步 prompt(复用统一函数,与 --prompt --pipeline 路径保持一致)
|
|
2435
|
-
const stepPrompt = await buildPipelineStepPrompt({
|
|
2436
|
-
currentStep: state.currentStep,
|
|
2437
|
-
isGlobalScope,
|
|
2438
|
-
iter: options.iteration || 'GLOBAL',
|
|
2439
|
-
stepIds: state.steps || [state.currentStep],
|
|
2440
|
-
options,
|
|
2441
|
-
});
|
|
2442
|
-
// v8.3.225+: 方式 Y 输出标记 — 宿主会话内继续执行即可,不再强制新对话
|
|
2443
|
-
const scopeFlag = isGlobalScope ? '--scope global' : `-I ${options.iteration}`;
|
|
2444
|
-
const nextCmd = `speccore analyze --prompt --pipeline ${scopeFlag}`;
|
|
2445
|
-
const output = [
|
|
2446
|
-
`[SPECCORE_STEP_DONE]`,
|
|
2447
|
-
`步骤 "${state.currentStep}" 已完成。`,
|
|
2448
|
-
``,
|
|
2449
|
-
`[SPECCORE_NEXT_STEP]`,
|
|
2450
|
-
`下一步: ${state.currentStep}`,
|
|
2451
|
-
`请在同一会话继续执行: ${nextCmd}`,
|
|
2452
|
-
``,
|
|
2453
|
-
`[SPECCORE_CONTEXT_SNAPSHOT]`,
|
|
2454
|
-
JSON.stringify(stepResult.contextSnapshot || {}),
|
|
2455
|
-
``,
|
|
2456
|
-
// v8.3.160+: 子 Agent 激活标记
|
|
2457
|
-
...(stepResult.subagent ? [
|
|
2458
|
-
`[SPECCORE_SESSION_AGENT: ${stepResult.subagent}]`,
|
|
2459
|
-
`[SPECCORE_CONTEXT_BUDGET: ${stepResult.contextBudget || 12000}]`,
|
|
2460
|
-
`[SPECCORE_CONTEXT_TYPE: ${stepResult.contextType || 'full'}]`,
|
|
2461
|
-
``,
|
|
2462
|
-
] : []),
|
|
2463
|
-
`--- 下一步 Prompt ---`,
|
|
2464
|
-
stepPrompt.prompt,
|
|
2465
|
-
].join('\n');
|
|
2466
|
-
process.stdout.write(output);
|
|
2467
|
-
process.exitCode = 10;
|
|
2468
|
-
}
|
|
2469
|
-
return;
|
|
2470
|
-
}
|
|
2471
|
-
}
|
|
2610
|
+
// v8.3.237+: RAG 刷新 + Pipeline 推进已抽取为 advancePipelineAfterApply(见 apply 分支开头),
|
|
2611
|
+
// 三个写入通道(JSON / [DOC:] / 单文件模式)统一调用,避免多文档通道提前 return 跳过推进。
|
|
2612
|
+
const pipelineHandled = await advancePipelineAfterApply();
|
|
2613
|
+
if (pipelineHandled)
|
|
2614
|
+
return;
|
|
2472
2615
|
// ── v6.64.0+: Phase 1 完成后自动触发 Phase 2(仅多端项目,非 Pipeline 模式)──
|
|
2473
2616
|
if (!options.phase && !isTaskLevel && options.iteration) {
|
|
2474
2617
|
// 检查是否有多个端
|
|
@@ -4806,18 +4949,14 @@ sequenceDiagram
|
|
|
4806
4949
|
`],
|
|
4807
4950
|
];
|
|
4808
4951
|
// 任务类型 × 文档矩阵: 每种类型生成哪些文档
|
|
4809
|
-
//
|
|
4952
|
+
// v8.3.239+: 类型收敛为 4 种(与 task-paths.ts TASK_TYPES 对齐)——
|
|
4953
|
+
// review/test/docs/deploy 是动作不是类型(feature 任务自身包含这些阶段),
|
|
4954
|
+
// security/performance 是 feature/refactor 的关注点(写入 TECH.md 安全/性能章节)
|
|
4810
4955
|
const DOC_MATRIX = {
|
|
4811
4956
|
feature: ['REQUIREMENT.md', 'ANALYSIS.md', 'TECH.md', 'TEST.md', 'REVIEW.md', 'RISK.md', 'DEPS.md', 'MONITOR.md', 'UI_SPEC.md', 'FUNCTION_MAP.md', 'INTERACTION_MAP.md', 'DEV_GUIDE.md'],
|
|
4812
4957
|
refactor: ['ANALYSIS.md', 'TECH.md', 'TEST.md', 'REVIEW.md', 'RISK.md', 'DEV_GUIDE.md'],
|
|
4813
4958
|
bugfix: ['ANALYSIS.md', 'TECH.md', 'TEST.md', 'DEV_GUIDE.md'],
|
|
4814
4959
|
research: ['ANALYSIS.md', 'DEV_GUIDE.md'],
|
|
4815
|
-
review: ['REVIEW.md', 'RISK.md', 'DEV_GUIDE.md'],
|
|
4816
|
-
test: ['TEST.md', 'RISK.md', 'DEV_GUIDE.md'],
|
|
4817
|
-
docs: ['ANALYSIS.md', 'DEV_GUIDE.md'],
|
|
4818
|
-
deploy: ['ANALYSIS.md', 'TECH.md', 'RISK.md', 'DEPS.md', 'MONITOR.md', 'DEV_GUIDE.md'],
|
|
4819
|
-
security: ['ANALYSIS.md', 'TEST.md', 'REVIEW.md', 'RISK.md', 'DEV_GUIDE.md'],
|
|
4820
|
-
performance: ['ANALYSIS.md', 'TECH.md', 'TEST.md', 'MONITOR.md', 'DEV_GUIDE.md'],
|
|
4821
4960
|
};
|
|
4822
4961
|
const includeDocs = isTask ? (DOC_MATRIX[taskType] || DOC_MATRIX['feature']) : DOC_MATRIX['feature'];
|
|
4823
4962
|
// ── v6.61.0+: 恢复 Phase 1/Phase 2 分步逻辑,但 CLI 自动触发 Phase 2 ──
|
|
@@ -4851,7 +4990,10 @@ sequenceDiagram
|
|
|
4851
4990
|
if (ctx.phase === '1') {
|
|
4852
4991
|
if (ctx.feature) {
|
|
4853
4992
|
// v8.3.167+: 功能模块级分析,只生成该模块的 overview 文档(不含全局索引)
|
|
4854
|
-
|
|
4993
|
+
// v8.3.238+: 文档集与骨架预建清单(FEATURE_OVERVIEW_DOC_NAMES)对齐——旧名单含
|
|
4994
|
+
// RISK/DEPS/REVIEW/MONITOR/DEV_GUIDE(任务级文档残留,骨架未预建),会导致 prompt
|
|
4995
|
+
// 要求生成骨架外的文档,且与步骤完成检查名单错位;CLARIFY.md 由澄清阶段产出,不在本步骤生成。
|
|
4996
|
+
taskDocs = taskDocs.filter(([n]) => spec_skeleton_1.FEATURE_OVERVIEW_DOC_NAMES.includes(n) && n !== 'CLARIFY.md');
|
|
4855
4997
|
}
|
|
4856
4998
|
else {
|
|
4857
4999
|
taskDocs = taskDocs.filter(([n]) => GLOBAL_DOCS.includes(n));
|
|
@@ -4870,7 +5012,13 @@ sequenceDiagram
|
|
|
4870
5012
|
if (iterDir) {
|
|
4871
5013
|
const specDir = (0, path_1.join)(iterDir, '020-specs');
|
|
4872
5014
|
const platforms = await (0, spec_paths_1.parsePlatformList)();
|
|
4873
|
-
|
|
5015
|
+
// v8.3.237+: 有功能模块的项目用功能模块 manifest(内容类骨架在 {feature}/overview/,
|
|
5016
|
+
// 全局 overview/ 只含索引类),避免旧 manifest 把 overview/REQUIREMENT.md 当
|
|
5017
|
+
// 「下一个待填充」但 --apply 又按路由表拦截 → 单文档模式死锁。
|
|
5018
|
+
const features = await (0, spec_paths_1.parseFeatureList)(iterDir);
|
|
5019
|
+
const manifest = features.length > 0
|
|
5020
|
+
? (0, spec_skeleton_1.computeFeatureBasedAnalyzeManifest)(features, platforms, undefined, ctx.iteration)
|
|
5021
|
+
: (0, spec_skeleton_1.computeAnalyzeManifest)(platforms, undefined, ctx.iteration);
|
|
4874
5022
|
perDocStatus = await (0, spec_skeleton_1.detectSkeletonProgress)(specDir, manifest);
|
|
4875
5023
|
if (perDocStatus.nextUnfilled) {
|
|
4876
5024
|
const nextDocName = perDocStatus.nextUnfilled.docName;
|
|
@@ -4914,7 +5062,13 @@ sequenceDiagram
|
|
|
4914
5062
|
else if (perDocStatus.filledCount === perDocStatus.totalCount) {
|
|
4915
5063
|
logger_1.logger.info(`🎉 所有 ${perDocStatus.totalCount} 个骨架文件均已填充`);
|
|
4916
5064
|
// v8.1.1+: DOC_MATRIX 交叉校验 — 检查骨架清单之外是否有遗漏的文档
|
|
4917
|
-
|
|
5065
|
+
// v8.3.237+: 有功能模块的项目全局 overview/ 只应有索引类文档,
|
|
5066
|
+
// 内容类(REQUIREMENT.md 等)归 {feature}/overview/,不得在此补建骨架
|
|
5067
|
+
const hasFeatures = (await (0, spec_paths_1.parseFeatureList)(iterDir)).length > 0;
|
|
5068
|
+
const expectedGlobalDocs = (hasFeatures
|
|
5069
|
+
? spec_skeleton_1.PHASE1_INDEX_DOC_NAMES
|
|
5070
|
+
: GLOBAL_DOCS)
|
|
5071
|
+
.filter(d => includeDocs.includes(d));
|
|
4918
5072
|
const missingDocs = [];
|
|
4919
5073
|
for (const doc of expectedGlobalDocs) {
|
|
4920
5074
|
const docPath = (0, path_1.join)(specDir, spec_paths_1.GLOBAL_SPECS_DIR, doc);
|
|
@@ -4976,11 +5130,6 @@ sequenceDiagram
|
|
|
4976
5130
|
refactor: ['REQ.md', 'TECH.md', 'TASK.md'],
|
|
4977
5131
|
bugfix: ['REQ.md', 'TECH.md'],
|
|
4978
5132
|
research: ['REQ.md'],
|
|
4979
|
-
review: ['REQ.md'],
|
|
4980
|
-
test: ['REQ.md', 'TECH.md'],
|
|
4981
|
-
deploy: ['REQ.md', 'TECH.md'],
|
|
4982
|
-
security: ['REQ.md', 'TECH.md'],
|
|
4983
|
-
performance: ['REQ.md', 'TECH.md'],
|
|
4984
5133
|
};
|
|
4985
5134
|
const taskDocNames = TASK_DOCS[taskType] || TASK_DOCS['feature'];
|
|
4986
5135
|
taskDocs = taskDocs.filter(([n]) => taskDocNames.includes(n));
|
|
@@ -5582,6 +5731,7 @@ status: "clarified"
|
|
|
5582
5731
|
prompt += `| PLATFORMS.md | 020-specs/overview/ | 索引类 | 端列表(全局) |\n`;
|
|
5583
5732
|
prompt += `| CLARIFY.md | 020-specs/overview/ | 索引类 | 全局澄清(跨功能共性问题) |\n`;
|
|
5584
5733
|
prompt += `| API_CONTRACT.yaml | 020-specs/overview/ | 索引类 | 跨功能接口契约(全局) |\n`;
|
|
5734
|
+
prompt += `| ARCHITECTURE.md | 020-specs/overview/ | 索引类 | 迭代全局架构(服务拓扑/数据流) |\n`;
|
|
5585
5735
|
prompt += `| REQUIREMENT.md | 020-specs/{feature}/overview/ | 内容类 | 该功能模块的需求规格 |\n`;
|
|
5586
5736
|
prompt += `| ANALYSIS.md | 020-specs/{feature}/overview/ | 内容类 | 该功能模块的需求分析 |\n`;
|
|
5587
5737
|
prompt += `| TECH.md | 020-specs/{feature}/overview/ | 内容类 | 该功能模块的技术方案(Phase 1) |\n`;
|
|
@@ -5597,7 +5747,8 @@ status: "clarified"
|
|
|
5597
5747
|
prompt += `> **重要规则**:\n`;
|
|
5598
5748
|
prompt += `> - 索引类文档(FUNCTION_MAP.md / INTERACTION_MAP.md / PLATFORMS.md / CLARIFY.md / API_CONTRACT.yaml)**必须**写入 020-specs/overview/,严禁写入 {feature}/overview/\n`;
|
|
5599
5749
|
prompt += `> - 内容类文档(REQUIREMENT.md / ANALYSIS.md / TECH.md / RISK.md / DEPS.md / REVIEW.md / MONITOR.md / DEV_GUIDE.md)**必须**写入 020-specs/{feature}/overview/,严禁写入 020-specs/overview/\n`;
|
|
5600
|
-
prompt += `> - ANALYSIS.md 属于内容类,严禁放在 020-specs/overview/ 下\n
|
|
5750
|
+
prompt += `> - ANALYSIS.md 属于内容类,严禁放在 020-specs/overview/ 下\n`;
|
|
5751
|
+
prompt += `> - 全局 overview/REQUIREMENT.md 由 split 阶段从各功能模块 {feature}/overview/REQUIREMENT.md 自动聚合生成,AI 不要直接写入(会被拦截)\n\n`;
|
|
5601
5752
|
const dirStepNum = platformTypes.size > 0 ? 10 : 9;
|
|
5602
5753
|
prompt += `${dirStepNum}. **目录结构(严格遵循,禁止自创目录)**:\n`;
|
|
5603
5754
|
prompt += ` - **全局索引**(跨端汇总)→ 通过 --apply 写入,CLI 自动路由到 \`020-specs/overview/{文件名}\`\n`;
|
|
@@ -6072,7 +6223,21 @@ async function generateQualityReport(iteration) {
|
|
|
6072
6223
|
async function buildClarifyPhasePrompt(iteration) {
|
|
6073
6224
|
// 生成质量报告(复用抽离的审查逻辑)+ 拿到文档清单与质量报告
|
|
6074
6225
|
const iterDir = await (0, context_1.getIterationDir)(iteration);
|
|
6075
|
-
const
|
|
6226
|
+
const raw = await generateQualityReport(iteration);
|
|
6227
|
+
// v8.3.236+: 过滤非需求文档——功能单元元数据(features/*/)、全局上下文(overview/GLOBAL_CONTEXT-*)
|
|
6228
|
+
// 与报告/索引类(VALIDATION/QUALITY_REPORT/INDEX)不进澄清清单,
|
|
6229
|
+
// 修复「78 份源文档大半是切片元数据、AI 为 CHANGELOG/_matrix 写澄清垃圾」
|
|
6230
|
+
const qualityReports = raw.reports.filter((r) => {
|
|
6231
|
+
const rel = (0, path_1.relative)(iterDir, r.filePath);
|
|
6232
|
+
if (rel.startsWith('features/') || rel.includes('/features/'))
|
|
6233
|
+
return false;
|
|
6234
|
+
if (rel.includes('GLOBAL_CONTEXT-'))
|
|
6235
|
+
return false;
|
|
6236
|
+
if (/(VALIDATION|QUALITY_REPORT|INDEX)\.md$/.test(r.filePath))
|
|
6237
|
+
return false;
|
|
6238
|
+
return true;
|
|
6239
|
+
});
|
|
6240
|
+
const docPaths = qualityReports.map((r) => r.filePath);
|
|
6076
6241
|
let allDocContent = '';
|
|
6077
6242
|
for (const p of docPaths) {
|
|
6078
6243
|
try {
|