speccore 7.4.5 → 7.5.0

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.
@@ -2500,12 +2500,13 @@ async function buildSplitPrompt(iteration, constitutionContent, reqContent, spec
2500
2500
  if (iterationDir) {
2501
2501
  try {
2502
2502
  const funcMapPath = (0, path_1.join)(iterationDir, '020-specs', spec_paths_1.GLOBAL_SPECS_DIR, 'FUNCTION_MAP.md');
2503
+ let funcUnitsInjected = false;
2503
2504
  if (await (0, fs_extra_1.pathExists)(funcMapPath)) {
2504
2505
  const funcMapContent = await (0, fs_extra_1.readFile)(funcMapPath, 'utf-8');
2505
2506
  const funcUnits = parseFunctionMap(funcMapContent, standardPlatforms);
2506
2507
  if (funcUnits.length > 0) {
2507
- p += `## 🧩 功能单元上下文(CLI 从分析文档精准提取)\n\n`;
2508
- p += `> 以下是从 FUNCTION_MAP.md / REQUIREMENT.md / _INDEX.md / _MODULES.md 中为每个功能单元提取的相关上下文。\n`;
2508
+ p += `## 🧩 功能单元上下文(CLI 从 FUNCTION_MAP.md 精准提取)\n\n`;
2509
+ p += `> 以下是从 FUNCTION_MAP.md 中为每个功能单元提取的相关上下文。\n`;
2509
2510
  p += `> 拆分时必须基于这些功能单元,每个功能单元对应 1~3 个任务。\n\n`;
2510
2511
  for (const unit of funcUnits) {
2511
2512
  const unitCtx = await assembleUnitContext(iterationDir, unit.name, unit.platforms);
@@ -2519,6 +2520,30 @@ async function buildSplitPrompt(iteration, constitutionContent, reqContent, spec
2519
2520
  p += `- 说明: ${unit.description}\n`;
2520
2521
  p += `\n${unitCtx}\n\n---\n\n`;
2521
2522
  }
2523
+ funcUnitsInjected = true;
2524
+ }
2525
+ }
2526
+ // v7.5.0+: FUNCTION_MAP.md 不存在时,从 ANALYSIS.md 提取功能单元清单
2527
+ if (!funcUnitsInjected) {
2528
+ const analysisPath = (0, path_1.join)(iterationDir, '020-specs', 'ANALYSIS.md');
2529
+ if (await (0, fs_extra_1.pathExists)(analysisPath)) {
2530
+ const analysisContent = await (0, fs_extra_1.readFile)(analysisPath, 'utf-8');
2531
+ const analysisUnits = parseAnalysisFunctionalUnits(analysisContent, standardPlatforms);
2532
+ if (analysisUnits.length > 0) {
2533
+ p += `## 🧩 功能单元清单(CLI 从 ANALYSIS.md 提取,v7.5.0+)\n\n`;
2534
+ p += `> 以下是从 ANALYSIS.md 功能点清单表中提取的 **全部功能单元**(F-xx 编号)。\n`;
2535
+ p += `> ⚠️ **你必须为每个功能单元创建至少 1 个任务,不允许遗漏任何功能单元。**\n`;
2536
+ p += `> 可以按合并规则将相关功能单元合并为同一个任务,但必须确保所有功能单元都被覆盖。\n\n`;
2537
+ p += `| 编号 | 功能单元 | 涉及端 | 核心功能 |\n`;
2538
+ p += `|:---|:---|:---|:---|\n`;
2539
+ for (const u of analysisUnits) {
2540
+ p += `| ${u.id} | ${u.name} | ${u.platforms.join(', ')} | ${u.description || '-'} |\n`;
2541
+ }
2542
+ p += `\n**拆分要求**:\n`;
2543
+ p += `- 每个功能单元必须在某个任务的 \`functionalUnit\` 字段中被引用\n`;
2544
+ p += `- 合并时,在任务描述中列出所有合并的功能单元编号(如 F-02, F-03)\n`;
2545
+ p += `- 不允许将不同端的功能单元合并到同一个任务\n\n`;
2546
+ }
2522
2547
  }
2523
2548
  }
2524
2549
  }
@@ -3185,6 +3210,71 @@ function parseFunctionMapTree(content, allPlatforms) {
3185
3210
  }
3186
3211
  return units;
3187
3212
  }
3213
+ /**
3214
+ * v7.5.0+: 从 ANALYSIS.md 提取功能单元清单
3215
+ * 解析按端分节的 F-xx 编号表格,提取每个功能单元及其涉及端
3216
+ * 格式: ### X.X 端名(platform-name)— N 个页面 + | F-01 | 功能名 | ...
3217
+ */
3218
+ function parseAnalysisFunctionalUnits(content, allPlatforms) {
3219
+ const units = [];
3220
+ const lines = content.split('\n');
3221
+ let currentPlatforms = [];
3222
+ for (let i = 0; i < lines.length; i++) {
3223
+ const line = lines[i];
3224
+ // 检测 ### 标题行,提取当前端名
3225
+ if (line.startsWith('### ')) {
3226
+ currentPlatforms = [];
3227
+ // 匹配括号内的端名: ### 1.1 后台管理端(admin-web)
3228
+ const parenMatch = line.match(/[((]([a-zA-Z][\w-]*)[))]/);
3229
+ if (parenMatch) {
3230
+ const name = parenMatch[1];
3231
+ const matched = allPlatforms.filter(p => p === name || p.includes(name) || name.includes(p));
3232
+ if (matched.length > 0)
3233
+ currentPlatforms.push(...matched);
3234
+ }
3235
+ // 回退:从标题文本匹配端名
3236
+ if (currentPlatforms.length === 0) {
3237
+ for (const p of allPlatforms) {
3238
+ if (line.includes(p)) {
3239
+ currentPlatforms.push(p);
3240
+ }
3241
+ }
3242
+ }
3243
+ // 后端服务检测
3244
+ if (/后端|服务|backend|service/i.test(line) && currentPlatforms.length === 0) {
3245
+ currentPlatforms.push(...allPlatforms.filter(p => /service|server|api|backend/i.test(p)));
3246
+ }
3247
+ continue;
3248
+ }
3249
+ // 解析表格行
3250
+ if (!line.startsWith('|') || line.includes(':---'))
3251
+ continue;
3252
+ const cells = line.split('|').map(c => c.trim()).filter(Boolean);
3253
+ if (cells.length < 2)
3254
+ continue;
3255
+ // 检测 F-xx 编号行
3256
+ const idMatch = cells[0]?.match(/^F-(\d+)$/i);
3257
+ if (!idMatch)
3258
+ continue;
3259
+ const id = `F-${idMatch[1]}`;
3260
+ const name = cells[1]?.replace(/[*_`]/g, '').trim();
3261
+ if (!name || name.length < 2)
3262
+ continue;
3263
+ // 提取描述(核心功能列,通常是第3或第4列)
3264
+ const desc = cells.length >= 4 ? cells[3]?.trim() || '' : '';
3265
+ // 匹配端
3266
+ const platforms = currentPlatforms.length > 0 ? [...currentPlatforms] : [...allPlatforms];
3267
+ units.push({ name, platforms, description: desc, id });
3268
+ }
3269
+ // 去重(按名称)
3270
+ const seen = new Set();
3271
+ return units.filter(u => {
3272
+ if (seen.has(u.name))
3273
+ return false;
3274
+ seen.add(u.name);
3275
+ return true;
3276
+ });
3277
+ }
3188
3278
  /**
3189
3279
  * 尝试模块驱动拆分:从功能模块创建任务目录结构
3190
3280
  * 成功返回 true,无功能模块时返回 false(回退到传统流程)
@@ -3246,6 +3336,31 @@ async function tryModuleDrivenSplit(iteration, iterationDir, options) {
3246
3336
  }
3247
3337
  catch { }
3248
3338
  }
3339
+ // 2.5 v7.5.0+: 回退:从 ANALYSIS.md 提取功能单元清单(按端分节的 F-xx 表格)
3340
+ if (!modulePlatformsParsed) {
3341
+ const analysisPath = (0, path_1.join)(iterationDir, '020-specs', 'ANALYSIS.md');
3342
+ if (await (0, fs_extra_1.pathExists)(analysisPath)) {
3343
+ try {
3344
+ const content = await (0, fs_extra_1.readFile)(analysisPath, 'utf-8');
3345
+ const parsed = parseAnalysisFunctionalUnits(content, allPlatforms);
3346
+ if (parsed.length > 0) {
3347
+ for (const u of parsed) {
3348
+ modules.push({
3349
+ name: u.name,
3350
+ slug: slugify(u.name),
3351
+ type: 'feature',
3352
+ sourceFile: 'ANALYSIS.md',
3353
+ platforms: u.platforms,
3354
+ description: u.description,
3355
+ });
3356
+ }
3357
+ modulePlatformsParsed = true;
3358
+ logger_1.logger.info(` 📋 从 ANALYSIS.md 提取到 ${parsed.length} 个功能单元(F-xx 编号表)`);
3359
+ }
3360
+ }
3361
+ catch { }
3362
+ }
3363
+ }
3249
3364
  // 3. 回退:读取 features/*/README.md(无涉及端信息,使用全端)
3250
3365
  if (!modulePlatformsParsed) {
3251
3366
  const featuresDir = (0, path_1.join)(reqDir, 'features');