@opengsd/gsd-core 1.5.0 → 1.6.0-rc.2

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 (95) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/agents/gsd-plan-checker.md +34 -0
  3. package/agents/gsd-planner.md +2 -0
  4. package/agents/gsd-roadmapper.md +6 -0
  5. package/bin/install.js +199 -365
  6. package/commands/gsd/capture.md +5 -1
  7. package/gemini-extension.json +1 -1
  8. package/gsd-core/bin/gsd-tools.cjs +695 -5
  9. package/gsd-core/bin/lib/adr-parser.cjs +45 -23
  10. package/gsd-core/bin/lib/audit.cjs +2 -2
  11. package/gsd-core/bin/lib/capability-consent.cjs +763 -0
  12. package/gsd-core/bin/lib/capability-ledger.cjs +831 -0
  13. package/gsd-core/bin/lib/capability-lifecycle.cjs +1551 -0
  14. package/gsd-core/bin/lib/capability-loader.cjs +764 -0
  15. package/gsd-core/bin/lib/capability-lock.cjs +553 -0
  16. package/gsd-core/bin/lib/capability-registry.cjs +198 -4
  17. package/gsd-core/bin/lib/capability-source.cjs +1242 -0
  18. package/gsd-core/bin/lib/capability-state.cjs +9 -6
  19. package/gsd-core/bin/lib/capability-trust.cjs +550 -0
  20. package/gsd-core/bin/lib/capability-validator.cjs +2066 -0
  21. package/gsd-core/bin/lib/capability-writer.cjs +14 -5
  22. package/gsd-core/bin/lib/check-command-router.cjs +69 -18
  23. package/gsd-core/bin/lib/command-aliases.cjs +8 -0
  24. package/gsd-core/bin/lib/commands.cjs +247 -0
  25. package/gsd-core/bin/lib/config-loader.cjs +98 -84
  26. package/gsd-core/bin/lib/config-schema.cjs +26 -7
  27. package/gsd-core/bin/lib/config.cjs +7 -1
  28. package/gsd-core/bin/lib/decisions.cjs +149 -60
  29. package/gsd-core/bin/lib/frontmatter.cjs +7 -3
  30. package/gsd-core/bin/lib/gap-checker.cjs +126 -11
  31. package/gsd-core/bin/lib/init.cjs +91 -22
  32. package/gsd-core/bin/lib/legacy-cleanup.cjs +96 -0
  33. package/gsd-core/bin/lib/loop-resolver.cjs +26 -2
  34. package/gsd-core/bin/lib/markdown-sectionizer.cjs +471 -0
  35. package/gsd-core/bin/lib/milestone.cjs +41 -2
  36. package/gsd-core/bin/lib/phase-command-router.cjs +5 -0
  37. package/gsd-core/bin/lib/phase-id.cjs +25 -11
  38. package/gsd-core/bin/lib/phase-lifecycle.cjs +14 -5
  39. package/gsd-core/bin/lib/phase.cjs +33 -4
  40. package/gsd-core/bin/lib/probe-core.cjs +7 -0
  41. package/gsd-core/bin/lib/prohibition-enforcement.cjs +59 -26
  42. package/gsd-core/bin/lib/project-root.cjs +89 -2
  43. package/gsd-core/bin/lib/resolution.cjs +26 -0
  44. package/gsd-core/bin/lib/roadmap-command-router.cjs +16 -3
  45. package/gsd-core/bin/lib/roadmap-parser.cjs +73 -106
  46. package/gsd-core/bin/lib/roadmap-upgrade.cjs +47 -17
  47. package/gsd-core/bin/lib/roadmap.cjs +5 -2
  48. package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +423 -3
  49. package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +77 -0
  50. package/gsd-core/bin/lib/runtime-artifact-layout.cjs +1 -28
  51. package/gsd-core/bin/lib/runtime-homes.cjs +53 -1
  52. package/gsd-core/bin/lib/runtime-name-policy.cjs +44 -0
  53. package/gsd-core/bin/lib/semver-compare.cjs +127 -0
  54. package/gsd-core/bin/lib/shell-command-projection.cjs +55 -1
  55. package/gsd-core/bin/lib/state-document.cjs +4 -2
  56. package/gsd-core/bin/lib/state.cjs +317 -161
  57. package/gsd-core/bin/lib/surface.cjs +12 -19
  58. package/gsd-core/bin/lib/uat-predicate.cjs +7 -47
  59. package/gsd-core/bin/lib/uat.cjs +39 -26
  60. package/gsd-core/bin/lib/validate.cjs +5 -2
  61. package/gsd-core/bin/lib/verify.cjs +40 -15
  62. package/gsd-core/bin/lib/worktree-safety.cjs +202 -0
  63. package/gsd-core/bin/shared/config-defaults.manifest.json +6 -1
  64. package/gsd-core/bin/shared/config-schema.manifest.json +5 -1
  65. package/gsd-core/references/context-budget.md +8 -8
  66. package/gsd-core/references/execute-phase-between-wave-reset.md +43 -0
  67. package/gsd-core/references/execute-phase-context-guard.md +16 -0
  68. package/gsd-core/references/execute-phase-wave-guard.md +33 -0
  69. package/gsd-core/references/planner-antipatterns.md +48 -0
  70. package/gsd-core/references/planning-config.md +4 -0
  71. package/gsd-core/references/prohibition-probe.md +15 -9
  72. package/gsd-core/references/scout-codebase.md +2 -2
  73. package/gsd-core/workflows/autonomous.md +33 -33
  74. package/gsd-core/workflows/diagnose-issues.md +6 -1
  75. package/gsd-core/workflows/discuss-phase/templates/context.md +1 -1
  76. package/gsd-core/workflows/discuss-phase.md +1 -2
  77. package/gsd-core/workflows/execute-phase.md +12 -12
  78. package/gsd-core/workflows/help/modes/full.md +10 -0
  79. package/gsd-core/workflows/list-seeds.md +63 -0
  80. package/gsd-core/workflows/manager.md +37 -37
  81. package/gsd-core/workflows/pr-branch.md +156 -0
  82. package/gsd-core/workflows/quick.md +6 -1
  83. package/gsd-core/workflows/review.md +10 -2
  84. package/gsd-core/workflows/spec-phase.md +8 -3
  85. package/gsd-core/workflows/verify-phase.md +2 -2
  86. package/package.json +6 -3
  87. package/scripts/gen-capability-matrix.cjs +284 -0
  88. package/scripts/gen-capability-registry.cjs +96 -1853
  89. package/scripts/lint-regression-test-names.allowlist.json +1 -0
  90. package/scripts/lint-resolution-provenance.allowlist.json +1 -0
  91. package/scripts/lint-resolution-provenance.cjs +192 -0
  92. package/scripts/lint-test-file-count.allowlist.json +9 -0
  93. package/scripts/prompt-injection-scan.sh +1 -0
  94. package/scripts/run-tests.cjs +14 -0
  95. package/scripts/sync-manifest-versions.cjs +77 -5
