speccore 8.3.253 → 8.3.254
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 +7 -10
- 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 +313 -821
- package/dist/commands/analyze.js.map +1 -1
- 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");
|
|
@@ -1486,31 +1485,9 @@ async function analyzeCommand(options) {
|
|
|
1486
1485
|
// 跳过澄清(off 或 auto 判定 high):仍生成质量报告,供后续修正参考
|
|
1487
1486
|
await generateQualityReport(iter);
|
|
1488
1487
|
}
|
|
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
1488
|
// v8.3.160+: 统一使用 buildPrompt 入口
|
|
1511
1489
|
const promptObj = await (0, prompt_builder_1.buildPrompt)('analyze', {
|
|
1512
1490
|
iteration: iter,
|
|
1513
|
-
task: options.task,
|
|
1514
1491
|
platform: options.platform,
|
|
1515
1492
|
analyzeOptions: options,
|
|
1516
1493
|
});
|
|
@@ -1549,33 +1526,11 @@ ${singlePrompt}`);
|
|
|
1549
1526
|
return;
|
|
1550
1527
|
}
|
|
1551
1528
|
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
1529
|
// v8.3.237+: Pipeline 推进逻辑抽取为共享函数——JSON / [DOC:] 多文档通道写入后
|
|
1573
1530
|
// 提前 return,此前会跳过尾部的 Pipeline 推进(bug: 只有单文件模式能推进),
|
|
1574
1531
|
// 现将「RAG 刷新 + Pipeline 推进」抽为共享函数,三个写入通道成功后都调用。
|
|
1575
1532
|
// 返回 true 表示存在活跃 Pipeline 并已处理(调用点应直接 return,不再输出后续提示)。
|
|
1576
1533
|
const advancePipelineAfterApply = async () => {
|
|
1577
|
-
if (isTaskLevel)
|
|
1578
|
-
return false;
|
|
1579
1534
|
// v8.3.238+: 步骤完成检查——计算当前步骤尚未填充的骨架清单(相对 020-specs/ 的路径)。
|
|
1580
1535
|
// 单文档模式一次 --apply 通常只写 1~2 个文档,但一个步骤(如 phase1-prompt)有多个
|
|
1581
1536
|
// 骨架待填充;此前写 1 个就推进,剩余骨架会错位到下一步骤。
|
|
@@ -1809,7 +1764,7 @@ ${singlePrompt}`);
|
|
|
1809
1764
|
logger_1.logger.info('');
|
|
1810
1765
|
}
|
|
1811
1766
|
// v8.3.190+: 需求澄清验证 — off 跳过门禁;auto/on 下质量不足且未澄清则拒绝写入
|
|
1812
|
-
if (!isGlobalScope &&
|
|
1767
|
+
if (!isGlobalScope && clarifyMode !== 'off') {
|
|
1813
1768
|
const { hasValidClarifiedDocs } = await Promise.resolve().then(() => __importStar(require('../core/requirement-clarifier')));
|
|
1814
1769
|
const hasClarifiedDocs = await hasValidClarifiedDocs(iterDir);
|
|
1815
1770
|
if (!hasClarifiedDocs && clarifyBlocks.size === 0) {
|
|
@@ -1883,29 +1838,7 @@ ${singlePrompt}`);
|
|
|
1883
1838
|
}
|
|
1884
1839
|
}
|
|
1885
1840
|
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) {
|
|
1841
|
+
if (isGlobalScope) {
|
|
1909
1842
|
// 全局级:写入 .speccore/GLOBAL/(与 platforms/ 同级,非迭代目录)
|
|
1910
1843
|
const globalBaseDir = (0, path_1.join)(_projectRoot, '.speccore', 'GLOBAL');
|
|
1911
1844
|
await (0, fs_extra_1.ensureDir)(globalBaseDir);
|
|
@@ -2384,28 +2317,7 @@ ${singlePrompt}`);
|
|
|
2384
2317
|
try {
|
|
2385
2318
|
const docs = JSON.parse(options.apply);
|
|
2386
2319
|
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) {
|
|
2320
|
+
if (isGlobalScope) {
|
|
2409
2321
|
const globalBaseDir = (0, path_1.join)(_projectRoot, '.speccore', 'GLOBAL');
|
|
2410
2322
|
await (0, fs_extra_1.ensureDir)(globalBaseDir);
|
|
2411
2323
|
const globalSet = new Set(spec_paths_2.GLOBAL_SPEC_FILES);
|
|
@@ -2613,27 +2525,7 @@ ${singlePrompt}`);
|
|
|
2613
2525
|
// 注意:不能提前 return,否则会跳过后续的 Pipeline 推进逻辑(--apply 靠它推进 confirm-check)
|
|
2614
2526
|
const hasClarifyMarkers = /\[CLARIFY:[^\]]+\]/.test(options.apply);
|
|
2615
2527
|
// 单文件模式
|
|
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) {
|
|
2528
|
+
if (!hasClarifyMarkers && isGlobalScope) {
|
|
2637
2529
|
// 全局级:写 .speccore/GLOBAL/ANALYSIS.md
|
|
2638
2530
|
const globalDir = (0, path_1.join)(_projectRoot, '.speccore', 'GLOBAL');
|
|
2639
2531
|
await (0, fs_extra_1.ensureDir)(globalDir);
|
|
@@ -2670,26 +2562,6 @@ ${singlePrompt}`);
|
|
|
2670
2562
|
logger_1.logger.info(` ⏭️ 用户取消覆盖`);
|
|
2671
2563
|
}
|
|
2672
2564
|
}
|
|
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
2565
|
printBackupSummary();
|
|
2694
2566
|
// v8.3.237+: RAG 刷新 + Pipeline 推进已抽取为 advancePipelineAfterApply(见 apply 分支开头),
|
|
2695
2567
|
// 三个写入通道(JSON / [DOC:] / 单文件模式)统一调用,避免多文档通道提前 return 跳过推进。
|
|
@@ -2697,7 +2569,7 @@ ${singlePrompt}`);
|
|
|
2697
2569
|
if (pipelineHandled)
|
|
2698
2570
|
return;
|
|
2699
2571
|
// ── v6.64.0+: Phase 1 完成后自动触发 Phase 2(仅多端项目,非 Pipeline 模式)──
|
|
2700
|
-
if (!options.phase &&
|
|
2572
|
+
if (!options.phase && options.iteration) {
|
|
2701
2573
|
// 检查是否有多个端
|
|
2702
2574
|
const platforms = await (0, spec_paths_2.parsePlatformList)();
|
|
2703
2575
|
// 只有多端项目(≥2 个端)才需要分两阶段:
|
|
@@ -2767,7 +2639,7 @@ ${singlePrompt}`);
|
|
|
2767
2639
|
}
|
|
2768
2640
|
catch { /* ignore */ }
|
|
2769
2641
|
}
|
|
2770
|
-
const prompt = await buildMultiDocPrompt('analyze', { iteration: iter,
|
|
2642
|
+
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
2643
|
await (0, ai_trace_1.recordAiTrace)(iter, 'prompt-out', { mode: 'analyze-main', scope: options.scope, promptLength: prompt.length, prompt });
|
|
2772
2644
|
process.stdout.write(`[SPECCORE_PROMPT]\n${prompt}`);
|
|
2773
2645
|
// v7.2.0+: 全局分析完成后输出下一步引导
|
|
@@ -2953,154 +2825,6 @@ async function generateIterationSpecDocs(iteration) {
|
|
|
2953
2825
|
}
|
|
2954
2826
|
logger_1.logger.info(`\n📄 Spec 文档: 新建 ${created} 个, 跳过 ${skipped} 个 (已存在) → ${specDir}/ (global/ + 根目录)`);
|
|
2955
2827
|
}
|
|
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
2828
|
/**
|
|
3105
2829
|
* 从 process.argv 手动解析选项 (Commander.js 偶发不传递部分选项)
|
|
3106
2830
|
*/
|
|
@@ -3108,7 +2832,6 @@ function parseArgv(options) {
|
|
|
3108
2832
|
const argv = process.argv;
|
|
3109
2833
|
const strFlags = [
|
|
3110
2834
|
[['--iteration', '-i', '-I'], (v) => { options.iteration = v; }],
|
|
3111
|
-
[['--task', '-t'], (v) => { options.task = v; }],
|
|
3112
2835
|
[['--platform'], (v) => { options.platform = v; }],
|
|
3113
2836
|
[['--scope'], (v) => { options.scope = v; }],
|
|
3114
2837
|
[['--src', '--source'], (v) => { options.source = v; }],
|
|
@@ -3728,10 +3451,7 @@ async function buildLayer3ModuleContext(projectRoot) {
|
|
|
3728
3451
|
// v8.3.160+: 改为 export,供 prompt-builder.ts 统一调用
|
|
3729
3452
|
async function buildMultiDocPrompt(command, ctx, options, clarifyCtx) {
|
|
3730
3453
|
const iter = ctx.iteration || '当前迭代';
|
|
3731
|
-
const task = ctx.task ? ` — ${ctx.task}` : '';
|
|
3732
|
-
const taskType = ctx.type || 'feature';
|
|
3733
3454
|
const now = new Date().toISOString().split('T')[0];
|
|
3734
|
-
const isTask = ctx.scope === 'task' || !!ctx.task;
|
|
3735
3455
|
const isGlobal = ctx.scope === 'global';
|
|
3736
3456
|
const autoMode = ctx.autoMode || false;
|
|
3737
3457
|
// v8.3.0+: 从 clarifyCtx 提取需求澄清状态
|
|
@@ -5046,7 +4766,7 @@ sequenceDiagram
|
|
|
5046
4766
|
bugfix: ['ANALYSIS.md', 'TECH.md', 'TEST.md', 'DEV_GUIDE.md'],
|
|
5047
4767
|
research: ['ANALYSIS.md', 'DEV_GUIDE.md'],
|
|
5048
4768
|
};
|
|
5049
|
-
const includeDocs =
|
|
4769
|
+
const includeDocs = DOC_MATRIX['feature'];
|
|
5050
4770
|
// ── v6.61.0+: 恢复 Phase 1/Phase 2 分步逻辑,但 CLI 自动触发 Phase 2 ──
|
|
5051
4771
|
// v8.3.160+: 改为功能模块优先 — 全局只保留索引文档,每个功能模块有自己的综合文档
|
|
5052
4772
|
// Phase 1: 全局索引(FUNCTION_MAP.md/INTERACTION_MAP.md/PLATFORMS.md) + 各功能模块 overview/
|
|
@@ -5094,7 +4814,7 @@ sequenceDiagram
|
|
|
5094
4814
|
// 替代 v7.5.0 的 detectIterationDocsStatus(基于文件是否存在)
|
|
5095
4815
|
let perDocStatus = null;
|
|
5096
4816
|
let lastDocQuality = null;
|
|
5097
|
-
if (!isGlobal && !
|
|
4817
|
+
if (!isGlobal && !ctx.phase && ctx.iteration && ctx.iteration !== 'GLOBAL') {
|
|
5098
4818
|
try {
|
|
5099
4819
|
const iterDir = await (0, context_1.getIterationDir)(ctx.iteration);
|
|
5100
4820
|
if (iterDir) {
|
|
@@ -5211,39 +4931,6 @@ sequenceDiagram
|
|
|
5211
4931
|
logger_1.logger.debug(`骨架进度检测失败(非关键): ${e.message}`);
|
|
5212
4932
|
}
|
|
5213
4933
|
}
|
|
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
4934
|
// v7.2.0+: 细粒度分析 — 只生成指定文档的指定功能
|
|
5248
4935
|
if (options?.docName && options?.featureName && !isGlobal) {
|
|
5249
4936
|
const targetDoc = options.docName.endsWith('.md') ? options.docName : `${options.docName}.md`;
|
|
@@ -5257,8 +4944,8 @@ sequenceDiagram
|
|
|
5257
4944
|
}
|
|
5258
4945
|
}
|
|
5259
4946
|
// ── 用户自定义模板集成(v6.45.0+)──
|
|
5260
|
-
const templateLevel = isGlobal ? 'global' :
|
|
5261
|
-
const userTemplates = await loadUserTemplates(templateLevel,
|
|
4947
|
+
const templateLevel = isGlobal ? 'global' : 'iteration';
|
|
4948
|
+
const userTemplates = await loadUserTemplates(templateLevel, undefined, ctx.platform);
|
|
5262
4949
|
const hasUserTemplates = userTemplates.size > 0;
|
|
5263
4950
|
if (hasUserTemplates) {
|
|
5264
4951
|
// 用户模板覆盖/追加:用户放了什么文档就生成什么文档
|
|
@@ -5281,13 +4968,13 @@ sequenceDiagram
|
|
|
5281
4968
|
techDoc[1] = `# 技术架构(跨端全局)\n\n> ${iter}\n\n## 写作要求\n撰写整体技术架构,覆盖所有端的交互关系:\n- 系统整体分层设计(各端在架构中的位置)\n- 跨端交互协议(前端↔后端通信方式、数据流向)\n- 中间件选型(缓存、消息队列、网关等)\n- 数据库整体设计(核心表结构、ER 关系)\n- 技术栈选型及理由\n`;
|
|
5282
4969
|
}
|
|
5283
4970
|
const featureLabel = ctx.feature ? ` · 功能模块:${ctx.feature}` : '';
|
|
5284
|
-
let prompt = `\n# 任务: ${command}
|
|
4971
|
+
let prompt = `\n# 任务: ${command} (${taskDocs.length}个文档 · 迭代全量${ctx.phase ? ` · Phase ${ctx.phase}` : ''}${featureLabel})\n\n`;
|
|
5285
4972
|
// v8.3.160+: 注入子 Agent 角色定义
|
|
5286
4973
|
if (agentContextText) {
|
|
5287
4974
|
prompt = agentContextText + '\n\n' + prompt;
|
|
5288
4975
|
}
|
|
5289
4976
|
// v8.3.0+: 如果检测到需求质量不足,在 prompt 开头注入强制澄清阶段
|
|
5290
|
-
if (needsClarify && !isGlobal && !
|
|
4977
|
+
if (needsClarify && !isGlobal && !ctx.phase) {
|
|
5291
4978
|
const now = new Date().toISOString();
|
|
5292
4979
|
prompt += `## 🚨 Phase 0: 需求澄清(强制前置 — 未完成则禁止进入 Phase 1)\n\n`;
|
|
5293
4980
|
prompt += `检测到以下需求文档质量不足,**必须先完成专业化整理,才能继续技术分析**:\n\n`;
|
|
@@ -5382,7 +5069,7 @@ status: "clarified"
|
|
|
5382
5069
|
logger_1.logger.warn(` ⚠️ 代码关联注入失败: ${e.message}`);
|
|
5383
5070
|
}
|
|
5384
5071
|
}
|
|
5385
|
-
if (!isGlobal &&
|
|
5072
|
+
if (!isGlobal && ctx.iteration && ctx.iteration !== 'GLOBAL') {
|
|
5386
5073
|
try {
|
|
5387
5074
|
const iterDir = await (0, context_1.getIterationDir)(ctx.iteration);
|
|
5388
5075
|
const reportPath = (0, path_1.join)(iterDir, '010-requirements', 'CLARIFY_REPORT.md');
|
|
@@ -5409,7 +5096,7 @@ status: "clarified"
|
|
|
5409
5096
|
catch { /* 忽略质量上下文注入失败 */ }
|
|
5410
5097
|
}
|
|
5411
5098
|
// ── v6.67.0+: 在 prompt 开头就明确完整工作流程 ──
|
|
5412
|
-
if (!ctx.phase
|
|
5099
|
+
if (!ctx.phase) {
|
|
5413
5100
|
const platforms = await (0, spec_paths_2.parsePlatformList)();
|
|
5414
5101
|
if (platforms.length >= 2) {
|
|
5415
5102
|
prompt += `## 📋 完整工作流程(两阶段分析)\n\n`;
|
|
@@ -5454,7 +5141,7 @@ status: "clarified"
|
|
|
5454
5141
|
// ── v8.1.0+: 逐文档模式自动注入 PRD 内容 + 前序文档摘要 ──
|
|
5455
5142
|
// v8.3.2+ 修复:PRD 注入不再仅限逐文档模式,Phase 1/2 模式下也必须注入需求内容
|
|
5456
5143
|
// v8.3.160+: 改为路径引用模式(不再全文塞进,让 AI 按需 Read)
|
|
5457
|
-
if (!isGlobal &&
|
|
5144
|
+
if (!isGlobal && ctx.iteration && ctx.iteration !== 'GLOBAL') {
|
|
5458
5145
|
try {
|
|
5459
5146
|
const iterDirForCtx = await (0, context_1.getIterationDir)(iter);
|
|
5460
5147
|
if (iterDirForCtx) {
|
|
@@ -5550,7 +5237,7 @@ status: "clarified"
|
|
|
5550
5237
|
prompt += `- 分析过程中,始终把迭代名称当作透明信息处理,不做任何功能推断\n\n`;
|
|
5551
5238
|
prompt += `## 分析范围说明\n`;
|
|
5552
5239
|
// v8.3.167+: 功能模块级分析专属指令
|
|
5553
|
-
if (ctx.feature
|
|
5240
|
+
if (ctx.feature) {
|
|
5554
5241
|
prompt += `- 当前是**功能模块级分析**,只分析「**${ctx.feature}**」功能模块\n`;
|
|
5555
5242
|
prompt += `- **只生成该功能模块的 overview/ 文档**,不要分析其他功能模块\n`;
|
|
5556
5243
|
prompt += `- 文档写入路径格式:\`${ctx.feature}/overview/{文件名}\`(如 \`${ctx.feature}/overview/REQUIREMENT.md\`)\n`;
|
|
@@ -5558,345 +5245,300 @@ status: "clarified"
|
|
|
5558
5245
|
prompt += `- 分析前请先 Read 全局索引文档,确保本模块内容与全局文档一致\n`;
|
|
5559
5246
|
prompt += `- 如果需求文档中该模块的描述不完整,标注"文档未充分描述",不要自行脑补\n\n`;
|
|
5560
5247
|
}
|
|
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 +=
|
|
5248
|
+
prompt += `- 当前是**迭代级分析**,需产出全部 8 个文档,覆盖需求→技术→测试→评审→风险→依赖→监控→UI规格\n`;
|
|
5249
|
+
if (ctx.platform) {
|
|
5250
|
+
prompt += `- **只分析 ${ctx.platform} 端**:从 CONSTITUTION.md 读取端列表,但只生成 ${ctx.platform} 端的专属文档\n`;
|
|
5251
|
+
prompt += `- 在 020-specs/{功能模块}/${ctx.platform}/ 下写入该端专属文档(ANALYSIS.md、TECH.md、TEST.md 等)\n`;
|
|
5252
|
+
prompt += `- 迭代综合文档写入 020-specs/overview/(REQUIREMENT.md、DEPS.md、RISK.md 等)\n`;
|
|
5253
|
+
prompt += `- **不要生成**其他端的子目录和文档\n`;
|
|
5254
|
+
}
|
|
5255
|
+
// ── v6.61.0+: 阶段专属指令(Phase 2)──
|
|
5256
|
+
if (ctx.phase === '2') {
|
|
5257
|
+
prompt += `## 要求\n\n`;
|
|
5258
|
+
prompt += `### Step 1: 读取全局上下文(Phase 1 产出)\n`;
|
|
5259
|
+
prompt += `依次 Read 以下文件,建立全局技术架构认知:\n`;
|
|
5260
|
+
prompt += `- Read .speccore/CONSTITUTION.md\n`;
|
|
5261
|
+
prompt += `- Read 020-specs/PLATFORMS.md → 获取端列表\n`;
|
|
5262
|
+
prompt += `- Read 020-specs/overview/REQUIREMENT.md → 需求规格\n`;
|
|
5263
|
+
prompt += `- Read 020-specs/overview/ANALYSIS.md → 分析报告\n`;
|
|
5264
|
+
prompt += `- Read 020-specs/overview/TECH.md → 整体技术架构\n`;
|
|
5265
|
+
prompt += `- Read 020-specs/overview/RISK.md、DEPS.md、REVIEW.md、MONITOR.md(如存在)\n\n`;
|
|
5266
|
+
prompt += `### Step 2: 按功能模块组织,为每个功能模块的每个端撰写专属文档(v8.3.1+ 强制四文档)\n`;
|
|
5267
|
+
prompt += `先 Read \`020-specs/overview/FUNCTION_MAP.md\` 获取功能模块清单。\n\n`;
|
|
5268
|
+
prompt += `**分批策略(v8.3.160+)**:\n`;
|
|
5269
|
+
prompt += `- 如功能模块数量 ≤ 2:一次生成所有模块的所有端文档\n`;
|
|
5270
|
+
prompt += `- 如功能模块数量 > 2:一次只处理 **1-2 个功能模块** 的所有端文档,确保质量深入\n`;
|
|
5271
|
+
prompt += `- 剩余模块在输出末尾标注 \`[PENDING: {模块名列表}]\`,供续批处理\n`;
|
|
5272
|
+
prompt += `- 续批命令:\`speccore analyze --prompt -I ${iter} --phase 2\`\n`;
|
|
5273
|
+
prompt += `- **关键原则**:宁可少分析几个模块,也要保证每个模块的分析质量(文档充实、有具体代码引用、表格有数据行)\n\n`;
|
|
5274
|
+
prompt += `每个功能模块涉及的每个端,分别撰写以下 **4 份文档**,缺一不可:\n\n`;
|
|
5275
|
+
prompt += `**文件路径格式**:\`{功能模块名}/{端名}/{文件名}\`(如 \`booking/backend/TECH.md\`、\`booking/h5-mobile/UI_SPEC.md\`)\n`;
|
|
5276
|
+
prompt += `> ⚠️ **严禁**只写 \`{端}/{文件}\` 格式(如 \`backend/TECH.md\`),这会导致所有功能模块的文档混在一起无法区分\n\n`;
|
|
5277
|
+
prompt += `**1. {功能模块}/{端}/TECH.md — 该端在该功能模块下的技术方案(必须对齐 overview/TECH.md 架构)**\n`;
|
|
5278
|
+
prompt += ` - 该端的分层架构、模块划分、核心接口设计(路径/方法/参数/响应/状态码)\n`;
|
|
5279
|
+
prompt += ` - 数据库表结构(字段/类型/索引/约束,如适用)\n`;
|
|
5280
|
+
prompt += ` - 业务规则实现(含边界条件和异常流)\n`;
|
|
5281
|
+
prompt += ` - ⚠️ **必须包含「业务-代码映射」章节**:在 TECH.md 末尾添加表格,列出本端涉及的业务模块及其对应的代码实体(文件/表/API/组件等),关系类型由你根据技术栈自主决定(如 api_controller、uses_table、page、component、route、middleware、interceptor、gateway 等)\n`;
|
|
5282
|
+
prompt += ` - 表格格式:| 业务模块 | 代码实体 | 关系类型 | 说明 |\n`;
|
|
5283
|
+
prompt += ` - 示例:| 会议室档案 | backend/RoomController.java | api_controller | REST 控制器 |\n`;
|
|
5284
|
+
prompt += ` - 示例:| 会议室档案 | admin-web/src/pages/RoomList.vue | page | 列表页 |\n\n`;
|
|
5285
|
+
prompt += `**2. {功能模块}/{端}/TEST.md — 该端在该功能模块下的测试计划**\n`;
|
|
5286
|
+
prompt += ` - 覆盖该端所有功能模块的测试用例(含前置条件、步骤、预期结果)\n`;
|
|
5287
|
+
prompt += ` - 边界值和异常输入测试\n`;
|
|
5288
|
+
prompt += ` - 必须覆盖 REQUIREMENT.md 中该端的验收标准\n\n`;
|
|
5289
|
+
prompt += `**3. {功能模块}/{端}/UI_SPEC.md — 该端在该功能模块下的 UI 规格(仅前端端需要)**\n`;
|
|
5290
|
+
prompt += ` - 页面结构与路由(每个页面的路径、入口、权限)\n`;
|
|
5291
|
+
prompt += ` - 组件清单和字段→UI 映射\n`;
|
|
5292
|
+
prompt += ` - 状态枚举和错误处理策略\n\n`;
|
|
5293
|
+
prompt += `**4. {功能模块}/{端}/DEV_GUIDE.md — 该端在该功能模块下的开发者实现指南(v8.3.1+ 新增强制要求)**\n`;
|
|
5294
|
+
prompt += ` - ⛔ **禁止只写框架/占位符** — 必须是可执行的实现指导\n`;
|
|
5295
|
+
prompt += ` - **本端改造范围清单**:列出本端所有需要新增/修改/删除的文件(相对项目根目录的具体路径),表格表头:| # | 类型 | 文件/目录 | 说明 |\n`;
|
|
5296
|
+
prompt += ` - **本端实施步骤(按依赖排序)**:Step-by-step,具体到文件/函数级,说明每步为什么先做\n`;
|
|
5297
|
+
prompt += ` - **代码级指引**:改哪几行、改什么、改造后的关键代码片段\n`;
|
|
5298
|
+
prompt += ` - **与后端联调**:接口状态、联调顺序、Mock 方案,表格表头:| # | 接口 | 后端状态 | 联调顺序 | Mock 方案 |\n`;
|
|
5299
|
+
prompt += ` - **验证方式**:本端特有的验证命令/操作和通过标准,表格表头:| # | 验证项 | 命令/操作 | 通过标准 |\n`;
|
|
5300
|
+
prompt += ` - **本端坑点**:已知陷阱及解决方式\n`;
|
|
5301
|
+
prompt += ` - **回滚方案**:代码回滚方式、配置回滚方式\n\n`;
|
|
5302
|
+
prompt += `### Step 3: 一致性检查\n`;
|
|
5303
|
+
prompt += `- 各端 TECH.md 的技术选型必须与 overview/TECH.md 一致\n`;
|
|
5304
|
+
prompt += `- UI_SPEC.md 的字段映射必须与后端 API 响应字段一一对应\n`;
|
|
5305
|
+
prompt += `- TEST.md 必须覆盖 REQUIREMENT.md 中该端的验收标准\n\n`;
|
|
5306
|
+
prompt += `### Step 3: 自检 — 确保所有文档已实质填充\n`;
|
|
5307
|
+
prompt += `生成完成后,逐个检查每个文档:\n`;
|
|
5308
|
+
prompt += `- 如果文档中仍含有 \`<!-- SPEC-SKELETON -->\` 标记或空表格 → **必须重新填充**\n`;
|
|
5309
|
+
prompt += `- 如果「改造范围」表格只有表头没有数据行 → **必须补充具体文件路径**\n`;
|
|
5310
|
+
prompt += `- 如果「验证方式」表格只有表头 → **必须补充可执行的命令/操作**\n`;
|
|
5311
|
+
prompt += `- DEV_GUIDE.md 必须有至少 3 个具体文件路径和 3 个可执行验证步骤\n\n`;
|
|
5601
5312
|
prompt += `### 写入方式\n`;
|
|
5602
|
-
prompt +=
|
|
5313
|
+
prompt += `**按功能模块批量写入**(推荐):一次 --apply 写入一个功能模块的所有端文档\n`;
|
|
5314
|
+
prompt += `\`\`\`bash\n`;
|
|
5315
|
+
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`;
|
|
5316
|
+
prompt += `\`\`\`\n\n`;
|
|
5317
|
+
prompt += `**或者逐功能模块-逐端写入**(每端一次 --apply):\n`;
|
|
5318
|
+
prompt += `\`\`\`bash\n`;
|
|
5319
|
+
prompt += `speccore analyze --apply '{"{功能模块}/{端}/TECH.md":"...","{功能模块}/{端}/TEST.md":"..."}' -I ${iter}\n`;
|
|
5320
|
+
prompt += `\`\`\`\n\n`;
|
|
5321
|
+
prompt += `### ⛔ 强制约束(违反则分析无效)\n`;
|
|
5322
|
+
prompt += `- **禁止省略任何一份文档** — 4 份文档(TECH.md + TEST.md + UI_SPEC.md + DEV_GUIDE.md)缺一不可\n`;
|
|
5323
|
+
prompt += `- **禁止输出空表格** — 每个 Markdown 表格必须有至少 1 行数据,只有表头的表格视为未完成\n`;
|
|
5324
|
+
prompt += `- **禁止输出占位符** — 不允许写「待填充」、「TODO」、「...」、「xxx」等占位内容\n`;
|
|
5325
|
+
prompt += `- **DEV_GUIDE.md 最低标准**:改造范围表格 ≥3 行、实施步骤 ≥3 步、验证方式表格 ≥3 行、坑点 ≥2 条\n`;
|
|
5326
|
+
prompt += `- **如果 token 不足**:优先保证 DEV_GUIDE.md 完整,其他文档可精简但不可空\n`;
|
|
5327
|
+
prompt += `- **自检必做**:写入前用 Read 工具检查每个文档,确认不含 \`<!-- SPEC-SKELETON -->\` 且所有表格有数据行\n\n`;
|
|
5603
5328
|
}
|
|
5604
5329
|
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
|
-
}
|
|
5330
|
+
// v6.61.0+: 一次性生成所有文档(global/ + {端}/)
|
|
5331
|
+
prompt += `## 要求\n1. Read .speccore/PATTERNS/TEMPLATES/specs/ 下的专业模板(如目录不存在或为空,用你的专业知识自由撰写,绝不允许产出一行垃圾)\n`;
|
|
5332
|
+
const templateMap = {
|
|
5333
|
+
'ANALYSIS.md': 'ANALYSIS-template.md', 'TECH.md': 'TECH-template.md', 'TEST.md': 'TEST-template.md',
|
|
5334
|
+
'REVIEW.md': 'REVIEW-template.md', 'RISK.md': 'RISK-template.md', 'DEPS.md': 'DEPS-template.md',
|
|
5335
|
+
'MONITOR.md': 'MONITOR-template.md', 'UI_SPEC.md': 'UI_SPEC-template.md'
|
|
5336
|
+
};
|
|
5337
|
+
for (const doc of taskDocs) {
|
|
5338
|
+
const tpl = templateMap[doc[0]] || '';
|
|
5339
|
+
prompt += ` - ${doc[0]} → 参考 ${tpl}\n`;
|
|
5340
|
+
}
|
|
5341
|
+
prompt += `2. 读取全局层产物(建立全局视角,按需 Read):\n`;
|
|
5342
|
+
prompt += ` \`.speccore/GLOBAL/\` 下已有系统级产物,按需读取了解当前状态:\n`;
|
|
5343
|
+
prompt += ` - \`requirements/REQUIREMENT.md\`、\`overview/FUNCTION_MAP.md\`、\`overview/API_CONTRACT.yaml\`\n`;
|
|
5344
|
+
prompt += ` - \`overview/ARCHITECTURE.md\`、\`platforms/{端}/_INDEX.md\`、\`platforms/_shared/_ASSOCIATION.md\`\n`;
|
|
5345
|
+
prompt += ` - 如不存在则跳过\n\n`;
|
|
5346
|
+
// v8.3.160+: 迭代级分析结合源码上下文
|
|
5347
|
+
if (ctx.withCode) {
|
|
5348
|
+
prompt += `2b. **源码上下文(--with-code)**:\n`;
|
|
5349
|
+
// v8.3.160+: 大项目按端分段读取,避免一次性加载全量结构化数据
|
|
5350
|
+
prompt += ` - 先 Read \`.speccore/cache/structured-data.json\` 的 stats 部分(了解各端规模)\n`;
|
|
5351
|
+
prompt += ` - **按需读取各端分段文件**:\`.speccore/cache/structured-data.{端名}.json\`(只读取本功能模块涉及的端)\n`;
|
|
5352
|
+
// v8.3.248+: 直接注入各端工程路径表(此前仅提示 AI「从 CONSTITUTION.md 获取源码路径」,
|
|
5353
|
+
// AI 需自行翻表定位;现在直接给出,减少一步间接查找、杜绝瞎猜)
|
|
5354
|
+
try {
|
|
5355
|
+
const projectInfoMap = await (0, spec_paths_1.parseProjectInfo)();
|
|
5356
|
+
if (projectInfoMap.size > 0) {
|
|
5357
|
+
prompt += ` - **各端工程路径**(结合源码分析时按此定位,CONSTITUTION.md 工程表权威):\n`;
|
|
5358
|
+
for (const [identifier, info] of projectInfoMap) {
|
|
5359
|
+
prompt += ` - \`${identifier}\`(${info.projectType || '-'})→ \`${info.srcPath}\`\n`;
|
|
5718
5360
|
}
|
|
5719
5361
|
}
|
|
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
5362
|
}
|
|
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
|
-
|
|
5363
|
+
catch { /* 路径解析失败不阻断分析 */ }
|
|
5364
|
+
prompt += ` - 再按需 Read 各端源码中与本功能模块相关的关键文件,重点关注:接口定义、数据模型、业务逻辑实现、状态管理\n\n`;
|
|
5365
|
+
}
|
|
5366
|
+
prompt += `3. 读取迭代需求文档(按功能模块组织):\n`;
|
|
5367
|
+
prompt += ` a. 先读 010-requirements/INDEX.md — 获取功能模块清单\n`;
|
|
5368
|
+
prompt += ` b. **按功能模块逐一读取**:每个功能模块独立分析,不要跨模块混淆\n`;
|
|
5369
|
+
prompt += ` - 读取该模块对应的 010-requirements/converted/*.md 或 features/{模块}/SOURCE.md(旧项目为 README.md)\n`;
|
|
5370
|
+
prompt += ` - 读取该模块涉及的原型文件(HTML/图片/链接)\n`;
|
|
5371
|
+
prompt += ` c. **文档长度自适应**:如果单个需求文档超过 5000 字,先快速扫描目录和章节标题,标记关键章节,再深入阅读\n`;
|
|
5372
|
+
prompt += `4. **按功能模块逐一撰写文档(重要)**:\n`;
|
|
5373
|
+
prompt += ` - 每个功能模块独立生成一套综合文档,放在 \`{功能模块}/overview/\` 目录下\n`;
|
|
5374
|
+
prompt += ` - 不要生成全局的 \`overview/REQUIREMENT.md\`(不要把所有模块拟合在一起)\n`;
|
|
5375
|
+
prompt += ` - 全局 \`overview/\` 只保留 FUNCTION_MAP.md、INTERACTION_MAP.md、PLATFORMS.md 作为索引\n`;
|
|
5376
|
+
prompt += ` - 示例:\`booking/overview/REQUIREMENT.md\`、\`booking/overview/ANALYSIS.md\`、\`payment/overview/REQUIREMENT.md\` 等\n`;
|
|
5377
|
+
prompt += `5. **文档忠实度约束(最高优先级)**:\n`;
|
|
5378
|
+
prompt += ` - **严禁臆造**:只能写需求文档中明确提及的功能、接口、页面、字段、业务规则\n`;
|
|
5379
|
+
prompt += ` - **严禁扩展**:不要基于迭代名称或你的知识补充文档中未提及的内容\n`;
|
|
5380
|
+
prompt += ` - **严禁推断**:不要从一句话推断出整个功能模块,只写文档中明确描述的内容\n`;
|
|
5381
|
+
prompt += ` - **边界处理**:如果文档对某功能描述不完整,标注"文档未充分描述",不要自行脑补完整方案\n`;
|
|
5382
|
+
prompt += ` - **交叉验证**:每写一个功能点,回头检查需求文档中是否有对应描述,没有则删除\n`;
|
|
5383
|
+
prompt += `6. 每个文档都要具体内容(禁止"待填充")\n`;
|
|
5384
|
+
prompt += `7. **辅助文档强制要求(v8.3.1+ 修复空模板问题)**:以下文档常被 AI 遗漏或敷衍,必须同等重视:\n`;
|
|
5385
|
+
prompt += ` - **FUNCTION_MAP.md**:功能单元 × 端映射表,必须用 Markdown 表格,每行一个功能单元,不允许合并。表头:| # | 功能单元 | 涉及端 | 全局对比 | 共享能力 | 依赖任务 | 说明 |\n`;
|
|
5386
|
+
prompt += ` - **INTERACTION_MAP.md**:每个功能单元一个 Mermaid sequenceDiagram,展示跨端交互时序,箭头标注接口路径\n`;
|
|
5387
|
+
prompt += ` - **DEV_GUIDE.md(迭代级)**:全局实施指导,必须包含:分支策略、改造范围清单(具体到文件路径)、改造顺序与依赖、代码级指引(含改造前后对比)、接口契约对照表、每步验证方式、回滚方案、常见坑点\n`;
|
|
5388
|
+
prompt += ` - **MONITOR.md**:必须按 Fatal/Critical/Warning/Info 四级定义告警规则,不能只列指标名称\n`;
|
|
5389
|
+
prompt += ` - **REVIEW.md**:安全检查必须逐接口列出鉴权需求,不能笼统写"需要鉴权"\n`;
|
|
5390
|
+
prompt += ` ⛔ **自检规则**:生成完成后检查每个文档,如果仍含 \`<!-- SPEC-SKELETON -->\` 或空表格只有表头 → 必须重新填充\n`;
|
|
5391
|
+
prompt += `8. **端发现(重要)**:先确定项目有哪些端,再按端组织文档\n`;
|
|
5392
|
+
prompt += ` - 第 1 步:Read .speccore/CONSTITUTION.md\n`;
|
|
5393
|
+
prompt += ` - 第 2 步:从「## 端列表」章节提取端名(这是全局权威来源)\n`;
|
|
5394
|
+
prompt += ` - 第 3 步:如果没有「端列表」章节,从「对应需求端」列提取\n`;
|
|
5395
|
+
prompt += ` - 第 4 步:如果以上都无法确定,根据需求文档内容判断\n`;
|
|
5396
|
+
prompt += ` - 第 5 步:将发现的端列表写入 020-specs/PLATFORMS.md\n`;
|
|
5397
|
+
// v6.70.0+: REQUIREMENT.md 以产品视角撰写(不按端分章节)
|
|
5398
|
+
// v8.3.160+: 压缩写作风格说明
|
|
5399
|
+
prompt += `9. **REQUIREMENT.md 写作风格**:产品/用户视角,按业务场景组织章节(不按端分节)\n`;
|
|
5400
|
+
prompt += ` - 必须包含:产品愿景、用户画像、核心场景地图(标注优先级)、功能全景图、功能优先级矩阵(P0/P1/P2)、发布里程碑、风险预判\n`;
|
|
5401
|
+
prompt += ` - 场景描述格式:用户操作 → 系统响应 → 业务规则 → 边界条件 → 验收标准\n`;
|
|
5402
|
+
prompt += ` - ✅ 正确:「用户点击预订,系统检查可用性,如可用则锁定并创建订单」\n`;
|
|
5403
|
+
prompt += ` - ❌ 错误:「后端新增 /api/bookings 接口,接收 roomId」→ 技术细节留给 TECH.md\n`;
|
|
5404
|
+
prompt += ` - 端的信息只在「功能模块清单」表格中标注,正文不区分端\n`;
|
|
5405
|
+
// v6.49.14+: 功能模块清单必须含涉及端列 + 来源链接
|
|
5406
|
+
// v6.71.3+: 增加「与全局层对比」列
|
|
5407
|
+
prompt += `10. **功能模块清单(重要)**:每个功能模块的 \`overview/REQUIREMENT.md\` 都必须包含功能模块清单表格\n`;
|
|
5408
|
+
prompt += ` - 表格格式:| # | 功能模块 | 涉及端 | 全局对比 | 来源 | 说明 |\n`;
|
|
5409
|
+
prompt += ` - 「涉及端」:每个模块标注需要**新开发工作**的端(标准端名,逗号分隔)\n`;
|
|
5410
|
+
prompt += ` - 「涉及」= 该端需要写新接口/新页面/新逻辑\n`;
|
|
5411
|
+
prompt += ` - 「不涉及」= 只是提到、调用已有接口、纯展示 → 不标注\n`;
|
|
5412
|
+
prompt += ` - 端名必须与 CONSTITUTION.md「端列表」中的标准端名完全匹配\n`;
|
|
5413
|
+
prompt += ` - 「全局对比」:该功能模块与全局层已有功能的关系(必须标注)\n`;
|
|
5414
|
+
prompt += ` - 「新增」:全局层不存在,本迭代全新开发\n`;
|
|
5415
|
+
prompt += ` - 「扩展」:全局层已有基础功能,本迭代增加新字段/新接口/新页面\n`;
|
|
5416
|
+
prompt += ` - 「重构」:全局层已有,本迭代修改实现方式(不新增功能)\n`;
|
|
5417
|
+
prompt += ` - 「复用」:全局层已有,本迭代直接使用(无需开发)\n`;
|
|
5418
|
+
prompt += ` - 示例:「扩展:增加会议室设备管理」或「新增」\n`;
|
|
5419
|
+
prompt += ` - 「来源」:该功能模块在需求文档中的具体位置,用 Markdown 链接格式\n`;
|
|
5420
|
+
prompt += ` - 格式:[文档名](相对路径#章节锚点),如 [PRD v2.0](../010-requirements/sources/PRD-v2.0.md#3-2-会议室管理)\n`;
|
|
5421
|
+
prompt += ` - 如果模块来自 converted 文档:[xxx需求](../010-requirements/converted/xxx.md#相关章节)\n`;
|
|
5422
|
+
prompt += ` - 如果来自 features 目录:[xxx功能](../010-requirements/features/xxx/SOURCE.md)\n`;
|
|
5423
|
+
prompt += ` - 目的是让阅读者能直接点击跳转到原始需求位置\n`;
|
|
5424
|
+
prompt += ` - 示例:| M-01 | 会议室档案管理 | room-service, admin-web | 扩展:增加设备管理 | [PRD v2.0](../010-requirements/sources/PRD-v2.0.md#2-1) | 会议室 CRUD、设备管理 |\n`;
|
|
5425
|
+
prompt += ` - split 命令将读取「涉及端」列来决定创建哪些端的子任务目录\n`;
|
|
5426
|
+
// v6.70.0+: 跨端功能映射表(FUNCTION_MAP.md)
|
|
5427
|
+
// v6.71.3+: 增加与全局层关联分析
|
|
5428
|
+
prompt += `7a. **迭代需求与全局层关联分析(重要)**:在每个功能模块的 \`overview/REQUIREMENT.md\` 中,必须对比全局层产物\n`;
|
|
5429
|
+
prompt += ` - 对比迭代需求中的功能模块 vs .speccore/GLOBAL/overview/FUNCTION_MAP.md 中的功能单元\n`;
|
|
5430
|
+
prompt += ` - 标注每个功能模块的「全局对比」类型(新增/扩展/重构/复用)\n`;
|
|
5431
|
+
prompt += ` - 识别冲突:如迭代需求修改了全局层已有接口的字段/路径 → 在 RISK.md 中标注\n`;
|
|
5432
|
+
prompt += ` - 识别依赖:如迭代的新功能依赖全局层的某个功能 → 在 FUNCTION_MAP.md「依赖任务」中标注\n\n`;
|
|
5433
|
+
prompt += `7b. **跨端功能映射表(重要)**:全局索引 \`overview/FUNCTION_MAP.md\` 汇总所有功能模块的映射关系\n`;
|
|
5434
|
+
prompt += ` - 这是 split 阶段的核心输入,决定任务如何按功能单元拆分\n`;
|
|
5435
|
+
prompt += ` - 表格格式:| # | 功能单元 | 涉及端 | 全局对比 | 共享能力 | 依赖任务 | 说明 |\n`;
|
|
5436
|
+
prompt += ` - 「功能单元」必须与 REQUIREMENT.md 功能模块清单一一对应,不允许合并\n`;
|
|
5437
|
+
prompt += ` - 「涉及端」标注所有需要新开发工作的端(标准端名,逗号分隔)\n`;
|
|
5438
|
+
prompt += ` - 「全局对比」标注与全局层的关系(新增/扩展/重构/复用)\n`;
|
|
5439
|
+
prompt += ` - 「共享能力」标注跨任务共享的通用组件(如"审批引擎"、"消息通知"),无则填"无"\n`;
|
|
5440
|
+
prompt += ` - 「依赖任务」标注当前迭代内该功能单元依赖的其他功能单元编号\n`;
|
|
5441
|
+
prompt += ` - 「说明」描述跨端交互关系(如"admin-web 调用 booking-service 的 /api/rooms 接口")\n`;
|
|
5442
|
+
prompt += ` - **示例**:| M-01 | 会议室档案管理 | room-service, admin-web | 扩展 | 无 | 无 | admin-web 调用 room-service CRUD 接口 |\n`;
|
|
5443
|
+
prompt += ` - **错误示例**(禁止):将"审批流程"和"定时任务"合并为一行\n`;
|
|
5444
|
+
prompt += ` - FUNCTION_MAP.md 生成后,split 将**严格按此表**创建任务目录,不再由 AI 推断\n`;
|
|
5445
|
+
// v6.70.0+: 跨端交互图谱(INTERACTION_MAP.md)
|
|
5446
|
+
prompt += `7c. **跨端交互图谱(重要)**:全局索引 \`overview/INTERACTION_MAP.md\` 汇总所有功能模块的交互时序\n`;
|
|
5447
|
+
prompt += ` - 按功能单元组织,每个功能单元一个 Mermaid sequenceDiagram\n`;
|
|
5448
|
+
prompt += ` - 展示完整的业务交互时序:用户操作 → 前端处理 → 后端调用 → 数据返回\n`;
|
|
5449
|
+
prompt += ` - 明确标出后端服务之间的内部调用(产品文档写"系统处理"的地方)\n`;
|
|
5450
|
+
prompt += ` - 箭头标注接口路径,标注 [contract] 表示接口在 API_CONTRACT.yaml 中有定义\n`;
|
|
5451
|
+
prompt += ` - 序列图后附「接口契约索引」表格:步骤号、接口、消费者端、提供者端\n`;
|
|
5452
|
+
prompt += ` - 如涉及状态变更,附「状态流转」表格\n`;
|
|
5453
|
+
prompt += ` - INTERACTION_MAP.md 是前后端开发者的共同参考,补全产品文档中隐含的技术交互\n`;
|
|
5454
|
+
// 注入工程类型信息(v6.49.0+)
|
|
5455
|
+
// v8.3.160+: 精简为路径引用,具体维度让 AI 从 CONSTITUTION.md 读取
|
|
5456
|
+
const platformTypes = await (0, spec_paths_2.parsePlatformTypes)();
|
|
5457
|
+
if (platformTypes.size > 0) {
|
|
5458
|
+
prompt += `8. **工程类型识别**:CONSTITUTION.md 已配置各端的工程类型,请 Read 后据此生成针对性内容\n`;
|
|
5459
|
+
prompt += ` | 工程标识 | 工程类型 |\n`;
|
|
5460
|
+
prompt += ` | :--- | :--- |\n`;
|
|
5461
|
+
for (const [name, type] of platformTypes) {
|
|
5462
|
+
prompt += ` | ${name} | ${type} |\n`;
|
|
5899
5463
|
}
|
|
5464
|
+
prompt += `\n`;
|
|
5465
|
+
}
|
|
5466
|
+
// v8.3.182+: 显式文档路由表
|
|
5467
|
+
prompt += `## 📋 文档路由表(v8.3.182+)\n\n`;
|
|
5468
|
+
prompt += `> 以下表格明确每类文档的写入路径。索引类放 overview/,内容类放 {feature}/overview/,端专属放 {feature}/{端}/。\n\n`;
|
|
5469
|
+
prompt += `| 文档名 | 路由目标 | 文档类型 | 说明 |\n`;
|
|
5470
|
+
prompt += `| :--- | :--- | :--- | :--- |\n`;
|
|
5471
|
+
prompt += `| FUNCTION_MAP.md | 020-specs/overview/ | 索引类 | 功能单元 × 端映射表(全局) |\n`;
|
|
5472
|
+
prompt += `| INTERACTION_MAP.md | 020-specs/overview/ | 索引类 | 跨端交互时序图(全局) |\n`;
|
|
5473
|
+
prompt += `| PLATFORMS.md | 020-specs/overview/ | 索引类 | 端列表(全局) |\n`;
|
|
5474
|
+
prompt += `| CLARIFY.md | 020-specs/overview/ | 索引类 | 全局澄清(跨功能共性问题) |\n`;
|
|
5475
|
+
prompt += `| API_CONTRACT.yaml | 020-specs/overview/ | 索引类 | 跨功能接口契约(全局) |\n`;
|
|
5476
|
+
prompt += `| ARCHITECTURE.md | 020-specs/overview/ | 索引类 | 迭代全局架构(服务拓扑/数据流) |\n`;
|
|
5477
|
+
prompt += `| REQUIREMENT.md | 020-specs/{feature}/overview/ | 内容类 | 该功能模块的需求规格 |\n`;
|
|
5478
|
+
prompt += `| ANALYSIS.md | 020-specs/{feature}/overview/ | 内容类 | 该功能模块的需求分析 |\n`;
|
|
5479
|
+
prompt += `| TECH.md | 020-specs/{feature}/overview/ | 内容类 | 该功能模块的技术方案(Phase 1) |\n`;
|
|
5480
|
+
prompt += `| RISK.md | 020-specs/{feature}/overview/ | 内容类 | 该功能模块的风险评估 |\n`;
|
|
5481
|
+
prompt += `| DEPS.md | 020-specs/{feature}/overview/ | 内容类 | 该功能模块的依赖清单 |\n`;
|
|
5482
|
+
prompt += `| REVIEW.md | 020-specs/{feature}/overview/ | 内容类 | 该功能模块的评审检查项 |\n`;
|
|
5483
|
+
prompt += `| MONITOR.md | 020-specs/{feature}/overview/ | 内容类 | 该功能模块的监控指标 |\n`;
|
|
5484
|
+
prompt += `| DEV_GUIDE.md | 020-specs/{feature}/overview/ | 内容类 | 该功能模块的实现指南(Phase 1) |\n`;
|
|
5485
|
+
prompt += `| TECH.md | 020-specs/{feature}/{端}/ | 端专属 | 该模块该端的技术方案(Phase 2) |\n`;
|
|
5486
|
+
prompt += `| TEST.md | 020-specs/{feature}/{端}/ | 端专属 | 该模块该端的测试用例(Phase 2) |\n`;
|
|
5487
|
+
prompt += `| UI_SPEC.md | 020-specs/{feature}/{端}/ | 端专属 | 该模块该端的 UI 规范(Phase 2) |\n`;
|
|
5488
|
+
prompt += `| DEV_GUIDE.md | 020-specs/{feature}/{端}/ | 端专属 | 该模块该端的实现指南(Phase 2) |\n\n`;
|
|
5489
|
+
prompt += `> **重要规则**:\n`;
|
|
5490
|
+
prompt += `> - 索引类文档(FUNCTION_MAP.md / INTERACTION_MAP.md / PLATFORMS.md / CLARIFY.md / API_CONTRACT.yaml)**必须**写入 020-specs/overview/,严禁写入 {feature}/overview/\n`;
|
|
5491
|
+
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`;
|
|
5492
|
+
prompt += `> - ANALYSIS.md 属于内容类,严禁放在 020-specs/overview/ 下\n`;
|
|
5493
|
+
prompt += `> - 全局 overview/REQUIREMENT.md 由 split 阶段从各功能模块 {feature}/overview/REQUIREMENT.md 自动聚合生成,AI 不要直接写入(会被拦截)\n\n`;
|
|
5494
|
+
const dirStepNum = platformTypes.size > 0 ? 10 : 9;
|
|
5495
|
+
prompt += `${dirStepNum}. **目录结构(严格遵循,禁止自创目录)**:\n`;
|
|
5496
|
+
prompt += ` - **全局索引**(跨端汇总)→ 通过 --apply 写入,CLI 自动路由到 \`020-specs/overview/{文件名}\`\n`;
|
|
5497
|
+
prompt += ` - FUNCTION_MAP.md(功能单元 × 端映射表)\n`;
|
|
5498
|
+
prompt += ` - INTERACTION_MAP.md(跨端交互时序图)\n`;
|
|
5499
|
+
prompt += ` - PLATFORMS.md(端列表)\n`;
|
|
5500
|
+
prompt += ` - **功能模块综合文档**(每个功能模块独立)→ 通过 --apply 写入,CLI 自动路由到 \`020-specs/{功能模块名}/overview/{文件名}\`\n`;
|
|
5501
|
+
prompt += ` - {功能模块}/overview/REQUIREMENT.md(该模块的需求规格)\n`;
|
|
5502
|
+
prompt += ` - {功能模块}/overview/ANALYSIS.md(该模块的需求分析)\n`;
|
|
5503
|
+
prompt += ` - {功能模块}/overview/TECH.md(该模块的技术方案)\n`;
|
|
5504
|
+
prompt += ` - {功能模块}/overview/RISK.md(该模块的风险评估)\n`;
|
|
5505
|
+
prompt += ` - {功能模块}/overview/DEPS.md(该模块的依赖清单)\n`;
|
|
5506
|
+
prompt += ` - {功能模块}/overview/REVIEW.md(该模块的评审检查项)\n`;
|
|
5507
|
+
prompt += ` - {功能模块}/overview/MONITOR.md(该模块的监控指标)\n`;
|
|
5508
|
+
prompt += ` - {功能模块}/overview/DEV_GUIDE.md(该模块的实现指南)\n`;
|
|
5509
|
+
prompt += ` - **端专属文档**(按功能模块组织)→ 通过 --apply 写入,CLI 自动路由到 \`020-specs/{功能模块名}/{端名}/{文件名}\`\n`;
|
|
5510
|
+
prompt += ` - {功能模块}/{端}/TECH.md(该模块该端的技术方案)\n`;
|
|
5511
|
+
prompt += ` - {功能模块}/{端}/TEST.md(该模块该端的测试用例)\n`;
|
|
5512
|
+
prompt += ` - {功能模块}/{端}/UI_SPEC.md(该模块该端的 UI 规范,仅前端端)\n`;
|
|
5513
|
+
prompt += ` - {功能模块}/{端}/DEV_GUIDE.md(该模块该端的实现指南)\n`;
|
|
5514
|
+
// v8.3.171+: 简化路径约束(骨架已预创建,CLI 掌控路径)
|
|
5515
|
+
prompt += ` - 文件已预创建,用 Write 工具覆盖已有路径即可,不要创建新文件或新目录\n`;
|
|
5516
|
+
if (ctx.phase !== '1') {
|
|
5517
|
+
// 端专业性约束只在默认模式(全量)中输出
|
|
5518
|
+
prompt += `\n## ⚠️ 端专业性约束\n`;
|
|
5519
|
+
prompt += `CONSTITUTION.md 中配置了多个端,每个端的文档必须有该端专属内容。\n`;
|
|
5520
|
+
prompt += `**先识别端类型,再应用对应的专业维度**:\n\n`;
|
|
5521
|
+
// v6.71.0+: 前后端文档差异化
|
|
5522
|
+
prompt += `### 后端服务(*service)必含内容 — 技术视角\n`;
|
|
5523
|
+
prompt += `- API 接口定义(路径/方法/参数/响应字段/状态码/错误码)\n`;
|
|
5524
|
+
prompt += `- 数据库表结构(字段/类型/索引/约束)\n`;
|
|
5525
|
+
prompt += `- 业务规则(含边界条件和异常流)\n`;
|
|
5526
|
+
prompt += `- 缓存策略/并发与事务/消息队列(如涉及)\n`;
|
|
5527
|
+
prompt += `- 安全:SQL 注入防护/接口鉴权/数据脱敏\n`;
|
|
5528
|
+
prompt += `- 性能:QPS 预估/慢查询优化/连接池配置\n`;
|
|
5529
|
+
prompt += `- **不需要**产品视角的需求描述(用户故事、业务场景已在 {功能模块}/overview/REQUIREMENT.md 中)\n\n`;
|
|
5530
|
+
prompt += `### 前端端(h5 / admin-web / miniapp)必含内容 — 产品+技术双视角\n`;
|
|
5531
|
+
prompt += `- **产品视角(主要)**:\n`;
|
|
5532
|
+
prompt += ` - 用户旅程:该端用户如何完成核心任务(步骤流程图)\n`;
|
|
5533
|
+
prompt += ` - 页面清单:所有页面名称、路径、核心功能、入口位置\n`;
|
|
5534
|
+
prompt += ` - 交互设计:关键操作流程、状态变化、异常提示方式\n`;
|
|
5535
|
+
prompt += ` - 字段展示:每个页面展示哪些字段、字段来源(后端哪个接口)\n`;
|
|
5536
|
+
prompt += ` - 权限控制:哪些页面/按钮需要权限、权限粒度\n`;
|
|
5537
|
+
prompt += `- **技术视角(辅助)**:\n`;
|
|
5538
|
+
prompt += ` - 页面路由表 + 组件清单\n`;
|
|
5539
|
+
prompt += ` - API 调用清单:前端调用了哪些后端接口(路径+方法+用途)\n`;
|
|
5540
|
+
prompt += ` - 状态管理:全局状态设计、与后端数据同步策略\n`;
|
|
5541
|
+
prompt += ` - 适配/性能/安全等前端专项(按端类型选择)\n\n`;
|
|
5900
5542
|
}
|
|
5901
5543
|
// v6.60.0+: 文档与端的对应关系(不再分 Phase)
|
|
5902
5544
|
// v6.71.0+: 前后端文档差异化
|
|
@@ -5924,16 +5566,13 @@ status: "clarified"
|
|
|
5924
5566
|
prompt += `- 分析完成后会自动生成 QUALITY_AUDIT.md 质量报告,检查各端内容是否完整\n`;
|
|
5925
5567
|
}
|
|
5926
5568
|
// 步骤 2-7 已在上面的 phase 分支中处理
|
|
5927
|
-
const taskFlag = isTask && ctx.task ? ` --task ${ctx.task}` : '';
|
|
5928
5569
|
const platformFlag = ctx.platform ? ` --platform ${ctx.platform}` : '';
|
|
5929
5570
|
// ── 链式生成指令(v6.45.0+)──
|
|
5930
|
-
if (hasUserTemplates
|
|
5571
|
+
if (hasUserTemplates) {
|
|
5931
5572
|
prompt += `\n## 🔗 链式生成(重要)\n\n`;
|
|
5932
5573
|
prompt += `请按以下顺序**逐个生成**文档,每个文档生成后立即 --apply 写入,然后 Read 自己的产出再生成下一个:\n\n`;
|
|
5933
5574
|
// 定义生成顺序
|
|
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'];
|
|
5575
|
+
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
5576
|
const orderedDocs = taskDocs.filter(([n]) => chainOrder.includes(n));
|
|
5938
5577
|
const customDocs = taskDocs.filter(([n]) => !chainOrder.includes(n));
|
|
5939
5578
|
const ordered = [...orderedDocs.sort((a, b) => chainOrder.indexOf(a[0]) - chainOrder.indexOf(b[0])), ...customDocs];
|
|
@@ -5947,19 +5586,19 @@ status: "clarified"
|
|
|
5947
5586
|
}
|
|
5948
5587
|
prompt += `\n每个文档用单独的 --apply 调用写入:\n`;
|
|
5949
5588
|
for (const doc of ordered) {
|
|
5950
|
-
prompt += `speccore analyze --apply '{"${doc[0]}":"..."}' -I ${iter}${
|
|
5589
|
+
prompt += `speccore analyze --apply '{"${doc[0]}":"..."}' -I ${iter}${platformFlag}\n`;
|
|
5951
5590
|
}
|
|
5952
5591
|
prompt += `\n`;
|
|
5953
5592
|
}
|
|
5954
5593
|
else if (ctx.phase !== '2') {
|
|
5955
5594
|
// 传统模式:一次性写入
|
|
5956
|
-
prompt += `7. 写入: speccore analyze --apply '{${taskDocs.map(([n]) => `"${n}":"..."`).join(',')}}' -I ${iter}${
|
|
5595
|
+
prompt += `7. 写入: speccore analyze --apply '{${taskDocs.map(([n]) => `"${n}":"..."`).join(',')}}' -I ${iter}${platformFlag}\n\n`;
|
|
5957
5596
|
}
|
|
5958
5597
|
// v6.60.0+: 移除两阶段分析提示,一次性生成所有文档
|
|
5959
5598
|
prompt += '\n' + (0, questions_1.buildAutoModeInstruction)('analyze', iter) + '\n';
|
|
5960
5599
|
// ── v6.65.0+: Phase 1 完成后主动询问用户是否继续 Phase 2 ──
|
|
5961
5600
|
// v6.70.0+: 自动模式下跳过确认,AI 直接推断执行
|
|
5962
|
-
if (!ctx.phase && !
|
|
5601
|
+
if (!ctx.phase && !autoMode) {
|
|
5963
5602
|
prompt += `\n## ⚠️ 重要:Phase 1 完成后的下一步\n\n`;
|
|
5964
5603
|
prompt += `当你通过 --apply 写入所有综合文档后,CLI 会检测到项目有多个端(≥2 个端)。\n`;
|
|
5965
5604
|
prompt += `**此时你需要主动询问用户**:\n\n`;
|
|
@@ -5970,7 +5609,7 @@ status: "clarified"
|
|
|
5970
5609
|
prompt += `\`speccore analyze --prompt -I ${iter} --phase 2\`\n\n`;
|
|
5971
5610
|
prompt += `**不要等待 CLI 的提示信息**,CLI 的输出你可能看不到。你需要主动询问用户。\n\n`;
|
|
5972
5611
|
}
|
|
5973
|
-
if (autoMode && !ctx.phase
|
|
5612
|
+
if (autoMode && !ctx.phase) {
|
|
5974
5613
|
prompt += `\n## 🤖 自动模式说明\n\n`;
|
|
5975
5614
|
prompt += `当前处于自动模式(--auto),不需要人工确认。\n`;
|
|
5976
5615
|
prompt += `请在生成综合文档后,**直接继续**生成各端专属文档(Phase 2)。\n`;
|
|
@@ -6007,7 +5646,7 @@ status: "clarified"
|
|
|
6007
5646
|
}
|
|
6008
5647
|
}
|
|
6009
5648
|
// ── v6.52.0+: 图谱 RAG 上下文注入(analyze 阶段也检索项目关联内容)──
|
|
6010
|
-
if (
|
|
5649
|
+
if (ctx.phase !== '2') {
|
|
6011
5650
|
try {
|
|
6012
5651
|
const ragResult = await (0, unified_retrieval_1.unifiedSearch)(((0, task_utils_1.findProjectRoot)() || process.cwd()), {
|
|
6013
5652
|
query: ctx.iteration || '',
|
|
@@ -6252,26 +5891,6 @@ async function buildContractFirstPrompt(iteration) {
|
|
|
6252
5891
|
prompt += `- 契约应**精确且完整**,避免后续各端分析时出现接口不一致\n\n`;
|
|
6253
5892
|
return await injectGraphSummary(prompt);
|
|
6254
5893
|
}
|
|
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
5894
|
// ═══════════════════════════════════════════════════════════
|
|
6276
5895
|
// ═══════════════════════════════════════════════════════════
|
|
6277
5896
|
/**
|
|
@@ -7007,7 +6626,7 @@ async function buildPipelineStepPrompt(args) {
|
|
|
7007
6626
|
// 全局层 Pipeline 步骤映射
|
|
7008
6627
|
if (currentStep === 'init' || currentStep === 'discovery') {
|
|
7009
6628
|
prompt = (0, prompt_builder_1.buildPromptText)(await (0, prompt_builder_1.buildPrompt)('analyze', {
|
|
7010
|
-
iteration: iter,
|
|
6629
|
+
iteration: iter, platform: options.platform,
|
|
7011
6630
|
analyzeOptions: options,
|
|
7012
6631
|
}));
|
|
7013
6632
|
}
|
|
@@ -7040,7 +6659,7 @@ async function buildPipelineStepPrompt(args) {
|
|
|
7040
6659
|
}
|
|
7041
6660
|
else if (currentStep === 'phase1-prompt') {
|
|
7042
6661
|
prompt = (0, prompt_builder_1.buildPromptText)(await (0, prompt_builder_1.buildPrompt)('analyze', {
|
|
7043
|
-
iteration: iter,
|
|
6662
|
+
iteration: iter, platform: options.platform,
|
|
7044
6663
|
analyzeOptions: options,
|
|
7045
6664
|
}));
|
|
7046
6665
|
}
|
|
@@ -7074,7 +6693,7 @@ async function buildPipelineStepPrompt(args) {
|
|
|
7074
6693
|
else {
|
|
7075
6694
|
// 回退到默认 prompt
|
|
7076
6695
|
prompt = (0, prompt_builder_1.buildPromptText)(await (0, prompt_builder_1.buildPrompt)('analyze', {
|
|
7077
|
-
iteration: iter,
|
|
6696
|
+
iteration: iter, platform: options.platform,
|
|
7078
6697
|
analyzeOptions: options,
|
|
7079
6698
|
}));
|
|
7080
6699
|
}
|
|
@@ -7302,133 +6921,6 @@ async function buildAndSaveQualityReport(iterDir, iter, units) {
|
|
|
7302
6921
|
logger_1.logger.info(` 📊 质量报告已落盘: 020-specs/QUALITY_AUDIT.md(${okCount} 成功 / ${failCount} 失败,平均 ${avgScore} 分)`);
|
|
7303
6922
|
return reportPath;
|
|
7304
6923
|
}
|
|
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
6924
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
7433
6925
|
// v8.3.177+: 总览生成辅助函数
|
|
7434
6926
|
// ═══════════════════════════════════════════════════════════════════════════════
|