@hegemonart/get-design-done 1.31.0 → 1.31.5

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 (163) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +44 -0
  4. package/NOTICE +224 -0
  5. package/README.md +1 -1
  6. package/agents/design-authority-watcher.md +1 -1
  7. package/agents/perf-analyzer.md +2 -2
  8. package/bin/gdd-mcp +78 -0
  9. package/bin/gdd-sdk +34 -24
  10. package/bin/gdd-state-mcp +78 -0
  11. package/{README.de.md → docs/i18n/README.de.md} +1 -1
  12. package/{README.fr.md → docs/i18n/README.fr.md} +1 -1
  13. package/{README.it.md → docs/i18n/README.it.md} +1 -1
  14. package/{README.ja.md → docs/i18n/README.ja.md} +1 -1
  15. package/{README.ko.md → docs/i18n/README.ko.md} +1 -1
  16. package/{README.zh-CN.md → docs/i18n/README.zh-CN.md} +1 -1
  17. package/hooks/_hook-emit.js +1 -1
  18. package/hooks/budget-enforcer.ts +5 -5
  19. package/hooks/context-exhaustion.ts +2 -2
  20. package/hooks/gdd-precompact-snapshot.js +3 -3
  21. package/hooks/gdd-read-injection-scanner.ts +2 -2
  22. package/hooks/gdd-sessionstart-recap.js +1 -1
  23. package/hooks/gdd-turn-closeout.js +1 -1
  24. package/package.json +20 -9
  25. package/recipes/.gitkeep +0 -0
  26. package/reference/schemas/recipe.schema.json +33 -0
  27. package/scripts/cli/gdd-events.mjs +5 -5
  28. package/scripts/lib/cache/gdd-cache-manager.cjs +1 -1
  29. package/scripts/lib/cli/index.ts +22 -160
  30. package/scripts/lib/connection-probe/index.cjs +1 -1
  31. package/scripts/lib/discuss-parallel-runner/aggregator.ts +1 -1
  32. package/scripts/lib/discuss-parallel-runner/index.ts +1 -1
  33. package/scripts/lib/error-classifier.cjs +24 -227
  34. package/scripts/lib/event-stream/index.ts +25 -193
  35. package/scripts/lib/gdd-errors/index.ts +24 -213
  36. package/scripts/lib/gdd-state/index.ts +23 -161
  37. package/scripts/lib/iteration-budget.cjs +23 -199
  38. package/scripts/lib/jittered-backoff.cjs +24 -107
  39. package/scripts/lib/lockfile.cjs +23 -195
  40. package/scripts/lib/logger/index.ts +1 -1
  41. package/scripts/lib/parallelism-engine/concurrency-tuner.cjs +1 -1
  42. package/scripts/lib/perf-analyzer/index.cjs +1 -1
  43. package/scripts/lib/pipeline-runner/index.ts +4 -4
  44. package/scripts/lib/pipeline-runner/state-machine.ts +1 -1
  45. package/scripts/lib/prompt-dedup/index.cjs +1 -1
  46. package/scripts/lib/rate-guard.cjs +2 -2
  47. package/scripts/lib/recipe-loader.cjs +142 -0
  48. package/scripts/lib/session-runner/errors.ts +3 -3
  49. package/scripts/lib/session-runner/index.ts +3 -3
  50. package/scripts/lib/session-runner/transcript.ts +1 -1
  51. package/scripts/lib/tool-scoping/index.ts +1 -1
  52. package/scripts/mcp-servers/gdd-mcp/server.ts +29 -311
  53. package/scripts/mcp-servers/gdd-state/server.ts +28 -282
  54. package/sdk/README.md +45 -0
  55. package/{scripts/lib → sdk}/cli/commands/audit.ts +3 -3
  56. package/{scripts/lib → sdk}/cli/commands/init.ts +3 -3
  57. package/{scripts/lib → sdk}/cli/commands/query.ts +4 -4
  58. package/{scripts/lib → sdk}/cli/commands/run.ts +5 -5
  59. package/{scripts/lib → sdk}/cli/commands/stage.ts +5 -5
  60. package/sdk/cli/index.js +8091 -0
  61. package/sdk/cli/index.ts +172 -0
  62. package/{scripts/lib → sdk}/cli/parse-args.ts +2 -2
  63. package/{scripts/lib/gdd-errors → sdk/errors}/classification.ts +1 -1
  64. package/sdk/errors/index.ts +218 -0
  65. package/{scripts/lib → sdk}/event-stream/emitter.ts +1 -1
  66. package/sdk/event-stream/index.ts +197 -0
  67. package/{scripts/lib → sdk}/event-stream/reader.ts +1 -1
  68. package/{scripts/lib → sdk}/event-stream/types.ts +2 -2
  69. package/{scripts/lib → sdk}/event-stream/writer.ts +1 -1
  70. package/sdk/index.ts +19 -0
  71. package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/README.md +3 -3
  72. package/sdk/mcp/gdd-mcp/server.js +1924 -0
  73. package/sdk/mcp/gdd-mcp/server.ts +325 -0
  74. package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/gdd_cycle_recap.ts +3 -3
  75. package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/gdd_decisions_list.ts +2 -2
  76. package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/gdd_events_tail.ts +3 -3
  77. package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/gdd_health.ts +2 -2
  78. package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/gdd_intel_get.ts +2 -2
  79. package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/gdd_learnings_digest.ts +2 -2
  80. package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/gdd_phase_current.ts +2 -2
  81. package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/gdd_phases_list.ts +2 -2
  82. package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/gdd_plans_list.ts +2 -2
  83. package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/gdd_reflections_latest.ts +2 -2
  84. package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/gdd_status.ts +3 -3
  85. package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/gdd_telemetry_query.ts +3 -3
  86. package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/index.ts +2 -2
  87. package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/shared.ts +3 -3
  88. package/sdk/mcp/gdd-state/server.js +2790 -0
  89. package/sdk/mcp/gdd-state/server.ts +294 -0
  90. package/{scripts/mcp-servers → sdk/mcp}/gdd-state/tools/add_blocker.ts +3 -3
  91. package/{scripts/mcp-servers → sdk/mcp}/gdd-state/tools/add_decision.ts +3 -3
  92. package/{scripts/mcp-servers → sdk/mcp}/gdd-state/tools/add_must_have.ts +3 -3
  93. package/{scripts/mcp-servers → sdk/mcp}/gdd-state/tools/checkpoint.ts +2 -2
  94. package/{scripts/mcp-servers → sdk/mcp}/gdd-state/tools/frontmatter_update.ts +2 -2
  95. package/{scripts/mcp-servers → sdk/mcp}/gdd-state/tools/get.ts +3 -3
  96. package/{scripts/mcp-servers → sdk/mcp}/gdd-state/tools/index.ts +1 -1
  97. package/{scripts/mcp-servers → sdk/mcp}/gdd-state/tools/probe_connections.ts +3 -3
  98. package/{scripts/mcp-servers → sdk/mcp}/gdd-state/tools/resolve_blocker.ts +3 -3
  99. package/{scripts/mcp-servers → sdk/mcp}/gdd-state/tools/set_status.ts +2 -2
  100. package/{scripts/mcp-servers → sdk/mcp}/gdd-state/tools/shared.ts +8 -8
  101. package/{scripts/mcp-servers → sdk/mcp}/gdd-state/tools/transition_stage.ts +4 -4
  102. package/{scripts/mcp-servers → sdk/mcp}/gdd-state/tools/update_progress.ts +2 -2
  103. package/sdk/primitives/error-classifier.cjs +232 -0
  104. package/sdk/primitives/iteration-budget.cjs +205 -0
  105. package/sdk/primitives/jittered-backoff.cjs +112 -0
  106. package/sdk/primitives/lockfile.cjs +201 -0
  107. package/{scripts/lib/gdd-state → sdk/state}/gates.ts +1 -1
  108. package/sdk/state/index.ts +167 -0
  109. package/{scripts/lib/gdd-state → sdk/state}/lockfile.ts +1 -1
  110. package/{scripts/lib/gdd-state → sdk/state}/mutator.ts +1 -1
  111. package/{scripts/lib/gdd-state → sdk/state}/parser.ts +1 -1
  112. package/{scripts/lib/gdd-state → sdk/state}/types.ts +4 -4
  113. package/skills/quality-gate/SKILL.md +2 -2
  114. package/scripts/aggregate-agent-metrics.ts +0 -282
  115. package/scripts/bootstrap-manifest.txt +0 -3
  116. package/scripts/bootstrap.sh +0 -80
  117. package/scripts/build-distribution-bundles.cjs +0 -549
  118. package/scripts/build-intel.cjs +0 -486
  119. package/scripts/codegen-schema-types.ts +0 -149
  120. package/scripts/detect-stale-refs.cjs +0 -107
  121. package/scripts/e2e/run-headless.ts +0 -514
  122. package/scripts/extract-changelog-section.cjs +0 -58
  123. package/scripts/gsd-cleanup-incubator.cjs +0 -367
  124. package/scripts/injection-patterns.cjs +0 -58
  125. package/scripts/lint-agentskills-spec.cjs +0 -457
  126. package/scripts/release-smoke-test.cjs +0 -200
  127. package/scripts/rollback-release.sh +0 -42
  128. package/scripts/run-injection-scanner-ci.cjs +0 -83
  129. package/scripts/tests/test-authority-rejected-kinds.sh +0 -58
  130. package/scripts/tests/test-authority-watcher-diff.sh +0 -113
  131. package/scripts/tests/test-motion-provenance.sh +0 -64
  132. package/scripts/validate-frontmatter.ts +0 -409
  133. package/scripts/validate-incubator-scope.cjs +0 -133
  134. package/scripts/validate-schemas.ts +0 -401
  135. package/scripts/validate-skill-length.cjs +0 -283
  136. package/scripts/verify-version-sync.cjs +0 -30
  137. /package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/schemas/gdd_cycle_recap.schema.json +0 -0
  138. /package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/schemas/gdd_decisions_list.schema.json +0 -0
  139. /package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/schemas/gdd_events_tail.schema.json +0 -0
  140. /package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/schemas/gdd_health.schema.json +0 -0
  141. /package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/schemas/gdd_intel_get.schema.json +0 -0
  142. /package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/schemas/gdd_learnings_digest.schema.json +0 -0
  143. /package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/schemas/gdd_phase_current.schema.json +0 -0
  144. /package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/schemas/gdd_phases_list.schema.json +0 -0
  145. /package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/schemas/gdd_plans_list.schema.json +0 -0
  146. /package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/schemas/gdd_reflections_latest.schema.json +0 -0
  147. /package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/schemas/gdd_status.schema.json +0 -0
  148. /package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/schemas/gdd_telemetry_query.schema.json +0 -0
  149. /package/{scripts/mcp-servers → sdk/mcp}/gdd-state/schemas/add_blocker.schema.json +0 -0
  150. /package/{scripts/mcp-servers → sdk/mcp}/gdd-state/schemas/add_decision.schema.json +0 -0
  151. /package/{scripts/mcp-servers → sdk/mcp}/gdd-state/schemas/add_must_have.schema.json +0 -0
  152. /package/{scripts/mcp-servers → sdk/mcp}/gdd-state/schemas/checkpoint.schema.json +0 -0
  153. /package/{scripts/mcp-servers → sdk/mcp}/gdd-state/schemas/frontmatter_update.schema.json +0 -0
  154. /package/{scripts/mcp-servers → sdk/mcp}/gdd-state/schemas/get.schema.json +0 -0
  155. /package/{scripts/mcp-servers → sdk/mcp}/gdd-state/schemas/probe_connections.schema.json +0 -0
  156. /package/{scripts/mcp-servers → sdk/mcp}/gdd-state/schemas/resolve_blocker.schema.json +0 -0
  157. /package/{scripts/mcp-servers → sdk/mcp}/gdd-state/schemas/set_status.schema.json +0 -0
  158. /package/{scripts/mcp-servers → sdk/mcp}/gdd-state/schemas/transition_stage.schema.json +0 -0
  159. /package/{scripts/mcp-servers → sdk/mcp}/gdd-state/schemas/update_progress.schema.json +0 -0
  160. /package/{scripts/lib → sdk/primitives}/error-classifier.d.cts +0 -0
  161. /package/{scripts/lib → sdk/primitives}/iteration-budget.d.cts +0 -0
  162. /package/{scripts/lib → sdk/primitives}/jittered-backoff.d.cts +0 -0
  163. /package/{scripts/lib → sdk/primitives}/lockfile.d.cts +0 -0