@@ -13,6 +13,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
13
13
  const node_fs_1 = __importDefault(require("node:fs"));
14
14
  const node_path_1 = __importDefault(require("node:path"));
15
15
  const security_cjs_1 = require("./security.cjs");
16
+ const markdown_sectionizer_cjs_1 = require("./markdown-sectionizer.cjs");
16
17
  const STATUS_REJECT_SET = new Set(['superseded', 'rejected', 'deprecated']);
17
18
  const CANONICAL_HEADERS = {
18
19
  status: ['status', 'state', 'lifecycle', 'stage'],
@@ -57,7 +58,6 @@ const CANONICAL_HEADERS = {
57
58
  'candidates',
58
59
  'approaches considered',
59
60
  'variants',
60
- 'trade-offs',
61
61
  'pros and cons of the options',
62
62
  'discussion',
63
63
  ],
@@ -189,15 +189,31 @@ function normalizeAdrHeader(raw) {
189
189
  .replace(/[^\w\s]/g, '')
190
190
  .trim();
191
191
  }
192
- function classifyHeader(normalizedHeader) {
192
+ // Normalized synonym index (audit M7). classifyHeader receives an ALREADY-normalized
193
+ // header (via normalizeAdrHeader), but historically compared it against the RAW synonym
194
+ // strings. Because normalizeAdrHeader collapses [\s:._-]+ to a space and strips [^\w\s],
195
+ // any synonym carrying a hyphen/apostrophe/etc. ('trade-offs', "won't do", 'post-grilling')
196
+ // could never match a normalized header — it was silently dead, and its ADR section went
197
+ // unmapped. Normalizing BOTH sides closes that abstraction asymmetry once, so every synonym
198
+ // (current and future) is reachable regardless of punctuation. Precomputed at module load to
199
+ // avoid re-normalizing the whole table per call; insertion order is preserved so first-match-
200
+ // wins and the exact-then-prefix precedence stay identical to the prior raw-compare loop.
201
+ const _NORMALIZED_SYNONYM_INDEX = (() => {
202
+ const index = [];
193
203
  for (const [canonical, synonyms] of Object.entries(CANONICAL_HEADERS)) {
194
204
  for (const synonym of synonyms) {
195
- if (normalizedHeader === synonym)
196
- return canonical;
197
- if (normalizedHeader.startsWith(`${synonym} `))
198
- return canonical;
205
+ index.push([normalizeAdrHeader(synonym), canonical]);
199
206
  }
200
207
  }
208
+ return index;
209
+ })();
210
+ function classifyHeader(normalizedHeader) {
211
+ for (const [synonym, canonical] of _NORMALIZED_SYNONYM_INDEX) {
212
+ if (normalizedHeader === synonym)
213
+ return canonical;
214
+ if (normalizedHeader.startsWith(`${synonym} `))
215
+ return canonical;
216
+ }
201
217
  return null;
202
218
  }
203
219
  function splitEntries(blockText) {
@@ -208,24 +224,30 @@ function splitEntries(blockText) {
208
224
  .map((line) => line.replace(/^[-*+]\s+/, '').trim())
209
225
  .filter(Boolean);
210
226
  }
227
+ /**
228
+ * Thin adapter: wraps the seam's `collectSections` to produce the same
229
+ * `{ heading: string | null, body: string[] }` shape the rest of adr-parser
230
+ * consumes. ADR-1372 T2 migration.
231
+ */
211
232
  function parseSections(markdown) {
212
- const lines = (typeof markdown === 'string' ? markdown : '').split(/\r?\n/);
213
- const sections = [];
214
- let current = { heading: null, body: [] };
215
- for (const line of lines) {
216
- const m = line.match(/^#{1,6}\s+(.*)$/);
217
- if (m) {
218
- if (current.heading || current.body.length)
219
- sections.push(current);
220
- current = { heading: m[1].trim(), body: [] };
221
- }
222
- else {
223
- current.body.push(line);
224
- }
225
- }
226
- if (current.heading || current.body.length)
227
- sections.push(current);
228
- return sections;
233
+ const content = typeof markdown === 'string' ? markdown : '';
234
+ // collectSections(content, () => true) collects every heading as a stop
235
+ // boundary — mirrors the old line-by-line heading walk exactly.
236
+ const sections = (0, markdown_sectionizer_cjs_1.collectSections)(content, () => true);
237
+ // Map seam Section → MarkdownSection. The seam's HeadingToken.text is the
238
+ // heading text after trimming (same as the old m[1].trim() capture).
239
+ // The body is a trimEnd()-ed joined string; split it back to lines to match
240
+ // the old string[] shape consumed by parseStatusFromSections / parseAdrMarkdown.
241
+ //
242
+ // Note: the old parseSections emitted a leading { heading: null, body: [...] }
243
+ // entry for preamble text before the first heading. Both consumers skip it
244
+ // immediately (parseAdrMarkdown: `if (!heading) continue`; parseStatusFromSections:
245
+ // `classifyHeader(normalizeAdrHeader(null))` → null ≠ 'status' → continue), so
246
+ // the preamble entry was dead code and is not reconstructed here.
247
+ return sections.map((sec) => ({
248
+ heading: sec.heading.text,
249
+ body: sec.body === '' ? [] : sec.body.split('\n'),
250
+ }));
229
251
  }
230
252
  function parseStatusFromSections(sections) {
231
253
  for (const section of sections) {
@@ -68,7 +68,7 @@ function scanDebugSessions(planDir) {
68
68
  continue;
69
69
  // Extract hypothesis from "Current Focus" block if parseable
70
70
  let hypothesis = '';
71
- const focusMatch = content.match(/##\s*Current Focus[^\n]*\n([\s\S]*?)(?=\n##\s|$)/i);
71
+ 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
72
72
  if (focusMatch) {
73
73
  const focusText = focusMatch[1].trim().split('\n')[0].trim();
74
74
  hypothesis = (0, security_cjs_1.sanitizeForDisplay)(focusText.slice(0, 100));
@@ -506,7 +506,7 @@ function scanContextQuestions(planDir) {
506
506
  }
507
507
  // Also check for ## Open Questions section in body
508
508
  if (questions.length === 0) {
509
- const oqMatch = content.match(/##\s*Open Questions[^\n]*\n([\s\S]*?)(?=\n##\s|$)/i);
509
+ 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
510
510
  if (oqMatch) {
511
511
  const oqBody = oqMatch[1].trim();
512
512
  if (oqBody && oqBody.length > 0 && !/^\s*none\s*$/i.test(oqBody)) {