@opengsd/gsd-core 1.7.0-rc.4 → 1.7.0-rc.6

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 (113) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.opencode/plugins/gsd-core.js +20 -0
  4. package/agents/gsd-doc-classifier.md +105 -0
  5. package/agents/gsd-doc-synthesizer.md +61 -0
  6. package/agents/gsd-ui-checker.md +30 -0
  7. package/agents/gsd-ui-researcher.md +1 -0
  8. package/bin/install.js +1568 -622
  9. package/gsd-core/bin/gsd-tools.cjs +40 -1
  10. package/gsd-core/bin/lib/api-coverage.cjs +466 -0
  11. package/gsd-core/bin/lib/audit.cjs +6 -3
  12. package/gsd-core/bin/lib/capability-loader.cjs +11 -9
  13. package/gsd-core/bin/lib/capability-registry.cjs +761 -84
  14. package/gsd-core/bin/lib/capability-validator.cjs +56 -18
  15. package/gsd-core/bin/lib/capability-writer.cjs +10 -1
  16. package/gsd-core/bin/lib/check-command-router.cjs +242 -3
  17. package/gsd-core/bin/lib/commands.cjs +7 -5
  18. package/gsd-core/bin/lib/config-loader.cjs +1 -0
  19. package/gsd-core/bin/lib/config.cjs +96 -0
  20. package/gsd-core/bin/lib/core-utils.cjs +4 -1
  21. package/gsd-core/bin/lib/host-integration-adapters/cline-sdk-binding.cjs +234 -0
  22. package/gsd-core/bin/lib/host-integration-adapters/imperative-hook-bus.cjs +145 -0
  23. package/gsd-core/bin/lib/host-integration.cjs +45 -4
  24. package/gsd-core/bin/lib/init.cjs +76 -39
  25. package/gsd-core/bin/lib/install-effort-resolver.cjs +213 -0
  26. package/gsd-core/bin/lib/install-engine.cjs +228 -18
  27. package/gsd-core/bin/lib/installer-migration-report.cjs +7 -0
  28. package/gsd-core/bin/lib/loop-resolver.cjs +68 -17
  29. package/gsd-core/bin/lib/markdown-sectionizer.cjs +50 -11
  30. package/gsd-core/bin/lib/mcp-server.cjs +18 -7
  31. package/gsd-core/bin/lib/milestone.cjs +3 -3
  32. package/gsd-core/bin/lib/normalize-test-command.cjs +187 -0
  33. package/gsd-core/bin/lib/phase-id.cjs +132 -3
  34. package/gsd-core/bin/lib/phase.cjs +78 -16
  35. package/gsd-core/bin/lib/planning-workspace.cjs +17 -0
  36. package/gsd-core/bin/lib/review-reviewer-selection.cjs +24 -7
  37. package/gsd-core/bin/lib/roadmap-command-router.cjs +5 -4
  38. package/gsd-core/bin/lib/roadmap-parser.cjs +21 -30
  39. package/gsd-core/bin/lib/roadmap-upgrade.cjs +9 -9
  40. package/gsd-core/bin/lib/roadmap.cjs +42 -56
  41. package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +248 -44
  42. package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +2 -2
  43. package/gsd-core/bin/lib/runtime-artifact-layout.cjs +39 -23
  44. package/gsd-core/bin/lib/runtime-config-adapter-registry.cjs +19 -5
  45. package/gsd-core/bin/lib/runtime-homes.cjs +30 -0
  46. package/gsd-core/bin/lib/runtime-hooks-surface.cjs +534 -37
  47. package/gsd-core/bin/lib/runtime-name-policy.cjs +63 -5
  48. package/gsd-core/bin/lib/security.cjs +6 -36
  49. package/gsd-core/bin/lib/shell-command-projection.cjs +115 -2
  50. package/gsd-core/bin/lib/spec-section.cjs +111 -0
  51. package/gsd-core/bin/lib/stale-bake-guard.cjs +30 -10
  52. package/gsd-core/bin/lib/state-transition.cjs +1 -1
  53. package/gsd-core/bin/lib/state.cjs +24 -24
  54. package/gsd-core/bin/lib/surface.cjs +40 -6
  55. package/gsd-core/bin/lib/uat.cjs +4 -1
  56. package/gsd-core/bin/lib/ui-consideration-probe.cjs +249 -0
  57. package/gsd-core/bin/lib/validate.cjs +15 -6
  58. package/gsd-core/bin/lib/verify.cjs +33 -37
  59. package/gsd-core/bin/shared/config-schema.manifest.json +2 -0
  60. package/gsd-core/bin/shared/model-catalog.json +14 -9
  61. package/gsd-core/references/api-coverage.md +104 -0
  62. package/gsd-core/references/model-profiles.md +2 -2
  63. package/gsd-core/references/planning-config.md +2 -0
  64. package/gsd-core/references/specless-probe-fallback.md +172 -0
  65. package/gsd-core/references/ui-consideration-probe.md +73 -0
  66. package/gsd-core/templates/UI-SPEC.md +25 -0
  67. package/gsd-core/templates/VALIDATION.md +2 -0
  68. package/gsd-core/templates/config.json +2 -1
  69. package/gsd-core/workflows/audit-fix.md +9 -1
  70. package/gsd-core/workflows/audit-milestone.md +7 -4
  71. package/gsd-core/workflows/code-review-fix.md +7 -3
  72. package/gsd-core/workflows/code-review.md +4 -1
  73. package/gsd-core/workflows/discuss-phase-assumptions.md +4 -1
  74. package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +8 -4
  75. package/gsd-core/workflows/execute-phase/steps/regression-gate.md +42 -0
  76. package/gsd-core/workflows/execute-phase.md +1 -25
  77. package/gsd-core/workflows/plan-phase.md +37 -2
  78. package/gsd-core/workflows/quick.md +2 -2
  79. package/gsd-core/workflows/review.md +59 -13
  80. package/gsd-core/workflows/settings-advanced.md +12 -9
  81. package/gsd-core/workflows/settings.md +2 -2
  82. package/gsd-core/workflows/ui-phase.md +146 -1
  83. package/gsd-core/workflows/validate-phase.md +2 -2
  84. package/gsd-core/workflows/verify-phase.md +3 -2
  85. package/gsd-core/workflows/verify-work.md +38 -0
  86. package/hooks/dist/gsd-cursor-pre-tool.js +76 -0
  87. package/hooks/dist/gsd-cursor-stop.js +48 -0
  88. package/hooks/dist/gsd-cursor-subagent-start.js +50 -0
  89. package/hooks/dist/gsd-cursor-subagent-stop.js +40 -0
  90. package/hooks/dist/gsd-windsurf-pre-command.js +275 -0
  91. package/hooks/dist/gsd-windsurf-pre-write.js +132 -0
  92. package/hooks/dist/managed-hooks-registry.cjs +6 -0
  93. package/hooks/gsd-cursor-pre-tool.js +76 -0
  94. package/hooks/gsd-cursor-stop.js +48 -0
  95. package/hooks/gsd-cursor-subagent-start.js +50 -0
  96. package/hooks/gsd-cursor-subagent-stop.js +40 -0
  97. package/hooks/gsd-windsurf-pre-command.js +275 -0
  98. package/hooks/gsd-windsurf-pre-write.js +132 -0
  99. package/hooks/managed-hooks-registry.cjs +6 -0
  100. package/package.json +9 -4
  101. package/pi/gsd.cjs +354 -0
  102. package/scripts/build-hooks.js +8 -1
  103. package/scripts/gen-golden-install-parity-zcode.cjs +11 -1
  104. package/scripts/gen-registry.cjs +128 -0
  105. package/scripts/lint-phase-id-drift.cjs +150 -0
  106. package/scripts/lint-test-file-count.allowlist.json +2 -1
  107. package/scripts/registry-schema.cjs +565 -0
  108. package/scripts/run-tests.cjs +21 -1
  109. package/scripts/validate-registry.cjs +117 -0
  110. package/vscode/browser.js +197 -0
  111. package/vscode/extension.js +383 -0
  112. package/vscode/host-binding.js +113 -0
  113. package/vscode/package.json +96 -0
