@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,117 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /**
5
+ * scripts/validate-registry.cjs — CLI validator for the third-party
6
+ * discoverability catalogs (issue #2182):
7
+ *
8
+ * - docs/registries/capabilities.json ("GSD Community Capability Registry")
9
+ * - docs/registries/eos.json ("GSD EoS Registry", PR2 — optional
10
+ * until that JSON file ships)
11
+ *
12
+ * Validates each source's JSON array against the closed schema in
13
+ * scripts/registry-schema.cjs (validateEntries). Human-readable errors go to
14
+ * stderr; `--json` additionally prints a structured verdict to stdout.
15
+ *
16
+ * Usage:
17
+ * node scripts/validate-registry.cjs # human-readable report
18
+ * node scripts/validate-registry.cjs --json # structured JSON verdict
19
+ *
20
+ * Exit codes:
21
+ * 0 every present source's entries are all valid
22
+ * 1 one or more entries failed validation (or a source's JSON is malformed)
23
+ */
24
+
25
+ const fs = require('node:fs');
26
+ const path = require('node:path');
27
+
28
+ const { ExitError, runMain } = require('./lib/cli-exit.cjs');
29
+ const { validateEntries } = require('./registry-schema.cjs');
30
+
31
+ // Resolved relative to process.cwd() (not __dirname) so the CLI validates
32
+ // whichever project it is invoked from — this is what lets tests drive it as
33
+ // a subprocess against isolated temp-fixture directories via `cwd`.
34
+ const SOURCES = [
35
+ { file: 'capabilities.json', type: 'capability' },
36
+ { file: 'eos.json', type: 'eos' },
37
+ ];
38
+
39
+ /**
40
+ * Load + validate a single registry JSON file.
41
+ *
42
+ * @param {string} jsonPath absolute path to the registry JSON file
43
+ * @param {'capability'|'eos'} type
44
+ * @returns {{ok: boolean, errors: Array<{index: number, id?: string, field: string, reason: string}>}}
45
+ */
46
+ function validateFile(jsonPath, type) {
47
+ let raw;
48
+ try {
49
+ raw = fs.readFileSync(jsonPath, 'utf8');
50
+ } catch (err) {
51
+ return {
52
+ ok: false,
53
+ errors: [{ index: -1, field: '<file>', reason: `could not read ${jsonPath}: ${err.message}` }],
54
+ };
55
+ }
56
+
57
+ let entries;
58
+ try {
59
+ entries = JSON.parse(raw);
60
+ } catch (err) {
61
+ return {
62
+ ok: false,
63
+ errors: [{ index: -1, field: '<file>', reason: `JSON parse error in ${jsonPath}: ${err.message}` }],
64
+ };
65
+ }
66
+
67
+ if (!Array.isArray(entries)) {
68
+ return {
69
+ ok: false,
70
+ errors: [{ index: -1, field: '<file>', reason: `${jsonPath} must be a JSON array of entries` }],
71
+ };
72
+ }
73
+
74
+ return validateEntries(entries, { type });
75
+ }
76
+
77
+ function main() {
78
+ const jsonMode = process.argv.includes('--json');
79
+ const registriesDir = path.join(process.cwd(), 'docs', 'registries');
80
+
81
+ const results = [];
82
+ let anyFailed = false;
83
+
84
+ for (const { file, type } of SOURCES) {
85
+ const jsonPath = path.join(registriesDir, file);
86
+ // eos.json is optional until PR2 ships it — skip silently when absent.
87
+ if (type === 'eos' && !fs.existsSync(jsonPath)) continue;
88
+
89
+ const verdict = validateFile(jsonPath, type);
90
+ results.push({ file, type, ok: verdict.ok, errors: verdict.errors });
91
+ if (!verdict.ok) anyFailed = true;
92
+ }
93
+
94
+ if (jsonMode) {
95
+ process.stdout.write(JSON.stringify({ ok: !anyFailed, results }, null, 2) + '\n');
96
+ } else if (anyFailed) {
97
+ process.stderr.write('\nERROR validate-registry: one or more entries failed validation\n');
98
+ for (const result of results) {
99
+ if (result.ok) continue;
100
+ process.stderr.write(`\n${result.file}:\n`);
101
+ for (const e of result.errors) {
102
+ const idPart = e.id ? ` (id: ${e.id})` : '';
103
+ process.stderr.write(` entry[${e.index}]${idPart} field "${e.field}": ${e.reason}\n`);
104
+ }
105
+ }
106
+ process.stderr.write('\n');
107
+ } else {
108
+ process.stdout.write('ok validate-registry: all entries valid\n');
109
+ }
110
+
111
+ if (anyFailed) throw new ExitError(1, 'registry validation failed');
112
+ return 0;
113
+ }
114
+
115
+ if (require.main === module) runMain(main);
116
+
117
+ module.exports = { main, validateFile, SOURCES };
@@ -0,0 +1,197 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * GSD extension for VS Code — WEB (browser) entry, #2103.
5
+ *
6
+ * This is the `browser` entry point (vscode/package.json `"browser": "./browser.js"`),
7
+ * loaded by VS Code Web / vscode.dev in a webworker context. It has ZERO Node
8
+ * APIs: no `require('fs')`, `require('path')`, or `require('child_process')`,
9
+ * and no Node globals (`process`, `Buffer`, `__dirname`, `__filename`). This is
10
+ * a HARD constraint, not a style preference — a Web Extension host does not
11
+ * have Node's core modules available at all; requiring one throws immediately
12
+ * at load time and breaks activation.
13
+ *
14
+ * WHY THIS FILE DOES NOT REQUIRE `./host-binding.js` OR `./extension.js`
15
+ * (a deliberate deviation from "compose the seams via bindGsdToVscode" — see
16
+ * the #2103 dispatch-crux note below):
17
+ *
18
+ * `host-binding.js`'s `bindGsdToVscode` is NOT actually web-safe once its
19
+ * transitive dependencies are checked — three of its four required engine-lib
20
+ * modules pull in Node's `fs`/`os`/`path` at module-load time (eagerly, on
21
+ * every `require()`, regardless of which code path runs):
22
+ * - gsd-core/bin/lib/state-io.cjs → requires 'node:fs' directly.
23
+ * - gsd-core/bin/lib/adapter-imperative.cjs → requires install-engine.cjs +
24
+ * capability-loader.cjs (fs/os/path).
25
+ * - gsd-core/bin/lib/model-adapter.cjs → requires model-resolver.cjs →
26
+ * config-loader.cjs (fs/os/path) +
27
+ * configuration.cjs (fs/path).
28
+ * (gsd-core/bin/lib/hook-bus.cjs alone has no requires and is genuinely
29
+ * web-safe.) Requiring `host-binding.js` here would transitively pull in
30
+ * `node:fs` and throw at web-worker load time — the opposite of "zero Node
31
+ * APIs". See host-binding.js's own header comment for the full chain. Fixing
32
+ * those engine-lib modules to be fs-free is a separate, much larger
33
+ * engine-wide refactor (config/capability loading genuinely reads files from
34
+ * disk for every OTHER host) — out of scope here; flagged rather than routed
35
+ * around silently.
36
+ *
37
+ * So this file implements its OWN minimal, independently-verified-zero-Node-API
38
+ * composition directly against `vscode.lm` — no engine-lib requires at all.
39
+ *
40
+ * DISPATCH STORY ON WEB (per the #2103 dispatch-crux design): full GSD engine
41
+ * dispatch (the gsd-tools.cjs subprocess-shim `dispatchGsdCommand` used by the
42
+ * desktop `extension.js`) is fundamentally a Node `child_process.spawnSync`
43
+ * call — there is no web-worker equivalent. On web, GSD command dispatch is
44
+ * available through VS Code's NATIVE MCP client connecting to the GSD
45
+ * companion MCP server (gsd-core/bin/lib/mcp-server.cjs, `gsd-mcp-server`
46
+ * bin entry — a separate, already-existing surface; this file does NOT
47
+ * implement an MCP client itself, it only points the user at that story).
48
+ * The chat participant and Language Model Tools registered below are
49
+ * therefore intentionally limited on web: they register (so the surface is
50
+ * discoverable and `#runSubagent`-eligible per VS Code's chat engine) but
51
+ * their handlers return an honest "configure the GSD MCP server for full
52
+ * dispatch on web" message rather than silently failing or faking success.
53
+ */
54
+
55
+ /**
56
+ * Tokenizes a raw chat/free-form prompt string. Kept local (not shared with
57
+ * extension.js) so this file has zero requires of any kind beyond `vscode`.
58
+ * @param {string} rawPrompt
59
+ * @returns {{family: string, subcommand: string|undefined, args: string[]}}
60
+ */
61
+ function parseChatPrompt(rawPrompt) {
62
+ const tokens = String(rawPrompt || '').trim().split(/\s+/).filter(Boolean);
63
+ return {
64
+ family: tokens[0] || '--help',
65
+ subcommand: tokens[1],
66
+ args: tokens.slice(2),
67
+ };
68
+ }
69
+
70
+ /**
71
+ * The honest "web mode" message every web-surface handler returns instead of
72
+ * attempting Node-only engine dispatch.
73
+ * @param {string} family
74
+ */
75
+ function webDispatchUnavailableMessage(family) {
76
+ return (
77
+ `GSD web mode: full engine dispatch for "${family}" is not available in the browser ` +
78
+ '(the VS Code Web/webworker host has no Node runtime, so the gsd-tools.cjs ' +
79
+ 'subprocess dispatch used on desktop cannot run here). Configure the GSD MCP ' +
80
+ 'server (gsd-mcp-server) as a VS Code MCP server for full command dispatch on web, ' +
81
+ 'or use the desktop GSD Core extension.'
82
+ );
83
+ }
84
+
85
+ /**
86
+ * Registers the `@gsd` chat participant in web mode (#2103). Its handler is
87
+ * honest about the web dispatch limitation — see webDispatchUnavailableMessage.
88
+ * Exported separately so it is testable with a mock `vscode.chat`.
89
+ * @param {object} vscode
90
+ * @param {import('vscode').ExtensionContext} context
91
+ * @returns {object|null} the created participant, or null if vscode.chat is absent.
92
+ */
93
+ function registerChatParticipant(vscode, context) {
94
+ if (!vscode || !vscode.chat || typeof vscode.chat.createChatParticipant !== 'function') {
95
+ return null;
96
+ }
97
+ const participant = vscode.chat.createChatParticipant('gsd', async (request, _chatContext, stream, _token) => {
98
+ const { family } = parseChatPrompt(request && request.prompt);
99
+ if (stream && typeof stream.markdown === 'function') {
100
+ stream.markdown(webDispatchUnavailableMessage(family));
101
+ }
102
+ return { metadata: { command: family, mode: 'web' } };
103
+ });
104
+ if (context && Array.isArray(context.subscriptions)) context.subscriptions.push(participant);
105
+ return participant;
106
+ }
107
+
108
+ /**
109
+ * The same representative LM tool NAMES as the desktop extension (must match
110
+ * package.json's contributes.languageModelTools[].name — underscored, the
111
+ * vscode.lm.registerTool registration name — so the manifest is identical
112
+ * across both entry points), but with web-mode invoke() handlers.
113
+ */
114
+ const LM_TOOL_NAMES = ['gsd_progress', 'gsd_workstreams', 'gsd_plan_phase'];
115
+
116
+ /**
117
+ * Registers web-mode LM tools via vscode.lm.registerTool (#2103). Each
118
+ * invoke() returns the honest web-dispatch-unavailable message — no engine-lib
119
+ * requires, no Node APIs.
120
+ * @param {object} vscode
121
+ * @param {import('vscode').ExtensionContext} context
122
+ * @returns {number} count of tools registered (0 if vscode.lm is absent — fail-soft).
123
+ */
124
+ function registerLanguageModelTools(vscode, context) {
125
+ if (!vscode || !vscode.lm || typeof vscode.lm.registerTool !== 'function') return 0;
126
+ let count = 0;
127
+ for (const name of LM_TOOL_NAMES) {
128
+ const impl = {
129
+ async invoke(_options, _token) {
130
+ return new vscode.LanguageModelToolResult([
131
+ new vscode.LanguageModelTextPart(webDispatchUnavailableMessage(name)),
132
+ ]);
133
+ },
134
+ };
135
+ const disposable = vscode.lm.registerTool(name, impl);
136
+ if (context && Array.isArray(context.subscriptions)) context.subscriptions.push(disposable);
137
+ count++;
138
+ }
139
+ return count;
140
+ }
141
+
142
+ /**
143
+ * Detects `#runSubagent` feature availability (`chat.subagents.allowInvocationsFromSubagents`,
144
+ * VS Code 1.105+). Fail-soft: never throws. Identical detection logic to the
145
+ * desktop extension.js (duplicated, not shared, to keep this file at zero
146
+ * requires) — see extension.js's registerSubagentDispatch for the rationale
147
+ * that VS Code's chat engine itself surfaces registered participants/tools to
148
+ * `#runSubagent`, with no separate registration API.
149
+ * @param {object} vscode
150
+ * @returns {{available: boolean}}
151
+ */
152
+ function detectSubagentSupport(vscode) {
153
+ let available = false;
154
+ try {
155
+ const cfg = vscode && vscode.workspace && typeof vscode.workspace.getConfiguration === 'function'
156
+ ? vscode.workspace.getConfiguration('chat.subagents')
157
+ : null;
158
+ available = !!(cfg && typeof cfg.get === 'function' && cfg.get('allowInvocationsFromSubagents') !== undefined);
159
+ } catch {
160
+ available = false;
161
+ }
162
+ return { available };
163
+ }
164
+
165
+ /**
166
+ * VS Code Web extension activation. Registers the chat participant + Language
167
+ * Model Tools in web mode. Does NOT register the `gsd.invoke` command with a
168
+ * real-dispatch handler (there is no Node dispatch on web) — the command is
169
+ * still contributed (contributes.commands in package.json is shared across
170
+ * desktop/web), so it is registered here too, but its handler returns the
171
+ * same honest web-mode message.
172
+ * @param {import('vscode').ExtensionContext} context
173
+ */
174
+ function activate(context) {
175
+ const vscode = require('vscode');
176
+
177
+ const gsdCommand = vscode.commands.registerCommand('gsd.invoke', async (args) => {
178
+ const a = (args && typeof args === 'object') ? args : {};
179
+ const family = (typeof a.family === 'string' && a.family) ? a.family : '--help';
180
+ return JSON.stringify({ ok: false, stdout: '', stderr: webDispatchUnavailableMessage(family), code: null, timedOut: false });
181
+ });
182
+ context.subscriptions.push(gsdCommand);
183
+
184
+ registerChatParticipant(vscode, context);
185
+ registerLanguageModelTools(vscode, context);
186
+ detectSubagentSupport(vscode);
187
+ }
188
+
189
+ module.exports = {
190
+ activate,
191
+ parseChatPrompt,
192
+ webDispatchUnavailableMessage,
193
+ registerChatParticipant,
194
+ registerLanguageModelTools,
195
+ detectSubagentSupport,
196
+ LM_TOOL_NAMES,
197
+ };