speccore 7.1.0 → 7.2.1
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/.agents/skills/spec-ask/SKILL.md +10 -5
- package/README.md +24 -0
- package/dist/cli.js +4 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/analyze.d.ts +6 -0
- package/dist/commands/analyze.d.ts.map +1 -1
- package/dist/commands/analyze.js +607 -56
- package/dist/commands/analyze.js.map +1 -1
- package/dist/commands/ask.d.ts +1 -1
- package/dist/commands/ask.d.ts.map +1 -1
- package/dist/commands/ask.js +2 -2
- package/dist/commands/ask.js.map +1 -1
- package/dist/commands/config.js +2 -2
- package/dist/commands/config.js.map +1 -1
- package/dist/commands/dev.js +30 -8
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/init.js +16 -276
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/iteration-from-global.js +2 -2
- package/dist/commands/prompts.d.ts +7 -0
- package/dist/commands/prompts.d.ts.map +1 -1
- package/dist/commands/prompts.js +650 -52
- package/dist/commands/prompts.js.map +1 -1
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +149 -0
- package/dist/commands/status.js.map +1 -1
- package/dist/core/ask-engine.d.ts.map +1 -1
- package/dist/core/ask-engine.js +58 -3
- package/dist/core/ask-engine.js.map +1 -1
- package/dist/core/change-impact-global.d.ts +20 -0
- package/dist/core/change-impact-global.d.ts.map +1 -0
- package/dist/core/change-impact-global.js +130 -0
- package/dist/core/change-impact-global.js.map +1 -0
- package/dist/core/dev-llm.d.ts +10 -0
- package/dist/core/dev-llm.d.ts.map +1 -1
- package/dist/core/dev-llm.js.map +1 -1
- package/dist/core/doc-cross-reference.d.ts +10 -0
- package/dist/core/doc-cross-reference.d.ts.map +1 -0
- package/dist/core/doc-cross-reference.js +108 -0
- package/dist/core/doc-cross-reference.js.map +1 -0
- package/dist/core/doc-quality-gate.d.ts +28 -0
- package/dist/core/doc-quality-gate.d.ts.map +1 -0
- package/dist/core/doc-quality-gate.js +209 -0
- package/dist/core/doc-quality-gate.js.map +1 -0
- package/dist/core/incremental-analyzer.js +1 -1
- package/dist/core/incremental-analyzer.js.map +1 -1
- package/dist/core/intent-recognition.d.ts.map +1 -1
- package/dist/core/intent-recognition.js +61 -2
- package/dist/core/intent-recognition.js.map +1 -1
- package/dist/core/iteration-cache.d.ts +48 -0
- package/dist/core/iteration-cache.d.ts.map +1 -0
- package/dist/core/iteration-cache.js +148 -0
- package/dist/core/iteration-cache.js.map +1 -0
- package/dist/core/platform-addition.js +3 -3
- package/dist/core/platform-addition.js.map +1 -1
- package/dist/core/semantic-locator.d.ts +42 -0
- package/dist/core/semantic-locator.d.ts.map +1 -0
- package/dist/core/semantic-locator.js +290 -0
- package/dist/core/semantic-locator.js.map +1 -0
- package/dist/core/structured-extractor.d.ts +89 -0
- package/dist/core/structured-extractor.d.ts.map +1 -0
- package/dist/core/structured-extractor.js +428 -0
- package/dist/core/structured-extractor.js.map +1 -0
- package/package.json +1 -1
package/dist/commands/analyze.js
CHANGED
|
@@ -70,6 +70,8 @@ const requirement_clarifier_1 = require("../core/requirement-clarifier");
|
|
|
70
70
|
const agents_1 = require("../core/agents");
|
|
71
71
|
// v6.91.0+: 代码知识图谱摘要注入
|
|
72
72
|
const code_graph_1 = require("../core/code-graph");
|
|
73
|
+
// v7.2.0+: 结构化代码数据提取
|
|
74
|
+
const structured_extractor_1 = require("../core/structured-extractor");
|
|
73
75
|
async function analyzeCommand(options) {
|
|
74
76
|
// v6.97.0+ 修复:全局分析时统一设置 iteration 为 'GLOBAL',避免任何 fallback 到 getDefaultIteration()
|
|
75
77
|
// 这是根治方案:后面所有 "options.iteration || await getDefaultIteration()" 都会命中 options.iteration
|
|
@@ -506,17 +508,17 @@ async function analyzeCommand(options) {
|
|
|
506
508
|
if (options.scope === 'global') {
|
|
507
509
|
// 全局分析:预创建 .speccore/GLOBAL/ 目录结构,不写迭代目录
|
|
508
510
|
// v6.81.0+: 需求文档单独放在 requirements/ 下,技术文档放在 platforms/ 下
|
|
509
|
-
//
|
|
511
|
+
// v7.2.0+: 全局技术文档统一放在 overview/ 子目录下,与迭代层 020-specs/overview/ 命名一致
|
|
510
512
|
const globalDir = (0, path_1.join)(process.cwd(), '.speccore', 'GLOBAL');
|
|
511
513
|
await (0, fs_extra_1.ensureDir)(globalDir);
|
|
512
|
-
await (0, fs_extra_1.ensureDir)((0, path_1.join)(globalDir, '
|
|
514
|
+
await (0, fs_extra_1.ensureDir)((0, path_1.join)(globalDir, 'overview'));
|
|
513
515
|
await (0, fs_extra_1.ensureDir)((0, path_1.join)(globalDir, 'platforms'));
|
|
514
516
|
await (0, fs_extra_1.ensureDir)((0, path_1.join)(globalDir, 'requirements'));
|
|
515
517
|
await (0, fs_extra_1.ensureDir)((0, path_1.join)(globalDir, 'requirements', 'images'));
|
|
516
518
|
await (0, fs_extra_1.ensureDir)((0, path_1.join)(globalDir, 'requirements', 'prototypes'));
|
|
517
519
|
// v7.0.0+: 图表可视化目录
|
|
518
520
|
await (0, fs_extra_1.ensureDir)((0, path_1.join)(globalDir, 'diagrams'));
|
|
519
|
-
logger_1.logger.info(`📁 已预创建 .speccore/GLOBAL/ 目录结构(
|
|
521
|
+
logger_1.logger.info(`📁 已预创建 .speccore/GLOBAL/ 目录结构(overview/ + platforms/ + requirements/ + diagrams/)`);
|
|
520
522
|
}
|
|
521
523
|
else {
|
|
522
524
|
const iterForDirs = options.iteration || await (0, context_1.getDefaultIteration)();
|
|
@@ -805,13 +807,13 @@ async function analyzeCommand(options) {
|
|
|
805
807
|
targetFilename = filename;
|
|
806
808
|
}
|
|
807
809
|
else if (globalSet.has(filename)) {
|
|
808
|
-
//
|
|
809
|
-
targetDir = (0, path_1.join)(globalBaseDir, '
|
|
810
|
+
// v7.2.0+: 全局技术文档统一放入 overview/ 子目录,与迭代层命名一致
|
|
811
|
+
targetDir = (0, path_1.join)(globalBaseDir, 'overview');
|
|
810
812
|
targetFilename = filename;
|
|
811
813
|
}
|
|
812
814
|
else {
|
|
813
|
-
//
|
|
814
|
-
targetDir = (0, path_1.join)(globalBaseDir, '
|
|
815
|
+
// v7.2.0+: 未知文件也归入 overview/ 子目录
|
|
816
|
+
targetDir = (0, path_1.join)(globalBaseDir, 'overview');
|
|
815
817
|
targetFilename = filename;
|
|
816
818
|
}
|
|
817
819
|
await (0, fs_extra_1.ensureDir)(targetDir);
|
|
@@ -829,6 +831,13 @@ async function analyzeCommand(options) {
|
|
|
829
831
|
count++;
|
|
830
832
|
}
|
|
831
833
|
logger_1.logger.success(`✅ ${count} 个全局文档已写入 .speccore/GLOBAL/`);
|
|
834
|
+
// v7.2.0+: 全局文档质量门禁
|
|
835
|
+
const { runGlobalQualityGate, printQualityReport } = await Promise.resolve().then(() => __importStar(require('../core/doc-quality-gate')));
|
|
836
|
+
const reports = await runGlobalQualityGate();
|
|
837
|
+
printQualityReport(reports);
|
|
838
|
+
// v7.2.0+: 自动生成文档间交叉引用
|
|
839
|
+
const { generateCrossReferences } = await Promise.resolve().then(() => __importStar(require('../core/doc-cross-reference')));
|
|
840
|
+
await generateCrossReferences();
|
|
832
841
|
}
|
|
833
842
|
else {
|
|
834
843
|
// 迭代级:写 020-specs/(综合文档写入 global/ 子目录,v6.41.0+)
|
|
@@ -903,6 +912,8 @@ async function analyzeCommand(options) {
|
|
|
903
912
|
logger_1.logger.warn(`⚠️ 共跳过 ${skippedCount} 个非法目录的文档,请检查 AI 输出是否包含非端名目录`);
|
|
904
913
|
}
|
|
905
914
|
logger_1.logger.success(`✅ ${count} 个 Spec 文档已写入 020-specs/`);
|
|
915
|
+
// v6.90.0+: 事后校验——检测并清理 AI 绕过 --apply 创建的非法目录/文件
|
|
916
|
+
await sanitizeSpecDirectories(iterDir);
|
|
906
917
|
// v6.74.0+: 流式分析自动检查(回退检测 + 最终核对)
|
|
907
918
|
if (options.streamingPhase) {
|
|
908
919
|
const phase = options.streamingPhase;
|
|
@@ -1205,8 +1216,29 @@ async function analyzeCommand(options) {
|
|
|
1205
1216
|
if (options.prompt) {
|
|
1206
1217
|
// v6.97.0+ 修复:全局分析时不应 fallback 到当前迭代
|
|
1207
1218
|
const iter = options.scope === 'global' ? 'GLOBAL' : (options.iteration || await (0, context_1.getDefaultIteration)());
|
|
1219
|
+
// v7.2.0+: 保存分析上下文到临时目录
|
|
1220
|
+
if (iter && iter !== 'GLOBAL') {
|
|
1221
|
+
try {
|
|
1222
|
+
const { saveAnalysisContext } = await Promise.resolve().then(() => __importStar(require('../core/iteration-cache')));
|
|
1223
|
+
await saveAnalysisContext({
|
|
1224
|
+
iteration: iter,
|
|
1225
|
+
docName: options.docName,
|
|
1226
|
+
featureName: options.featureName,
|
|
1227
|
+
withCode: options.withCode,
|
|
1228
|
+
timestamp: new Date().toISOString(),
|
|
1229
|
+
});
|
|
1230
|
+
}
|
|
1231
|
+
catch { /* ignore */ }
|
|
1232
|
+
}
|
|
1208
1233
|
const prompt = await buildMultiDocPrompt('analyze', { iteration: iter, task: options.task, type: options.type, scope: options.scope, withCode: options.withCode, platform: options.platform, phase: options.phase, autoMode: options.auto }, options);
|
|
1209
1234
|
process.stdout.write(`[SPECCORE_PROMPT]\n${prompt}`);
|
|
1235
|
+
// v7.2.0+: 全局分析完成后输出下一步引导
|
|
1236
|
+
if (options.scope === 'global') {
|
|
1237
|
+
const guide = await buildGlobalAnalysisGuide(options);
|
|
1238
|
+
if (guide) {
|
|
1239
|
+
process.stdout.write(`\n[SPECCORE_GUIDE]\n${guide}`);
|
|
1240
|
+
}
|
|
1241
|
+
}
|
|
1210
1242
|
process.exitCode = 10;
|
|
1211
1243
|
return;
|
|
1212
1244
|
}
|
|
@@ -1219,7 +1251,7 @@ async function preCreateSpecDirectories(iteration) {
|
|
|
1219
1251
|
const iterDir = await (0, context_1.getIterationDir)(iteration);
|
|
1220
1252
|
const specDir = (0, path_1.join)(iterDir, '020-specs');
|
|
1221
1253
|
await (0, fs_extra_1.ensureDir)(specDir);
|
|
1222
|
-
// 预创建 global/
|
|
1254
|
+
// 预创建 overview/ 子目录(v6.78.0+ 从 global/ 改名)
|
|
1223
1255
|
const globalDir = (0, path_1.join)(specDir, spec_paths_1.GLOBAL_SPECS_DIR);
|
|
1224
1256
|
await (0, fs_extra_1.ensureDir)(globalDir);
|
|
1225
1257
|
// 读取端列表并预创建各端目录
|
|
@@ -1228,10 +1260,103 @@ async function preCreateSpecDirectories(iteration) {
|
|
|
1228
1260
|
await (0, fs_extra_1.ensureDir)((0, path_1.join)(specDir, platform));
|
|
1229
1261
|
}
|
|
1230
1262
|
if (platforms.length > 0) {
|
|
1231
|
-
logger_1.logger.info(`📁 已预创建 020-specs/ 目录结构:
|
|
1263
|
+
logger_1.logger.info(`📁 已预创建 020-specs/ 目录结构: ${spec_paths_1.GLOBAL_SPECS_DIR}/ + ${platforms.length} 个端目录 (${platforms.join(', ')})`);
|
|
1232
1264
|
}
|
|
1233
1265
|
else {
|
|
1234
|
-
logger_1.logger.info(`📁 已预创建 020-specs/ 目录结构:
|
|
1266
|
+
logger_1.logger.info(`📁 已预创建 020-specs/ 目录结构: ${spec_paths_1.GLOBAL_SPECS_DIR}/`);
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1269
|
+
/**
|
|
1270
|
+
* v6.90.0+: 事后校验——检测并清理 020-specs/ 下 AI 绕过 --apply 创建的非法目录和文件
|
|
1271
|
+
*
|
|
1272
|
+
* 解决的问题:AI 用 Write 工具直接写文件,绕过 CLI --apply,导致:
|
|
1273
|
+
* - 非法子目录(如 1001/、工程标识/、错误码/)
|
|
1274
|
+
* - 遗留的 global/ 旧目录(应迁移到 overview/)
|
|
1275
|
+
* - 根目录散落的 .md 文件(应在 overview/ 内)
|
|
1276
|
+
*/
|
|
1277
|
+
async function sanitizeSpecDirectories(iterDir) {
|
|
1278
|
+
const specDir = (0, path_1.join)(iterDir, '020-specs');
|
|
1279
|
+
if (!await (0, fs_extra_1.pathExists)(specDir))
|
|
1280
|
+
return;
|
|
1281
|
+
const platforms = await (0, spec_paths_1.parsePlatformList)();
|
|
1282
|
+
const validDirs = new Set([spec_paths_1.GLOBAL_SPECS_DIR, ...platforms]);
|
|
1283
|
+
const globalSet = new Set(spec_paths_1.GLOBAL_SPEC_FILES);
|
|
1284
|
+
const entries = await (0, fs_extra_2.readdir)(specDir);
|
|
1285
|
+
let illegalDirCount = 0;
|
|
1286
|
+
let orphanFileCount = 0;
|
|
1287
|
+
let legacyGlobalMigrated = false;
|
|
1288
|
+
for (const entry of entries) {
|
|
1289
|
+
const entryPath = (0, path_1.join)(specDir, entry);
|
|
1290
|
+
const entryStat = await (0, fs_extra_1.stat)(entryPath);
|
|
1291
|
+
if (entryStat.isDirectory()) {
|
|
1292
|
+
// 检测遗留的 global/ 目录(v6.78.0+ 已改名为 overview/)
|
|
1293
|
+
if (entry === 'global') {
|
|
1294
|
+
logger_1.logger.warn(`⚠️ 检测到遗留 global/ 目录(v6.78.0+ 已改名为 ${spec_paths_1.GLOBAL_SPECS_DIR}/)`);
|
|
1295
|
+
// 将 global/ 中的文件迁移到 overview/
|
|
1296
|
+
const globalFiles = await (0, fs_extra_2.readdir)(entryPath);
|
|
1297
|
+
const overviewDir = (0, path_1.join)(specDir, spec_paths_1.GLOBAL_SPECS_DIR);
|
|
1298
|
+
await (0, fs_extra_1.ensureDir)(overviewDir);
|
|
1299
|
+
for (const f of globalFiles) {
|
|
1300
|
+
const src = (0, path_1.join)(entryPath, f);
|
|
1301
|
+
const dest = (0, path_1.join)(overviewDir, f);
|
|
1302
|
+
if (!await (0, fs_extra_1.pathExists)(dest)) {
|
|
1303
|
+
await (0, fs_extra_1.rename)(src, dest);
|
|
1304
|
+
logger_1.logger.info(` 📦 迁移: global/${f} → ${spec_paths_1.GLOBAL_SPECS_DIR}/${f}`);
|
|
1305
|
+
}
|
|
1306
|
+
}
|
|
1307
|
+
// 删除空的 global/ 目录
|
|
1308
|
+
const remaining = await (0, fs_extra_2.readdir)(entryPath);
|
|
1309
|
+
if (remaining.length === 0) {
|
|
1310
|
+
await (0, fs_extra_1.rename)(entryPath, (0, path_1.join)(specDir, `global.migrated-${Date.now()}`));
|
|
1311
|
+
logger_1.logger.info(` 🗑️ global/ 已迁移并归档`);
|
|
1312
|
+
}
|
|
1313
|
+
else {
|
|
1314
|
+
logger_1.logger.warn(` ⚠️ global/ 仍有 ${remaining.length} 个文件未迁移,已重命名归档`);
|
|
1315
|
+
await (0, fs_extra_1.rename)(entryPath, (0, path_1.join)(specDir, `global.archived-${Date.now()}`));
|
|
1316
|
+
}
|
|
1317
|
+
legacyGlobalMigrated = true;
|
|
1318
|
+
continue;
|
|
1319
|
+
}
|
|
1320
|
+
// 白名单校验:非 overview/ 且非端名的目录 → 非法
|
|
1321
|
+
if (!validDirs.has(entry)) {
|
|
1322
|
+
const archivedName = `${entry}.invalid-${Date.now()}`;
|
|
1323
|
+
await (0, fs_extra_1.rename)(entryPath, (0, path_1.join)(specDir, archivedName));
|
|
1324
|
+
logger_1.logger.warn(`⚠️ 非法目录已归档: 020-specs/${entry}/ → ${archivedName}/`);
|
|
1325
|
+
illegalDirCount++;
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
else if (entryStat.isFile()) {
|
|
1329
|
+
// 根目录散落的 .md 文件 → 检查是否应归入 overview/
|
|
1330
|
+
if (entry.endsWith('.md') && globalSet.has(entry)) {
|
|
1331
|
+
const overviewDir = (0, path_1.join)(specDir, spec_paths_1.GLOBAL_SPECS_DIR);
|
|
1332
|
+
await (0, fs_extra_1.ensureDir)(overviewDir);
|
|
1333
|
+
const dest = (0, path_1.join)(overviewDir, entry);
|
|
1334
|
+
if (!await (0, fs_extra_1.pathExists)(dest)) {
|
|
1335
|
+
await (0, fs_extra_1.rename)(entryPath, dest);
|
|
1336
|
+
logger_1.logger.warn(`⚠️ 散落文件已归位: 020-specs/${entry} → ${spec_paths_1.GLOBAL_SPECS_DIR}/${entry}`);
|
|
1337
|
+
orphanFileCount++;
|
|
1338
|
+
}
|
|
1339
|
+
else {
|
|
1340
|
+
// overview/ 中已有同名文件,归档根目录版本
|
|
1341
|
+
const archivedName = `${entry}.orphan-${Date.now()}`;
|
|
1342
|
+
await (0, fs_extra_1.rename)(entryPath, (0, path_1.join)(specDir, archivedName));
|
|
1343
|
+
logger_1.logger.warn(`⚠️ 重复散落文件已归档: 020-specs/${entry} → ${archivedName}`);
|
|
1344
|
+
orphanFileCount++;
|
|
1345
|
+
}
|
|
1346
|
+
}
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1349
|
+
// 汇总报告
|
|
1350
|
+
if (illegalDirCount > 0 || orphanFileCount > 0 || legacyGlobalMigrated) {
|
|
1351
|
+
logger_1.logger.info('');
|
|
1352
|
+
logger_1.logger.info(`🔍 020-specs/ 目录校验报告:`);
|
|
1353
|
+
if (illegalDirCount > 0)
|
|
1354
|
+
logger_1.logger.warn(` ⚠️ ${illegalDirCount} 个非法目录已归档(AI 绕过 --apply 创建)`);
|
|
1355
|
+
if (orphanFileCount > 0)
|
|
1356
|
+
logger_1.logger.warn(` ⚠️ ${orphanFileCount} 个散落文件已归位到 ${spec_paths_1.GLOBAL_SPECS_DIR}/`);
|
|
1357
|
+
if (legacyGlobalMigrated)
|
|
1358
|
+
logger_1.logger.info(` 📦 遗留 global/ 已迁移到 ${spec_paths_1.GLOBAL_SPECS_DIR}/`);
|
|
1359
|
+
logger_1.logger.info(` ✅ 当前合法目录: ${Array.from(validDirs).join(', ')}`);
|
|
1235
1360
|
}
|
|
1236
1361
|
}
|
|
1237
1362
|
/**
|
|
@@ -1658,6 +1783,148 @@ async function injectGraphSummary(prompt) {
|
|
|
1658
1783
|
return prompt;
|
|
1659
1784
|
}
|
|
1660
1785
|
}
|
|
1786
|
+
// ── v7.2.0+: 检测全局分析当前进度 ──
|
|
1787
|
+
// Layer 4 拆分为子层: 4a=产品文档, 4b=全局技术核心, 4c=全局技术扩展, 4d=各端技术
|
|
1788
|
+
async function detectGlobalLayerProgress() {
|
|
1789
|
+
const globalDir = (0, path_1.join)(process.cwd(), '.speccore', 'GLOBAL');
|
|
1790
|
+
let completedLayer = 0;
|
|
1791
|
+
const missing = [];
|
|
1792
|
+
// Layer 1: 检查各端 _INDEX.md
|
|
1793
|
+
try {
|
|
1794
|
+
const platformsDir = (0, path_1.join)(globalDir, 'platforms');
|
|
1795
|
+
const entries = await (0, fs_extra_2.readdir)(platformsDir, { withFileTypes: true });
|
|
1796
|
+
const platformDirs = entries.filter(e => e.isDirectory() && e.name !== '_shared').map(e => e.name);
|
|
1797
|
+
const hasIndex = platformDirs.length > 0 && (await Promise.all(platformDirs.map(async (d) => (0, fs_extra_1.pathExists)((0, path_1.join)(platformsDir, d, '_INDEX.md'))))).some(Boolean);
|
|
1798
|
+
if (hasIndex)
|
|
1799
|
+
completedLayer = 1;
|
|
1800
|
+
else
|
|
1801
|
+
missing.push('Layer 1: platforms/{端}/_INDEX.md');
|
|
1802
|
+
}
|
|
1803
|
+
catch {
|
|
1804
|
+
missing.push('Layer 1: platforms/{端}/_INDEX.md');
|
|
1805
|
+
}
|
|
1806
|
+
// Layer 2: 检查 _ASSOCIATION.md
|
|
1807
|
+
if (completedLayer >= 1) {
|
|
1808
|
+
if (await (0, fs_extra_1.pathExists)((0, path_1.join)(globalDir, 'platforms', '_shared', '_ASSOCIATION.md'))) {
|
|
1809
|
+
completedLayer = 2;
|
|
1810
|
+
}
|
|
1811
|
+
else {
|
|
1812
|
+
missing.push('Layer 2: platforms/_shared/_ASSOCIATION.md + _MODULES.md');
|
|
1813
|
+
}
|
|
1814
|
+
}
|
|
1815
|
+
// Layer 3: 检查 _MODULES.md
|
|
1816
|
+
if (completedLayer >= 2) {
|
|
1817
|
+
if (await (0, fs_extra_1.pathExists)((0, path_1.join)(globalDir, 'platforms', '_shared', '_MODULES.md'))) {
|
|
1818
|
+
completedLayer = 3;
|
|
1819
|
+
}
|
|
1820
|
+
else {
|
|
1821
|
+
missing.push('Layer 3: platforms/_shared/_MODULES.md(功能模块候选清单)');
|
|
1822
|
+
}
|
|
1823
|
+
}
|
|
1824
|
+
// Layer 4 子层检测
|
|
1825
|
+
let subLayer;
|
|
1826
|
+
if (completedLayer >= 3) {
|
|
1827
|
+
const overviewDir = (0, path_1.join)(globalDir, 'overview');
|
|
1828
|
+
const requirementsDir = (0, path_1.join)(globalDir, 'requirements');
|
|
1829
|
+
const completedSubLayers = [];
|
|
1830
|
+
// 4a: 产品文档
|
|
1831
|
+
const hasReq = await (0, fs_extra_1.pathExists)((0, path_1.join)(requirementsDir, 'REQUIREMENT.md'));
|
|
1832
|
+
if (hasReq)
|
|
1833
|
+
completedSubLayers.push('4a');
|
|
1834
|
+
// 4b: 全局技术核心文档
|
|
1835
|
+
const hasCoreTech = await (0, fs_extra_1.pathExists)((0, path_1.join)(overviewDir, 'ARCHITECTURE.md'))
|
|
1836
|
+
&& await (0, fs_extra_1.pathExists)((0, path_1.join)(overviewDir, 'FUNCTION_MAP.md'));
|
|
1837
|
+
if (hasCoreTech)
|
|
1838
|
+
completedSubLayers.push('4b');
|
|
1839
|
+
// 4c: 全局技术扩展文档
|
|
1840
|
+
const hasExtTech = await (0, fs_extra_1.pathExists)((0, path_1.join)(overviewDir, 'SECURITY_AUDIT.md'))
|
|
1841
|
+
|| await (0, fs_extra_1.pathExists)((0, path_1.join)(overviewDir, 'DATA_FLOW.md'));
|
|
1842
|
+
if (hasExtTech)
|
|
1843
|
+
completedSubLayers.push('4c');
|
|
1844
|
+
// 4d: 各端技术文档
|
|
1845
|
+
try {
|
|
1846
|
+
const platformsDir = (0, path_1.join)(globalDir, 'platforms');
|
|
1847
|
+
const entries = await (0, fs_extra_2.readdir)(platformsDir, { withFileTypes: true });
|
|
1848
|
+
const platformDirs = entries.filter(e => e.isDirectory() && e.name !== '_shared').map(e => e.name);
|
|
1849
|
+
const hasPlatformDoc = platformDirs.length > 0 && (await Promise.all(platformDirs.map(async (d) => (0, fs_extra_1.pathExists)((0, path_1.join)(platformsDir, d, 'API_INVENTORY.md'))
|
|
1850
|
+
|| (0, fs_extra_1.pathExists)((0, path_1.join)(platformsDir, d, 'UI_FLOW.md'))))).some(Boolean);
|
|
1851
|
+
if (hasPlatformDoc)
|
|
1852
|
+
completedSubLayers.push('4d');
|
|
1853
|
+
}
|
|
1854
|
+
catch { /* ignore */ }
|
|
1855
|
+
if (completedSubLayers.length === 4) {
|
|
1856
|
+
completedLayer = 4;
|
|
1857
|
+
}
|
|
1858
|
+
else {
|
|
1859
|
+
const subLayerOrder = ['4a', '4b', '4c', '4d'];
|
|
1860
|
+
const nextSub = subLayerOrder.find(s => !completedSubLayers.includes(s)) || '4d';
|
|
1861
|
+
missing.push(`Layer 4${nextSub}: 全局汇总文档子层`);
|
|
1862
|
+
subLayer = { completed: completedSubLayers, next: nextSub };
|
|
1863
|
+
}
|
|
1864
|
+
}
|
|
1865
|
+
return { completedLayer, nextLayer: Math.min(completedLayer + 1, 4), missing, subLayer };
|
|
1866
|
+
}
|
|
1867
|
+
// ── v7.2.0+: 全局分析下一步引导 ──
|
|
1868
|
+
async function buildGlobalAnalysisGuide(options) {
|
|
1869
|
+
const progress = await detectGlobalLayerProgress();
|
|
1870
|
+
const { completedLayer, nextLayer, subLayer } = progress;
|
|
1871
|
+
const deepDoc = options?.deep;
|
|
1872
|
+
let guide = '';
|
|
1873
|
+
// 进度条
|
|
1874
|
+
const layers = ['Layer 1 索引扫描', 'Layer 2 跨端关联', 'Layer 3 模块深入', 'Layer 4 全局汇总'];
|
|
1875
|
+
guide += `\n📊 全局分析进度: ${completedLayer}/4 层完成\n`;
|
|
1876
|
+
guide += layers.map((l, i) => {
|
|
1877
|
+
const status = i < completedLayer ? '✅' : i === completedLayer ? '▶️' : '⬜';
|
|
1878
|
+
return ` ${status} ${l}`;
|
|
1879
|
+
}).join('\n');
|
|
1880
|
+
guide += '\n';
|
|
1881
|
+
if (completedLayer === 4 && (!subLayer || subLayer.completed.length === 4)) {
|
|
1882
|
+
guide += '\n🎉 全局分析全部完成!\n';
|
|
1883
|
+
guide += ' 所有文档已生成在 .speccore/GLOBAL/ 目录\n';
|
|
1884
|
+
guide += ' 如需补充某份文档: speccore analyze --scope global --layer 4 --deep <文档名>\n';
|
|
1885
|
+
return guide;
|
|
1886
|
+
}
|
|
1887
|
+
// 下一步命令
|
|
1888
|
+
guide += '\n➡️ 下一步:\n';
|
|
1889
|
+
if (deepDoc && options?.iterative) {
|
|
1890
|
+
const outlinePath = (0, path_1.join)(process.cwd(), '.speccore', 'cache', `deep-outline-${deepDoc.replace(/\//g, '-')}.md`);
|
|
1891
|
+
const hasOutline = await (0, fs_extra_1.pathExists)(outlinePath);
|
|
1892
|
+
if (!hasOutline) {
|
|
1893
|
+
guide += ` 1. 将 AI 输出的大纲保存到: ${outlinePath}\n`;
|
|
1894
|
+
guide += ` 2. 审核/修改大纲\n`;
|
|
1895
|
+
guide += ` 3. 再次执行: speccore analyze --scope global --layer 4 --deep ${deepDoc} --iterative\n`;
|
|
1896
|
+
}
|
|
1897
|
+
else {
|
|
1898
|
+
guide += ` 1. 将 AI 输出的本节内容追加到文档\n`;
|
|
1899
|
+
guide += ` 2. 继续下一节: speccore analyze --scope global --layer 4 --deep ${deepDoc} --iterative\n`;
|
|
1900
|
+
}
|
|
1901
|
+
}
|
|
1902
|
+
else if (deepDoc) {
|
|
1903
|
+
guide += ` speccore analyze --scope global --layer 4 --deep ${deepDoc}\n`;
|
|
1904
|
+
guide += ` (如需迭代式补全: 加 --iterative 参数)\n`;
|
|
1905
|
+
}
|
|
1906
|
+
else if (nextLayer === 4 && subLayer) {
|
|
1907
|
+
const subNames = {
|
|
1908
|
+
'4a': '产品视角文档(requirements/)',
|
|
1909
|
+
'4b': '全局技术核心文档(overview/)',
|
|
1910
|
+
'4c': '全局技术扩展文档(overview/)',
|
|
1911
|
+
'4d': '各端技术文档(platforms/)',
|
|
1912
|
+
};
|
|
1913
|
+
guide += ` speccore analyze --scope global --layer 4\n`;
|
|
1914
|
+
guide += ` (即将生成: ${subNames[subLayer.next] || subLayer.next})\n`;
|
|
1915
|
+
}
|
|
1916
|
+
else {
|
|
1917
|
+
guide += ` speccore analyze --scope global --layer ${nextLayer}\n`;
|
|
1918
|
+
}
|
|
1919
|
+
// 快捷命令提示
|
|
1920
|
+
guide += '\n💡 快捷命令:\n';
|
|
1921
|
+
guide += ` 查看进度: speccore status\n`;
|
|
1922
|
+
if (completedLayer >= 3) {
|
|
1923
|
+
guide += ` 深度分析单文档: speccore analyze --scope global --layer 4 --deep ARCHITECTURE.md\n`;
|
|
1924
|
+
}
|
|
1925
|
+
guide += ` 全量重新分析: speccore analyze --scope global --with-code\n`;
|
|
1926
|
+
return guide;
|
|
1927
|
+
}
|
|
1661
1928
|
// ── buildMultiDocPrompt: 多文档协议 ──
|
|
1662
1929
|
async function buildMultiDocPrompt(command, ctx, options) {
|
|
1663
1930
|
const iter = ctx.iteration || '当前迭代';
|
|
@@ -1673,21 +1940,78 @@ async function buildMultiDocPrompt(command, ctx, options) {
|
|
|
1673
1940
|
if (ctx.withCode && (options?.streaming || options?.streamingPhase)) {
|
|
1674
1941
|
return buildStreamingGlobalPrompt(command, ctx, options);
|
|
1675
1942
|
}
|
|
1943
|
+
// v7.2.0+: 全局分析分层执行 — 检测当前进度,自动分配 layer
|
|
1944
|
+
const progress = await detectGlobalLayerProgress();
|
|
1945
|
+
const targetLayer = options?.layer || progress.nextLayer;
|
|
1946
|
+
const isLayered = !!options?.layer || progress.completedLayer < 4;
|
|
1947
|
+
// v7.2.0+: 结构化代码数据提取 — Layer 1 之前自动执行
|
|
1948
|
+
let structuredDataHint = '';
|
|
1949
|
+
if (targetLayer === 1 && ctx.withCode) {
|
|
1950
|
+
try {
|
|
1951
|
+
// 读取 CONSTITUTION.md 获取源码路径
|
|
1952
|
+
const constitutionPath = (0, path_1.join)(process.cwd(), '.speccore', 'CONSTITUTION.md');
|
|
1953
|
+
let sourcePaths = ['src'];
|
|
1954
|
+
if (await (0, fs_extra_1.pathExists)(constitutionPath)) {
|
|
1955
|
+
const content = await (0, fs_extra_2.readFile)(constitutionPath, 'utf-8');
|
|
1956
|
+
const match = content.match(/源码路径[\s\S]*?\n\s*-\s*`?([^`\n]+)`?/g);
|
|
1957
|
+
if (match) {
|
|
1958
|
+
sourcePaths = match.map(m => m.replace(/.*-\s*`?/, '').replace(/`?$/, '').trim()).filter(Boolean);
|
|
1959
|
+
}
|
|
1960
|
+
}
|
|
1961
|
+
await (0, structured_extractor_1.extractStructuredData)(process.cwd(), sourcePaths);
|
|
1962
|
+
structuredDataHint = '\n> 📊 **结构化数据**: 已提取到 `.speccore/cache/structured-data.json`,包含 API/Entity/Route/Component 清单\n';
|
|
1963
|
+
}
|
|
1964
|
+
catch (e) {
|
|
1965
|
+
logger_1.logger.warn(` ⚠️ 结构化数据提取失败: ${e.message}`);
|
|
1966
|
+
}
|
|
1967
|
+
}
|
|
1968
|
+
// Layer 角色定义
|
|
1969
|
+
const LAYER_ROLES = {
|
|
1970
|
+
1: { role: '代码索引专家', focus: '全面扫描各端源码结构,提取目录/接口/实体/配置等索引信息', output: '各端 _INDEX.md + PATTERNS 模式提取 + semantic-tags.json' },
|
|
1971
|
+
2: { role: '系统架构师', focus: '基于 Layer 1 索引进行跨端关联分析、接口匹配、模块聚类', output: '_ASSOCIATION.md + _MODULES.md' },
|
|
1972
|
+
3: { role: '业务分析师', focus: '按功能模块深入分析业务逻辑、数据流、规则、时序', output: '各端功能模块深入文档 + 模块级 PATTERNS' },
|
|
1973
|
+
4: { role: '产品总监 + 技术负责人', focus: '全局汇总,分 4 个子层执行(4a产品→4b技术核心→4c技术扩展→4d各端)', output: 'requirements/ + overview/ + platforms/' },
|
|
1974
|
+
};
|
|
1975
|
+
const layerMeta = LAYER_ROLES[targetLayer];
|
|
1676
1976
|
let prompt = `\n# 任务: ${command} (全局分析${ctx.withCode ? '+源码' : ''})\n\n`;
|
|
1977
|
+
// v7.2.0+: 分层专注指令 — 强烈约束 AI 只执行当前 layer
|
|
1978
|
+
prompt += `## 🎯 当前执行层级: Layer ${targetLayer}/4 — ${layerMeta.role}\n\n`;
|
|
1979
|
+
prompt += `> ⚠️ **重要约束**: 你当前只需要完成 **Layer ${targetLayer}** 的工作。不要提前做后续层的内容。\n`;
|
|
1980
|
+
prompt += `> 每层完成后通过 \`speccore analyze --scope global --layer ${targetLayer + 1 <= 4 ? targetLayer + 1 : 4}\` 进入下一层。\n\n`;
|
|
1981
|
+
prompt += `| 层级 | 角色 | 核心任务 | 产出物 |\n`;
|
|
1982
|
+
prompt += `| :--- | :--- | :--- | :--- |\n`;
|
|
1983
|
+
prompt += `| 1 | 代码索引专家 | 扫描源码结构,提取索引 | platforms/{端}/_INDEX.md |\n`;
|
|
1984
|
+
prompt += `| 2 | 系统架构师 | 跨端关联、接口匹配、模块聚类 | platforms/_shared/_ASSOCIATION.md + _MODULES.md |\n`;
|
|
1985
|
+
prompt += `| 3 | 业务分析师 | 功能模块深入分析 | 各端功能模块文档 |\n`;
|
|
1986
|
+
prompt += `| 4 | 产品总监+技术负责人 | 全局汇总、需求总纲、一致性校验 | overview/ + requirements/ |\n\n`;
|
|
1987
|
+
if (progress.completedLayer > 0) {
|
|
1988
|
+
prompt += `📊 检测进度: 已完成 Layer ${progress.completedLayer}/4`;
|
|
1989
|
+
if (progress.missing.length > 0) {
|
|
1990
|
+
prompt += `,待补齐: ${progress.missing.join('; ')}`;
|
|
1991
|
+
}
|
|
1992
|
+
prompt += `\n\n`;
|
|
1993
|
+
}
|
|
1994
|
+
prompt += `## 你的专注任务(Layer ${targetLayer})\n`;
|
|
1995
|
+
prompt += `- **角色**: ${layerMeta.role}\n`;
|
|
1996
|
+
prompt += `- **核心任务**: ${layerMeta.focus}\n`;
|
|
1997
|
+
prompt += `- **预期产出**: ${layerMeta.output}\n\n`;
|
|
1677
1998
|
prompt += `## 要求\n`;
|
|
1678
|
-
prompt += `1. **先读宪法**: Read .speccore/CONSTITUTION.md
|
|
1679
|
-
prompt += `
|
|
1680
|
-
prompt += `
|
|
1681
|
-
prompt += ` - 「## 端列表」章节 → 全局权威端名列表(如 backend/h5/admin)\n`;
|
|
1682
|
-
prompt += ` - 每个工程独立分析,文档输出到: .speccore/GLOBAL/platforms/{端名}/\n`;
|
|
1683
|
-
prompt += `2. Read .speccore/GLOBAL/ 下所有文档了解跨项目需求\n`;
|
|
1684
|
-
prompt += `3. **禁止行为(重要)**: 不要打开浏览器、不要模拟用户操作、不要访问 URL。所有分析必须基于直接 Read 源码文件完成。如果文件不存在,跳过即可,不要尝试其他方式获取。\n`;
|
|
1999
|
+
prompt += `1. **先读宪法**: Read .speccore/CONSTITUTION.md,获取工程名、源码路径、端列表。\n`;
|
|
2000
|
+
prompt += `2. Read .speccore/GLOBAL/ 下已有文档(特别是前一层的产物)作为输入。\n`;
|
|
2001
|
+
prompt += `3. **禁止行为**: 不要打开浏览器、不要模拟用户操作、不要访问 URL。所有分析基于直接 Read 源码。\n`;
|
|
1685
2002
|
if (ctx.withCode) {
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
prompt +=
|
|
2003
|
+
prompt += `4. 从 CONSTITUTION.md 的「源码路径」列读取所有工程目录。\n`;
|
|
2004
|
+
}
|
|
2005
|
+
// 根据 targetLayer 注入对应的专注内容
|
|
2006
|
+
if (targetLayer === 1) {
|
|
2007
|
+
prompt += `\n## 📊 Layer 1: 快速扫描所有端(基于结构化数据生成索引)\n\n`;
|
|
2008
|
+
prompt += `${structuredDataHint}`;
|
|
2009
|
+
prompt += `**重要**: 不要直接扫描源码文件。已使用代码扫描工具提取了结构化数据,你只需要读取这些数据并整理成 _INDEX.md。\n\n`;
|
|
2010
|
+
prompt += `**步骤**: \n`;
|
|
2011
|
+
prompt += `1. Read \`.speccore/cache/structured-data.json\` — 获取所有端的 API/Entity/Route/Component 清单\n`;
|
|
2012
|
+
prompt += `2. 对每个端,基于结构化数据生成 \`_INDEX.md\`(补充扫描工具未覆盖的内容)\n`;
|
|
2013
|
+
prompt += `3. 扫描工具未覆盖的维度(消息队列、定时任务、配置、外部集成、日志监控、错误处理),需要 Read 相关配置文件补充\n\n`;
|
|
2014
|
+
prompt += `**后端端 _INDEX.md 维度(基于 structured-data.json + 补充扫描)**:\n`;
|
|
1691
2015
|
prompt += `| 扫描项 | 读取位置 | 提取内容 |\n`;
|
|
1692
2016
|
prompt += `| :--- | :--- | :--- |\n`;
|
|
1693
2017
|
prompt += `| 接口层 | Controller/Handler/Resource 目录 | 接口类名、接口路径(从注解/装饰器推断)、鉴权注解 |\n`;
|
|
@@ -1759,7 +2083,8 @@ async function buildMultiDocPrompt(command, ctx, options) {
|
|
|
1759
2083
|
prompt += ` }\n`;
|
|
1760
2084
|
prompt += `]\n`;
|
|
1761
2085
|
prompt += `\`\`\`\n\n`;
|
|
1762
|
-
prompt += `## 🔗 Layer 2: 跨端关联分析(基于 Layer 1
|
|
2086
|
+
prompt += `## 🔗 Layer 2: 跨端关联分析(基于 Layer 1 索引 + structured-data.json)\n\n`;
|
|
2087
|
+
prompt += `> 📊 **结构化数据**: Read \`.speccore/cache/structured-data.json\` 获取 API/Entity 清单,与 Layer 1 索引交叉验证\n\n`;
|
|
1763
2088
|
prompt += `1. **匹配前后端接口**:\n`;
|
|
1764
2089
|
prompt += ` - 前端 \`_INDEX.md\` 中的 API 调用路径 vs 后端 \`_INDEX.md\` 中的接口路径\n`;
|
|
1765
2090
|
prompt += ` - **匹配上** → 建立「前端页面 → 前端 API 调用 → 后端接口 → 后端服务」链路\n`;
|
|
@@ -1863,23 +2188,23 @@ async function buildMultiDocPrompt(command, ctx, options) {
|
|
|
1863
2188
|
prompt += ` - **响应式/适配策略**:不同设备尺寸下的布局变化、断点设计\n`;
|
|
1864
2189
|
prompt += ` - **无障碍要求**:键盘导航、屏幕阅读器、色彩对比度(如有要求)\n`;
|
|
1865
2190
|
prompt += ` - 每个前端端需求目录下可放 images/(截图/流程图/原型图)和 prototypes/(可交互原型文件)\n`;
|
|
1866
|
-
prompt += ` **技术视角 → 存放到 .speccore/GLOBAL/
|
|
1867
|
-
prompt += ` - \`
|
|
1868
|
-
prompt += ` - \`
|
|
2191
|
+
prompt += ` **技术视角 → 存放到 .speccore/GLOBAL/overview/(不与 platforms/requirements 平级):**\n`;
|
|
2192
|
+
prompt += ` - \`overview/FUNCTION_MAP.md\`:功能单元 × 端映射表\n`;
|
|
2193
|
+
prompt += ` - \`overview/INTERACTION_MAP.md\`:跨端交互时序图(含同步 API + 异步消息,从 Layer 3 汇总)\n`;
|
|
1869
2194
|
prompt += ` - 必须包含 **Mermaid sequenceDiagram**,展示各端之间的核心交互时序\n`;
|
|
1870
|
-
prompt += ` - \`
|
|
1871
|
-
prompt += ` - \`
|
|
2195
|
+
prompt += ` - \`overview/API_CONTRACT.yaml\`:全局接口契约(汇总所有后端 API_INVENTORY,含 rate limit、幂等性、版本策略)\n`;
|
|
2196
|
+
prompt += ` - \`overview/ARCHITECTURE.md\`:全局架构文档(服务拓扑、数据流、部署关系、容错设计、降级策略、扩容方案)\n`;
|
|
1872
2197
|
prompt += ` - 必须包含 **Mermaid architecture diagram**(graph TB),展示服务拓扑和部署关系\n`;
|
|
1873
2198
|
prompt += ` - 必须包含 **Mermaid graph LR**,展示模块间的依赖关系\n`;
|
|
1874
|
-
prompt += ` - \`
|
|
1875
|
-
prompt += ` - \`
|
|
1876
|
-
prompt += ` - \`
|
|
2199
|
+
prompt += ` - \`overview/SECURITY_AUDIT.md\`:全局安全审计(鉴权策略矩阵、敏感数据流、攻击面分析、CVE 清单、合规检查)\n`;
|
|
2200
|
+
prompt += ` - \`overview/PERFORMANCE_BASELINE.md\`:性能基线(慢查询清单、缓存策略矩阵、并发承载评估、关键路径耗时)\n`;
|
|
2201
|
+
prompt += ` - \`overview/DATA_FLOW.md\`:数据流与隐私分析(PII 识别与追踪、数据生命周期、存储/传输/归档策略、GDPR 合规检查)\n`;
|
|
1877
2202
|
prompt += ` - 必须包含 **Mermaid flowchart**,展示数据从产生到销毁的完整生命周期\n`;
|
|
1878
|
-
prompt += ` - \`
|
|
1879
|
-
prompt += ` - \`
|
|
2203
|
+
prompt += ` - \`overview/EXTERNAL_INTEGRATIONS.md\`:外部集成审计(第三方服务清单、SDK 版本与风险、Webhook 接收分布、重复集成识别)\n`;
|
|
2204
|
+
prompt += ` - \`overview/DEPLOYMENT.md\`:部署运维分析(容器化状态、CI/CD 流水线、健康检查端点、环境配置差异、日志聚合方案)\n`;
|
|
1880
2205
|
prompt += ` - 必须包含 **Mermaid flowchart**,展示 CI/CD 流水线流程\n`;
|
|
1881
|
-
prompt += ` - \`
|
|
1882
|
-
prompt += ` - \`
|
|
2206
|
+
prompt += ` - \`overview/OBSERVABILITY.md\`:可观测性分析(日志链路追踪、错误码体系、监控埋点清单、告警策略、SLA 定义)\n`;
|
|
2207
|
+
prompt += ` - \`overview/CONSISTENCY_CHECK.md\`:一致性校验报告(字段/状态/接口/消息/配置)\n\n`;
|
|
1883
2208
|
prompt += `3. **生成各端详细文档**(技术视角,从 Layer 3 汇总):\n`;
|
|
1884
2209
|
prompt += ` > 存放: .speccore/GLOBAL/platforms/{端名}/\n`;
|
|
1885
2210
|
prompt += ` **后端端(9项)**:\n`;
|
|
@@ -2022,21 +2347,21 @@ async function buildMultiDocPrompt(command, ctx, options) {
|
|
|
2022
2347
|
prompt += `| :--- | :--- | :--- |\n`;
|
|
2023
2348
|
prompt += `| requirements/REQUIREMENT.md | 产品总监视角 | 需求总纲:愿景、用户画像、场景地图、功能全景、优先级矩阵、里程碑、风险预判 |\n`;
|
|
2024
2349
|
prompt += `| requirements/{前端端}/REQUIREMENT.md | 前端产品经理视角 | 信息架构、用户旅程、页面清单、交互设计、状态反馈、权限角色、响应式策略 |\n`;
|
|
2025
|
-
prompt += `\n### 全局技术性文档(Layer 4 汇总生成,
|
|
2026
|
-
prompt += `> 存放: .speccore/GLOBAL/
|
|
2350
|
+
prompt += `\n### 全局技术性文档(Layer 4 汇总生成,v7.2.0+)\n`;
|
|
2351
|
+
prompt += `> 存放: .speccore/GLOBAL/overview/(不与 platforms/requirements 平级)\n\n`;
|
|
2027
2352
|
prompt += `| 文档 | 视角 | 内容 |\n`;
|
|
2028
2353
|
prompt += `| :--- | :--- | :--- |\n`;
|
|
2029
|
-
prompt += `|
|
|
2030
|
-
prompt += `|
|
|
2031
|
-
prompt += `|
|
|
2032
|
-
prompt += `|
|
|
2033
|
-
prompt += `|
|
|
2034
|
-
prompt += `|
|
|
2035
|
-
prompt += `|
|
|
2036
|
-
prompt += `|
|
|
2037
|
-
prompt += `|
|
|
2038
|
-
prompt += `|
|
|
2039
|
-
prompt += `|
|
|
2354
|
+
prompt += `| overview/FUNCTION_MAP.md | 架构视角 | 功能单元 × 端映射表 |\n`;
|
|
2355
|
+
prompt += `| overview/INTERACTION_MAP.md | 架构视角 | 跨端交互时序图(同步 API + 异步消息) |\n`;
|
|
2356
|
+
prompt += `| overview/API_CONTRACT.yaml | 技术视角 | 全局接口契约(含 rate limit、幂等性、版本策略、废弃标记) |\n`;
|
|
2357
|
+
prompt += `| overview/ARCHITECTURE.md | 技术视角 | 全局架构(服务拓扑、数据流、部署关系、容错、降级、扩容) |\n`;
|
|
2358
|
+
prompt += `| overview/SECURITY_AUDIT.md | 安全视角 | 鉴权矩阵、敏感数据流、攻击面、CVE 清单、合规检查 |\n`;
|
|
2359
|
+
prompt += `| overview/PERFORMANCE_BASELINE.md | 性能视角 | 慢查询清单、缓存策略矩阵、并发承载、关键路径耗时 |\n`;
|
|
2360
|
+
prompt += `| overview/DATA_FLOW.md | 数据视角 | PII 识别追踪、数据生命周期、存储/传输/归档、GDPR 合规 |\n`;
|
|
2361
|
+
prompt += `| overview/EXTERNAL_INTEGRATIONS.md | 集成视角 | 第三方服务清单、SDK 风险、Webhook 分布、重复集成识别 |\n`;
|
|
2362
|
+
prompt += `| overview/DEPLOYMENT.md | 运维视角 | 容器化、CI/CD、健康检查、环境差异、日志聚合 |\n`;
|
|
2363
|
+
prompt += `| overview/OBSERVABILITY.md | 可观测视角 | 日志链路、错误码体系、监控埋点、告警策略、SLA |\n`;
|
|
2364
|
+
prompt += `| overview/CONSISTENCY_CHECK.md | 质量视角 | 一致性校验报告(字段/状态/接口/消息/配置) |\n`;
|
|
2040
2365
|
prompt += `\n### 后端端技术性文档(9项,Layer 3/4 汇总)\n`;
|
|
2041
2366
|
prompt += `> 存放: .speccore/GLOBAL/platforms/{后端端名}/\n\n`;
|
|
2042
2367
|
prompt += `| 文档 | 内容 |\n`;
|
|
@@ -2097,6 +2422,175 @@ async function buildMultiDocPrompt(command, ctx, options) {
|
|
|
2097
2422
|
prompt += `\n⚠️ 如 CONSTITUTION.md 中「源码路径」为空或路径不存在: 提示用户先配置,给出三个选项:\n`;
|
|
2098
2423
|
prompt += ` [1] 停止分析 → 配置后重来 | [2] 跳过源码 → 只用文档分析 | [3] 手动指定路径后继续\n`;
|
|
2099
2424
|
prompt += '\n' + (0, questions_1.buildAutoModeInstruction)('analyze', iter) + '\n';
|
|
2425
|
+
// v7.2.0+: Layer 专注指令 — 在 Prompt 末尾再次强化约束
|
|
2426
|
+
prompt += `\n## 🎯 Layer ${targetLayer} 执行清单(必须逐项完成)\n\n`;
|
|
2427
|
+
prompt += `> ⚠️ **再次强调**: 你只执行 Layer ${targetLayer},不要提前做其他层。\n\n`;
|
|
2428
|
+
if (targetLayer === 1) {
|
|
2429
|
+
prompt += `- [ ] Read CONSTITUTION.md 获取所有端和源码路径\n`;
|
|
2430
|
+
prompt += `- [ ] 对每个端扫描 10 个维度,生成 _INDEX.md\n`;
|
|
2431
|
+
prompt += `- [ ] 提取可复用模式写入 PATTERNS/\n`;
|
|
2432
|
+
prompt += `- [ ] 提取语义标签写入 semantic-tags.json\n`;
|
|
2433
|
+
prompt += `- [ ] 写入完成后执行: \`speccore analyze --scope global --layer 2\`\n`;
|
|
2434
|
+
}
|
|
2435
|
+
else if (targetLayer === 2) {
|
|
2436
|
+
prompt += `- [ ] Read 所有 Layer 1 生成的 _INDEX.md\n`;
|
|
2437
|
+
prompt += `- [ ] 匹配前后端接口,生成关联矩阵\n`;
|
|
2438
|
+
prompt += `- [ ] 识别公共服务、消息流、定时任务影响\n`;
|
|
2439
|
+
prompt += `- [ ] 归纳功能模块,生成 _MODULES.md\n`;
|
|
2440
|
+
prompt += `- [ ] 写入 _ASSOCIATION.md + _MODULES.md\n`;
|
|
2441
|
+
prompt += `- [ ] 写入完成后执行: \`speccore analyze --scope global --layer 3\`\n`;
|
|
2442
|
+
}
|
|
2443
|
+
else if (targetLayer === 3) {
|
|
2444
|
+
const filter = options?.filter;
|
|
2445
|
+
prompt += `- [ ] Read Layer 2 的 _MODULES.md 获取功能模块清单\n`;
|
|
2446
|
+
if (filter) {
|
|
2447
|
+
prompt += `> 🔍 **按需分析**: 只分析与 "${filter}" 匹配的模块\n`;
|
|
2448
|
+
prompt += `> 从 _MODULES.md 中筛选涉及 ${filter} 的功能模块,其他模块跳过\n`;
|
|
2449
|
+
}
|
|
2450
|
+
prompt += `- [ ] ${filter ? '筛选匹配的模块,逐个' : '逐个功能模块'}深入分析(读详细源码)\n`;
|
|
2451
|
+
prompt += `- [ ] 每个模块生成:API 设计、数据模型、业务规则、交互时序\n`;
|
|
2452
|
+
prompt += `- [ ] 时序图/流程图/状态图用 Mermaid 嵌入\n`;
|
|
2453
|
+
prompt += `- [ ] 提取模块级模式补充到 PATTERNS/\n`;
|
|
2454
|
+
prompt += `- [ ] 写入完成后执行: \`speccore analyze --scope global --layer 4${filter ? ' --filter ' + filter : ''}\`\n`;
|
|
2455
|
+
}
|
|
2456
|
+
else if (targetLayer === 4) {
|
|
2457
|
+
// v7.2.0+: Layer 4 拆分子层或单文档深度分析
|
|
2458
|
+
const deepDoc = options?.deep;
|
|
2459
|
+
const subLayerTarget = progress.subLayer?.next || '4a';
|
|
2460
|
+
if (deepDoc) {
|
|
2461
|
+
// --deep 模式:单文档深度分析
|
|
2462
|
+
const outlinePath = (0, path_1.join)(process.cwd(), '.speccore', 'cache', `deep-outline-${deepDoc.replace(/\//g, '-')}.md`);
|
|
2463
|
+
const hasOutline = await (0, fs_extra_1.pathExists)(outlinePath);
|
|
2464
|
+
if (options?.iterative && !hasOutline) {
|
|
2465
|
+
// 迭代模式第一步:输出大纲
|
|
2466
|
+
prompt += `## 🎯 Layer 4 — 单文档深度分析: ${deepDoc}(大纲阶段)\n\n`;
|
|
2467
|
+
prompt += `> ⚠️ **当前阶段**: 你只需要输出 **${deepDoc} 的文档大纲**。\n`;
|
|
2468
|
+
prompt += `> 不要写详细内容,只输出章节结构 + 每个章节的一句话说明。\n\n`;
|
|
2469
|
+
prompt += `**强制输入**: \n`;
|
|
2470
|
+
prompt += `- Read Layer 1 的所有 _INDEX.md\n`;
|
|
2471
|
+
prompt += `- Read Layer 2 的 _ASSOCIATION.md + _MODULES.md\n`;
|
|
2472
|
+
prompt += `- Read Layer 3 的功能模块深入文档\n`;
|
|
2473
|
+
prompt += `- Read \`.speccore/cache/structured-data.json\`(API/Entity 结构化数据)\n\n`;
|
|
2474
|
+
prompt += `**输出格式**: \n`;
|
|
2475
|
+
prompt += `\`\`\`markdown\n`;
|
|
2476
|
+
prompt += `# ${deepDoc.replace('.md', '')}\n`;
|
|
2477
|
+
prompt += `\n`;
|
|
2478
|
+
prompt += `## 1. 章节标题\n`;
|
|
2479
|
+
prompt += `> 一句话说明该章节内容\n`;
|
|
2480
|
+
prompt += `\n`;
|
|
2481
|
+
prompt += `## 2. 章节标题\n`;
|
|
2482
|
+
prompt += `> 一句话说明该章节内容\n`;
|
|
2483
|
+
prompt += `\`\`\`\n\n`;
|
|
2484
|
+
prompt += `**下一步**: 大纲输出后,用户会审核修改,然后执行 \`speccore analyze --scope global --layer 4 --deep ${deepDoc} --iterative\` 进入逐节填充阶段。\n`;
|
|
2485
|
+
}
|
|
2486
|
+
else if (options?.iterative && hasOutline) {
|
|
2487
|
+
// 迭代模式第二步:根据大纲逐节填充
|
|
2488
|
+
const outlineContent = await (0, fs_extra_2.readFile)(outlinePath, 'utf-8');
|
|
2489
|
+
prompt += `## 🎯 Layer 4 — 单文档深度分析: ${deepDoc}(逐节填充阶段)\n\n`;
|
|
2490
|
+
prompt += `> ⚠️ **当前阶段**: 根据已确认的大纲,逐节填充详细内容。\n`;
|
|
2491
|
+
prompt += `> 每次只填充 **一节**,确保深度和质量。\n\n`;
|
|
2492
|
+
prompt += `**已确认大纲**: \n`;
|
|
2493
|
+
prompt += outlineContent.slice(0, 2000); // 限制长度
|
|
2494
|
+
prompt += `\n\n`;
|
|
2495
|
+
prompt += `**强制输入**: \n`;
|
|
2496
|
+
prompt += `- Read Layer 1-3 的所有产物\n`;
|
|
2497
|
+
prompt += `- Read \`.speccore/cache/structured-data.json\`\n`;
|
|
2498
|
+
prompt += `- 如涉及代码细节,Read 相关源码文件\n\n`;
|
|
2499
|
+
prompt += `**深度要求**: \n`;
|
|
2500
|
+
prompt += `- 不要写"待导入"、"待补充"等占位内容\n`;
|
|
2501
|
+
prompt += `- 每个表格必须有真实数据\n`;
|
|
2502
|
+
prompt += `- 每个结论必须有证据(引用文件名/类名/方法名)\n`;
|
|
2503
|
+
prompt += `- 必须包含 Mermaid 图表(如适用)\n\n`;
|
|
2504
|
+
prompt += `**输出**: 只输出当前节的完整内容(不是整份文档)\n`;
|
|
2505
|
+
}
|
|
2506
|
+
else {
|
|
2507
|
+
// 非迭代模式:一次性输出完整文档
|
|
2508
|
+
prompt += `## 🎯 Layer 4 — 单文档深度分析: ${deepDoc}\n\n`;
|
|
2509
|
+
prompt += `> ⚠️ **专注约束**: 你只生成 **${deepDoc}** 这一份文档,不要生成其他文档。\n`;
|
|
2510
|
+
prompt += `> 你必须深入分析,不要写框架/占位符。每节必须有实质性内容。\n\n`;
|
|
2511
|
+
prompt += `**强制输入**: \n`;
|
|
2512
|
+
prompt += `- Read Layer 1 的所有 _INDEX.md(获取源码结构索引)\n`;
|
|
2513
|
+
prompt += `- Read Layer 2 的 _ASSOCIATION.md + _MODULES.md(获取跨端关联)\n`;
|
|
2514
|
+
prompt += `- Read Layer 3 的功能模块深入文档(获取详细分析结果)\n`;
|
|
2515
|
+
prompt += `- Read \`.speccore/cache/structured-data.json\`(API/Entity 结构化数据)\n`;
|
|
2516
|
+
prompt += `- 如果涉及代码细节,直接 Read 相关源码文件,引用具体代码片段\n\n`;
|
|
2517
|
+
prompt += `**深度要求**: \n`;
|
|
2518
|
+
prompt += `- 不要写"待导入"、"待补充"、"示例"等占位内容\n`;
|
|
2519
|
+
prompt += `- 每个表格必须有真实数据(从 Layer 1-3 提取)\n`;
|
|
2520
|
+
prompt += `- 每个结论必须有证据(引用具体文件名/类名/方法名)\n`;
|
|
2521
|
+
prompt += `- 如果信息不足,明确标注"信息不足: 需要读取 xxx 文件"\n`;
|
|
2522
|
+
prompt += `- 必须包含 Mermaid 图表(如适用)\n\n`;
|
|
2523
|
+
prompt += `**输出**: 只输出 \`${deepDoc}\` 的完整内容\n`;
|
|
2524
|
+
}
|
|
2525
|
+
}
|
|
2526
|
+
else {
|
|
2527
|
+
// 子层模式:每次只生成一个子层
|
|
2528
|
+
prompt += `## 🌍 Layer 4: 全局汇总 — 子层 ${subLayerTarget}/4\n\n`;
|
|
2529
|
+
prompt += `> ⚠️ **专注约束**: 你当前只执行 **子层 ${subLayerTarget}**,不要生成其他子层的文档。\n`;
|
|
2530
|
+
prompt += `> 子层完成后执行: \`speccore analyze --scope global --layer 4\` 进入下一子层。\n\n`;
|
|
2531
|
+
prompt += `**强制输入**: \n`;
|
|
2532
|
+
prompt += `- Read Layer 1 的所有 _INDEX.md\n`;
|
|
2533
|
+
prompt += `- Read Layer 2 的 _ASSOCIATION.md + _MODULES.md\n`;
|
|
2534
|
+
prompt += `- Read Layer 3 的功能模块深入文档\n\n`;
|
|
2535
|
+
if (subLayerTarget === '4a') {
|
|
2536
|
+
prompt += `**子层 4a: 产品视角文档(2-3 份)**\n`;
|
|
2537
|
+
prompt += `1. \`requirements/REQUIREMENT.md\` — 全局需求总纲\n`;
|
|
2538
|
+
prompt += ` - 产品愿景、目标用户画像、核心场景地图\n`;
|
|
2539
|
+
prompt += ` - 按业务场景组织:用户故事 → 操作流程 → 业务规则 → 边界条件 → 验收标准\n`;
|
|
2540
|
+
prompt += ` - 功能优先级矩阵(P0/P1/P2)\n`;
|
|
2541
|
+
prompt += ` - 必须从 Layer 3 的功能模块分析中提取真实内容,不要臆造\n`;
|
|
2542
|
+
prompt += `2. 各前端端 \`requirements/{端}/REQUIREMENT.md\` — 只生成已有前端端的需求\n`;
|
|
2543
|
+
prompt += ` - 信息架构、用户旅程、页面清单、交互设计\n`;
|
|
2544
|
+
prompt += ` - 从 Layer 1 的前端 _INDEX.md 提取页面/路由信息\n`;
|
|
2545
|
+
prompt += ` - 从 Layer 3 的模块分析提取交互流程\n`;
|
|
2546
|
+
}
|
|
2547
|
+
else if (subLayerTarget === '4b') {
|
|
2548
|
+
prompt += `**子层 4b: 全局技术核心文档(3-4 份)**\n`;
|
|
2549
|
+
prompt += `1. \`overview/FUNCTION_MAP.md\` — 功能单元 × 端映射表\n`;
|
|
2550
|
+
prompt += ` - 从 Layer 2 的 _MODULES.md 提取功能模块\n`;
|
|
2551
|
+
prompt += ` - 每个功能单元标注:涉及端、核心页面、核心接口、状态枚举\n`;
|
|
2552
|
+
prompt += `2. \`overview/ARCHITECTURE.md\` — 全局架构\n`;
|
|
2553
|
+
prompt += ` - 服务拓扑(从 Layer 1 的后端 _INDEX.md 提取服务名和依赖)\n`;
|
|
2554
|
+
prompt += ` - 数据流(从 Layer 3 的模块分析提取)\n`;
|
|
2555
|
+
prompt += ` - 必须包含 Mermaid architecture diagram\n`;
|
|
2556
|
+
prompt += `3. \`overview/API_CONTRACT.yaml\` — 全局接口契约\n`;
|
|
2557
|
+
prompt += ` - 汇总所有后端端的 API_INVENTORY(从 Layer 1 提取)\n`;
|
|
2558
|
+
prompt += ` - 标注 rate limit、幂等性、版本策略\n`;
|
|
2559
|
+
prompt += `4. \`overview/INTERACTION_MAP.md\` — 跨端交互时序图\n`;
|
|
2560
|
+
prompt += ` - 从 Layer 3 的模块时序图汇总\n`;
|
|
2561
|
+
prompt += ` - 必须包含 Mermaid sequenceDiagram\n`;
|
|
2562
|
+
}
|
|
2563
|
+
else if (subLayerTarget === '4c') {
|
|
2564
|
+
prompt += `**子层 4c: 全局技术扩展文档(4-5 份)**\n`;
|
|
2565
|
+
prompt += `1. \`overview/SECURITY_AUDIT.md\` — 安全审计\n`;
|
|
2566
|
+
prompt += `2. \`overview/PERFORMANCE_BASELINE.md\` — 性能基线\n`;
|
|
2567
|
+
prompt += `3. \`overview/DATA_FLOW.md\` — 数据流与隐私\n`;
|
|
2568
|
+
prompt += `4. \`overview/DEPLOYMENT.md\` — 部署运维\n`;
|
|
2569
|
+
prompt += `5. \`overview/CONSISTENCY_CHECK.md\` — 一致性校验\n`;
|
|
2570
|
+
prompt += ` - 从 Layer 2 的关联分析提取不一致项\n`;
|
|
2571
|
+
}
|
|
2572
|
+
else if (subLayerTarget === '4d') {
|
|
2573
|
+
prompt += `**子层 4d: 各端技术文档**\n`;
|
|
2574
|
+
prompt += `后端端(每端 9 项,但本次只生成核心 3 项,其余后续补充):\n`;
|
|
2575
|
+
prompt += `1. \`API_INVENTORY.md\` — 从 Layer 1 的 _INDEX.md 提取完整接口清单\n`;
|
|
2576
|
+
prompt += `2. \`DATA_MODEL.md\` — 从 Layer 1 的 Entity 目录 + Layer 3 的模块分析提取\n`;
|
|
2577
|
+
prompt += `3. \`BUSINESS_RULES.md\` — 从 Layer 3 的模块分析提取业务规则\n`;
|
|
2578
|
+
prompt += `前端端(每端 9 项,但本次只生成核心 3 项):\n`;
|
|
2579
|
+
prompt += `1. \`UI_FLOW.md\` — 从 Layer 1 的路由 + Layer 3 的模块分析提取\n`;
|
|
2580
|
+
prompt += `2. \`API_CALL_MAP.md\` — 从 Layer 1 的 API 调用提取\n`;
|
|
2581
|
+
prompt += `3. \`STATE_MANAGEMENT.md\` — 从 Layer 1 的 store 目录提取\n`;
|
|
2582
|
+
}
|
|
2583
|
+
prompt += `\n**质量要求**: \n`;
|
|
2584
|
+
prompt += `- 禁止写"待导入"、"待补充"等占位符\n`;
|
|
2585
|
+
prompt += `- 每个表格必须有真实数据(从 Layer 1-3 提取)\n`;
|
|
2586
|
+
prompt += `- 如果信息不足,明确标注"信息不足: 需要读取 xxx"\n`;
|
|
2587
|
+
}
|
|
2588
|
+
}
|
|
2589
|
+
prompt += `\n## 📝 写入方式\n`;
|
|
2590
|
+
prompt += `使用 \`speccore analyze --apply '{"文件路径":"内容"}' --scope global\` 写入。\n`;
|
|
2591
|
+
prompt += `- platforms/ 和 requirements/ 下的文件按原路径写(如 \`platforms/backend/_INDEX.md\`)\n`;
|
|
2592
|
+
prompt += `- overview/ 下的文件写纯文件名即可(如 \`ARCHITECTURE.md\` 自动路由到 overview/)\n`;
|
|
2593
|
+
prompt += `- PATTERNS/ 下的文件写 \`PATTERNS/{分类}/{模式名}.md\`\n`;
|
|
2100
2594
|
return await injectGraphSummary(prompt);
|
|
2101
2595
|
}
|
|
2102
2596
|
const docs = [
|
|
@@ -2445,6 +2939,18 @@ sequenceDiagram
|
|
|
2445
2939
|
}
|
|
2446
2940
|
}
|
|
2447
2941
|
}
|
|
2942
|
+
// v7.2.0+: 细粒度分析 — 只生成指定文档的指定功能
|
|
2943
|
+
if (options?.docName && options?.featureName && !isGlobal) {
|
|
2944
|
+
const targetDoc = options.docName.endsWith('.md') ? options.docName : `${options.docName}.md`;
|
|
2945
|
+
const filtered = taskDocs.filter(([n]) => n === targetDoc || n === options.docName);
|
|
2946
|
+
if (filtered.length > 0) {
|
|
2947
|
+
taskDocs = filtered;
|
|
2948
|
+
}
|
|
2949
|
+
else {
|
|
2950
|
+
// 不在默认文档列表中,创建单文档分析任务
|
|
2951
|
+
taskDocs = [[targetDoc, `# ${targetDoc.replace('.md', '')} — ${options.featureName}\n\n> ${iter} | ${now}\n> 分析范围: 仅「${options.featureName}」功能单元\n\n## 写作要求\n针对「${options.featureName}」进行深入分析,基于需求文档和代码上下文补充详细内容。\n`]];
|
|
2952
|
+
}
|
|
2953
|
+
}
|
|
2448
2954
|
// ── 用户自定义模板集成(v6.45.0+)──
|
|
2449
2955
|
const templateLevel = isGlobal ? 'global' : (isTask ? 'task' : 'iteration');
|
|
2450
2956
|
const userTemplates = await loadUserTemplates(templateLevel, isTask ? taskType : undefined, ctx.platform);
|
|
@@ -2470,6 +2976,51 @@ sequenceDiagram
|
|
|
2470
2976
|
techDoc[1] = `# 技术架构(跨端全局)\n\n> ${iter}\n\n## 写作要求\n撰写整体技术架构,覆盖所有端的交互关系:\n- 系统整体分层设计(各端在架构中的位置)\n- 跨端交互协议(前端↔后端通信方式、数据流向)\n- 中间件选型(缓存、消息队列、网关等)\n- 数据库整体设计(核心表结构、ER 关系)\n- 技术栈选型及理由\n`;
|
|
2471
2977
|
}
|
|
2472
2978
|
let prompt = `\n# 任务: ${command}${task} (${taskDocs.length}个文档 · ${isTask ? `类型:${taskType}` : '迭代全量'}${ctx.phase ? ` · Phase ${ctx.phase}` : ''})\n\n`;
|
|
2979
|
+
// v7.2.0+: 迭代分析代码关联 — 注入结构化数据和语义定位上下文
|
|
2980
|
+
if (!isGlobal && ctx.withCode && ctx.iteration && ctx.iteration !== 'GLOBAL') {
|
|
2981
|
+
try {
|
|
2982
|
+
// 1. 提取结构化数据(如果还没有)
|
|
2983
|
+
const structuredDataPath = (0, path_1.join)(process.cwd(), '.speccore', 'cache', 'structured-data.json');
|
|
2984
|
+
if (!(await (0, fs_extra_1.pathExists)(structuredDataPath))) {
|
|
2985
|
+
const constitutionPath = (0, path_1.join)(process.cwd(), '.speccore', 'CONSTITUTION.md');
|
|
2986
|
+
let sourcePaths = ['src'];
|
|
2987
|
+
if (await (0, fs_extra_1.pathExists)(constitutionPath)) {
|
|
2988
|
+
const content = await (0, fs_extra_2.readFile)(constitutionPath, 'utf-8');
|
|
2989
|
+
const match = content.match(/源码路径[\s\S]*?\n\s*-\s*`?([^`\n]+)`?/g);
|
|
2990
|
+
if (match) {
|
|
2991
|
+
sourcePaths = match.map(m => m.replace(/.*-\s*`?/, '').replace(/`?$/, '').trim()).filter(Boolean);
|
|
2992
|
+
}
|
|
2993
|
+
}
|
|
2994
|
+
const { extractStructuredData } = await Promise.resolve().then(() => __importStar(require('../core/structured-extractor')));
|
|
2995
|
+
await extractStructuredData(process.cwd(), sourcePaths);
|
|
2996
|
+
}
|
|
2997
|
+
// 2. 注入结构化数据摘要到 Prompt
|
|
2998
|
+
if (await (0, fs_extra_1.pathExists)(structuredDataPath)) {
|
|
2999
|
+
const data = await (0, fs_extra_2.readFile)(structuredDataPath, 'utf-8');
|
|
3000
|
+
const structured = JSON.parse(data);
|
|
3001
|
+
const stats = structured.stats || {};
|
|
3002
|
+
prompt += `## 📊 代码结构化数据(自动提取)\n\n`;
|
|
3003
|
+
prompt += `> 已扫描项目源码,提取以下结构化信息供分析参考:\n\n`;
|
|
3004
|
+
prompt += `- API 接口: ${stats.totalApis || 0} 个\n`;
|
|
3005
|
+
prompt += `- 数据实体: ${stats.totalEntities || 0} 个\n`;
|
|
3006
|
+
prompt += `- 页面路由: ${stats.totalRoutes || 0} 个\n`;
|
|
3007
|
+
prompt += `- 前端组件: ${stats.totalComponents || 0} 个\n`;
|
|
3008
|
+
prompt += `- 扫描文件: ${stats.totalFiles || 0} 个\n\n`;
|
|
3009
|
+
prompt += `> 💡 分析技术方案时,可参考 \`.speccore/cache/structured-data.json\` 中的 API/Entity/Route/Component 清单\n`;
|
|
3010
|
+
prompt += `> 如需了解具体实现,直接 Read 对应源码文件\n\n`;
|
|
3011
|
+
}
|
|
3012
|
+
// 3. 语义定位:如果指定了功能名,自动关联上下文
|
|
3013
|
+
if (options?.featureName) {
|
|
3014
|
+
const { buildFeatureContext, buildFeatureContextPrompt } = await Promise.resolve().then(() => __importStar(require('../core/semantic-locator')));
|
|
3015
|
+
const iterDir = await (0, context_1.getIterationDir)(ctx.iteration);
|
|
3016
|
+
const featureCtx = await buildFeatureContext(process.cwd(), iterDir, options.featureName, options.docName);
|
|
3017
|
+
prompt += buildFeatureContextPrompt(featureCtx);
|
|
3018
|
+
}
|
|
3019
|
+
}
|
|
3020
|
+
catch (e) {
|
|
3021
|
+
logger_1.logger.warn(` ⚠️ 代码关联注入失败: ${e.message}`);
|
|
3022
|
+
}
|
|
3023
|
+
}
|
|
2473
3024
|
// v6.80.0+: 注入需求质量上下文(迭代级分析时)
|
|
2474
3025
|
if (!isGlobal && !isTask && ctx.iteration && ctx.iteration !== 'GLOBAL') {
|
|
2475
3026
|
try {
|
|
@@ -2518,7 +3069,7 @@ sequenceDiagram
|
|
|
2518
3069
|
prompt += `## 🚨 最高优先级警告(违反将导致分析失败)\n\n`;
|
|
2519
3070
|
prompt += `### ⛔ 绝对禁止创建任何额外目录\n`;
|
|
2520
3071
|
prompt += `- ❌ **错误行为**:创建 020-specs/1001/、020-specs/1002/、020-specs/错误码/、020-specs/工程标识/ 等垃圾目录\n`;
|
|
2521
|
-
prompt += `- ✅ **正确行为**:只使用 CLI 预创建的
|
|
3072
|
+
prompt += `- ✅ **正确行为**:只使用 CLI 预创建的 overview/ 和 {端名}/ 目录,不要手动 mkdir 或 Write 到不存在的目录\n`;
|
|
2522
3073
|
prompt += `- ⚠️ **后果**:如果创建额外目录,会导致后续 split/execute 命令找不到文件,整个工作流失败\n\n`;
|
|
2523
3074
|
prompt += `### 绝对禁止直接用 Write 工具写文件\n`;
|
|
2524
3075
|
prompt += `- ❌ **错误行为**:Write("020-specs/overview/ANALYSIS.md", content) 或直接 Write 到任何路径\n`;
|
|
@@ -2528,7 +3079,7 @@ sequenceDiagram
|
|
|
2528
3079
|
prompt += `### ✅ 正确的目录结构\n`;
|
|
2529
3080
|
prompt += `\`\`\`\n`;
|
|
2530
3081
|
prompt += `020-specs/\n`;
|
|
2531
|
-
prompt += `├──
|
|
3082
|
+
prompt += `├── overview/ ← REQUIREMENT.md, ANALYSIS.md, DEPS.md(跨端通用)\n`;
|
|
2532
3083
|
prompt += `├── admin-web/ ← TECH.md, TEST.md, UI_SPEC.md(Admin 端专属)\n`;
|
|
2533
3084
|
prompt += `├── booking-service/ ← TECH.md, TEST.md(后端服务专属)\n`;
|
|
2534
3085
|
prompt += `├── h5-mobile/ ← TECH.md, TEST.md, UI_SPEC.md(H5 端专属)\n`;
|
|
@@ -2640,9 +3191,9 @@ sequenceDiagram
|
|
|
2640
3191
|
prompt += `2. 读取全局层产物(建立全局视角,重要)\n`;
|
|
2641
3192
|
prompt += ` 在读取迭代需求之前,先 Read 全局层已有产物,了解系统当前状态:\n`;
|
|
2642
3193
|
prompt += ` a. Read .speccore/GLOBAL/requirements/REQUIREMENT.md → 系统已有功能清单\n`;
|
|
2643
|
-
prompt += ` b. Read .speccore/GLOBAL/
|
|
2644
|
-
prompt += ` c. Read .speccore/GLOBAL/
|
|
2645
|
-
prompt += ` d. Read .speccore/GLOBAL/
|
|
3194
|
+
prompt += ` b. Read .speccore/GLOBAL/overview/FUNCTION_MAP.md → 已有功能单元和涉及端\n`;
|
|
3195
|
+
prompt += ` c. Read .speccore/GLOBAL/overview/API_CONTRACT.yaml → 已有接口契约\n`;
|
|
3196
|
+
prompt += ` d. Read .speccore/GLOBAL/overview/ARCHITECTURE.md → 全局架构(如有)\n`;
|
|
2646
3197
|
prompt += ` e. Read .speccore/GLOBAL/platforms/{相关端}/_INDEX.md → 各端已有页面和接口索引\n`;
|
|
2647
3198
|
prompt += ` f. Read .speccore/GLOBAL/platforms/_shared/_ASSOCIATION.md → 前后端关联矩阵(如有)\n`;
|
|
2648
3199
|
prompt += ` g. Read .speccore/GLOBAL/platforms/_shared/_MODULES.md → 功能模块候选(如有)\n`;
|
|
@@ -2711,7 +3262,7 @@ sequenceDiagram
|
|
|
2711
3262
|
// v6.70.0+: 跨端功能映射表(FUNCTION_MAP.md)
|
|
2712
3263
|
// v6.71.3+: 增加与全局层关联分析
|
|
2713
3264
|
prompt += `7a. **迭代需求与全局层关联分析(重要)**:在生成功能模块清单时,必须对比全局层产物\n`;
|
|
2714
|
-
prompt += ` - 对比迭代需求中的功能模块 vs .speccore/GLOBAL/
|
|
3265
|
+
prompt += ` - 对比迭代需求中的功能模块 vs .speccore/GLOBAL/overview/FUNCTION_MAP.md 中的功能单元\n`;
|
|
2715
3266
|
prompt += ` - 标注每个功能模块的「全局对比」类型(新增/扩展/重构/复用)\n`;
|
|
2716
3267
|
prompt += ` - 识别冲突:如迭代需求修改了全局层已有接口的字段/路径 → 在 RISK.md 中标注\n`;
|
|
2717
3268
|
prompt += ` - 识别依赖:如迭代的新功能依赖全局层的某个功能 → 在 FUNCTION_MAP.md「依赖任务」中标注\n\n`;
|
|
@@ -2773,7 +3324,7 @@ sequenceDiagram
|
|
|
2773
3324
|
prompt += ` - MONITOR.md(监控指标)\n`;
|
|
2774
3325
|
prompt += ` - **禁止**:不要创建 020-specs/ 下的任何额外子目录(如数字编号、中文名称等)\n`;
|
|
2775
3326
|
prompt += ` - **禁止直接用 Write 工具写文件到 020-specs/**:必须通过 \`speccore analyze --apply '{"文件名":"内容"}' -I ${iter}\` 写入\n`;
|
|
2776
|
-
prompt += ` - ⚠️ 直接 Write 会导致目录结构错误(所有文件扁平在根目录),必须走 --apply 让 CLI 自动路由到
|
|
3327
|
+
prompt += ` - ⚠️ 直接 Write 会导致目录结构错误(所有文件扁平在根目录),必须走 --apply 让 CLI 自动路由到 overview/ 或 {端名}/ 子目录\n`;
|
|
2777
3328
|
if (ctx.phase !== '1') {
|
|
2778
3329
|
// 端专业性约束只在默认模式(全量)中输出
|
|
2779
3330
|
prompt += `\n## ⚠️ 端专业性约束\n`;
|
|
@@ -2864,7 +3415,7 @@ sequenceDiagram
|
|
|
2864
3415
|
prompt += `\n## ⚠️ 重要:Phase 1 完成后的下一步\n\n`;
|
|
2865
3416
|
prompt += `当你通过 --apply 写入所有综合文档后,CLI 会检测到项目有多个端(≥2 个端)。\n`;
|
|
2866
3417
|
prompt += `**此时你需要主动询问用户**:\n\n`;
|
|
2867
|
-
prompt += `"✅ Phase 1 已完成,生成了
|
|
3418
|
+
prompt += `"✅ Phase 1 已完成,生成了 overview/ 的综合文档。\n`;
|
|
2868
3419
|
prompt += `检测到项目有 ${'{'}端列表{'}'} 个端,需要继续执行 Phase 2 生成各端专属文档吗?\n`;
|
|
2869
3420
|
prompt += `请确认:输入 '继续' 或 'yes' 以执行 Phase 2"\n\n`;
|
|
2870
3421
|
prompt += `**如果用户确认继续**,你需要执行:\n`;
|