speccore 8.3.159 → 8.3.161
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 +2 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/analyze.d.ts +16 -0
- package/dist/commands/analyze.d.ts.map +1 -1
- package/dist/commands/analyze.js +538 -316
- package/dist/commands/analyze.js.map +1 -1
- package/dist/commands/change.d.ts.map +1 -1
- package/dist/commands/change.js +14 -14
- package/dist/commands/change.js.map +1 -1
- package/dist/commands/execute.d.ts +1 -0
- package/dist/commands/execute.d.ts.map +1 -1
- package/dist/commands/execute.js +92 -10
- package/dist/commands/execute.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +6 -2
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/iteration/split.d.ts.map +1 -1
- package/dist/commands/iteration/split.js +299 -52
- package/dist/commands/iteration/split.js.map +1 -1
- package/dist/commands/plan.d.ts.map +1 -1
- package/dist/commands/plan.js +45 -36
- package/dist/commands/plan.js.map +1 -1
- package/dist/core/agent-adapter.d.ts +81 -0
- package/dist/core/agent-adapter.d.ts.map +1 -0
- package/dist/core/agent-adapter.js +425 -0
- package/dist/core/agent-adapter.js.map +1 -0
- package/dist/core/ask-engine.d.ts.map +1 -1
- package/dist/core/ask-engine.js +60 -0
- package/dist/core/ask-engine.js.map +1 -1
- package/dist/core/pipeline-engine.d.ts +49 -8
- package/dist/core/pipeline-engine.d.ts.map +1 -1
- package/dist/core/pipeline-engine.js +221 -34
- package/dist/core/pipeline-engine.js.map +1 -1
- package/dist/core/prompt-builder.d.ts +12 -3
- package/dist/core/prompt-builder.d.ts.map +1 -1
- package/dist/core/prompt-builder.js +238 -15
- package/dist/core/prompt-builder.js.map +1 -1
- package/dist/core/requirement-clarifier.d.ts.map +1 -1
- package/dist/core/requirement-clarifier.js +36 -3
- package/dist/core/requirement-clarifier.js.map +1 -1
- package/dist/core/schemas/task.schema.d.ts +2 -2
- package/dist/core/structured-extractor.d.ts +2 -0
- package/dist/core/structured-extractor.d.ts.map +1 -1
- package/dist/core/structured-extractor.js +61 -0
- package/dist/core/structured-extractor.js.map +1 -1
- package/dist/core/unified-retrieval.d.ts +2 -1
- package/dist/core/unified-retrieval.d.ts.map +1 -1
- package/dist/core/unified-retrieval.js +34 -15
- package/dist/core/unified-retrieval.js.map +1 -1
- package/package.json +1 -1
package/dist/commands/analyze.js
CHANGED
|
@@ -35,6 +35,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.UNITS_CACHE_DIR = void 0;
|
|
37
37
|
exports.analyzeCommand = analyzeCommand;
|
|
38
|
+
exports.buildMultiDocPrompt = buildMultiDocPrompt;
|
|
38
39
|
exports.extractIterationUnits = extractIterationUnits;
|
|
39
40
|
exports.saveUnitAnalysis = saveUnitAnalysis;
|
|
40
41
|
exports.loadUnitAnalyses = loadUnitAnalyses;
|
|
@@ -64,6 +65,8 @@ const task_paths_1 = require("../core/task-paths");
|
|
|
64
65
|
const analyze_engine_1 = require("../core/analyze-engine");
|
|
65
66
|
const fs_extra_2 = require("fs-extra");
|
|
66
67
|
const prompt_builder_1 = require("../core/prompt-builder");
|
|
68
|
+
// v8.3.160+: 子 Agent 适配层(analyze 自定义 Prompt 中手动注入)
|
|
69
|
+
const agent_adapter_1 = require("../core/agent-adapter");
|
|
67
70
|
const questions_1 = require("../core/questions");
|
|
68
71
|
const platform_registry_1 = require("../core/platform-registry");
|
|
69
72
|
const index_guard_1 = require("../core/index-guard");
|
|
@@ -850,8 +853,8 @@ async function analyzeCommand(options) {
|
|
|
850
853
|
engine = result.engine;
|
|
851
854
|
steps = result.steps;
|
|
852
855
|
pipelineKey = iter;
|
|
853
|
-
//
|
|
854
|
-
initStep = options.skipClarify ? 'phase1-prompt' : 'clarify-
|
|
856
|
+
// v8.3.160+: clarify 拆分为 product → interaction → security 三个子步骤
|
|
857
|
+
initStep = options.skipClarify ? 'phase1-prompt' : 'clarify-product';
|
|
855
858
|
}
|
|
856
859
|
// 检查是否有活跃的 Pipeline(恢复模式)
|
|
857
860
|
const hasActive = await pipeline_engine_1.PipelineEngine.hasActivePipeline(_projectRoot, pipelineKey);
|
|
@@ -871,15 +874,15 @@ async function analyzeCommand(options) {
|
|
|
871
874
|
if (isGlobalScope) {
|
|
872
875
|
// 全局层 Pipeline 步骤映射
|
|
873
876
|
if (currentStep === 'init' || currentStep === 'discovery') {
|
|
874
|
-
prompt = await
|
|
875
|
-
iteration: iter || 'GLOBAL', task: options.task,
|
|
876
|
-
|
|
877
|
-
}
|
|
877
|
+
prompt = (0, prompt_builder_1.buildPromptText)(await (0, prompt_builder_1.buildPrompt)('analyze', {
|
|
878
|
+
iteration: iter || 'GLOBAL', task: options.task, platform: options.platform,
|
|
879
|
+
analyzeOptions: options,
|
|
880
|
+
}));
|
|
878
881
|
}
|
|
879
882
|
else if (currentStep === 'global-analysis') {
|
|
880
|
-
prompt = await
|
|
881
|
-
iteration: iter || 'GLOBAL',
|
|
882
|
-
}
|
|
883
|
+
prompt = (0, prompt_builder_1.buildPromptText)(await (0, prompt_builder_1.buildPrompt)('analyze', {
|
|
884
|
+
iteration: iter || 'GLOBAL', analyzeOptions: options,
|
|
885
|
+
}));
|
|
883
886
|
}
|
|
884
887
|
else if (currentStep === 'consistency-check') {
|
|
885
888
|
prompt = `\n# 任务: 全局一致性检查\n\n` +
|
|
@@ -891,13 +894,13 @@ async function analyzeCommand(options) {
|
|
|
891
894
|
`汇总所有分析结果,生成 .speccore/GLOBAL/INDEX.md。\n`;
|
|
892
895
|
}
|
|
893
896
|
else {
|
|
894
|
-
prompt = await
|
|
895
|
-
iteration: iter || 'GLOBAL',
|
|
896
|
-
}
|
|
897
|
+
prompt = (0, prompt_builder_1.buildPromptText)(await (0, prompt_builder_1.buildPrompt)('analyze', {
|
|
898
|
+
iteration: iter || 'GLOBAL', analyzeOptions: options,
|
|
899
|
+
}));
|
|
897
900
|
}
|
|
898
901
|
}
|
|
899
|
-
else if (currentStep === 'clarify-
|
|
900
|
-
//
|
|
902
|
+
else if (currentStep === 'clarify-product' || currentStep === 'clarify-interaction' || currentStep === 'clarify-security') {
|
|
903
|
+
// v8.3.160+: Phase 0 需求澄清(拆分为 product / interaction / security 三个子步骤)
|
|
901
904
|
prompt = await buildClarifyPhasePrompt(iter);
|
|
902
905
|
}
|
|
903
906
|
else if (currentStep === 'confirm-check') {
|
|
@@ -905,10 +908,10 @@ async function analyzeCommand(options) {
|
|
|
905
908
|
prompt = await buildConfirmCheckPrompt(iter);
|
|
906
909
|
}
|
|
907
910
|
else if (currentStep === 'phase1-prompt') {
|
|
908
|
-
prompt = await
|
|
909
|
-
iteration: iter, task: options.task,
|
|
910
|
-
|
|
911
|
-
}
|
|
911
|
+
prompt = (0, prompt_builder_1.buildPromptText)(await (0, prompt_builder_1.buildPrompt)('analyze', {
|
|
912
|
+
iteration: iter, task: options.task, platform: options.platform,
|
|
913
|
+
analyzeOptions: options,
|
|
914
|
+
}));
|
|
912
915
|
}
|
|
913
916
|
else if (currentStep === 'contract-prompt') {
|
|
914
917
|
// 契约先行阶段:基于 Phase 1 文档生成跨端契约
|
|
@@ -917,16 +920,17 @@ async function analyzeCommand(options) {
|
|
|
917
920
|
else if (platformMatch) {
|
|
918
921
|
// 逐端推进阶段:为指定端生成专属文档
|
|
919
922
|
const platform = platformMatch[1];
|
|
920
|
-
prompt = await
|
|
921
|
-
iteration: iter,
|
|
922
|
-
|
|
923
|
+
prompt = (0, prompt_builder_1.buildPromptText)(await (0, prompt_builder_1.buildPrompt)('analyze', {
|
|
924
|
+
iteration: iter, platform,
|
|
925
|
+
analyzeOptions: { ...options, phase: '2' },
|
|
926
|
+
}));
|
|
923
927
|
}
|
|
924
928
|
else {
|
|
925
929
|
// 回退到默认 prompt
|
|
926
|
-
prompt = await
|
|
927
|
-
iteration: iter, task: options.task,
|
|
928
|
-
|
|
929
|
-
}
|
|
930
|
+
prompt = (0, prompt_builder_1.buildPromptText)(await (0, prompt_builder_1.buildPrompt)('analyze', {
|
|
931
|
+
iteration: iter, task: options.task, platform: options.platform,
|
|
932
|
+
analyzeOptions: options,
|
|
933
|
+
}));
|
|
930
934
|
}
|
|
931
935
|
// 添加 Pipeline 继续指令
|
|
932
936
|
const totalSteps = steps.filter(s => !s.id.endsWith('-done') && s.id !== 'done').length;
|
|
@@ -941,22 +945,27 @@ async function analyzeCommand(options) {
|
|
|
941
945
|
: '全局 Pipeline';
|
|
942
946
|
}
|
|
943
947
|
else {
|
|
944
|
-
progressLabel = currentStep === 'clarify-
|
|
945
|
-
? 'Phase
|
|
946
|
-
: currentStep === '
|
|
947
|
-
? 'Phase
|
|
948
|
-
: currentStep === '
|
|
949
|
-
? 'Phase
|
|
950
|
-
: currentStep === '
|
|
951
|
-
? 'Phase
|
|
952
|
-
:
|
|
953
|
-
?
|
|
954
|
-
: '
|
|
955
|
-
|
|
956
|
-
|
|
948
|
+
progressLabel = currentStep === 'clarify-product'
|
|
949
|
+
? 'Phase 0a/3: 产品分析'
|
|
950
|
+
: currentStep === 'clarify-interaction'
|
|
951
|
+
? 'Phase 0b/3: 交互设计'
|
|
952
|
+
: currentStep === 'clarify-security'
|
|
953
|
+
? 'Phase 0c/3: 安全审查'
|
|
954
|
+
: currentStep === 'confirm-check'
|
|
955
|
+
? 'Phase 0/3: 需求确认'
|
|
956
|
+
: currentStep === 'phase1-prompt'
|
|
957
|
+
? 'Phase 1/3: 迭代综合文档'
|
|
958
|
+
: currentStep === 'contract-prompt'
|
|
959
|
+
? 'Phase 2/3: 契约定义'
|
|
960
|
+
: platformMatch
|
|
961
|
+
? `Phase 2/3: ${platformMatch[1]} 端专属文档`
|
|
962
|
+
: 'Pipeline';
|
|
963
|
+
}
|
|
964
|
+
// v8.3.160+: 步骤隔离模式,每步完成后需新会话继续
|
|
965
|
+
const finalPrompt = prompt + `\n\n## ⚠️ Pipeline 模式:步骤隔离 (${progressLabel})\n\n` +
|
|
957
966
|
`当前是 Pipeline 模式(步骤 ${currentStepIndex + 1}/${totalSteps})。` +
|
|
958
|
-
`当你通过 --apply 写入文档后,CLI 会自动输出 [
|
|
959
|
-
`**看到 [
|
|
967
|
+
`当你通过 --apply 写入文档后,CLI 会自动输出 [SPECCORE_STEP_DONE] 标记和下一步命令。\n\n` +
|
|
968
|
+
`**看到 [SPECCORE_STEP_DONE] 后,请在新会话中执行提示的命令**,继续下一步。\n`;
|
|
960
969
|
process.stdout.write(`[SPECCORE_PROMPT]\n${finalPrompt}`);
|
|
961
970
|
process.exitCode = 10;
|
|
962
971
|
return;
|
|
@@ -983,11 +992,10 @@ async function analyzeCommand(options) {
|
|
|
983
992
|
const prompts = [];
|
|
984
993
|
for (let i = 0; i < targetTasks.length; i++) {
|
|
985
994
|
const taskId = targetTasks[i];
|
|
986
|
-
const singlePrompt = await
|
|
987
|
-
iteration: iter, task: taskId,
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
}, options);
|
|
995
|
+
const singlePrompt = (0, prompt_builder_1.buildPromptText)(await (0, prompt_builder_1.buildPrompt)('analyze', {
|
|
996
|
+
iteration: iter, task: taskId, platform: options.platform,
|
|
997
|
+
analyzeOptions: options,
|
|
998
|
+
}));
|
|
991
999
|
prompts.push(`<!-- ═══════════════════════════════════════════ -->
|
|
992
1000
|
<!-- 任务 ${i + 1}/${targetTasks.length}: ${taskId} -->
|
|
993
1001
|
<!-- ═══════════════════════════════════════════ -->
|
|
@@ -998,7 +1006,14 @@ ${singlePrompt}`);
|
|
|
998
1006
|
process.exitCode = 10;
|
|
999
1007
|
return;
|
|
1000
1008
|
}
|
|
1001
|
-
|
|
1009
|
+
// v8.3.160+: 统一使用 buildPrompt 入口
|
|
1010
|
+
const promptObj = await (0, prompt_builder_1.buildPrompt)('analyze', {
|
|
1011
|
+
iteration: iter,
|
|
1012
|
+
task: options.task,
|
|
1013
|
+
platform: options.platform,
|
|
1014
|
+
analyzeOptions: options,
|
|
1015
|
+
});
|
|
1016
|
+
const prompt = (0, prompt_builder_1.buildPromptText)(promptObj);
|
|
1002
1017
|
process.stdout.write(`[SPECCORE_PROMPT]\n${prompt}`);
|
|
1003
1018
|
process.exitCode = 10;
|
|
1004
1019
|
return;
|
|
@@ -1363,15 +1378,36 @@ ${singlePrompt}`);
|
|
|
1363
1378
|
skippedCount++;
|
|
1364
1379
|
continue;
|
|
1365
1380
|
}
|
|
1366
|
-
// 综合文档写入 overview/ 子目录,端专属文档写入 {端}/ 子目录
|
|
1381
|
+
// 综合文档写入 overview/ 子目录,端专属文档写入 {功能模块}/{端}/ 子目录
|
|
1367
1382
|
// v8.3.24+: 按文件名前缀路由,支持多段指定
|
|
1368
|
-
//
|
|
1383
|
+
// v8.3.25+: 如果 AI 输出旧格式 {端}/{文件},尝试回退到功能模块路由
|
|
1369
1384
|
let targetDir;
|
|
1370
1385
|
if (globalSet.has(cleanFilename)) {
|
|
1371
1386
|
targetDir = (0, path_1.join)(specDir, spec_paths_1.GLOBAL_SPECS_DIR);
|
|
1372
1387
|
}
|
|
1373
1388
|
else if (platformDir && validPlatforms.has(platformDir)) {
|
|
1374
|
-
|
|
1389
|
+
// 检查是否应归入功能模块(骨架文件已按功能模块创建)
|
|
1390
|
+
let featureFallback = null;
|
|
1391
|
+
try {
|
|
1392
|
+
const specEntries = await (0, fs_extra_2.readdir)(specDir, { withFileTypes: true });
|
|
1393
|
+
for (const entry of specEntries) {
|
|
1394
|
+
if (!entry.isDirectory() || entry.name === spec_paths_1.GLOBAL_SPECS_DIR || validPlatforms.has(entry.name))
|
|
1395
|
+
continue;
|
|
1396
|
+
const platformInFeature = (0, path_1.join)(specDir, entry.name, platformDir);
|
|
1397
|
+
if (await (0, fs_extra_1.pathExists)(platformInFeature)) {
|
|
1398
|
+
featureFallback = entry.name;
|
|
1399
|
+
break;
|
|
1400
|
+
}
|
|
1401
|
+
}
|
|
1402
|
+
}
|
|
1403
|
+
catch { }
|
|
1404
|
+
if (featureFallback) {
|
|
1405
|
+
targetDir = (0, path_1.join)(specDir, featureFallback, platformDir);
|
|
1406
|
+
logger_1.logger.info(` 📁 自动归入功能模块: ${featureFallback}/${platformDir}/${cleanFilename.split('/').pop()}`);
|
|
1407
|
+
}
|
|
1408
|
+
else {
|
|
1409
|
+
targetDir = (0, path_1.join)(specDir, platformDir);
|
|
1410
|
+
}
|
|
1375
1411
|
}
|
|
1376
1412
|
else {
|
|
1377
1413
|
targetDir = specDir;
|
|
@@ -1905,7 +1941,8 @@ ${singlePrompt}`);
|
|
|
1905
1941
|
platformOrder: platformOrder.length > 0 ? platformOrder : undefined,
|
|
1906
1942
|
})).engine;
|
|
1907
1943
|
}
|
|
1908
|
-
|
|
1944
|
+
// v8.3.160+: 默认步骤隔离模式
|
|
1945
|
+
const stepResult = await engine.advance();
|
|
1909
1946
|
const state = await engine.getState();
|
|
1910
1947
|
if (state?.currentStep === 'done') {
|
|
1911
1948
|
logger_1.logger.success('🎉 Pipeline 完成!');
|
|
@@ -1924,9 +1961,9 @@ ${singlePrompt}`);
|
|
|
1924
1961
|
if (isGlobalScope) {
|
|
1925
1962
|
// 全局层 Pipeline 步骤映射
|
|
1926
1963
|
if (state.currentStep === 'global-analysis') {
|
|
1927
|
-
nextPrompt = await
|
|
1928
|
-
iteration: options.iteration || 'GLOBAL',
|
|
1929
|
-
});
|
|
1964
|
+
nextPrompt = (0, prompt_builder_1.buildPromptText)(await (0, prompt_builder_1.buildPrompt)('analyze', {
|
|
1965
|
+
iteration: options.iteration || 'GLOBAL', analyzeOptions: options,
|
|
1966
|
+
}));
|
|
1930
1967
|
}
|
|
1931
1968
|
else if (state.currentStep === 'consistency-check') {
|
|
1932
1969
|
nextPrompt = `\n# 任务: 全局一致性检查\n\n` +
|
|
@@ -1938,13 +1975,13 @@ ${singlePrompt}`);
|
|
|
1938
1975
|
`汇总所有分析结果,生成 .speccore/GLOBAL/INDEX.md。\n`;
|
|
1939
1976
|
}
|
|
1940
1977
|
else {
|
|
1941
|
-
nextPrompt = await
|
|
1942
|
-
iteration: options.iteration || 'GLOBAL',
|
|
1943
|
-
});
|
|
1978
|
+
nextPrompt = (0, prompt_builder_1.buildPromptText)(await (0, prompt_builder_1.buildPrompt)('analyze', {
|
|
1979
|
+
iteration: options.iteration || 'GLOBAL', analyzeOptions: options,
|
|
1980
|
+
}));
|
|
1944
1981
|
}
|
|
1945
|
-
//
|
|
1982
|
+
// v8.3.160+: clarify 阶段推进(拆分为 product / interaction / security)
|
|
1946
1983
|
}
|
|
1947
|
-
else if (state.currentStep === 'clarify-
|
|
1984
|
+
else if (state.currentStep === 'clarify-product' || state.currentStep === 'clarify-interaction' || state.currentStep === 'clarify-security') {
|
|
1948
1985
|
nextPrompt = await buildClarifyPhasePrompt(options.iteration);
|
|
1949
1986
|
}
|
|
1950
1987
|
else if (state.currentStep === 'confirm-check') {
|
|
@@ -1955,25 +1992,49 @@ ${singlePrompt}`);
|
|
|
1955
1992
|
}
|
|
1956
1993
|
else if (nextPlatformMatch) {
|
|
1957
1994
|
const platform = nextPlatformMatch[1];
|
|
1958
|
-
nextPrompt = await
|
|
1959
|
-
iteration: options.iteration,
|
|
1960
|
-
phase: '2',
|
|
1961
|
-
|
|
1962
|
-
});
|
|
1995
|
+
nextPrompt = (0, prompt_builder_1.buildPromptText)(await (0, prompt_builder_1.buildPrompt)('analyze', {
|
|
1996
|
+
iteration: options.iteration, platform,
|
|
1997
|
+
analyzeOptions: { ...options, phase: '2' },
|
|
1998
|
+
}));
|
|
1963
1999
|
}
|
|
1964
2000
|
else if (state.currentStep === 'phase2-prompt') {
|
|
1965
2001
|
// 兼容旧 Pipeline 步骤名
|
|
1966
|
-
nextPrompt = await
|
|
2002
|
+
nextPrompt = (0, prompt_builder_1.buildPromptText)(await (0, prompt_builder_1.buildPrompt)('analyze', {
|
|
1967
2003
|
iteration: options.iteration,
|
|
1968
|
-
phase: '2',
|
|
1969
|
-
});
|
|
2004
|
+
analyzeOptions: { ...options, phase: '2' },
|
|
2005
|
+
}));
|
|
1970
2006
|
}
|
|
1971
2007
|
else {
|
|
1972
|
-
nextPrompt = await
|
|
2008
|
+
nextPrompt = (0, prompt_builder_1.buildPromptText)(await (0, prompt_builder_1.buildPrompt)('analyze', {
|
|
1973
2009
|
iteration: options.iteration,
|
|
1974
|
-
|
|
2010
|
+
analyzeOptions: options,
|
|
2011
|
+
}));
|
|
1975
2012
|
}
|
|
1976
|
-
|
|
2013
|
+
// v8.3.160+: 步骤隔离模式输出标记 + 子 Agent 激活
|
|
2014
|
+
const scopeFlag = isGlobalScope ? '--scope global' : `-I ${options.iteration}`;
|
|
2015
|
+
const nextCmd = `speccore analyze ${scopeFlag} --resume`;
|
|
2016
|
+
const output = [
|
|
2017
|
+
`[SPECCORE_STEP_DONE]`,
|
|
2018
|
+
`步骤 "${state.currentStep}" 已完成。`,
|
|
2019
|
+
``,
|
|
2020
|
+
`[SPECCORE_NEXT_STEP]`,
|
|
2021
|
+
`下一步: ${state.currentStep}`,
|
|
2022
|
+
`请在新的对话中执行: ${nextCmd}`,
|
|
2023
|
+
``,
|
|
2024
|
+
`[SPECCORE_CONTEXT_SNAPSHOT]`,
|
|
2025
|
+
JSON.stringify(stepResult.contextSnapshot || {}),
|
|
2026
|
+
``,
|
|
2027
|
+
// v8.3.160+: 子 Agent 激活标记
|
|
2028
|
+
...(stepResult.subagent ? [
|
|
2029
|
+
`[SPECCORE_SUBAGENT: ${stepResult.subagent}]`,
|
|
2030
|
+
`[SPECCORE_CONTEXT_BUDGET: ${stepResult.contextBudget || 12000}]`,
|
|
2031
|
+
`[SPECCORE_CONTEXT_TYPE: ${stepResult.contextType || 'full'}]`,
|
|
2032
|
+
``,
|
|
2033
|
+
] : []),
|
|
2034
|
+
`--- 下一步 Prompt ---`,
|
|
2035
|
+
nextPrompt,
|
|
2036
|
+
].join('\n');
|
|
2037
|
+
process.stdout.write(output);
|
|
1977
2038
|
process.exitCode = 10;
|
|
1978
2039
|
}
|
|
1979
2040
|
return;
|
|
@@ -2626,30 +2687,60 @@ async function detectGlobalLayerProgress() {
|
|
|
2626
2687
|
missing.push('Layer 2: platforms/_shared/_ASSOCIATION.md');
|
|
2627
2688
|
}
|
|
2628
2689
|
}
|
|
2629
|
-
// Layer 3:
|
|
2690
|
+
// Layer 3: 检查功能模块深入文档(v8.3.160+: 支持模块级进度追踪)
|
|
2691
|
+
let pendingModules;
|
|
2630
2692
|
if (completedLayer >= 2) {
|
|
2631
2693
|
try {
|
|
2694
|
+
// v8.3.160+: 优先从 _MODULES.md 读取模块列表,进行模块级进度检测
|
|
2695
|
+
const moduleNames = await parseModuleNamesFromModulesMd(globalDir);
|
|
2632
2696
|
const platformsDir = (0, path_1.join)(globalDir, 'platforms');
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2697
|
+
if (moduleNames && moduleNames.length > 0) {
|
|
2698
|
+
// 检查 platforms/_shared/modules/{模块名}.md(v8.3.160+ 统一存放位置)
|
|
2699
|
+
const sharedModulesDir = (0, path_1.join)(platformsDir, '_shared', 'modules');
|
|
2700
|
+
const completedModules = new Set();
|
|
2701
|
+
if (await (0, fs_extra_1.pathExists)(sharedModulesDir)) {
|
|
2702
|
+
const files = await (0, fs_extra_2.readdir)(sharedModulesDir).catch(() => []);
|
|
2703
|
+
for (const f of files.filter(f => f.endsWith('.md'))) {
|
|
2704
|
+
const baseName = f.replace('.md', '').replace(/-/g, '');
|
|
2705
|
+
for (const modName of moduleNames) {
|
|
2706
|
+
const normalizedMod = modName.toLowerCase().replace(/[^\u4e00-\u9fffa-z0-9]/g, '');
|
|
2707
|
+
if (baseName.includes(normalizedMod) || normalizedMod.includes(baseName)) {
|
|
2708
|
+
completedModules.add(modName);
|
|
2709
|
+
}
|
|
2710
|
+
}
|
|
2711
|
+
}
|
|
2712
|
+
}
|
|
2713
|
+
pendingModules = moduleNames.filter(m => !completedModules.has(m));
|
|
2714
|
+
if (pendingModules.length === 0) {
|
|
2715
|
+
completedLayer = 3;
|
|
2716
|
+
}
|
|
2717
|
+
else {
|
|
2718
|
+
missing.push(`Layer 3: 还有 ${pendingModules.length}/${moduleNames.length} 个功能模块待分析`);
|
|
2719
|
+
}
|
|
2637
2720
|
}
|
|
2638
2721
|
else {
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
return files.some(f => f.endsWith('.md'));
|
|
2645
|
-
}));
|
|
2646
|
-
if (moduleDocChecks.every(Boolean)) {
|
|
2647
|
-
completedLayer = 3;
|
|
2722
|
+
// 回退:检查各端 modules/ 目录是否有文档(旧逻辑)
|
|
2723
|
+
const entries = await (0, fs_extra_2.readdir)(platformsDir, { withFileTypes: true });
|
|
2724
|
+
const platformDirs = entries.filter(e => e.isDirectory() && e.name !== '_shared').map(e => e.name);
|
|
2725
|
+
if (platformDirs.length === 0) {
|
|
2726
|
+
missing.push('Layer 3: platforms/{端}/modules/*.md(功能模块深入文档)');
|
|
2648
2727
|
}
|
|
2649
2728
|
else {
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2729
|
+
const moduleDocChecks = await Promise.all(platformDirs.map(async (d) => {
|
|
2730
|
+
const modulesDir = (0, path_1.join)(platformsDir, d, 'modules');
|
|
2731
|
+
if (!await (0, fs_extra_1.pathExists)(modulesDir))
|
|
2732
|
+
return false;
|
|
2733
|
+
const files = await (0, fs_extra_2.readdir)(modulesDir).catch(() => []);
|
|
2734
|
+
return files.some(f => f.endsWith('.md'));
|
|
2735
|
+
}));
|
|
2736
|
+
if (moduleDocChecks.every(Boolean)) {
|
|
2737
|
+
completedLayer = 3;
|
|
2738
|
+
}
|
|
2739
|
+
else {
|
|
2740
|
+
for (const [i, d] of platformDirs.entries()) {
|
|
2741
|
+
if (!moduleDocChecks[i]) {
|
|
2742
|
+
missing.push(`Layer 3: platforms/${d}/modules/*.md(功能模块深入文档)`);
|
|
2743
|
+
}
|
|
2653
2744
|
}
|
|
2654
2745
|
}
|
|
2655
2746
|
}
|
|
@@ -2661,6 +2752,7 @@ async function detectGlobalLayerProgress() {
|
|
|
2661
2752
|
}
|
|
2662
2753
|
// Layer 4 子层检测
|
|
2663
2754
|
let subLayer;
|
|
2755
|
+
let pendingPlatforms = [];
|
|
2664
2756
|
if (completedLayer >= 3) {
|
|
2665
2757
|
const overviewDir = (0, path_1.join)(globalDir, 'overview');
|
|
2666
2758
|
const requirementsDir = (0, path_1.join)(globalDir, 'requirements');
|
|
@@ -2721,6 +2813,7 @@ async function detectGlobalLayerProgress() {
|
|
|
2721
2813
|
}
|
|
2722
2814
|
}
|
|
2723
2815
|
// 4d: 各端技术文档(v8.3.0+ 修复:每个端都要有,不再用 some(Boolean))
|
|
2816
|
+
// v8.3.160+: 记录未完成的端,用于端级分批
|
|
2724
2817
|
try {
|
|
2725
2818
|
const platformsDir = (0, path_1.join)(globalDir, 'platforms');
|
|
2726
2819
|
const entries = await (0, fs_extra_2.readdir)(platformsDir, { withFileTypes: true });
|
|
@@ -2728,25 +2821,38 @@ async function detectGlobalLayerProgress() {
|
|
|
2728
2821
|
const missingPlatformDocs = [];
|
|
2729
2822
|
for (const d of platformDirs) {
|
|
2730
2823
|
const isBackend = /service|server|api|backend/i.test(d);
|
|
2824
|
+
let hasMissing = false;
|
|
2731
2825
|
if (isBackend) {
|
|
2732
2826
|
const hasApi = await (0, fs_extra_1.pathExists)((0, path_1.join)(platformsDir, d, 'API_INVENTORY.md'));
|
|
2733
2827
|
const hasData = await (0, fs_extra_1.pathExists)((0, path_1.join)(platformsDir, d, 'DATA_MODEL.md'));
|
|
2734
|
-
if (!hasApi)
|
|
2828
|
+
if (!hasApi) {
|
|
2735
2829
|
missingPlatformDocs.push(`platforms/${d}/API_INVENTORY.md`);
|
|
2736
|
-
|
|
2830
|
+
hasMissing = true;
|
|
2831
|
+
}
|
|
2832
|
+
if (!hasData) {
|
|
2737
2833
|
missingPlatformDocs.push(`platforms/${d}/DATA_MODEL.md`);
|
|
2834
|
+
hasMissing = true;
|
|
2835
|
+
}
|
|
2738
2836
|
}
|
|
2739
2837
|
else {
|
|
2740
2838
|
const hasUi = await (0, fs_extra_1.pathExists)((0, path_1.join)(platformsDir, d, 'UI_FLOW.md'));
|
|
2741
2839
|
const hasApiMap = await (0, fs_extra_1.pathExists)((0, path_1.join)(platformsDir, d, 'API_CALL_MAP.md'));
|
|
2742
2840
|
const hasState = await (0, fs_extra_1.pathExists)((0, path_1.join)(platformsDir, d, 'STATE_MANAGEMENT.md'));
|
|
2743
|
-
if (!hasUi)
|
|
2841
|
+
if (!hasUi) {
|
|
2744
2842
|
missingPlatformDocs.push(`platforms/${d}/UI_FLOW.md`);
|
|
2745
|
-
|
|
2843
|
+
hasMissing = true;
|
|
2844
|
+
}
|
|
2845
|
+
if (!hasApiMap) {
|
|
2746
2846
|
missingPlatformDocs.push(`platforms/${d}/API_CALL_MAP.md`);
|
|
2747
|
-
|
|
2847
|
+
hasMissing = true;
|
|
2848
|
+
}
|
|
2849
|
+
if (!hasState) {
|
|
2748
2850
|
missingPlatformDocs.push(`platforms/${d}/STATE_MANAGEMENT.md`);
|
|
2851
|
+
hasMissing = true;
|
|
2852
|
+
}
|
|
2749
2853
|
}
|
|
2854
|
+
if (hasMissing)
|
|
2855
|
+
pendingPlatforms.push(d);
|
|
2750
2856
|
}
|
|
2751
2857
|
if (missingPlatformDocs.length === 0 && platformDirs.length > 0) {
|
|
2752
2858
|
completedSubLayers.push('4d');
|
|
@@ -2768,12 +2874,12 @@ async function detectGlobalLayerProgress() {
|
|
|
2768
2874
|
subLayer = { completed: completedSubLayers, next: nextSub };
|
|
2769
2875
|
}
|
|
2770
2876
|
}
|
|
2771
|
-
return { completedLayer, nextLayer: Math.min(completedLayer + 1, 4), missing, subLayer };
|
|
2877
|
+
return { completedLayer, nextLayer: Math.min(completedLayer + 1, 4), missing, subLayer, pendingModules, pendingPlatforms };
|
|
2772
2878
|
}
|
|
2773
2879
|
// ── v7.2.0+: 全局分析下一步引导 ──
|
|
2774
2880
|
async function buildGlobalAnalysisGuide(options) {
|
|
2775
2881
|
const progress = await detectGlobalLayerProgress();
|
|
2776
|
-
const { completedLayer, nextLayer, subLayer } = progress;
|
|
2882
|
+
const { completedLayer, nextLayer, subLayer, pendingModules, pendingPlatforms } = progress;
|
|
2777
2883
|
const deepDoc = options?.deep;
|
|
2778
2884
|
let guide = '';
|
|
2779
2885
|
// 进度条
|
|
@@ -2784,6 +2890,23 @@ async function buildGlobalAnalysisGuide(options) {
|
|
|
2784
2890
|
return ` ${status} ${l}`;
|
|
2785
2891
|
}).join('\n');
|
|
2786
2892
|
guide += '\n';
|
|
2893
|
+
// v8.3.160+: Layer 3 模块级进度显示
|
|
2894
|
+
if (pendingModules && pendingModules.length > 0) {
|
|
2895
|
+
guide += `\n 📦 Layer 3 待分析模块 (${pendingModules.length} 个):\n`;
|
|
2896
|
+
for (const mod of pendingModules.slice(0, 10)) {
|
|
2897
|
+
guide += ` ⬜ ${mod}\n`;
|
|
2898
|
+
}
|
|
2899
|
+
if (pendingModules.length > 10) {
|
|
2900
|
+
guide += ` ... 还有 ${pendingModules.length - 10} 个模块\n`;
|
|
2901
|
+
}
|
|
2902
|
+
}
|
|
2903
|
+
// v8.3.160+: Layer 4d 端级进度显示
|
|
2904
|
+
if (pendingPlatforms && pendingPlatforms.length > 0) {
|
|
2905
|
+
guide += `\n 🖥️ Layer 4d 待处理端 (${pendingPlatforms.length} 个):\n`;
|
|
2906
|
+
for (const p of pendingPlatforms) {
|
|
2907
|
+
guide += ` ⬜ ${p}\n`;
|
|
2908
|
+
}
|
|
2909
|
+
}
|
|
2787
2910
|
if (completedLayer === 4 && (!subLayer || subLayer.completed.length === 4)) {
|
|
2788
2911
|
guide += '\n🎉 全局分析全部完成!\n';
|
|
2789
2912
|
guide += ' 所有文档已生成在 .speccore/GLOBAL/ 目录\n';
|
|
@@ -2833,9 +2956,9 @@ async function buildGlobalAnalysisGuide(options) {
|
|
|
2833
2956
|
}
|
|
2834
2957
|
// ── v7.4.5+: Layer 3 功能模块代码上下文预提取 ──
|
|
2835
2958
|
// 从 _MODULES.md 解析功能模块名,从 structured-data.json 交叉匹配相关 API/Entity/Component/Route
|
|
2836
|
-
|
|
2959
|
+
// v8.3.160+: 提取公共模块解析函数,供进度检测复用
|
|
2960
|
+
async function parseModuleNamesFromModulesMd(projectRoot) {
|
|
2837
2961
|
const modulesPath = (0, path_1.join)(projectRoot, '.speccore', 'GLOBAL', 'platforms', '_shared', '_MODULES.md');
|
|
2838
|
-
const structDataPath = (0, path_1.join)(projectRoot, '.speccore', 'cache', 'structured-data.json');
|
|
2839
2962
|
if (!(await (0, fs_extra_1.pathExists)(modulesPath)))
|
|
2840
2963
|
return null;
|
|
2841
2964
|
const modulesContent = await (0, fs_extra_2.readFile)(modulesPath, 'utf-8');
|
|
@@ -2857,8 +2980,13 @@ async function buildLayer3ModuleContext(projectRoot) {
|
|
|
2857
2980
|
moduleNames.push(name);
|
|
2858
2981
|
}
|
|
2859
2982
|
}
|
|
2860
|
-
|
|
2983
|
+
return moduleNames.length > 0 ? moduleNames : null;
|
|
2984
|
+
}
|
|
2985
|
+
async function buildLayer3ModuleContext(projectRoot) {
|
|
2986
|
+
const moduleNames = await parseModuleNamesFromModulesMd(projectRoot);
|
|
2987
|
+
if (!moduleNames)
|
|
2861
2988
|
return null;
|
|
2989
|
+
const structDataPath = (0, path_1.join)(projectRoot, '.speccore', 'cache', 'structured-data.json');
|
|
2862
2990
|
// 读取结构化数据
|
|
2863
2991
|
let structData = null;
|
|
2864
2992
|
if (await (0, fs_extra_1.pathExists)(structDataPath)) {
|
|
@@ -2934,6 +3062,7 @@ async function buildLayer3ModuleContext(projectRoot) {
|
|
|
2934
3062
|
return ctx.join('\n\n---\n\n');
|
|
2935
3063
|
}
|
|
2936
3064
|
// ── buildMultiDocPrompt: 多文档协议 ──
|
|
3065
|
+
// v8.3.160+: 改为 export,供 prompt-builder.ts 统一调用
|
|
2937
3066
|
async function buildMultiDocPrompt(command, ctx, options, clarifyCtx) {
|
|
2938
3067
|
const iter = ctx.iteration || '当前迭代';
|
|
2939
3068
|
const task = ctx.task ? ` — ${ctx.task}` : '';
|
|
@@ -2945,6 +3074,27 @@ async function buildMultiDocPrompt(command, ctx, options, clarifyCtx) {
|
|
|
2945
3074
|
// v8.3.0+: 从 clarifyCtx 提取需求澄清状态
|
|
2946
3075
|
const needsClarify = clarifyCtx?.needsClarify ?? false;
|
|
2947
3076
|
const clarifyTargets = clarifyCtx?.clarifyTargets ?? [];
|
|
3077
|
+
// v8.3.160+: 按 Phase 注入子 Agent 角色定义
|
|
3078
|
+
let agentContextText = '';
|
|
3079
|
+
const phaseSubagentMap = {
|
|
3080
|
+
'0': 'product-analyst',
|
|
3081
|
+
'1': 'spec-analyzer',
|
|
3082
|
+
'2': 'spec-analyzer',
|
|
3083
|
+
};
|
|
3084
|
+
const subagent = phaseSubagentMap[ctx.phase || ''] || (isGlobal ? 'spec-global-analyzer' : 'spec-analyzer');
|
|
3085
|
+
try {
|
|
3086
|
+
const agentCtx = {
|
|
3087
|
+
subagent,
|
|
3088
|
+
iteration: ctx.iteration || '',
|
|
3089
|
+
contextBudget: 12000,
|
|
3090
|
+
contextType: ctx.phase === '2' ? 'platform-only' : 'full',
|
|
3091
|
+
cwd: process.cwd(),
|
|
3092
|
+
};
|
|
3093
|
+
agentContextText = await agent_adapter_1.defaultAdapter.prepareContext(agentCtx);
|
|
3094
|
+
}
|
|
3095
|
+
catch (e) {
|
|
3096
|
+
logger_1.logger.debug?.(`agent-adapter 注入失败(非关键): ${e.message}`);
|
|
3097
|
+
}
|
|
2948
3098
|
// global 范围: 从源码反推需求 + 生成技术栈配置
|
|
2949
3099
|
if (isGlobal) {
|
|
2950
3100
|
// v6.74.0+: 流式全局分析模式
|
|
@@ -3034,6 +3184,10 @@ async function buildMultiDocPrompt(command, ctx, options, clarifyCtx) {
|
|
|
3034
3184
|
};
|
|
3035
3185
|
const layerMeta = LAYER_ROLES[targetLayer];
|
|
3036
3186
|
let prompt = `\n# 任务: ${command} (全局分析${ctx.withCode ? '+源码' : ''})\n\n`;
|
|
3187
|
+
// v8.3.160+: 注入子 Agent 角色定义
|
|
3188
|
+
if (agentContextText) {
|
|
3189
|
+
prompt = agentContextText + '\n\n' + prompt;
|
|
3190
|
+
}
|
|
3037
3191
|
// v7.2.0+: 分层专注指令 — 强烈约束 AI 只执行当前 layer
|
|
3038
3192
|
prompt += `## 🎯 当前执行层级: Layer ${targetLayer}/4 — ${layerMeta.role}\n\n`;
|
|
3039
3193
|
prompt += `> ⚠️ **重要约束**: 你当前只需要完成 **Layer ${targetLayer}** 的工作。不要提前做后续层的内容。\n`;
|
|
@@ -3069,36 +3223,22 @@ async function buildMultiDocPrompt(command, ctx, options, clarifyCtx) {
|
|
|
3069
3223
|
prompt += `**重要**: 不要直接扫描源码文件。已使用代码扫描工具提取了结构化数据,你只需要读取这些数据并整理成 _INDEX.md。\n\n`;
|
|
3070
3224
|
prompt += `**步骤**: \n`;
|
|
3071
3225
|
prompt += `1. Read \`.speccore/cache/structured-data.json\` — 获取所有端的 API/Entity/Route/Component 清单\n`;
|
|
3226
|
+
prompt += ` - **大项目优化**:如端数量 > 3,可按端读取 \`structured-data.{端名}.json\` 分段文件,避免一次性加载过多数据\n`;
|
|
3072
3227
|
prompt += `2. 对每个端,基于结构化数据生成 \`_INDEX.md\`(补充扫描工具未覆盖的内容)\n`;
|
|
3073
3228
|
prompt += `3. 扫描工具未覆盖的维度(消息队列、定时任务、配置、外部集成、日志监控、错误处理),需要 Read 相关配置文件补充\n\n`;
|
|
3074
|
-
|
|
3075
|
-
prompt +=
|
|
3076
|
-
prompt +=
|
|
3077
|
-
prompt +=
|
|
3078
|
-
prompt +=
|
|
3079
|
-
prompt +=
|
|
3080
|
-
prompt +=
|
|
3081
|
-
prompt +=
|
|
3082
|
-
prompt += `| 定时任务 | 搜索定时任务(@Scheduled/cron/agenda/node-cron) | 任务名、触发频率、执行类 |\n`;
|
|
3083
|
-
prompt += `| 配置管理 | 配置文件(application*.yml/.env/config/) | 环境变量名、Feature Flag、配置中心引用 |\n`;
|
|
3084
|
-
prompt += `| 外部集成 | 搜索第三方调用(HTTP client/SDK/微信支付/短信/邮件/OSS) | 集成目标、调用位置 |\n`;
|
|
3085
|
-
prompt += `| 日志监控 | 搜索日志/监控/埋点代码(logger/metrics/tracing) | 日志级别策略、埋点事件名 |\n`;
|
|
3086
|
-
prompt += `| 错误处理 | 搜索全局异常处理器(ExceptionHandler/ErrorBoundary) | 异常处理类名、错误码范围 |\n`;
|
|
3087
|
-
prompt += `| 依赖项 | pom.xml/package.json/go.mod/requirements.txt | 依赖项列表(识别公共服务候选、过期版本、已知 CVE) |\n\n`;
|
|
3088
|
-
prompt += `**前端端扫描维度(10项)**:\n`;
|
|
3089
|
-
prompt += `| 扫描项 | 读取位置 | 提取内容 |\n`;
|
|
3090
|
-
prompt += `| :--- | :--- | :--- |\n`;
|
|
3091
|
-
prompt += `| 路由 | router/routes 配置文件 | 页面路径、页面名称、组件名、懒加载标记 |\n`;
|
|
3092
|
-
prompt += `| 页面 | pages/views/screens 目录 | 页面名称、主要功能(从文件名推断) |\n`;
|
|
3093
|
-
prompt += `| API 调用 | 搜索 API 调用模式(axios/fetch/$.ajax/uni.request) | 调用的接口路径列表、调用位置 |\n`;
|
|
3094
|
-
prompt += `| 状态管理 | store/pinia/vuex/redux 目录 | 全局状态名称、actions 名称、持久化策略 |\n`;
|
|
3095
|
-
prompt += `| 组件库 | components/ui 目录、设计系统配置 | 组件名、复用度、设计 token |\n`;
|
|
3096
|
-
prompt += `| 拦截器 | 请求/响应拦截器配置 | 拦截器逻辑(鉴权头注入、错误统一处理) |\n`;
|
|
3097
|
-
prompt += `| 外部 SDK | 搜索第三方 SDK 引入(埋点/推送/地图/支付/分享) | SDK 名称、初始化位置、使用范围 |\n`;
|
|
3098
|
-
prompt += `| 国际化 | i18n/locales/lang 目录 | 支持语言、命名空间、 key 数量级 |\n`;
|
|
3099
|
-
prompt += `| 错误处理 | 错误边界/全局错误处理器 | 错误捕获范围、降级策略、上报机制 |\n`;
|
|
3100
|
-
prompt += `| 性能 | 搜索性能相关代码(懒加载/虚拟滚动/缓存/预加载) | 优化手段、适用场景 |\n\n`;
|
|
3229
|
+
// v8.3.160+: 扫描维度从详细表格精简为分类列表
|
|
3230
|
+
prompt += `**后端端扫描维度**(从 structured-data.json + 源码补充):\n`;
|
|
3231
|
+
prompt += `- 接口层(Controller/Handler)、数据层(Entity/Model)、业务层(Service/UseCase)\n`;
|
|
3232
|
+
prompt += `- 中间件、消息队列、定时任务、配置管理、外部集成\n`;
|
|
3233
|
+
prompt += `- 日志监控、错误处理、依赖项(识别过期版本/CVE)\n\n`;
|
|
3234
|
+
prompt += `**前端端扫描维度**:\n`;
|
|
3235
|
+
prompt += `- 路由、页面、API 调用、状态管理、组件库\n`;
|
|
3236
|
+
prompt += `- 拦截器、外部 SDK、国际化、错误处理、性能优化\n\n`;
|
|
3101
3237
|
prompt += `**输出**:每个端一个 \`_INDEX.md\`,按上述维度组织,只含名称和路径列表,不含详细逻辑\n`;
|
|
3238
|
+
prompt += `**分批策略(v8.3.160+)**:\n`;
|
|
3239
|
+
prompt += `- 如端数量 ≤ 5:一次生成所有端的 \`_INDEX.md\`\n`;
|
|
3240
|
+
prompt += `- 如端数量 > 5:一次处理 3-5 个端,剩余端在输出末尾标注 \`[PENDING: {端名列表}]\`,供续批处理\n`;
|
|
3241
|
+
prompt += `- 续批命令:\`speccore analyze --scope global --layer 1 --resume\`\n`;
|
|
3102
3242
|
prompt += `**最小内容标准(v8.3.61+)**:\n`;
|
|
3103
3243
|
prompt += `- 每个 \_INDEX.md ≥ 80 行(维度少可接受 50+,但严禁 < 30 行)\n`;
|
|
3104
3244
|
prompt += `- 必须包含:端名、技术栈、目录结构概览、各维度清单表格\n`;
|
|
@@ -3140,48 +3280,25 @@ async function buildMultiDocPrompt(command, ctx, options, clarifyCtx) {
|
|
|
3140
3280
|
// ── Layer 2 指令(仅 targetLayer === 2 时注入)──
|
|
3141
3281
|
if (targetLayer === 2) {
|
|
3142
3282
|
prompt += `## 🔗 Layer 2: 跨端关联分析(基于 Layer 1 索引 + structured-data.json)\n\n`;
|
|
3143
|
-
prompt += `> 📊
|
|
3144
|
-
prompt +=
|
|
3145
|
-
prompt +=
|
|
3146
|
-
prompt +=
|
|
3147
|
-
prompt +=
|
|
3148
|
-
prompt +=
|
|
3149
|
-
prompt +=
|
|
3150
|
-
prompt +=
|
|
3151
|
-
prompt +=
|
|
3152
|
-
prompt +=
|
|
3153
|
-
prompt +=
|
|
3154
|
-
prompt +=
|
|
3155
|
-
prompt += ` - 识别「异步事件触发 → 多端状态同步」链路\n`;
|
|
3156
|
-
prompt += ` - 标注:无消费者的消息、无生产者的消息( orphaned topic )\n\n`;
|
|
3157
|
-
prompt += `4. **定时任务影响分析**:\n`;
|
|
3158
|
-
prompt += ` - 哪些定时任务修改了被前端展示的数据(数据新鲜度风险)\n`;
|
|
3159
|
-
prompt += ` - 哪些定时任务触发了前端需要感知的通知/推送\n`;
|
|
3160
|
-
prompt += ` - 批处理任务 vs 实时接口的数据竞争风险\n\n`;
|
|
3161
|
-
prompt += `5. **外部集成一致性检查**:\n`;
|
|
3162
|
-
prompt += ` - 多个端是否独立调用了同一第三方 API(重复集成 = 维护风险)\n`;
|
|
3163
|
-
prompt += ` - 前端 SDK 与后端 SDK 版本是否一致(如支付 SDK)\n`;
|
|
3164
|
-
prompt += ` - 第三方回调/Webhook 的接收端分布\n\n`;
|
|
3165
|
-
prompt += `6. **配置一致性检查**:\n`;
|
|
3166
|
-
prompt += ` - 各端的超时配置、重试策略、限流阈值是否一致\n`;
|
|
3167
|
-
prompt += ` - 跨端共享的 Feature Flag 定义是否一致\n`;
|
|
3168
|
-
prompt += ` - 环境变量命名是否规范(如 API_BASE_URL 在各端是否指向同一值)\n\n`;
|
|
3169
|
-
prompt += `7. **归纳功能模块**(从索引聚类,不是从代码反推):\n`;
|
|
3170
|
-
prompt += ` - **从页面聚类**:哪些页面经常一起出现(如 RoomList + RoomDetail + RoomEdit)\n`;
|
|
3171
|
-
prompt += ` - **从接口聚类**:哪些接口共享同一实体前缀(如 /api/rooms/*)\n`;
|
|
3172
|
-
prompt += ` - **从消息聚类**:哪些消息队列共享同一业务领域\n`;
|
|
3173
|
-
prompt += ` - **交叉验证**:页面聚类 vs 接口聚类 vs 消息聚类 → 确定功能模块边界\n`;
|
|
3174
|
-
prompt += ` - 每个功能模块标注:涉及端、核心页面、核心接口、实体名称、消息队列\n\n`;
|
|
3283
|
+
prompt += `> 📊 **输入**: Read \`.speccore/cache/structured-data.json\` 获取 API/Entity 清单,与 Layer 1 索引交叉验证\n\n`;
|
|
3284
|
+
prompt += `**分析维度**(7 项,每项产出写入 \_ASSOCIATION.md 对应章节):\n\n`;
|
|
3285
|
+
prompt += `| # | 维度 | 核心动作 | 风险标注 |\n`;
|
|
3286
|
+
prompt += `| :--- | :--- | :--- | :--- |\n`;
|
|
3287
|
+
prompt += `| 1 | 接口匹配 | 前端 API 路径 vs 后端接口路径 → 建立调用链路 | 接口缺口 / 未使用接口 |\n`;
|
|
3288
|
+
prompt += `| 2 | 公共服务 | 被 2+ 端调用的服务 / 独立部署服务 | 重复实现 / 版本不一致 |\n`;
|
|
3289
|
+
prompt += `| 3 | 消息流 | 生产者 ↔ 队列 ↔ 消费者 ↔ 前端推送链路 | orphaned topic / 多端同步缺失 |\n`;
|
|
3290
|
+
prompt += `| 4 | 定时任务 | 修改前端数据的任务 / 触发通知的任务 | 数据新鲜度 / 竞争风险 |\n`;
|
|
3291
|
+
prompt += `| 5 | 外部集成 | 多端独立调用同一第三方 API / SDK 版本 | 重复集成 / 版本漂移 |\n`;
|
|
3292
|
+
prompt += `| 6 | 配置一致性 | 超时/重试/限流 / Feature Flag / 环境变量 | 配置漂移 / 环境差异 |\n`;
|
|
3293
|
+
prompt += `| 7 | 功能模块聚类 | 页面聚类 vs 接口聚类 vs 消息聚类 → 交叉验证 | 边界模糊 / 职责不清 |\n\n`;
|
|
3294
|
+
prompt += `**模块归纳方法**:从索引聚类(非代码反推),每个模块标注:涉及端、核心页面、核心接口、实体名、消息队列\n\n`;
|
|
3175
3295
|
prompt += `**输出**:\n`;
|
|
3176
|
-
prompt += `- \`_ASSOCIATION.md
|
|
3177
|
-
prompt +=
|
|
3178
|
-
prompt += ` - 此文档中必须包含 **接口依赖 Mermaid 图**(graph TD),展示前端页面 → 后端接口的调用关系\n`;
|
|
3179
|
-
prompt += `- \`_MODULES.md\`:功能模块候选清单(从源码聚类,含消息/定时任务维度,供 Layer 3 验证)\n`;
|
|
3180
|
-
prompt += ` - 此文档中必须包含 **模块全景 Mermaid 图**(graph LR),展示所有功能模块及其所属端\n`;
|
|
3296
|
+
prompt += `- \`_ASSOCIATION.md\`:关联矩阵 + 接口缺口/未使用清单 + 消息流 + 定时任务影响 + 外部集成 + 配置风险 + 模块关系 Mermaid 图(graph LR)+ 接口依赖 Mermaid 图(graph TD)\n`;
|
|
3297
|
+
prompt += `- \`_MODULES.md\`:功能模块候选清单(含消息/定时任务维度)+ 模块全景 Mermaid 图(graph LR)\n`;
|
|
3181
3298
|
prompt += `**最小内容标准(v8.3.61+)**:\n`;
|
|
3182
|
-
prompt += `- \_ASSOCIATION.md ≥ 100
|
|
3183
|
-
prompt += `- \_MODULES.md ≥ 50
|
|
3184
|
-
prompt += `-
|
|
3299
|
+
prompt += `- \_ASSOCIATION.md ≥ 100 行,含关联矩阵 + ≥1 个 Mermaid 图\n`;
|
|
3300
|
+
prompt += `- \_MODULES.md ≥ 50 行,含模块清单 + 模块全景 Mermaid 图\n`;
|
|
3301
|
+
prompt += `- 严禁空壳文档\n`;
|
|
3185
3302
|
prompt += `**存放**:\`.speccore/GLOBAL/platforms/_shared/\`\n\n`;
|
|
3186
3303
|
} // end if (targetLayer === 2)
|
|
3187
3304
|
// ── Layer 3 指令(仅 targetLayer === 3 时注入)──
|
|
@@ -3210,6 +3327,12 @@ async function buildMultiDocPrompt(command, ctx, options, clarifyCtx) {
|
|
|
3210
3327
|
prompt += `- 读取 room-service: RoomController#getAvailability, RoomService 的详细逻辑\n`;
|
|
3211
3328
|
prompt += `- 关联验证:前端提交的数据字段 vs 后端接收的 DTO 字段是否一致\n`;
|
|
3212
3329
|
prompt += `- 关联验证:前端展示的状态 vs 后端实体的状态枚举是否一致\n\n`;
|
|
3330
|
+
prompt += `**分批策略(v8.3.160+)**:\n`;
|
|
3331
|
+
prompt += `- 如功能模块数量 ≤ 2:一次生成所有模块的分析文档\n`;
|
|
3332
|
+
prompt += `- 如功能模块数量 > 2:一次处理 1-2 个模块,确保每个模块分析深入、不空洞\n`;
|
|
3333
|
+
prompt += `- 剩余模块在输出末尾标注 \`[PENDING: {模块名列表}]\`,供续批处理\n`;
|
|
3334
|
+
prompt += `- 续批命令:\`speccore analyze --scope global --layer 3 --resume\`\n`;
|
|
3335
|
+
prompt += `- **关键原则**:宁可少分析几个模块,也要保证每个模块的分析质量(≥100 行、有 Mermaid 图、有具体代码引用)\n\n`;
|
|
3213
3336
|
prompt += `**每个功能模块输出**:\n`;
|
|
3214
3337
|
prompt += `- 后端端:该功能模块相关的 API 详细设计、数据模型、业务规则、安全策略\n`;
|
|
3215
3338
|
prompt += `- 前端端:该功能模块相关的页面详细设计、交互流程、字段映射、错误处理\n`;
|
|
@@ -3710,22 +3833,35 @@ async function buildMultiDocPrompt(command, ctx, options, clarifyCtx) {
|
|
|
3710
3833
|
}
|
|
3711
3834
|
}
|
|
3712
3835
|
// 4d: 动态构建各端文档清单
|
|
3836
|
+
// v8.3.160+: 按端分批,一次处理 1-2 个端的所有缺失文档
|
|
3837
|
+
const platformMissingDocs = {};
|
|
3713
3838
|
if (subLayerTarget === '4d') {
|
|
3714
3839
|
try {
|
|
3715
3840
|
const entries = await (0, fs_extra_2.readdir)(platformsDir, { withFileTypes: true });
|
|
3716
3841
|
const platformNames = entries.filter(e => e.isDirectory() && e.name !== '_shared').map(e => e.name);
|
|
3717
3842
|
for (const p of platformNames) {
|
|
3718
3843
|
const isBackend = /service|server|api|backend/i.test(p);
|
|
3844
|
+
const expectedPlatformDocs = [];
|
|
3719
3845
|
if (isBackend) {
|
|
3720
|
-
|
|
3721
|
-
|
|
3722
|
-
|
|
3846
|
+
expectedPlatformDocs.push(`platforms/${p}/API_INVENTORY.md`);
|
|
3847
|
+
expectedPlatformDocs.push(`platforms/${p}/DATA_MODEL.md`);
|
|
3848
|
+
expectedPlatformDocs.push(`platforms/${p}/BUSINESS_RULES.md`);
|
|
3723
3849
|
}
|
|
3724
3850
|
else {
|
|
3725
|
-
|
|
3726
|
-
|
|
3727
|
-
|
|
3851
|
+
expectedPlatformDocs.push(`platforms/${p}/UI_FLOW.md`);
|
|
3852
|
+
expectedPlatformDocs.push(`platforms/${p}/API_CALL_MAP.md`);
|
|
3853
|
+
expectedPlatformDocs.push(`platforms/${p}/STATE_MANAGEMENT.md`);
|
|
3728
3854
|
}
|
|
3855
|
+
SUB_LAYER_DOCS['4d'].push(...expectedPlatformDocs);
|
|
3856
|
+
// 检查该端哪些文档缺失
|
|
3857
|
+
const missing = [];
|
|
3858
|
+
for (const doc of expectedPlatformDocs) {
|
|
3859
|
+
const fullPath = (0, path_1.join)(((0, task_utils_1.findProjectRoot)() || process.cwd()), '.speccore', 'GLOBAL', doc);
|
|
3860
|
+
if (!(await (0, fs_extra_1.pathExists)(fullPath)))
|
|
3861
|
+
missing.push(doc);
|
|
3862
|
+
}
|
|
3863
|
+
if (missing.length > 0)
|
|
3864
|
+
platformMissingDocs[p] = missing;
|
|
3729
3865
|
}
|
|
3730
3866
|
}
|
|
3731
3867
|
catch { }
|
|
@@ -3743,10 +3879,24 @@ async function buildMultiDocPrompt(command, ctx, options, clarifyCtx) {
|
|
|
3743
3879
|
missingDocs.push(doc);
|
|
3744
3880
|
}
|
|
3745
3881
|
}
|
|
3746
|
-
|
|
3747
|
-
|
|
3748
|
-
|
|
3749
|
-
|
|
3882
|
+
// v8.3.160+: 4d 按端分批模式
|
|
3883
|
+
let targetPlatforms = [];
|
|
3884
|
+
let nextDoc = null;
|
|
3885
|
+
if (subLayerTarget === '4d' && Object.keys(platformMissingDocs).length > 0) {
|
|
3886
|
+
targetPlatforms = Object.keys(platformMissingDocs).slice(0, 2); // 一次 1-2 个端
|
|
3887
|
+
}
|
|
3888
|
+
else if (missingDocs.length > 0) {
|
|
3889
|
+
nextDoc = missingDocs[0];
|
|
3890
|
+
}
|
|
3891
|
+
prompt += `## 🌍 Layer 4: 全局汇总 — 子层 ${subLayerTarget}/4${subLayerTarget === '4d' ? '(v8.3.160+ 按端分批模式)' : '(v7.5.0+ 逐文档模式)'}\n\n`;
|
|
3892
|
+
if (subLayerTarget === '4d' && targetPlatforms.length > 0) {
|
|
3893
|
+
prompt += `> ⚠️ **专注约束**: 你当前只处理 **${targetPlatforms.join(', ')}** 端的技术文档。\n`;
|
|
3894
|
+
prompt += `> 一次生成这些端的所有缺失文档,不要处理其他端。\n`;
|
|
3895
|
+
}
|
|
3896
|
+
else if (nextDoc) {
|
|
3897
|
+
prompt += `> ⚠️ **专注约束**: 你当前只执行 **子层 ${subLayerTarget}** 中的 **下一个缺失文档**。\n`;
|
|
3898
|
+
prompt += `> 忽略前面关于其他子层文档的要求,只关注当前要生成的文档。\n`;
|
|
3899
|
+
}
|
|
3750
3900
|
prompt += `> 已存在: ${existingDocs.length}/${expectedDocs.length} 份文档\n`;
|
|
3751
3901
|
if (missingDocs.length > 0) {
|
|
3752
3902
|
prompt += `> 缺失: ${missingDocs.slice(0, 5).join('、')}${missingDocs.length > 5 ? '...' : ''}\n`;
|
|
@@ -3756,7 +3906,20 @@ async function buildMultiDocPrompt(command, ctx, options, clarifyCtx) {
|
|
|
3756
3906
|
prompt += `- Read Layer 1 的所有 _INDEX.md\n`;
|
|
3757
3907
|
prompt += `- Read Layer 2 的 _ASSOCIATION.md + _MODULES.md\n`;
|
|
3758
3908
|
prompt += `- Read Layer 3 的功能模块深入文档\n\n`;
|
|
3759
|
-
if (
|
|
3909
|
+
if (subLayerTarget === '4d' && targetPlatforms.length > 0) {
|
|
3910
|
+
// v8.3.160+: 按端分批生成所有缺失文档
|
|
3911
|
+
prompt += `## 🎯 本次处理端: ${targetPlatforms.join(', ')}\n\n`;
|
|
3912
|
+
for (const p of targetPlatforms) {
|
|
3913
|
+
const docs = platformMissingDocs[p];
|
|
3914
|
+
prompt += `**${p} 端缺失文档 (${docs.length} 份)**: \n`;
|
|
3915
|
+
for (const doc of docs) {
|
|
3916
|
+
const docName = doc.split('/').pop();
|
|
3917
|
+
prompt += `- \`${docName}\`\n`;
|
|
3918
|
+
}
|
|
3919
|
+
prompt += `\n`;
|
|
3920
|
+
}
|
|
3921
|
+
}
|
|
3922
|
+
else if (nextDoc) {
|
|
3760
3923
|
prompt += `## 🎯 本次只生成: \`${nextDoc}\`\n\n`;
|
|
3761
3924
|
// 根据文档路径生成专属指令
|
|
3762
3925
|
const docName = nextDoc.split('/').pop();
|
|
@@ -3808,7 +3971,15 @@ async function buildMultiDocPrompt(command, ctx, options, clarifyCtx) {
|
|
|
3808
3971
|
prompt += `- 必须包含真实数据,禁止占位符\n`;
|
|
3809
3972
|
}
|
|
3810
3973
|
// 自动链式推进
|
|
3811
|
-
|
|
3974
|
+
// v8.3.160+: 4d 按端分批续批
|
|
3975
|
+
const remainingPlatforms = Object.keys(platformMissingDocs).filter(p => !targetPlatforms.includes(p));
|
|
3976
|
+
if (subLayerTarget === '4d' && remainingPlatforms.length > 0) {
|
|
3977
|
+
prompt += `\n## 🔗 自动链式推进\n\n`;
|
|
3978
|
+
prompt += `> 当前批次(${targetPlatforms.join(', ')})写入完成后,**还有 ${remainingPlatforms.length} 个端待处理**。\n`;
|
|
3979
|
+
prompt += `> **立即输出以下标记**继续子层 4d:\n\n`;
|
|
3980
|
+
prompt += `[SPECCORE_EXEC: speccore analyze --scope global --layer 4${options?.withCode ? ' --with-code' : ''}]\n\n`;
|
|
3981
|
+
}
|
|
3982
|
+
else if (missingDocs.length > 1) {
|
|
3812
3983
|
prompt += `\n## 🔗 自动链式推进\n\n`;
|
|
3813
3984
|
prompt += `> 当前文档写入完成后,**立即输出以下标记**推进下一个文档:\n\n`;
|
|
3814
3985
|
prompt += `[SPECCORE_EXEC: speccore analyze --scope global --layer 4${options?.withCode ? ' --with-code' : ''}]\n\n`;
|
|
@@ -3856,14 +4027,26 @@ async function buildMultiDocPrompt(command, ctx, options, clarifyCtx) {
|
|
|
3856
4027
|
prompt += `- platforms/ 和 requirements/ 下的文件按原路径写(如 \`platforms/backend/_INDEX.md\`)\n`;
|
|
3857
4028
|
prompt += `- overview/ 下的文件写纯文件名即可(如 \`ARCHITECTURE.md\` 自动路由到 overview/)\n`;
|
|
3858
4029
|
prompt += `- PATTERNS/ 下的文件写 \`PATTERNS/{分类}/{模式名}.md\`\n`;
|
|
4030
|
+
prompt += `- **Layer 3 功能模块分析文档写** \`platforms/_shared/modules/{kebab-case模块名}.md\`(v8.3.160+ 统一存放,便于进度追踪)\n`;
|
|
3859
4031
|
// v7.4.5+: 自动链式推进 — 用 [SPECCORE_EXEC:] 标记触发下一层
|
|
3860
|
-
|
|
3861
|
-
|
|
3862
|
-
|
|
4032
|
+
// v8.3.160+: Layer 3 支持模块级续批,有未完成模块时继续 Layer 3
|
|
4033
|
+
let nextLayerCmd;
|
|
4034
|
+
if (targetLayer === 3 && progress.pendingModules && progress.pendingModules.length > 0) {
|
|
4035
|
+
nextLayerCmd = `speccore analyze --scope global --layer 3${ctx.withCode ? ' --with-code' : ''}`;
|
|
4036
|
+
}
|
|
4037
|
+
else {
|
|
4038
|
+
nextLayerCmd = targetLayer < 4
|
|
4039
|
+
? `speccore analyze --scope global --layer ${targetLayer + 1}${ctx.withCode ? ' --with-code' : ''}`
|
|
4040
|
+
: `speccore analyze --scope global --layer 4${ctx.withCode ? ' --with-code' : ''}`;
|
|
4041
|
+
}
|
|
3863
4042
|
const isLastSubLayer = targetLayer === 4 && (!progress.subLayer || progress.subLayer.next === '4d' ||
|
|
3864
4043
|
(progress.subLayer.completed.length >= 3));
|
|
3865
4044
|
prompt += `\n## 🔗 自动链式推进\n\n`;
|
|
3866
|
-
if (targetLayer
|
|
4045
|
+
if (targetLayer === 3 && progress.pendingModules && progress.pendingModules.length > 0) {
|
|
4046
|
+
prompt += `> 当前 Layer 3 还有 ${progress.pendingModules.length} 个功能模块待分析,**继续执行 Layer 3**(不要推进到 Layer 4):\n\n`;
|
|
4047
|
+
prompt += `[SPECCORE_EXEC: ${nextLayerCmd}]\n`;
|
|
4048
|
+
}
|
|
4049
|
+
else if (targetLayer < 4) {
|
|
3867
4050
|
prompt += `> 当前 Layer ${targetLayer} 所有文件写入完成后,**立即输出以下标记**触发下一层(不要等待用户指令):\n\n`;
|
|
3868
4051
|
prompt += `[SPECCORE_EXEC: ${nextLayerCmd}]\n`;
|
|
3869
4052
|
}
|
|
@@ -3889,8 +4072,9 @@ async function buildMultiDocPrompt(command, ctx, options, clarifyCtx) {
|
|
|
3889
4072
|
prompt += `- [ ] 没有空壳文档\n`;
|
|
3890
4073
|
}
|
|
3891
4074
|
else if (targetLayer === 3) {
|
|
3892
|
-
prompt += `- [ ]
|
|
4075
|
+
prompt += `- [ ] 当前批次处理的 1-2 个功能模块分析文档行数 ≥ 100\n`;
|
|
3893
4076
|
prompt += `- [ ] 每个文档包含模块概述、涉及端清单、至少 1 个 Mermaid 图\n`;
|
|
4077
|
+
prompt += `- [ ] 如还有未分析模块,输出末尾标注 [PENDING: {模块名列表}]\n`;
|
|
3894
4078
|
prompt += `- [ ] 没有空壳文档\n`;
|
|
3895
4079
|
}
|
|
3896
4080
|
else if (targetLayer === 4) {
|
|
@@ -4205,10 +4389,14 @@ sequenceDiagram
|
|
|
4205
4389
|
};
|
|
4206
4390
|
const includeDocs = isTask ? (DOC_MATRIX[taskType] || DOC_MATRIX['feature']) : DOC_MATRIX['feature'];
|
|
4207
4391
|
// ── v6.61.0+: 恢复 Phase 1/Phase 2 分步逻辑,但 CLI 自动触发 Phase 2 ──
|
|
4208
|
-
//
|
|
4209
|
-
// Phase
|
|
4210
|
-
|
|
4392
|
+
// v8.3.160+: 改为功能模块优先 — 全局只保留索引文档,每个功能模块有自己的综合文档
|
|
4393
|
+
// Phase 1: 全局索引(FUNCTION_MAP.md/INTERACTION_MAP.md/PLATFORMS.md) + 各功能模块 overview/
|
|
4394
|
+
// Phase 2: 各功能模块的各端专属文档({功能模块}/{端}/TECH.md 等)
|
|
4395
|
+
const GLOBAL_INDEX_DOCS = ['FUNCTION_MAP.md', 'INTERACTION_MAP.md', 'PLATFORMS.md'];
|
|
4396
|
+
const FEATURE_OVERVIEW_DOCS_LIST = ['REQUIREMENT.md', 'ANALYSIS.md', 'TECH.md', 'RISK.md', 'DEPS.md', 'REVIEW.md', 'MONITOR.md', 'DEV_GUIDE.md'];
|
|
4211
4397
|
const PLATFORM_DOCS = ['TECH.md', 'TEST.md', 'UI_SPEC.md', 'DEV_GUIDE.md'];
|
|
4398
|
+
// 向后兼容:保留 GLOBAL_DOCS 引用
|
|
4399
|
+
const GLOBAL_DOCS = [...GLOBAL_INDEX_DOCS, ...FEATURE_OVERVIEW_DOCS_LIST];
|
|
4212
4400
|
let taskDocs = docs.filter(([n]) => includeDocs.includes(n));
|
|
4213
4401
|
if (ctx.phase === '1') {
|
|
4214
4402
|
taskDocs = taskDocs.filter(([n]) => GLOBAL_DOCS.includes(n));
|
|
@@ -4401,6 +4589,10 @@ sequenceDiagram
|
|
|
4401
4589
|
techDoc[1] = `# 技术架构(跨端全局)\n\n> ${iter}\n\n## 写作要求\n撰写整体技术架构,覆盖所有端的交互关系:\n- 系统整体分层设计(各端在架构中的位置)\n- 跨端交互协议(前端↔后端通信方式、数据流向)\n- 中间件选型(缓存、消息队列、网关等)\n- 数据库整体设计(核心表结构、ER 关系)\n- 技术栈选型及理由\n`;
|
|
4402
4590
|
}
|
|
4403
4591
|
let prompt = `\n# 任务: ${command}${task} (${taskDocs.length}个文档 · ${isTask ? `类型:${taskType}` : '迭代全量'}${ctx.phase ? ` · Phase ${ctx.phase}` : ''})\n\n`;
|
|
4592
|
+
// v8.3.160+: 注入子 Agent 角色定义
|
|
4593
|
+
if (agentContextText) {
|
|
4594
|
+
prompt = agentContextText + '\n\n' + prompt;
|
|
4595
|
+
}
|
|
4404
4596
|
// v8.3.0+: 如果检测到需求质量不足,在 prompt 开头注入强制澄清阶段
|
|
4405
4597
|
if (needsClarify && !isGlobal && !isTask && !ctx.phase) {
|
|
4406
4598
|
const now = new Date().toISOString();
|
|
@@ -4568,29 +4760,21 @@ status: "clarified"
|
|
|
4568
4760
|
prompt += `- 写完后用 Read 验证文件内容已替换占位内容\n\n`;
|
|
4569
4761
|
// ── v8.1.0+: 逐文档模式自动注入 PRD 内容 + 前序文档摘要 ──
|
|
4570
4762
|
// v8.3.2+ 修复:PRD 注入不再仅限逐文档模式,Phase 1/2 模式下也必须注入需求内容
|
|
4763
|
+
// v8.3.160+: 改为路径引用模式(不再全文塞进,让 AI 按需 Read)
|
|
4571
4764
|
if (!isGlobal && !isTask && ctx.iteration && ctx.iteration !== 'GLOBAL') {
|
|
4572
4765
|
try {
|
|
4573
4766
|
const iterDirForCtx = await (0, context_1.getIterationDir)(iter);
|
|
4574
4767
|
if (iterDirForCtx) {
|
|
4575
|
-
// v8.
|
|
4576
|
-
|
|
4577
|
-
let prdContent = '';
|
|
4578
|
-
const seenPaths = new Set();
|
|
4768
|
+
// v8.3.160+: 路径引用模式 — 列出文档清单,让 AI 按需 Read
|
|
4769
|
+
const docList = [];
|
|
4579
4770
|
const goldenReqDir = (0, path_1.join)(iterDirForCtx, '020-specs', 'requirements');
|
|
4580
4771
|
if (await (0, fs_extra_1.pathExists)(goldenReqDir)) {
|
|
4581
|
-
// 黄金需求目录存在,只读这里(唯一依据)
|
|
4582
4772
|
const files = (await Promise.resolve().then(() => __importStar(require('fs-extra')))).readdirSync(goldenReqDir).filter((f) => f.endsWith('.md')).slice(0, 10);
|
|
4583
4773
|
for (const f of files) {
|
|
4584
|
-
|
|
4585
|
-
let content = await (await Promise.resolve().then(() => __importStar(require('fs-extra')))).readFile(filePath, 'utf-8');
|
|
4586
|
-
content = await (0, prompt_builder_1.processMarkdownContent)(content, filePath, seenPaths, undefined, {
|
|
4587
|
-
maxLinkDepth: 2, maxLinkChars: 1200, maxSvgChars: 1500,
|
|
4588
|
-
});
|
|
4589
|
-
prdContent += `\n### ${f}\n${content.slice(0, 2000)}\n`;
|
|
4774
|
+
docList.push(`020-specs/requirements/${f}`);
|
|
4590
4775
|
}
|
|
4591
4776
|
}
|
|
4592
4777
|
else {
|
|
4593
|
-
// 回退:从 010-requirements/ 读取原始需求
|
|
4594
4778
|
const reqDir = (0, path_1.join)(iterDirForCtx, '010-requirements');
|
|
4595
4779
|
const prdSources = (0, path_1.join)(reqDir, 'sources');
|
|
4596
4780
|
const prdConverted = (0, path_1.join)(reqDir, 'converted');
|
|
@@ -4599,42 +4783,30 @@ status: "clarified"
|
|
|
4599
4783
|
if (await (0, fs_extra_1.pathExists)(dir)) {
|
|
4600
4784
|
const files = (await Promise.resolve().then(() => __importStar(require('fs-extra')))).readdirSync(dir).filter((f) => f.endsWith('.md')).slice(0, 5);
|
|
4601
4785
|
for (const f of files) {
|
|
4602
|
-
const
|
|
4603
|
-
|
|
4604
|
-
content = await (0, prompt_builder_1.processMarkdownContent)(content, filePath, seenPaths, undefined, {
|
|
4605
|
-
maxLinkDepth: 2, maxLinkChars: 1200, maxSvgChars: 1500,
|
|
4606
|
-
});
|
|
4607
|
-
prdContent += `\n### ${f}\n${content.slice(0, 2000)}\n`;
|
|
4786
|
+
const relDir = (0, path_1.relative)(iterDirForCtx, dir);
|
|
4787
|
+
docList.push(`${relDir}/${f}`);
|
|
4608
4788
|
}
|
|
4609
4789
|
}
|
|
4610
4790
|
}
|
|
4611
4791
|
}
|
|
4612
|
-
if (
|
|
4613
|
-
prompt += `\n## 📎
|
|
4614
|
-
|
|
4615
|
-
prompt += `>
|
|
4616
|
-
|
|
4617
|
-
|
|
4792
|
+
if (docList.length > 0) {
|
|
4793
|
+
prompt += `\n## 📎 需求文档清单(请按需 Read)\n\n`;
|
|
4794
|
+
prompt += `> 以下是你撰写专业分析文档的核心输入。所有分析必须基于这些内容,不要臆造。\n`;
|
|
4795
|
+
prompt += `> 用 Read 工具按需读取,不需要一次性读完所有文档。\n\n`;
|
|
4796
|
+
for (const docPath of docList) {
|
|
4797
|
+
prompt += `- \`${docPath}\`\n`;
|
|
4798
|
+
}
|
|
4799
|
+
prompt += `\n`;
|
|
4618
4800
|
}
|
|
4619
|
-
// 2.
|
|
4801
|
+
// 2. 前序已填充文档路径(改为路径引用,不再注入摘要)
|
|
4620
4802
|
if (perDocStatus?.filled.length && perDocStatus.filled.length > 0) {
|
|
4621
|
-
|
|
4622
|
-
prompt +=
|
|
4623
|
-
prompt += `>
|
|
4803
|
+
prompt += `## 📎 前序已填充文档(必须与之保持一致)\n\n`;
|
|
4804
|
+
prompt += `> 以下文档已生成,当前文档必须与之保持一致(字段名、接口路径、状态枚举等不能冲突)。\n`;
|
|
4805
|
+
prompt += `> 用 Read 工具读取这些文件,提取关键约束。\n\n`;
|
|
4624
4806
|
for (const filledPath of perDocStatus.filled) {
|
|
4625
|
-
|
|
4626
|
-
try {
|
|
4627
|
-
let content = await (await Promise.resolve().then(() => __importStar(require('fs-extra')))).readFile(fullPath, 'utf-8');
|
|
4628
|
-
if (fullPath.endsWith('.md')) {
|
|
4629
|
-
content = await (0, prompt_builder_1.processMarkdownContent)(content, fullPath, seenPaths, undefined, {
|
|
4630
|
-
maxLinkDepth: 1, maxLinkChars: 800, maxSvgChars: 1000,
|
|
4631
|
-
});
|
|
4632
|
-
}
|
|
4633
|
-
const summary = content.slice(0, 1500);
|
|
4634
|
-
prompt += `### ${filledPath}\n\`\`\`\n${summary}${content.length > 1500 ? '\n... (截断)' : ''}\n\`\`\`\n\n`;
|
|
4635
|
-
}
|
|
4636
|
-
catch { /* skip unreadable */ }
|
|
4807
|
+
prompt += `- \`020-specs/${filledPath}\`\n`;
|
|
4637
4808
|
}
|
|
4809
|
+
prompt += `\n`;
|
|
4638
4810
|
}
|
|
4639
4811
|
}
|
|
4640
4812
|
}
|
|
@@ -4712,9 +4884,18 @@ status: "clarified"
|
|
|
4712
4884
|
prompt += `- Read 020-specs/overview/ANALYSIS.md → 分析报告\n`;
|
|
4713
4885
|
prompt += `- Read 020-specs/overview/TECH.md → 整体技术架构\n`;
|
|
4714
4886
|
prompt += `- Read 020-specs/overview/RISK.md、DEPS.md、REVIEW.md、MONITOR.md(如存在)\n\n`;
|
|
4715
|
-
prompt += `### Step 2:
|
|
4716
|
-
prompt +=
|
|
4717
|
-
prompt +=
|
|
4887
|
+
prompt += `### Step 2: 按功能模块组织,为每个功能模块的每个端撰写专属文档(v8.3.1+ 强制四文档)\n`;
|
|
4888
|
+
prompt += `先 Read \`020-specs/overview/FUNCTION_MAP.md\` 获取功能模块清单。\n\n`;
|
|
4889
|
+
prompt += `**分批策略(v8.3.160+)**:\n`;
|
|
4890
|
+
prompt += `- 如功能模块数量 ≤ 2:一次生成所有模块的所有端文档\n`;
|
|
4891
|
+
prompt += `- 如功能模块数量 > 2:一次只处理 **1-2 个功能模块** 的所有端文档,确保质量深入\n`;
|
|
4892
|
+
prompt += `- 剩余模块在输出末尾标注 \`[PENDING: {模块名列表}]\`,供续批处理\n`;
|
|
4893
|
+
prompt += `- 续批命令:\`speccore analyze --prompt -I ${iter} --phase 2\`\n`;
|
|
4894
|
+
prompt += `- **关键原则**:宁可少分析几个模块,也要保证每个模块的分析质量(文档充实、有具体代码引用、表格有数据行)\n\n`;
|
|
4895
|
+
prompt += `每个功能模块涉及的每个端,分别撰写以下 **4 份文档**,缺一不可:\n\n`;
|
|
4896
|
+
prompt += `**文件路径格式**:\`{功能模块名}/{端名}/{文件名}\`(如 \`booking/backend/TECH.md\`、\`booking/h5-mobile/UI_SPEC.md\`)\n`;
|
|
4897
|
+
prompt += `> ⚠️ **严禁**只写 \`{端}/{文件}\` 格式(如 \`backend/TECH.md\`),这会导致所有功能模块的文档混在一起无法区分\n\n`;
|
|
4898
|
+
prompt += `**1. {功能模块}/{端}/TECH.md — 该端在该功能模块下的技术方案(必须对齐 overview/TECH.md 架构)**\n`;
|
|
4718
4899
|
prompt += ` - 该端的分层架构、模块划分、核心接口设计(路径/方法/参数/响应/状态码)\n`;
|
|
4719
4900
|
prompt += ` - 数据库表结构(字段/类型/索引/约束,如适用)\n`;
|
|
4720
4901
|
prompt += ` - 业务规则实现(含边界条件和异常流)\n`;
|
|
@@ -4722,15 +4903,15 @@ status: "clarified"
|
|
|
4722
4903
|
prompt += ` - 表格格式:| 业务模块 | 代码实体 | 关系类型 | 说明 |\n`;
|
|
4723
4904
|
prompt += ` - 示例:| 会议室档案 | backend/RoomController.java | api_controller | REST 控制器 |\n`;
|
|
4724
4905
|
prompt += ` - 示例:| 会议室档案 | admin-web/src/pages/RoomList.vue | page | 列表页 |\n\n`;
|
|
4725
|
-
prompt += `**2. {端}/TEST.md —
|
|
4906
|
+
prompt += `**2. {功能模块}/{端}/TEST.md — 该端在该功能模块下的测试计划**\n`;
|
|
4726
4907
|
prompt += ` - 覆盖该端所有功能模块的测试用例(含前置条件、步骤、预期结果)\n`;
|
|
4727
4908
|
prompt += ` - 边界值和异常输入测试\n`;
|
|
4728
4909
|
prompt += ` - 必须覆盖 REQUIREMENT.md 中该端的验收标准\n\n`;
|
|
4729
|
-
prompt += `**3. {端}/UI_SPEC.md —
|
|
4910
|
+
prompt += `**3. {功能模块}/{端}/UI_SPEC.md — 该端在该功能模块下的 UI 规格(仅前端端需要)**\n`;
|
|
4730
4911
|
prompt += ` - 页面结构与路由(每个页面的路径、入口、权限)\n`;
|
|
4731
4912
|
prompt += ` - 组件清单和字段→UI 映射\n`;
|
|
4732
4913
|
prompt += ` - 状态枚举和错误处理策略\n\n`;
|
|
4733
|
-
prompt += `**4. {端}/DEV_GUIDE.md —
|
|
4914
|
+
prompt += `**4. {功能模块}/{端}/DEV_GUIDE.md — 该端在该功能模块下的开发者实现指南(v8.3.1+ 新增强制要求)**\n`;
|
|
4734
4915
|
prompt += ` - ⛔ **禁止只写框架/占位符** — 必须是可执行的实现指导\n`;
|
|
4735
4916
|
prompt += ` - **本端改造范围清单**:列出本端所有需要新增/修改/删除的文件(相对项目根目录的具体路径),表格表头:| # | 类型 | 文件/目录 | 说明 |\n`;
|
|
4736
4917
|
prompt += ` - **本端实施步骤(按依赖排序)**:Step-by-step,具体到文件/函数级,说明每步为什么先做\n`;
|
|
@@ -4750,10 +4931,14 @@ status: "clarified"
|
|
|
4750
4931
|
prompt += `- 如果「验证方式」表格只有表头 → **必须补充可执行的命令/操作**\n`;
|
|
4751
4932
|
prompt += `- DEV_GUIDE.md 必须有至少 3 个具体文件路径和 3 个可执行验证步骤\n\n`;
|
|
4752
4933
|
prompt += `### 写入方式\n`;
|
|
4753
|
-
prompt +=
|
|
4754
|
-
prompt +=
|
|
4755
|
-
prompt +=
|
|
4756
|
-
prompt +=
|
|
4934
|
+
prompt += `**按功能模块批量写入**(推荐):一次 --apply 写入一个功能模块的所有端文档\n`;
|
|
4935
|
+
prompt += `\`\`\`bash\n`;
|
|
4936
|
+
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`;
|
|
4937
|
+
prompt += `\`\`\`\n\n`;
|
|
4938
|
+
prompt += `**或者逐功能模块-逐端写入**(每端一次 --apply):\n`;
|
|
4939
|
+
prompt += `\`\`\`bash\n`;
|
|
4940
|
+
prompt += `speccore analyze --apply '{"{功能模块}/{端}/TECH.md":"...","{功能模块}/{端}/TEST.md":"..."}' -I ${iter}\n`;
|
|
4941
|
+
prompt += `\`\`\`\n\n`;
|
|
4757
4942
|
prompt += `### ⛔ 强制约束(违反则分析无效)\n`;
|
|
4758
4943
|
prompt += `- **禁止省略任何一份文档** — 4 份文档(TECH.md + TEST.md + UI_SPEC.md + DEV_GUIDE.md)缺一不可\n`;
|
|
4759
4944
|
prompt += `- **禁止输出空表格** — 每个 Markdown 表格必须有至少 1 行数据,只有表头的表格视为未完成\n`;
|
|
@@ -4774,25 +4959,31 @@ status: "clarified"
|
|
|
4774
4959
|
const tpl = templateMap[doc[0]] || '';
|
|
4775
4960
|
prompt += ` - ${doc[0]} → 参考 ${tpl}\n`;
|
|
4776
4961
|
}
|
|
4777
|
-
prompt += `2.
|
|
4778
|
-
prompt += `
|
|
4779
|
-
prompt += `
|
|
4780
|
-
prompt += `
|
|
4781
|
-
prompt += `
|
|
4782
|
-
|
|
4783
|
-
|
|
4784
|
-
|
|
4785
|
-
|
|
4786
|
-
|
|
4787
|
-
|
|
4788
|
-
|
|
4789
|
-
|
|
4790
|
-
|
|
4791
|
-
prompt += `
|
|
4792
|
-
prompt += `
|
|
4793
|
-
prompt += `
|
|
4794
|
-
prompt += `
|
|
4795
|
-
prompt += `
|
|
4962
|
+
prompt += `2. 读取全局层产物(建立全局视角,按需 Read):\n`;
|
|
4963
|
+
prompt += ` \`.speccore/GLOBAL/\` 下已有系统级产物,按需读取了解当前状态:\n`;
|
|
4964
|
+
prompt += ` - \`requirements/REQUIREMENT.md\`、\`overview/FUNCTION_MAP.md\`、\`overview/API_CONTRACT.yaml\`\n`;
|
|
4965
|
+
prompt += ` - \`overview/ARCHITECTURE.md\`、\`platforms/{端}/_INDEX.md\`、\`platforms/_shared/_ASSOCIATION.md\`\n`;
|
|
4966
|
+
prompt += ` - 如不存在则跳过\n\n`;
|
|
4967
|
+
// v8.3.160+: 迭代级分析结合源码上下文
|
|
4968
|
+
if (ctx.withCode) {
|
|
4969
|
+
prompt += `2b. **源码上下文(--with-code)**:\n`;
|
|
4970
|
+
// v8.3.160+: 大项目按端分段读取,避免一次性加载全量结构化数据
|
|
4971
|
+
prompt += ` - 先 Read \`.speccore/cache/structured-data.json\` 的 stats 部分(了解各端规模)\n`;
|
|
4972
|
+
prompt += ` - **按需读取各端分段文件**:\`.speccore/cache/structured-data.{端名}.json\`(只读取本功能模块涉及的端)\n`;
|
|
4973
|
+
prompt += ` - 再按需 Read 各端源码中与本功能模块相关的关键文件(从 CONSTITUTION.md 获取源码路径)\n`;
|
|
4974
|
+
prompt += ` - 重点关注:接口定义、数据模型、业务逻辑实现、状态管理\n\n`;
|
|
4975
|
+
}
|
|
4976
|
+
prompt += `3. 读取迭代需求文档(按功能模块组织):\n`;
|
|
4977
|
+
prompt += ` a. 先读 010-requirements/INDEX.md — 获取功能模块清单\n`;
|
|
4978
|
+
prompt += ` b. **按功能模块逐一读取**:每个功能模块独立分析,不要跨模块混淆\n`;
|
|
4979
|
+
prompt += ` - 读取该模块对应的 010-requirements/converted/*.md 或 features/{模块}/README.md\n`;
|
|
4980
|
+
prompt += ` - 读取该模块涉及的原型文件(HTML/图片/链接)\n`;
|
|
4981
|
+
prompt += ` c. **文档长度自适应**:如果单个需求文档超过 5000 字,先快速扫描目录和章节标题,标记关键章节,再深入阅读\n`;
|
|
4982
|
+
prompt += `4. **按功能模块逐一撰写文档(重要)**:\n`;
|
|
4983
|
+
prompt += ` - 每个功能模块独立生成一套综合文档,放在 \`{功能模块}/overview/\` 目录下\n`;
|
|
4984
|
+
prompt += ` - 不要生成全局的 \`overview/REQUIREMENT.md\`(不要把所有模块拟合在一起)\n`;
|
|
4985
|
+
prompt += ` - 全局 \`overview/\` 只保留 FUNCTION_MAP.md、INTERACTION_MAP.md、PLATFORMS.md 作为索引\n`;
|
|
4986
|
+
prompt += ` - 示例:\`booking/overview/REQUIREMENT.md\`、\`booking/overview/ANALYSIS.md\`、\`payment/overview/REQUIREMENT.md\` 等\n`;
|
|
4796
4987
|
prompt += `5. **文档忠实度约束(最高优先级)**:\n`;
|
|
4797
4988
|
prompt += ` - **严禁臆造**:只能写需求文档中明确提及的功能、接口、页面、字段、业务规则\n`;
|
|
4798
4989
|
prompt += ` - **严禁扩展**:不要基于迭代名称或你的知识补充文档中未提及的内容\n`;
|
|
@@ -4814,26 +5005,16 @@ status: "clarified"
|
|
|
4814
5005
|
prompt += ` - 第 4 步:如果以上都无法确定,根据需求文档内容判断\n`;
|
|
4815
5006
|
prompt += ` - 第 5 步:将发现的端列表写入 020-specs/PLATFORMS.md\n`;
|
|
4816
5007
|
// v6.70.0+: REQUIREMENT.md 以产品视角撰写(不按端分章节)
|
|
4817
|
-
//
|
|
4818
|
-
prompt += `9. **REQUIREMENT.md
|
|
4819
|
-
prompt += ` -
|
|
4820
|
-
prompt += ` -
|
|
4821
|
-
prompt += `
|
|
4822
|
-
prompt += `
|
|
4823
|
-
prompt += ` - **核心场景地图**:按业务流程组织的场景列表,每个场景标注优先级\n`;
|
|
4824
|
-
prompt += ` - **功能全景图**:所有功能模块的可视化列表(表格或脑图描述)\n`;
|
|
4825
|
-
prompt += ` - **功能优先级矩阵**:P0(必须)/ P1(重要)/ P2(可选)标注\n`;
|
|
4826
|
-
prompt += ` - **发布里程碑**:如有分期计划,标注各阶段交付内容\n`;
|
|
4827
|
-
prompt += ` - **风险预判**:技术风险、业务风险、依赖风险(如有)\n`;
|
|
4828
|
-
prompt += ` - 每个场景描述:用户操作 → 系统响应 → 业务规则 → 边界条件 → 验收标准\n`;
|
|
4829
|
-
prompt += ` - 系统响应中自然包含前后端交互,但不刻意标注技术实现细节\n`;
|
|
4830
|
-
prompt += ` - 示例正确写法:「用户选择时间段后点击预订,系统检查会议室可用性,如可用则锁定会议室并创建待支付订单」\n`;
|
|
4831
|
-
prompt += ` - 示例错误写法:「后端 booking-service 需要新增 /api/bookings 接口,接收 roomId 参数」\n`;
|
|
4832
|
-
prompt += ` - 技术实现细节留在 TECH.md 和各端专属文档中,不在 REQUIREMENT.md 展开\n`;
|
|
5008
|
+
// v8.3.160+: 压缩写作风格说明
|
|
5009
|
+
prompt += `9. **REQUIREMENT.md 写作风格**:产品/用户视角,按业务场景组织章节(不按端分节)\n`;
|
|
5010
|
+
prompt += ` - 必须包含:产品愿景、用户画像、核心场景地图(标注优先级)、功能全景图、功能优先级矩阵(P0/P1/P2)、发布里程碑、风险预判\n`;
|
|
5011
|
+
prompt += ` - 场景描述格式:用户操作 → 系统响应 → 业务规则 → 边界条件 → 验收标准\n`;
|
|
5012
|
+
prompt += ` - ✅ 正确:「用户点击预订,系统检查可用性,如可用则锁定并创建订单」\n`;
|
|
5013
|
+
prompt += ` - ❌ 错误:「后端新增 /api/bookings 接口,接收 roomId」→ 技术细节留给 TECH.md\n`;
|
|
4833
5014
|
prompt += ` - 端的信息只在「功能模块清单」表格中标注,正文不区分端\n`;
|
|
4834
5015
|
// v6.49.14+: 功能模块清单必须含涉及端列 + 来源链接
|
|
4835
5016
|
// v6.71.3+: 增加「与全局层对比」列
|
|
4836
|
-
prompt += `10.
|
|
5017
|
+
prompt += `10. **功能模块清单(重要)**:每个功能模块的 \`overview/REQUIREMENT.md\` 都必须包含功能模块清单表格\n`;
|
|
4837
5018
|
prompt += ` - 表格格式:| # | 功能模块 | 涉及端 | 全局对比 | 来源 | 说明 |\n`;
|
|
4838
5019
|
prompt += ` - 「涉及端」:每个模块标注需要**新开发工作**的端(标准端名,逗号分隔)\n`;
|
|
4839
5020
|
prompt += ` - 「涉及」= 该端需要写新接口/新页面/新逻辑\n`;
|
|
@@ -4854,12 +5035,12 @@ status: "clarified"
|
|
|
4854
5035
|
prompt += ` - split 命令将读取「涉及端」列来决定创建哪些端的子任务目录\n`;
|
|
4855
5036
|
// v6.70.0+: 跨端功能映射表(FUNCTION_MAP.md)
|
|
4856
5037
|
// v6.71.3+: 增加与全局层关联分析
|
|
4857
|
-
prompt += `7a.
|
|
5038
|
+
prompt += `7a. **迭代需求与全局层关联分析(重要)**:在每个功能模块的 \`overview/REQUIREMENT.md\` 中,必须对比全局层产物\n`;
|
|
4858
5039
|
prompt += ` - 对比迭代需求中的功能模块 vs .speccore/GLOBAL/overview/FUNCTION_MAP.md 中的功能单元\n`;
|
|
4859
5040
|
prompt += ` - 标注每个功能模块的「全局对比」类型(新增/扩展/重构/复用)\n`;
|
|
4860
5041
|
prompt += ` - 识别冲突:如迭代需求修改了全局层已有接口的字段/路径 → 在 RISK.md 中标注\n`;
|
|
4861
5042
|
prompt += ` - 识别依赖:如迭代的新功能依赖全局层的某个功能 → 在 FUNCTION_MAP.md「依赖任务」中标注\n\n`;
|
|
4862
|
-
prompt += `7b.
|
|
5043
|
+
prompt += `7b. **跨端功能映射表(重要)**:全局索引 \`overview/FUNCTION_MAP.md\` 汇总所有功能模块的映射关系\n`;
|
|
4863
5044
|
prompt += ` - 这是 split 阶段的核心输入,决定任务如何按功能单元拆分\n`;
|
|
4864
5045
|
prompt += ` - 表格格式:| # | 功能单元 | 涉及端 | 全局对比 | 共享能力 | 依赖任务 | 说明 |\n`;
|
|
4865
5046
|
prompt += ` - 「功能单元」必须与 REQUIREMENT.md 功能模块清单一一对应,不允许合并\n`;
|
|
@@ -4872,7 +5053,7 @@ status: "clarified"
|
|
|
4872
5053
|
prompt += ` - **错误示例**(禁止):将"审批流程"和"定时任务"合并为一行\n`;
|
|
4873
5054
|
prompt += ` - FUNCTION_MAP.md 生成后,split 将**严格按此表**创建任务目录,不再由 AI 推断\n`;
|
|
4874
5055
|
// v6.70.0+: 跨端交互图谱(INTERACTION_MAP.md)
|
|
4875
|
-
prompt += `7c.
|
|
5056
|
+
prompt += `7c. **跨端交互图谱(重要)**:全局索引 \`overview/INTERACTION_MAP.md\` 汇总所有功能模块的交互时序\n`;
|
|
4876
5057
|
prompt += ` - 按功能单元组织,每个功能单元一个 Mermaid sequenceDiagram\n`;
|
|
4877
5058
|
prompt += ` - 展示完整的业务交互时序:用户操作 → 前端处理 → 后端调用 → 数据返回\n`;
|
|
4878
5059
|
prompt += ` - 明确标出后端服务之间的内部调用(产品文档写"系统处理"的地方)\n`;
|
|
@@ -4881,43 +5062,40 @@ status: "clarified"
|
|
|
4881
5062
|
prompt += ` - 如涉及状态变更,附「状态流转」表格\n`;
|
|
4882
5063
|
prompt += ` - INTERACTION_MAP.md 是前后端开发者的共同参考,补全产品文档中隐含的技术交互\n`;
|
|
4883
5064
|
// 注入工程类型信息(v6.49.0+)
|
|
5065
|
+
// v8.3.160+: 精简为路径引用,具体维度让 AI 从 CONSTITUTION.md 读取
|
|
4884
5066
|
const platformTypes = await (0, spec_paths_1.parsePlatformTypes)();
|
|
4885
5067
|
if (platformTypes.size > 0) {
|
|
4886
|
-
prompt += `8. **工程类型识别**:CONSTITUTION.md
|
|
5068
|
+
prompt += `8. **工程类型识别**:CONSTITUTION.md 已配置各端的工程类型,请 Read 后据此生成针对性内容\n`;
|
|
4887
5069
|
prompt += ` | 工程标识 | 工程类型 |\n`;
|
|
4888
5070
|
prompt += ` | :--- | :--- |\n`;
|
|
4889
5071
|
for (const [name, type] of platformTypes) {
|
|
4890
5072
|
prompt += ` | ${name} | ${type} |\n`;
|
|
4891
5073
|
}
|
|
4892
|
-
prompt += `\n
|
|
4893
|
-
prompt += ` - Java服务 → API设计、数据库、缓存、消息队列、安全、性能\n`;
|
|
4894
|
-
prompt += ` - Node服务 → API设计、数据库、中间件、异步处理、安全\n`;
|
|
4895
|
-
prompt += ` - Go服务 → API设计、数据库、并发、微服务、性能\n`;
|
|
4896
|
-
prompt += ` - Python服务 → API设计、数据库、数据分析、AI/ML集成\n`;
|
|
4897
|
-
prompt += ` - H5微信公众号 → 微信JS-SDK、OAuth授权、分享、支付、模板消息\n`;
|
|
4898
|
-
prompt += ` - H5移动端 → 响应式、viewport适配、触摸交互、弱网优化、首屏性能\n`;
|
|
4899
|
-
prompt += ` - Android移动端 → 生命周期、权限、推送、适配、内存优化\n`;
|
|
4900
|
-
prompt += ` - iOS移动端 → Swift/SwiftUI、App Store规范、推送、性能\n`;
|
|
4901
|
-
prompt += ` - 微信小程序 → 包体积(2MB)、平台API、setData优化、页面栈\n`;
|
|
4902
|
-
prompt += ` - Web管理后台 → 复杂表单、数据表格、权限UI、状态管理\n`;
|
|
4903
|
-
prompt += ` - 桌面应用 → 本地存储、系统API、自动更新、离线支持\n`;
|
|
5074
|
+
prompt += `\n`;
|
|
4904
5075
|
}
|
|
4905
5076
|
const dirStepNum = platformTypes.size > 0 ? 9 : 8;
|
|
4906
5077
|
prompt += `${dirStepNum}. **目录结构(严格遵循,禁止自创目录)**:\n`;
|
|
4907
|
-
prompt += ` -
|
|
4908
|
-
prompt += ` -
|
|
4909
|
-
prompt += ` -
|
|
4910
|
-
prompt += ` -
|
|
4911
|
-
prompt += ` -
|
|
4912
|
-
prompt += ` -
|
|
4913
|
-
prompt += ` -
|
|
4914
|
-
prompt += ` -
|
|
4915
|
-
prompt += ` - RISK.md
|
|
4916
|
-
prompt += ` -
|
|
4917
|
-
prompt += ` -
|
|
5078
|
+
prompt += ` - **全局索引**(跨端汇总)→ 通过 --apply 写入,CLI 自动路由到 \`020-specs/overview/{文件名}\`\n`;
|
|
5079
|
+
prompt += ` - FUNCTION_MAP.md(功能单元 × 端映射表)\n`;
|
|
5080
|
+
prompt += ` - INTERACTION_MAP.md(跨端交互时序图)\n`;
|
|
5081
|
+
prompt += ` - PLATFORMS.md(端列表)\n`;
|
|
5082
|
+
prompt += ` - **功能模块综合文档**(每个功能模块独立)→ 通过 --apply 写入,CLI 自动路由到 \`020-specs/{功能模块名}/overview/{文件名}\`\n`;
|
|
5083
|
+
prompt += ` - {功能模块}/overview/REQUIREMENT.md(该模块的需求规格)\n`;
|
|
5084
|
+
prompt += ` - {功能模块}/overview/ANALYSIS.md(该模块的需求分析)\n`;
|
|
5085
|
+
prompt += ` - {功能模块}/overview/TECH.md(该模块的技术方案)\n`;
|
|
5086
|
+
prompt += ` - {功能模块}/overview/RISK.md(该模块的风险评估)\n`;
|
|
5087
|
+
prompt += ` - {功能模块}/overview/DEPS.md(该模块的依赖清单)\n`;
|
|
5088
|
+
prompt += ` - {功能模块}/overview/REVIEW.md(该模块的评审检查项)\n`;
|
|
5089
|
+
prompt += ` - {功能模块}/overview/MONITOR.md(该模块的监控指标)\n`;
|
|
5090
|
+
prompt += ` - {功能模块}/overview/DEV_GUIDE.md(该模块的实现指南)\n`;
|
|
5091
|
+
prompt += ` - **端专属文档**(按功能模块组织)→ 通过 --apply 写入,CLI 自动路由到 \`020-specs/{功能模块名}/{端名}/{文件名}\`\n`;
|
|
5092
|
+
prompt += ` - {功能模块}/{端}/TECH.md(该模块该端的技术方案)\n`;
|
|
5093
|
+
prompt += ` - {功能模块}/{端}/TEST.md(该模块该端的测试用例)\n`;
|
|
5094
|
+
prompt += ` - {功能模块}/{端}/UI_SPEC.md(该模块该端的 UI 规范,仅前端端)\n`;
|
|
5095
|
+
prompt += ` - {功能模块}/{端}/DEV_GUIDE.md(该模块该端的实现指南)\n`;
|
|
4918
5096
|
prompt += ` - **禁止**:不要创建 020-specs/ 下的任何额外子目录(如数字编号、中文名称等)\n`;
|
|
4919
5097
|
prompt += ` - **禁止直接用 Write 工具写文件到 020-specs/**:必须通过 \`speccore analyze --apply '{"文件名":"内容"}' -I ${iter}\` 写入\n`;
|
|
4920
|
-
prompt += ` - ⚠️ 直接 Write
|
|
5098
|
+
prompt += ` - ⚠️ 直接 Write 会导致目录结构错误,必须走 --apply 让 CLI 自动路由到正确子目录\n`;
|
|
4921
5099
|
if (ctx.phase !== '1') {
|
|
4922
5100
|
// 端专业性约束只在默认模式(全量)中输出
|
|
4923
5101
|
prompt += `\n## ⚠️ 端专业性约束\n`;
|
|
@@ -4931,7 +5109,7 @@ status: "clarified"
|
|
|
4931
5109
|
prompt += `- 缓存策略/并发与事务/消息队列(如涉及)\n`;
|
|
4932
5110
|
prompt += `- 安全:SQL 注入防护/接口鉴权/数据脱敏\n`;
|
|
4933
5111
|
prompt += `- 性能:QPS 预估/慢查询优化/连接池配置\n`;
|
|
4934
|
-
prompt += `- **不需要**产品视角的需求描述(用户故事、业务场景已在 overview/REQUIREMENT.md 中)\n\n`;
|
|
5112
|
+
prompt += `- **不需要**产品视角的需求描述(用户故事、业务场景已在 {功能模块}/overview/REQUIREMENT.md 中)\n\n`;
|
|
4935
5113
|
prompt += `### 前端端(h5 / admin-web / miniapp)必含内容 — 产品+技术双视角\n`;
|
|
4936
5114
|
prompt += `- **产品视角(主要)**:\n`;
|
|
4937
5115
|
prompt += ` - 用户旅程:该端用户如何完成核心任务(步骤流程图)\n`;
|
|
@@ -4949,24 +5127,26 @@ status: "clarified"
|
|
|
4949
5127
|
// v6.60.0+: 文档与端的对应关系(不再分 Phase)
|
|
4950
5128
|
// v6.71.0+: 前后端文档差异化
|
|
4951
5129
|
prompt += `### 文档与端的对应关系\n`;
|
|
4952
|
-
prompt += `- **overview/
|
|
4953
|
-
prompt += `- **overview/
|
|
4954
|
-
prompt += `- **overview/
|
|
4955
|
-
prompt += `- **overview/
|
|
4956
|
-
prompt += `- **overview/
|
|
4957
|
-
prompt += `- **overview/
|
|
4958
|
-
prompt += `-
|
|
4959
|
-
prompt += `-
|
|
4960
|
-
prompt += `-
|
|
5130
|
+
prompt += `- **overview/FUNCTION_MAP.md**:全局索引 — 所有功能单元 × 端映射表\n`;
|
|
5131
|
+
prompt += `- **overview/INTERACTION_MAP.md**:全局索引 — 所有跨端交互时序图\n`;
|
|
5132
|
+
prompt += `- **overview/PLATFORMS.md**:全局索引 — 端列表\n`;
|
|
5133
|
+
prompt += `- **{功能模块}/overview/REQUIREMENT.md**:该功能模块的需求规格(产品视角,按业务场景组织)\n`;
|
|
5134
|
+
prompt += `- **{功能模块}/overview/ANALYSIS.md**:该功能模块的需求分析\n`;
|
|
5135
|
+
prompt += `- **{功能模块}/overview/DEPS.md**:该功能模块的依赖清单\n`;
|
|
5136
|
+
prompt += `- **{功能模块}/overview/DEV_GUIDE.md**:该功能模块的开发者实现指南\n`;
|
|
5137
|
+
prompt += `- **{功能模块}/overview/RISK.md**:该功能模块的风险评估\n`;
|
|
5138
|
+
prompt += `- **{功能模块}/overview/REVIEW.md**:该功能模块的评审检查项\n`;
|
|
5139
|
+
prompt += `- **{功能模块}/overview/MONITOR.md**:该功能模块的监控指标\n`;
|
|
5140
|
+
prompt += `- **后端端(*service)/{功能模块}/{端}/TECH.md**:纯技术视角 — 接口设计+数据模型+架构+性能\n`;
|
|
5141
|
+
prompt += `- **前端端(h5/admin/miniapp)/{功能模块}/{端}/TECH.md**:产品+技术双视角 — 用户旅程+页面清单+交互流程+API调用链\n`;
|
|
5142
|
+
prompt += `- **前端端/{功能模块}/{端}/UI_SPEC.md**:UI 规格(字段映射、组件设计、交互细节)\n`;
|
|
4961
5143
|
prompt += ` - ⚠️ **必须包含「业务-代码映射」章节**:在 TECH.md 末尾添加一个表格,列出本端涉及的业务模块及其对应的代码实体(文件/表/API/组件等),关系类型由你根据技术栈自主决定(如 api_controller、uses_table、page、component、route、middleware、interceptor、gateway 等)\n`;
|
|
4962
5144
|
prompt += ` - 表格格式:| 业务模块 | 代码实体 | 关系类型 | 说明 |\n`;
|
|
4963
5145
|
prompt += ` - 示例:| 会议室档案 | backend/RoomController.java | api_controller | REST 控制器 |\n`;
|
|
4964
5146
|
prompt += ` - 示例:| 会议室档案 | admin-web/src/pages/RoomList.vue | page | 列表页 |\n`;
|
|
4965
|
-
prompt += `- **{端}/TEST.md
|
|
4966
|
-
prompt += `- **{端}/
|
|
4967
|
-
prompt += `- **{端}/
|
|
4968
|
-
prompt += `- **{端}/MONITOR.md**:该端专属监控指标\n`;
|
|
4969
|
-
prompt += `- **{端}/UI_SPEC.md**:前端端专属 UI 规格,字段映射必须与后端 API 响应字段一一对应\n`;
|
|
5147
|
+
prompt += `- **{功能模块}/{端}/TEST.md**:该功能模块下该端的测试计划\n`;
|
|
5148
|
+
prompt += `- **{功能模块}/{端}/DEV_GUIDE.md**:该功能模块下该端的实现指南\n`;
|
|
5149
|
+
prompt += `- **{功能模块}/{端}/UI_SPEC.md**:前端端专属 UI 规格,字段映射必须与后端 API 响应字段一一对应\n`;
|
|
4970
5150
|
prompt += `- 分析完成后会自动生成 QUALITY_AUDIT.md 质量报告,检查各端内容是否完整\n`;
|
|
4971
5151
|
}
|
|
4972
5152
|
// 步骤 2-7 已在上面的 phase 分支中处理
|
|
@@ -5431,6 +5611,17 @@ async function buildClarifyPhasePrompt(iteration) {
|
|
|
5431
5611
|
prompt += `3. \`010-requirements/converted/*.md\` — 已转换文档\n`;
|
|
5432
5612
|
prompt += `4. \`010-requirements/features/*/README.md\` — 功能级补充\n`;
|
|
5433
5613
|
prompt += `5. \`010-requirements/prototypes/\` — 原型文件\n\n`;
|
|
5614
|
+
// v8.3.160+ 修复:列出实际扫描到的源文档清单,明确每份文档独立澄清
|
|
5615
|
+
if (docPaths.length > 0) {
|
|
5616
|
+
prompt += `## 源文档清单(本次需澄清的原始文档)\n\n`;
|
|
5617
|
+
prompt += `共扫描到 ${docPaths.length} 份需求文档,**每份文档必须独立澄清**,分别生成对应的黄金需求文档:\n\n`;
|
|
5618
|
+
for (const p of docPaths) {
|
|
5619
|
+
const name = p.split('/').pop() || '-';
|
|
5620
|
+
const clarifiedName = name.replace(/\.md$/, '') + '-clarified.md';
|
|
5621
|
+
prompt += `- \`${name}\` → 澄清为 \`${clarifiedName}\`\n`;
|
|
5622
|
+
}
|
|
5623
|
+
prompt += `\n> ⚠️ **重要**:每份源文档对应一份独立的澄清文档,禁止将多份源文档合并为一份。\n\n`;
|
|
5624
|
+
}
|
|
5434
5625
|
// v8.2.0+: 如果检测到多个功能单元,启用单元化澄清模式
|
|
5435
5626
|
if (hasMultipleUnits) {
|
|
5436
5627
|
prompt += `## ⚠️ 功能单元拆分澄清模式(v8.2.0+)\n\n`;
|
|
@@ -5546,9 +5737,40 @@ async function buildClarifyPhasePrompt(iteration) {
|
|
|
5546
5737
|
prompt += ` - 记录取舍:每轮迭代记录「采纳/拒绝/待确认」的决策\n\n`;
|
|
5547
5738
|
prompt += `## Step 5: 确认写入\n\n`;
|
|
5548
5739
|
prompt += `当用户确认 "满意,可以写入" 后:\n\n`;
|
|
5549
|
-
prompt +=
|
|
5550
|
-
|
|
5551
|
-
|
|
5740
|
+
prompt += `**必须为每份源文档分别生成独立的澄清文档**,使用 \`[CLARIFY:requirements/文件名-clarified.md]\` 标记分隔:\n\n`;
|
|
5741
|
+
if (docPaths.length > 1) {
|
|
5742
|
+
prompt += `本次共 ${docPaths.length} 份源文档,需要输出 ${docPaths.length} 个 \`[CLARIFY:...]\` 标记块:\n\n`;
|
|
5743
|
+
for (const p of docPaths) {
|
|
5744
|
+
const name = p.split('/').pop() || '-';
|
|
5745
|
+
const baseName = name.replace(/\.md$/, '');
|
|
5746
|
+
prompt += `\`\`\`\n`;
|
|
5747
|
+
prompt += `[CLARIFY:requirements/${baseName}-clarified.md]\n`;
|
|
5748
|
+
prompt += `---\n`;
|
|
5749
|
+
prompt += `source: "010-requirements/.../${name}"\n`;
|
|
5750
|
+
prompt += `clarified-at: "${new Date().toISOString()}"\n`;
|
|
5751
|
+
prompt += `status: "clarified"\n`;
|
|
5752
|
+
prompt += `---\n\n`;
|
|
5753
|
+
prompt += `# ${baseName}(澄清后的专业 PRD)\n`;
|
|
5754
|
+
prompt += `...\n`;
|
|
5755
|
+
prompt += `\`\`\`\n\n`;
|
|
5756
|
+
}
|
|
5757
|
+
}
|
|
5758
|
+
else {
|
|
5759
|
+
prompt += `\`\`\`\n`;
|
|
5760
|
+
prompt += `[CLARIFY:requirements/xxx-clarified.md]\n`;
|
|
5761
|
+
prompt += `---\n`;
|
|
5762
|
+
prompt += `source: "原始文档路径"\n`;
|
|
5763
|
+
prompt += `clarified-at: "${new Date().toISOString()}"\n`;
|
|
5764
|
+
prompt += `status: "clarified"\n`;
|
|
5765
|
+
prompt += `---\n\n`;
|
|
5766
|
+
prompt += `# 功能名称\n`;
|
|
5767
|
+
prompt += `...\n`;
|
|
5768
|
+
prompt += `\`\`\`\n\n`;
|
|
5769
|
+
}
|
|
5770
|
+
prompt += `> ⚠️ **禁止合并**:每份源文档必须对应一份独立的 \`-clarified.md\`,即使内容有关联也要分开输出。\n\n`;
|
|
5771
|
+
prompt += `同时生成对应的对比报告文件(可选):\n`;
|
|
5772
|
+
prompt += `- \`020-specs/requirements/{源文件名}-clarified-diff.md\` — 保存最终对比报告\n\n`;
|
|
5773
|
+
prompt += `使用以下命令写入:\n\n`;
|
|
5552
5774
|
prompt += `\`\`\`bash\n`;
|
|
5553
5775
|
prompt += `speccore analyze --apply '{"020-specs/requirements/xxx-clarified.md":"...","020-specs/requirements/xxx-clarified-diff.md":"..."}' -I ${iteration}\n`;
|
|
5554
5776
|
prompt += `\`\`\`\n\n`;
|
|
@@ -5603,7 +5825,7 @@ async function buildConfirmCheckPrompt(iteration) {
|
|
|
5603
5825
|
prompt += `\`\`\`bash\n`;
|
|
5604
5826
|
prompt += `speccore analyze --prompt -I ${iteration} --pipeline --skip-clarify=false\n`;
|
|
5605
5827
|
prompt += `\`\`\`\n`;
|
|
5606
|
-
prompt += `> 重新进入 clarify-
|
|
5828
|
+
prompt += `> 重新进入 clarify-product 阶段,AI 会基于已有 draft 继续迭代。\n\n`;
|
|
5607
5829
|
prompt += `**选项 C:跳过确认,直接进入分析(风险自负)**\n`;
|
|
5608
5830
|
prompt += `\`\`\`bash\n`;
|
|
5609
5831
|
prompt += `speccore analyze --prompt -I ${iteration} --pipeline --skip-clarify\n`;
|