speccore 8.3.276 → 8.3.278
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/README.en.md +5 -0
- package/README.md +5 -0
- package/dist/commands/about.d.ts.map +1 -1
- package/dist/commands/about.js +9 -0
- package/dist/commands/about.js.map +1 -1
- package/dist/commands/analyze.d.ts.map +1 -1
- package/dist/commands/analyze.js +30 -5
- package/dist/commands/analyze.js.map +1 -1
- package/dist/commands/doc2spec.d.ts.map +1 -1
- package/dist/commands/doc2spec.js +53 -12
- package/dist/commands/doc2spec.js.map +1 -1
- package/dist/commands/help.js +14 -3
- package/dist/commands/help.js.map +1 -1
- package/dist/commands/iteration/taskify.d.ts.map +1 -1
- package/dist/commands/iteration/taskify.js +55 -12
- package/dist/commands/iteration/taskify.js.map +1 -1
- package/dist/core/feature-metadata.d.ts.map +1 -1
- package/dist/core/feature-metadata.js +7 -1
- package/dist/core/feature-metadata.js.map +1 -1
- package/dist/core/global-counters.d.ts.map +1 -1
- package/dist/core/global-counters.js +8 -2
- package/dist/core/global-counters.js.map +1 -1
- package/dist/core/spec-paths.d.ts.map +1 -1
- package/dist/core/spec-paths.js +5 -0
- package/dist/core/spec-paths.js.map +1 -1
- package/dist/core/spec-skeleton.d.ts +26 -1
- package/dist/core/spec-skeleton.d.ts.map +1 -1
- package/dist/core/spec-skeleton.js +110 -3
- package/dist/core/spec-skeleton.js.map +1 -1
- package/dist/utils/task-utils.d.ts +3 -1
- package/dist/utils/task-utils.d.ts.map +1 -1
- package/dist/utils/task-utils.js +4 -2
- package/dist/utils/task-utils.js.map +1 -1
- package/package.json +1 -1
|
@@ -640,8 +640,10 @@ async function iterationTaskifyCommand(options) {
|
|
|
640
640
|
}
|
|
641
641
|
const extracted = extractJsonArray(options.response);
|
|
642
642
|
// 尝试解析 AI 返回的 JSON Task 列表
|
|
643
|
+
let jsonParsed = false;
|
|
643
644
|
try {
|
|
644
645
|
const tasks = JSON.parse(extracted.json);
|
|
646
|
+
jsonParsed = true;
|
|
645
647
|
if (Array.isArray(tasks)) {
|
|
646
648
|
const allPlatforms = await detectPlatforms(iterDirFull);
|
|
647
649
|
const sections = [];
|
|
@@ -716,8 +718,12 @@ async function iterationTaskifyCommand(options) {
|
|
|
716
718
|
section.functionalUnit = task.functionalUnit;
|
|
717
719
|
if (task.reason)
|
|
718
720
|
section._reason = task.reason;
|
|
719
|
-
if (taskScopePlatforms.length > 0)
|
|
721
|
+
if (taskScopePlatforms.length > 0) {
|
|
720
722
|
section._scopePlatforms = taskScopePlatforms;
|
|
723
|
+
// v8.3.277: 标记 AI 显式声明了 scope(用于区分「推断出的端」与「AI 明确意图」,
|
|
724
|
+
// 显式 scope 不自动补后端,推断 scope 保留自动补后端)
|
|
725
|
+
section._explicitScope = true;
|
|
726
|
+
}
|
|
721
727
|
sections.push(section);
|
|
722
728
|
}
|
|
723
729
|
// v8.3.223+: 过滤骨架质量标准标题泄漏为功能单元名(spec-skeleton 注入的引导标题
|
|
@@ -980,8 +986,25 @@ async function iterationTaskifyCommand(options) {
|
|
|
980
986
|
}
|
|
981
987
|
// 逐任务交互确认
|
|
982
988
|
const createdSections = [];
|
|
989
|
+
const moduleFilter = (unit) => {
|
|
990
|
+
if (scopeModules.length === 0)
|
|
991
|
+
return true;
|
|
992
|
+
const u = (unit || '').toLowerCase();
|
|
993
|
+
return scopeModules.some((m) => {
|
|
994
|
+
const mf = m.toLowerCase();
|
|
995
|
+
return u === mf || u.includes(mf) || mf.includes(u);
|
|
996
|
+
});
|
|
997
|
+
};
|
|
998
|
+
let filteredCount = 0;
|
|
983
999
|
for (let i = 0; i < sections.length; i++) {
|
|
984
1000
|
const sec = sections[i];
|
|
1001
|
+
// v8.3.277: --modules 指定范围重建时,只创建匹配模块的任务,其余跳过(避免重复)
|
|
1002
|
+
const secUnit = sec.functionalUnit || sec.name || '';
|
|
1003
|
+
if (!moduleFilter(secUnit)) {
|
|
1004
|
+
filteredCount++;
|
|
1005
|
+
logger_1.logger.info(` ⏭️ 跳过「${sec.name}」(不属于 --modules 指定范围,已保留旧任务)`);
|
|
1006
|
+
continue;
|
|
1007
|
+
}
|
|
985
1008
|
const complexity = sec._complexity || {};
|
|
986
1009
|
const taskType = sec._taskType || 'feature';
|
|
987
1010
|
const deps = (tasks[i].dependencies || []);
|
|
@@ -1032,7 +1055,7 @@ async function iterationTaskifyCommand(options) {
|
|
|
1032
1055
|
// 输出下一步操作标记:引导宿主 AI 填充任务级 00-specs/ 文档(analyze --task 已删除)
|
|
1033
1056
|
outputAnalyzeTaskHints(iter, createdSections);
|
|
1034
1057
|
}
|
|
1035
|
-
logger_1.logger.success(`✅ 创建了 ${createdSections.length}/${sections.length} 个任务(${sections.length - createdSections.length} 个跳过)`);
|
|
1058
|
+
logger_1.logger.success(`✅ 创建了 ${createdSections.length}/${sections.length - filteredCount} 个任务(${sections.length - createdSections.length - filteredCount} 个跳过)`);
|
|
1036
1059
|
// v8.3.160+: 检测是否有待拆分的功能模块
|
|
1037
1060
|
if (extracted.pending) {
|
|
1038
1061
|
logger_1.logger.info('');
|
|
@@ -1054,14 +1077,25 @@ async function iterationTaskifyCommand(options) {
|
|
|
1054
1077
|
await (0, fs_extra_1.writeFile)(reqPath, options.response);
|
|
1055
1078
|
}
|
|
1056
1079
|
}
|
|
1057
|
-
catch {
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1080
|
+
catch (e) {
|
|
1081
|
+
if (!jsonParsed) {
|
|
1082
|
+
// JSON 解析失败 → 回退 Markdown(保留原始输出)
|
|
1083
|
+
const reqPath = (0, path_1.join)(iterDir, 'REQUIREMENT.md');
|
|
1084
|
+
const bk = await (0, task_utils_1.backupWithTimestamp)(reqPath);
|
|
1085
|
+
if (bk) {
|
|
1086
|
+
backups.push(bk);
|
|
1087
|
+
logger_1.logger.info(` 📦 已备份: ${bk.split('/').pop()}`);
|
|
1088
|
+
}
|
|
1089
|
+
await (0, fs_extra_1.writeFile)(reqPath, options.response);
|
|
1090
|
+
}
|
|
1091
|
+
else {
|
|
1092
|
+
// 任务创建过程异常 → 必须显式报错,禁止静默吞掉冒充成功
|
|
1093
|
+
logger_1.logger.error(`❌ 任务创建失败: ${e?.message || e}`);
|
|
1094
|
+
if (backups.length > 0)
|
|
1095
|
+
logger_1.logger.info(` 📦 已备份文件仍保留,可回滚: ${backups.join(', ')}`);
|
|
1096
|
+
process.exitCode = 1;
|
|
1097
|
+
return;
|
|
1063
1098
|
}
|
|
1064
|
-
await (0, fs_extra_1.writeFile)(reqPath, options.response);
|
|
1065
1099
|
}
|
|
1066
1100
|
// v7.4.0+: 从 AI 输出中提取疑问并持久化
|
|
1067
1101
|
const extractedQs = (0, questions_1.extractQuestionsFromText)(options.response);
|
|
@@ -2350,8 +2384,12 @@ ${!isBk && docSet.hasComponentTree ? '| COMPONENT_TREE.md | ⏳ | ./COMPONENT_TR
|
|
|
2350
2384
|
}
|
|
2351
2385
|
// 确保至少有 backend 子任务(AI 未输出 backend 时自动补充,仅非 research)
|
|
2352
2386
|
// v6.48.0+:从 CONSTITUTION.md 端列表查找后端端名,不再硬编码 'backend'
|
|
2387
|
+
// v8.3.277: 仅当 AI 未显式声明 scope(_scopePlatforms 缺失/为空)时才自动补充;
|
|
2388
|
+
// scope 已显式给出时尊重 AI 意图,不制造幽灵后端子任务
|
|
2353
2389
|
const hasAnyBackend = taskPlatforms.some((p) => p === 'backend' || p.startsWith('后台') || /-(service|api|server|backend)$/i.test(p));
|
|
2354
|
-
|
|
2390
|
+
// AI 显式声明 scope 时尊重意图,不自动补后端;推断出的 scope 保留自动补充
|
|
2391
|
+
const shouldAutoBackend = !section._explicitScope;
|
|
2392
|
+
if (!isResearch && !hasAnyBackend && shouldAutoBackend) {
|
|
2355
2393
|
// 从端列表查找第一个后端端名作为 fallback(v6.48.0+)
|
|
2356
2394
|
const allPlatforms = await (0, spec_paths_1.parsePlatformList)();
|
|
2357
2395
|
const fallbackBackend = allPlatforms.find(p => p === 'backend' || p.startsWith('后台') || /-(service|api|server|backend)$/i.test(p)) || 'backend';
|
|
@@ -2478,6 +2516,8 @@ ${taskPlatforms.map((p) => `| ${p} | 待补充 |`).join('\n')}
|
|
|
2478
2516
|
}
|
|
2479
2517
|
async function updateProjectGraph(iterationDir, sections) {
|
|
2480
2518
|
const graphPath = (0, path_1.join)(iterationDir, '000-overview', 'PROJECT_GRAPH.md');
|
|
2519
|
+
// 防护:000-overview/ 可能尚不存在(全新迭代首次拆分),写前先建目录
|
|
2520
|
+
await (0, fs_extra_1.ensureDir)((0, path_1.join)(iterationDir, '000-overview'));
|
|
2481
2521
|
let content = '';
|
|
2482
2522
|
if (await (0, fs_extra_1.pathExists)(graphPath)) {
|
|
2483
2523
|
content = await (0, fs_extra_1.readFile)(graphPath, 'utf-8');
|
|
@@ -3082,7 +3122,9 @@ async function readMatrixPlatforms(iterationDir, featureName) {
|
|
|
3082
3122
|
return [];
|
|
3083
3123
|
const entries = await (0, fs_extra_1.readdir)(featuresDir, { withFileTypes: true });
|
|
3084
3124
|
for (const e of entries) {
|
|
3085
|
-
|
|
3125
|
+
// v8.3.277: 排除时间戳备份目录——备份目录名仍包含 featureName,
|
|
3126
|
+
// 读到旧版 _matrix.md 会返回过期的端覆盖声明
|
|
3127
|
+
if (!e.isDirectory() || !e.name.includes(featureName) || (0, task_utils_1.isTimestampBackup)(e.name))
|
|
3086
3128
|
continue;
|
|
3087
3129
|
const matrixPath = (0, path_1.join)(featuresDir, e.name, '_matrix.md');
|
|
3088
3130
|
if (!(await (0, fs_extra_1.pathExists)(matrixPath)))
|
|
@@ -4674,7 +4716,8 @@ async function tryModuleDrivenSplit(iteration, iterationDir, options) {
|
|
|
4674
4716
|
try {
|
|
4675
4717
|
const entries = await (0, fs_extra_1.readdir)(featuresDir, { withFileTypes: true });
|
|
4676
4718
|
for (const entry of entries) {
|
|
4677
|
-
|
|
4719
|
+
// v8.3.277: 排除时间戳备份目录(unitize overwrite 产生的 xxx-20260924083058)
|
|
4720
|
+
if (entry.isDirectory() && !entry.name.startsWith('.') && !(0, task_utils_1.isTimestampBackup)(entry.name)) {
|
|
4678
4721
|
const readmePath = await (0, feature_source_1.resolveUnitSourcePath)((0, path_1.join)(featuresDir, entry.name));
|
|
4679
4722
|
if (await (0, fs_extra_1.pathExists)(readmePath)) {
|
|
4680
4723
|
let unitPlatforms = [...allPlatforms];
|