speccore 7.5.1 → 8.0.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/dist/commands/analyze.d.ts.map +1 -1
- package/dist/commands/analyze.js +73 -47
- package/dist/commands/analyze.js.map +1 -1
- package/dist/commands/iteration/split.d.ts.map +1 -1
- package/dist/commands/iteration/split.js +9 -4
- package/dist/commands/iteration/split.js.map +1 -1
- package/dist/core/spec-skeleton.d.ts +72 -0
- package/dist/core/spec-skeleton.d.ts.map +1 -0
- package/dist/core/spec-skeleton.js +427 -0
- package/dist/core/spec-skeleton.js.map +1 -0
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"analyze.d.ts","sourceRoot":"","sources":["../../src/commands/analyze.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"analyze.d.ts","sourceRoot":"","sources":["../../src/commands/analyze.ts"],"names":[],"mappings":"AAiEA,MAAM,WAAW,cAAc;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,QAAQ,GAAG,WAAW,GAAG,MAAM,CAAC;IACxC,KAAK,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAC;IACpC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,YAAY,CAAC,EAAE,OAAO,CAAC;IAEvB,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB,WAAW,CAAC,EAAE,OAAO,CAAC;IAEtB,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,wBAAsB,cAAc,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CA2rC3E"}
|
package/dist/commands/analyze.js
CHANGED
|
@@ -59,6 +59,7 @@ const questions_1 = require("../core/questions");
|
|
|
59
59
|
const platform_registry_1 = require("../core/platform-registry");
|
|
60
60
|
const index_guard_1 = require("../core/index-guard");
|
|
61
61
|
const spec_paths_1 = require("../core/spec-paths");
|
|
62
|
+
const spec_skeleton_1 = require("../core/spec-skeleton");
|
|
62
63
|
const unified_retrieval_1 = require("../core/unified-retrieval");
|
|
63
64
|
const pipeline_engine_1 = require("../core/pipeline-engine");
|
|
64
65
|
const change_detection_1 = require("../core/change-detection");
|
|
@@ -523,10 +524,23 @@ async function analyzeCommand(options) {
|
|
|
523
524
|
else {
|
|
524
525
|
const iterForDirs = options.iteration || await (0, context_1.getDefaultIteration)();
|
|
525
526
|
if (iterForDirs) {
|
|
526
|
-
//
|
|
527
|
-
const
|
|
528
|
-
|
|
529
|
-
await
|
|
527
|
+
// v8.0.0+: 骨架优先架构 — CLI 预创建所有文件,AI 只覆盖内容
|
|
528
|
+
const iterDirForSkeleton = await (0, context_1.getIterationDir)(iterForDirs);
|
|
529
|
+
const specDir = (0, path_1.join)(iterDirForSkeleton, '020-specs');
|
|
530
|
+
const platforms = await (0, spec_paths_1.parsePlatformList)();
|
|
531
|
+
const iterName = iterForDirs;
|
|
532
|
+
// v7.4.3+ 兼容:先运行一次旧版 sanitize 迁移遗留文件(一次性)
|
|
533
|
+
await sanitizeSpecDirectories(iterDirForSkeleton);
|
|
534
|
+
// v8.0.0+: 计算 manifest 并生成骨架
|
|
535
|
+
const phase = options.phase;
|
|
536
|
+
const manifest = (0, spec_skeleton_1.computeAnalyzeManifest)(platforms, phase, iterName);
|
|
537
|
+
const skeletonResult = await (0, spec_skeleton_1.generateSkeleton)(specDir, manifest);
|
|
538
|
+
if (skeletonResult.created.length > 0) {
|
|
539
|
+
logger_1.logger.info(`🦴 已创建 ${skeletonResult.created.length} 个文件骨架: ${skeletonResult.created.join(', ')}`);
|
|
540
|
+
}
|
|
541
|
+
if (skeletonResult.skipped.length > 0) {
|
|
542
|
+
logger_1.logger.info(`✅ 已有 ${skeletonResult.skipped.length} 个文件保留原内容`);
|
|
543
|
+
}
|
|
530
544
|
}
|
|
531
545
|
}
|
|
532
546
|
}
|
|
@@ -1265,9 +1279,10 @@ async function analyzeCommand(options) {
|
|
|
1265
1279
|
}
|
|
1266
1280
|
}
|
|
1267
1281
|
/**
|
|
1268
|
-
*
|
|
1269
|
-
*
|
|
1282
|
+
* @deprecated v8.0.0+ 已被 computeAnalyzeManifest + generateSkeleton 替代
|
|
1283
|
+
* 保留用于旧项目迁移参考,新流程不再调用。
|
|
1270
1284
|
*/
|
|
1285
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1271
1286
|
async function preCreateSpecDirectories(iteration) {
|
|
1272
1287
|
const iterDir = await (0, context_1.getIterationDir)(iteration);
|
|
1273
1288
|
const specDir = (0, path_1.join)(iterDir, '020-specs');
|
|
@@ -1804,8 +1819,9 @@ async function injectGraphSummary(prompt) {
|
|
|
1804
1819
|
return prompt;
|
|
1805
1820
|
}
|
|
1806
1821
|
}
|
|
1807
|
-
// ──
|
|
1808
|
-
//
|
|
1822
|
+
// ── @deprecated v8.0.0+ 已被 detectSkeletonProgress 替代 ──
|
|
1823
|
+
// 保留用于旧项目迁移参考,新流程不再调用。
|
|
1824
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1809
1825
|
async function detectIterationDocsStatus(iterDir) {
|
|
1810
1826
|
const specsDir = (0, path_1.join)(iterDir, '020-specs');
|
|
1811
1827
|
const overviewDir = (0, path_1.join)(specsDir, spec_paths_1.GLOBAL_SPECS_DIR);
|
|
@@ -3215,19 +3231,22 @@ sequenceDiagram
|
|
|
3215
3231
|
else if (ctx.phase === '2') {
|
|
3216
3232
|
taskDocs = taskDocs.filter(([n]) => PLATFORM_DOCS.includes(n));
|
|
3217
3233
|
}
|
|
3218
|
-
//
|
|
3219
|
-
//
|
|
3234
|
+
// v8.0.0+: 骨架进度检测 — 每次只生成下一个未填充的文档
|
|
3235
|
+
// 替代 v7.5.0 的 detectIterationDocsStatus(基于文件是否存在)
|
|
3220
3236
|
let perDocStatus = null;
|
|
3221
3237
|
if (!isGlobal && !isTask && !ctx.phase && ctx.iteration && ctx.iteration !== 'GLOBAL') {
|
|
3222
3238
|
try {
|
|
3223
3239
|
const iterDir = await (0, context_1.getIterationDir)(ctx.iteration);
|
|
3224
3240
|
if (iterDir) {
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
|
|
3241
|
+
const specDir = (0, path_1.join)(iterDir, '020-specs');
|
|
3242
|
+
const platforms = await (0, spec_paths_1.parsePlatformList)();
|
|
3243
|
+
const manifest = (0, spec_skeleton_1.computeAnalyzeManifest)(platforms, undefined, ctx.iteration);
|
|
3244
|
+
perDocStatus = await (0, spec_skeleton_1.detectSkeletonProgress)(specDir, manifest);
|
|
3245
|
+
if (perDocStatus.nextUnfilled) {
|
|
3246
|
+
const nextDocName = perDocStatus.nextUnfilled.docName;
|
|
3247
|
+
const nextDocPlatform = perDocStatus.nextUnfilled.platform || null;
|
|
3248
|
+
// 过滤 taskDocs 只保留下一个未填充的文档
|
|
3249
|
+
if (nextDocPlatform && perDocStatus.nextUnfilled.category === 'platform') {
|
|
3231
3250
|
// Phase 2 文档(端专属)
|
|
3232
3251
|
taskDocs = taskDocs.filter(([n]) => n === nextDocName);
|
|
3233
3252
|
ctx.platform = nextDocPlatform;
|
|
@@ -3236,11 +3255,15 @@ sequenceDiagram
|
|
|
3236
3255
|
// Phase 1 文档(overview)
|
|
3237
3256
|
taskDocs = taskDocs.filter(([n]) => n === nextDocName);
|
|
3238
3257
|
}
|
|
3258
|
+
logger_1.logger.info(`🦴 骨架进度: ${perDocStatus.filledCount}/${perDocStatus.totalCount} 已填充,下一个: ${perDocStatus.nextUnfilled.relPath}`);
|
|
3259
|
+
}
|
|
3260
|
+
else if (perDocStatus.filledCount === perDocStatus.totalCount) {
|
|
3261
|
+
logger_1.logger.info(`🎉 所有 ${perDocStatus.totalCount} 个骨架文件均已填充`);
|
|
3239
3262
|
}
|
|
3240
3263
|
}
|
|
3241
3264
|
}
|
|
3242
3265
|
catch (e) {
|
|
3243
|
-
logger_1.logger.debug(
|
|
3266
|
+
logger_1.logger.debug(`骨架进度检测失败(非关键): ${e.message}`);
|
|
3244
3267
|
}
|
|
3245
3268
|
}
|
|
3246
3269
|
// ── 任务级文档覆盖:00-specs/ 使用任务级文档集(v6.44.0+) ──
|
|
@@ -3407,28 +3430,29 @@ sequenceDiagram
|
|
|
3407
3430
|
prompt += `---\n\n`;
|
|
3408
3431
|
}
|
|
3409
3432
|
}
|
|
3410
|
-
// ──
|
|
3411
|
-
prompt += `##
|
|
3412
|
-
prompt +=
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
prompt +=
|
|
3430
|
-
prompt += `-
|
|
3431
|
-
prompt += `-
|
|
3433
|
+
// ── v8.0.0+: 骨架优先架构 — 简洁的文件写入指令 ──
|
|
3434
|
+
prompt += `## 📝 文件写入方式(v8.0.0+ 骨架模式)\n\n`;
|
|
3435
|
+
prompt += `CLI 已预创建所有文件骨架(含占位内容)。请逐个用 Write 工具覆盖为专业分析内容:\n\n`;
|
|
3436
|
+
// 动态生成骨架文件列表
|
|
3437
|
+
try {
|
|
3438
|
+
const iterDir = await (0, context_1.getIterationDir)(iter);
|
|
3439
|
+
if (iterDir) {
|
|
3440
|
+
const platforms = await (0, spec_paths_1.parsePlatformList)();
|
|
3441
|
+
const phase = options?.phase;
|
|
3442
|
+
const manifest = (0, spec_skeleton_1.computeAnalyzeManifest)(platforms, phase, iter);
|
|
3443
|
+
const specDir = (0, path_1.join)(iterDir, '020-specs');
|
|
3444
|
+
prompt += (0, spec_skeleton_1.buildSkeletonFileList)(manifest, specDir);
|
|
3445
|
+
}
|
|
3446
|
+
}
|
|
3447
|
+
catch {
|
|
3448
|
+
// 回退:静态列表
|
|
3449
|
+
prompt += `- \`020-specs/overview/\` 下的 REQUIREMENT.md, ANALYSIS.md, TECH.md, DEPS.md, RISK.md, REVIEW.md, MONITOR.md, FUNCTION_MAP.md\n`;
|
|
3450
|
+
prompt += `- \`020-specs/{端名}/\` 下的 TECH.md, TEST.md, UI_SPEC.md\n\n`;
|
|
3451
|
+
}
|
|
3452
|
+
prompt += `\n**注意**:\n`;
|
|
3453
|
+
prompt += `- 只覆盖上述已存在的文件,不要创建新文件或新目录\n`;
|
|
3454
|
+
prompt += `- 每个文件写入正确路径(如 \`overview/ANALYSIS.md\`,不是根目录的 \`ANALYSIS.md\`)\n`;
|
|
3455
|
+
prompt += `- 写完后用 Read 验证文件内容已替换占位内容\n\n`;
|
|
3432
3456
|
prompt += `## ⚠️ 迭代名称仅为目录标识(重要)\n\n`;
|
|
3433
3457
|
prompt += `- 迭代名称("${iter}")仅为目录标识符,**不代表需求内容**\n`;
|
|
3434
3458
|
prompt += `- **需求文档是唯一事实来源**:所有分析必须 100% 基于 010-requirements/ 下的文档内容\n`;
|
|
@@ -3771,21 +3795,23 @@ sequenceDiagram
|
|
|
3771
3795
|
prompt += `请在生成综合文档后,**直接继续**生成各端专属文档(Phase 2)。\n`;
|
|
3772
3796
|
prompt += `执行命令:\`speccore analyze --prompt -I ${iter} --phase 2\`\n\n`;
|
|
3773
3797
|
}
|
|
3774
|
-
//
|
|
3775
|
-
if (perDocStatus && perDocStatus.
|
|
3776
|
-
const remainingAfterThis = perDocStatus.
|
|
3777
|
-
prompt += `\n## 🔗 逐文档自动链式推进(
|
|
3798
|
+
// v8.0.0+: 逐文档自动链式推进(基于骨架进度)
|
|
3799
|
+
if (perDocStatus && perDocStatus.filledCount < perDocStatus.totalCount) {
|
|
3800
|
+
const remainingAfterThis = perDocStatus.unfilled.length - 1;
|
|
3801
|
+
prompt += `\n## 🔗 逐文档自动链式推进(v8.0.0+ 骨架模式)\n\n`;
|
|
3778
3802
|
prompt += `> 当前为**单文档模式**:本次只生成 1 个文档,写入后立即推进下一个。\n`;
|
|
3779
|
-
prompt += `>
|
|
3803
|
+
prompt += `> 骨架进度: ${perDocStatus.filledCount}/${perDocStatus.totalCount} 已填充,剩余 ${perDocStatus.unfilled.length} 个\n`;
|
|
3804
|
+
prompt += `> 待填充: ${perDocStatus.unfilled.slice(0, 5).join('、')}${perDocStatus.unfilled.length > 5 ? '...' : ''}\n\n`;
|
|
3780
3805
|
if (remainingAfterThis > 0) {
|
|
3781
3806
|
prompt += `**当前文档写入完成后,立即输出以下标记推进下一个文档**:\n\n`;
|
|
3782
3807
|
prompt += `[SPECCORE_EXEC: speccore analyze --prompt -I ${iter}${options?.auto ? ' --auto' : ''}${options?.withCode ? ' --with-code' : ''}]\n\n`;
|
|
3783
|
-
prompt += `> 💡 CLI
|
|
3808
|
+
prompt += `> 💡 CLI 会自动检测下一个未填充的骨架文件并生成对应的 prompt。重复执行直到所有文件填充完毕。\n`;
|
|
3784
3809
|
}
|
|
3785
3810
|
else {
|
|
3786
|
-
prompt += `> 🎉
|
|
3811
|
+
prompt += `> 🎉 这是最后一个骨架文件!填充后所有迭代级分析文档将完整。\n`;
|
|
3787
3812
|
const platforms = await (0, spec_paths_1.parsePlatformList)();
|
|
3788
|
-
|
|
3813
|
+
const hasPlatformUnfilled = perDocStatus.unfilled.some(p => p.includes('/') && !p.startsWith('overview/'));
|
|
3814
|
+
if (platforms.length >= 2 && hasPlatformUnfilled) {
|
|
3789
3815
|
prompt += `> 下一步: 生成各端专属文档 → \`speccore analyze --prompt -I ${iter} --phase 2\`\n`;
|
|
3790
3816
|
}
|
|
3791
3817
|
else {
|