speccore 8.3.253 → 8.3.255
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 +40 -11
- package/dist/cli.js.map +1 -1
- package/dist/commands/analyze.d.ts +1 -6
- package/dist/commands/analyze.d.ts.map +1 -1
- package/dist/commands/analyze.js +338 -842
- package/dist/commands/analyze.js.map +1 -1
- package/dist/commands/global.d.ts +35 -0
- package/dist/commands/global.d.ts.map +1 -0
- package/dist/commands/global.js +35 -0
- package/dist/commands/global.js.map +1 -0
- package/dist/commands/iteration/taskify.d.ts +21 -0
- package/dist/commands/iteration/taskify.d.ts.map +1 -0
- package/dist/commands/iteration/taskify.js +4676 -0
- package/dist/commands/iteration/taskify.js.map +1 -0
- package/package.json +1 -1
package/dist/commands/analyze.js
CHANGED
|
@@ -62,7 +62,6 @@ const task_utils_1 = require("../utils/task-utils");
|
|
|
62
62
|
const cleanup_1 = require("./cleanup");
|
|
63
63
|
const logger_1 = require("../utils/logger");
|
|
64
64
|
const context_1 = require("../core/context");
|
|
65
|
-
const task_paths_1 = require("../core/task-paths");
|
|
66
65
|
const feature_source_1 = require("../core/feature-source");
|
|
67
66
|
const analyze_engine_1 = require("../core/analyze-engine");
|
|
68
67
|
const fs_extra_2 = require("fs-extra");
|
|
@@ -319,6 +318,10 @@ async function tryLlmGenerate(prompt, iter) {
|
|
|
319
318
|
return false;
|
|
320
319
|
}
|
|
321
320
|
async function analyzeCommand(options) {
|
|
321
|
+
// v8.3.255+: 全局层分析已独立为 speccore global 命令,旧入口保留一版过渡
|
|
322
|
+
if (options.scope === 'global' && !options._fromGlobal) {
|
|
323
|
+
logger_1.logger.warn('⚠️ analyze --scope global 已废弃,请改用: speccore global analyze');
|
|
324
|
+
}
|
|
322
325
|
// v6.97.0+ 修复:全局分析时统一设置 iteration 为 'GLOBAL',避免任何 fallback 到 getDefaultIteration()
|
|
323
326
|
// 这是根治方案:后面所有 "options.iteration || await getDefaultIteration()" 都会命中 options.iteration
|
|
324
327
|
// v8.3.102+: 向上查找项目根目录(支持在子目录执行)
|
|
@@ -1331,8 +1334,8 @@ async function analyzeCommand(options) {
|
|
|
1331
1334
|
catch { /* 默认 host */ }
|
|
1332
1335
|
// v8.3.225+: 方式 Y 调度循环 — SDK/llm 成功落盘后进程内 advance 继续下一步,直到 Pipeline 完成;
|
|
1333
1336
|
// host 模式输出自包含 prompt 接力(exitCode 10,宿主会话内 --apply 后继续调用本命令即可断点续跑)
|
|
1334
|
-
const scopeFlag = isGlobalScope ? '
|
|
1335
|
-
const resumeCmd = `speccore analyze --prompt --pipeline ${scopeFlag}`;
|
|
1337
|
+
const scopeFlag = isGlobalScope ? '' : `-I ${iter}`;
|
|
1338
|
+
const resumeCmd = isGlobalScope ? 'speccore global analyze --prompt --pipeline' : `speccore analyze --prompt --pipeline ${scopeFlag}`;
|
|
1336
1339
|
const maxRounds = steps.length * 2 + 2; // 防异常死循环保护
|
|
1337
1340
|
let round = 0;
|
|
1338
1341
|
while (round < maxRounds) {
|
|
@@ -1486,31 +1489,9 @@ async function analyzeCommand(options) {
|
|
|
1486
1489
|
// 跳过澄清(off 或 auto 判定 high):仍生成质量报告,供后续修正参考
|
|
1487
1490
|
await generateQualityReport(iter);
|
|
1488
1491
|
}
|
|
1489
|
-
// v8.3.94+: 多子任务分析支持
|
|
1490
|
-
const targetTasks = await resolveTargetTasks(options);
|
|
1491
|
-
if (targetTasks.length > 1) {
|
|
1492
|
-
logger_1.logger.info(`🔄 批量分析 ${targetTasks.length} 个子任务: ${targetTasks.join(', ')}`);
|
|
1493
|
-
const prompts = [];
|
|
1494
|
-
for (let i = 0; i < targetTasks.length; i++) {
|
|
1495
|
-
const taskId = targetTasks[i];
|
|
1496
|
-
const singlePrompt = (0, prompt_builder_1.buildPromptText)(await (0, prompt_builder_1.buildPrompt)('analyze', {
|
|
1497
|
-
iteration: iter, task: taskId, platform: options.platform,
|
|
1498
|
-
analyzeOptions: options,
|
|
1499
|
-
}));
|
|
1500
|
-
prompts.push(`<!-- ═══════════════════════════════════════════ -->
|
|
1501
|
-
<!-- 任务 ${i + 1}/${targetTasks.length}: ${taskId} -->
|
|
1502
|
-
<!-- ═══════════════════════════════════════════ -->
|
|
1503
|
-
${singlePrompt}`);
|
|
1504
|
-
}
|
|
1505
|
-
const mergedPrompt = `# 批量分析 ${targetTasks.length} 个子任务\n\n请分别为以下每个任务生成分析文档。每个任务之间用分隔线隔开。\n\n${prompts.join('\n\n---\n\n')}`;
|
|
1506
|
-
process.stdout.write(`[SPECCORE_PROMPT]\n${mergedPrompt}`);
|
|
1507
|
-
process.exitCode = 10;
|
|
1508
|
-
return;
|
|
1509
|
-
}
|
|
1510
1492
|
// v8.3.160+: 统一使用 buildPrompt 入口
|
|
1511
1493
|
const promptObj = await (0, prompt_builder_1.buildPrompt)('analyze', {
|
|
1512
1494
|
iteration: iter,
|
|
1513
|
-
task: options.task,
|
|
1514
1495
|
platform: options.platform,
|
|
1515
1496
|
analyzeOptions: options,
|
|
1516
1497
|
});
|
|
@@ -1549,33 +1530,11 @@ ${singlePrompt}`);
|
|
|
1549
1530
|
return;
|
|
1550
1531
|
}
|
|
1551
1532
|
const iterDir = isGlobalScope ? undefined : await (0, context_1.getIterationDir)(options.iteration);
|
|
1552
|
-
// v8.3.94+: 多子任务 apply 检测
|
|
1553
|
-
if (options.task && options.task.includes(',')) {
|
|
1554
|
-
logger_1.logger.error('❌ --apply 模式下暂不支持多任务批量写入');
|
|
1555
|
-
logger_1.logger.info(' 请逐个任务执行 apply:');
|
|
1556
|
-
const taskIds = parseTaskIds(options.task);
|
|
1557
|
-
for (const taskId of taskIds) {
|
|
1558
|
-
logger_1.logger.info(` speccore analyze --apply '<JSON>' -I ${options.iteration} --task ${taskId}`);
|
|
1559
|
-
}
|
|
1560
|
-
return;
|
|
1561
|
-
}
|
|
1562
|
-
const isTaskLevel = !isGlobalScope && !!options.task;
|
|
1563
|
-
let taskDir = null;
|
|
1564
|
-
if (isTaskLevel) {
|
|
1565
|
-
const taskId = options.task.startsWith('Task-') ? options.task : `Task-${options.task}`;
|
|
1566
|
-
taskDir = await (0, task_paths_1.findTaskDir)((0, path_1.join)(iterDir, '030-tasks'), taskId);
|
|
1567
|
-
if (!taskDir) {
|
|
1568
|
-
logger_1.logger.error(`未找到任务: ${taskId}`);
|
|
1569
|
-
return;
|
|
1570
|
-
}
|
|
1571
|
-
}
|
|
1572
1533
|
// v8.3.237+: Pipeline 推进逻辑抽取为共享函数——JSON / [DOC:] 多文档通道写入后
|
|
1573
1534
|
// 提前 return,此前会跳过尾部的 Pipeline 推进(bug: 只有单文件模式能推进),
|
|
1574
1535
|
// 现将「RAG 刷新 + Pipeline 推进」抽为共享函数,三个写入通道成功后都调用。
|
|
1575
1536
|
// 返回 true 表示存在活跃 Pipeline 并已处理(调用点应直接 return,不再输出后续提示)。
|
|
1576
1537
|
const advancePipelineAfterApply = async () => {
|
|
1577
|
-
if (isTaskLevel)
|
|
1578
|
-
return false;
|
|
1579
1538
|
// v8.3.238+: 步骤完成检查——计算当前步骤尚未填充的骨架清单(相对 020-specs/ 的路径)。
|
|
1580
1539
|
// 单文档模式一次 --apply 通常只写 1~2 个文档,但一个步骤(如 phase1-prompt)有多个
|
|
1581
1540
|
// 骨架待填充;此前写 1 个就推进,剩余骨架会错位到下一步骤。
|
|
@@ -1700,8 +1659,8 @@ ${singlePrompt}`);
|
|
|
1700
1659
|
stepIds: stateBefore.steps || [stateBefore.currentStep],
|
|
1701
1660
|
options,
|
|
1702
1661
|
});
|
|
1703
|
-
const scopeFlag = isGlobalScope ? '
|
|
1704
|
-
const nextCmd = `speccore analyze --prompt --pipeline ${scopeFlag}`;
|
|
1662
|
+
const scopeFlag = isGlobalScope ? '' : `-I ${options.iteration}`;
|
|
1663
|
+
const nextCmd = isGlobalScope ? 'speccore global analyze --prompt --pipeline' : `speccore analyze --prompt --pipeline ${scopeFlag}`;
|
|
1705
1664
|
const output = [
|
|
1706
1665
|
`[SPECCORE_NEXT_STEP]`,
|
|
1707
1666
|
`当前步骤 "${stateBefore.currentStep}" 尚未完成,请继续填充剩余骨架后再次 --apply。`,
|
|
@@ -1742,8 +1701,8 @@ ${singlePrompt}`);
|
|
|
1742
1701
|
options,
|
|
1743
1702
|
});
|
|
1744
1703
|
// v8.3.225+: 方式 Y 输出标记 — 宿主会话内继续执行即可,不再强制新对话
|
|
1745
|
-
const scopeFlag = isGlobalScope ? '
|
|
1746
|
-
const nextCmd = `speccore analyze --prompt --pipeline ${scopeFlag}`;
|
|
1704
|
+
const scopeFlag = isGlobalScope ? '' : `-I ${options.iteration}`;
|
|
1705
|
+
const nextCmd = isGlobalScope ? 'speccore global analyze --prompt --pipeline' : `speccore analyze --prompt --pipeline ${scopeFlag}`;
|
|
1747
1706
|
const output = [
|
|
1748
1707
|
`[SPECCORE_STEP_DONE]`,
|
|
1749
1708
|
`步骤 "${state.currentStep}" 已完成。`,
|
|
@@ -1809,7 +1768,7 @@ ${singlePrompt}`);
|
|
|
1809
1768
|
logger_1.logger.info('');
|
|
1810
1769
|
}
|
|
1811
1770
|
// v8.3.190+: 需求澄清验证 — off 跳过门禁;auto/on 下质量不足且未澄清则拒绝写入
|
|
1812
|
-
if (!isGlobalScope &&
|
|
1771
|
+
if (!isGlobalScope && clarifyMode !== 'off') {
|
|
1813
1772
|
const { hasValidClarifiedDocs } = await Promise.resolve().then(() => __importStar(require('../core/requirement-clarifier')));
|
|
1814
1773
|
const hasClarifiedDocs = await hasValidClarifiedDocs(iterDir);
|
|
1815
1774
|
if (!hasClarifiedDocs && clarifyBlocks.size === 0) {
|
|
@@ -1883,29 +1842,7 @@ ${singlePrompt}`);
|
|
|
1883
1842
|
}
|
|
1884
1843
|
}
|
|
1885
1844
|
let count = 0;
|
|
1886
|
-
if (
|
|
1887
|
-
// 任务级:写 Task/00-specs/(v6.44.0+ 统一写入 00-specs/)
|
|
1888
|
-
const targetSubDir = options.platform && !isTaskLevel ? options.platform : '00-specs';
|
|
1889
|
-
const taskSpecDir = (0, path_1.join)(taskDir, targetSubDir);
|
|
1890
|
-
await (0, fs_extra_1.ensureDir)(taskSpecDir);
|
|
1891
|
-
for (const [filename, content] of Object.entries(docs)) {
|
|
1892
|
-
const fp = (0, path_1.join)(taskSpecDir, filename);
|
|
1893
|
-
if (!(await (0, task_utils_1.shouldOverwrite)(fp, !!options.interactive))) {
|
|
1894
|
-
logger_1.logger.info(` ⏭️ 跳过: ${filename}`);
|
|
1895
|
-
continue;
|
|
1896
|
-
}
|
|
1897
|
-
const bk = await (0, task_utils_1.backupWithTimestamp)(fp);
|
|
1898
|
-
if (bk) {
|
|
1899
|
-
backups.push(bk);
|
|
1900
|
-
logger_1.logger.info(` 📦 ${filename} 已备份: ${(0, path_1.basename)(bk)}`);
|
|
1901
|
-
}
|
|
1902
|
-
await (0, fs_extra_1.writeFile)(fp, content);
|
|
1903
|
-
count++;
|
|
1904
|
-
}
|
|
1905
|
-
const platformLabel = options.platform ? `/${options.platform}` : '';
|
|
1906
|
-
logger_1.logger.success(`✅ ${count} 个 Spec 文档已写入 ${options.task}${platformLabel}/(任务级,迭代基线不变)`);
|
|
1907
|
-
}
|
|
1908
|
-
else if (isGlobalScope) {
|
|
1845
|
+
if (isGlobalScope) {
|
|
1909
1846
|
// 全局级:写入 .speccore/GLOBAL/(与 platforms/ 同级,非迭代目录)
|
|
1910
1847
|
const globalBaseDir = (0, path_1.join)(_projectRoot, '.speccore', 'GLOBAL');
|
|
1911
1848
|
await (0, fs_extra_1.ensureDir)(globalBaseDir);
|
|
@@ -1989,14 +1926,14 @@ ${singlePrompt}`);
|
|
|
1989
1926
|
logger_1.logger.info(`\n💡 AI 请按以下步骤修复:`);
|
|
1990
1927
|
logger_1.logger.info(` 1. 读取上述问题文档的当前内容`);
|
|
1991
1928
|
logger_1.logger.info(` 2. 根据质量报告中的错误信息补充/修正内容`);
|
|
1992
|
-
logger_1.logger.info(` 3. 用 speccore analyze --apply '{"文件路径":"修正后内容"}'
|
|
1929
|
+
logger_1.logger.info(` 3. 用 speccore global analyze --apply '{"文件路径":"修正后内容"}' 重新写入`);
|
|
1993
1930
|
process.stdout.write(`\n[SPECCORE_PROMPT]\n# 质量门禁修复任务\n\n以下全局分析文档未通过质量校验,请修复后重新写入:\n\n`);
|
|
1994
1931
|
for (const r of criticalErrors.slice(0, 5)) {
|
|
1995
1932
|
const fname = r.file.split('/').slice(-2).join('/');
|
|
1996
1933
|
const errs = r.issues.filter(i => i.severity === 'error').map(i => `- ${i.message}`).join('\n');
|
|
1997
1934
|
process.stdout.write(`## ${fname}\n${errs}\n\n`);
|
|
1998
1935
|
}
|
|
1999
|
-
process.stdout.write(`修复完成后重新执行:speccore analyze --
|
|
1936
|
+
process.stdout.write(`修复完成后重新执行:speccore global analyze --layer 4\n`);
|
|
2000
1937
|
process.exitCode = 10;
|
|
2001
1938
|
return;
|
|
2002
1939
|
}
|
|
@@ -2384,28 +2321,7 @@ ${singlePrompt}`);
|
|
|
2384
2321
|
try {
|
|
2385
2322
|
const docs = JSON.parse(options.apply);
|
|
2386
2323
|
let count = 0;
|
|
2387
|
-
if (
|
|
2388
|
-
const targetSubDir = options.platform && !isTaskLevel ? options.platform : '00-specs';
|
|
2389
|
-
const taskSpecDir = (0, path_1.join)(taskDir, targetSubDir);
|
|
2390
|
-
await (0, fs_extra_1.ensureDir)(taskSpecDir);
|
|
2391
|
-
for (const [filename, content] of Object.entries(docs)) {
|
|
2392
|
-
const fp = (0, path_1.join)(taskSpecDir, filename);
|
|
2393
|
-
if (!(await (0, task_utils_1.shouldOverwrite)(fp, !!options.interactive))) {
|
|
2394
|
-
logger_1.logger.info(` ⏭️ 跳过: ${filename}`);
|
|
2395
|
-
continue;
|
|
2396
|
-
}
|
|
2397
|
-
const bk = await (0, task_utils_1.backupWithTimestamp)(fp);
|
|
2398
|
-
if (bk) {
|
|
2399
|
-
backups.push(bk);
|
|
2400
|
-
logger_1.logger.info(` 📦 ${filename} 已备份: ${(0, path_1.basename)(bk)}`);
|
|
2401
|
-
}
|
|
2402
|
-
await (0, fs_extra_1.writeFile)(fp, content);
|
|
2403
|
-
count++;
|
|
2404
|
-
}
|
|
2405
|
-
const platformLabel = options.platform ? `/${options.platform}` : '';
|
|
2406
|
-
logger_1.logger.success(`✅ ${count} 个 Spec 文档已写入 ${options.task}${platformLabel}/(任务级,迭代基线不变)`);
|
|
2407
|
-
}
|
|
2408
|
-
else if (isGlobalScope) {
|
|
2324
|
+
if (isGlobalScope) {
|
|
2409
2325
|
const globalBaseDir = (0, path_1.join)(_projectRoot, '.speccore', 'GLOBAL');
|
|
2410
2326
|
await (0, fs_extra_1.ensureDir)(globalBaseDir);
|
|
2411
2327
|
const globalSet = new Set(spec_paths_2.GLOBAL_SPEC_FILES);
|
|
@@ -2613,27 +2529,7 @@ ${singlePrompt}`);
|
|
|
2613
2529
|
// 注意:不能提前 return,否则会跳过后续的 Pipeline 推进逻辑(--apply 靠它推进 confirm-check)
|
|
2614
2530
|
const hasClarifyMarkers = /\[CLARIFY:[^\]]+\]/.test(options.apply);
|
|
2615
2531
|
// 单文件模式
|
|
2616
|
-
if (!hasClarifyMarkers &&
|
|
2617
|
-
// 任务级:写 Task/00-specs/(v6.44.0+)
|
|
2618
|
-
const targetSubDir = '00-specs';
|
|
2619
|
-
const taskSpecDir = (0, path_1.join)(taskDir, targetSubDir);
|
|
2620
|
-
await (0, fs_extra_1.ensureDir)(taskSpecDir);
|
|
2621
|
-
const taskAnalysisPath = (0, path_1.join)(taskSpecDir, 'ANALYSIS.md');
|
|
2622
|
-
if (await (0, task_utils_1.shouldOverwrite)(taskAnalysisPath, !!options.interactive)) {
|
|
2623
|
-
const taskBackup = await (0, task_utils_1.backupWithTimestamp)(taskAnalysisPath);
|
|
2624
|
-
if (taskBackup) {
|
|
2625
|
-
backups.push(taskBackup);
|
|
2626
|
-
logger_1.logger.info(` 📦 已备份: ${taskBackup.split('/').pop()}`);
|
|
2627
|
-
}
|
|
2628
|
-
await (0, fs_extra_1.writeFile)(taskAnalysisPath, options.apply);
|
|
2629
|
-
const platformLabel = options.platform ? `/${options.platform}` : '';
|
|
2630
|
-
logger_1.logger.success(`✅ ANALYSIS.md 已写入 ${options.task}${platformLabel}/`);
|
|
2631
|
-
}
|
|
2632
|
-
else {
|
|
2633
|
-
logger_1.logger.info(` ⏭️ 用户取消覆盖`);
|
|
2634
|
-
}
|
|
2635
|
-
}
|
|
2636
|
-
else if (!hasClarifyMarkers && isGlobalScope) {
|
|
2532
|
+
if (!hasClarifyMarkers && isGlobalScope) {
|
|
2637
2533
|
// 全局级:写 .speccore/GLOBAL/ANALYSIS.md
|
|
2638
2534
|
const globalDir = (0, path_1.join)(_projectRoot, '.speccore', 'GLOBAL');
|
|
2639
2535
|
await (0, fs_extra_1.ensureDir)(globalDir);
|
|
@@ -2670,26 +2566,6 @@ ${singlePrompt}`);
|
|
|
2670
2566
|
logger_1.logger.info(` ⏭️ 用户取消覆盖`);
|
|
2671
2567
|
}
|
|
2672
2568
|
}
|
|
2673
|
-
// ── --sync: 任务分析后局部回写 020-specs/ ──
|
|
2674
|
-
if (options.sync && isTaskLevel && taskDir) {
|
|
2675
|
-
logger_1.logger.info('');
|
|
2676
|
-
logger_1.logger.info('🔄 局部回写: 将任务分析结果同步到 020-specs/ ...');
|
|
2677
|
-
try {
|
|
2678
|
-
const { syncTaskToSpecs } = await Promise.resolve().then(() => __importStar(require('../core/spec-merger')));
|
|
2679
|
-
const iterDirPath = iterDir;
|
|
2680
|
-
const taskName = options.task || '';
|
|
2681
|
-
const mergeResult = await syncTaskToSpecs(iterDirPath, taskDir, taskName);
|
|
2682
|
-
if (mergeResult.filesUpdated > 0) {
|
|
2683
|
-
logger_1.logger.success(`✅ 局部回写完成: ${mergeResult.filesUpdated} 个 spec 文件已更新`);
|
|
2684
|
-
}
|
|
2685
|
-
else {
|
|
2686
|
-
logger_1.logger.info(' ℹ️ 未找到匹配的 spec 文件,无需回写');
|
|
2687
|
-
}
|
|
2688
|
-
}
|
|
2689
|
-
catch (e) {
|
|
2690
|
-
logger_1.logger.debug('局部回写失败(非关键):', e);
|
|
2691
|
-
}
|
|
2692
|
-
}
|
|
2693
2569
|
printBackupSummary();
|
|
2694
2570
|
// v8.3.237+: RAG 刷新 + Pipeline 推进已抽取为 advancePipelineAfterApply(见 apply 分支开头),
|
|
2695
2571
|
// 三个写入通道(JSON / [DOC:] / 单文件模式)统一调用,避免多文档通道提前 return 跳过推进。
|
|
@@ -2697,7 +2573,7 @@ ${singlePrompt}`);
|
|
|
2697
2573
|
if (pipelineHandled)
|
|
2698
2574
|
return;
|
|
2699
2575
|
// ── v6.64.0+: Phase 1 完成后自动触发 Phase 2(仅多端项目,非 Pipeline 模式)──
|
|
2700
|
-
if (!options.phase &&
|
|
2576
|
+
if (!options.phase && options.iteration) {
|
|
2701
2577
|
// 检查是否有多个端
|
|
2702
2578
|
const platforms = await (0, spec_paths_2.parsePlatformList)();
|
|
2703
2579
|
// 只有多端项目(≥2 个端)才需要分两阶段:
|
|
@@ -2735,8 +2611,8 @@ ${singlePrompt}`);
|
|
|
2735
2611
|
const progress = await detectGlobalLayerProgress();
|
|
2736
2612
|
if (progress.completedLayer < 4) {
|
|
2737
2613
|
const nextCmd = progress.nextLayer === 4 && progress.subLayer
|
|
2738
|
-
? `speccore analyze --
|
|
2739
|
-
: `speccore analyze --
|
|
2614
|
+
? `speccore global analyze --layer 4${options.withCode ? ' --with-code' : ''}`
|
|
2615
|
+
: `speccore global analyze --layer ${progress.nextLayer}${options.withCode ? ' --with-code' : ''}`;
|
|
2740
2616
|
process.stdout.write(`\n[SPECCORE_EXEC: ${nextCmd}]\n`);
|
|
2741
2617
|
process.stdout.write(`> 全局分析 ${progress.completedLayer}/4 层完成,自动推进到下一层...\n`);
|
|
2742
2618
|
process.exitCode = 10;
|
|
@@ -2767,7 +2643,7 @@ ${singlePrompt}`);
|
|
|
2767
2643
|
}
|
|
2768
2644
|
catch { /* ignore */ }
|
|
2769
2645
|
}
|
|
2770
|
-
const prompt = await buildMultiDocPrompt('analyze', { iteration: iter,
|
|
2646
|
+
const prompt = await buildMultiDocPrompt('analyze', { iteration: iter, scope: options.scope, withCode: options.withCode, platform: options.platform, phase: options.phase, autoMode: options.auto }, options, needsClarify ? { needsClarify, clarifyTargets } : undefined);
|
|
2771
2647
|
await (0, ai_trace_1.recordAiTrace)(iter, 'prompt-out', { mode: 'analyze-main', scope: options.scope, promptLength: prompt.length, prompt });
|
|
2772
2648
|
process.stdout.write(`[SPECCORE_PROMPT]\n${prompt}`);
|
|
2773
2649
|
// v7.2.0+: 全局分析完成后输出下一步引导
|
|
@@ -2953,154 +2829,6 @@ async function generateIterationSpecDocs(iteration) {
|
|
|
2953
2829
|
}
|
|
2954
2830
|
logger_1.logger.info(`\n📄 Spec 文档: 新建 ${created} 个, 跳过 ${skipped} 个 (已存在) → ${specDir}/ (global/ + 根目录)`);
|
|
2955
2831
|
}
|
|
2956
|
-
/**
|
|
2957
|
-
* 任务级文档补全 (原 perTaskAnalyze 逻辑)
|
|
2958
|
-
*/
|
|
2959
|
-
async function enrichTaskDocs(iteration, taskId, reqFiles) {
|
|
2960
|
-
const { readdirSync } = require('fs');
|
|
2961
|
-
const iterDir = await (0, context_1.getIterationDir)(iteration);
|
|
2962
|
-
if (!(await (0, fs_extra_1.pathExists)(iterDir)))
|
|
2963
|
-
return;
|
|
2964
|
-
const entries = readdirSync(iterDir, { withFileTypes: true });
|
|
2965
|
-
const taskEntry = entries.find((e) => e.isDirectory() && e.name.startsWith(taskId));
|
|
2966
|
-
// enrichTaskDocs continues, but taskEntry logic remains...
|
|
2967
|
-
if (!taskEntry)
|
|
2968
|
-
return;
|
|
2969
|
-
if (!taskEntry) {
|
|
2970
|
-
logger_1.logger.info(` ℹ️ 未找到任务目录 ${taskId},跳过文档补全`);
|
|
2971
|
-
return;
|
|
2972
|
-
}
|
|
2973
|
-
const fullTaskDir = (0, path_1.join)(iterDir, taskEntry.name);
|
|
2974
|
-
// _shared/ → 00-specs/
|
|
2975
|
-
const specsDir = (await (0, fs_extra_1.pathExists)((0, path_1.join)(fullTaskDir, '_shared')))
|
|
2976
|
-
? (0, path_1.join)(fullTaskDir, '_shared')
|
|
2977
|
-
: (0, path_1.join)(fullTaskDir, '00-specs');
|
|
2978
|
-
if (!(await (0, fs_extra_1.pathExists)(specsDir)))
|
|
2979
|
-
return;
|
|
2980
|
-
let reqContent = '';
|
|
2981
|
-
// 读取任务 REQ 或传入的需求文件
|
|
2982
|
-
const taskReqPath = (0, path_1.join)(specsDir, 'REQ.md');
|
|
2983
|
-
if (await (0, fs_extra_1.pathExists)(taskReqPath)) {
|
|
2984
|
-
reqContent = await require('fs-extra').readFile(taskReqPath, 'utf-8');
|
|
2985
|
-
}
|
|
2986
|
-
else if (reqFiles.length > 0) {
|
|
2987
|
-
for (const f of reqFiles) {
|
|
2988
|
-
if (await (0, fs_extra_1.pathExists)(f))
|
|
2989
|
-
reqContent += await require('fs-extra').readFile(f, 'utf-8') + '\n';
|
|
2990
|
-
}
|
|
2991
|
-
}
|
|
2992
|
-
if (!reqContent)
|
|
2993
|
-
return;
|
|
2994
|
-
// 补全 TECH.md
|
|
2995
|
-
const techPath = (0, path_1.join)(specsDir, 'TECH.md');
|
|
2996
|
-
let techContent = '';
|
|
2997
|
-
if (await (0, fs_extra_1.pathExists)(techPath)) {
|
|
2998
|
-
techContent = await require('fs-extra').readFile(techPath, 'utf-8');
|
|
2999
|
-
if (!techContent.includes('## 分析建议')) {
|
|
3000
|
-
const items = [];
|
|
3001
|
-
const apis = (reqContent.match(/\/api\/[a-zA-Z0-9\/-]+/g) || []).map((a) => a.trim());
|
|
3002
|
-
if (apis.length > 0) {
|
|
3003
|
-
items.push(`检测到 ${apis.length} 个 API:`);
|
|
3004
|
-
for (const api of [...new Set(apis)])
|
|
3005
|
-
items.push(` \`${api}\``);
|
|
3006
|
-
}
|
|
3007
|
-
if (reqContent.match(/数据库|表|DDL/))
|
|
3008
|
-
items.push('涉及数据库变更,请补充 DDL');
|
|
3009
|
-
if (reqContent.match(/权限|RBAC|鉴权/))
|
|
3010
|
-
items.push('涉及权限控制,注意鉴权边界');
|
|
3011
|
-
if (items.length > 0) {
|
|
3012
|
-
techContent += `\n\n---\n\n## 分析建议\n\n> 自动生成\n\n${items.map(i => `- ${i}`).join('\n')}\n`;
|
|
3013
|
-
await (0, fs_extra_1.writeFile)(techPath, techContent);
|
|
3014
|
-
logger_1.logger.info(` 📄 更新 TECH.md`);
|
|
3015
|
-
}
|
|
3016
|
-
}
|
|
3017
|
-
}
|
|
3018
|
-
// 补全 TEST.md(v6.49.9+: 扫描平铺的端目录)
|
|
3019
|
-
const testFiles = [];
|
|
3020
|
-
const subtaskPaths = await getSubtaskDirs(fullTaskDir);
|
|
3021
|
-
for (const stDir of subtaskPaths) {
|
|
3022
|
-
const fp = (0, path_1.join)(stDir, 'TEST.md');
|
|
3023
|
-
if (await (0, fs_extra_1.pathExists)(fp))
|
|
3024
|
-
testFiles.push(fp);
|
|
3025
|
-
}
|
|
3026
|
-
if (testFiles.length === 0) {
|
|
3027
|
-
const legacy = (0, path_1.join)(fullTaskDir, '99-artifacts', 'TEST.md');
|
|
3028
|
-
if (await (0, fs_extra_1.pathExists)(legacy))
|
|
3029
|
-
testFiles.push(legacy);
|
|
3030
|
-
}
|
|
3031
|
-
for (const testPath of testFiles) {
|
|
3032
|
-
let testContent = await require('fs-extra').readFile(testPath, 'utf-8');
|
|
3033
|
-
if (!testContent.includes('## 补充分析')) {
|
|
3034
|
-
const items = [];
|
|
3035
|
-
if (reqContent.includes('POST') || reqContent.includes('创建'))
|
|
3036
|
-
items.push('[ ] 正常参数 + 异常参数测试');
|
|
3037
|
-
if (reqContent.includes('GET') || reqContent.includes('查询'))
|
|
3038
|
-
items.push('[ ] 分页 / 筛选 / 空结果测试');
|
|
3039
|
-
if (reqContent.includes('DELETE') || reqContent.includes('删除'))
|
|
3040
|
-
items.push('[ ] 删除确认 + 级联处理');
|
|
3041
|
-
if (reqContent.includes('权限') || reqContent.includes('RBAC'))
|
|
3042
|
-
items.push('[ ] 无权限访问 + 越权检测');
|
|
3043
|
-
if (items.length > 0) {
|
|
3044
|
-
testContent += `\n\n---\n\n## 补充分析\n${items.join('\n')}\n`;
|
|
3045
|
-
await (0, fs_extra_1.writeFile)(testPath, testContent);
|
|
3046
|
-
logger_1.logger.info(` 📄 更新 ${(0, path_1.relative)(fullTaskDir, testPath)}`);
|
|
3047
|
-
}
|
|
3048
|
-
}
|
|
3049
|
-
}
|
|
3050
|
-
// 补全 REVIEW.md(v6.49.9+: 扫描平铺的端目录)
|
|
3051
|
-
const reviewFiles = [];
|
|
3052
|
-
for (const stDir of subtaskPaths) {
|
|
3053
|
-
const fp = (0, path_1.join)(stDir, 'REVIEW.md');
|
|
3054
|
-
if (await (0, fs_extra_1.pathExists)(fp))
|
|
3055
|
-
reviewFiles.push(fp);
|
|
3056
|
-
}
|
|
3057
|
-
if (reviewFiles.length === 0) {
|
|
3058
|
-
const legacy = (0, path_1.join)(fullTaskDir, '99-artifacts', 'REVIEW.md');
|
|
3059
|
-
if (await (0, fs_extra_1.pathExists)(legacy))
|
|
3060
|
-
reviewFiles.push(legacy);
|
|
3061
|
-
}
|
|
3062
|
-
for (const reviewPath of reviewFiles) {
|
|
3063
|
-
let reviewContent = await require('fs-extra').readFile(reviewPath, 'utf-8');
|
|
3064
|
-
if (!reviewContent.includes('## 本任务专项检查')) {
|
|
3065
|
-
const items = [];
|
|
3066
|
-
if (reqContent.includes('POST') || reqContent.includes('创建'))
|
|
3067
|
-
items.push('[ ] 参数校验 + 幂等性处理');
|
|
3068
|
-
if (reqContent.includes('数据库') || reqContent.includes('表'))
|
|
3069
|
-
items.push('[ ] 索引覆盖 + 迁移脚本可回滚');
|
|
3070
|
-
if (reqContent.includes('权限') || reqContent.includes('RBAC'))
|
|
3071
|
-
items.push('[ ] 鉴权注解/中间件正确配置');
|
|
3072
|
-
if (items.length > 0) {
|
|
3073
|
-
reviewContent += `\n\n---\n\n## 本任务专项检查\n${items.join('\n')}\n`;
|
|
3074
|
-
await (0, fs_extra_1.writeFile)(reviewPath, reviewContent);
|
|
3075
|
-
logger_1.logger.info(` 📄 更新 ${(0, path_1.relative)(fullTaskDir, reviewPath)}`);
|
|
3076
|
-
}
|
|
3077
|
-
}
|
|
3078
|
-
}
|
|
3079
|
-
// 创建缺失文件(v6.49.9+: 扫描平铺的端目录)
|
|
3080
|
-
const subtaskDirs = subtaskPaths;
|
|
3081
|
-
// 结构回退
|
|
3082
|
-
if (subtaskDirs.length === 0) {
|
|
3083
|
-
const legacyArtifacts = (0, path_1.join)(fullTaskDir, '99-artifacts');
|
|
3084
|
-
if (await (0, fs_extra_1.pathExists)(legacyArtifacts))
|
|
3085
|
-
subtaskDirs.push(legacyArtifacts);
|
|
3086
|
-
else
|
|
3087
|
-
subtaskDirs.push(fullTaskDir); // 最后回退到任务根
|
|
3088
|
-
}
|
|
3089
|
-
const templates = [
|
|
3090
|
-
['RISK.md', `# 风险评估\n\n> analyze | ${new Date().toISOString().split('T')[0]}\n\n## 风险矩阵\n| 风险 | 可能 | 影响 | 缓解 |\n| :--- | :--- | :--- | :--- |\n| 兼容性 | 中 | 高 | 版本号+测试 |\n\n## 回滚\n1. 触发: 线上错误率 > 1%\n2. 步骤: git revert → 重部署\n`],
|
|
3091
|
-
['DEPS.md', `# 依赖清单\n\n## 上游依赖\n| 服务 | 版本 | 用途 |\n| :--- | :--- | :--- |\n| _待补充_ | — | — |\n`],
|
|
3092
|
-
['MONITOR.md', `# 监控\n\n## 关键指标\n| 指标 | 阈值 | 级别 |\n| :--- | :--- | :--- |\n| 成功率 | <99.9% | P1 |\n| P99延迟 | >1000ms | P2 |\n`],
|
|
3093
|
-
];
|
|
3094
|
-
for (const subtaskDir of subtaskDirs) {
|
|
3095
|
-
for (const [filename, content] of templates) {
|
|
3096
|
-
const fp = (0, path_1.join)(subtaskDir, filename);
|
|
3097
|
-
if (!(await (0, fs_extra_1.pathExists)(fp))) {
|
|
3098
|
-
await (0, fs_extra_1.writeFile)(fp, content);
|
|
3099
|
-
logger_1.logger.info(` 📄 创建 ${(0, path_1.relative)(fullTaskDir, fp)}`);
|
|
3100
|
-
}
|
|
3101
|
-
}
|
|
3102
|
-
}
|
|
3103
|
-
}
|
|
3104
2832
|
/**
|
|
3105
2833
|
* 从 process.argv 手动解析选项 (Commander.js 偶发不传递部分选项)
|
|
3106
2834
|
*/
|
|
@@ -3108,7 +2836,6 @@ function parseArgv(options) {
|
|
|
3108
2836
|
const argv = process.argv;
|
|
3109
2837
|
const strFlags = [
|
|
3110
2838
|
[['--iteration', '-i', '-I'], (v) => { options.iteration = v; }],
|
|
3111
|
-
[['--task', '-t'], (v) => { options.task = v; }],
|
|
3112
2839
|
[['--platform'], (v) => { options.platform = v; }],
|
|
3113
2840
|
[['--scope'], (v) => { options.scope = v; }],
|
|
3114
2841
|
[['--src', '--source'], (v) => { options.source = v; }],
|
|
@@ -3573,7 +3300,7 @@ async function buildGlobalAnalysisGuide(options) {
|
|
|
3573
3300
|
if (completedLayer === 4 && (!subLayer || subLayer.completed.length === 4)) {
|
|
3574
3301
|
guide += '\n🎉 全局分析全部完成!\n';
|
|
3575
3302
|
guide += ' 所有文档已生成在 .speccore/GLOBAL/ 目录\n';
|
|
3576
|
-
guide += ' 如需补充某份文档: speccore analyze --
|
|
3303
|
+
guide += ' 如需补充某份文档: speccore global analyze --layer 4 --deep <文档名>\n';
|
|
3577
3304
|
return guide;
|
|
3578
3305
|
}
|
|
3579
3306
|
// 下一步命令
|
|
@@ -3584,15 +3311,15 @@ async function buildGlobalAnalysisGuide(options) {
|
|
|
3584
3311
|
if (!hasOutline) {
|
|
3585
3312
|
guide += ` 1. 将 AI 输出的大纲保存到: ${outlinePath}\n`;
|
|
3586
3313
|
guide += ` 2. 审核/修改大纲\n`;
|
|
3587
|
-
guide += ` 3. 再次执行: speccore analyze --
|
|
3314
|
+
guide += ` 3. 再次执行: speccore global analyze --layer 4 --deep ${deepDoc} --iterative\n`;
|
|
3588
3315
|
}
|
|
3589
3316
|
else {
|
|
3590
3317
|
guide += ` 1. 将 AI 输出的本节内容追加到文档\n`;
|
|
3591
|
-
guide += ` 2. 继续下一节: speccore analyze --
|
|
3318
|
+
guide += ` 2. 继续下一节: speccore global analyze --layer 4 --deep ${deepDoc} --iterative\n`;
|
|
3592
3319
|
}
|
|
3593
3320
|
}
|
|
3594
3321
|
else if (deepDoc) {
|
|
3595
|
-
guide += ` speccore analyze --
|
|
3322
|
+
guide += ` speccore global analyze --layer 4 --deep ${deepDoc}\n`;
|
|
3596
3323
|
guide += ` (如需迭代式补全: 加 --iterative 参数)\n`;
|
|
3597
3324
|
}
|
|
3598
3325
|
else if (nextLayer === 4 && subLayer) {
|
|
@@ -3602,19 +3329,19 @@ async function buildGlobalAnalysisGuide(options) {
|
|
|
3602
3329
|
'4c': '全局技术扩展文档(overview/)',
|
|
3603
3330
|
'4d': '各端技术文档(platforms/)',
|
|
3604
3331
|
};
|
|
3605
|
-
guide += ` speccore analyze --
|
|
3332
|
+
guide += ` speccore global analyze --layer 4\n`;
|
|
3606
3333
|
guide += ` (即将生成: ${subNames[subLayer.next] || subLayer.next})\n`;
|
|
3607
3334
|
}
|
|
3608
3335
|
else {
|
|
3609
|
-
guide += ` speccore analyze --
|
|
3336
|
+
guide += ` speccore global analyze --layer ${nextLayer}\n`;
|
|
3610
3337
|
}
|
|
3611
3338
|
// 快捷命令提示
|
|
3612
3339
|
guide += '\n💡 快捷命令:\n';
|
|
3613
3340
|
guide += ` 查看进度: speccore status\n`;
|
|
3614
3341
|
if (completedLayer >= 3) {
|
|
3615
|
-
guide += ` 深度分析单文档: speccore analyze --
|
|
3342
|
+
guide += ` 深度分析单文档: speccore global analyze --layer 4 --deep ARCHITECTURE.md\n`;
|
|
3616
3343
|
}
|
|
3617
|
-
guide += ` 全量重新分析: speccore analyze --
|
|
3344
|
+
guide += ` 全量重新分析: speccore global analyze --with-code\n`;
|
|
3618
3345
|
return guide;
|
|
3619
3346
|
}
|
|
3620
3347
|
// ── v7.4.5+: Layer 3 功能模块代码上下文预提取 ──
|
|
@@ -3728,10 +3455,7 @@ async function buildLayer3ModuleContext(projectRoot) {
|
|
|
3728
3455
|
// v8.3.160+: 改为 export,供 prompt-builder.ts 统一调用
|
|
3729
3456
|
async function buildMultiDocPrompt(command, ctx, options, clarifyCtx) {
|
|
3730
3457
|
const iter = ctx.iteration || '当前迭代';
|
|
3731
|
-
const task = ctx.task ? ` — ${ctx.task}` : '';
|
|
3732
|
-
const taskType = ctx.type || 'feature';
|
|
3733
3458
|
const now = new Date().toISOString().split('T')[0];
|
|
3734
|
-
const isTask = ctx.scope === 'task' || !!ctx.task;
|
|
3735
3459
|
const isGlobal = ctx.scope === 'global';
|
|
3736
3460
|
const autoMode = ctx.autoMode || false;
|
|
3737
3461
|
// v8.3.0+: 从 clarifyCtx 提取需求澄清状态
|
|
@@ -3854,7 +3578,7 @@ async function buildMultiDocPrompt(command, ctx, options, clarifyCtx) {
|
|
|
3854
3578
|
// v7.2.0+: 分层专注指令 — 强烈约束 AI 只执行当前 layer
|
|
3855
3579
|
prompt += `## 🎯 当前执行层级: Layer ${targetLayer}/4 — ${layerMeta.role}\n\n`;
|
|
3856
3580
|
prompt += `> ⚠️ **重要约束**: 你当前只需要完成 **Layer ${targetLayer}** 的工作。不要提前做后续层的内容。\n`;
|
|
3857
|
-
prompt += `> 每层完成后通过 \`speccore analyze --
|
|
3581
|
+
prompt += `> 每层完成后通过 \`speccore global analyze --layer ${targetLayer + 1 <= 4 ? targetLayer + 1 : 4}\` 进入下一层。\n\n`;
|
|
3858
3582
|
prompt += `| 层级 | 角色 | 核心任务 | 产出物 |\n`;
|
|
3859
3583
|
prompt += `| :--- | :--- | :--- | :--- |\n`;
|
|
3860
3584
|
prompt += `| 1 | 代码索引专家 | 扫描源码结构,提取索引 | platforms/{端}/_INDEX.md |\n`;
|
|
@@ -3901,7 +3625,7 @@ async function buildMultiDocPrompt(command, ctx, options, clarifyCtx) {
|
|
|
3901
3625
|
prompt += `**分批策略(v8.3.160+)**:\n`;
|
|
3902
3626
|
prompt += `- 如端数量 ≤ 5:一次生成所有端的 \`_INDEX.md\`\n`;
|
|
3903
3627
|
prompt += `- 如端数量 > 5:一次处理 3-5 个端,剩余端在输出末尾标注 \`[PENDING: {端名列表}]\`,供续批处理\n`;
|
|
3904
|
-
prompt += `- 续批命令:\`speccore analyze --
|
|
3628
|
+
prompt += `- 续批命令:\`speccore global analyze --layer 1 --resume\`\n`;
|
|
3905
3629
|
prompt += `**最小内容标准(v8.3.61+)**:\n`;
|
|
3906
3630
|
prompt += `- 每个 \_INDEX.md ≥ 80 行(维度少可接受 50+,但严禁 < 30 行)\n`;
|
|
3907
3631
|
prompt += `- 必须包含:端名、技术栈、目录结构概览、各维度清单表格\n`;
|
|
@@ -3994,7 +3718,7 @@ async function buildMultiDocPrompt(command, ctx, options, clarifyCtx) {
|
|
|
3994
3718
|
prompt += `- 如功能模块数量 ≤ 2:一次生成所有模块的分析文档\n`;
|
|
3995
3719
|
prompt += `- 如功能模块数量 > 2:一次处理 1-2 个模块,确保每个模块分析深入、不空洞\n`;
|
|
3996
3720
|
prompt += `- 剩余模块在输出末尾标注 \`[PENDING: {模块名列表}]\`,供续批处理\n`;
|
|
3997
|
-
prompt += `- 续批命令:\`speccore analyze --
|
|
3721
|
+
prompt += `- 续批命令:\`speccore global analyze --layer 3 --resume\`\n`;
|
|
3998
3722
|
prompt += `- **关键原则**:宁可少分析几个模块,也要保证每个模块的分析质量(≥100 行、有 Mermaid 图、有具体代码引用)\n\n`;
|
|
3999
3723
|
prompt += `**每个功能模块输出**:\n`;
|
|
4000
3724
|
prompt += `- 后端端:该功能模块相关的 API 详细设计、数据模型、业务规则、安全策略\n`;
|
|
@@ -5046,7 +4770,7 @@ sequenceDiagram
|
|
|
5046
4770
|
bugfix: ['ANALYSIS.md', 'TECH.md', 'TEST.md', 'DEV_GUIDE.md'],
|
|
5047
4771
|
research: ['ANALYSIS.md', 'DEV_GUIDE.md'],
|
|
5048
4772
|
};
|
|
5049
|
-
const includeDocs =
|
|
4773
|
+
const includeDocs = DOC_MATRIX['feature'];
|
|
5050
4774
|
// ── v6.61.0+: 恢复 Phase 1/Phase 2 分步逻辑,但 CLI 自动触发 Phase 2 ──
|
|
5051
4775
|
// v8.3.160+: 改为功能模块优先 — 全局只保留索引文档,每个功能模块有自己的综合文档
|
|
5052
4776
|
// Phase 1: 全局索引(FUNCTION_MAP.md/INTERACTION_MAP.md/PLATFORMS.md) + 各功能模块 overview/
|
|
@@ -5094,7 +4818,7 @@ sequenceDiagram
|
|
|
5094
4818
|
// 替代 v7.5.0 的 detectIterationDocsStatus(基于文件是否存在)
|
|
5095
4819
|
let perDocStatus = null;
|
|
5096
4820
|
let lastDocQuality = null;
|
|
5097
|
-
if (!isGlobal && !
|
|
4821
|
+
if (!isGlobal && !ctx.phase && ctx.iteration && ctx.iteration !== 'GLOBAL') {
|
|
5098
4822
|
try {
|
|
5099
4823
|
const iterDir = await (0, context_1.getIterationDir)(ctx.iteration);
|
|
5100
4824
|
if (iterDir) {
|
|
@@ -5211,39 +4935,6 @@ sequenceDiagram
|
|
|
5211
4935
|
logger_1.logger.debug(`骨架进度检测失败(非关键): ${e.message}`);
|
|
5212
4936
|
}
|
|
5213
4937
|
}
|
|
5214
|
-
// ── 任务级文档覆盖:00-specs/ 使用任务级文档集(v6.44.0+) ──
|
|
5215
|
-
if (isTask && !ctx.phase) {
|
|
5216
|
-
const TASK_DOCS = {
|
|
5217
|
-
feature: ['REQ.md', 'TECH.md', 'TASK.md', 'SCHEMA.md'],
|
|
5218
|
-
refactor: ['REQ.md', 'TECH.md', 'TASK.md'],
|
|
5219
|
-
bugfix: ['REQ.md', 'TECH.md'],
|
|
5220
|
-
research: ['REQ.md'],
|
|
5221
|
-
};
|
|
5222
|
-
const taskDocNames = TASK_DOCS[taskType] || TASK_DOCS['feature'];
|
|
5223
|
-
taskDocs = taskDocs.filter(([n]) => taskDocNames.includes(n));
|
|
5224
|
-
// 补充 TASK.md 和 SCHEMA.md(不在全局 docs 数组中)
|
|
5225
|
-
if (taskDocNames.includes('TASK.md') && !taskDocs.find(([n]) => n === 'TASK.md')) {
|
|
5226
|
-
taskDocs.push(['TASK.md', '']);
|
|
5227
|
-
}
|
|
5228
|
-
if (taskDocNames.includes('SCHEMA.md') && !taskDocs.find(([n]) => n === 'SCHEMA.md')) {
|
|
5229
|
-
taskDocs.push(['SCHEMA.md', '']);
|
|
5230
|
-
}
|
|
5231
|
-
// 任务级模板覆盖(v6.44.0+)
|
|
5232
|
-
for (const doc of taskDocs) {
|
|
5233
|
-
if (doc[0] === 'REQ.md') {
|
|
5234
|
-
doc[1] = `# \u672c\u4efb\u52a1\u9700\u6c42\u89c4\u683c\n\n> ${iter} | ${ctx.task} | ${now}\n\n## \u5199\u4f5c\u8981\u6c42\n\u6839\u636e split \u4ea7\u51fa\u7684\u9700\u6c42\u5207\u7247\uff0c\u7ed3\u5408\u5168\u5c40\u4e0a\u4e0b\u6587\uff0c\u91cd\u65b0\u7ec4\u7ec7\u672c\u4efb\u52a1\u7684\u9700\u6c42\u89c4\u683c\uff1a\n- \u660e\u786e\u672c\u4efb\u52a1\u7684\u9a8c\u6536\u6807\u51c6\uff08\u53ef\u6d4b\u8bd5\u7684\u3001\u5177\u4f53\u7684\uff09\n- \u7ec6\u5316\u4e1a\u52a1\u89c4\u5219\u548c\u8fb9\u754c\u6761\u4ef6\n- \u5217\u51fa\u5f02\u5e38\u573a\u666f\u548c\u5904\u7406\u65b9\u5f0f\n- \u6807\u6ce8\u4e0e\u5176\u4ed6 Task \u7684\u4f9d\u8d56\u5173\u7cfb\n`;
|
|
5235
|
-
}
|
|
5236
|
-
else if (doc[0] === 'TECH.md') {
|
|
5237
|
-
doc[1] = `# \u672c\u4efb\u52a1\u6280\u672f\u65b9\u6848\n\n> ${iter} | ${ctx.task} | ${now}\n\n## \u5199\u4f5c\u8981\u6c42\n\u57fa\u4e8e overview/TECH.md \u7684\u6574\u4f53\u67b6\u6784\uff0c\u7ec6\u5316\u5230\u51fd\u6570/\u63a5\u53e3\u7ea7\u522b\uff1a\n- \u5177\u4f53\u7684\u63a5\u53e3\u5b9a\u4e49\uff08\u8def\u5f84/\u53c2\u6570/\u54cd\u5e94\uff09\n- \u6570\u636e\u6a21\u578b\u8bbe\u8ba1\uff08Entity/DTO/VO \u5b57\u6bb5\u6620\u5c04\uff09\n- \u6838\u5fc3\u4e1a\u52a1\u903b\u8f91\u7684\u4f2a\u4ee3\u7801\u6216\u6d41\u7a0b\u63cf\u8ff0\n- \u524d\u7aef\u7ec4\u4ef6\u62c6\u5206\u548c\u72b6\u6001\u8bbe\u8ba1\n- \u5fc5\u987b\u4e0e overview/TECH.md \u7684\u6574\u4f53\u67b6\u6784\u4fdd\u6301\u4e00\u81f4\n`;
|
|
5238
|
-
}
|
|
5239
|
-
else if (doc[0] === 'TASK.md') {
|
|
5240
|
-
doc[1] = `# \u5b9e\u65bd\u8ba1\u5212\n\n> ${iter} | ${ctx.task} | ${now}\n\n## \u5199\u4f5c\u8981\u6c42\n\u6839\u636e\u672c\u4efb\u52a1\u7684\u9700\u6c42\u548c\u6280\u672f\u65b9\u6848\uff0c\u5236\u5b9a\u5177\u4f53\u5b9e\u65bd\u6b65\u9aa4\uff1a\n- \u6309\u5f00\u53d1\u987a\u5e8f\u5217\u51fa\u5177\u4f53\u6b65\u9aa4\n- \u6bcf\u4e2a\u6b65\u9aa4\u6709\u660e\u786e\u7684\u5b8c\u6210\u6807\u51c6\n- \u6807\u6ce8\u6b65\u9aa4\u95f4\u7684\u4f9d\u8d56\u5173\u7cfb\n- \u4f30\u7b97\u6bcf\u6b65\u7684\u5de5\u4f5c\u91cf\n`;
|
|
5241
|
-
}
|
|
5242
|
-
else if (doc[0] === 'SCHEMA.md') {
|
|
5243
|
-
doc[1] = `# \u6570\u636e\u6a21\u578b\n\n> ${iter} | ${ctx.task} | ${now}\n\n## \u5199\u4f5c\u8981\u6c42\n\u68b3\u7406\u672c\u4efb\u52a1\u6d89\u53ca\u7684\u6570\u636e\u6a21\u578b\u53d8\u66f4\uff1a\n- \u5b8c\u6574\u7684\u5b57\u6bb5\u5b9a\u4e49\uff08\u540d\u79f0/\u7c7b\u578b/\u7ea6\u675f/\u8bf4\u660e\uff09\n- \u7d22\u5f15\u8bbe\u8ba1\n- DDL \u8bed\u53e5\uff08\u5982\u9002\u7528\uff09\n- \u5b57\u6bb5\u2192API \u54cd\u5e94\u5b57\u6bb5\u7684\u6620\u5c04\u5173\u7cfb\n`;
|
|
5244
|
-
}
|
|
5245
|
-
}
|
|
5246
|
-
}
|
|
5247
4938
|
// v7.2.0+: 细粒度分析 — 只生成指定文档的指定功能
|
|
5248
4939
|
if (options?.docName && options?.featureName && !isGlobal) {
|
|
5249
4940
|
const targetDoc = options.docName.endsWith('.md') ? options.docName : `${options.docName}.md`;
|
|
@@ -5257,8 +4948,8 @@ sequenceDiagram
|
|
|
5257
4948
|
}
|
|
5258
4949
|
}
|
|
5259
4950
|
// ── 用户自定义模板集成(v6.45.0+)──
|
|
5260
|
-
const templateLevel = isGlobal ? 'global' :
|
|
5261
|
-
const userTemplates = await loadUserTemplates(templateLevel,
|
|
4951
|
+
const templateLevel = isGlobal ? 'global' : 'iteration';
|
|
4952
|
+
const userTemplates = await loadUserTemplates(templateLevel, undefined, ctx.platform);
|
|
5262
4953
|
const hasUserTemplates = userTemplates.size > 0;
|
|
5263
4954
|
if (hasUserTemplates) {
|
|
5264
4955
|
// 用户模板覆盖/追加:用户放了什么文档就生成什么文档
|
|
@@ -5281,13 +4972,13 @@ sequenceDiagram
|
|
|
5281
4972
|
techDoc[1] = `# 技术架构(跨端全局)\n\n> ${iter}\n\n## 写作要求\n撰写整体技术架构,覆盖所有端的交互关系:\n- 系统整体分层设计(各端在架构中的位置)\n- 跨端交互协议(前端↔后端通信方式、数据流向)\n- 中间件选型(缓存、消息队列、网关等)\n- 数据库整体设计(核心表结构、ER 关系)\n- 技术栈选型及理由\n`;
|
|
5282
4973
|
}
|
|
5283
4974
|
const featureLabel = ctx.feature ? ` · 功能模块:${ctx.feature}` : '';
|
|
5284
|
-
let prompt = `\n# 任务: ${command}
|
|
4975
|
+
let prompt = `\n# 任务: ${command} (${taskDocs.length}个文档 · 迭代全量${ctx.phase ? ` · Phase ${ctx.phase}` : ''}${featureLabel})\n\n`;
|
|
5285
4976
|
// v8.3.160+: 注入子 Agent 角色定义
|
|
5286
4977
|
if (agentContextText) {
|
|
5287
4978
|
prompt = agentContextText + '\n\n' + prompt;
|
|
5288
4979
|
}
|
|
5289
4980
|
// v8.3.0+: 如果检测到需求质量不足,在 prompt 开头注入强制澄清阶段
|
|
5290
|
-
if (needsClarify && !isGlobal && !
|
|
4981
|
+
if (needsClarify && !isGlobal && !ctx.phase) {
|
|
5291
4982
|
const now = new Date().toISOString();
|
|
5292
4983
|
prompt += `## 🚨 Phase 0: 需求澄清(强制前置 — 未完成则禁止进入 Phase 1)\n\n`;
|
|
5293
4984
|
prompt += `检测到以下需求文档质量不足,**必须先完成专业化整理,才能继续技术分析**:\n\n`;
|
|
@@ -5382,7 +5073,7 @@ status: "clarified"
|
|
|
5382
5073
|
logger_1.logger.warn(` ⚠️ 代码关联注入失败: ${e.message}`);
|
|
5383
5074
|
}
|
|
5384
5075
|
}
|
|
5385
|
-
if (!isGlobal &&
|
|
5076
|
+
if (!isGlobal && ctx.iteration && ctx.iteration !== 'GLOBAL') {
|
|
5386
5077
|
try {
|
|
5387
5078
|
const iterDir = await (0, context_1.getIterationDir)(ctx.iteration);
|
|
5388
5079
|
const reportPath = (0, path_1.join)(iterDir, '010-requirements', 'CLARIFY_REPORT.md');
|
|
@@ -5409,7 +5100,7 @@ status: "clarified"
|
|
|
5409
5100
|
catch { /* 忽略质量上下文注入失败 */ }
|
|
5410
5101
|
}
|
|
5411
5102
|
// ── v6.67.0+: 在 prompt 开头就明确完整工作流程 ──
|
|
5412
|
-
if (!ctx.phase
|
|
5103
|
+
if (!ctx.phase) {
|
|
5413
5104
|
const platforms = await (0, spec_paths_2.parsePlatformList)();
|
|
5414
5105
|
if (platforms.length >= 2) {
|
|
5415
5106
|
prompt += `## 📋 完整工作流程(两阶段分析)\n\n`;
|
|
@@ -5454,7 +5145,7 @@ status: "clarified"
|
|
|
5454
5145
|
// ── v8.1.0+: 逐文档模式自动注入 PRD 内容 + 前序文档摘要 ──
|
|
5455
5146
|
// v8.3.2+ 修复:PRD 注入不再仅限逐文档模式,Phase 1/2 模式下也必须注入需求内容
|
|
5456
5147
|
// v8.3.160+: 改为路径引用模式(不再全文塞进,让 AI 按需 Read)
|
|
5457
|
-
if (!isGlobal &&
|
|
5148
|
+
if (!isGlobal && ctx.iteration && ctx.iteration !== 'GLOBAL') {
|
|
5458
5149
|
try {
|
|
5459
5150
|
const iterDirForCtx = await (0, context_1.getIterationDir)(iter);
|
|
5460
5151
|
if (iterDirForCtx) {
|
|
@@ -5550,7 +5241,7 @@ status: "clarified"
|
|
|
5550
5241
|
prompt += `- 分析过程中,始终把迭代名称当作透明信息处理,不做任何功能推断\n\n`;
|
|
5551
5242
|
prompt += `## 分析范围说明\n`;
|
|
5552
5243
|
// v8.3.167+: 功能模块级分析专属指令
|
|
5553
|
-
if (ctx.feature
|
|
5244
|
+
if (ctx.feature) {
|
|
5554
5245
|
prompt += `- 当前是**功能模块级分析**,只分析「**${ctx.feature}**」功能模块\n`;
|
|
5555
5246
|
prompt += `- **只生成该功能模块的 overview/ 文档**,不要分析其他功能模块\n`;
|
|
5556
5247
|
prompt += `- 文档写入路径格式:\`${ctx.feature}/overview/{文件名}\`(如 \`${ctx.feature}/overview/REQUIREMENT.md\`)\n`;
|
|
@@ -5558,345 +5249,300 @@ status: "clarified"
|
|
|
5558
5249
|
prompt += `- 分析前请先 Read 全局索引文档,确保本模块内容与全局文档一致\n`;
|
|
5559
5250
|
prompt += `- 如果需求文档中该模块的描述不完整,标注"文档未充分描述",不要自行脑补\n\n`;
|
|
5560
5251
|
}
|
|
5561
|
-
|
|
5562
|
-
|
|
5563
|
-
prompt += `-
|
|
5564
|
-
prompt += `-
|
|
5565
|
-
prompt += `-
|
|
5566
|
-
|
|
5567
|
-
|
|
5568
|
-
|
|
5569
|
-
|
|
5570
|
-
prompt +=
|
|
5571
|
-
prompt +=
|
|
5572
|
-
prompt +=
|
|
5573
|
-
prompt += `- Read
|
|
5574
|
-
prompt += `- Read
|
|
5575
|
-
prompt +=
|
|
5576
|
-
prompt += `- Read
|
|
5577
|
-
prompt += `- Read 020-specs/
|
|
5578
|
-
prompt += `- Read 020-specs/overview/
|
|
5579
|
-
prompt +=
|
|
5580
|
-
prompt +=
|
|
5581
|
-
prompt +=
|
|
5582
|
-
prompt +=
|
|
5583
|
-
prompt +=
|
|
5584
|
-
prompt += `-
|
|
5585
|
-
prompt += `-
|
|
5586
|
-
prompt += `-
|
|
5587
|
-
prompt +=
|
|
5588
|
-
prompt +=
|
|
5589
|
-
prompt +=
|
|
5590
|
-
prompt +=
|
|
5591
|
-
prompt +=
|
|
5592
|
-
prompt +=
|
|
5593
|
-
prompt +=
|
|
5594
|
-
prompt +=
|
|
5595
|
-
prompt +=
|
|
5596
|
-
prompt +=
|
|
5597
|
-
prompt +=
|
|
5598
|
-
prompt +=
|
|
5599
|
-
prompt +=
|
|
5600
|
-
prompt +=
|
|
5252
|
+
prompt += `- 当前是**迭代级分析**,需产出全部 8 个文档,覆盖需求→技术→测试→评审→风险→依赖→监控→UI规格\n`;
|
|
5253
|
+
if (ctx.platform) {
|
|
5254
|
+
prompt += `- **只分析 ${ctx.platform} 端**:从 CONSTITUTION.md 读取端列表,但只生成 ${ctx.platform} 端的专属文档\n`;
|
|
5255
|
+
prompt += `- 在 020-specs/{功能模块}/${ctx.platform}/ 下写入该端专属文档(ANALYSIS.md、TECH.md、TEST.md 等)\n`;
|
|
5256
|
+
prompt += `- 迭代综合文档写入 020-specs/overview/(REQUIREMENT.md、DEPS.md、RISK.md 等)\n`;
|
|
5257
|
+
prompt += `- **不要生成**其他端的子目录和文档\n`;
|
|
5258
|
+
}
|
|
5259
|
+
// ── v6.61.0+: 阶段专属指令(Phase 2)──
|
|
5260
|
+
if (ctx.phase === '2') {
|
|
5261
|
+
prompt += `## 要求\n\n`;
|
|
5262
|
+
prompt += `### Step 1: 读取全局上下文(Phase 1 产出)\n`;
|
|
5263
|
+
prompt += `依次 Read 以下文件,建立全局技术架构认知:\n`;
|
|
5264
|
+
prompt += `- Read .speccore/CONSTITUTION.md\n`;
|
|
5265
|
+
prompt += `- Read 020-specs/PLATFORMS.md → 获取端列表\n`;
|
|
5266
|
+
prompt += `- Read 020-specs/overview/REQUIREMENT.md → 需求规格\n`;
|
|
5267
|
+
prompt += `- Read 020-specs/overview/ANALYSIS.md → 分析报告\n`;
|
|
5268
|
+
prompt += `- Read 020-specs/overview/TECH.md → 整体技术架构\n`;
|
|
5269
|
+
prompt += `- Read 020-specs/overview/RISK.md、DEPS.md、REVIEW.md、MONITOR.md(如存在)\n\n`;
|
|
5270
|
+
prompt += `### Step 2: 按功能模块组织,为每个功能模块的每个端撰写专属文档(v8.3.1+ 强制四文档)\n`;
|
|
5271
|
+
prompt += `先 Read \`020-specs/overview/FUNCTION_MAP.md\` 获取功能模块清单。\n\n`;
|
|
5272
|
+
prompt += `**分批策略(v8.3.160+)**:\n`;
|
|
5273
|
+
prompt += `- 如功能模块数量 ≤ 2:一次生成所有模块的所有端文档\n`;
|
|
5274
|
+
prompt += `- 如功能模块数量 > 2:一次只处理 **1-2 个功能模块** 的所有端文档,确保质量深入\n`;
|
|
5275
|
+
prompt += `- 剩余模块在输出末尾标注 \`[PENDING: {模块名列表}]\`,供续批处理\n`;
|
|
5276
|
+
prompt += `- 续批命令:\`speccore analyze --prompt -I ${iter} --phase 2\`\n`;
|
|
5277
|
+
prompt += `- **关键原则**:宁可少分析几个模块,也要保证每个模块的分析质量(文档充实、有具体代码引用、表格有数据行)\n\n`;
|
|
5278
|
+
prompt += `每个功能模块涉及的每个端,分别撰写以下 **4 份文档**,缺一不可:\n\n`;
|
|
5279
|
+
prompt += `**文件路径格式**:\`{功能模块名}/{端名}/{文件名}\`(如 \`booking/backend/TECH.md\`、\`booking/h5-mobile/UI_SPEC.md\`)\n`;
|
|
5280
|
+
prompt += `> ⚠️ **严禁**只写 \`{端}/{文件}\` 格式(如 \`backend/TECH.md\`),这会导致所有功能模块的文档混在一起无法区分\n\n`;
|
|
5281
|
+
prompt += `**1. {功能模块}/{端}/TECH.md — 该端在该功能模块下的技术方案(必须对齐 overview/TECH.md 架构)**\n`;
|
|
5282
|
+
prompt += ` - 该端的分层架构、模块划分、核心接口设计(路径/方法/参数/响应/状态码)\n`;
|
|
5283
|
+
prompt += ` - 数据库表结构(字段/类型/索引/约束,如适用)\n`;
|
|
5284
|
+
prompt += ` - 业务规则实现(含边界条件和异常流)\n`;
|
|
5285
|
+
prompt += ` - ⚠️ **必须包含「业务-代码映射」章节**:在 TECH.md 末尾添加表格,列出本端涉及的业务模块及其对应的代码实体(文件/表/API/组件等),关系类型由你根据技术栈自主决定(如 api_controller、uses_table、page、component、route、middleware、interceptor、gateway 等)\n`;
|
|
5286
|
+
prompt += ` - 表格格式:| 业务模块 | 代码实体 | 关系类型 | 说明 |\n`;
|
|
5287
|
+
prompt += ` - 示例:| 会议室档案 | backend/RoomController.java | api_controller | REST 控制器 |\n`;
|
|
5288
|
+
prompt += ` - 示例:| 会议室档案 | admin-web/src/pages/RoomList.vue | page | 列表页 |\n\n`;
|
|
5289
|
+
prompt += `**2. {功能模块}/{端}/TEST.md — 该端在该功能模块下的测试计划**\n`;
|
|
5290
|
+
prompt += ` - 覆盖该端所有功能模块的测试用例(含前置条件、步骤、预期结果)\n`;
|
|
5291
|
+
prompt += ` - 边界值和异常输入测试\n`;
|
|
5292
|
+
prompt += ` - 必须覆盖 REQUIREMENT.md 中该端的验收标准\n\n`;
|
|
5293
|
+
prompt += `**3. {功能模块}/{端}/UI_SPEC.md — 该端在该功能模块下的 UI 规格(仅前端端需要)**\n`;
|
|
5294
|
+
prompt += ` - 页面结构与路由(每个页面的路径、入口、权限)\n`;
|
|
5295
|
+
prompt += ` - 组件清单和字段→UI 映射\n`;
|
|
5296
|
+
prompt += ` - 状态枚举和错误处理策略\n\n`;
|
|
5297
|
+
prompt += `**4. {功能模块}/{端}/DEV_GUIDE.md — 该端在该功能模块下的开发者实现指南(v8.3.1+ 新增强制要求)**\n`;
|
|
5298
|
+
prompt += ` - ⛔ **禁止只写框架/占位符** — 必须是可执行的实现指导\n`;
|
|
5299
|
+
prompt += ` - **本端改造范围清单**:列出本端所有需要新增/修改/删除的文件(相对项目根目录的具体路径),表格表头:| # | 类型 | 文件/目录 | 说明 |\n`;
|
|
5300
|
+
prompt += ` - **本端实施步骤(按依赖排序)**:Step-by-step,具体到文件/函数级,说明每步为什么先做\n`;
|
|
5301
|
+
prompt += ` - **代码级指引**:改哪几行、改什么、改造后的关键代码片段\n`;
|
|
5302
|
+
prompt += ` - **与后端联调**:接口状态、联调顺序、Mock 方案,表格表头:| # | 接口 | 后端状态 | 联调顺序 | Mock 方案 |\n`;
|
|
5303
|
+
prompt += ` - **验证方式**:本端特有的验证命令/操作和通过标准,表格表头:| # | 验证项 | 命令/操作 | 通过标准 |\n`;
|
|
5304
|
+
prompt += ` - **本端坑点**:已知陷阱及解决方式\n`;
|
|
5305
|
+
prompt += ` - **回滚方案**:代码回滚方式、配置回滚方式\n\n`;
|
|
5306
|
+
prompt += `### Step 3: 一致性检查\n`;
|
|
5307
|
+
prompt += `- 各端 TECH.md 的技术选型必须与 overview/TECH.md 一致\n`;
|
|
5308
|
+
prompt += `- UI_SPEC.md 的字段映射必须与后端 API 响应字段一一对应\n`;
|
|
5309
|
+
prompt += `- TEST.md 必须覆盖 REQUIREMENT.md 中该端的验收标准\n\n`;
|
|
5310
|
+
prompt += `### Step 3: 自检 — 确保所有文档已实质填充\n`;
|
|
5311
|
+
prompt += `生成完成后,逐个检查每个文档:\n`;
|
|
5312
|
+
prompt += `- 如果文档中仍含有 \`<!-- SPEC-SKELETON -->\` 标记或空表格 → **必须重新填充**\n`;
|
|
5313
|
+
prompt += `- 如果「改造范围」表格只有表头没有数据行 → **必须补充具体文件路径**\n`;
|
|
5314
|
+
prompt += `- 如果「验证方式」表格只有表头 → **必须补充可执行的命令/操作**\n`;
|
|
5315
|
+
prompt += `- DEV_GUIDE.md 必须有至少 3 个具体文件路径和 3 个可执行验证步骤\n\n`;
|
|
5601
5316
|
prompt += `### 写入方式\n`;
|
|
5602
|
-
prompt +=
|
|
5317
|
+
prompt += `**按功能模块批量写入**(推荐):一次 --apply 写入一个功能模块的所有端文档\n`;
|
|
5318
|
+
prompt += `\`\`\`bash\n`;
|
|
5319
|
+
prompt += `speccore analyze --apply '{"booking/backend/TECH.md":"...","booking/backend/TEST.md":"...","booking/h5-mobile/TECH.md":"...","booking/h5-mobile/UI_SPEC.md":"..."}' -I ${iter}\n`;
|
|
5320
|
+
prompt += `\`\`\`\n\n`;
|
|
5321
|
+
prompt += `**或者逐功能模块-逐端写入**(每端一次 --apply):\n`;
|
|
5322
|
+
prompt += `\`\`\`bash\n`;
|
|
5323
|
+
prompt += `speccore analyze --apply '{"{功能模块}/{端}/TECH.md":"...","{功能模块}/{端}/TEST.md":"..."}' -I ${iter}\n`;
|
|
5324
|
+
prompt += `\`\`\`\n\n`;
|
|
5325
|
+
prompt += `### ⛔ 强制约束(违反则分析无效)\n`;
|
|
5326
|
+
prompt += `- **禁止省略任何一份文档** — 4 份文档(TECH.md + TEST.md + UI_SPEC.md + DEV_GUIDE.md)缺一不可\n`;
|
|
5327
|
+
prompt += `- **禁止输出空表格** — 每个 Markdown 表格必须有至少 1 行数据,只有表头的表格视为未完成\n`;
|
|
5328
|
+
prompt += `- **禁止输出占位符** — 不允许写「待填充」、「TODO」、「...」、「xxx」等占位内容\n`;
|
|
5329
|
+
prompt += `- **DEV_GUIDE.md 最低标准**:改造范围表格 ≥3 行、实施步骤 ≥3 步、验证方式表格 ≥3 行、坑点 ≥2 条\n`;
|
|
5330
|
+
prompt += `- **如果 token 不足**:优先保证 DEV_GUIDE.md 完整,其他文档可精简但不可空\n`;
|
|
5331
|
+
prompt += `- **自检必做**:写入前用 Read 工具检查每个文档,确认不含 \`<!-- SPEC-SKELETON -->\` 且所有表格有数据行\n\n`;
|
|
5603
5332
|
}
|
|
5604
5333
|
else {
|
|
5605
|
-
|
|
5606
|
-
|
|
5607
|
-
|
|
5608
|
-
|
|
5609
|
-
|
|
5610
|
-
|
|
5611
|
-
}
|
|
5612
|
-
|
|
5613
|
-
|
|
5614
|
-
prompt +=
|
|
5615
|
-
|
|
5616
|
-
|
|
5617
|
-
|
|
5618
|
-
|
|
5619
|
-
|
|
5620
|
-
|
|
5621
|
-
|
|
5622
|
-
|
|
5623
|
-
prompt +=
|
|
5624
|
-
|
|
5625
|
-
prompt +=
|
|
5626
|
-
prompt +=
|
|
5627
|
-
|
|
5628
|
-
|
|
5629
|
-
|
|
5630
|
-
|
|
5631
|
-
|
|
5632
|
-
|
|
5633
|
-
|
|
5634
|
-
|
|
5635
|
-
prompt += ` - 该端的分层架构、模块划分、核心接口设计(路径/方法/参数/响应/状态码)\n`;
|
|
5636
|
-
prompt += ` - 数据库表结构(字段/类型/索引/约束,如适用)\n`;
|
|
5637
|
-
prompt += ` - 业务规则实现(含边界条件和异常流)\n`;
|
|
5638
|
-
prompt += ` - ⚠️ **必须包含「业务-代码映射」章节**:在 TECH.md 末尾添加表格,列出本端涉及的业务模块及其对应的代码实体(文件/表/API/组件等),关系类型由你根据技术栈自主决定(如 api_controller、uses_table、page、component、route、middleware、interceptor、gateway 等)\n`;
|
|
5639
|
-
prompt += ` - 表格格式:| 业务模块 | 代码实体 | 关系类型 | 说明 |\n`;
|
|
5640
|
-
prompt += ` - 示例:| 会议室档案 | backend/RoomController.java | api_controller | REST 控制器 |\n`;
|
|
5641
|
-
prompt += ` - 示例:| 会议室档案 | admin-web/src/pages/RoomList.vue | page | 列表页 |\n\n`;
|
|
5642
|
-
prompt += `**2. {功能模块}/{端}/TEST.md — 该端在该功能模块下的测试计划**\n`;
|
|
5643
|
-
prompt += ` - 覆盖该端所有功能模块的测试用例(含前置条件、步骤、预期结果)\n`;
|
|
5644
|
-
prompt += ` - 边界值和异常输入测试\n`;
|
|
5645
|
-
prompt += ` - 必须覆盖 REQUIREMENT.md 中该端的验收标准\n\n`;
|
|
5646
|
-
prompt += `**3. {功能模块}/{端}/UI_SPEC.md — 该端在该功能模块下的 UI 规格(仅前端端需要)**\n`;
|
|
5647
|
-
prompt += ` - 页面结构与路由(每个页面的路径、入口、权限)\n`;
|
|
5648
|
-
prompt += ` - 组件清单和字段→UI 映射\n`;
|
|
5649
|
-
prompt += ` - 状态枚举和错误处理策略\n\n`;
|
|
5650
|
-
prompt += `**4. {功能模块}/{端}/DEV_GUIDE.md — 该端在该功能模块下的开发者实现指南(v8.3.1+ 新增强制要求)**\n`;
|
|
5651
|
-
prompt += ` - ⛔ **禁止只写框架/占位符** — 必须是可执行的实现指导\n`;
|
|
5652
|
-
prompt += ` - **本端改造范围清单**:列出本端所有需要新增/修改/删除的文件(相对项目根目录的具体路径),表格表头:| # | 类型 | 文件/目录 | 说明 |\n`;
|
|
5653
|
-
prompt += ` - **本端实施步骤(按依赖排序)**:Step-by-step,具体到文件/函数级,说明每步为什么先做\n`;
|
|
5654
|
-
prompt += ` - **代码级指引**:改哪几行、改什么、改造后的关键代码片段\n`;
|
|
5655
|
-
prompt += ` - **与后端联调**:接口状态、联调顺序、Mock 方案,表格表头:| # | 接口 | 后端状态 | 联调顺序 | Mock 方案 |\n`;
|
|
5656
|
-
prompt += ` - **验证方式**:本端特有的验证命令/操作和通过标准,表格表头:| # | 验证项 | 命令/操作 | 通过标准 |\n`;
|
|
5657
|
-
prompt += ` - **本端坑点**:已知陷阱及解决方式\n`;
|
|
5658
|
-
prompt += ` - **回滚方案**:代码回滚方式、配置回滚方式\n\n`;
|
|
5659
|
-
prompt += `### Step 3: 一致性检查\n`;
|
|
5660
|
-
prompt += `- 各端 TECH.md 的技术选型必须与 overview/TECH.md 一致\n`;
|
|
5661
|
-
prompt += `- UI_SPEC.md 的字段映射必须与后端 API 响应字段一一对应\n`;
|
|
5662
|
-
prompt += `- TEST.md 必须覆盖 REQUIREMENT.md 中该端的验收标准\n\n`;
|
|
5663
|
-
prompt += `### Step 3: 自检 — 确保所有文档已实质填充\n`;
|
|
5664
|
-
prompt += `生成完成后,逐个检查每个文档:\n`;
|
|
5665
|
-
prompt += `- 如果文档中仍含有 \`<!-- SPEC-SKELETON -->\` 标记或空表格 → **必须重新填充**\n`;
|
|
5666
|
-
prompt += `- 如果「改造范围」表格只有表头没有数据行 → **必须补充具体文件路径**\n`;
|
|
5667
|
-
prompt += `- 如果「验证方式」表格只有表头 → **必须补充可执行的命令/操作**\n`;
|
|
5668
|
-
prompt += `- DEV_GUIDE.md 必须有至少 3 个具体文件路径和 3 个可执行验证步骤\n\n`;
|
|
5669
|
-
prompt += `### 写入方式\n`;
|
|
5670
|
-
prompt += `**按功能模块批量写入**(推荐):一次 --apply 写入一个功能模块的所有端文档\n`;
|
|
5671
|
-
prompt += `\`\`\`bash\n`;
|
|
5672
|
-
prompt += `speccore analyze --apply '{"booking/backend/TECH.md":"...","booking/backend/TEST.md":"...","booking/h5-mobile/TECH.md":"...","booking/h5-mobile/UI_SPEC.md":"..."}' -I ${iter}\n`;
|
|
5673
|
-
prompt += `\`\`\`\n\n`;
|
|
5674
|
-
prompt += `**或者逐功能模块-逐端写入**(每端一次 --apply):\n`;
|
|
5675
|
-
prompt += `\`\`\`bash\n`;
|
|
5676
|
-
prompt += `speccore analyze --apply '{"{功能模块}/{端}/TECH.md":"...","{功能模块}/{端}/TEST.md":"..."}' -I ${iter}\n`;
|
|
5677
|
-
prompt += `\`\`\`\n\n`;
|
|
5678
|
-
prompt += `### ⛔ 强制约束(违反则分析无效)\n`;
|
|
5679
|
-
prompt += `- **禁止省略任何一份文档** — 4 份文档(TECH.md + TEST.md + UI_SPEC.md + DEV_GUIDE.md)缺一不可\n`;
|
|
5680
|
-
prompt += `- **禁止输出空表格** — 每个 Markdown 表格必须有至少 1 行数据,只有表头的表格视为未完成\n`;
|
|
5681
|
-
prompt += `- **禁止输出占位符** — 不允许写「待填充」、「TODO」、「...」、「xxx」等占位内容\n`;
|
|
5682
|
-
prompt += `- **DEV_GUIDE.md 最低标准**:改造范围表格 ≥3 行、实施步骤 ≥3 步、验证方式表格 ≥3 行、坑点 ≥2 条\n`;
|
|
5683
|
-
prompt += `- **如果 token 不足**:优先保证 DEV_GUIDE.md 完整,其他文档可精简但不可空\n`;
|
|
5684
|
-
prompt += `- **自检必做**:写入前用 Read 工具检查每个文档,确认不含 \`<!-- SPEC-SKELETON -->\` 且所有表格有数据行\n\n`;
|
|
5685
|
-
}
|
|
5686
|
-
else {
|
|
5687
|
-
// v6.61.0+: 一次性生成所有文档(global/ + {端}/)
|
|
5688
|
-
prompt += `## 要求\n1. Read .speccore/PATTERNS/TEMPLATES/specs/ 下的专业模板(如目录不存在或为空,用你的专业知识自由撰写,绝不允许产出一行垃圾)\n`;
|
|
5689
|
-
const templateMap = {
|
|
5690
|
-
'ANALYSIS.md': 'ANALYSIS-template.md', 'TECH.md': 'TECH-template.md', 'TEST.md': 'TEST-template.md',
|
|
5691
|
-
'REVIEW.md': 'REVIEW-template.md', 'RISK.md': 'RISK-template.md', 'DEPS.md': 'DEPS-template.md',
|
|
5692
|
-
'MONITOR.md': 'MONITOR-template.md', 'UI_SPEC.md': 'UI_SPEC-template.md'
|
|
5693
|
-
};
|
|
5694
|
-
for (const doc of taskDocs) {
|
|
5695
|
-
const tpl = templateMap[doc[0]] || '';
|
|
5696
|
-
prompt += ` - ${doc[0]} → 参考 ${tpl}\n`;
|
|
5697
|
-
}
|
|
5698
|
-
prompt += `2. 读取全局层产物(建立全局视角,按需 Read):\n`;
|
|
5699
|
-
prompt += ` \`.speccore/GLOBAL/\` 下已有系统级产物,按需读取了解当前状态:\n`;
|
|
5700
|
-
prompt += ` - \`requirements/REQUIREMENT.md\`、\`overview/FUNCTION_MAP.md\`、\`overview/API_CONTRACT.yaml\`\n`;
|
|
5701
|
-
prompt += ` - \`overview/ARCHITECTURE.md\`、\`platforms/{端}/_INDEX.md\`、\`platforms/_shared/_ASSOCIATION.md\`\n`;
|
|
5702
|
-
prompt += ` - 如不存在则跳过\n\n`;
|
|
5703
|
-
// v8.3.160+: 迭代级分析结合源码上下文
|
|
5704
|
-
if (ctx.withCode) {
|
|
5705
|
-
prompt += `2b. **源码上下文(--with-code)**:\n`;
|
|
5706
|
-
// v8.3.160+: 大项目按端分段读取,避免一次性加载全量结构化数据
|
|
5707
|
-
prompt += ` - 先 Read \`.speccore/cache/structured-data.json\` 的 stats 部分(了解各端规模)\n`;
|
|
5708
|
-
prompt += ` - **按需读取各端分段文件**:\`.speccore/cache/structured-data.{端名}.json\`(只读取本功能模块涉及的端)\n`;
|
|
5709
|
-
// v8.3.248+: 直接注入各端工程路径表(此前仅提示 AI「从 CONSTITUTION.md 获取源码路径」,
|
|
5710
|
-
// AI 需自行翻表定位;现在直接给出,减少一步间接查找、杜绝瞎猜)
|
|
5711
|
-
try {
|
|
5712
|
-
const projectInfoMap = await (0, spec_paths_1.parseProjectInfo)();
|
|
5713
|
-
if (projectInfoMap.size > 0) {
|
|
5714
|
-
prompt += ` - **各端工程路径**(结合源码分析时按此定位,CONSTITUTION.md 工程表权威):\n`;
|
|
5715
|
-
for (const [identifier, info] of projectInfoMap) {
|
|
5716
|
-
prompt += ` - \`${identifier}\`(${info.projectType || '-'})→ \`${info.srcPath}\`\n`;
|
|
5717
|
-
}
|
|
5334
|
+
// v6.61.0+: 一次性生成所有文档(global/ + {端}/)
|
|
5335
|
+
prompt += `## 要求\n1. Read .speccore/PATTERNS/TEMPLATES/specs/ 下的专业模板(如目录不存在或为空,用你的专业知识自由撰写,绝不允许产出一行垃圾)\n`;
|
|
5336
|
+
const templateMap = {
|
|
5337
|
+
'ANALYSIS.md': 'ANALYSIS-template.md', 'TECH.md': 'TECH-template.md', 'TEST.md': 'TEST-template.md',
|
|
5338
|
+
'REVIEW.md': 'REVIEW-template.md', 'RISK.md': 'RISK-template.md', 'DEPS.md': 'DEPS-template.md',
|
|
5339
|
+
'MONITOR.md': 'MONITOR-template.md', 'UI_SPEC.md': 'UI_SPEC-template.md'
|
|
5340
|
+
};
|
|
5341
|
+
for (const doc of taskDocs) {
|
|
5342
|
+
const tpl = templateMap[doc[0]] || '';
|
|
5343
|
+
prompt += ` - ${doc[0]} → 参考 ${tpl}\n`;
|
|
5344
|
+
}
|
|
5345
|
+
prompt += `2. 读取全局层产物(建立全局视角,按需 Read):\n`;
|
|
5346
|
+
prompt += ` \`.speccore/GLOBAL/\` 下已有系统级产物,按需读取了解当前状态:\n`;
|
|
5347
|
+
prompt += ` - \`requirements/REQUIREMENT.md\`、\`overview/FUNCTION_MAP.md\`、\`overview/API_CONTRACT.yaml\`\n`;
|
|
5348
|
+
prompt += ` - \`overview/ARCHITECTURE.md\`、\`platforms/{端}/_INDEX.md\`、\`platforms/_shared/_ASSOCIATION.md\`\n`;
|
|
5349
|
+
prompt += ` - 如不存在则跳过\n\n`;
|
|
5350
|
+
// v8.3.160+: 迭代级分析结合源码上下文
|
|
5351
|
+
if (ctx.withCode) {
|
|
5352
|
+
prompt += `2b. **源码上下文(--with-code)**:\n`;
|
|
5353
|
+
// v8.3.160+: 大项目按端分段读取,避免一次性加载全量结构化数据
|
|
5354
|
+
prompt += ` - 先 Read \`.speccore/cache/structured-data.json\` 的 stats 部分(了解各端规模)\n`;
|
|
5355
|
+
prompt += ` - **按需读取各端分段文件**:\`.speccore/cache/structured-data.{端名}.json\`(只读取本功能模块涉及的端)\n`;
|
|
5356
|
+
// v8.3.248+: 直接注入各端工程路径表(此前仅提示 AI「从 CONSTITUTION.md 获取源码路径」,
|
|
5357
|
+
// AI 需自行翻表定位;现在直接给出,减少一步间接查找、杜绝瞎猜)
|
|
5358
|
+
try {
|
|
5359
|
+
const projectInfoMap = await (0, spec_paths_1.parseProjectInfo)();
|
|
5360
|
+
if (projectInfoMap.size > 0) {
|
|
5361
|
+
prompt += ` - **各端工程路径**(结合源码分析时按此定位,CONSTITUTION.md 工程表权威):\n`;
|
|
5362
|
+
for (const [identifier, info] of projectInfoMap) {
|
|
5363
|
+
prompt += ` - \`${identifier}\`(${info.projectType || '-'})→ \`${info.srcPath}\`\n`;
|
|
5718
5364
|
}
|
|
5719
5365
|
}
|
|
5720
|
-
catch { /* 路径解析失败不阻断分析 */ }
|
|
5721
|
-
prompt += ` - 再按需 Read 各端源码中与本功能模块相关的关键文件,重点关注:接口定义、数据模型、业务逻辑实现、状态管理\n\n`;
|
|
5722
|
-
}
|
|
5723
|
-
prompt += `3. 读取迭代需求文档(按功能模块组织):\n`;
|
|
5724
|
-
prompt += ` a. 先读 010-requirements/INDEX.md — 获取功能模块清单\n`;
|
|
5725
|
-
prompt += ` b. **按功能模块逐一读取**:每个功能模块独立分析,不要跨模块混淆\n`;
|
|
5726
|
-
prompt += ` - 读取该模块对应的 010-requirements/converted/*.md 或 features/{模块}/SOURCE.md(旧项目为 README.md)\n`;
|
|
5727
|
-
prompt += ` - 读取该模块涉及的原型文件(HTML/图片/链接)\n`;
|
|
5728
|
-
prompt += ` c. **文档长度自适应**:如果单个需求文档超过 5000 字,先快速扫描目录和章节标题,标记关键章节,再深入阅读\n`;
|
|
5729
|
-
prompt += `4. **按功能模块逐一撰写文档(重要)**:\n`;
|
|
5730
|
-
prompt += ` - 每个功能模块独立生成一套综合文档,放在 \`{功能模块}/overview/\` 目录下\n`;
|
|
5731
|
-
prompt += ` - 不要生成全局的 \`overview/REQUIREMENT.md\`(不要把所有模块拟合在一起)\n`;
|
|
5732
|
-
prompt += ` - 全局 \`overview/\` 只保留 FUNCTION_MAP.md、INTERACTION_MAP.md、PLATFORMS.md 作为索引\n`;
|
|
5733
|
-
prompt += ` - 示例:\`booking/overview/REQUIREMENT.md\`、\`booking/overview/ANALYSIS.md\`、\`payment/overview/REQUIREMENT.md\` 等\n`;
|
|
5734
|
-
prompt += `5. **文档忠实度约束(最高优先级)**:\n`;
|
|
5735
|
-
prompt += ` - **严禁臆造**:只能写需求文档中明确提及的功能、接口、页面、字段、业务规则\n`;
|
|
5736
|
-
prompt += ` - **严禁扩展**:不要基于迭代名称或你的知识补充文档中未提及的内容\n`;
|
|
5737
|
-
prompt += ` - **严禁推断**:不要从一句话推断出整个功能模块,只写文档中明确描述的内容\n`;
|
|
5738
|
-
prompt += ` - **边界处理**:如果文档对某功能描述不完整,标注"文档未充分描述",不要自行脑补完整方案\n`;
|
|
5739
|
-
prompt += ` - **交叉验证**:每写一个功能点,回头检查需求文档中是否有对应描述,没有则删除\n`;
|
|
5740
|
-
prompt += `6. 每个文档都要具体内容(禁止"待填充")\n`;
|
|
5741
|
-
prompt += `7. **辅助文档强制要求(v8.3.1+ 修复空模板问题)**:以下文档常被 AI 遗漏或敷衍,必须同等重视:\n`;
|
|
5742
|
-
prompt += ` - **FUNCTION_MAP.md**:功能单元 × 端映射表,必须用 Markdown 表格,每行一个功能单元,不允许合并。表头:| # | 功能单元 | 涉及端 | 全局对比 | 共享能力 | 依赖任务 | 说明 |\n`;
|
|
5743
|
-
prompt += ` - **INTERACTION_MAP.md**:每个功能单元一个 Mermaid sequenceDiagram,展示跨端交互时序,箭头标注接口路径\n`;
|
|
5744
|
-
prompt += ` - **DEV_GUIDE.md(迭代级)**:全局实施指导,必须包含:分支策略、改造范围清单(具体到文件路径)、改造顺序与依赖、代码级指引(含改造前后对比)、接口契约对照表、每步验证方式、回滚方案、常见坑点\n`;
|
|
5745
|
-
prompt += ` - **MONITOR.md**:必须按 Fatal/Critical/Warning/Info 四级定义告警规则,不能只列指标名称\n`;
|
|
5746
|
-
prompt += ` - **REVIEW.md**:安全检查必须逐接口列出鉴权需求,不能笼统写"需要鉴权"\n`;
|
|
5747
|
-
prompt += ` ⛔ **自检规则**:生成完成后检查每个文档,如果仍含 \`<!-- SPEC-SKELETON -->\` 或空表格只有表头 → 必须重新填充\n`;
|
|
5748
|
-
prompt += `8. **端发现(重要)**:先确定项目有哪些端,再按端组织文档\n`;
|
|
5749
|
-
prompt += ` - 第 1 步:Read .speccore/CONSTITUTION.md\n`;
|
|
5750
|
-
prompt += ` - 第 2 步:从「## 端列表」章节提取端名(这是全局权威来源)\n`;
|
|
5751
|
-
prompt += ` - 第 3 步:如果没有「端列表」章节,从「对应需求端」列提取\n`;
|
|
5752
|
-
prompt += ` - 第 4 步:如果以上都无法确定,根据需求文档内容判断\n`;
|
|
5753
|
-
prompt += ` - 第 5 步:将发现的端列表写入 020-specs/PLATFORMS.md\n`;
|
|
5754
|
-
// v6.70.0+: REQUIREMENT.md 以产品视角撰写(不按端分章节)
|
|
5755
|
-
// v8.3.160+: 压缩写作风格说明
|
|
5756
|
-
prompt += `9. **REQUIREMENT.md 写作风格**:产品/用户视角,按业务场景组织章节(不按端分节)\n`;
|
|
5757
|
-
prompt += ` - 必须包含:产品愿景、用户画像、核心场景地图(标注优先级)、功能全景图、功能优先级矩阵(P0/P1/P2)、发布里程碑、风险预判\n`;
|
|
5758
|
-
prompt += ` - 场景描述格式:用户操作 → 系统响应 → 业务规则 → 边界条件 → 验收标准\n`;
|
|
5759
|
-
prompt += ` - ✅ 正确:「用户点击预订,系统检查可用性,如可用则锁定并创建订单」\n`;
|
|
5760
|
-
prompt += ` - ❌ 错误:「后端新增 /api/bookings 接口,接收 roomId」→ 技术细节留给 TECH.md\n`;
|
|
5761
|
-
prompt += ` - 端的信息只在「功能模块清单」表格中标注,正文不区分端\n`;
|
|
5762
|
-
// v6.49.14+: 功能模块清单必须含涉及端列 + 来源链接
|
|
5763
|
-
// v6.71.3+: 增加「与全局层对比」列
|
|
5764
|
-
prompt += `10. **功能模块清单(重要)**:每个功能模块的 \`overview/REQUIREMENT.md\` 都必须包含功能模块清单表格\n`;
|
|
5765
|
-
prompt += ` - 表格格式:| # | 功能模块 | 涉及端 | 全局对比 | 来源 | 说明 |\n`;
|
|
5766
|
-
prompt += ` - 「涉及端」:每个模块标注需要**新开发工作**的端(标准端名,逗号分隔)\n`;
|
|
5767
|
-
prompt += ` - 「涉及」= 该端需要写新接口/新页面/新逻辑\n`;
|
|
5768
|
-
prompt += ` - 「不涉及」= 只是提到、调用已有接口、纯展示 → 不标注\n`;
|
|
5769
|
-
prompt += ` - 端名必须与 CONSTITUTION.md「端列表」中的标准端名完全匹配\n`;
|
|
5770
|
-
prompt += ` - 「全局对比」:该功能模块与全局层已有功能的关系(必须标注)\n`;
|
|
5771
|
-
prompt += ` - 「新增」:全局层不存在,本迭代全新开发\n`;
|
|
5772
|
-
prompt += ` - 「扩展」:全局层已有基础功能,本迭代增加新字段/新接口/新页面\n`;
|
|
5773
|
-
prompt += ` - 「重构」:全局层已有,本迭代修改实现方式(不新增功能)\n`;
|
|
5774
|
-
prompt += ` - 「复用」:全局层已有,本迭代直接使用(无需开发)\n`;
|
|
5775
|
-
prompt += ` - 示例:「扩展:增加会议室设备管理」或「新增」\n`;
|
|
5776
|
-
prompt += ` - 「来源」:该功能模块在需求文档中的具体位置,用 Markdown 链接格式\n`;
|
|
5777
|
-
prompt += ` - 格式:[文档名](相对路径#章节锚点),如 [PRD v2.0](../010-requirements/sources/PRD-v2.0.md#3-2-会议室管理)\n`;
|
|
5778
|
-
prompt += ` - 如果模块来自 converted 文档:[xxx需求](../010-requirements/converted/xxx.md#相关章节)\n`;
|
|
5779
|
-
prompt += ` - 如果来自 features 目录:[xxx功能](../010-requirements/features/xxx/SOURCE.md)\n`;
|
|
5780
|
-
prompt += ` - 目的是让阅读者能直接点击跳转到原始需求位置\n`;
|
|
5781
|
-
prompt += ` - 示例:| M-01 | 会议室档案管理 | room-service, admin-web | 扩展:增加设备管理 | [PRD v2.0](../010-requirements/sources/PRD-v2.0.md#2-1) | 会议室 CRUD、设备管理 |\n`;
|
|
5782
|
-
prompt += ` - split 命令将读取「涉及端」列来决定创建哪些端的子任务目录\n`;
|
|
5783
|
-
// v6.70.0+: 跨端功能映射表(FUNCTION_MAP.md)
|
|
5784
|
-
// v6.71.3+: 增加与全局层关联分析
|
|
5785
|
-
prompt += `7a. **迭代需求与全局层关联分析(重要)**:在每个功能模块的 \`overview/REQUIREMENT.md\` 中,必须对比全局层产物\n`;
|
|
5786
|
-
prompt += ` - 对比迭代需求中的功能模块 vs .speccore/GLOBAL/overview/FUNCTION_MAP.md 中的功能单元\n`;
|
|
5787
|
-
prompt += ` - 标注每个功能模块的「全局对比」类型(新增/扩展/重构/复用)\n`;
|
|
5788
|
-
prompt += ` - 识别冲突:如迭代需求修改了全局层已有接口的字段/路径 → 在 RISK.md 中标注\n`;
|
|
5789
|
-
prompt += ` - 识别依赖:如迭代的新功能依赖全局层的某个功能 → 在 FUNCTION_MAP.md「依赖任务」中标注\n\n`;
|
|
5790
|
-
prompt += `7b. **跨端功能映射表(重要)**:全局索引 \`overview/FUNCTION_MAP.md\` 汇总所有功能模块的映射关系\n`;
|
|
5791
|
-
prompt += ` - 这是 split 阶段的核心输入,决定任务如何按功能单元拆分\n`;
|
|
5792
|
-
prompt += ` - 表格格式:| # | 功能单元 | 涉及端 | 全局对比 | 共享能力 | 依赖任务 | 说明 |\n`;
|
|
5793
|
-
prompt += ` - 「功能单元」必须与 REQUIREMENT.md 功能模块清单一一对应,不允许合并\n`;
|
|
5794
|
-
prompt += ` - 「涉及端」标注所有需要新开发工作的端(标准端名,逗号分隔)\n`;
|
|
5795
|
-
prompt += ` - 「全局对比」标注与全局层的关系(新增/扩展/重构/复用)\n`;
|
|
5796
|
-
prompt += ` - 「共享能力」标注跨任务共享的通用组件(如"审批引擎"、"消息通知"),无则填"无"\n`;
|
|
5797
|
-
prompt += ` - 「依赖任务」标注当前迭代内该功能单元依赖的其他功能单元编号\n`;
|
|
5798
|
-
prompt += ` - 「说明」描述跨端交互关系(如"admin-web 调用 booking-service 的 /api/rooms 接口")\n`;
|
|
5799
|
-
prompt += ` - **示例**:| M-01 | 会议室档案管理 | room-service, admin-web | 扩展 | 无 | 无 | admin-web 调用 room-service CRUD 接口 |\n`;
|
|
5800
|
-
prompt += ` - **错误示例**(禁止):将"审批流程"和"定时任务"合并为一行\n`;
|
|
5801
|
-
prompt += ` - FUNCTION_MAP.md 生成后,split 将**严格按此表**创建任务目录,不再由 AI 推断\n`;
|
|
5802
|
-
// v6.70.0+: 跨端交互图谱(INTERACTION_MAP.md)
|
|
5803
|
-
prompt += `7c. **跨端交互图谱(重要)**:全局索引 \`overview/INTERACTION_MAP.md\` 汇总所有功能模块的交互时序\n`;
|
|
5804
|
-
prompt += ` - 按功能单元组织,每个功能单元一个 Mermaid sequenceDiagram\n`;
|
|
5805
|
-
prompt += ` - 展示完整的业务交互时序:用户操作 → 前端处理 → 后端调用 → 数据返回\n`;
|
|
5806
|
-
prompt += ` - 明确标出后端服务之间的内部调用(产品文档写"系统处理"的地方)\n`;
|
|
5807
|
-
prompt += ` - 箭头标注接口路径,标注 [contract] 表示接口在 API_CONTRACT.yaml 中有定义\n`;
|
|
5808
|
-
prompt += ` - 序列图后附「接口契约索引」表格:步骤号、接口、消费者端、提供者端\n`;
|
|
5809
|
-
prompt += ` - 如涉及状态变更,附「状态流转」表格\n`;
|
|
5810
|
-
prompt += ` - INTERACTION_MAP.md 是前后端开发者的共同参考,补全产品文档中隐含的技术交互\n`;
|
|
5811
|
-
// 注入工程类型信息(v6.49.0+)
|
|
5812
|
-
// v8.3.160+: 精简为路径引用,具体维度让 AI 从 CONSTITUTION.md 读取
|
|
5813
|
-
const platformTypes = await (0, spec_paths_2.parsePlatformTypes)();
|
|
5814
|
-
if (platformTypes.size > 0) {
|
|
5815
|
-
prompt += `8. **工程类型识别**:CONSTITUTION.md 已配置各端的工程类型,请 Read 后据此生成针对性内容\n`;
|
|
5816
|
-
prompt += ` | 工程标识 | 工程类型 |\n`;
|
|
5817
|
-
prompt += ` | :--- | :--- |\n`;
|
|
5818
|
-
for (const [name, type] of platformTypes) {
|
|
5819
|
-
prompt += ` | ${name} | ${type} |\n`;
|
|
5820
|
-
}
|
|
5821
|
-
prompt += `\n`;
|
|
5822
5366
|
}
|
|
5823
|
-
|
|
5824
|
-
prompt +=
|
|
5825
|
-
|
|
5826
|
-
|
|
5827
|
-
|
|
5828
|
-
|
|
5829
|
-
|
|
5830
|
-
|
|
5831
|
-
|
|
5832
|
-
|
|
5833
|
-
|
|
5834
|
-
|
|
5835
|
-
|
|
5836
|
-
|
|
5837
|
-
|
|
5838
|
-
|
|
5839
|
-
|
|
5840
|
-
|
|
5841
|
-
|
|
5842
|
-
|
|
5843
|
-
|
|
5844
|
-
|
|
5845
|
-
|
|
5846
|
-
|
|
5847
|
-
|
|
5848
|
-
|
|
5849
|
-
|
|
5850
|
-
|
|
5851
|
-
|
|
5852
|
-
|
|
5853
|
-
|
|
5854
|
-
|
|
5855
|
-
|
|
5856
|
-
|
|
5857
|
-
|
|
5858
|
-
|
|
5859
|
-
|
|
5860
|
-
|
|
5861
|
-
|
|
5862
|
-
|
|
5863
|
-
|
|
5864
|
-
|
|
5865
|
-
|
|
5866
|
-
|
|
5867
|
-
|
|
5868
|
-
|
|
5869
|
-
|
|
5870
|
-
|
|
5871
|
-
|
|
5872
|
-
|
|
5873
|
-
|
|
5874
|
-
|
|
5875
|
-
|
|
5876
|
-
|
|
5877
|
-
|
|
5878
|
-
|
|
5879
|
-
|
|
5880
|
-
|
|
5881
|
-
|
|
5882
|
-
|
|
5883
|
-
|
|
5884
|
-
|
|
5885
|
-
|
|
5886
|
-
|
|
5887
|
-
|
|
5888
|
-
|
|
5889
|
-
|
|
5890
|
-
|
|
5891
|
-
|
|
5892
|
-
|
|
5893
|
-
|
|
5894
|
-
|
|
5895
|
-
|
|
5896
|
-
|
|
5897
|
-
|
|
5898
|
-
|
|
5367
|
+
catch { /* 路径解析失败不阻断分析 */ }
|
|
5368
|
+
prompt += ` - 再按需 Read 各端源码中与本功能模块相关的关键文件,重点关注:接口定义、数据模型、业务逻辑实现、状态管理\n\n`;
|
|
5369
|
+
}
|
|
5370
|
+
prompt += `3. 读取迭代需求文档(按功能模块组织):\n`;
|
|
5371
|
+
prompt += ` a. 先读 010-requirements/INDEX.md — 获取功能模块清单\n`;
|
|
5372
|
+
prompt += ` b. **按功能模块逐一读取**:每个功能模块独立分析,不要跨模块混淆\n`;
|
|
5373
|
+
prompt += ` - 读取该模块对应的 010-requirements/converted/*.md 或 features/{模块}/SOURCE.md(旧项目为 README.md)\n`;
|
|
5374
|
+
prompt += ` - 读取该模块涉及的原型文件(HTML/图片/链接)\n`;
|
|
5375
|
+
prompt += ` c. **文档长度自适应**:如果单个需求文档超过 5000 字,先快速扫描目录和章节标题,标记关键章节,再深入阅读\n`;
|
|
5376
|
+
prompt += `4. **按功能模块逐一撰写文档(重要)**:\n`;
|
|
5377
|
+
prompt += ` - 每个功能模块独立生成一套综合文档,放在 \`{功能模块}/overview/\` 目录下\n`;
|
|
5378
|
+
prompt += ` - 不要生成全局的 \`overview/REQUIREMENT.md\`(不要把所有模块拟合在一起)\n`;
|
|
5379
|
+
prompt += ` - 全局 \`overview/\` 只保留 FUNCTION_MAP.md、INTERACTION_MAP.md、PLATFORMS.md 作为索引\n`;
|
|
5380
|
+
prompt += ` - 示例:\`booking/overview/REQUIREMENT.md\`、\`booking/overview/ANALYSIS.md\`、\`payment/overview/REQUIREMENT.md\` 等\n`;
|
|
5381
|
+
prompt += `5. **文档忠实度约束(最高优先级)**:\n`;
|
|
5382
|
+
prompt += ` - **严禁臆造**:只能写需求文档中明确提及的功能、接口、页面、字段、业务规则\n`;
|
|
5383
|
+
prompt += ` - **严禁扩展**:不要基于迭代名称或你的知识补充文档中未提及的内容\n`;
|
|
5384
|
+
prompt += ` - **严禁推断**:不要从一句话推断出整个功能模块,只写文档中明确描述的内容\n`;
|
|
5385
|
+
prompt += ` - **边界处理**:如果文档对某功能描述不完整,标注"文档未充分描述",不要自行脑补完整方案\n`;
|
|
5386
|
+
prompt += ` - **交叉验证**:每写一个功能点,回头检查需求文档中是否有对应描述,没有则删除\n`;
|
|
5387
|
+
prompt += `6. 每个文档都要具体内容(禁止"待填充")\n`;
|
|
5388
|
+
prompt += `7. **辅助文档强制要求(v8.3.1+ 修复空模板问题)**:以下文档常被 AI 遗漏或敷衍,必须同等重视:\n`;
|
|
5389
|
+
prompt += ` - **FUNCTION_MAP.md**:功能单元 × 端映射表,必须用 Markdown 表格,每行一个功能单元,不允许合并。表头:| # | 功能单元 | 涉及端 | 全局对比 | 共享能力 | 依赖任务 | 说明 |\n`;
|
|
5390
|
+
prompt += ` - **INTERACTION_MAP.md**:每个功能单元一个 Mermaid sequenceDiagram,展示跨端交互时序,箭头标注接口路径\n`;
|
|
5391
|
+
prompt += ` - **DEV_GUIDE.md(迭代级)**:全局实施指导,必须包含:分支策略、改造范围清单(具体到文件路径)、改造顺序与依赖、代码级指引(含改造前后对比)、接口契约对照表、每步验证方式、回滚方案、常见坑点\n`;
|
|
5392
|
+
prompt += ` - **MONITOR.md**:必须按 Fatal/Critical/Warning/Info 四级定义告警规则,不能只列指标名称\n`;
|
|
5393
|
+
prompt += ` - **REVIEW.md**:安全检查必须逐接口列出鉴权需求,不能笼统写"需要鉴权"\n`;
|
|
5394
|
+
prompt += ` ⛔ **自检规则**:生成完成后检查每个文档,如果仍含 \`<!-- SPEC-SKELETON -->\` 或空表格只有表头 → 必须重新填充\n`;
|
|
5395
|
+
prompt += `8. **端发现(重要)**:先确定项目有哪些端,再按端组织文档\n`;
|
|
5396
|
+
prompt += ` - 第 1 步:Read .speccore/CONSTITUTION.md\n`;
|
|
5397
|
+
prompt += ` - 第 2 步:从「## 端列表」章节提取端名(这是全局权威来源)\n`;
|
|
5398
|
+
prompt += ` - 第 3 步:如果没有「端列表」章节,从「对应需求端」列提取\n`;
|
|
5399
|
+
prompt += ` - 第 4 步:如果以上都无法确定,根据需求文档内容判断\n`;
|
|
5400
|
+
prompt += ` - 第 5 步:将发现的端列表写入 020-specs/PLATFORMS.md\n`;
|
|
5401
|
+
// v6.70.0+: REQUIREMENT.md 以产品视角撰写(不按端分章节)
|
|
5402
|
+
// v8.3.160+: 压缩写作风格说明
|
|
5403
|
+
prompt += `9. **REQUIREMENT.md 写作风格**:产品/用户视角,按业务场景组织章节(不按端分节)\n`;
|
|
5404
|
+
prompt += ` - 必须包含:产品愿景、用户画像、核心场景地图(标注优先级)、功能全景图、功能优先级矩阵(P0/P1/P2)、发布里程碑、风险预判\n`;
|
|
5405
|
+
prompt += ` - 场景描述格式:用户操作 → 系统响应 → 业务规则 → 边界条件 → 验收标准\n`;
|
|
5406
|
+
prompt += ` - ✅ 正确:「用户点击预订,系统检查可用性,如可用则锁定并创建订单」\n`;
|
|
5407
|
+
prompt += ` - ❌ 错误:「后端新增 /api/bookings 接口,接收 roomId」→ 技术细节留给 TECH.md\n`;
|
|
5408
|
+
prompt += ` - 端的信息只在「功能模块清单」表格中标注,正文不区分端\n`;
|
|
5409
|
+
// v6.49.14+: 功能模块清单必须含涉及端列 + 来源链接
|
|
5410
|
+
// v6.71.3+: 增加「与全局层对比」列
|
|
5411
|
+
prompt += `10. **功能模块清单(重要)**:每个功能模块的 \`overview/REQUIREMENT.md\` 都必须包含功能模块清单表格\n`;
|
|
5412
|
+
prompt += ` - 表格格式:| # | 功能模块 | 涉及端 | 全局对比 | 来源 | 说明 |\n`;
|
|
5413
|
+
prompt += ` - 「涉及端」:每个模块标注需要**新开发工作**的端(标准端名,逗号分隔)\n`;
|
|
5414
|
+
prompt += ` - 「涉及」= 该端需要写新接口/新页面/新逻辑\n`;
|
|
5415
|
+
prompt += ` - 「不涉及」= 只是提到、调用已有接口、纯展示 → 不标注\n`;
|
|
5416
|
+
prompt += ` - 端名必须与 CONSTITUTION.md「端列表」中的标准端名完全匹配\n`;
|
|
5417
|
+
prompt += ` - 「全局对比」:该功能模块与全局层已有功能的关系(必须标注)\n`;
|
|
5418
|
+
prompt += ` - 「新增」:全局层不存在,本迭代全新开发\n`;
|
|
5419
|
+
prompt += ` - 「扩展」:全局层已有基础功能,本迭代增加新字段/新接口/新页面\n`;
|
|
5420
|
+
prompt += ` - 「重构」:全局层已有,本迭代修改实现方式(不新增功能)\n`;
|
|
5421
|
+
prompt += ` - 「复用」:全局层已有,本迭代直接使用(无需开发)\n`;
|
|
5422
|
+
prompt += ` - 示例:「扩展:增加会议室设备管理」或「新增」\n`;
|
|
5423
|
+
prompt += ` - 「来源」:该功能模块在需求文档中的具体位置,用 Markdown 链接格式\n`;
|
|
5424
|
+
prompt += ` - 格式:[文档名](相对路径#章节锚点),如 [PRD v2.0](../010-requirements/sources/PRD-v2.0.md#3-2-会议室管理)\n`;
|
|
5425
|
+
prompt += ` - 如果模块来自 converted 文档:[xxx需求](../010-requirements/converted/xxx.md#相关章节)\n`;
|
|
5426
|
+
prompt += ` - 如果来自 features 目录:[xxx功能](../010-requirements/features/xxx/SOURCE.md)\n`;
|
|
5427
|
+
prompt += ` - 目的是让阅读者能直接点击跳转到原始需求位置\n`;
|
|
5428
|
+
prompt += ` - 示例:| M-01 | 会议室档案管理 | room-service, admin-web | 扩展:增加设备管理 | [PRD v2.0](../010-requirements/sources/PRD-v2.0.md#2-1) | 会议室 CRUD、设备管理 |\n`;
|
|
5429
|
+
prompt += ` - split 命令将读取「涉及端」列来决定创建哪些端的子任务目录\n`;
|
|
5430
|
+
// v6.70.0+: 跨端功能映射表(FUNCTION_MAP.md)
|
|
5431
|
+
// v6.71.3+: 增加与全局层关联分析
|
|
5432
|
+
prompt += `7a. **迭代需求与全局层关联分析(重要)**:在每个功能模块的 \`overview/REQUIREMENT.md\` 中,必须对比全局层产物\n`;
|
|
5433
|
+
prompt += ` - 对比迭代需求中的功能模块 vs .speccore/GLOBAL/overview/FUNCTION_MAP.md 中的功能单元\n`;
|
|
5434
|
+
prompt += ` - 标注每个功能模块的「全局对比」类型(新增/扩展/重构/复用)\n`;
|
|
5435
|
+
prompt += ` - 识别冲突:如迭代需求修改了全局层已有接口的字段/路径 → 在 RISK.md 中标注\n`;
|
|
5436
|
+
prompt += ` - 识别依赖:如迭代的新功能依赖全局层的某个功能 → 在 FUNCTION_MAP.md「依赖任务」中标注\n\n`;
|
|
5437
|
+
prompt += `7b. **跨端功能映射表(重要)**:全局索引 \`overview/FUNCTION_MAP.md\` 汇总所有功能模块的映射关系\n`;
|
|
5438
|
+
prompt += ` - 这是 split 阶段的核心输入,决定任务如何按功能单元拆分\n`;
|
|
5439
|
+
prompt += ` - 表格格式:| # | 功能单元 | 涉及端 | 全局对比 | 共享能力 | 依赖任务 | 说明 |\n`;
|
|
5440
|
+
prompt += ` - 「功能单元」必须与 REQUIREMENT.md 功能模块清单一一对应,不允许合并\n`;
|
|
5441
|
+
prompt += ` - 「涉及端」标注所有需要新开发工作的端(标准端名,逗号分隔)\n`;
|
|
5442
|
+
prompt += ` - 「全局对比」标注与全局层的关系(新增/扩展/重构/复用)\n`;
|
|
5443
|
+
prompt += ` - 「共享能力」标注跨任务共享的通用组件(如"审批引擎"、"消息通知"),无则填"无"\n`;
|
|
5444
|
+
prompt += ` - 「依赖任务」标注当前迭代内该功能单元依赖的其他功能单元编号\n`;
|
|
5445
|
+
prompt += ` - 「说明」描述跨端交互关系(如"admin-web 调用 booking-service 的 /api/rooms 接口")\n`;
|
|
5446
|
+
prompt += ` - **示例**:| M-01 | 会议室档案管理 | room-service, admin-web | 扩展 | 无 | 无 | admin-web 调用 room-service CRUD 接口 |\n`;
|
|
5447
|
+
prompt += ` - **错误示例**(禁止):将"审批流程"和"定时任务"合并为一行\n`;
|
|
5448
|
+
prompt += ` - FUNCTION_MAP.md 生成后,split 将**严格按此表**创建任务目录,不再由 AI 推断\n`;
|
|
5449
|
+
// v6.70.0+: 跨端交互图谱(INTERACTION_MAP.md)
|
|
5450
|
+
prompt += `7c. **跨端交互图谱(重要)**:全局索引 \`overview/INTERACTION_MAP.md\` 汇总所有功能模块的交互时序\n`;
|
|
5451
|
+
prompt += ` - 按功能单元组织,每个功能单元一个 Mermaid sequenceDiagram\n`;
|
|
5452
|
+
prompt += ` - 展示完整的业务交互时序:用户操作 → 前端处理 → 后端调用 → 数据返回\n`;
|
|
5453
|
+
prompt += ` - 明确标出后端服务之间的内部调用(产品文档写"系统处理"的地方)\n`;
|
|
5454
|
+
prompt += ` - 箭头标注接口路径,标注 [contract] 表示接口在 API_CONTRACT.yaml 中有定义\n`;
|
|
5455
|
+
prompt += ` - 序列图后附「接口契约索引」表格:步骤号、接口、消费者端、提供者端\n`;
|
|
5456
|
+
prompt += ` - 如涉及状态变更,附「状态流转」表格\n`;
|
|
5457
|
+
prompt += ` - INTERACTION_MAP.md 是前后端开发者的共同参考,补全产品文档中隐含的技术交互\n`;
|
|
5458
|
+
// 注入工程类型信息(v6.49.0+)
|
|
5459
|
+
// v8.3.160+: 精简为路径引用,具体维度让 AI 从 CONSTITUTION.md 读取
|
|
5460
|
+
const platformTypes = await (0, spec_paths_2.parsePlatformTypes)();
|
|
5461
|
+
if (platformTypes.size > 0) {
|
|
5462
|
+
prompt += `8. **工程类型识别**:CONSTITUTION.md 已配置各端的工程类型,请 Read 后据此生成针对性内容\n`;
|
|
5463
|
+
prompt += ` | 工程标识 | 工程类型 |\n`;
|
|
5464
|
+
prompt += ` | :--- | :--- |\n`;
|
|
5465
|
+
for (const [name, type] of platformTypes) {
|
|
5466
|
+
prompt += ` | ${name} | ${type} |\n`;
|
|
5899
5467
|
}
|
|
5468
|
+
prompt += `\n`;
|
|
5469
|
+
}
|
|
5470
|
+
// v8.3.182+: 显式文档路由表
|
|
5471
|
+
prompt += `## 📋 文档路由表(v8.3.182+)\n\n`;
|
|
5472
|
+
prompt += `> 以下表格明确每类文档的写入路径。索引类放 overview/,内容类放 {feature}/overview/,端专属放 {feature}/{端}/。\n\n`;
|
|
5473
|
+
prompt += `| 文档名 | 路由目标 | 文档类型 | 说明 |\n`;
|
|
5474
|
+
prompt += `| :--- | :--- | :--- | :--- |\n`;
|
|
5475
|
+
prompt += `| FUNCTION_MAP.md | 020-specs/overview/ | 索引类 | 功能单元 × 端映射表(全局) |\n`;
|
|
5476
|
+
prompt += `| INTERACTION_MAP.md | 020-specs/overview/ | 索引类 | 跨端交互时序图(全局) |\n`;
|
|
5477
|
+
prompt += `| PLATFORMS.md | 020-specs/overview/ | 索引类 | 端列表(全局) |\n`;
|
|
5478
|
+
prompt += `| CLARIFY.md | 020-specs/overview/ | 索引类 | 全局澄清(跨功能共性问题) |\n`;
|
|
5479
|
+
prompt += `| API_CONTRACT.yaml | 020-specs/overview/ | 索引类 | 跨功能接口契约(全局) |\n`;
|
|
5480
|
+
prompt += `| ARCHITECTURE.md | 020-specs/overview/ | 索引类 | 迭代全局架构(服务拓扑/数据流) |\n`;
|
|
5481
|
+
prompt += `| REQUIREMENT.md | 020-specs/{feature}/overview/ | 内容类 | 该功能模块的需求规格 |\n`;
|
|
5482
|
+
prompt += `| ANALYSIS.md | 020-specs/{feature}/overview/ | 内容类 | 该功能模块的需求分析 |\n`;
|
|
5483
|
+
prompt += `| TECH.md | 020-specs/{feature}/overview/ | 内容类 | 该功能模块的技术方案(Phase 1) |\n`;
|
|
5484
|
+
prompt += `| RISK.md | 020-specs/{feature}/overview/ | 内容类 | 该功能模块的风险评估 |\n`;
|
|
5485
|
+
prompt += `| DEPS.md | 020-specs/{feature}/overview/ | 内容类 | 该功能模块的依赖清单 |\n`;
|
|
5486
|
+
prompt += `| REVIEW.md | 020-specs/{feature}/overview/ | 内容类 | 该功能模块的评审检查项 |\n`;
|
|
5487
|
+
prompt += `| MONITOR.md | 020-specs/{feature}/overview/ | 内容类 | 该功能模块的监控指标 |\n`;
|
|
5488
|
+
prompt += `| DEV_GUIDE.md | 020-specs/{feature}/overview/ | 内容类 | 该功能模块的实现指南(Phase 1) |\n`;
|
|
5489
|
+
prompt += `| TECH.md | 020-specs/{feature}/{端}/ | 端专属 | 该模块该端的技术方案(Phase 2) |\n`;
|
|
5490
|
+
prompt += `| TEST.md | 020-specs/{feature}/{端}/ | 端专属 | 该模块该端的测试用例(Phase 2) |\n`;
|
|
5491
|
+
prompt += `| UI_SPEC.md | 020-specs/{feature}/{端}/ | 端专属 | 该模块该端的 UI 规范(Phase 2) |\n`;
|
|
5492
|
+
prompt += `| DEV_GUIDE.md | 020-specs/{feature}/{端}/ | 端专属 | 该模块该端的实现指南(Phase 2) |\n\n`;
|
|
5493
|
+
prompt += `> **重要规则**:\n`;
|
|
5494
|
+
prompt += `> - 索引类文档(FUNCTION_MAP.md / INTERACTION_MAP.md / PLATFORMS.md / CLARIFY.md / API_CONTRACT.yaml)**必须**写入 020-specs/overview/,严禁写入 {feature}/overview/\n`;
|
|
5495
|
+
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`;
|
|
5496
|
+
prompt += `> - ANALYSIS.md 属于内容类,严禁放在 020-specs/overview/ 下\n`;
|
|
5497
|
+
prompt += `> - 全局 overview/REQUIREMENT.md 由 split 阶段从各功能模块 {feature}/overview/REQUIREMENT.md 自动聚合生成,AI 不要直接写入(会被拦截)\n\n`;
|
|
5498
|
+
const dirStepNum = platformTypes.size > 0 ? 10 : 9;
|
|
5499
|
+
prompt += `${dirStepNum}. **目录结构(严格遵循,禁止自创目录)**:\n`;
|
|
5500
|
+
prompt += ` - **全局索引**(跨端汇总)→ 通过 --apply 写入,CLI 自动路由到 \`020-specs/overview/{文件名}\`\n`;
|
|
5501
|
+
prompt += ` - FUNCTION_MAP.md(功能单元 × 端映射表)\n`;
|
|
5502
|
+
prompt += ` - INTERACTION_MAP.md(跨端交互时序图)\n`;
|
|
5503
|
+
prompt += ` - PLATFORMS.md(端列表)\n`;
|
|
5504
|
+
prompt += ` - **功能模块综合文档**(每个功能模块独立)→ 通过 --apply 写入,CLI 自动路由到 \`020-specs/{功能模块名}/overview/{文件名}\`\n`;
|
|
5505
|
+
prompt += ` - {功能模块}/overview/REQUIREMENT.md(该模块的需求规格)\n`;
|
|
5506
|
+
prompt += ` - {功能模块}/overview/ANALYSIS.md(该模块的需求分析)\n`;
|
|
5507
|
+
prompt += ` - {功能模块}/overview/TECH.md(该模块的技术方案)\n`;
|
|
5508
|
+
prompt += ` - {功能模块}/overview/RISK.md(该模块的风险评估)\n`;
|
|
5509
|
+
prompt += ` - {功能模块}/overview/DEPS.md(该模块的依赖清单)\n`;
|
|
5510
|
+
prompt += ` - {功能模块}/overview/REVIEW.md(该模块的评审检查项)\n`;
|
|
5511
|
+
prompt += ` - {功能模块}/overview/MONITOR.md(该模块的监控指标)\n`;
|
|
5512
|
+
prompt += ` - {功能模块}/overview/DEV_GUIDE.md(该模块的实现指南)\n`;
|
|
5513
|
+
prompt += ` - **端专属文档**(按功能模块组织)→ 通过 --apply 写入,CLI 自动路由到 \`020-specs/{功能模块名}/{端名}/{文件名}\`\n`;
|
|
5514
|
+
prompt += ` - {功能模块}/{端}/TECH.md(该模块该端的技术方案)\n`;
|
|
5515
|
+
prompt += ` - {功能模块}/{端}/TEST.md(该模块该端的测试用例)\n`;
|
|
5516
|
+
prompt += ` - {功能模块}/{端}/UI_SPEC.md(该模块该端的 UI 规范,仅前端端)\n`;
|
|
5517
|
+
prompt += ` - {功能模块}/{端}/DEV_GUIDE.md(该模块该端的实现指南)\n`;
|
|
5518
|
+
// v8.3.171+: 简化路径约束(骨架已预创建,CLI 掌控路径)
|
|
5519
|
+
prompt += ` - 文件已预创建,用 Write 工具覆盖已有路径即可,不要创建新文件或新目录\n`;
|
|
5520
|
+
if (ctx.phase !== '1') {
|
|
5521
|
+
// 端专业性约束只在默认模式(全量)中输出
|
|
5522
|
+
prompt += `\n## ⚠️ 端专业性约束\n`;
|
|
5523
|
+
prompt += `CONSTITUTION.md 中配置了多个端,每个端的文档必须有该端专属内容。\n`;
|
|
5524
|
+
prompt += `**先识别端类型,再应用对应的专业维度**:\n\n`;
|
|
5525
|
+
// v6.71.0+: 前后端文档差异化
|
|
5526
|
+
prompt += `### 后端服务(*service)必含内容 — 技术视角\n`;
|
|
5527
|
+
prompt += `- API 接口定义(路径/方法/参数/响应字段/状态码/错误码)\n`;
|
|
5528
|
+
prompt += `- 数据库表结构(字段/类型/索引/约束)\n`;
|
|
5529
|
+
prompt += `- 业务规则(含边界条件和异常流)\n`;
|
|
5530
|
+
prompt += `- 缓存策略/并发与事务/消息队列(如涉及)\n`;
|
|
5531
|
+
prompt += `- 安全:SQL 注入防护/接口鉴权/数据脱敏\n`;
|
|
5532
|
+
prompt += `- 性能:QPS 预估/慢查询优化/连接池配置\n`;
|
|
5533
|
+
prompt += `- **不需要**产品视角的需求描述(用户故事、业务场景已在 {功能模块}/overview/REQUIREMENT.md 中)\n\n`;
|
|
5534
|
+
prompt += `### 前端端(h5 / admin-web / miniapp)必含内容 — 产品+技术双视角\n`;
|
|
5535
|
+
prompt += `- **产品视角(主要)**:\n`;
|
|
5536
|
+
prompt += ` - 用户旅程:该端用户如何完成核心任务(步骤流程图)\n`;
|
|
5537
|
+
prompt += ` - 页面清单:所有页面名称、路径、核心功能、入口位置\n`;
|
|
5538
|
+
prompt += ` - 交互设计:关键操作流程、状态变化、异常提示方式\n`;
|
|
5539
|
+
prompt += ` - 字段展示:每个页面展示哪些字段、字段来源(后端哪个接口)\n`;
|
|
5540
|
+
prompt += ` - 权限控制:哪些页面/按钮需要权限、权限粒度\n`;
|
|
5541
|
+
prompt += `- **技术视角(辅助)**:\n`;
|
|
5542
|
+
prompt += ` - 页面路由表 + 组件清单\n`;
|
|
5543
|
+
prompt += ` - API 调用清单:前端调用了哪些后端接口(路径+方法+用途)\n`;
|
|
5544
|
+
prompt += ` - 状态管理:全局状态设计、与后端数据同步策略\n`;
|
|
5545
|
+
prompt += ` - 适配/性能/安全等前端专项(按端类型选择)\n\n`;
|
|
5900
5546
|
}
|
|
5901
5547
|
// v6.60.0+: 文档与端的对应关系(不再分 Phase)
|
|
5902
5548
|
// v6.71.0+: 前后端文档差异化
|
|
@@ -5924,16 +5570,13 @@ status: "clarified"
|
|
|
5924
5570
|
prompt += `- 分析完成后会自动生成 QUALITY_AUDIT.md 质量报告,检查各端内容是否完整\n`;
|
|
5925
5571
|
}
|
|
5926
5572
|
// 步骤 2-7 已在上面的 phase 分支中处理
|
|
5927
|
-
const taskFlag = isTask && ctx.task ? ` --task ${ctx.task}` : '';
|
|
5928
5573
|
const platformFlag = ctx.platform ? ` --platform ${ctx.platform}` : '';
|
|
5929
5574
|
// ── 链式生成指令(v6.45.0+)──
|
|
5930
|
-
if (hasUserTemplates
|
|
5575
|
+
if (hasUserTemplates) {
|
|
5931
5576
|
prompt += `\n## 🔗 链式生成(重要)\n\n`;
|
|
5932
5577
|
prompt += `请按以下顺序**逐个生成**文档,每个文档生成后立即 --apply 写入,然后 Read 自己的产出再生成下一个:\n\n`;
|
|
5933
5578
|
// 定义生成顺序
|
|
5934
|
-
const chainOrder =
|
|
5935
|
-
? ['REQ.md', 'TECH.md', 'SCHEMA.md', 'TASK.md']
|
|
5936
|
-
: ['REQUIREMENT.md', 'FUNCTION_MAP.md', 'INTERACTION_MAP.md', 'API_CONTRACT.yaml', 'ANALYSIS.md', 'TECH.md', 'TEST.md', 'REVIEW.md', 'RISK.md', 'DEPS.md', 'MONITOR.md', 'UI_SPEC.md', 'DEV_GUIDE.md'];
|
|
5579
|
+
const chainOrder = ['REQUIREMENT.md', 'FUNCTION_MAP.md', 'INTERACTION_MAP.md', 'API_CONTRACT.yaml', 'ANALYSIS.md', 'TECH.md', 'TEST.md', 'REVIEW.md', 'RISK.md', 'DEPS.md', 'MONITOR.md', 'UI_SPEC.md', 'DEV_GUIDE.md'];
|
|
5937
5580
|
const orderedDocs = taskDocs.filter(([n]) => chainOrder.includes(n));
|
|
5938
5581
|
const customDocs = taskDocs.filter(([n]) => !chainOrder.includes(n));
|
|
5939
5582
|
const ordered = [...orderedDocs.sort((a, b) => chainOrder.indexOf(a[0]) - chainOrder.indexOf(b[0])), ...customDocs];
|
|
@@ -5947,19 +5590,19 @@ status: "clarified"
|
|
|
5947
5590
|
}
|
|
5948
5591
|
prompt += `\n每个文档用单独的 --apply 调用写入:\n`;
|
|
5949
5592
|
for (const doc of ordered) {
|
|
5950
|
-
prompt += `speccore analyze --apply '{"${doc[0]}":"..."}' -I ${iter}${
|
|
5593
|
+
prompt += `speccore analyze --apply '{"${doc[0]}":"..."}' -I ${iter}${platformFlag}\n`;
|
|
5951
5594
|
}
|
|
5952
5595
|
prompt += `\n`;
|
|
5953
5596
|
}
|
|
5954
5597
|
else if (ctx.phase !== '2') {
|
|
5955
5598
|
// 传统模式:一次性写入
|
|
5956
|
-
prompt += `7. 写入: speccore analyze --apply '{${taskDocs.map(([n]) => `"${n}":"..."`).join(',')}}' -I ${iter}${
|
|
5599
|
+
prompt += `7. 写入: speccore analyze --apply '{${taskDocs.map(([n]) => `"${n}":"..."`).join(',')}}' -I ${iter}${platformFlag}\n\n`;
|
|
5957
5600
|
}
|
|
5958
5601
|
// v6.60.0+: 移除两阶段分析提示,一次性生成所有文档
|
|
5959
5602
|
prompt += '\n' + (0, questions_1.buildAutoModeInstruction)('analyze', iter) + '\n';
|
|
5960
5603
|
// ── v6.65.0+: Phase 1 完成后主动询问用户是否继续 Phase 2 ──
|
|
5961
5604
|
// v6.70.0+: 自动模式下跳过确认,AI 直接推断执行
|
|
5962
|
-
if (!ctx.phase && !
|
|
5605
|
+
if (!ctx.phase && !autoMode) {
|
|
5963
5606
|
prompt += `\n## ⚠️ 重要:Phase 1 完成后的下一步\n\n`;
|
|
5964
5607
|
prompt += `当你通过 --apply 写入所有综合文档后,CLI 会检测到项目有多个端(≥2 个端)。\n`;
|
|
5965
5608
|
prompt += `**此时你需要主动询问用户**:\n\n`;
|
|
@@ -5970,7 +5613,7 @@ status: "clarified"
|
|
|
5970
5613
|
prompt += `\`speccore analyze --prompt -I ${iter} --phase 2\`\n\n`;
|
|
5971
5614
|
prompt += `**不要等待 CLI 的提示信息**,CLI 的输出你可能看不到。你需要主动询问用户。\n\n`;
|
|
5972
5615
|
}
|
|
5973
|
-
if (autoMode && !ctx.phase
|
|
5616
|
+
if (autoMode && !ctx.phase) {
|
|
5974
5617
|
prompt += `\n## 🤖 自动模式说明\n\n`;
|
|
5975
5618
|
prompt += `当前处于自动模式(--auto),不需要人工确认。\n`;
|
|
5976
5619
|
prompt += `请在生成综合文档后,**直接继续**生成各端专属文档(Phase 2)。\n`;
|
|
@@ -6007,7 +5650,7 @@ status: "clarified"
|
|
|
6007
5650
|
}
|
|
6008
5651
|
}
|
|
6009
5652
|
// ── v6.52.0+: 图谱 RAG 上下文注入(analyze 阶段也检索项目关联内容)──
|
|
6010
|
-
if (
|
|
5653
|
+
if (ctx.phase !== '2') {
|
|
6011
5654
|
try {
|
|
6012
5655
|
const ragResult = await (0, unified_retrieval_1.unifiedSearch)(((0, task_utils_1.findProjectRoot)() || process.cwd()), {
|
|
6013
5656
|
query: ctx.iteration || '',
|
|
@@ -6252,26 +5895,6 @@ async function buildContractFirstPrompt(iteration) {
|
|
|
6252
5895
|
prompt += `- 契约应**精确且完整**,避免后续各端分析时出现接口不一致\n\n`;
|
|
6253
5896
|
return await injectGraphSummary(prompt);
|
|
6254
5897
|
}
|
|
6255
|
-
// ── v6.49.9+: 扫描平铺的端目录,返回所有子任务目录路径 ──
|
|
6256
|
-
async function getSubtaskDirs(taskDir) {
|
|
6257
|
-
const result = [];
|
|
6258
|
-
const platformList = await (0, spec_paths_2.parsePlatformList)();
|
|
6259
|
-
for (const platform of platformList) {
|
|
6260
|
-
const platDir = (0, path_1.join)(taskDir, platform);
|
|
6261
|
-
if (!(await (0, fs_extra_1.pathExists)(platDir)))
|
|
6262
|
-
continue;
|
|
6263
|
-
try {
|
|
6264
|
-
const entries = await (0, fs_extra_2.readdir)(platDir, { withFileTypes: true });
|
|
6265
|
-
for (const entry of entries) {
|
|
6266
|
-
if (entry.isDirectory() && !entry.name.startsWith('.')) {
|
|
6267
|
-
result.push((0, path_1.join)(platDir, entry.name));
|
|
6268
|
-
}
|
|
6269
|
-
}
|
|
6270
|
-
}
|
|
6271
|
-
catch { /* ignore */ }
|
|
6272
|
-
}
|
|
6273
|
-
return result;
|
|
6274
|
-
}
|
|
6275
5898
|
// ═══════════════════════════════════════════════════════════
|
|
6276
5899
|
// ═══════════════════════════════════════════════════════════
|
|
6277
5900
|
/**
|
|
@@ -7007,7 +6630,7 @@ async function buildPipelineStepPrompt(args) {
|
|
|
7007
6630
|
// 全局层 Pipeline 步骤映射
|
|
7008
6631
|
if (currentStep === 'init' || currentStep === 'discovery') {
|
|
7009
6632
|
prompt = (0, prompt_builder_1.buildPromptText)(await (0, prompt_builder_1.buildPrompt)('analyze', {
|
|
7010
|
-
iteration: iter,
|
|
6633
|
+
iteration: iter, platform: options.platform,
|
|
7011
6634
|
analyzeOptions: options,
|
|
7012
6635
|
}));
|
|
7013
6636
|
}
|
|
@@ -7040,7 +6663,7 @@ async function buildPipelineStepPrompt(args) {
|
|
|
7040
6663
|
}
|
|
7041
6664
|
else if (currentStep === 'phase1-prompt') {
|
|
7042
6665
|
prompt = (0, prompt_builder_1.buildPromptText)(await (0, prompt_builder_1.buildPrompt)('analyze', {
|
|
7043
|
-
iteration: iter,
|
|
6666
|
+
iteration: iter, platform: options.platform,
|
|
7044
6667
|
analyzeOptions: options,
|
|
7045
6668
|
}));
|
|
7046
6669
|
}
|
|
@@ -7074,7 +6697,7 @@ async function buildPipelineStepPrompt(args) {
|
|
|
7074
6697
|
else {
|
|
7075
6698
|
// 回退到默认 prompt
|
|
7076
6699
|
prompt = (0, prompt_builder_1.buildPromptText)(await (0, prompt_builder_1.buildPrompt)('analyze', {
|
|
7077
|
-
iteration: iter,
|
|
6700
|
+
iteration: iter, platform: options.platform,
|
|
7078
6701
|
analyzeOptions: options,
|
|
7079
6702
|
}));
|
|
7080
6703
|
}
|
|
@@ -7302,133 +6925,6 @@ async function buildAndSaveQualityReport(iterDir, iter, units) {
|
|
|
7302
6925
|
logger_1.logger.info(` 📊 质量报告已落盘: 020-specs/QUALITY_AUDIT.md(${okCount} 成功 / ${failCount} 失败,平均 ${avgScore} 分)`);
|
|
7303
6926
|
return reportPath;
|
|
7304
6927
|
}
|
|
7305
|
-
// ═══════════════════════════════════════════════════════════
|
|
7306
|
-
// 多子任务分析支持(v8.3.94+)
|
|
7307
|
-
// ═══════════════════════════════════════════════════════════
|
|
7308
|
-
/** 解析逗号分隔的任务 ID */
|
|
7309
|
-
function parseTaskIds(taskOpt) {
|
|
7310
|
-
return taskOpt
|
|
7311
|
-
.split(',')
|
|
7312
|
-
.map(t => t.trim())
|
|
7313
|
-
.filter(Boolean)
|
|
7314
|
-
.map(t => t.startsWith('Task-') ? t : `Task-${t}`);
|
|
7315
|
-
}
|
|
7316
|
-
/**
|
|
7317
|
-
* 根据过滤条件扫描迭代目录下的任务
|
|
7318
|
-
* 支持: status:doing | owner:张三 | type:feature | platform:web | keyword:auth
|
|
7319
|
-
*/
|
|
7320
|
-
async function filterTasks(iterDir, filter) {
|
|
7321
|
-
const tasksDir = (0, path_1.join)(iterDir, '030-tasks');
|
|
7322
|
-
if (!await (0, fs_extra_1.pathExists)(tasksDir))
|
|
7323
|
-
return [];
|
|
7324
|
-
const entries = await (0, fs_extra_2.readdir)(tasksDir, { withFileTypes: true });
|
|
7325
|
-
const taskDirs = entries.filter(e => e.isDirectory() && e.name.startsWith('Task-')).map(e => e.name);
|
|
7326
|
-
if (taskDirs.length === 0)
|
|
7327
|
-
return [];
|
|
7328
|
-
const matched = [];
|
|
7329
|
-
for (const taskId of taskDirs) {
|
|
7330
|
-
const taskDir = (0, path_1.join)(tasksDir, taskId);
|
|
7331
|
-
const metaDir = (0, path_1.join)(taskDir, '.meta');
|
|
7332
|
-
// 解析过滤条件
|
|
7333
|
-
const filterLower = filter.toLowerCase();
|
|
7334
|
-
// 1. 状态过滤: status:doing
|
|
7335
|
-
if (filterLower.startsWith('status:')) {
|
|
7336
|
-
const statusFile = (0, path_1.join)(metaDir, 'status');
|
|
7337
|
-
if (await (0, fs_extra_1.pathExists)(statusFile)) {
|
|
7338
|
-
const status = (await (0, fs_extra_2.readFile)(statusFile, 'utf-8')).trim().toLowerCase();
|
|
7339
|
-
if (status === filterLower.slice(7).trim())
|
|
7340
|
-
matched.push(taskId);
|
|
7341
|
-
}
|
|
7342
|
-
continue;
|
|
7343
|
-
}
|
|
7344
|
-
// 2. 负责人过滤: owner:张三
|
|
7345
|
-
if (filterLower.startsWith('owner:')) {
|
|
7346
|
-
const ownerFile = (0, path_1.join)(metaDir, 'owner');
|
|
7347
|
-
if (await (0, fs_extra_1.pathExists)(ownerFile)) {
|
|
7348
|
-
const owner = (await (0, fs_extra_2.readFile)(ownerFile, 'utf-8')).trim();
|
|
7349
|
-
if (owner.includes(filter.slice(6).trim()))
|
|
7350
|
-
matched.push(taskId);
|
|
7351
|
-
}
|
|
7352
|
-
continue;
|
|
7353
|
-
}
|
|
7354
|
-
// 3. 类型过滤: type:feature
|
|
7355
|
-
if (filterLower.startsWith('type:')) {
|
|
7356
|
-
const typeFile = (0, path_1.join)(metaDir, 'type');
|
|
7357
|
-
if (await (0, fs_extra_1.pathExists)(typeFile)) {
|
|
7358
|
-
const type = (await (0, fs_extra_2.readFile)(typeFile, 'utf-8')).trim().toLowerCase();
|
|
7359
|
-
if (type === filterLower.slice(5).trim())
|
|
7360
|
-
matched.push(taskId);
|
|
7361
|
-
}
|
|
7362
|
-
continue;
|
|
7363
|
-
}
|
|
7364
|
-
// 4. 端过滤: platform:web
|
|
7365
|
-
if (filterLower.startsWith('platform:')) {
|
|
7366
|
-
const targetPlat = filterLower.slice(9).trim();
|
|
7367
|
-
try {
|
|
7368
|
-
const subEntries = await (0, fs_extra_2.readdir)(taskDir, { withFileTypes: true });
|
|
7369
|
-
const hasPlatform = subEntries.some(e => e.isDirectory() &&
|
|
7370
|
-
!e.name.startsWith('.') &&
|
|
7371
|
-
e.name !== '_shared' &&
|
|
7372
|
-
e.name !== '00-specs' &&
|
|
7373
|
-
e.name.toLowerCase().includes(targetPlat));
|
|
7374
|
-
if (hasPlatform)
|
|
7375
|
-
matched.push(taskId);
|
|
7376
|
-
}
|
|
7377
|
-
catch { /* ignore */ }
|
|
7378
|
-
continue;
|
|
7379
|
-
}
|
|
7380
|
-
// 5. 关键词过滤: keyword:auth(或无前缀的默认行为)
|
|
7381
|
-
const keyword = filterLower.startsWith('keyword:') ? filterLower.slice(8).trim() : filterLower;
|
|
7382
|
-
// 检查任务目录名、feature 名、TASK.md 内容
|
|
7383
|
-
let hasKeyword = taskId.toLowerCase().includes(keyword);
|
|
7384
|
-
if (!hasKeyword) {
|
|
7385
|
-
const featureFile = (0, path_1.join)(metaDir, 'feature');
|
|
7386
|
-
if (await (0, fs_extra_1.pathExists)(featureFile)) {
|
|
7387
|
-
const feature = (await (0, fs_extra_2.readFile)(featureFile, 'utf-8')).trim().toLowerCase();
|
|
7388
|
-
if (feature.includes(keyword))
|
|
7389
|
-
hasKeyword = true;
|
|
7390
|
-
}
|
|
7391
|
-
}
|
|
7392
|
-
if (!hasKeyword) {
|
|
7393
|
-
const taskMd = (0, path_1.join)(taskDir, '00-specs', 'REQ.md');
|
|
7394
|
-
if (await (0, fs_extra_1.pathExists)(taskMd)) {
|
|
7395
|
-
const content = (await (0, fs_extra_2.readFile)(taskMd, 'utf-8')).toLowerCase();
|
|
7396
|
-
if (content.includes(keyword))
|
|
7397
|
-
hasKeyword = true;
|
|
7398
|
-
}
|
|
7399
|
-
}
|
|
7400
|
-
if (hasKeyword)
|
|
7401
|
-
matched.push(taskId);
|
|
7402
|
-
}
|
|
7403
|
-
return matched;
|
|
7404
|
-
}
|
|
7405
|
-
/**
|
|
7406
|
-
* 综合解析目标任务列表
|
|
7407
|
-
* - 优先使用 options.task(支持逗号分隔)
|
|
7408
|
-
* - 其次使用 options.filter 自动发现
|
|
7409
|
-
*/
|
|
7410
|
-
async function resolveTargetTasks(options) {
|
|
7411
|
-
// 1. 直接指定多个任务
|
|
7412
|
-
if (options.task && options.task.includes(',')) {
|
|
7413
|
-
return parseTaskIds(options.task);
|
|
7414
|
-
}
|
|
7415
|
-
// 2. 单任务
|
|
7416
|
-
if (options.task) {
|
|
7417
|
-
const taskId = options.task.startsWith('Task-') ? options.task : `Task-${options.task}`;
|
|
7418
|
-
return [taskId];
|
|
7419
|
-
}
|
|
7420
|
-
// 3. 通过 filter 自动发现
|
|
7421
|
-
if (options.filter && options.iteration) {
|
|
7422
|
-
const iterDir = await (0, context_1.getIterationDir)(options.iteration);
|
|
7423
|
-
const matched = await filterTasks(iterDir, options.filter);
|
|
7424
|
-
if (matched.length > 0) {
|
|
7425
|
-
logger_1.logger.info(`🔍 filter "${options.filter}" 匹配到 ${matched.length} 个任务: ${matched.join(', ')}`);
|
|
7426
|
-
return matched;
|
|
7427
|
-
}
|
|
7428
|
-
logger_1.logger.warn(`⚠️ filter "${options.filter}" 未匹配到任何任务`);
|
|
7429
|
-
}
|
|
7430
|
-
return [];
|
|
7431
|
-
}
|
|
7432
6928
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
7433
6929
|
// v8.3.177+: 总览生成辅助函数
|
|
7434
6930
|
// ═══════════════════════════════════════════════════════════════════════════════
|