@opengsd/gsd-core 1.7.0-rc.5 → 1.7.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.
Files changed (112) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/agents/gsd-executor.md +2 -1
  4. package/agents/gsd-security-auditor.md +13 -15
  5. package/agents/gsd-ui-checker.md +2 -0
  6. package/agents/gsd-ui-researcher.md +1 -0
  7. package/bin/install.js +975 -196
  8. package/commands/gsd/mempalace-capture.md +27 -1
  9. package/commands/gsd/surface.md +6 -6
  10. package/gsd-core/bin/gsd-tools.cjs +63 -2
  11. package/gsd-core/bin/lib/api-coverage.cjs +3 -4
  12. package/gsd-core/bin/lib/audit.cjs +7 -6
  13. package/gsd-core/bin/lib/capability-registry.cjs +503 -87
  14. package/gsd-core/bin/lib/capability-validator.cjs +56 -18
  15. package/gsd-core/bin/lib/check-command-router.cjs +1 -1
  16. package/gsd-core/bin/lib/clock.cjs +19 -0
  17. package/gsd-core/bin/lib/commands.cjs +48 -9
  18. package/gsd-core/bin/lib/config-loader.cjs +6 -2
  19. package/gsd-core/bin/lib/config.cjs +12 -0
  20. package/gsd-core/bin/lib/core-utils.cjs +8 -2
  21. package/gsd-core/bin/lib/drift.cjs +4 -4
  22. package/gsd-core/bin/lib/frontmatter.cjs +22 -0
  23. package/gsd-core/bin/lib/gsd2-import.cjs +2 -1
  24. package/gsd-core/bin/lib/host-integration.cjs +33 -8
  25. package/gsd-core/bin/lib/init.cjs +60 -53
  26. package/gsd-core/bin/lib/install-engine.cjs +93 -22
  27. package/gsd-core/bin/lib/installer-migration-authoring.cjs +2 -1
  28. package/gsd-core/bin/lib/installer-migration-report.cjs +3 -0
  29. package/gsd-core/bin/lib/installer-migrations.cjs +1 -1
  30. package/gsd-core/bin/lib/markdown-sectionizer.cjs +342 -0
  31. package/gsd-core/bin/lib/markdown-table.cjs +698 -0
  32. package/gsd-core/bin/lib/mcp-server.cjs +18 -7
  33. package/gsd-core/bin/lib/milestone.cjs +217 -31
  34. package/gsd-core/bin/lib/phase-command-router.cjs +50 -2
  35. package/gsd-core/bin/lib/phase-lifecycle.cjs +62 -36
  36. package/gsd-core/bin/lib/phase-locator.cjs +23 -2
  37. package/gsd-core/bin/lib/phase.cjs +436 -61
  38. package/gsd-core/bin/lib/plan-scan.cjs +3 -0
  39. package/gsd-core/bin/lib/review-reviewer-selection.cjs +24 -7
  40. package/gsd-core/bin/lib/roadmap-parser.cjs +218 -13
  41. package/gsd-core/bin/lib/roadmap.cjs +100 -49
  42. package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +242 -44
  43. package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +3 -2
  44. package/gsd-core/bin/lib/runtime-artifact-layout.cjs +24 -14
  45. package/gsd-core/bin/lib/runtime-config-adapter-registry.cjs +19 -5
  46. package/gsd-core/bin/lib/runtime-homes.cjs +22 -0
  47. package/gsd-core/bin/lib/runtime-hooks-surface.cjs +526 -29
  48. package/gsd-core/bin/lib/runtime-name-policy.cjs +63 -5
  49. package/gsd-core/bin/lib/schema-detect.cjs +2 -1
  50. package/gsd-core/bin/lib/security.cjs +7 -37
  51. package/gsd-core/bin/lib/shell-command-projection.cjs +176 -27
  52. package/gsd-core/bin/lib/smart-entry.cjs +4 -3
  53. package/gsd-core/bin/lib/stale-bake-guard.cjs +30 -10
  54. package/gsd-core/bin/lib/state-transition.cjs +100 -45
  55. package/gsd-core/bin/lib/state.cjs +391 -126
  56. package/gsd-core/bin/lib/surface.cjs +12 -8
  57. package/gsd-core/bin/lib/template.cjs +2 -1
  58. package/gsd-core/bin/lib/uat.cjs +54 -8
  59. package/gsd-core/bin/lib/ui-consideration-probe.cjs +249 -0
  60. package/gsd-core/bin/lib/ui-safety-gate.cjs +23 -1
  61. package/gsd-core/bin/lib/verify.cjs +4 -3
  62. package/gsd-core/bin/lib/workstream.cjs +3 -2
  63. package/gsd-core/bin/lib/worktree-safety.cjs +1 -1
  64. package/gsd-core/bin/lib/write-set.cjs +38 -0
  65. package/gsd-core/bin/shared/config-schema.manifest.json +2 -0
  66. package/gsd-core/bin/shared/model-catalog.json +8 -3
  67. package/gsd-core/references/checkpoints.md +12 -0
  68. package/gsd-core/references/ui-consideration-probe.md +73 -0
  69. package/gsd-core/templates/UI-SPEC.md +25 -0
  70. package/gsd-core/templates/VALIDATION.md +2 -0
  71. package/gsd-core/workflows/add-tests.md +1 -1
  72. package/gsd-core/workflows/audit-milestone.md +7 -4
  73. package/gsd-core/workflows/debug.md +2 -0
  74. package/gsd-core/workflows/execute-phase.md +5 -3
  75. package/gsd-core/workflows/fast.md +8 -22
  76. package/gsd-core/workflows/plan-phase.md +6 -0
  77. package/gsd-core/workflows/progress.md +2 -2
  78. package/gsd-core/workflows/quick.md +2 -0
  79. package/gsd-core/workflows/review.md +42 -3
  80. package/gsd-core/workflows/secure-phase.md +1 -1
  81. package/gsd-core/workflows/settings-advanced.md +7 -4
  82. package/gsd-core/workflows/ship.md +8 -2
  83. package/gsd-core/workflows/spec-phase.md +1 -1
  84. package/gsd-core/workflows/transition.md +1 -1
  85. package/gsd-core/workflows/ui-phase.md +146 -1
  86. package/gsd-core/workflows/validate-phase.md +2 -2
  87. package/hooks/dist/gsd-statusline.js +164 -14
  88. package/hooks/dist/gsd-windsurf-pre-command.js +275 -0
  89. package/hooks/dist/gsd-windsurf-pre-write.js +132 -0
  90. package/hooks/dist/managed-hooks-registry.cjs +2 -0
  91. package/hooks/gsd-statusline.js +164 -14
  92. package/hooks/gsd-windsurf-pre-command.js +275 -0
  93. package/hooks/gsd-windsurf-pre-write.js +132 -0
  94. package/hooks/managed-hooks-registry.cjs +2 -0
  95. package/package.json +10 -4
  96. package/pi/gsd.cjs +354 -0
  97. package/scripts/build-hooks.js +3 -0
  98. package/scripts/ci-test-scope.cjs +39 -1
  99. package/scripts/gen-golden-install-parity-zcode.cjs +35 -35
  100. package/scripts/gen-install-tree-fixtures.cjs +75 -0
  101. package/scripts/gen-registry.cjs +128 -0
  102. package/scripts/lint-allow-test-rule-refs.allowlist.json +0 -1
  103. package/scripts/lint-table-schema-drift.cjs +157 -0
  104. package/scripts/lint-test-file-count.allowlist.json +2 -1
  105. package/scripts/registry-schema.cjs +565 -0
  106. package/scripts/validate-registry.cjs +117 -0
  107. package/skills/gsd-mempalace-capture/SKILL.md +27 -1
  108. package/skills/gsd-surface/SKILL.md +6 -6
  109. package/vscode/browser.js +197 -0
  110. package/vscode/extension.js +383 -0
  111. package/vscode/host-binding.js +113 -0
  112. package/vscode/package.json +96 -0