@@ -0,0 +1,150 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /**
5
+ * Anti-divergence drift guard for the phase-identifier parsing seam
6
+ * (epic #2121, Phase 4 / issue #2128, locked by ADR-2121 Decision 7).
7
+ *
8
+ * `src/phase-id.cts` is the SINGLE canonical owner of phase-ID parsing. Its
9
+ * `PHASE_NUMBER_TOKEN_SOURCE` (and `phaseMarkdownRegexSource` for a known number)
10
+ * is the one place the phase-number-token grammar `\d+[A-Z]?(?:\.\d+)*` is
11
+ * defined. Every other module that scans/enumerates phase headings must build
12
+ * its regex from that source rather than re-deriving the grammar as a literal —
13
+ * otherwise the trio drifts again (the #2111 / #2114 / #2104 recurrence loop this
14
+ * epic closes).
15
+ *
16
+ * This lint makes the invariant machine-enforced: it FAILS the moment a literal
17
+ * re-derivation of the canonical token grammar is introduced anywhere in
18
+ * `src/**` outside `phase-id.cts`, unless the site is deliberately sanctioned
19
+ * with a `// phase-id-owner: <reason>` comment (on the same line or the line
20
+ * directly above). Sites that build their regex from `PHASE_NUMBER_TOKEN_SOURCE`
21
+ * carry no literal grammar and pass automatically.
22
+ *
23
+ * Detection is intentionally NARROW: only the contiguous canonical token
24
+ * (`\d+[A-Z]?(?:\.\d+)*`, its `[A-Za-z]` and `[.-]` near-variants, in both
25
+ * regex-literal `\d` and `new RegExp` template `\\d` escaping) is drift. Bare
26
+ * `\d+` probes, `[\w][\w.-]*` ids, digits-only captures, status-message text
27
+ * (`Phase\s+\d`), and pipe-table structures are NOT phase-token re-derivations
28
+ * and are not flagged.
29
+ */
30
+
31
+ const fs = require('node:fs');
32
+ const path = require('node:path');
33
+
34
+ // The canonical phase-number token as it appears in SOURCE TEXT:
35
+ // \d+[A-Z]?(?:\.\d+)* in a regex literal -> one backslash before d/.
36
+ // \\d+[A-Z]?(?:\\.\\d+)* in a template string -> two backslashes
37
+ // Tolerated near-variants so a trivial rewrite does not silently evade the guard:
38
+ // digit class \d \\d or [0-9]
39
+ // letter class [A-Z] or [A-Za-z]
40
+ // sub-phase sep \. \\. or [.-] (dot-or-dash)
41
+ // KNOWN, ACCEPTED limits of a per-line textual scan (covered instead by the
42
+ // identity guard + code review, not by this regex): a re-derivation split
43
+ // across lines via string concatenation, a capturing `(\.\d+)*` in place of the
44
+ // non-capturing group, or a semantically-equivalent restructuring. This guard
45
+ // targets the common case — an accidental copy of the exact grammar — not an
46
+ // adversary deliberately obfuscating a re-derivation.
47
+ const TOKEN_DRIFT_RE = /(?:\\{1,2}d|\[0-9\])\+\[A-Z(?:a-z)?\]\??\(\?:(?:\\{1,2}\.|\[\.-\])(?:\\{1,2}d|\[0-9\])\+\)\*/;
48
+
49
+ // A `phase-id-owner:` sanction must be a DEDICATED `//` comment line (the marker
50
+ // as the line's leading token). A `//` or the phrase embedded in a string
51
+ // literal or trailing a code line is NOT a comment and must never suppress a real
52
+ // flag — so sanctions live on their own line directly above the regex.
53
+ const OWNER_RE = /^\s*\/\/.*phase-id-owner:/;
54
+ const CANON_REF = 'PHASE_NUMBER_TOKEN_SOURCE';
55
+
56
+ /**
57
+ * Pure: find every literal re-derivation of the canonical phase-number token in
58
+ * `text` that is NOT sanctioned. A site is sanctioned when the nearest preceding
59
+ * NON-BLANK line is a dedicated `// phase-id-owner:` comment (blank lines between
60
+ * the comment and the regex are tolerated, so an auto-formatter cannot reactivate
61
+ * the flag), or when the regex line references `PHASE_NUMBER_TOKEN_SOURCE` (built
62
+ * from the canonical source, not a literal). A `//`/phrase inside a string or
63
+ * trailing a code line does NOT count — put the sanction on its own line above.
64
+ * Returns [{ line, found }].
65
+ */
66
+ function findPhaseIdRegexDrift(text) {
67
+ const out = [];
68
+ const lines = text.split('\n');
69
+ for (let i = 0; i < lines.length; i++) {
70
+ const line = lines[i];
71
+ const m = TOKEN_DRIFT_RE.exec(line);
72
+ if (!m) continue;
73
+ if (line.includes(CANON_REF)) continue;
74
+ let j = i - 1;
75
+ while (j >= 0 && lines[j].trim() === '') j--; // nearest preceding non-blank line
76
+ if (j >= 0 && OWNER_RE.test(lines[j])) continue;
77
+ out.push({ line: i + 1, found: m[0] });
78
+ }
79
+ return out;
80
+ }
81
+
82
+ // Authored TypeScript source only (the generated bin/lib/*.cjs mirror it).
83
+ const SCAN_DIRS = ['src'];
84
+ const SCAN_EXT = new Set(['.cts', '.ts', '.mts']);
85
+ // The canonical owner defines the grammar; it is exempt by construction.
86
+ const EXEMPT = new Set([path.join('src', 'phase-id.cts')]);
87
+
88
+ function walk(dir, acc) {
89
+ let entries;
90
+ try {
91
+ entries = fs.readdirSync(dir, { withFileTypes: true });
92
+ } catch {
93
+ return acc;
94
+ }
95
+ for (const entry of entries) {
96
+ const full = path.join(dir, entry.name);
97
+ if (entry.isDirectory()) {
98
+ if (entry.name === 'node_modules' || entry.name === 'dist' || entry.name === '.git') continue;
99
+ walk(full, acc);
100
+ } else if (entry.isFile() && SCAN_EXT.has(path.extname(entry.name))) {
101
+ acc.push(full);
102
+ }
103
+ }
104
+ return acc;
105
+ }
106
+
107
+ /**
108
+ * Scan the authored source tree and return every unsanctioned phase-token
109
+ * re-derivation, each annotated with the repo-relative file path.
110
+ */
111
+ function scanRepo(root) {
112
+ const violations = [];
113
+ for (const dir of SCAN_DIRS) {
114
+ for (const file of walk(path.join(root, dir), [])) {
115
+ const rel = path.relative(root, file);
116
+ if (EXEMPT.has(rel)) continue;
117
+ let text;
118
+ try {
119
+ text = fs.readFileSync(file, 'utf8');
120
+ } catch {
121
+ continue;
122
+ }
123
+ for (const d of findPhaseIdRegexDrift(text)) {
124
+ violations.push({ file: rel, ...d });
125
+ }
126
+ }
127
+ }
128
+ return violations;
129
+ }
130
+
131
+ function main() {
132
+ const root = path.join(__dirname, '..');
133
+ const violations = scanRepo(root);
134
+ if (violations.length === 0) {
135
+ process.stdout.write('ok phase-id-drift: no unsanctioned phase-token re-derivations outside phase-id.cts\n');
136
+ return;
137
+ }
138
+ process.stderr.write('phase-id-drift: literal re-derivation(s) of the canonical phase-number token found.\n');
139
+ process.stderr.write('Build the regex from phase-id.cjs `PHASE_NUMBER_TOKEN_SOURCE` (or phaseMarkdownRegexSource for a\n');
140
+ process.stderr.write('known number), or sanction the site with a dedicated `// phase-id-owner: <reason>`\n');
141
+ process.stderr.write('comment on the line directly above the regex:\n');
142
+ for (const d of violations) {
143
+ process.stderr.write(` ${d.file}:${d.line} ${d.found}\n`);
144
+ }
145
+ process.exitCode = 1;
146
+ }
147
+
148
+ if (require.main === module) main();
149
+
150
+ module.exports = { findPhaseIdRegexDrift, scanRepo, TOKEN_DRIFT_RE };
@@ -50,11 +50,12 @@
50
50
  },
51
51
  "security": {
52
52
  "files": [
53
+ "security-dead-exports.regression.test.cjs",
53
54
  "security-prompt-injection.security.test.cjs",
54
55
  "security-scan.security.test.cjs",
55
56
  "security.test.cjs"
56
57
  ],
57
- "issue": "TBD"
58
+ "issue": "2198"
58
59
  },
59
60
  "state": {
60
61
  "files": [