@@ -0,0 +1,294 @@
1
+ #!/usr/bin/env -S node --experimental-strip-types
2
+ // sdk/mcp/gdd-state/server.ts
3
+ //
4
+ // MCP server `gdd-state` — exposes the 11 typed STATE.md tools implemented
5
+ // under `./tools/` via stdio transport. Plan 20-05 (SDK-06/07).
6
+ //
7
+ // Lifecycle:
8
+ // 1. Construct a low-level Server (we use the low-level surface so we
9
+ // can speak JSON Schema directly — the high-level McpServer wants
10
+ // Zod shapes, and our per-tool schemas are Draft-07 JSON.)
11
+ // 2. Register `tools/list` — returns all 11 tools with their input
12
+ // JSON Schemas loaded from disk.
13
+ // 3. Register `tools/call` — dispatches by name to the matching
14
+ // handler. Each handler returns a typed ToolResponse; the server
15
+ // wraps it into the MCP CallToolResult shape (one text content
16
+ // item, JSON-stringified response; plus `structuredContent` for
17
+ // richer clients; `isError: true` when `success:false`).
18
+ // 4. Attach StdioServerTransport; await connect.
19
+ // 5. On SIGINT / SIGTERM: close the transport, flush nothing (the
20
+ // event writer uses `appendFileSync` so every write is already
21
+ // durable), and exit 0.
22
+ //
23
+ // Invariant: handler throws are contained. The dispatcher wraps every
24
+ // call in a try/catch that funnels through toToolError() — the MCP
25
+ // harness never sees an uncaught throw from our tools.
26
+
27
+ import { readFileSync, existsSync } from 'node:fs';
28
+ import { dirname, join, resolve } from 'node:path';
29
+
30
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
31
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
32
+ import {
33
+ CallToolRequestSchema,
34
+ ListToolsRequestSchema,
35
+ } from '@modelcontextprotocol/sdk/types.js';
36
+
37
+ import { toToolError } from '../../errors/classification.ts';
38
+ import { TOOL_MODULES, type ToolModule } from './tools/index.ts';
39
+
40
+ /** Server metadata advertised on initialize. */
41
+ const SERVER_NAME = 'gdd-state';
42
+ const SERVER_VERSION = '1.20.0';
43
+
44
+ /**
45
+ * Resolve this module's directory. We deliberately avoid `import.meta.url`
46
+ * (not permitted by our tsconfig's Node16+CommonJS-compatible module
47
+ * resolution) and `__dirname` (not portable under strip-types ESM).
48
+ *
49
+ * Strategy: when this module is invoked as a script, `process.argv[1]`
50
+ * points at this file; resolve its dirname. When it is imported for
51
+ * tests, we fall back to walking from `process.cwd()` — tests run
52
+ * from the repo root by convention, so `sdk/mcp/gdd-state`
53
+ * resolves reliably. Both branches are canonicalized against the
54
+ * on-disk tools directory.
55
+ */
56
+ function here(): string {
57
+ const expectedRel = join('sdk', 'mcp', 'gdd-state');
58
+ // Script invocation: process.argv[1] === .../server.ts (or a shim).
59
+ const entry = process.argv[1];
60
+ if (typeof entry === 'string' && entry.length > 0) {
61
+ const entryDir = dirname(resolve(entry));
62
+ if (existsSync(join(entryDir, 'tools', 'index.ts'))) {
63
+ return entryDir;
64
+ }
65
+ }
66
+ // Library-import path (tests): walk cwd forward.
67
+ const candidate = resolve(process.cwd(), expectedRel);
68
+ if (existsSync(join(candidate, 'tools', 'index.ts'))) {
69
+ return candidate;
70
+ }
71
+ // Last-resort: return the expected path even if it does not exist —
72
+ // the subsequent readFileSync() call will produce a clear error.
73
+ return candidate;
74
+ }
75
+
76
+ /** Eager cache of input schemas keyed by tool name. We load them once at
77
+ * startup so a tool-call handler never hits the filesystem in the hot
78
+ * path; subsequent schema edits (JSON file on disk) require a server
79
+ * restart, which matches every other part of the pipeline. */
80
+ interface LoadedTool extends ToolModule {
81
+ inputSchema: Record<string, unknown>;
82
+ }
83
+
84
+ function loadTools(): LoadedTool[] {
85
+ const baseDir = here();
86
+ return TOOL_MODULES.map((m) => {
87
+ const absPath = join(baseDir, 'tools', m.schemaPath);
88
+ const raw = readFileSync(absPath, 'utf8');
89
+ const parsed = JSON.parse(raw) as {
90
+ properties?: {
91
+ input?: { type?: string; properties?: Record<string, unknown> };
92
+ };
93
+ };
94
+ // The per-tool schema files are Draft-07 wrappers shaped as:
95
+ // { type: "object", properties: { input: {...}, output: {...} } }
96
+ // MCP's tools/list advertises only the INPUT half. We project
97
+ // `properties.input` here; when the schema is malformed we fall
98
+ // back to an open object so the tool is still listable.
99
+ const rawInput = parsed.properties?.input;
100
+ const inputSchema: Record<string, unknown> =
101
+ rawInput !== undefined && typeof rawInput === 'object'
102
+ ? (rawInput as Record<string, unknown>)
103
+ : { type: 'object' };
104
+ // MCP clients require inputSchema.type === "object" for tools/list.
105
+ // Our per-tool inputs are already objects; defensively stamp the
106
+ // field when absent.
107
+ if (!('type' in inputSchema)) inputSchema['type'] = 'object';
108
+ return { ...m, inputSchema };
109
+ });
110
+ }
111
+
112
+ /**
113
+ * Tool descriptions — short, scannable, lifted from the plan. Skill
114
+ * prose uses these verbatim when suggesting a tool to the model.
115
+ */
116
+ const TOOL_DESCRIPTIONS: Record<string, string> = {
117
+ gdd_state__get:
118
+ 'Read current STATE.md (parsed). Read-only; no event emitted. Optionally projects a subset of fields.',
119
+ gdd_state__update_progress:
120
+ 'Update <position>.task_progress and/or status. Emits state.mutation.',
121
+ gdd_state__transition_stage:
122
+ 'Run gate and advance <position>.stage on pass. Gate vetoes return {success:false, error:{context:{blockers:[...]}}}; never crashes the server. Emits state.transition.',
123
+ gdd_state__add_blocker:
124
+ 'Append one entry to <blockers>. Defaults stage to current position.stage and date to today (UTC). Emits state.mutation.',
125
+ gdd_state__resolve_blocker:
126
+ 'Remove one <blockers> entry by 0-based index or exact text match. Returns operation_failed when no row matches. Emits state.mutation on removal.',
127
+ gdd_state__add_decision:
128
+ 'Append one entry to <decisions>. Auto-allocates D-N id when not supplied. Emits state.mutation.',
129
+ gdd_state__add_must_have:
130
+ 'Append one entry to <must_haves>. Auto-allocates M-N id when not supplied. Emits state.mutation.',
131
+ gdd_state__set_status:
132
+ 'Update <position>.status. Emits state.mutation.',
133
+ gdd_state__checkpoint:
134
+ 'Update frontmatter.last_checkpoint and append a <timestamps> entry. Emits state.mutation.',
135
+ gdd_state__probe_connections:
136
+ 'Merge probe results into <connections>. Overwrites keys present in the input; does NOT delete keys not in the input. Emits state.mutation.',
137
+ gdd_state__frontmatter_update:
138
+ 'Patch one or more frontmatter fields. Rejects pipeline_state_version and stage (use transition_stage). Emits state.mutation.',
139
+ };
140
+
141
+ /** Human-readable annotation hints (MCP clients use these to style the
142
+ * tool in UI). `readOnlyHint: true` — tells clients this tool does NOT
143
+ * modify disk; `false` — tells them it does. */
144
+ const TOOL_READONLY: Record<string, boolean> = {
145
+ gdd_state__get: true,
146
+ };
147
+
148
+ /**
149
+ * Build the MCP server. The tools list and call handlers are the only
150
+ * two request handlers we register; everything else (initialize, ping,
151
+ * cancellation, shutdown) is handled internally by the Protocol class.
152
+ */
153
+ export function buildServer(): Server {
154
+ const tools = loadTools();
155
+ const byName: Map<string, LoadedTool> = new Map();
156
+ for (const t of tools) byName.set(t.name, t);
157
+
158
+ const server = new Server(
159
+ { name: SERVER_NAME, version: SERVER_VERSION },
160
+ {
161
+ capabilities: { tools: {} },
162
+ },
163
+ );
164
+
165
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
166
+ return {
167
+ tools: tools.map((t) => {
168
+ const description = TOOL_DESCRIPTIONS[t.name] ?? t.name;
169
+ const readOnly = TOOL_READONLY[t.name] ?? false;
170
+ return {
171
+ name: t.name,
172
+ description,
173
+ inputSchema: t.inputSchema as {
174
+ type: 'object';
175
+ properties?: Record<string, unknown>;
176
+ required?: string[];
177
+ },
178
+ annotations: {
179
+ readOnlyHint: readOnly,
180
+ destructiveHint: !readOnly,
181
+ idempotentHint: false,
182
+ },
183
+ };
184
+ }),
185
+ };
186
+ });
187
+
188
+ server.setRequestHandler(CallToolRequestSchema, async (req) => {
189
+ const { name: toolName, arguments: args } = req.params;
190
+ const tool = byName.get(toolName);
191
+ if (tool === undefined) {
192
+ // Unknown tool — return as CallToolResult isError=true so the
193
+ // client gets a structured error rather than a JSON-RPC error.
194
+ const payload = toToolError(
195
+ new Error(`unknown tool: ${toolName}`),
196
+ );
197
+ return {
198
+ isError: true,
199
+ content: [
200
+ { type: 'text' as const, text: JSON.stringify({ success: false, error: payload.error }) },
201
+ ],
202
+ structuredContent: { success: false, error: payload.error },
203
+ };
204
+ }
205
+
206
+ let response;
207
+ try {
208
+ response = await tool.handle(args ?? {});
209
+ } catch (err) {
210
+ // Defensive catch — handlers shouldn't throw, but if one does
211
+ // we translate rather than crashing the server.
212
+ const payload = toToolError(err);
213
+ response = { success: false as const, error: payload.error };
214
+ }
215
+
216
+ const text = JSON.stringify(response);
217
+ if (response.success === true) {
218
+ return {
219
+ content: [{ type: 'text' as const, text }],
220
+ structuredContent: response as unknown as Record<string, unknown>,
221
+ };
222
+ }
223
+ return {
224
+ isError: true,
225
+ content: [{ type: 'text' as const, text }],
226
+ structuredContent: response as unknown as Record<string, unknown>,
227
+ };
228
+ });
229
+
230
+ return server;
231
+ }
232
+
233
+ /**
234
+ * Run the server against stdio and block until the transport closes.
235
+ * Called from CLI when this file is invoked as a script.
236
+ */
237
+ export async function runStdio(): Promise<void> {
238
+ const server = buildServer();
239
+ const transport = new StdioServerTransport();
240
+
241
+ const shutdown = async (signal: string): Promise<void> => {
242
+ // Re-entrant: signal handlers can fire more than once on flaky
243
+ // shells. Guard with a module-level flag.
244
+ if (SHUTTING_DOWN) return;
245
+ SHUTTING_DOWN = true;
246
+ try {
247
+ await server.close();
248
+ } catch {
249
+ // best-effort; we're exiting anyway.
250
+ }
251
+ // SIGTERM / SIGINT convention: exit(0) — orderly shutdown.
252
+ process.exit(signal === 'SIGTERM' ? 0 : 0);
253
+ };
254
+
255
+ process.on('SIGINT', () => {
256
+ void shutdown('SIGINT');
257
+ });
258
+ process.on('SIGTERM', () => {
259
+ void shutdown('SIGTERM');
260
+ });
261
+
262
+ await server.connect(transport);
263
+ }
264
+
265
+ /** Re-entrancy guard for `shutdown()`. */
266
+ let SHUTTING_DOWN = false;
267
+
268
+ /**
269
+ * Are we being invoked as a script? We compare the argv[1] file path's
270
+ * basename to `server.ts` — test imports never match this because
271
+ * `node --test tests/*.ts` sets argv[1] to the test runner entry, not
272
+ * our file. A direct `node sdk/mcp/gdd-state/server.ts`
273
+ * invocation DOES match.
274
+ */
275
+ // Extension-agnostic (.ts | .js | .cjs | .mjs): the dual-mode bin trampoline
276
+ // (Plan 31-5-9.5, D-16) runs the raw `.ts` in-repo via --experimental-strip-types
277
+ // AND the esbuild-bundled `.js` from a packed/installed tarball. argv[1] ends in
278
+ // `.js` in the compiled path, so a `.ts`-only check would never start the server.
279
+ function isMain(): boolean {
280
+ const entry = process.argv[1];
281
+ if (typeof entry !== 'string' || entry.length === 0) return false;
282
+ return /sdk\/mcp\/gdd-state\/server\.(ts|js|cjs|mjs)$/.test(
283
+ entry.replace(/\\/g, '/'),
284
+ );
285
+ }
286
+
287
+ if (isMain()) {
288
+ runStdio().catch((err) => {
289
+ const msg = err instanceof Error ? err.message : String(err);
290
+ // eslint-disable-next-line no-console
291
+ console.error(`[gdd-state] fatal: ${msg}`);
292
+ process.exit(1);
293
+ });
294
+ }
@@ -1,12 +1,12 @@
1
- // scripts/mcp-servers/gdd-state/tools/add_blocker.ts
1
+ // sdk/mcp/gdd-state/tools/add_blocker.ts
2
2
  //
