speccore 8.3.248 → 8.3.250
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 +62 -6
- package/dist/commands/analyze.js.map +1 -1
- package/dist/commands/iteration/split.d.ts.map +1 -1
- package/dist/commands/iteration/split.js +87 -7
- package/dist/commands/iteration/split.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"split.d.ts","sourceRoot":"","sources":["../../../src/commands/iteration/split.ts"],"names":[],"mappings":"AA0KA,MAAM,WAAW,qBAAqB;IACpC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,OAAO,CAAC;IAEhB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;IAEhB,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;
|
|
1
|
+
{"version":3,"file":"split.d.ts","sourceRoot":"","sources":["../../../src/commands/iteration/split.ts"],"names":[],"mappings":"AA0KA,MAAM,WAAW,qBAAqB;IACpC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,OAAO,CAAC;IAEhB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;IAEhB,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;AAqLD,wBAAsB,qBAAqB,CAAC,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CAmsCzF"}
|
|
@@ -235,13 +235,33 @@ async function preSplitGate(iterationDir) {
|
|
|
235
235
|
level: 'block',
|
|
236
236
|
});
|
|
237
237
|
// v8.3.221+: 检查全局 ANALYSIS.md(阻塞级——功能分析,骨架说明 analyze 未落盘)
|
|
238
|
+
// v8.3.248+: 模块级回退——v8.3.238 后 analyze 指示 AI 把 ANALYSIS.md 写在 {feature}/overview/ 下
|
|
239
|
+
// (全局 overview 只留索引类文档),若仍硬性要求全局文件,新流程将永远阻塞无法拆分。
|
|
240
|
+
// 全局缺失/骨架时,任一功能模块的 overview/ANALYSIS.md 非骨架即视为分析已落盘。
|
|
238
241
|
const analysisGatePath = (0, path_1.join)(specDir, spec_paths_1.GLOBAL_SPECS_DIR, 'ANALYSIS.md');
|
|
239
|
-
|
|
242
|
+
let analysisGateExists = await (0, fs_extra_1.pathExists)(analysisGatePath);
|
|
240
243
|
let analysisGateIsSkeleton = false;
|
|
241
244
|
if (analysisGateExists) {
|
|
242
245
|
const ac = await (0, fs_extra_1.readFile)(analysisGatePath, 'utf-8');
|
|
243
246
|
analysisGateIsSkeleton = ac.includes('<!-- SPEC-SKELETON -->') || ac.trim().length < 100;
|
|
244
247
|
}
|
|
248
|
+
if (!analysisGateExists || analysisGateIsSkeleton) {
|
|
249
|
+
try {
|
|
250
|
+
const fList = await (0, spec_paths_1.parseFeatureList)(iterationDir);
|
|
251
|
+
for (const f of fList) {
|
|
252
|
+
const fAnalysisPath = (0, path_1.join)(specDir, f, 'overview', 'ANALYSIS.md');
|
|
253
|
+
if (!(await (0, fs_extra_1.pathExists)(fAnalysisPath)))
|
|
254
|
+
continue;
|
|
255
|
+
const c = await (0, fs_extra_1.readFile)(fAnalysisPath, 'utf-8');
|
|
256
|
+
if (!c.includes('<!-- SPEC-SKELETON -->') && c.trim().length >= 100) {
|
|
257
|
+
analysisGateExists = true;
|
|
258
|
+
analysisGateIsSkeleton = false;
|
|
259
|
+
break;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
catch { /* 回退失败保持原判定 */ }
|
|
264
|
+
}
|
|
245
265
|
items.push({
|
|
246
266
|
path: analysisGatePath,
|
|
247
267
|
name: 'ANALYSIS.md',
|
|
@@ -1109,6 +1129,9 @@ async function iterationSplitCommand(options) {
|
|
|
1109
1129
|
}
|
|
1110
1130
|
}
|
|
1111
1131
|
// ── 1. 检查 ANALYSIS.md + AI 智能拆分建议 ──
|
|
1132
|
+
// v8.3.249+: reqContent2/reqContent2Aggregated 提升到块外声明,供下方实际拆分源复用
|
|
1133
|
+
let reqContent2 = '';
|
|
1134
|
+
let reqContent2Aggregated = false;
|
|
1112
1135
|
const analysisPathNew = (0, path_1.join)(iterationDir, '020-specs', spec_paths_1.GLOBAL_SPECS_DIR, 'ANALYSIS.md');
|
|
1113
1136
|
const analysisPathOld = (0, path_1.join)(iterationDir, '020-specs', 'ANALYSIS.md');
|
|
1114
1137
|
const analysisPath = await (0, fs_extra_1.pathExists)(analysisPathNew) ? analysisPathNew : analysisPathOld;
|
|
@@ -1147,12 +1170,24 @@ async function iterationSplitCommand(options) {
|
|
|
1147
1170
|
const specDir2 = (0, path_1.join)(iterationDir, '020-specs');
|
|
1148
1171
|
// REQUIREMENT.md 从 overview/ 读取
|
|
1149
1172
|
const reqPath2 = (0, path_1.join)(specDir2, spec_paths_1.GLOBAL_SPECS_DIR, 'REQUIREMENT.md');
|
|
1150
|
-
let reqContent2 = '';
|
|
1151
1173
|
if (await (0, fs_extra_1.pathExists)(reqPath2)) {
|
|
1152
1174
|
reqContent2 = await (0, fs_extra_1.readFile)(reqPath2, 'utf-8');
|
|
1153
1175
|
}
|
|
1154
1176
|
// v8.3.160+: 如果全局 REQUIREMENT.md 为空/骨架,从各功能模块 overview/REQUIREMENT.md 聚合
|
|
1155
|
-
|
|
1177
|
+
// v8.3.249+: 增加陈旧检测——聚合文档不含当前 features 单元名时视为过期,强制重新聚合。
|
|
1178
|
+
// 此前仅按空/骨架判断,旧聚合文档与新切分单元不同步时 taskify 用陈旧数据拆分,
|
|
1179
|
+
// 漏掉新单元(--modules 指定新单元也匹配不到任何 section,反而全量拆分)。
|
|
1180
|
+
let reqContent2IsStale = false;
|
|
1181
|
+
if (reqContent2 && reqContent2.trim().length >= 200 && !reqContent2.includes('<!-- SPEC-SKELETON -->')) {
|
|
1182
|
+
const featuresAll = await (0, spec_paths_1.parseFeatureList)(iterationDir);
|
|
1183
|
+
for (const feature of featuresAll) {
|
|
1184
|
+
if (!reqContent2.includes(feature)) {
|
|
1185
|
+
reqContent2IsStale = true;
|
|
1186
|
+
break;
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
if (!reqContent2 || reqContent2.trim().length < 200 || reqContent2.includes('<!-- SPEC-SKELETON -->') || reqContent2IsStale) {
|
|
1156
1191
|
const featureReqParts = [];
|
|
1157
1192
|
const features = await (0, spec_paths_1.parseFeatureList)(iterationDir);
|
|
1158
1193
|
for (const feature of features) {
|
|
@@ -1166,6 +1201,7 @@ async function iterationSplitCommand(options) {
|
|
|
1166
1201
|
}
|
|
1167
1202
|
if (featureReqParts.length > 0) {
|
|
1168
1203
|
reqContent2 = featureReqParts.join('\n\n---\n\n');
|
|
1204
|
+
reqContent2Aggregated = true;
|
|
1169
1205
|
}
|
|
1170
1206
|
}
|
|
1171
1207
|
const specContents = [];
|
|
@@ -1253,7 +1289,9 @@ async function iterationSplitCommand(options) {
|
|
|
1253
1289
|
return;
|
|
1254
1290
|
}
|
|
1255
1291
|
const content = await (0, fs_extra_1.readFile)(reqFile, 'utf-8');
|
|
1256
|
-
|
|
1292
|
+
// v8.3.249+: 聚合后的最新需求内容(含新切分单元)优先于陈旧聚合文档作为实际拆分源
|
|
1293
|
+
const finalContent = (reqContent2Aggregated && reqContent2) ? reqContent2 : content;
|
|
1294
|
+
let sections = extractSections(finalContent, options.sections);
|
|
1257
1295
|
// v6.76.0+: 按功能模块过滤
|
|
1258
1296
|
if (options.modules) {
|
|
1259
1297
|
const moduleFilters = options.modules.split(',').map(m => m.trim()).filter(Boolean);
|
|
@@ -4378,7 +4416,9 @@ async function tryModuleDrivenSplit(iteration, iterationDir, options) {
|
|
|
4378
4416
|
catch { }
|
|
4379
4417
|
}
|
|
4380
4418
|
}
|
|
4381
|
-
// 3. 回退:读取 features/*/SOURCE.md(v8.3.226+,兼容旧 README.md
|
|
4419
|
+
// 3. 回退:读取 features/*/SOURCE.md(v8.3.226+,兼容旧 README.md)
|
|
4420
|
+
// v8.3.249+: 优先读 _matrix.md 端声明(unitize 切分时写入),替代全端兜底——
|
|
4421
|
+
// 此前全部配全端,单端功能(如仅 booking-service)也会拆出 4 个子任务
|
|
4382
4422
|
if (!modulePlatformsParsed) {
|
|
4383
4423
|
const featuresDir = (0, path_1.join)(reqDir, 'features');
|
|
4384
4424
|
if (await (0, fs_extra_1.pathExists)(featuresDir)) {
|
|
@@ -4388,10 +4428,23 @@ async function tryModuleDrivenSplit(iteration, iterationDir, options) {
|
|
|
4388
4428
|
if (entry.isDirectory() && !entry.name.startsWith('.')) {
|
|
4389
4429
|
const readmePath = await (0, feature_source_1.resolveUnitSourcePath)((0, path_1.join)(featuresDir, entry.name));
|
|
4390
4430
|
if (await (0, fs_extra_1.pathExists)(readmePath)) {
|
|
4431
|
+
let unitPlatforms = [...allPlatforms];
|
|
4432
|
+
const matrixPath = (0, path_1.join)(featuresDir, entry.name, '_matrix.md');
|
|
4433
|
+
if (await (0, fs_extra_1.pathExists)(matrixPath)) {
|
|
4434
|
+
const mc = await (0, fs_extra_1.readFile)(matrixPath, 'utf-8');
|
|
4435
|
+
const m = mc.match(/^platforms:\s*\n((?:\s+-\s+\S+\s*\n?)+)/m);
|
|
4436
|
+
if (m) {
|
|
4437
|
+
const declared = m[1].split('\n')
|
|
4438
|
+
.map(l => l.replace(/^\s+-\s+/, '').trim()).filter(Boolean);
|
|
4439
|
+
if (declared.length > 0) {
|
|
4440
|
+
unitPlatforms = declared.filter(p => allPlatforms.includes(p));
|
|
4441
|
+
}
|
|
4442
|
+
}
|
|
4443
|
+
}
|
|
4391
4444
|
modules.push({
|
|
4392
4445
|
name: entry.name, slug: slugify(entry.name), type: 'feature',
|
|
4393
4446
|
sourceFile: `features/${entry.name}/${(0, path_1.basename)(readmePath)}`,
|
|
4394
|
-
platforms:
|
|
4447
|
+
platforms: unitPlatforms,
|
|
4395
4448
|
});
|
|
4396
4449
|
}
|
|
4397
4450
|
}
|
|
@@ -4425,6 +4478,20 @@ async function tryModuleDrivenSplit(iteration, iterationDir, options) {
|
|
|
4425
4478
|
if (modules.length === 0) {
|
|
4426
4479
|
return false; // 无功能模块,回退到传统流程
|
|
4427
4480
|
}
|
|
4481
|
+
// v8.3.249+: --modules 过滤——模块驱动拆分此前完全忽略该选项,
|
|
4482
|
+
// 指定单个单元时仍全量拆分(FUNCTION_MAP 全部单元照拆)
|
|
4483
|
+
if (options.modules) {
|
|
4484
|
+
const moduleFilters = options.modules.split(',').map(m => m.trim()).filter(Boolean);
|
|
4485
|
+
const beforeCount = modules.length;
|
|
4486
|
+
const filtered = modules.filter(mod => moduleFilters.some(f => mod.name.includes(f) || mod.slug.includes(f)));
|
|
4487
|
+
if (filtered.length === 0) {
|
|
4488
|
+
logger_1.logger.warn(`没有匹配 "${options.modules}" 的功能模块(可用: ${modules.slice(0, 5).map(m => m.name).join(', ')}${modules.length > 5 ? '...' : ''}),回退传统流程`);
|
|
4489
|
+
return false;
|
|
4490
|
+
}
|
|
4491
|
+
modules.length = 0;
|
|
4492
|
+
modules.push(...filtered);
|
|
4493
|
+
logger_1.logger.info(` 🎯 模块过滤: ${beforeCount} → ${filtered.length}(只拆分: ${moduleFilters.join(', ')})`);
|
|
4494
|
+
}
|
|
4428
4495
|
// 检测已有任务
|
|
4429
4496
|
const existingTasks = await detectExistingTasks(iterationDir);
|
|
4430
4497
|
if (existingTasks.length > 0 && !options.force && !options.prune) {
|
|
@@ -4481,9 +4548,22 @@ async function tryModuleDrivenSplit(iteration, iterationDir, options) {
|
|
|
4481
4548
|
for (const mod of modules) {
|
|
4482
4549
|
const { id: taskId } = await (0, global_counters_1.nextTaskId)(mod.name, mod.slug);
|
|
4483
4550
|
const modPlatforms = mod.platforms.length > 0 ? mod.platforms : [...allPlatforms];
|
|
4551
|
+
// v8.3.249+: section.content 从 sourceFile(SOURCE.md 等)读取——此前硬编码空串,
|
|
4552
|
+
// 导致 REQ.md 四级回退全部落空(aiReq/analyzeReq/短 section.content 均不满足),
|
|
4553
|
+
// 最终写出「待补充」骨架,execute 阶段拿不到有价值的需求文档
|
|
4554
|
+
let sectionContent = '';
|
|
4555
|
+
if (mod.sourceFile) {
|
|
4556
|
+
try {
|
|
4557
|
+
const srcPath = (0, path_1.join)(iterationDir, '010-requirements', mod.sourceFile);
|
|
4558
|
+
if (await (0, fs_extra_1.pathExists)(srcPath)) {
|
|
4559
|
+
sectionContent = await (0, fs_extra_1.readFile)(srcPath, 'utf-8');
|
|
4560
|
+
}
|
|
4561
|
+
}
|
|
4562
|
+
catch { /* 读取失败保持空 */ }
|
|
4563
|
+
}
|
|
4484
4564
|
const section = {
|
|
4485
4565
|
name: mod.name,
|
|
4486
|
-
content:
|
|
4566
|
+
content: sectionContent,
|
|
4487
4567
|
level: 2,
|
|
4488
4568
|
};
|
|
4489
4569
|
section._topic = mod.slug;
|