speccore 8.3.249 → 8.3.251

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.
@@ -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;AAqLD,wBAAsB,qBAAqB,CAAC,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CAorCzF"}
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"}
@@ -1129,6 +1129,9 @@ async function iterationSplitCommand(options) {
1129
1129
  }
1130
1130
  }
1131
1131
  // ── 1. 检查 ANALYSIS.md + AI 智能拆分建议 ──
1132
+ // v8.3.249+: reqContent2/reqContent2Aggregated 提升到块外声明,供下方实际拆分源复用
1133
+ let reqContent2 = '';
1134
+ let reqContent2Aggregated = false;
1132
1135
  const analysisPathNew = (0, path_1.join)(iterationDir, '020-specs', spec_paths_1.GLOBAL_SPECS_DIR, 'ANALYSIS.md');
1133
1136
  const analysisPathOld = (0, path_1.join)(iterationDir, '020-specs', 'ANALYSIS.md');
1134
1137
  const analysisPath = await (0, fs_extra_1.pathExists)(analysisPathNew) ? analysisPathNew : analysisPathOld;
@@ -1167,12 +1170,24 @@ async function iterationSplitCommand(options) {
1167
1170
  const specDir2 = (0, path_1.join)(iterationDir, '020-specs');
1168
1171
  // REQUIREMENT.md 从 overview/ 读取
1169
1172
  const reqPath2 = (0, path_1.join)(specDir2, spec_paths_1.GLOBAL_SPECS_DIR, 'REQUIREMENT.md');
1170
- let reqContent2 = '';
1171
1173
  if (await (0, fs_extra_1.pathExists)(reqPath2)) {
1172
1174
  reqContent2 = await (0, fs_extra_1.readFile)(reqPath2, 'utf-8');
1173
1175
  }
1174
1176
  // v8.3.160+: 如果全局 REQUIREMENT.md 为空/骨架,从各功能模块 overview/REQUIREMENT.md 聚合
1175
- if (!reqContent2 || reqContent2.trim().length < 200 || reqContent2.includes('<!-- SPEC-SKELETON -->')) {
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) {
1176
1191
  const featureReqParts = [];
1177
1192
  const features = await (0, spec_paths_1.parseFeatureList)(iterationDir);
1178
1193
  for (const feature of features) {
@@ -1186,6 +1201,7 @@ async function iterationSplitCommand(options) {
1186
1201
  }
1187
1202
  if (featureReqParts.length > 0) {
1188
1203
  reqContent2 = featureReqParts.join('\n\n---\n\n');
1204
+ reqContent2Aggregated = true;
1189
1205
  }
1190
1206
  }
1191
1207
  const specContents = [];
@@ -1273,7 +1289,9 @@ async function iterationSplitCommand(options) {
1273
1289
  return;
1274
1290
  }
1275
1291
  const content = await (0, fs_extra_1.readFile)(reqFile, 'utf-8');
1276
- let sections = extractSections(content, options.sections);
1292
+ // v8.3.249+: 聚合后的最新需求内容(含新切分单元)优先于陈旧聚合文档作为实际拆分源
1293
+ const finalContent = (reqContent2Aggregated && reqContent2) ? reqContent2 : content;
1294
+ let sections = extractSections(finalContent, options.sections);
1277
1295
  // v6.76.0+: 按功能模块过滤
1278
1296
  if (options.modules) {
1279
1297
  const moduleFilters = options.modules.split(',').map(m => m.trim()).filter(Boolean);
@@ -4398,7 +4416,9 @@ async function tryModuleDrivenSplit(iteration, iterationDir, options) {
4398
4416
  catch { }
4399
4417
  }
4400
4418
  }
4401
- // 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 个子任务
4402
4422
  if (!modulePlatformsParsed) {
4403
4423
  const featuresDir = (0, path_1.join)(reqDir, 'features');
4404
4424
  if (await (0, fs_extra_1.pathExists)(featuresDir)) {
@@ -4408,10 +4428,23 @@ async function tryModuleDrivenSplit(iteration, iterationDir, options) {
4408
4428
  if (entry.isDirectory() && !entry.name.startsWith('.')) {
4409
4429
  const readmePath = await (0, feature_source_1.resolveUnitSourcePath)((0, path_1.join)(featuresDir, entry.name));
4410
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
+ }
4411
4444
  modules.push({
4412
4445
  name: entry.name, slug: slugify(entry.name), type: 'feature',
4413
4446
  sourceFile: `features/${entry.name}/${(0, path_1.basename)(readmePath)}`,
4414
- platforms: [...allPlatforms],
4447
+ platforms: unitPlatforms,
4415
4448
  });
4416
4449
  }
4417
4450
  }
@@ -4445,6 +4478,20 @@ async function tryModuleDrivenSplit(iteration, iterationDir, options) {
4445
4478
  if (modules.length === 0) {
4446
4479
  return false; // 无功能模块,回退到传统流程
4447
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
+ }
4448
4495
  // 检测已有任务
4449
4496
  const existingTasks = await detectExistingTasks(iterationDir);
4450
4497
  if (existingTasks.length > 0 && !options.force && !options.prune) {
@@ -4501,9 +4548,22 @@ async function tryModuleDrivenSplit(iteration, iterationDir, options) {
4501
4548
  for (const mod of modules) {
4502
4549
  const { id: taskId } = await (0, global_counters_1.nextTaskId)(mod.name, mod.slug);
4503
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
+ }
4504
4564
  const section = {
4505
4565
  name: mod.name,
4506
- content: '',
4566
+ content: sectionContent,
4507
4567
  level: 2,
4508
4568
  };
4509
4569
  section._topic = mod.slug;