speccore 8.0.0 → 8.1.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.
@@ -1279,9 +1279,10 @@ async function analyzeCommand(options) {
1279
1279
  }
1280
1280
  }
1281
1281
  /**
1282
- * v6.49.13+: 预创建 020-specs/ 目录结构
1283
- * CLI 控制目录创建(确定性操作),AI 只负责内容生成
1282
+ * @deprecated v8.0.0+ 已被 computeAnalyzeManifest + generateSkeleton 替代
1283
+ * 保留用于旧项目迁移参考,新流程不再调用。
1284
1284
  */
1285
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
1285
1286
  async function preCreateSpecDirectories(iteration) {
1286
1287
  const iterDir = await (0, context_1.getIterationDir)(iteration);
1287
1288
  const specDir = (0, path_1.join)(iterDir, '020-specs');
@@ -1818,8 +1819,9 @@ async function injectGraphSummary(prompt) {
1818
1819
  return prompt;
1819
1820
  }
1820
1821
  }
1821
- // ── v7.5.0+: 检测迭代级文档生成进度 ──
1822
- // 检查 020-specs/overview/ 和 020-specs/{platform}/ 下哪些文档已存在
1822
+ // ── @deprecated v8.0.0+ 已被 detectSkeletonProgress 替代 ──
1823
+ // 保留用于旧项目迁移参考,新流程不再调用。
1824
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
1823
1825
  async function detectIterationDocsStatus(iterDir) {
1824
1826
  const specsDir = (0, path_1.join)(iterDir, '020-specs');
1825
1827
  const overviewDir = (0, path_1.join)(specsDir, spec_paths_1.GLOBAL_SPECS_DIR);
@@ -3232,6 +3234,7 @@ sequenceDiagram
3232
3234
  // v8.0.0+: 骨架进度检测 — 每次只生成下一个未填充的文档
3233
3235
  // 替代 v7.5.0 的 detectIterationDocsStatus(基于文件是否存在)
3234
3236
  let perDocStatus = null;
3237
+ let lastDocQuality = null;
3235
3238
  if (!isGlobal && !isTask && !ctx.phase && ctx.iteration && ctx.iteration !== 'GLOBAL') {
3236
3239
  try {
3237
3240
  const iterDir = await (0, context_1.getIterationDir)(ctx.iteration);
@@ -3254,6 +3257,27 @@ sequenceDiagram
3254
3257
  taskDocs = taskDocs.filter(([n]) => n === nextDocName);
3255
3258
  }
3256
3259
  logger_1.logger.info(`🦴 骨架进度: ${perDocStatus.filledCount}/${perDocStatus.totalCount} 已填充,下一个: ${perDocStatus.nextUnfilled.relPath}`);
3260
+ // v8.1.0+: 校验上一个已填充文档的质量
3261
+ if (perDocStatus.filled.length > 0) {
3262
+ const lastFilled = perDocStatus.filled[perDocStatus.filled.length - 1];
3263
+ const lastFilledPath = (0, path_1.join)(specDir, lastFilled);
3264
+ if (await (0, fs_extra_1.pathExists)(lastFilledPath)) {
3265
+ try {
3266
+ const lastContent = await (0, fs_extra_2.readFile)(lastFilledPath, 'utf-8');
3267
+ const lastDocName = lastFilled.includes('/') ? lastFilled.split('/').pop() : lastFilled;
3268
+ const lastPlatform = lastFilled.includes('/') ? lastFilled.split('/')[0] : undefined;
3269
+ const quality = await (0, spec_skeleton_1.validateContentQuality)(lastDocName, lastContent, lastPlatform !== lastDocName ? lastPlatform : undefined);
3270
+ lastDocQuality = { docName: lastFilled, score: quality.score, issues: quality.issues };
3271
+ if (quality.score < 60) {
3272
+ logger_1.logger.warn(`⚠️ ${lastFilled} 质量评分: ${quality.score}/100 — ${quality.issues.join(';')}`);
3273
+ }
3274
+ else if (quality.score < 80) {
3275
+ logger_1.logger.info(`📝 ${lastFilled} 质量评分: ${quality.score}/100 — ${quality.issues.length > 0 ? quality.issues.join(';') : '良好'}`);
3276
+ }
3277
+ }
3278
+ catch { /* skip */ }
3279
+ }
3280
+ }
3257
3281
  }
3258
3282
  else if (perDocStatus.filledCount === perDocStatus.totalCount) {
3259
3283
  logger_1.logger.info(`🎉 所有 ${perDocStatus.totalCount} 个骨架文件均已填充`);
@@ -3451,6 +3475,53 @@ sequenceDiagram
3451
3475
  prompt += `- 只覆盖上述已存在的文件,不要创建新文件或新目录\n`;
3452
3476
  prompt += `- 每个文件写入正确路径(如 \`overview/ANALYSIS.md\`,不是根目录的 \`ANALYSIS.md\`)\n`;
3453
3477
  prompt += `- 写完后用 Read 验证文件内容已替换占位内容\n\n`;
3478
+ // ── v8.1.0+: 逐文档模式自动注入 PRD 内容 + 前序文档摘要 ──
3479
+ if (perDocStatus?.nextUnfilled) {
3480
+ try {
3481
+ const iterDirForCtx = await (0, context_1.getIterationDir)(iter);
3482
+ if (iterDirForCtx) {
3483
+ // 1. 注入 PRD 内容(010-requirements/ 下的需求文档)
3484
+ const reqDir = (0, path_1.join)(iterDirForCtx, '010-requirements');
3485
+ let prdContent = '';
3486
+ const prdSources = (0, path_1.join)(reqDir, 'sources');
3487
+ const prdConverted = (0, path_1.join)(reqDir, 'converted');
3488
+ const prdFeatures = (0, path_1.join)(reqDir, 'features');
3489
+ for (const dir of [prdSources, prdConverted, prdFeatures]) {
3490
+ if (await (0, fs_extra_1.pathExists)(dir)) {
3491
+ const files = (await Promise.resolve().then(() => __importStar(require('fs-extra')))).readdirSync(dir).filter((f) => f.endsWith('.md')).slice(0, 5);
3492
+ for (const f of files) {
3493
+ const content = await (await Promise.resolve().then(() => __importStar(require('fs-extra')))).readFile((0, path_1.join)(dir, f), 'utf-8');
3494
+ prdContent += `\n### ${f}\n${content.slice(0, 2000)}\n`;
3495
+ }
3496
+ }
3497
+ }
3498
+ if (prdContent.length > 0) {
3499
+ prompt += `\n## 📎 需求文档原文(PRD,写作核心输入)\n\n`;
3500
+ prompt += `> 以下是 010-requirements/ 下的需求文档,这是你撰写专业分析文档的核心输入。所有分析必须基于这些内容,不要臆造。\n`;
3501
+ prompt += prdContent.slice(0, 5000);
3502
+ prompt += `\n\n`;
3503
+ }
3504
+ // 2. 注入前序已填充文档摘要
3505
+ const specDir = (0, path_1.join)(iterDirForCtx, '020-specs');
3506
+ if (perDocStatus.filled.length > 0) {
3507
+ prompt += `## 📎 前序已填充文档摘要(当前文档必须与之保持一致)\n\n`;
3508
+ prompt += `> 以下文档已生成,当前文档必须与之保持一致(字段名、接口路径、状态枚举等不能冲突)。\n\n`;
3509
+ for (const filledPath of perDocStatus.filled) {
3510
+ const fullPath = (0, path_1.join)(specDir, filledPath);
3511
+ try {
3512
+ const content = await (await Promise.resolve().then(() => __importStar(require('fs-extra')))).readFile(fullPath, 'utf-8');
3513
+ const summary = content.slice(0, 1500);
3514
+ prompt += `### ${filledPath}\n\`\`\`\n${summary}${content.length > 1500 ? '\n... (截断)' : ''}\n\`\`\`\n\n`;
3515
+ }
3516
+ catch { /* skip unreadable */ }
3517
+ }
3518
+ }
3519
+ }
3520
+ }
3521
+ catch (e) {
3522
+ logger_1.logger.debug?.(`PRD/前序文档注入失败(非关键): ${e.message}`);
3523
+ }
3524
+ }
3454
3525
  prompt += `## ⚠️ 迭代名称仅为目录标识(重要)\n\n`;
3455
3526
  prompt += `- 迭代名称("${iter}")仅为目录标识符,**不代表需求内容**\n`;
3456
3527
  prompt += `- **需求文档是唯一事实来源**:所有分析必须 100% 基于 010-requirements/ 下的文档内容\n`;
@@ -3800,6 +3871,12 @@ sequenceDiagram
3800
3871
  prompt += `> 当前为**单文档模式**:本次只生成 1 个文档,写入后立即推进下一个。\n`;
3801
3872
  prompt += `> 骨架进度: ${perDocStatus.filledCount}/${perDocStatus.totalCount} 已填充,剩余 ${perDocStatus.unfilled.length} 个\n`;
3802
3873
  prompt += `> 待填充: ${perDocStatus.unfilled.slice(0, 5).join('、')}${perDocStatus.unfilled.length > 5 ? '...' : ''}\n\n`;
3874
+ // v8.1.0+: 注入上一文档质量警告
3875
+ if (lastDocQuality && lastDocQuality.score < 80) {
3876
+ prompt += `> ⚠️ **上一文档 ${lastDocQuality.docName} 质量评分仅 ${lastDocQuality.score}/100**\n`;
3877
+ prompt += `> 问题: ${lastDocQuality.issues.join(';')}\n`;
3878
+ prompt += `> 请确保当前文档达到专业标准——内容充实、章节完整、无占位符残留\n\n`;
3879
+ }
3803
3880
  if (remainingAfterThis > 0) {
3804
3881
  prompt += `**当前文档写入完成后,立即输出以下标记推进下一个文档**:\n\n`;
3805
3882
  prompt += `[SPECCORE_EXEC: speccore analyze --prompt -I ${iter}${options?.auto ? ' --auto' : ''}${options?.withCode ? ' --with-code' : ''}]\n\n`;