@@ -55,7 +55,33 @@ This step is `onError: skip` at `discuss:post` / `plan:post` / `verify:post` --
55
55
  On any error or timeout, stop and let the phase continue -- capture is best-effort.
56
56
 
57
57
  1. **Dedup first.** Interactive: `mempalace_check_duplicate` on the artifact's deterministic drawer id. Headless: rely on `mempalace mine`'s content-hash idempotency.
58
- 2. **Add the drawer (verbatim).** File the exact artifact text into `room: <room>` of `wing: <wing>` with provenance (`source_file`, phase id). Interactive: `mempalace_add_drawer`. Headless: `mempalace mine <path> --wing <wing> --room <room>`.
58
+ 2. **Add the drawer (verbatim).** File the exact artifact text into `room: <room>` of `wing: <wing>` with provenance (`source_file`, phase id). Interactive: `mempalace_add_drawer`. Headless: see below.
59
+
60
+ **`mempalace mine` has no `--room` flag** — only `search` accepts `--room` ([CLI reference: https://mempalaceofficial.com/reference/cli.html](https://mempalaceofficial.com/reference/cli.html)). Room assignment is driven by `detect_room()` matching folder-path segments against the `rooms:` list in `mempalace.yaml` ([mining guide: https://mempalaceofficial.com/guide/mining.html](https://mempalaceofficial.com/guide/mining.html) — "Rooms are auto-detected from your folder structure"; [config reference: https://mempalaceofficial.com/guide/configuration.html](https://mempalaceofficial.com/guide/configuration.html)). Stage the artifact under a room-named folder so `detect_room()` assigns it correctly:
61
+
62
+ ```bash
63
+ STAGE=".planning/.mempalace-stage"
64
+ # One-time: declare the GSD room taxonomy so detect_room() recognizes these folders
65
+ mkdir -p "$STAGE"
66
+ [ -f "$STAGE/mempalace.yaml" ] || cat > "$STAGE/mempalace.yaml" <<'YAML'
67
+ rooms:
68
+ - decisions
69
+ - planning
70
+ - milestones
71
+ - problems
72
+ - general
73
+ YAML
74
+ # Suppress MemPalace cache artifacts written into the scanned tree
75
+ [ -f "$STAGE/.gitignore" ] || echo "mempalace_embedder.json" > "$STAGE/.gitignore"
76
+ # Stage under <room>/<phase-id>/<basename> — stable path so mine's content-hash
77
+ # idempotency (file_already_mined keys on absolute source_file + mtime) deduplicates
78
+ # instead of creating duplicate drawers on re-runs.
79
+ ROOM_DIR="$STAGE/<room>/<phase-id>"
80
+ mkdir -p "$ROOM_DIR"
81
+ cp "<artifact-path>" "$ROOM_DIR/<basename>"
82
+ # Mine with --wing only — no --room flag; detect_room() assigns from the folder path
83
+ mempalace mine "$STAGE" --wing <wing>
84
+ ```
59
85
  3. **Mirror KG facts** when `config.mempalace.mirror_kg` is true: extract decision/delivery facts and `mempalace_kg_add` them with `valid_from` = the phase date (e.g. `(<project>, decided, <decision>)` from CONTEXT; `(<phase>, delivered, <capability>)` from SUMMARY). Under `augment` these are an *additive* mirror of GSD's native `.planning/graphs/`. Under `kg_backend`/`replace` the palace KG is the *authoritative* fact store — GSD still produces `.planning/graphs/` through its normal graphify, so an unreachable palace never loses a fact.
60
86
  4. Re-running a phase MUST NOT create duplicate drawers (deterministic ids + `check_duplicate`).
61
87
 
@@ -37,9 +37,9 @@ Parse the first token of $ARGUMENTS:
37
37
  ## list / status
38
38
 
39
39
  Load the capability registry and call `listSurface(runtimeConfigDir, manifest, CLUSTERS, registry)` from
40
- `gsd-core/bin/lib/surface.cjs`. The registry is loaded via:
40
+ the engine module at `${runtimeConfigDir}/gsd-core/bin/lib/surface.cjs`. The registry is loaded via:
41
41
  ```js
42
- const registry = require('gsd-core/bin/lib/capability-registry.cjs');
42
+ const registry = require(runtimeConfigDir + '/gsd-core/bin/lib/capability-registry.cjs');
43
43
  ```
44
44
  Display:
45
45
 
@@ -71,7 +71,7 @@ Install profile: standard (from .gsd-profile)
71
71
  3. `writeSurface(runtimeConfigDir, surfaceState)`.
72
72
  4. Resolve and re-apply:
73
73
  ```js
74
- const registry = require('gsd-core/bin/lib/capability-registry.cjs');
74
+ const registry = require(runtimeConfigDir + '/gsd-core/bin/lib/capability-registry.cjs');
75
75
  const layout = resolveRuntimeArtifactLayout(runtime, runtimeConfigDir, scope);
76
76
  applySurface(runtimeConfigDir, layout, manifest, CLUSTERS, registry);
77
77
  ```
@@ -89,7 +89,7 @@ Valid cluster names: `core_loop`, `audit_review`, `milestone`, `research_ideate`
89
89
  3. Add cluster to `surfaceState.disabledClusters` (deduplicate).
90
90
  4. `writeSurface` → resolve layout → `applySurface`:
91
91
  ```js
92
- const registry = require('gsd-core/bin/lib/capability-registry.cjs');
92
+ const registry = require(runtimeConfigDir + '/gsd-core/bin/lib/capability-registry.cjs');
93
93
  const layout = resolveRuntimeArtifactLayout(runtime, runtimeConfigDir, scope);
94
94
  applySurface(runtimeConfigDir, layout, manifest, CLUSTERS, registry);
95
95
  ```
@@ -103,7 +103,7 @@ Valid cluster names: `core_loop`, `audit_review`, `milestone`, `research_ideate`
103
103
  2. Remove cluster from `surfaceState.disabledClusters`.
104
104
  3. `writeSurface` → resolve layout → `applySurface`:
105
105
  ```js
106
- const registry = require('gsd-core/bin/lib/capability-registry.cjs');
106
+ const registry = require(runtimeConfigDir + '/gsd-core/bin/lib/capability-registry.cjs');
107
107
  const layout = resolveRuntimeArtifactLayout(runtime, runtimeConfigDir, scope);
108
108
  applySurface(runtimeConfigDir, layout, manifest, CLUSTERS, registry);
109
109
  ```
@@ -151,7 +151,7 @@ All paths can be overridden by reading the `CLAUDE_CONFIG_DIR` env var if set.
151
151
 
152
152
  - Unknown cluster name → list valid cluster names, exit without writing.
153
153
  - Unknown profile name → list known profiles (`core`, `standard`, `full`), exit.
154
- - Missing `surface.cjs` → prompt: "Run `npm i -g gsd-core` to reinstall GSD."
154
+ - Missing `surface.cjs` → prompt: "Run `npm i -g @opengsd/gsd-core` to reinstall GSD."
155
155
 
156
156
  <execution_context>
157
157
  Surface state file: `~/.claude/.gsd-surface.json`
@@ -27,6 +27,12 @@
27
27
  * list-todos [area] Count and enumerate pending todos
28
28
  * list-seeds [status] List captured seeds (optional status filter)
29
29
  * verify-path-exists <path> Check file/directory existence
30
+ * quick-tasks-append --task <text> Append a row to STATE.md's "Quick Tasks
31
+ * Completed" table (schema-backed via
32
+ * markdown-table.cjs; #2133/ADR-2143).
33
+ * Fails loud (non-zero exit) on a missing
34
+ * or unrecognized table instead of the old
35
+ * awk NF-2 silent-skip guess.
30
36
  * config-ensure-section Initialize .planning/config.json
31
37
  * history-digest Aggregate all SUMMARY.md data
32
38
  * summary-extract <path> [--fields] Extract structured data from SUMMARY.md
@@ -693,7 +699,7 @@ async function main() {
693
699
  'from-gsd2, frontmatter, gap-analysis, generate-claude-md, generate-claude-profile, ' +
694
700
  'generate-dev-preferences, generate-slug, graphify, history-digest, init, intel, ' +
695
701
  'capability, classify-confidence, git, learnings, list-seeds, list-todos, loop, milestone, package-legitimacy, phase, phase-plan-index, phases, profile-questionnaire, ' +
696
- 'profile-sample, progress, project-instruction-file, prompt-budget, requirements, research-plan, research-store, resolve-granularity, resolve-model, roadmap, scaffold, smart-entry, state, ' +
702
+ 'profile-sample, progress, project-instruction-file, prompt-budget, quick-tasks-append, requirements, research-plan, research-store, resolve-granularity, resolve-model, roadmap, scaffold, smart-entry, state, ' +
697
703
  'task, template, user-story, validate, verify, verify-path-exists, verify-summary, eval, workstream, worktree\n\n' +
698
704
  'Global flags:\n' +
699
705
  ' --raw Emit raw output without post-processing\n' +
@@ -1213,6 +1219,59 @@ async function runCommand(command, args, cwd, raw, defaultValue, originalCommand
1213
1219
  break;
1214
1220
  }
1215
1221
 
1222
+ case 'quick-tasks-append': {
1223
+ // #2133 / ADR-2143 §3,§7: schema-backed replacement for fast.md's inline
1224
+ // `awk NF-2` Quick Tasks column arithmetic. Row construction is delegated
1225
+ // to the pure appendQuickTaskRow (markdown-table.cjs); this case only
1226
+ // handles the I/O (read STATE.md, resolve date/commit, write STATE.md).
1227
+ const qtaArgs = args.slice(1);
1228
+ const qtaTask = parseNamedArgs(qtaArgs, ['task']).task || args[1];
1229
+ if (!qtaTask) {
1230
+ error('quick-tasks-append requires --task <description> (or a positional description)', ERROR_REASON.USAGE);
1231
+ }
1232
+
1233
+ const statePath = path.join(cwd, '.planning', 'STATE.md');
1234
+ if (!fs.existsSync(statePath)) {
1235
+ error(`quick-tasks-append: STATE.md not found at ${statePath}`, ERROR_REASON.USAGE);
1236
+ }
1237
+
1238
+ const date = new Date().toISOString().slice(0, 10);
1239
+ const { execGit } = require('./lib/shell-command-projection.cjs');
1240
+ const hashResult = execGit(['rev-parse', '--short', 'HEAD'], { cwd });
1241
+ const commit = hashResult.exitCode === 0 && hashResult.stdout ? hashResult.stdout : '—';
1242
+
1243
+ const { appendQuickTaskRow } = require('./lib/markdown-table.cjs');
1244
+
1245
+ // #2242 review fix: route the read -> mutate -> write cycle through
1246
+ // state.readModifyWriteStateMd (lib/state.cjs) instead of a raw
1247
+ // fs.readFileSync + fs.writeFileSync pair, so the whole read-modify-write
1248
+ // is atomic under STATE.md's lockfile — closing the lost-update race a
1249
+ // raw read/write pair left open (cf. #500/#905/#1230). This mirrors the
1250
+ // pattern every other STATE.md-mutating case in state.cts uses (e.g.
1251
+ // cmdStateAddBlocker, cmdStateAddDecision): a mutable outer variable
1252
+ // captures the pure helper's side output, and a fail-loud reason throws
1253
+ // ExitError from INSIDE the transform (readModifyWriteStateMd's finally
1254
+ // still releases the lock before the throw propagates; the transform
1255
+ // throws before returning new content, so nothing is ever written).
1256
+ let mutation;
1257
+ state.readModifyWriteStateMd(statePath, (content) => {
1258
+ const result = appendQuickTaskRow(content, { description: qtaTask, date, commit });
1259
+ if (!result.ok) {
1260
+ // Mirrors fast.md's old "skip with a brief log" behaviour (#2133): this
1261
+ // is an expected, recoverable condition (no table / unrecognized
1262
+ // schema), not a hard crash. ExitError sets a non-zero exit code (so
1263
+ // fast.md's `|| echo ...` fallback fires) without calling
1264
+ // process.exit() directly — stdout stays flushed and untouched.
1265
+ throw new ExitError(1, `⚠ quick-tasks-append: ${result.reason}`);
1266
+ }
1267
+ mutation = result.value;
1268
+ return result.value.content;
1269
+ }, cwd);
1270
+
1271
+ output({ ok: true, row: mutation.row, variant: mutation.variant }, raw, mutation.row);
1272
+ break;
1273
+ }
1274
+
1216
1275
  case 'config-ensure-section': {
1217
1276
  // Phase 6 (#3575): dispatch via SDK executeForCjs. The catalog rebinds
1218
1277
  // 'config-ensure-section' to configNewProject in
@@ -1500,7 +1559,9 @@ async function runCommand(command, args, cwd, raw, defaultValue, originalCommand
1500
1559
  // new-milestone never inherits un-archived dirs. --no-archive-phases opts out.
1501
1560
  const archivePhases = !args.includes('--no-archive-phases');
1502
1561
  const force = args.includes('--force');
1503
- milestone.cmdMilestoneComplete(cwd, args[2], { name: milestoneName, archivePhases, force }, raw);
1562
+ // #2118: --dry-run prints a preview plan without mutating.
1563
+ const dryRun = args.includes('--dry-run');
1564
+ milestone.cmdMilestoneComplete(cwd, args[2], { name: milestoneName, archivePhases, force, dryRun }, raw);
1504
1565
  } else {
1505
1566
  error('Unknown milestone subcommand. Available: complete', ERROR_REASON.SDK_UNKNOWN_COMMAND);
1506
1567
  }
@@ -264,13 +264,12 @@ function parseCoverageMatrix(text) {
264
264
  const src = text.replace(/\r\n/g, '\n');
265
265
  // (1) fenced ```coverage JSON block takes precedence if present.
266
266
  // Case-insensitive info string (```coverage and ```Coverage are both legal CommonMark).
267
- // allow-adhoc-markdown: extracting a NAMED ```coverage fence (extraction of one tagged block), not stripping all fences — stripFencedCode/extractTaggedBlocks do not cover named-fence extraction.
268
- const fenceMatch = src.match(/```coverage\s*\n([\s\S]*?)\n```/i);
269
- if (fenceMatch && fenceMatch[1]) {
267
+ const fenceBody = (0, markdown_sectionizer_cjs_1.extractFencedBlock)(src, 'coverage');
268
+ if (fenceBody) {
270
269
  out.format = 'json';
271
270
  let parsed;
272
271
  try {
273
- parsed = JSON.parse(fenceMatch[1]);
272
+ parsed = JSON.parse(fenceBody);
274
273
  }
275
274
  catch {
276
275
  out.errors.push('fenced ```coverage block is not valid JSON');
@@ -17,6 +17,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
17
17
  const node_fs_1 = __importDefault(require("node:fs"));
18
18
  const node_path_1 = __importDefault(require("node:path"));
19
19
  const shell_command_projection_cjs_1 = require("./shell-command-projection.cjs");
20
+ const markdown_sectionizer_cjs_1 = require("./markdown-sectionizer.cjs");
20
21
  // eslint-disable-next-line @typescript-eslint/no-require-imports
21
22
  const planningWorkspace = require("./planning-workspace.cjs");
22
23
  const { planningDir } = planningWorkspace;
@@ -71,9 +72,9 @@ function scanDebugSessions(planDir) {
71
72
  continue;
72
73
  // Extract hypothesis from "Current Focus" block if parseable
73
74
  let hypothesis = '';
74
- const focusMatch = content.match(/##\s*Current Focus[^\n]*\n([\s\S]*?)(?=\n##\s|$)/i); // allow-adhoc-markdown: pre-seam read-only section extract in audit.cts; pending migration #1372
75
- if (focusMatch) {
76
- const focusText = focusMatch[1].trim().split('\n')[0].trim();
75
+ const focusSection = (0, markdown_sectionizer_cjs_1.collectSection)(content, (h) => h.level === 2 && h.text.trim().toLowerCase().startsWith('current focus'), { levelBounded: true });
76
+ if (focusSection) {
77
+ const focusText = focusSection.body.trim().split('\n')[0].trim();
77
78
  hypothesis = (0, security_cjs_1.sanitizeForDisplay)(focusText.slice(0, 100));
78
79
  }
79
80
  const slug = node_path_1.default.basename(entry.name, '.md');
@@ -509,9 +510,9 @@ function scanContextQuestions(planDir) {
509
510
  }
510
511
  // Also check for ## Open Questions section in body
511
512
  if (questions.length === 0) {
512
- const oqMatch = content.match(/##\s*Open Questions[^\n]*\n([\s\S]*?)(?=\n##\s|$)/i); // allow-adhoc-markdown: pre-seam read-only section extract in audit.cts; pending migration #1372
513
- if (oqMatch) {
514
- const oqBody = oqMatch[1].trim();
513
+ const oqSection = (0, markdown_sectionizer_cjs_1.collectSection)(content, (h) => h.level === 2 && h.text.trim().toLowerCase().startsWith('open questions'), { levelBounded: true });
514
+ if (oqSection) {
515
+ const oqBody = oqSection.body.trim();
515
516
  if (oqBody && oqBody.length > 0 && !/^\s*none\s*$/i.test(oqBody)) {
516
517
  const items = oqBody.split('\n')
517
518
  .map((l) => l.trim())