@gordon.gan/specflow 1.8.2-beta → 1.8.3-beta

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.
@@ -53,6 +53,17 @@ export declare function stripEntityJsonBlocks(text: string): string;
53
53
  * 仅当首行非空是 H1 且其内容与章节标题 trim 后一致时才剥离,避免误删正文中的真实内容。
54
54
  */
55
55
  export declare function stripLeadingTitle(text: string, chapterTitle: string): string;
56
+ /**
57
+ * 章节叙述标题整体降级 n 级(默认 1)。原因:renderDocument 已为每章输出 `## <章节标题>`(H2),
58
+ * 而 Agent 生成的叙述内小节标题习惯用 `##`(H2)、分仓小节用 `###`(H3)——与章节头同级,
59
+ * 造成最终文档目录层级乱(`## 需求` 与 `## 跨仓总目标` 平级)。渲染时降级:`##`→`###`、`###`→`####`…,
60
+ * 使章节头 H2 → 叙述小节 H3 → 分仓 H4,层级正确。
61
+ *
62
+ * 只处理行首的 markdown 标题(`#{1,6}\s`),并跳过围栏代码块(mermaid/``` 内部行不动),
63
+ * 避免把代码内容里的 `#` 误判成标题。H6 封顶不再升。质量门禁 `### <repo>` 检查的是原始章节
64
+ * 文件(chapters/*.md),不在此处降级影响范围——仅最终渲染的 document.md 生效。
65
+ */
66
+ export declare function demoteHeadings(text: string, levels?: number): string;
56
67
  export declare function renderDocument(input: RenderDocumentInput): string;
57
68
  /** 附录 B · 去AI味自检(frontend-dev-guide §十一 → README §1.6 五维表)。 */
58
69
  export declare function renderAppendixAntiAI(content?: string): string;
@@ -146,13 +146,46 @@ export function stripLeadingTitle(text, chapterTitle) {
146
146
  const rest = trimmed.slice(firstLine.length).replace(/^\n+/, '');
147
147
  return rest;
148
148
  }
149
+ /**
150
+ * 章节叙述标题整体降级 n 级(默认 1)。原因:renderDocument 已为每章输出 `## <章节标题>`(H2),
151
+ * 而 Agent 生成的叙述内小节标题习惯用 `##`(H2)、分仓小节用 `###`(H3)——与章节头同级,
152
+ * 造成最终文档目录层级乱(`## 需求` 与 `## 跨仓总目标` 平级)。渲染时降级:`##`→`###`、`###`→`####`…,
153
+ * 使章节头 H2 → 叙述小节 H3 → 分仓 H4,层级正确。
154
+ *
155
+ * 只处理行首的 markdown 标题(`#{1,6}\s`),并跳过围栏代码块(mermaid/``` 内部行不动),
156
+ * 避免把代码内容里的 `#` 误判成标题。H6 封顶不再升。质量门禁 `### <repo>` 检查的是原始章节
157
+ * 文件(chapters/*.md),不在此处降级影响范围——仅最终渲染的 document.md 生效。
158
+ */
159
+ export function demoteHeadings(text, levels = 1) {
160
+ if (levels <= 0)
161
+ return text;
162
+ const lines = text.split('\n');
163
+ let inFence = false;
164
+ const out = [];
165
+ for (const line of lines) {
166
+ const trimmed = line.trimStart();
167
+ if (trimmed.startsWith('```'))
168
+ inFence = !inFence;
169
+ if (!inFence) {
170
+ const m = /^(#{1,6})(\s.*)$/.exec(line);
171
+ if (m) {
172
+ const hashes = Math.min(m[1].length + levels, 6);
173
+ out.push('#'.repeat(hashes) + m[2]);
174
+ continue;
175
+ }
176
+ }
177
+ out.push(line);
178
+ }
179
+ return out.join('\n');
180
+ }
149
181
  export function renderDocument(input) {
150
182
  const { outline, entities, narratives, profile, appendixAntiAI } = input;
151
183
  const body = [`# 方案文档:${escapeInline(outline.profile)}`, ''];
152
184
  for (const ch of outline.chapters) {
153
185
  body.push(`## ${escapeInline(ch.title)}`, '');
154
- // 依次净化:剥内嵌契约 JSON 块(机器中间格式)→ 剥与章节标题重复的首行 H1 → 剥引擎修复注记。
155
- const narrative = stripLeadingTitle(stripEntityJsonBlocks(stripReviewNotes(narratives.get(ch.id) ?? '')), ch.title);
186
+ // 依次净化:剥内嵌契约 JSON 块(机器中间格式)→ 剥与章节标题重复的首行 H1 → 剥引擎修复注记,
187
+ // 再把叙述内标题降级一级(章节头已占 H2,叙述小节 H2→H3、分仓 H3→H4),保证目录层级正确。
188
+ const narrative = demoteHeadings(stripLeadingTitle(stripEntityJsonBlocks(stripReviewNotes(narratives.get(ch.id) ?? '')), ch.title));
156
189
  if (narrative.trim())
157
190
  body.push(narrative.trim(), '');
158
191
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gordon.gan/specflow",
3
- "version": "1.8.2-beta",
3
+ "version": "1.8.3-beta",
4
4
  "type": "module",
5
5
  "description": "SpecFlow — unified spec-driven development: OpenSpec planning + Superpowers execution in one CLI and cross-IDE workflow",
6
6
  "keywords": [