speccore 8.3.121 → 8.3.127
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 +1 -1
- package/dist/cli.js.map +1 -1
- package/dist/commands/about.d.ts.map +1 -1
- package/dist/commands/about.js +16 -0
- package/dist/commands/about.js.map +1 -1
- package/dist/commands/analyze.d.ts +1 -0
- package/dist/commands/analyze.d.ts.map +1 -1
- package/dist/commands/analyze.js +198 -70
- package/dist/commands/analyze.js.map +1 -1
- package/dist/commands/change.js +14 -7
- package/dist/commands/change.js.map +1 -1
- package/dist/commands/context.js +25 -31
- package/dist/commands/context.js.map +1 -1
- package/dist/commands/deploy.d.ts.map +1 -1
- package/dist/commands/deploy.js +6 -1
- package/dist/commands/deploy.js.map +1 -1
- package/dist/commands/done.js +29 -28
- package/dist/commands/done.js.map +1 -1
- package/dist/commands/execute.d.ts.map +1 -1
- package/dist/commands/execute.js +281 -20
- package/dist/commands/execute.js.map +1 -1
- package/dist/commands/import.js +1 -1
- package/dist/commands/import.js.map +1 -1
- package/dist/commands/init.d.ts +1 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +513 -28
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/iteration/create.js +1 -1
- package/dist/commands/iteration/split.d.ts.map +1 -1
- package/dist/commands/iteration/split.js +158 -70
- package/dist/commands/iteration/split.js.map +1 -1
- package/dist/commands/iteration-from-global.js +1 -2
- package/dist/commands/iteration-from-global.js.map +1 -1
- package/dist/commands/pattern.js +30 -16
- package/dist/commands/pattern.js.map +1 -1
- package/dist/commands/pipeline.d.ts.map +1 -1
- package/dist/commands/pipeline.js +15 -4
- package/dist/commands/pipeline.js.map +1 -1
- package/dist/commands/retro.d.ts.map +1 -1
- package/dist/commands/retro.js +15 -8
- package/dist/commands/retro.js.map +1 -1
- package/dist/commands/status-panel.js +1 -1
- package/dist/commands/status-panel.js.map +1 -1
- package/dist/commands/sync.js +15 -8
- package/dist/commands/sync.js.map +1 -1
- package/dist/commands/trace.js +27 -24
- package/dist/commands/trace.js.map +1 -1
- package/dist/commands/update-env-configs.d.ts.map +1 -1
- package/dist/commands/update-env-configs.js +33 -5
- package/dist/commands/update-env-configs.js.map +1 -1
- package/dist/commands/update.d.ts.map +1 -1
- package/dist/commands/update.js +35 -0
- package/dist/commands/update.js.map +1 -1
- package/dist/commands/verify.d.ts +1 -1
- package/dist/commands/verify.d.ts.map +1 -1
- package/dist/commands/verify.js +25 -0
- package/dist/commands/verify.js.map +1 -1
- package/dist/core/ai-context-generator.js +35 -22
- package/dist/core/ai-context-generator.js.map +1 -1
- package/dist/core/ai-impact-analyzer.d.ts.map +1 -1
- package/dist/core/ai-impact-analyzer.js +1 -1
- package/dist/core/ai-impact-analyzer.js.map +1 -1
- package/dist/core/analyze-engine.d.ts +2 -1
- package/dist/core/analyze-engine.d.ts.map +1 -1
- package/dist/core/analyze-engine.js +217 -53
- package/dist/core/analyze-engine.js.map +1 -1
- package/dist/core/ask-engine.d.ts.map +1 -1
- package/dist/core/ask-engine.js +31 -13
- package/dist/core/ask-engine.js.map +1 -1
- package/dist/core/change-detection.d.ts.map +1 -1
- package/dist/core/change-detection.js +21 -5
- package/dist/core/change-detection.js.map +1 -1
- package/dist/core/code-scanner.d.ts +12 -3
- package/dist/core/code-scanner.d.ts.map +1 -1
- package/dist/core/code-scanner.js +376 -10
- package/dist/core/code-scanner.js.map +1 -1
- package/dist/core/context-builder.d.ts +2 -1
- package/dist/core/context-builder.d.ts.map +1 -1
- package/dist/core/context-builder.js +104 -25
- package/dist/core/context-builder.js.map +1 -1
- package/dist/core/deploy/engine.d.ts.map +1 -1
- package/dist/core/deploy/engine.js +21 -6
- package/dist/core/deploy/engine.js.map +1 -1
- package/dist/core/git-integration.d.ts +5 -1
- package/dist/core/git-integration.d.ts.map +1 -1
- package/dist/core/git-integration.js +52 -15
- package/dist/core/git-integration.js.map +1 -1
- package/dist/core/knowledge-graph.d.ts +2 -0
- package/dist/core/knowledge-graph.d.ts.map +1 -1
- package/dist/core/knowledge-graph.js +246 -232
- package/dist/core/knowledge-graph.js.map +1 -1
- package/dist/core/name-validator.js +1 -1
- package/dist/core/name-validator.js.map +1 -1
- package/dist/core/pipeline-test.d.ts.map +1 -1
- package/dist/core/pipeline-test.js +7 -4
- package/dist/core/pipeline-test.js.map +1 -1
- package/dist/core/prompt-builder.d.ts +3 -0
- package/dist/core/prompt-builder.d.ts.map +1 -1
- package/dist/core/prompt-builder.js +629 -77
- package/dist/core/prompt-builder.js.map +1 -1
- package/dist/core/rag-engine.d.ts +10 -5
- package/dist/core/rag-engine.d.ts.map +1 -1
- package/dist/core/rag-engine.js +134 -16
- package/dist/core/rag-engine.js.map +1 -1
- package/dist/core/spec-paths.d.ts +42 -2
- package/dist/core/spec-paths.d.ts.map +1 -1
- package/dist/core/spec-paths.js +207 -11
- package/dist/core/spec-paths.js.map +1 -1
- package/dist/core/structured-extractor.d.ts +67 -0
- package/dist/core/structured-extractor.d.ts.map +1 -1
- package/dist/core/structured-extractor.js +1298 -23
- package/dist/core/structured-extractor.js.map +1 -1
- package/dist/core/test-config.d.ts +1 -1
- package/dist/core/test-config.d.ts.map +1 -1
- package/dist/core/test-config.js +85 -65
- package/dist/core/test-config.js.map +1 -1
- package/dist/core/ui-verify/report-generator.d.ts.map +1 -1
- package/dist/core/ui-verify/report-generator.js +7 -3
- package/dist/core/ui-verify/report-generator.js.map +1 -1
- package/dist/core/ui-verify/smoke-engine.js +7 -1
- package/dist/core/ui-verify/smoke-engine.js.map +1 -1
- package/dist/core/unified-config.d.ts +43 -3
- package/dist/core/unified-config.d.ts.map +1 -1
- package/dist/core/unified-config.js +133 -13
- package/dist/core/unified-config.js.map +1 -1
- package/dist/core/unified-retrieval.d.ts.map +1 -1
- package/dist/core/unified-retrieval.js +41 -15
- package/dist/core/unified-retrieval.js.map +1 -1
- package/dist/core/unit-context-assembler.js +2 -2
- package/dist/core/unit-context-assembler.js.map +1 -1
- package/dist/core/validator.d.ts.map +1 -1
- package/dist/core/validator.js +59 -44
- package/dist/core/validator.js.map +1 -1
- package/dist/core/verify-engine.d.ts +9 -2
- package/dist/core/verify-engine.d.ts.map +1 -1
- package/dist/core/verify-engine.js +274 -108
- package/dist/core/verify-engine.js.map +1 -1
- package/dist/core/verify-report-aggregator.js +9 -20
- package/dist/core/verify-report-aggregator.js.map +1 -1
- package/dist/utils/synonyms.d.ts +24 -0
- package/dist/utils/synonyms.d.ts.map +1 -0
- package/dist/utils/synonyms.js +114 -0
- package/dist/utils/synonyms.js.map +1 -0
- package/package.json +1 -1
package/dist/commands/analyze.js
CHANGED
|
@@ -83,9 +83,101 @@ const agents_1 = require("../core/agents");
|
|
|
83
83
|
const code_graph_1 = require("../core/code-graph");
|
|
84
84
|
// v7.2.0+: 结构化代码数据提取
|
|
85
85
|
const structured_extractor_1 = require("../core/structured-extractor");
|
|
86
|
+
/**
|
|
87
|
+
* 按 Markdown 标题合并文档内容
|
|
88
|
+
* v8.3.122+: 新内容中存在的章节替换旧章节,新章节追加,旧文档中未变动的章节保留
|
|
89
|
+
*/
|
|
90
|
+
function mergeDocumentContent(existing, newContent) {
|
|
91
|
+
if (!existing.trim())
|
|
92
|
+
return newContent;
|
|
93
|
+
// 解析文档为标题块
|
|
94
|
+
function parseSections(text) {
|
|
95
|
+
const sections = new Map();
|
|
96
|
+
const lines = text.split('\n');
|
|
97
|
+
let currentHeading = '';
|
|
98
|
+
let currentLevel = 0;
|
|
99
|
+
let currentBody = [];
|
|
100
|
+
let headerLines = []; // 文档开头无标题的内容
|
|
101
|
+
for (const line of lines) {
|
|
102
|
+
const hm = line.match(/^(#{1,3})\s+(.+)/);
|
|
103
|
+
if (hm) {
|
|
104
|
+
if (currentHeading) {
|
|
105
|
+
sections.set(currentHeading, {
|
|
106
|
+
heading: currentHeading,
|
|
107
|
+
level: currentLevel,
|
|
108
|
+
body: currentBody.join('\n'),
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
else if (currentBody.length > 0) {
|
|
112
|
+
headerLines = [...currentBody];
|
|
113
|
+
}
|
|
114
|
+
currentHeading = hm[2].trim();
|
|
115
|
+
currentLevel = hm[1].length;
|
|
116
|
+
currentBody = [line];
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
currentBody.push(line);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
if (currentHeading) {
|
|
123
|
+
sections.set(currentHeading, {
|
|
124
|
+
heading: currentHeading,
|
|
125
|
+
level: currentLevel,
|
|
126
|
+
body: currentBody.join('\n'),
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
// 保存文档头部内容(如 frontmatter、标题等)
|
|
130
|
+
if (headerLines.length > 0) {
|
|
131
|
+
sections.set('__HEADER__', { heading: '__HEADER__', level: 0, body: headerLines.join('\n') });
|
|
132
|
+
}
|
|
133
|
+
return sections;
|
|
134
|
+
}
|
|
135
|
+
const oldSections = parseSections(existing);
|
|
136
|
+
const newSections = parseSections(newContent);
|
|
137
|
+
// 构建合并结果:保留旧文档的头部,按旧文档顺序输出
|
|
138
|
+
// 对于每个旧章节:如果新文档有同名章节,用新内容替换;否则保留旧内容
|
|
139
|
+
// 新文档中有但旧文档中没有的章节,追加到最后
|
|
140
|
+
const result = [];
|
|
141
|
+
// 先输出头部
|
|
142
|
+
const header = oldSections.get('__HEADER__');
|
|
143
|
+
if (header) {
|
|
144
|
+
result.push(header.body);
|
|
145
|
+
oldSections.delete('__HEADER__');
|
|
146
|
+
}
|
|
147
|
+
newSections.delete('__HEADER__');
|
|
148
|
+
// 收集旧文档中存在的章节标题顺序
|
|
149
|
+
const oldOrder = [...oldSections.keys()];
|
|
150
|
+
const addedNew = new Set();
|
|
151
|
+
for (const heading of oldOrder) {
|
|
152
|
+
if (newSections.has(heading)) {
|
|
153
|
+
result.push(newSections.get(heading).body);
|
|
154
|
+
addedNew.add(heading);
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
result.push(oldSections.get(heading).body);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
// 追加新文档中旧文档没有的章节
|
|
161
|
+
for (const [heading, section] of newSections) {
|
|
162
|
+
if (!addedNew.has(heading)) {
|
|
163
|
+
result.push(section.body);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return result.join('\n\n');
|
|
167
|
+
}
|
|
168
|
+
/** 写入文件,支持 --merge 模式(与现有内容按标题合并) */
|
|
169
|
+
async function writeFileMerged(fp, content, merge) {
|
|
170
|
+
if (merge && await (0, fs_extra_1.pathExists)(fp)) {
|
|
171
|
+
const existing = await (0, fs_extra_2.readFile)(fp, 'utf-8');
|
|
172
|
+
content = mergeDocumentContent(existing, content);
|
|
173
|
+
}
|
|
174
|
+
await (0, fs_extra_1.writeFile)(fp, content);
|
|
175
|
+
}
|
|
86
176
|
async function analyzeCommand(options) {
|
|
87
177
|
// v6.97.0+ 修复:全局分析时统一设置 iteration 为 'GLOBAL',避免任何 fallback 到 getDefaultIteration()
|
|
88
178
|
// 这是根治方案:后面所有 "options.iteration || await getDefaultIteration()" 都会命中 options.iteration
|
|
179
|
+
// v8.3.102+: 向上查找项目根目录(支持在子目录执行)
|
|
180
|
+
const _projectRoot = (0, task_utils_1.findProjectRoot)() || process.cwd();
|
|
89
181
|
if (options.scope === 'global' && !options.iteration) {
|
|
90
182
|
options.iteration = 'GLOBAL';
|
|
91
183
|
}
|
|
@@ -321,7 +413,7 @@ async function analyzeCommand(options) {
|
|
|
321
413
|
// 自动刷新知识图谱
|
|
322
414
|
try {
|
|
323
415
|
const { refreshKnowledgeGraph } = await Promise.resolve().then(() => __importStar(require('../core/knowledge-graph')));
|
|
324
|
-
await refreshKnowledgeGraph(
|
|
416
|
+
await refreshKnowledgeGraph(_projectRoot, iter);
|
|
325
417
|
logger_1.logger.info('🧠 知识图谱已刷新');
|
|
326
418
|
}
|
|
327
419
|
catch { }
|
|
@@ -355,7 +447,7 @@ async function analyzeCommand(options) {
|
|
|
355
447
|
// 自动刷新知识图谱
|
|
356
448
|
try {
|
|
357
449
|
const { refreshKnowledgeGraph } = await Promise.resolve().then(() => __importStar(require('../core/knowledge-graph')));
|
|
358
|
-
await refreshKnowledgeGraph(
|
|
450
|
+
await refreshKnowledgeGraph(_projectRoot, iter);
|
|
359
451
|
logger_1.logger.info('🧠 知识图谱已刷新');
|
|
360
452
|
}
|
|
361
453
|
catch { }
|
|
@@ -375,7 +467,7 @@ async function analyzeCommand(options) {
|
|
|
375
467
|
const iterDir = await (0, context_1.getIterationDir)(iter);
|
|
376
468
|
// Step 1: 提取功能单元清单
|
|
377
469
|
let units = [];
|
|
378
|
-
const unitsCachePath = (0, path_1.join)(
|
|
470
|
+
const unitsCachePath = (0, path_1.join)(_projectRoot, exports.UNITS_CACHE_DIR, iter, 'units.json');
|
|
379
471
|
if (await (0, fs_extra_1.pathExists)(unitsCachePath)) {
|
|
380
472
|
try {
|
|
381
473
|
units = JSON.parse(await (0, fs_extra_2.readFile)(unitsCachePath, 'utf-8'));
|
|
@@ -495,7 +587,7 @@ async function analyzeCommand(options) {
|
|
|
495
587
|
// 命令前索引新鲜度检查(非阻塞)
|
|
496
588
|
// v6.97.0+ 修复:全局分析时不检查迭代级索引,避免误导用户
|
|
497
589
|
if (options.scope !== 'global') {
|
|
498
|
-
await (0, index_guard_1.warnIfIndexStale)(
|
|
590
|
+
await (0, index_guard_1.warnIfIndexStale)(_projectRoot, 'analyze', options.iteration);
|
|
499
591
|
}
|
|
500
592
|
// v8.3.0+: 需求澄清检测状态(跨代码块共享)
|
|
501
593
|
let needsClarify = false;
|
|
@@ -658,7 +750,7 @@ async function analyzeCommand(options) {
|
|
|
658
750
|
// 全局分析:预创建 .speccore/GLOBAL/ 目录结构,不写迭代目录
|
|
659
751
|
// v6.81.0+: 需求文档单独放在 requirements/ 下,技术文档放在 platforms/ 下
|
|
660
752
|
// v7.2.0+: 全局技术文档统一放在 overview/ 子目录下,与迭代层 020-specs/overview/ 命名一致
|
|
661
|
-
const globalDir = (0, path_1.join)(
|
|
753
|
+
const globalDir = (0, path_1.join)(_projectRoot, '.speccore', 'GLOBAL');
|
|
662
754
|
await (0, fs_extra_1.ensureDir)(globalDir);
|
|
663
755
|
await (0, fs_extra_1.ensureDir)((0, path_1.join)(globalDir, 'overview'));
|
|
664
756
|
await (0, fs_extra_1.ensureDir)((0, path_1.join)(globalDir, 'platforms'));
|
|
@@ -736,7 +828,7 @@ async function analyzeCommand(options) {
|
|
|
736
828
|
let platformOrder;
|
|
737
829
|
if (!isGlobalScope && iter) {
|
|
738
830
|
// 变更感知:检测 Git 变更影响的端
|
|
739
|
-
affectedPlatforms = await (0, change_detection_1.detectAffectedPlatforms)(
|
|
831
|
+
affectedPlatforms = await (0, change_detection_1.detectAffectedPlatforms)(_projectRoot);
|
|
740
832
|
// 关键路径优先:按任务优先级排序端
|
|
741
833
|
platformOrder = await (0, change_detection_1.detectPlatformPriorityOrder)(iter);
|
|
742
834
|
}
|
|
@@ -750,7 +842,7 @@ async function analyzeCommand(options) {
|
|
|
750
842
|
}
|
|
751
843
|
else {
|
|
752
844
|
// 迭代层:使用 createAnalyzePipeline(支持契约先行 + 逐端推进 + 变更感知 + 关键路径优先 + 需求澄清)
|
|
753
|
-
const result = await (0, pipeline_engine_1.createAnalyzePipeline)(iter,
|
|
845
|
+
const result = await (0, pipeline_engine_1.createAnalyzePipeline)(iter, _projectRoot, {
|
|
754
846
|
affectedPlatforms: affectedPlatforms && affectedPlatforms.length > 0 ? affectedPlatforms : undefined,
|
|
755
847
|
platformOrder: platformOrder && platformOrder.length > 0 ? platformOrder : undefined,
|
|
756
848
|
skipClarify: options.skipClarify,
|
|
@@ -762,10 +854,10 @@ async function analyzeCommand(options) {
|
|
|
762
854
|
initStep = options.skipClarify ? 'phase1-prompt' : 'clarify-prompt';
|
|
763
855
|
}
|
|
764
856
|
// 检查是否有活跃的 Pipeline(恢复模式)
|
|
765
|
-
const hasActive = await pipeline_engine_1.PipelineEngine.hasActivePipeline(
|
|
857
|
+
const hasActive = await pipeline_engine_1.PipelineEngine.hasActivePipeline(_projectRoot, pipelineKey);
|
|
766
858
|
let currentStep;
|
|
767
859
|
if (hasActive) {
|
|
768
|
-
const existingState = await pipeline_engine_1.PipelineEngine.loadExistingState(
|
|
860
|
+
const existingState = await pipeline_engine_1.PipelineEngine.loadExistingState(_projectRoot, pipelineKey);
|
|
769
861
|
currentStep = existingState?.currentStep || initStep;
|
|
770
862
|
logger_1.logger.info(`🔄 恢复 Pipeline: ${currentStep}`);
|
|
771
863
|
}
|
|
@@ -967,7 +1059,7 @@ ${singlePrompt}`);
|
|
|
967
1059
|
const fp = (0, path_1.join)(goldenDir, (0, path_1.basename)(filename));
|
|
968
1060
|
await (0, fs_extra_1.ensureDir)((0, path_1.dirname)(fp));
|
|
969
1061
|
await (0, fs_extra_1.writeFile)(fp, finalContent);
|
|
970
|
-
logger_1.logger.info(` ✅ 澄清文档已写入: ${(0, path_1.relative)(
|
|
1062
|
+
logger_1.logger.info(` ✅ 澄清文档已写入: ${(0, path_1.relative)(_projectRoot, fp)}`);
|
|
971
1063
|
}
|
|
972
1064
|
logger_1.logger.info('');
|
|
973
1065
|
}
|
|
@@ -1039,13 +1131,13 @@ ${singlePrompt}`);
|
|
|
1039
1131
|
}
|
|
1040
1132
|
else if (isGlobalScope) {
|
|
1041
1133
|
// 全局级:写入 .speccore/GLOBAL/(与 platforms/ 同级,非迭代目录)
|
|
1042
|
-
const globalBaseDir = (0, path_1.join)(
|
|
1134
|
+
const globalBaseDir = (0, path_1.join)(_projectRoot, '.speccore', 'GLOBAL');
|
|
1043
1135
|
await (0, fs_extra_1.ensureDir)(globalBaseDir);
|
|
1044
1136
|
const globalSet = new Set(spec_paths_1.GLOBAL_SPEC_FILES);
|
|
1045
1137
|
for (const [filename, content] of Object.entries(docs)) {
|
|
1046
1138
|
// PATTERNS/ 文件特殊处理 → 写入 .speccore/PATTERNS/
|
|
1047
1139
|
if (filename.startsWith('PATTERNS/')) {
|
|
1048
|
-
const patternsDir = (0, path_1.join)(
|
|
1140
|
+
const patternsDir = (0, path_1.join)(_projectRoot, '.speccore', 'PATTERNS');
|
|
1049
1141
|
await (0, fs_extra_1.ensureDir)(patternsDir);
|
|
1050
1142
|
const patternFile = filename.slice('PATTERNS/'.length);
|
|
1051
1143
|
const fp = (0, path_1.join)(patternsDir, patternFile);
|
|
@@ -1186,7 +1278,7 @@ ${singlePrompt}`);
|
|
|
1186
1278
|
}
|
|
1187
1279
|
// v6.72.0+: PATTERNS/ 文件特殊处理 → 写入 .speccore/PATTERNS/
|
|
1188
1280
|
if (filename.startsWith('PATTERNS/')) {
|
|
1189
|
-
const patternsDir = (0, path_1.join)(
|
|
1281
|
+
const patternsDir = (0, path_1.join)(_projectRoot, '.speccore', 'PATTERNS');
|
|
1190
1282
|
await (0, fs_extra_1.ensureDir)(patternsDir);
|
|
1191
1283
|
const patternFile = filename.slice('PATTERNS/'.length);
|
|
1192
1284
|
const fp = (0, path_1.join)(patternsDir, patternFile);
|
|
@@ -1215,7 +1307,7 @@ ${singlePrompt}`);
|
|
|
1215
1307
|
const targetDir = (0, path_1.join)(specDir, spec_paths_1.GLOBAL_SPECS_DIR);
|
|
1216
1308
|
await (0, fs_extra_1.ensureDir)(targetDir);
|
|
1217
1309
|
const fp = (0, path_1.join)(targetDir, parts.slice(1).join('/'));
|
|
1218
|
-
if (!(await (0, task_utils_1.shouldOverwrite)(fp, !!options.interactive))) {
|
|
1310
|
+
if (!options.merge && !(await (0, task_utils_1.shouldOverwrite)(fp, !!options.interactive))) {
|
|
1219
1311
|
logger_1.logger.info(` ⏭️ 跳过: ${filename}`);
|
|
1220
1312
|
continue;
|
|
1221
1313
|
}
|
|
@@ -1224,7 +1316,9 @@ ${singlePrompt}`);
|
|
|
1224
1316
|
backups.push(bk);
|
|
1225
1317
|
logger_1.logger.info(` 📦 ${filename} 旧版已备份: ${(0, path_1.basename)(bk)}`);
|
|
1226
1318
|
}
|
|
1227
|
-
await (
|
|
1319
|
+
await writeFileMerged(fp, content, !!options.merge);
|
|
1320
|
+
if (options.merge)
|
|
1321
|
+
logger_1.logger.info(` 🔀 ${filename} 已合并`);
|
|
1228
1322
|
count++;
|
|
1229
1323
|
continue;
|
|
1230
1324
|
}
|
|
@@ -1235,7 +1329,7 @@ ${singlePrompt}`);
|
|
|
1235
1329
|
const targetDir = (0, path_1.join)(specDir, ...parts.slice(0, -1));
|
|
1236
1330
|
await (0, fs_extra_1.ensureDir)(targetDir);
|
|
1237
1331
|
const fp = (0, path_1.join)(targetDir, parts[parts.length - 1]);
|
|
1238
|
-
if (!(await (0, task_utils_1.shouldOverwrite)(fp, !!options.interactive))) {
|
|
1332
|
+
if (!options.merge && !(await (0, task_utils_1.shouldOverwrite)(fp, !!options.interactive))) {
|
|
1239
1333
|
logger_1.logger.info(` ⏭️ 跳过: ${filename}`);
|
|
1240
1334
|
continue;
|
|
1241
1335
|
}
|
|
@@ -1244,7 +1338,9 @@ ${singlePrompt}`);
|
|
|
1244
1338
|
backups.push(bk);
|
|
1245
1339
|
logger_1.logger.info(` 📦 ${filename} 旧版已备份: ${(0, path_1.basename)(bk)}`);
|
|
1246
1340
|
}
|
|
1247
|
-
await (
|
|
1341
|
+
await writeFileMerged(fp, content, !!options.merge);
|
|
1342
|
+
if (options.merge)
|
|
1343
|
+
logger_1.logger.info(` 🔀 ${filename} 已合并`);
|
|
1248
1344
|
count++;
|
|
1249
1345
|
continue;
|
|
1250
1346
|
}
|
|
@@ -1282,7 +1378,7 @@ ${singlePrompt}`);
|
|
|
1282
1378
|
}
|
|
1283
1379
|
await (0, fs_extra_1.ensureDir)(targetDir);
|
|
1284
1380
|
const fp = (0, path_1.join)(targetDir, cleanFilename);
|
|
1285
|
-
if (!(await (0, task_utils_1.shouldOverwrite)(fp, !!options.interactive))) {
|
|
1381
|
+
if (!options.merge && !(await (0, task_utils_1.shouldOverwrite)(fp, !!options.interactive))) {
|
|
1286
1382
|
logger_1.logger.info(` ⏭️ 跳过: ${filename}`);
|
|
1287
1383
|
continue;
|
|
1288
1384
|
}
|
|
@@ -1291,7 +1387,9 @@ ${singlePrompt}`);
|
|
|
1291
1387
|
backups.push(bk);
|
|
1292
1388
|
logger_1.logger.info(` 📦 ${filename} 旧版已备份: ${(0, path_1.basename)(bk)}`);
|
|
1293
1389
|
}
|
|
1294
|
-
await (
|
|
1390
|
+
await writeFileMerged(fp, content, !!options.merge);
|
|
1391
|
+
if (options.merge)
|
|
1392
|
+
logger_1.logger.info(` 🔀 ${filename} 已合并`);
|
|
1295
1393
|
count++;
|
|
1296
1394
|
}
|
|
1297
1395
|
if (skippedCount > 0) {
|
|
@@ -1356,7 +1454,7 @@ ${singlePrompt}`);
|
|
|
1356
1454
|
await sanitizeSpecDirectories(iterDir);
|
|
1357
1455
|
// v8.3.14+: analyze --apply 后自动清理归档文件和临时文件
|
|
1358
1456
|
await (0, cleanup_1.cleanupByType)({
|
|
1359
|
-
cwd:
|
|
1457
|
+
cwd: _projectRoot,
|
|
1360
1458
|
types: ['archiveFiles', 'archiveDirs', 'tempFiles'],
|
|
1361
1459
|
days: 0, // 不限天数,清理所有归档和临时文件
|
|
1362
1460
|
silent: true,
|
|
@@ -1476,12 +1574,12 @@ ${singlePrompt}`);
|
|
|
1476
1574
|
logger_1.logger.success(`✅ ${count} 个 Spec 文档已写入 ${options.task}${platformLabel}/(任务级,迭代基线不变)`);
|
|
1477
1575
|
}
|
|
1478
1576
|
else if (isGlobalScope) {
|
|
1479
|
-
const globalBaseDir = (0, path_1.join)(
|
|
1577
|
+
const globalBaseDir = (0, path_1.join)(_projectRoot, '.speccore', 'GLOBAL');
|
|
1480
1578
|
await (0, fs_extra_1.ensureDir)(globalBaseDir);
|
|
1481
1579
|
const globalSet = new Set(spec_paths_1.GLOBAL_SPEC_FILES);
|
|
1482
1580
|
for (const [filename, content] of Object.entries(docs)) {
|
|
1483
1581
|
if (filename.startsWith('PATTERNS/')) {
|
|
1484
|
-
const patternsDir = (0, path_1.join)(
|
|
1582
|
+
const patternsDir = (0, path_1.join)(_projectRoot, '.speccore', 'PATTERNS');
|
|
1485
1583
|
await (0, fs_extra_1.ensureDir)(patternsDir);
|
|
1486
1584
|
const patternFile = filename.slice('PATTERNS/'.length);
|
|
1487
1585
|
const fp = (0, path_1.join)(patternsDir, patternFile);
|
|
@@ -1576,7 +1674,7 @@ ${singlePrompt}`);
|
|
|
1576
1674
|
continue;
|
|
1577
1675
|
}
|
|
1578
1676
|
if (filename.startsWith('PATTERNS/')) {
|
|
1579
|
-
const patternsDir = (0, path_1.join)(
|
|
1677
|
+
const patternsDir = (0, path_1.join)(_projectRoot, '.speccore', 'PATTERNS');
|
|
1580
1678
|
await (0, fs_extra_1.ensureDir)(patternsDir);
|
|
1581
1679
|
const patternFile = filename.slice('PATTERNS/'.length);
|
|
1582
1680
|
const fp = (0, path_1.join)(patternsDir, patternFile);
|
|
@@ -1639,7 +1737,7 @@ ${singlePrompt}`);
|
|
|
1639
1737
|
await sanitizeSpecDirectories(iterDir);
|
|
1640
1738
|
// v8.3.14+: analyze --apply 后自动清理归档文件和临时文件
|
|
1641
1739
|
await (0, cleanup_1.cleanupByType)({
|
|
1642
|
-
cwd:
|
|
1740
|
+
cwd: _projectRoot,
|
|
1643
1741
|
types: ['archiveFiles', 'archiveDirs', 'tempFiles'],
|
|
1644
1742
|
days: 0,
|
|
1645
1743
|
silent: true,
|
|
@@ -1648,7 +1746,7 @@ ${singlePrompt}`);
|
|
|
1648
1746
|
// v8.2.0+: 单元分析结果保存到缓存(供 --consolidate 汇总使用)
|
|
1649
1747
|
if (options.unit && options.iteration) {
|
|
1650
1748
|
try {
|
|
1651
|
-
const unitsCachePath = (0, path_1.join)(
|
|
1749
|
+
const unitsCachePath = (0, path_1.join)(_projectRoot, exports.UNITS_CACHE_DIR, options.iteration, 'units.json');
|
|
1652
1750
|
let unitName = options.unit;
|
|
1653
1751
|
if (await (0, fs_extra_1.pathExists)(unitsCachePath)) {
|
|
1654
1752
|
const allUnits = JSON.parse(await (0, fs_extra_2.readFile)(unitsCachePath, 'utf-8'));
|
|
@@ -1702,7 +1800,7 @@ ${singlePrompt}`);
|
|
|
1702
1800
|
}
|
|
1703
1801
|
else if (isGlobalScope) {
|
|
1704
1802
|
// 全局级:写 .speccore/GLOBAL/ANALYSIS.md
|
|
1705
|
-
const globalDir = (0, path_1.join)(
|
|
1803
|
+
const globalDir = (0, path_1.join)(_projectRoot, '.speccore', 'GLOBAL');
|
|
1706
1804
|
await (0, fs_extra_1.ensureDir)(globalDir);
|
|
1707
1805
|
const globalAnalysisPath = (0, path_1.join)(globalDir, 'ANALYSIS.md');
|
|
1708
1806
|
if (await (0, task_utils_1.shouldOverwrite)(globalAnalysisPath, !!options.interactive)) {
|
|
@@ -1762,7 +1860,7 @@ ${singlePrompt}`);
|
|
|
1762
1860
|
if (options.iteration) {
|
|
1763
1861
|
try {
|
|
1764
1862
|
const { refreshKnowledgeGraph } = await Promise.resolve().then(() => __importStar(require('../core/knowledge-graph')));
|
|
1765
|
-
await refreshKnowledgeGraph(
|
|
1863
|
+
await refreshKnowledgeGraph(_projectRoot, options.iteration);
|
|
1766
1864
|
logger_1.logger.info('🧠 知识图谱已刷新');
|
|
1767
1865
|
}
|
|
1768
1866
|
catch { }
|
|
@@ -1777,7 +1875,7 @@ ${singlePrompt}`);
|
|
|
1777
1875
|
if (await (0, fs_extra_1.pathExists)(reqDir))
|
|
1778
1876
|
dirs.push(reqDir);
|
|
1779
1877
|
if (dirs.length > 0) {
|
|
1780
|
-
await indexDirectoryDocuments(
|
|
1878
|
+
await indexDirectoryDocuments(_projectRoot, dirs, `${options.iteration}_iteration_all`, `rag-index-${options.iteration}.json`);
|
|
1781
1879
|
logger_1.logger.info('🔍 迭代 RAG 索引已刷新');
|
|
1782
1880
|
}
|
|
1783
1881
|
}
|
|
@@ -1790,7 +1888,7 @@ ${singlePrompt}`);
|
|
|
1790
1888
|
const pipelineKey = isGlobalScope ? 'GLOBAL' : options.iteration;
|
|
1791
1889
|
if (!pipelineKey)
|
|
1792
1890
|
return;
|
|
1793
|
-
const hasPipeline = await pipeline_engine_1.PipelineEngine.hasActivePipeline(
|
|
1891
|
+
const hasPipeline = await pipeline_engine_1.PipelineEngine.hasActivePipeline(_projectRoot, pipelineKey);
|
|
1794
1892
|
if (hasPipeline) {
|
|
1795
1893
|
const { createAnalyzePipeline, createGlobalAnalyzePipeline } = await Promise.resolve().then(() => __importStar(require('../core/pipeline-engine')));
|
|
1796
1894
|
// v6.69.0+: 推进时保持与初始化时相同的过滤和排序条件
|
|
@@ -1800,9 +1898,9 @@ ${singlePrompt}`);
|
|
|
1800
1898
|
}
|
|
1801
1899
|
else {
|
|
1802
1900
|
const { detectAffectedPlatforms, detectPlatformPriorityOrder } = await Promise.resolve().then(() => __importStar(require('../core/change-detection')));
|
|
1803
|
-
const affectedPlatforms = await detectAffectedPlatforms(
|
|
1901
|
+
const affectedPlatforms = await detectAffectedPlatforms(_projectRoot);
|
|
1804
1902
|
const platformOrder = await detectPlatformPriorityOrder(options.iteration);
|
|
1805
|
-
engine = (await createAnalyzePipeline(options.iteration,
|
|
1903
|
+
engine = (await createAnalyzePipeline(options.iteration, _projectRoot, {
|
|
1806
1904
|
affectedPlatforms: affectedPlatforms.length > 0 ? affectedPlatforms : undefined,
|
|
1807
1905
|
platformOrder: platformOrder.length > 0 ? platformOrder : undefined,
|
|
1808
1906
|
})).engine;
|
|
@@ -2444,7 +2542,7 @@ async function injectGraphSummary(prompt) {
|
|
|
2444
2542
|
if (_graphSummaryCache && Date.now() - _graphSummaryCacheTime < GRAPH_SUMMARY_TTL) {
|
|
2445
2543
|
return prompt + _graphSummaryCache;
|
|
2446
2544
|
}
|
|
2447
|
-
const cg = await (0, code_graph_1.loadCodeGraph)(process.cwd());
|
|
2545
|
+
const cg = await (0, code_graph_1.loadCodeGraph)(((0, task_utils_1.findProjectRoot)() || process.cwd()));
|
|
2448
2546
|
if (!cg)
|
|
2449
2547
|
return prompt;
|
|
2450
2548
|
const lines = [];
|
|
@@ -2495,7 +2593,7 @@ async function injectGraphSummary(prompt) {
|
|
|
2495
2593
|
// ── v7.2.0+: 检测全局分析当前进度 ──
|
|
2496
2594
|
// Layer 4 拆分为子层: 4a=产品文档, 4b=全局技术核心, 4c=全局技术扩展, 4d=各端技术
|
|
2497
2595
|
async function detectGlobalLayerProgress() {
|
|
2498
|
-
const globalDir = (0, path_1.join)(process.cwd(), '.speccore', 'GLOBAL');
|
|
2596
|
+
const globalDir = (0, path_1.join)(((0, task_utils_1.findProjectRoot)() || process.cwd()), '.speccore', 'GLOBAL');
|
|
2499
2597
|
let completedLayer = 0;
|
|
2500
2598
|
const missing = [];
|
|
2501
2599
|
// Layer 1: 检查各端 _INDEX.md
|
|
@@ -2695,7 +2793,7 @@ async function buildGlobalAnalysisGuide(options) {
|
|
|
2695
2793
|
// 下一步命令
|
|
2696
2794
|
guide += '\n➡️ 下一步:\n';
|
|
2697
2795
|
if (deepDoc && options?.iterative) {
|
|
2698
|
-
const outlinePath = (0, path_1.join)(process.cwd(), '.speccore', 'cache', `deep-outline-${deepDoc.replace(/\//g, '-')}.md`);
|
|
2796
|
+
const outlinePath = (0, path_1.join)(((0, task_utils_1.findProjectRoot)() || process.cwd()), '.speccore', 'cache', `deep-outline-${deepDoc.replace(/\//g, '-')}.md`);
|
|
2699
2797
|
const hasOutline = await (0, fs_extra_1.pathExists)(outlinePath);
|
|
2700
2798
|
if (!hasOutline) {
|
|
2701
2799
|
guide += ` 1. 将 AI 输出的大纲保存到: ${outlinePath}\n`;
|
|
@@ -2858,11 +2956,13 @@ async function buildMultiDocPrompt(command, ctx, options, clarifyCtx) {
|
|
|
2858
2956
|
const targetLayer = options?.layer || progress.nextLayer;
|
|
2859
2957
|
const isLayered = !!options?.layer || progress.completedLayer < 4;
|
|
2860
2958
|
// v7.2.0+: 结构化代码数据提取 — Layer 1 之前自动执行
|
|
2959
|
+
// v8.3.125+: 按迭代涉及的端筛选源码路径(避免全量扫描无关端)
|
|
2861
2960
|
let structuredDataHint = '';
|
|
2862
2961
|
if (targetLayer === 1 && ctx.withCode) {
|
|
2863
2962
|
try {
|
|
2963
|
+
const projectRoot = (0, task_utils_1.findProjectRoot)() || process.cwd();
|
|
2864
2964
|
// 读取 CONSTITUTION.md 获取源码路径
|
|
2865
|
-
const constitutionPath = (0, path_1.join)(
|
|
2965
|
+
const constitutionPath = (0, path_1.join)(projectRoot, '.speccore', 'CONSTITUTION.md');
|
|
2866
2966
|
let sourcePaths = ['src'];
|
|
2867
2967
|
if (await (0, fs_extra_1.pathExists)(constitutionPath)) {
|
|
2868
2968
|
const content = await (0, fs_extra_2.readFile)(constitutionPath, 'utf-8');
|
|
@@ -2871,7 +2971,54 @@ async function buildMultiDocPrompt(command, ctx, options, clarifyCtx) {
|
|
|
2871
2971
|
sourcePaths = match.map(m => m.replace(/.*-\s*`?/, '').replace(/`?$/, '').trim()).filter(Boolean);
|
|
2872
2972
|
}
|
|
2873
2973
|
}
|
|
2874
|
-
|
|
2974
|
+
// v8.3.125+: 按迭代端名筛选源码路径(宁可多扫不要漏扫)
|
|
2975
|
+
const iterDir = ctx.iteration ? await (0, context_1.getIterationDir)(ctx.iteration) : null;
|
|
2976
|
+
let filteredPaths = sourcePaths;
|
|
2977
|
+
if (iterDir && sourcePaths.length > 1) {
|
|
2978
|
+
try {
|
|
2979
|
+
// 1. 尝试从 PLATFORMS.md 读取端列表
|
|
2980
|
+
let platformNames = [];
|
|
2981
|
+
const platformsPath = (0, path_1.join)(iterDir, '020-specs', 'PLATFORMS.md');
|
|
2982
|
+
if (await (0, fs_extra_1.pathExists)(platformsPath)) {
|
|
2983
|
+
const pContent = await (0, fs_extra_2.readFile)(platformsPath, 'utf-8');
|
|
2984
|
+
const pMatch = pContent.match(/-\s*(\w+)/g);
|
|
2985
|
+
if (pMatch)
|
|
2986
|
+
platformNames = pMatch.map(m => m.replace(/^-\s*/, '').trim().toLowerCase());
|
|
2987
|
+
}
|
|
2988
|
+
// 2. fallback:从 020-specs/ 子目录推断端名
|
|
2989
|
+
if (platformNames.length === 0) {
|
|
2990
|
+
const specsDir = (0, path_1.join)(iterDir, '020-specs');
|
|
2991
|
+
if (await (0, fs_extra_1.pathExists)(specsDir)) {
|
|
2992
|
+
const entries = await (0, fs_extra_2.readdir)(specsDir, { withFileTypes: true });
|
|
2993
|
+
platformNames = entries
|
|
2994
|
+
.filter(e => e.isDirectory() && e.name !== 'overview' && e.name !== 'requirements')
|
|
2995
|
+
.map(e => e.name.toLowerCase());
|
|
2996
|
+
}
|
|
2997
|
+
}
|
|
2998
|
+
// 3. 筛选 sourcePaths(路径包含端名则保留)
|
|
2999
|
+
if (platformNames.length > 0) {
|
|
3000
|
+
const matched = sourcePaths.filter(sp => {
|
|
3001
|
+
const lower = sp.toLowerCase();
|
|
3002
|
+
return platformNames.some(pn => lower.includes(pn));
|
|
3003
|
+
});
|
|
3004
|
+
// 同时保留 shared/common/lib 等公共路径
|
|
3005
|
+
const sharedPaths = sourcePaths.filter(sp => /shared|common|lib|utils|pkg/.test(sp.toLowerCase()));
|
|
3006
|
+
const combined = [...new Set([...matched, ...sharedPaths])];
|
|
3007
|
+
// 宁可多扫不要漏扫:如果筛选后为空,回退全量
|
|
3008
|
+
if (combined.length > 0) {
|
|
3009
|
+
filteredPaths = combined;
|
|
3010
|
+
logger_1.logger.info(` 📂 结构化数据扫描已按端筛选: ${filteredPaths.join(', ')}(涉及端: ${platformNames.join(', ')})`);
|
|
3011
|
+
}
|
|
3012
|
+
else {
|
|
3013
|
+
logger_1.logger.info(` 📂 无法按端筛选,回退全量扫描: ${sourcePaths.join(', ')}`);
|
|
3014
|
+
}
|
|
3015
|
+
}
|
|
3016
|
+
}
|
|
3017
|
+
catch {
|
|
3018
|
+
// 筛选失败不阻断,回退全量
|
|
3019
|
+
}
|
|
3020
|
+
}
|
|
3021
|
+
await (0, structured_extractor_1.extractStructuredData)(projectRoot, filteredPaths);
|
|
2875
3022
|
structuredDataHint = '\n> 📊 **结构化数据**: 已提取到 `.speccore/cache/structured-data.json`,包含 API/Entity/Route/Component 清单\n';
|
|
2876
3023
|
}
|
|
2877
3024
|
catch (e) {
|
|
@@ -2909,7 +3056,7 @@ async function buildMultiDocPrompt(command, ctx, options, clarifyCtx) {
|
|
|
2909
3056
|
prompt += `- **核心任务**: ${layerMeta.focus}\n`;
|
|
2910
3057
|
prompt += `- **预期产出**: ${layerMeta.output}\n\n`;
|
|
2911
3058
|
prompt += `## 要求\n`;
|
|
2912
|
-
prompt += `1. **先读宪法**: Read .speccore/CONSTITUTION.md
|
|
3059
|
+
prompt += `1. **先读宪法**: Read .speccore/CONSTITUTION.md,获取工程标识、源码路径、端列表。\n`;
|
|
2913
3060
|
prompt += `2. Read .speccore/GLOBAL/ 下已有文档(特别是前一层的产物)作为输入。\n`;
|
|
2914
3061
|
prompt += `3. **禁止行为**: 不要打开浏览器、不要模拟用户操作、不要访问 URL。所有分析基于直接 Read 源码。\n`;
|
|
2915
3062
|
if (ctx.withCode) {
|
|
@@ -3044,7 +3191,7 @@ async function buildMultiDocPrompt(command, ctx, options, clarifyCtx) {
|
|
|
3044
3191
|
prompt += `**每个功能模块涉及哪些端,就读取那些端的详细源码**:\n\n`;
|
|
3045
3192
|
// v7.4.5+: CLI 预提取每个功能模块的代码上下文
|
|
3046
3193
|
try {
|
|
3047
|
-
const modulesCtx = await buildLayer3ModuleContext(process.cwd());
|
|
3194
|
+
const modulesCtx = await buildLayer3ModuleContext(((0, task_utils_1.findProjectRoot)() || process.cwd()));
|
|
3048
3195
|
if (modulesCtx) {
|
|
3049
3196
|
prompt += `## 📎 CLI 预提取的功能模块代码上下文(v7.4.5+)\n\n`;
|
|
3050
3197
|
prompt += `> 以下内容由 CLI 从 structured-data.json 和 _MODULES.md 自动提取,你不需要自己搜索。\n`;
|
|
@@ -3468,7 +3615,7 @@ async function buildMultiDocPrompt(command, ctx, options, clarifyCtx) {
|
|
|
3468
3615
|
const subLayerTarget = progress.subLayer?.next || '4a';
|
|
3469
3616
|
if (deepDoc) {
|
|
3470
3617
|
// --deep 模式:单文档深度分析
|
|
3471
|
-
const outlinePath = (0, path_1.join)(process.cwd(), '.speccore', 'cache', `deep-outline-${deepDoc.replace(/\//g, '-')}.md`);
|
|
3618
|
+
const outlinePath = (0, path_1.join)(((0, task_utils_1.findProjectRoot)() || process.cwd()), '.speccore', 'cache', `deep-outline-${deepDoc.replace(/\//g, '-')}.md`);
|
|
3472
3619
|
const hasOutline = await (0, fs_extra_1.pathExists)(outlinePath);
|
|
3473
3620
|
if (options?.iterative && !hasOutline) {
|
|
3474
3621
|
// 迭代模式第一步:输出大纲
|
|
@@ -3534,7 +3681,7 @@ async function buildMultiDocPrompt(command, ctx, options, clarifyCtx) {
|
|
|
3534
3681
|
}
|
|
3535
3682
|
else {
|
|
3536
3683
|
// v7.5.0+: 子层内逐文档生成 — 检测当前子层中哪些文档已存在,每次只生成下一个缺失的
|
|
3537
|
-
const globalDir = (0, path_1.join)(process.cwd(), '.speccore', 'GLOBAL');
|
|
3684
|
+
const globalDir = (0, path_1.join)(((0, task_utils_1.findProjectRoot)() || process.cwd()), '.speccore', 'GLOBAL');
|
|
3538
3685
|
const overviewDir = (0, path_1.join)(globalDir, 'overview');
|
|
3539
3686
|
const requirementsDir = (0, path_1.join)(globalDir, 'requirements');
|
|
3540
3687
|
const platformsDir = (0, path_1.join)(globalDir, 'platforms');
|
|
@@ -3588,7 +3735,7 @@ async function buildMultiDocPrompt(command, ctx, options, clarifyCtx) {
|
|
|
3588
3735
|
const existingDocs = [];
|
|
3589
3736
|
const missingDocs = [];
|
|
3590
3737
|
for (const doc of expectedDocs) {
|
|
3591
|
-
const fullPath = (0, path_1.join)(process.cwd(), '.speccore', 'GLOBAL', doc);
|
|
3738
|
+
const fullPath = (0, path_1.join)(((0, task_utils_1.findProjectRoot)() || process.cwd()), '.speccore', 'GLOBAL', doc);
|
|
3592
3739
|
if (await (0, fs_extra_1.pathExists)(fullPath)) {
|
|
3593
3740
|
existingDocs.push(doc);
|
|
3594
3741
|
}
|
|
@@ -4309,9 +4456,9 @@ status: "clarified"
|
|
|
4309
4456
|
if (!isGlobal && ctx.withCode && ctx.iteration && ctx.iteration !== 'GLOBAL') {
|
|
4310
4457
|
try {
|
|
4311
4458
|
// 1. 提取结构化数据(如果还没有)
|
|
4312
|
-
const structuredDataPath = (0, path_1.join)(process.cwd(), '.speccore', 'cache', 'structured-data.json');
|
|
4459
|
+
const structuredDataPath = (0, path_1.join)(((0, task_utils_1.findProjectRoot)() || process.cwd()), '.speccore', 'cache', 'structured-data.json');
|
|
4313
4460
|
if (!(await (0, fs_extra_1.pathExists)(structuredDataPath))) {
|
|
4314
|
-
const constitutionPath = (0, path_1.join)(process.cwd(), '.speccore', 'CONSTITUTION.md');
|
|
4461
|
+
const constitutionPath = (0, path_1.join)(((0, task_utils_1.findProjectRoot)() || process.cwd()), '.speccore', 'CONSTITUTION.md');
|
|
4315
4462
|
let sourcePaths = ['src'];
|
|
4316
4463
|
if (await (0, fs_extra_1.pathExists)(constitutionPath)) {
|
|
4317
4464
|
const content = await (0, fs_extra_2.readFile)(constitutionPath, 'utf-8');
|
|
@@ -4321,7 +4468,7 @@ status: "clarified"
|
|
|
4321
4468
|
}
|
|
4322
4469
|
}
|
|
4323
4470
|
const { extractStructuredData } = await Promise.resolve().then(() => __importStar(require('../core/structured-extractor')));
|
|
4324
|
-
await extractStructuredData(process.cwd(), sourcePaths);
|
|
4471
|
+
await extractStructuredData(((0, task_utils_1.findProjectRoot)() || process.cwd()), sourcePaths);
|
|
4325
4472
|
}
|
|
4326
4473
|
// 2. 注入结构化数据摘要到 Prompt
|
|
4327
4474
|
if (await (0, fs_extra_1.pathExists)(structuredDataPath)) {
|
|
@@ -4342,7 +4489,7 @@ status: "clarified"
|
|
|
4342
4489
|
if (options?.featureName) {
|
|
4343
4490
|
const { buildFeatureContext, buildFeatureContextPrompt } = await Promise.resolve().then(() => __importStar(require('../core/semantic-locator')));
|
|
4344
4491
|
const iterDir = await (0, context_1.getIterationDir)(ctx.iteration);
|
|
4345
|
-
const featureCtx = await buildFeatureContext(process.cwd(), iterDir, options.featureName, options.docName);
|
|
4492
|
+
const featureCtx = await buildFeatureContext(((0, task_utils_1.findProjectRoot)() || process.cwd()), iterDir, options.featureName, options.docName);
|
|
4346
4493
|
prompt += buildFeatureContextPrompt(featureCtx);
|
|
4347
4494
|
}
|
|
4348
4495
|
}
|
|
@@ -4663,7 +4810,7 @@ status: "clarified"
|
|
|
4663
4810
|
prompt += `8. **端发现(重要)**:先确定项目有哪些端,再按端组织文档\n`;
|
|
4664
4811
|
prompt += ` - 第 1 步:Read .speccore/CONSTITUTION.md\n`;
|
|
4665
4812
|
prompt += ` - 第 2 步:从「## 端列表」章节提取端名(这是全局权威来源)\n`;
|
|
4666
|
-
prompt += ` - 第 3
|
|
4813
|
+
prompt += ` - 第 3 步:如果没有「端列表」章节,从「对应需求端」列提取\n`;
|
|
4667
4814
|
prompt += ` - 第 4 步:如果以上都无法确定,根据需求文档内容判断\n`;
|
|
4668
4815
|
prompt += ` - 第 5 步:将发现的端列表写入 020-specs/PLATFORMS.md\n`;
|
|
4669
4816
|
// v6.70.0+: REQUIREMENT.md 以产品视角撰写(不按端分章节)
|
|
@@ -4908,7 +5055,7 @@ status: "clarified"
|
|
|
4908
5055
|
// ── v6.52.0+: 图谱 RAG 上下文注入(analyze 阶段也检索项目关联内容)──
|
|
4909
5056
|
if (!isTask && ctx.phase !== '2') {
|
|
4910
5057
|
try {
|
|
4911
|
-
const ragResult = await (0, unified_retrieval_1.unifiedSearch)(process.cwd(), {
|
|
5058
|
+
const ragResult = await (0, unified_retrieval_1.unifiedSearch)(((0, task_utils_1.findProjectRoot)() || process.cwd()), {
|
|
4912
5059
|
query: ctx.iteration || '',
|
|
4913
5060
|
iteration: ctx.iteration,
|
|
4914
5061
|
platform: ctx.platform,
|
|
@@ -5169,26 +5316,7 @@ async function getSubtaskDirs(taskDir) {
|
|
|
5169
5316
|
}
|
|
5170
5317
|
catch { /* ignore */ }
|
|
5171
5318
|
}
|
|
5172
|
-
//
|
|
5173
|
-
if (result.length === 0) {
|
|
5174
|
-
for (const catDir of ['10-backend', '20-frontend']) {
|
|
5175
|
-
const catPath = (0, path_1.join)(taskDir, catDir);
|
|
5176
|
-
if (!(await (0, fs_extra_1.pathExists)(catPath)))
|
|
5177
|
-
continue;
|
|
5178
|
-
try {
|
|
5179
|
-
for (const svc of await (0, fs_extra_2.readdir)(catPath, { withFileTypes: true })) {
|
|
5180
|
-
if (!svc.isDirectory())
|
|
5181
|
-
continue;
|
|
5182
|
-
for (const sub of await (0, fs_extra_2.readdir)((0, path_1.join)(catPath, svc.name), { withFileTypes: true })) {
|
|
5183
|
-
if (sub.isDirectory() && !sub.name.startsWith('.')) {
|
|
5184
|
-
result.push((0, path_1.join)(catPath, svc.name, sub.name));
|
|
5185
|
-
}
|
|
5186
|
-
}
|
|
5187
|
-
}
|
|
5188
|
-
}
|
|
5189
|
-
catch { /* ignore */ }
|
|
5190
|
-
}
|
|
5191
|
-
}
|
|
5319
|
+
// v8.3.121+: 已移除 10-backend/20-frontend 旧结构回退
|
|
5192
5320
|
return result;
|
|
5193
5321
|
}
|
|
5194
5322
|
// ═══════════════════════════════════════════════════════════
|
|
@@ -5245,7 +5373,7 @@ async function buildClarifyPhasePrompt(iteration) {
|
|
|
5245
5373
|
const hasMultipleUnits = extractedUnits.length >= 2;
|
|
5246
5374
|
// v6.84.0+: 从 AGENTS 规范数据库动态加载角色
|
|
5247
5375
|
let prompt = `\n# 任务: 需求专业化(Phase 0: 需求澄清,v6.84.0+)\n\n`;
|
|
5248
|
-
const projectRoot = (0, task_utils_1.findProjectRoot)() || process.cwd();
|
|
5376
|
+
const projectRoot = (0, task_utils_1.findProjectRoot)() || ((0, task_utils_1.findProjectRoot)() || process.cwd());
|
|
5249
5377
|
const agentContext = {
|
|
5250
5378
|
iteration,
|
|
5251
5379
|
iterationDir: iterDir,
|
|
@@ -5578,14 +5706,14 @@ async function extractIterationUnits(iterDir) {
|
|
|
5578
5706
|
}
|
|
5579
5707
|
/** 保存单元分析结果到缓存 */
|
|
5580
5708
|
async function saveUnitAnalysis(iteration, analysis) {
|
|
5581
|
-
const cacheDir = (0, path_1.join)(process.cwd(), exports.UNITS_CACHE_DIR, iteration);
|
|
5709
|
+
const cacheDir = (0, path_1.join)(((0, task_utils_1.findProjectRoot)() || process.cwd()), exports.UNITS_CACHE_DIR, iteration);
|
|
5582
5710
|
await (0, fs_extra_1.ensureDir)(cacheDir);
|
|
5583
5711
|
const fp = (0, path_1.join)(cacheDir, `${analysis.id}.json`);
|
|
5584
5712
|
await (0, fs_extra_1.writeFile)(fp, JSON.stringify(analysis, null, 2));
|
|
5585
5713
|
}
|
|
5586
5714
|
/** 读取已完成的单元分析 */
|
|
5587
5715
|
async function loadUnitAnalyses(iteration) {
|
|
5588
|
-
const cacheDir = (0, path_1.join)(process.cwd(), exports.UNITS_CACHE_DIR, iteration);
|
|
5716
|
+
const cacheDir = (0, path_1.join)(((0, task_utils_1.findProjectRoot)() || process.cwd()), exports.UNITS_CACHE_DIR, iteration);
|
|
5589
5717
|
if (!await (0, fs_extra_1.pathExists)(cacheDir))
|
|
5590
5718
|
return [];
|
|
5591
5719
|
const files = (await (0, fs_extra_2.readdir)(cacheDir)).filter(f => f.endsWith('.json'));
|