3
3
  // Tool: gdd_state__add_blocker
4
4
  // Purpose: Append one entry to <blockers>. Defaults the stage to the
5
5
  // current <position>.stage and the date to today (UTC) when omitted.
6
6
  // Emits state.mutation on success.
7
7
 
8
- import { mutate } from '../../../lib/gdd-state/index.ts';
9
- import type { Blocker } from '../../../lib/gdd-state/types.ts';
8
+ import { mutate } from '../../../state/index.ts';
9
+ import type { Blocker } from '../../../state/types.ts';
10
10
  import {
11
11
  emitStateMutation,
12
12
  errorResponse,
@@ -1,12 +1,12 @@
1
- // scripts/mcp-servers/gdd-state/tools/add_decision.ts
1
+ // sdk/mcp/gdd-state/tools/add_decision.ts
2
2
  //
3
3
  // Tool: gdd_state__add_decision
4
4
  // Purpose: Append one entry to <decisions>. Auto-allocates D-N id by
5
5
  // scanning existing decisions when the caller doesn't supply one.
6
6
  // Emits state.mutation on success.
7
7
 
8
- import { mutate } from '../../../lib/gdd-state/index.ts';
9
- import type { Decision } from '../../../lib/gdd-state/types.ts';
8
+ import { mutate } from '../../../state/index.ts';
9
+ import type { Decision } from '../../../state/types.ts';
10
10
  import {
11
11
  emitStateMutation,
12
12
  errorResponse,
@@ -1,4 +1,4 @@
1
- // scripts/mcp-servers/gdd-state/tools/add_must_have.ts
1
+ // sdk/mcp/gdd-state/tools/add_must_have.ts
2
2
  //
3
3
  // Tool: gdd_state__add_must_have
4
4
  // Purpose: Insert-or-update one entry in <must_haves>. Auto-allocates
@@ -9,8 +9,8 @@
9
9
  // skill (see skills/verify/SKILL.md "Flipping a must-have status").
10
10
  // Emits state.mutation on success with `action: "insert" | "update"`.
11
11
 
12
- import { mutate } from '../../../lib/gdd-state/index.ts';
13
- import type { MustHave } from '../../../lib/gdd-state/types.ts';
12
+ import { mutate } from '../../../state/index.ts';
13
+ import type { MustHave } from '../../../state/types.ts';
14
14
  import {
15
15
  emitStateMutation,
16
16
  errorResponse,
@@ -1,4 +1,4 @@
1
- // scripts/mcp-servers/gdd-state/tools/checkpoint.ts
1
+ // sdk/mcp/gdd-state/tools/checkpoint.ts
2
2
  //
3
3
  // Tool: gdd_state__checkpoint
4
4
  // Purpose: Update frontmatter.last_checkpoint and append an entry in
@@ -6,7 +6,7 @@
6
6
  // supplied, otherwise `<stage>_checkpoint_at` falling back to
7
7
  // `checkpoint_at` if the position.stage is empty. Emits state.mutation.
8
8
 
9
- import { mutate } from '../../../lib/gdd-state/index.ts';
9
+ import { mutate } from '../../../state/index.ts';
10
10
  import {
11
11
  emitStateMutation,
12
12
  errorResponse,
@@ -1,4 +1,4 @@
1
- // scripts/mcp-servers/gdd-state/tools/frontmatter_update.ts
1
+ // sdk/mcp/gdd-state/tools/frontmatter_update.ts
2
2
  //
3
3
  // Tool: gdd_state__frontmatter_update
4
4
  // Purpose: Patch one or more frontmatter fields. Rejects two forbidden
@@ -8,7 +8,7 @@
8
8
  // gates and emits state.transition)
9
9
  // Emits state.mutation on success.
10
10
 
11
- import { mutate } from '../../../lib/gdd-state/index.ts';
11
+ import { mutate } from '../../../state/index.ts';
12
12
  import {
13
13
  emitStateMutation,
14
14
  errorResponse,
@@ -1,4 +1,4 @@
1
- // scripts/mcp-servers/gdd-state/tools/get.ts
1
+ // sdk/mcp/gdd-state/tools/get.ts
2
2
  //
3
3
  // Tool: gdd_state__get
4
4
  // Purpose: Read current STATE.md (parsed). Read-only; does NOT emit an
@@ -6,8 +6,8 @@
6
6
  // provided; unknown field names are silently ignored so callers can pass
7
7
  // a broad list without pre-flight knowledge of ParsedState shape.
8
8
 
9
- import { read } from '../../../lib/gdd-state/index.ts';
10
- import type { ParsedState } from '../../../lib/gdd-state/types.ts';
9
+ import { read } from '../../../state/index.ts';
10
+ import type { ParsedState } from '../../../state/types.ts';
11
11
  import {
12
12
  errorResponse,
13
13
  okResponse,
@@ -1,4 +1,4 @@
1
- // scripts/mcp-servers/gdd-state/tools/index.ts
1
+ // sdk/mcp/gdd-state/tools/index.ts
2
2
  //
3
3
  // Aggregates the 11 tool handlers. `server.ts` imports from here so the
4
4
  // registration loop is one call per tool and the tool set is expanded
@@ -1,15 +1,15 @@
1
- // scripts/mcp-servers/gdd-state/tools/probe_connections.ts
1
+ // sdk/mcp/gdd-state/tools/probe_connections.ts
2
2
  //
3
3
  // Tool: gdd_state__probe_connections
4
4
  // Purpose: Merge probe results into <connections>. Overwrites keys
5
5
  // present in the input; DOES NOT delete keys not in the input (plan
6
6
  // contract). Emits state.mutation on success.
7
7
 
8
- import { mutate } from '../../../lib/gdd-state/index.ts';
8
+ import { mutate } from '../../../state/index.ts';
9
9
  import {
10
10
  isConnectionStatus,
11
11
  type ConnectionStatus,
12
- } from '../../../lib/gdd-state/types.ts';
12
+ } from '../../../state/types.ts';
13
13
  import {
14
14
  emitStateMutation,
15
15
  errorResponse,
@@ -1,4 +1,4 @@
1
- // scripts/mcp-servers/gdd-state/tools/resolve_blocker.ts
1
+ // sdk/mcp/gdd-state/tools/resolve_blocker.ts
2
2
  //
3
3
  // Tool: gdd_state__resolve_blocker
4
4
  // Purpose: Remove one entry from <blockers> by 0-based index OR by
@@ -6,8 +6,8 @@
6
6
  // matches — input is well-formed, but the operation cannot complete in
7
7
  // the current state. Emits state.mutation on successful removal.
8
8
 
9
- import { mutate } from '../../../lib/gdd-state/index.ts';
10
- import type { Blocker } from '../../../lib/gdd-state/types.ts';
9
+ import { mutate } from '../../../state/index.ts';
10
+ import type { Blocker } from '../../../state/types.ts';
11
11
  import {
12
12
  emitStateMutation,
13
13
  errorResponse,
@@ -1,11 +1,11 @@
1
- // scripts/mcp-servers/gdd-state/tools/set_status.ts
1
+ // sdk/mcp/gdd-state/tools/set_status.ts
2
2
  //
3
3
  // Tool: gdd_state__set_status
4
4
  // Purpose: Update <position>.status. Thin convenience wrapper over
5
5
  // update_progress — kept separate so skill prose can call the narrowest
6
6
  // tool possible (reduces mis-writes to task_progress). Emits state.mutation.
7
7
 
8
- import { mutate } from '../../../lib/gdd-state/index.ts';
8
+ import { mutate } from '../../../state/index.ts';
9
9
  import {
10
10
  emitStateMutation,
11
11
  errorResponse,
@@ -1,4 +1,4 @@
1
- // scripts/mcp-servers/gdd-state/tools/shared.ts
1
+ // sdk/mcp/gdd-state/tools/shared.ts
2
2
  //
3
3
  // Shared types + helpers for the 11 gdd-state tool handlers (Plan 20-05).
4
4
  // Every handler returns one of:
@@ -7,23 +7,23 @@
7
7
  // { success: false, error: { code, message, kind, context? } }
8
8
  //
9
9
  // Handlers NEVER throw out to the harness — every catch-all funnels
10
- // through `toToolError()` from `scripts/lib/gdd-errors/classification.ts`.
10
+ // through `toToolError()` from `sdk/errors/classification.ts`.
11
11
  // This mirrors the invariant in the plan: "Tool errors are returned as
12
12
  // {success:false, error} — handlers never propagate exceptions."
13
13
 
14
14
  import {
15
15
  ValidationError,
16
16
  OperationFailedError,
17
- } from '../../../lib/gdd-errors/index.ts';
18
- import { toToolError } from '../../../lib/gdd-errors/classification.ts';
19
- import type { ToolErrorPayload } from '../../../lib/gdd-errors/classification.ts';
17
+ } from '../../../errors/index.ts';
18
+ import { toToolError } from '../../../errors/classification.ts';
19
+ import type { ToolErrorPayload } from '../../../errors/classification.ts';
20
20
  import {
21
21
  appendEvent,
22
22
  type BaseEvent,
23
23
  type StateMutationEvent,
24
24
  type StateTransitionEvent,
25
- } from '../../../lib/event-stream/index.ts';
26
- import type { ParsedState, Stage } from '../../../lib/gdd-state/types.ts';
25
+ } from '../../../event-stream/index.ts';
26
+ import type { ParsedState, Stage } from '../../../state/types.ts';
27
27
 
28
28
  /** Public tool-handler response shape (consistent across all 11 tools). */
29
29
  export type ToolResponse =
@@ -84,7 +84,7 @@ export function hasStage(state: ParsedState): state is ParsedState {
84
84
  * the mutating tool's `name` and an opaque `diff` describing what changed.
85
85
  *
86
86
  * The event is persisted to the JSONL stream AND broadcast on the bus —
87
- * see `scripts/lib/event-stream/index.ts`. `appendEvent()` never throws
87
+ * see `sdk/event-stream/index.ts`. `appendEvent()` never throws
88
88
  * on I/O (the writer swallows write errors into `writeErrors`), so this
89
89
  * helper is safe to call inside a `success: true` return path.
90
90
  */
@@ -1,4 +1,4 @@
1
- // scripts/mcp-servers/gdd-state/tools/transition_stage.ts
1
+ // sdk/mcp/gdd-state/tools/transition_stage.ts
2
2
  //
3
3
  // Tool: gdd_state__transition_stage
4
4
  // Purpose: Run gate and advance <position>.stage / frontmatter.stage on
@@ -11,9 +11,9 @@
11
11
  // (pass=false) on gate veto. Plan 20-06's event-stream surface accepts
12
12
  // both forms; Plan 22+ dashboards render both.
13
13
 
14
- import { read, transition } from '../../../lib/gdd-state/index.ts';
15
- import { isStage, type Stage } from '../../../lib/gdd-state/types.ts';
16
- import { TransitionGateFailed } from '../../../lib/gdd-errors/index.ts';
14
+ import { read, transition } from '../../../state/index.ts';
15
+ import { isStage, type Stage } from '../../../state/types.ts';
16
+ import { TransitionGateFailed } from '../../../errors/index.ts';
17
17
  import {
18
18
  emitStateTransition,
19
19
  errorResponse,
@@ -1,10 +1,10 @@
1
- // scripts/mcp-servers/gdd-state/tools/update_progress.ts
1
+ // sdk/mcp/gdd-state/tools/update_progress.ts
2
2
  //
3
3
  // Tool: gdd_state__update_progress
4
4
  // Purpose: Update <position>.task_progress and/or <position>.status.
5
5
  // Emits state.mutation on success.
6
6
 
7
- import { mutate } from '../../../lib/gdd-state/index.ts';
7
+ import { mutate } from '../../../state/index.ts';
8
8
  import {
9
9
  emitStateMutation,
10
10
  errorResponse,