@hegemonart/get-design-done 1.30.6 → 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 (175) hide show
  1. package/.claude-plugin/marketplace.json +6 -3
  2. package/.claude-plugin/plugin.json +5 -2
  3. package/CHANGELOG.md +105 -0
  4. package/NOTICE +224 -0
  5. package/README.md +22 -1
  6. package/SKILL.md +1 -0
  7. package/agents/design-authority-watcher.md +1 -1
  8. package/agents/perf-analyzer.md +2 -2
  9. package/bin/gdd-mcp +78 -0
  10. package/bin/gdd-sdk +34 -24
  11. package/bin/gdd-state-mcp +78 -0
  12. package/{README.de.md → docs/i18n/README.de.md} +1 -1
  13. package/{README.fr.md → docs/i18n/README.fr.md} +1 -1
  14. package/{README.it.md → docs/i18n/README.it.md} +1 -1
  15. package/{README.ja.md → docs/i18n/README.ja.md} +1 -1
  16. package/{README.ko.md → docs/i18n/README.ko.md} +1 -1
  17. package/{README.zh-CN.md → docs/i18n/README.zh-CN.md} +1 -1
  18. package/hooks/_hook-emit.js +1 -1
  19. package/hooks/budget-enforcer.ts +5 -5
  20. package/hooks/context-exhaustion.ts +2 -2
  21. package/hooks/gdd-precompact-snapshot.js +3 -3
  22. package/hooks/gdd-read-injection-scanner.ts +2 -2
  23. package/hooks/gdd-sessionstart-recap.js +1 -1
  24. package/hooks/gdd-turn-closeout.js +1 -1
  25. package/package.json +24 -10
  26. package/recipes/.gitkeep +0 -0
  27. package/reference/schemas/recipe.schema.json +33 -0
  28. package/scripts/cli/gdd-events.mjs +5 -5
  29. package/scripts/lib/cache/gdd-cache-manager.cjs +1 -1
  30. package/scripts/lib/cli/index.ts +22 -160
  31. package/scripts/lib/connection-probe/index.cjs +1 -1
  32. package/scripts/lib/discuss-parallel-runner/aggregator.ts +1 -1
  33. package/scripts/lib/discuss-parallel-runner/index.ts +1 -1
  34. package/scripts/lib/error-classifier.cjs +24 -227
  35. package/scripts/lib/event-stream/index.ts +25 -193
  36. package/scripts/lib/figma-extract/digest.cjs +430 -0
  37. package/scripts/lib/figma-extract/parse-url.cjs +87 -0
  38. package/scripts/lib/figma-extract/payload-schema.json +108 -0
  39. package/scripts/lib/figma-extract/pull.cjs +394 -0
  40. package/scripts/lib/figma-extract/receiver.cjs +273 -0
  41. package/scripts/lib/figma-extract/render-md.cjs +143 -0
  42. package/scripts/lib/figma-extract/styles-resolver.cjs +147 -0
  43. package/scripts/lib/figma-extract/walk.cjs +100 -0
  44. package/scripts/lib/gdd-errors/index.ts +24 -213
  45. package/scripts/lib/gdd-state/index.ts +23 -161
  46. package/scripts/lib/health-mirror/index.cjs +88 -1
  47. package/scripts/lib/iteration-budget.cjs +23 -199
  48. package/scripts/lib/jittered-backoff.cjs +24 -107
  49. package/scripts/lib/lockfile.cjs +23 -195
  50. package/scripts/lib/logger/index.ts +1 -1
  51. package/scripts/lib/parallelism-engine/concurrency-tuner.cjs +1 -1
  52. package/scripts/lib/perf-analyzer/index.cjs +1 -1
  53. package/scripts/lib/pipeline-runner/index.ts +4 -4
  54. package/scripts/lib/pipeline-runner/state-machine.ts +1 -1
  55. package/scripts/lib/prompt-dedup/index.cjs +1 -1
  56. package/scripts/lib/rate-guard.cjs +2 -2
  57. package/scripts/lib/recipe-loader.cjs +142 -0
  58. package/scripts/lib/session-runner/errors.ts +3 -3
  59. package/scripts/lib/session-runner/index.ts +3 -3
  60. package/scripts/lib/session-runner/transcript.ts +1 -1
  61. package/scripts/lib/tool-scoping/index.ts +1 -1
  62. package/scripts/mcp-servers/gdd-mcp/server.ts +29 -311
  63. package/scripts/mcp-servers/gdd-state/server.ts +28 -282
  64. package/sdk/README.md +45 -0
  65. package/{scripts/lib → sdk}/cli/commands/audit.ts +3 -3
  66. package/{scripts/lib → sdk}/cli/commands/init.ts +3 -3
  67. package/{scripts/lib → sdk}/cli/commands/query.ts +4 -4
  68. package/{scripts/lib → sdk}/cli/commands/run.ts +5 -5
  69. package/{scripts/lib → sdk}/cli/commands/stage.ts +5 -5
  70. package/sdk/cli/index.js +8091 -0
  71. package/sdk/cli/index.ts +172 -0
  72. package/{scripts/lib → sdk}/cli/parse-args.ts +2 -2
  73. package/{scripts/lib/gdd-errors → sdk/errors}/classification.ts +1 -1
  74. package/sdk/errors/index.ts +218 -0
  75. package/{scripts/lib → sdk}/event-stream/emitter.ts +1 -1
  76. package/sdk/event-stream/index.ts +197 -0
  77. package/{scripts/lib → sdk}/event-stream/reader.ts +1 -1
  78. package/{scripts/lib → sdk}/event-stream/types.ts +2 -2
  79. package/{scripts/lib → sdk}/event-stream/writer.ts +1 -1
  80. package/sdk/index.ts +19 -0
  81. package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/README.md +3 -3
  82. package/sdk/mcp/gdd-mcp/server.js +1924 -0
  83. package/sdk/mcp/gdd-mcp/server.ts +325 -0
  84. package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/gdd_cycle_recap.ts +3 -3
  85. package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/gdd_decisions_list.ts +2 -2
  86. package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/gdd_events_tail.ts +3 -3
  87. package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/gdd_health.ts +2 -2
  88. package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/gdd_intel_get.ts +2 -2
  89. package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/gdd_learnings_digest.ts +2 -2
  90. package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/gdd_phase_current.ts +2 -2
  91. package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/gdd_phases_list.ts +2 -2
  92. package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/gdd_plans_list.ts +2 -2
  93. package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/gdd_reflections_latest.ts +2 -2
  94. package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/gdd_status.ts +3 -3
  95. package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/gdd_telemetry_query.ts +3 -3
  96. package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/index.ts +2 -2
  97. package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/tools/shared.ts +3 -3
  98. package/sdk/mcp/gdd-state/server.js +2790 -0
  99. package/sdk/mcp/gdd-state/server.ts +294 -0
  100. package/{scripts/mcp-servers → sdk/mcp}/gdd-state/tools/add_blocker.ts +3 -3
  101. package/{scripts/mcp-servers → sdk/mcp}/gdd-state/tools/add_decision.ts +3 -3
  102. package/{scripts/mcp-servers → sdk/mcp}/gdd-state/tools/add_must_have.ts +3 -3
  103. package/{scripts/mcp-servers → sdk/mcp}/gdd-state/tools/checkpoint.ts +2 -2
  104. package/{scripts/mcp-servers → sdk/mcp}/gdd-state/tools/frontmatter_update.ts +2 -2
  105. package/{scripts/mcp-servers → sdk/mcp}/gdd-state/tools/get.ts +3 -3
  106. package/{scripts/mcp-servers → sdk/mcp}/gdd-state/tools/index.ts +1 -1
  107. package/{scripts/mcp-servers → sdk/mcp}/gdd-state/tools/probe_connections.ts +3 -3
  108. package/{scripts/mcp-servers → sdk/mcp}/gdd-state/tools/resolve_blocker.ts +3 -3
  109. package/{scripts/mcp-servers → sdk/mcp}/gdd-state/tools/set_status.ts +2 -2
  110. package/{scripts/mcp-servers → sdk/mcp}/gdd-state/tools/shared.ts +8 -8
  111. package/{scripts/mcp-servers → sdk/mcp}/gdd-state/tools/transition_stage.ts +4 -4
  112. package/{scripts/mcp-servers → sdk/mcp}/gdd-state/tools/update_progress.ts +2 -2
  113. package/sdk/primitives/error-classifier.cjs +232 -0
  114. package/sdk/primitives/iteration-budget.cjs +205 -0
  115. package/sdk/primitives/jittered-backoff.cjs +112 -0
  116. package/sdk/primitives/lockfile.cjs +201 -0
  117. package/{scripts/lib/gdd-state → sdk/state}/gates.ts +1 -1
  118. package/sdk/state/index.ts +167 -0
  119. package/{scripts/lib/gdd-state → sdk/state}/lockfile.ts +1 -1
  120. package/{scripts/lib/gdd-state → sdk/state}/mutator.ts +1 -1
  121. package/{scripts/lib/gdd-state → sdk/state}/parser.ts +1 -1
  122. package/{scripts/lib/gdd-state → sdk/state}/types.ts +4 -4
  123. package/skills/figma-extract/SKILL.md +64 -0
  124. package/skills/health/SKILL.md +10 -0
  125. package/skills/quality-gate/SKILL.md +2 -2
  126. package/scripts/aggregate-agent-metrics.ts +0 -282
  127. package/scripts/bootstrap-manifest.txt +0 -3
  128. package/scripts/bootstrap.sh +0 -80
  129. package/scripts/build-distribution-bundles.cjs +0 -549
  130. package/scripts/build-intel.cjs +0 -486
  131. package/scripts/codegen-schema-types.ts +0 -149
  132. package/scripts/detect-stale-refs.cjs +0 -107
  133. package/scripts/e2e/run-headless.ts +0 -514
  134. package/scripts/extract-changelog-section.cjs +0 -58
  135. package/scripts/gsd-cleanup-incubator.cjs +0 -367
  136. package/scripts/injection-patterns.cjs +0 -58
  137. package/scripts/lint-agentskills-spec.cjs +0 -457
  138. package/scripts/release-smoke-test.cjs +0 -200
  139. package/scripts/rollback-release.sh +0 -42
  140. package/scripts/run-injection-scanner-ci.cjs +0 -83
  141. package/scripts/tests/test-authority-rejected-kinds.sh +0 -58
  142. package/scripts/tests/test-authority-watcher-diff.sh +0 -113
  143. package/scripts/tests/test-motion-provenance.sh +0 -64
  144. package/scripts/validate-frontmatter.ts +0 -409
  145. package/scripts/validate-incubator-scope.cjs +0 -133
  146. package/scripts/validate-schemas.ts +0 -401
  147. package/scripts/validate-skill-length.cjs +0 -283
  148. package/scripts/verify-version-sync.cjs +0 -30
  149. /package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/schemas/gdd_cycle_recap.schema.json +0 -0
  150. /package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/schemas/gdd_decisions_list.schema.json +0 -0
  151. /package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/schemas/gdd_events_tail.schema.json +0 -0
  152. /package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/schemas/gdd_health.schema.json +0 -0
  153. /package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/schemas/gdd_intel_get.schema.json +0 -0
  154. /package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/schemas/gdd_learnings_digest.schema.json +0 -0
  155. /package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/schemas/gdd_phase_current.schema.json +0 -0
  156. /package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/schemas/gdd_phases_list.schema.json +0 -0
  157. /package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/schemas/gdd_plans_list.schema.json +0 -0
  158. /package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/schemas/gdd_reflections_latest.schema.json +0 -0
  159. /package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/schemas/gdd_status.schema.json +0 -0
  160. /package/{scripts/mcp-servers → sdk/mcp}/gdd-mcp/schemas/gdd_telemetry_query.schema.json +0 -0
  161. /package/{scripts/mcp-servers → sdk/mcp}/gdd-state/schemas/add_blocker.schema.json +0 -0
  162. /package/{scripts/mcp-servers → sdk/mcp}/gdd-state/schemas/add_decision.schema.json +0 -0
  163. /package/{scripts/mcp-servers → sdk/mcp}/gdd-state/schemas/add_must_have.schema.json +0 -0
  164. /package/{scripts/mcp-servers → sdk/mcp}/gdd-state/schemas/checkpoint.schema.json +0 -0
  165. /package/{scripts/mcp-servers → sdk/mcp}/gdd-state/schemas/frontmatter_update.schema.json +0 -0
  166. /package/{scripts/mcp-servers → sdk/mcp}/gdd-state/schemas/get.schema.json +0 -0
  167. /package/{scripts/mcp-servers → sdk/mcp}/gdd-state/schemas/probe_connections.schema.json +0 -0
  168. /package/{scripts/mcp-servers → sdk/mcp}/gdd-state/schemas/resolve_blocker.schema.json +0 -0
  169. /package/{scripts/mcp-servers → sdk/mcp}/gdd-state/schemas/set_status.schema.json +0 -0
  170. /package/{scripts/mcp-servers → sdk/mcp}/gdd-state/schemas/transition_stage.schema.json +0 -0
  171. /package/{scripts/mcp-servers → sdk/mcp}/gdd-state/schemas/update_progress.schema.json +0 -0
  172. /package/{scripts/lib → sdk/primitives}/error-classifier.d.cts +0 -0
  173. /package/{scripts/lib → sdk/primitives}/iteration-budget.d.cts +0 -0
  174. /package/{scripts/lib → sdk/primitives}/jittered-backoff.d.cts +0 -0
  175. /package/{scripts/lib → sdk/primitives}/lockfile.d.cts +0 -0
@@ -20,7 +20,7 @@
20
20
  // `resetAt` wins. This matches the D-01 rule in the plan.
21
21
  //
22
22
  // State-file writes are atomic (temp + rename) and coordinated by
23
- // scripts/lib/lockfile.cjs so two child processes hitting `ingestHeaders`
23
+ // sdk/primitives/lockfile.cjs so two child processes hitting `ingestHeaders`
24
24
  // concurrently can never corrupt the file. The lock is scoped to the
25
25
  // state file, not to the provider directory, so different providers can
26
26
  // update in parallel.
@@ -30,7 +30,7 @@
30
30
  const fs = require('node:fs');
31
31
  const path = require('node:path');
32
32
 
33
- const { acquire, renameWithRetry } = require('./lockfile.cjs');
33
+ const { acquire, renameWithRetry } = require('../../sdk/primitives/lockfile.cjs');
34
34
 
35
35
  const STATE_DIR_REL = path.join('.design', 'rate-limits');
36
36
  const LOCK_MAX_WAIT_MS = 3_000;
@@ -0,0 +1,142 @@
1
+ /**
2
+ * recipe-loader.cjs — recipes/ scaffold loader (Plan 31-5-03, RECIPE-01 / SC#14).
3
+ *
4
+ * The top-level recipes/ directory ships EMPTY of recipes; it is populated
5
+ * downstream by Phase 32 (skill-trigger recipes), Phase 33.6 (per-provider),
6
+ * Phase 26 (per-runtime/per-model), and Phase 23.5 (bandit-arm shape). This
7
+ * module fixes the loading contract those phases build against so each does not
8
+ * reinvent it. Modelled on Storybloq's src/autonomous/recipes/ loader.ts.
9
+ *
10
+ * Contract:
11
+ * loadRecipe(name, opts?) -> Recipe
12
+ * name : recipe stem; resolves <repoRoot>/recipes/<name>.json
13
+ * opts : { recipesDir, schemaPath, fs } — all injectable for tests
14
+ * returns : the validated, parsed Recipe object
15
+ * throws : Error('recipe not found: <name>') if the file is absent
16
+ * Error('recipe <name> failed schema validation: …') if invalid
17
+ *
18
+ * Cache (SC#14 "caches by SHA"):
19
+ * Keyed by name + ':' + sha256(fileBytes). Each call reads the file once to
20
+ * compute the content hash; on a HIT (unchanged bytes) it returns the cached
21
+ * object WITHOUT re-parsing / re-validating. On a MISS (changed bytes) it
22
+ * parses + validates + stores. Keying by content SHA — not just name — means
23
+ * an edited recipe is correctly re-validated.
24
+ *
25
+ * Empty-dir safety: this module requires cleanly when recipes/ is empty, and a
26
+ * directory listing of an empty (just-.gitkeep) dir does not throw. loadRecipe
27
+ * of a missing name throws the clear not-found error — distinct from "the empty
28
+ * dir is broken" (it is a valid scaffold state).
29
+ *
30
+ * Uses the repo's existing `ajv` dependency (package.json "ajv": "^8.18.0") —
31
+ * no new dependency. The schema is compiled once per schemaPath (singleton).
32
+ */
33
+
34
+ 'use strict';
35
+
36
+ const fs = require('node:fs');
37
+ const path = require('node:path');
38
+ const crypto = require('node:crypto');
39
+ const Ajv = require('ajv');
40
+
41
+ // recipe-loader.cjs lives in scripts/lib/ → two levels up is the repo root.
42
+ const REPO_ROOT = path.resolve(__dirname, '..', '..');
43
+ const DEFAULT_RECIPES_DIR = path.join(REPO_ROOT, 'recipes');
44
+ const DEFAULT_SCHEMA_PATH = path.join(REPO_ROOT, 'reference', 'schemas', 'recipe.schema.json');
45
+
46
+ // Ajv 8 CJS: require('ajv') is (or wraps) the constructor.
47
+ const AjvCtor = Ajv.default || Ajv;
48
+
49
+ // SHA-keyed cache: `name + ':' + sha256(bytes)` → parsed Recipe object.
50
+ const cache = new Map();
51
+
52
+ // Compiled-validator singletons, keyed by resolved schemaPath. The schema is a
53
+ // small trusted local file, so allErrors:true (full error list → clear message)
54
+ // is safe here — unlike the untrusted-HTTP receiver which fails fast.
55
+ const validators = new Map();
56
+
57
+ // Test-observability counter: increments once per parse+validate (i.e. per
58
+ // cache MISS). A cache HIT does not bump it. Tests assert on the delta to prove
59
+ // SHA-keyed hit/miss semantics without depending on raw read counts (a hit must
60
+ // still read the file once to hash it).
61
+ let _validations = 0;
62
+
63
+ /**
64
+ * Get (compiling once) the validator for a schema path.
65
+ * @param {string} schemaPath
66
+ * @param {typeof fs} fsImpl
67
+ * @returns {import('ajv').ValidateFunction}
68
+ */
69
+ function getValidator(schemaPath, fsImpl) {
70
+ const resolved = path.resolve(schemaPath);
71
+ let validate = validators.get(resolved);
72
+ if (validate) return validate;
73
+
74
+ const schema = JSON.parse(fsImpl.readFileSync(resolved, 'utf8'));
75
+ const ajv = new AjvCtor({ allErrors: true, strict: false });
76
+ validate = ajv.compile(schema);
77
+ validators.set(resolved, validate);
78
+ return validate;
79
+ }
80
+
81
+ /**
82
+ * Load + validate a recipe by name.
83
+ * @param {string} name - recipe stem; resolves <recipesDir>/<name>.json
84
+ * @param {{ recipesDir?: string, schemaPath?: string, fs?: typeof fs }} [opts]
85
+ * @returns {Record<string, unknown>} the validated, parsed Recipe object
86
+ */
87
+ function loadRecipe(name, opts = {}) {
88
+ if (typeof name !== 'string' || name.length === 0) {
89
+ throw new Error('recipe name must be a non-empty string');
90
+ }
91
+
92
+ const fsImpl = opts.fs || fs;
93
+ const dir = opts.recipesDir || DEFAULT_RECIPES_DIR;
94
+ const schemaPath = opts.schemaPath || DEFAULT_SCHEMA_PATH;
95
+ const file = path.join(dir, name + '.json');
96
+
97
+ if (!fsImpl.existsSync(file)) {
98
+ throw new Error('recipe not found: ' + name);
99
+ }
100
+
101
+ // Read once per call to compute the content hash (the cache key).
102
+ const bytes = fsImpl.readFileSync(file);
103
+ const sha = crypto.createHash('sha256').update(bytes).digest('hex');
104
+ const key = name + ':' + sha;
105
+
106
+ // Cache HIT: unchanged bytes → return cached object, skip parse + validate.
107
+ if (cache.has(key)) {
108
+ return cache.get(key);
109
+ }
110
+
111
+ // Cache MISS: parse + validate + store.
112
+ let parsed;
113
+ try {
114
+ parsed = JSON.parse(bytes.toString('utf8'));
115
+ } catch (err) {
116
+ throw new Error('recipe ' + name + ' is not valid JSON: ' + err.message);
117
+ }
118
+
119
+ const validate = getValidator(schemaPath, fsImpl);
120
+ _validations += 1;
121
+ const ok = validate(parsed) === true;
122
+ if (!ok) {
123
+ throw new Error(
124
+ 'recipe ' + name + ' failed schema validation: ' + JSON.stringify(validate.errors || []),
125
+ );
126
+ }
127
+
128
+ cache.set(key, parsed);
129
+ return parsed;
130
+ }
131
+
132
+ /** Test hook: clear the SHA cache (does not drop compiled validators). */
133
+ function _clearCache() {
134
+ cache.clear();
135
+ }
136
+
137
+ /** Test hook: introspect parse+validate counts (one per cache MISS). */
138
+ function _stats() {
139
+ return { validations: _validations, cacheSize: cache.size };
140
+ }
141
+
142
+ module.exports = { loadRecipe, _clearCache, _stats };
@@ -38,7 +38,7 @@ import {
38
38
  OperationFailedError,
39
39
  StateConflictError,
40
40
  type GDDError,
41
- } from '../gdd-errors/index.ts';
41
+ } from '../../../sdk/errors/index.ts';
42
42
 
43
43
  /**
44
44
  * Build an absolute path to a repo-root-relative file. We can't use
@@ -68,7 +68,7 @@ const REPO_ROOT = findRepoRoot();
68
68
  */
69
69
  const nodeRequire = createRequire(join(REPO_ROOT, 'package.json'));
70
70
  const transportClassifier = nodeRequire(
71
- resolve(REPO_ROOT, 'scripts/lib/error-classifier.cjs'),
71
+ resolve(REPO_ROOT, 'sdk/primitives/error-classifier.cjs'),
72
72
  ) as {
73
73
  classify: (err: unknown) => {
74
74
  reason: string;
@@ -375,7 +375,7 @@ export function mapSdkError(err: unknown): MappedSdkError {
375
375
  }
376
376
 
377
377
  // 9. Transport-layer classification (ECONNRESET, ETIMEDOUT, etc.).
378
- // Delegate to scripts/lib/error-classifier.cjs which knows the errno
378
+ // Delegate to sdk/primitives/error-classifier.cjs which knows the errno
379
379
  // vocabulary. Only trust its `retryable` flag for transient network
380
380
  // classes — other classes were already handled above.
381
381
  const classified = transportClassifier.classify(err);
@@ -26,8 +26,8 @@
26
26
  // (always; payload status mirrors SessionResult.status). Optional
27
27
  // `session.budget_exceeded` emitted when the budget trips.
28
28
 
29
- import { appendEvent } from '../event-stream/index.ts';
30
- import type { BaseEvent } from '../event-stream/index.ts';
29
+ import { appendEvent } from '../../../sdk/event-stream/index.ts';
30
+ import type { BaseEvent } from '../../../sdk/event-stream/index.ts';
31
31
  import { sanitize as defaultSanitize } from '../prompt-sanitizer/index.ts';
32
32
 
33
33
  import { mapSdkError } from './errors.ts';
@@ -64,7 +64,7 @@ function _findRepoRoot(): string {
64
64
  const _REPO_ROOT = _findRepoRoot();
65
65
  const _nodeRequire = createRequire(_join(_REPO_ROOT, 'package.json'));
66
66
  const jitteredBackoff = _nodeRequire(
67
- _resolve(_REPO_ROOT, 'scripts/lib/jittered-backoff.cjs'),
67
+ _resolve(_REPO_ROOT, 'sdk/primitives/jittered-backoff.cjs'),
68
68
  ) as {
69
69
  delayMs: (attempt: number, opts?: { baseMs?: number; maxMs?: number; factor?: number; jitter?: number }) => number;
70
70
  };
@@ -2,7 +2,7 @@
2
2
  // transcript writer for Phase 21 headless Agent SDK sessions
3
3
  // (Plan 21-01 Task 4).
4
4
  //
5
- // Design mirrors scripts/lib/event-stream/writer.ts but is scoped to one
5
+ // Design mirrors sdk/event-stream/writer.ts but is scoped to one
6
6
  // session per file rather than the global telemetry stream. Each session
7
7
  // owns a dedicated `.design/sessions/<ISO>-<stage>.jsonl` file; the
8
8
  // filename is stable for the full run and survives retries (retries
@@ -21,7 +21,7 @@
21
21
  // * Plan 21-01 `session-runner` — computes `allowedTools` for each session.
22
22
  // * Plan 21-05 `pipeline-runner` — picks the correct scope per stage.
23
23
 
24
- import { ValidationError } from '../gdd-errors/index.ts';
24
+ import { ValidationError } from '../../../sdk/errors/index.ts';
25
25
  import type { Scope, ScopeInput, ScopeViolation, Stage } from './types.ts';
26
26
  import {
27
27
  NATIVE_TOOLS,
@@ -1,317 +1,35 @@
1
- #!/usr/bin/env -S node --experimental-strip-types
2
- // scripts/mcp-servers/gdd-mcp/server.ts
1
+ // scripts/mcp-servers/gdd-mcp/server.ts — GDD-DEPRECATION-SHIM (Plan 31-5-06, SDK-05, D-02).
3
2
  //
4
- // MCP server `gdd-mcp` read-mostly project-state surface (Phase 27.7).
5
- // Exposes STATE.md sections, phases, decisions, plans, telemetry, intel
6
- // slices, and the latest reflection as typed MCP tools backed by the
7
- // same `scripts/lib/*` modules the CLI uses.
3
+ // Thin deprecation shim. The real MCP `gdd-mcp` server moved to
4
+ // sdk/mcp/gdd-mcp/server.ts in Plan 31-5-05 (SDK consolidation, D-08). This
5
+ // file is re-created at the OLD path so undocumented EXTERNAL importers /
6
+ // invokers (anyone who reached into node_modules/@hegemonart/get-design-done/
7
+ // scripts/mcp-servers/gdd-mcp/server.ts directly) keep working for one minor
8
+ // grace window.
8
9
  //
9
- // Lifecycle (mirrors Phase 20 `gdd-state` server):
10
- // 1. Construct a low-level Server (we use the low-level surface so we
11
- // can speak JSON Schema directly the high-level McpServer wants
12
- // Zod shapes, and our per-tool schemas are Draft-07 JSON.)
13
- // 2. Register `tools/list` returns the registered tools with their
14
- // input JSON Schemas loaded from disk. Scaffold ships with 0
15
- // tools; Plan 27.7-02 populates `TOOL_MODULES` with 12 entries.
16
- // 3. Register `tools/call` — dispatches by name to the matching
17
- // handler. Each handler returns a typed ToolResponse; the server
18
- // wraps it into the MCP CallToolResult shape. Unknown tool names
19
- // return `isError: true` with a structured payload.
20
- // 4. Attach StdioServerTransport; await connect. NO port allocation
21
- // (D-05 stdio-only).
22
- // 5. On SIGINT / SIGTERM: close the transport, exit 0. Re-entrant
23
- // shutdown is guarded with a module-level `SHUTTING_DOWN` flag.
10
+ // REMOVED IN v1.33.0 (D-02). Grace window: 1.31.5 ships with shims →
11
+ // 1.32.0 still has them 1.33.0 removes them. The canonical invocation is
12
+ // now the `bin/gdd-mcp` trampoline (Plan 31-5-05); internal callers already
13
+ // use the sdk/ path. This shim is external-only; 31-5-10's
14
+ // no-stale-internal-refs guard excludes files carrying the
15
+ // GDD-DEPRECATION-SHIM marker above.
24
16
  //
25
- // Project-root discovery (D-05): `resolveProjectRoot()` lives in
26
- // `./tools/shared.ts` and walks up from `process.cwd()` looking for
27
- // `.design/` OR `.planning/` OR `.claude-plugin/plugin.json`. Server
28
- // infrastructure is allowed to import `node:fs`/`node:path` directly;
29
- // only individual TOOL files are bound by the thin-wrapper rule (D-06).
30
- //
31
- // Invariant: handler throws are contained. The dispatcher wraps every
32
- // call in a try/catch that funnels through `toToolError()` — the MCP
33
- // harness never sees an uncaught throw from our tools.
34
-
35
- import { readFileSync, existsSync } from 'node:fs';
36
- import { dirname, join, resolve } from 'node:path';
37
-
38
- import { Server } from '@modelcontextprotocol/sdk/server/index.js';
39
- import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
40
- import {
41
- CallToolRequestSchema,
42
- ListToolsRequestSchema,
43
- } from '@modelcontextprotocol/sdk/types.js';
44
-
45
- import { toToolError } from '../../lib/gdd-errors/classification.ts';
46
- import { TOOL_MODULES, type ToolModule } from './tools/index.ts';
47
-
48
- /** Server metadata advertised on initialize. */
49
- export const SERVER_NAME = 'gdd-mcp';
50
- export const SERVER_VERSION = '1.27.7';
51
-
52
- /**
53
- * Resolve this module's directory. We deliberately avoid `import.meta.url`
54
- * (not permitted by our tsconfig's Node16+CommonJS-compatible module
55
- * resolution) and `__dirname` (not portable under strip-types ESM).
56
- *
57
- * Strategy: when this module is invoked as a script, `process.argv[1]`
58
- * points at this file; resolve its dirname. When it is imported for
59
- * tests, we fall back to walking from `process.cwd()` — tests run
60
- * from the repo root by convention, so `scripts/mcp-servers/gdd-mcp`
61
- * resolves reliably. Both branches are canonicalized against the
62
- * on-disk tools directory.
63
- */
64
- function here(): string {
65
- const expectedRel = join('scripts', 'mcp-servers', 'gdd-mcp');
66
- // Script invocation: process.argv[1] === .../server.ts (or a shim).
67
- const entry = process.argv[1];
68
- if (typeof entry === 'string' && entry.length > 0) {
69
- const entryDir = dirname(resolve(entry));
70
- if (existsSync(join(entryDir, 'tools', 'index.ts'))) {
71
- return entryDir;
72
- }
73
- }
74
- // Library-import path (tests): walk cwd forward.
75
- const candidate = resolve(process.cwd(), expectedRel);
76
- if (existsSync(join(candidate, 'tools', 'index.ts'))) {
77
- return candidate;
78
- }
79
- // Last-resort: return the expected path even if it does not exist —
80
- // the subsequent readFileSync() call will produce a clear error.
81
- return candidate;
82
- }
83
-
84
- /** Eager cache of input schemas keyed by tool name. We load them once at
85
- * startup so a tool-call handler never hits the filesystem in the hot
86
- * path; subsequent schema edits (JSON file on disk) require a server
87
- * restart, which matches every other part of the pipeline.
88
- *
89
- * Scaffold ships with 0 tools — loadTools() returns []. Plan 27.7-02
90
- * adds 12 tool modules, each with its own `schemaPath` pointing into
91
- * `scripts/mcp-servers/gdd-mcp/schemas/`. */
92
- interface LoadedTool extends ToolModule {
93
- inputSchema: Record<string, unknown>;
94
- }
95
-
96
- function loadTools(): LoadedTool[] {
97
- const baseDir = here();
98
- return TOOL_MODULES.map((m) => {
99
- const absPath = join(baseDir, 'tools', m.schemaPath);
100
- const raw = readFileSync(absPath, 'utf8');
101
- const parsed = JSON.parse(raw) as {
102
- properties?: {
103
- input?: { type?: string; properties?: Record<string, unknown> };
104
- };
105
- };
106
- // The per-tool schema files are Draft-07 wrappers shaped as:
107
- // { type: "object", properties: { input: {...}, output: {...} } }
108
- // MCP's tools/list advertises only the INPUT half. We project
109
- // `properties.input` here; when the schema is malformed we fall
110
- // back to an open object so the tool is still listable.
111
- const rawInput = parsed.properties?.input;
112
- const inputSchema: Record<string, unknown> =
113
- rawInput !== undefined && typeof rawInput === 'object'
114
- ? (rawInput as Record<string, unknown>)
115
- : { type: 'object' };
116
- if (!('type' in inputSchema)) inputSchema['type'] = 'object';
117
- return { ...m, inputSchema };
118
- });
119
- }
120
-
121
- /**
122
- * Tool descriptions — short, scannable, lifted from the plan. Skill
123
- * prose uses these verbatim when suggesting a tool to the model.
124
- *
125
- * Plan 27.7-02 populates 12 entries — one per tool name in the canonical
126
- * 12-tool list.
127
- */
128
- export const TOOL_DESCRIPTIONS: Record<string, string> = {
129
- gdd_status:
130
- 'gdd_status: current cycle phase, branch, last-3 decisions, last-3 completed plans, blocker count.',
131
- gdd_phase_current:
132
- 'gdd_phase_current: STATE.md <position> block (phase, stage, task_progress, status).',
133
- gdd_phases_list:
134
- 'gdd_phases_list: parsed ROADMAP.md overview (phase number, name, target version, shipped/planned).',
135
- gdd_plans_list:
136
- 'gdd_plans_list: plans tracked in STATE.md must_haves (optionally filtered by input.phase).',
137
- gdd_decisions_list:
138
- 'gdd_decisions_list: decisions from STATE.md (optionally filtered by input.status).',
139
- gdd_intel_get:
140
- 'gdd_intel_get: read a slice from .design/intel/ (input.slice_id required); directory_not_found if absent.',
141
- gdd_telemetry_query:
142
- 'gdd_telemetry_query: typed reader over .design/telemetry/*.jsonl with type/since/limit filters.',
143
- gdd_cycle_recap:
144
- 'gdd_cycle_recap: diff current STATE against latest .design/snapshots/ snapshot; directory_not_found if absent.',
145
- gdd_reflections_latest:
146
- 'gdd_reflections_latest: newest .design/reflections/ file (excerpt <= 4 KB); directory_not_found if absent.',
147
- gdd_learnings_digest:
148
- 'gdd_learnings_digest: aggregate last N reflections into a compact digest (<= 5 KB).',
149
- gdd_events_tail:
150
- 'gdd_events_tail: last-N events from .design/telemetry/events.jsonl with optional type filter.',
151
- gdd_health:
152
- 'gdd_health: read-only mirror of gdd-health SKILL — 4 checks (CLAUDE.md, .planning/, .design/, package.json).',
153
- };
154
-
155
- /** Human-readable annotation hints (MCP clients use these to style the
156
- * tool in UI). `readOnlyHint: true` — tells clients this tool does NOT
157
- * modify disk. v1 is read-only (D-04) so every entry is `true`. */
158
- export const TOOL_READONLY: Record<string, boolean> = {
159
- gdd_status: true,
160
- gdd_phase_current: true,
161
- gdd_phases_list: true,
162
- gdd_plans_list: true,
163
- gdd_decisions_list: true,
164
- gdd_intel_get: true,
165
- gdd_telemetry_query: true,
166
- gdd_cycle_recap: true,
167
- gdd_reflections_latest: true,
168
- gdd_learnings_digest: true,
169
- gdd_events_tail: true,
170
- gdd_health: true,
171
- };
172
-
173
- /**
174
- * Build the MCP server. The tools list and call handlers are the only
175
- * two request handlers we register; everything else (initialize, ping,
176
- * cancellation, shutdown) is handled internally by the Protocol class.
177
- */
178
- export function buildServer(): Server {
179
- const tools = loadTools();
180
- const byName: Map<string, LoadedTool> = new Map();
181
- for (const t of tools) byName.set(t.name, t);
182
-
183
- const server = new Server(
184
- { name: SERVER_NAME, version: SERVER_VERSION },
185
- {
186
- capabilities: { tools: {} },
187
- },
17
+ // Re-exporting the sdk/ server keeps the library surface (buildServer,
18
+ // runStdio, SERVER_NAME, SERVER_VERSION, TOOL_DESCRIPTIONS, TOOL_READONLY)
19
+ // reachable via the old path. The sdk/ server's own isMain() entry guard
20
+ // keys off process.argv[1] ending with its own sdk/ path, so a re-export
21
+ // does NOT auto-start the server direct execution should go through the
22
+ // bin trampoline. Runs under --experimental-strip-types.
23
+
24
+ import { emitWarning } from 'node:process';
25
+
26
+ let warned = false;
27
+ if (!warned) {
28
+ warned = true;
29
+ emitWarning(
30
+ 'scripts/mcp-servers/gdd-mcp/server.ts is deprecated; use the bin/gdd-mcp trampoline or import sdk/mcp/gdd-mcp instead. Removed in v1.33.0.',
31
+ 'DeprecationWarning',
188
32
  );
189
-
190
- server.setRequestHandler(ListToolsRequestSchema, async () => {
191
- return {
192
- tools: tools.map((t) => {
193
- const description = TOOL_DESCRIPTIONS[t.name] ?? t.name;
194
- const readOnly = TOOL_READONLY[t.name] ?? true;
195
- return {
196
- name: t.name,
197
- description,
198
- inputSchema: t.inputSchema as {
199
- type: 'object';
200
- properties?: Record<string, unknown>;
201
- required?: string[];
202
- },
203
- annotations: {
204
- readOnlyHint: readOnly,
205
- destructiveHint: !readOnly,
206
- idempotentHint: false,
207
- },
208
- };
209
- }),
210
- };
211
- });
212
-
213
- server.setRequestHandler(CallToolRequestSchema, async (req) => {
214
- const { name: toolName, arguments: args } = req.params;
215
- const tool = byName.get(toolName);
216
- if (tool === undefined) {
217
- // Unknown tool — return as CallToolResult isError=true so the
218
- // client gets a structured error rather than a JSON-RPC error.
219
- const payload = toToolError(
220
- new Error(`unknown tool: ${toolName}`),
221
- );
222
- return {
223
- isError: true,
224
- content: [
225
- {
226
- type: 'text' as const,
227
- text: JSON.stringify({ success: false, error: payload.error }),
228
- },
229
- ],
230
- structuredContent: { success: false, error: payload.error },
231
- };
232
- }
233
-
234
- let response;
235
- try {
236
- response = await tool.handle(args ?? {});
237
- } catch (err) {
238
- // Defensive catch — handlers shouldn't throw, but if one does
239
- // we translate rather than crashing the server.
240
- const payload = toToolError(err);
241
- response = { success: false as const, error: payload.error };
242
- }
243
-
244
- const text = JSON.stringify(response);
245
- if (response.success === true) {
246
- return {
247
- content: [{ type: 'text' as const, text }],
248
- structuredContent: response as unknown as Record<string, unknown>,
249
- };
250
- }
251
- return {
252
- isError: true,
253
- content: [{ type: 'text' as const, text }],
254
- structuredContent: response as unknown as Record<string, unknown>,
255
- };
256
- });
257
-
258
- return server;
259
- }
260
-
261
- /**
262
- * Run the server against stdio and block until the transport closes.
263
- * Called from CLI when this file is invoked as a script.
264
- */
265
- export async function runStdio(): Promise<void> {
266
- const server = buildServer();
267
- const transport = new StdioServerTransport();
268
-
269
- const shutdown = async (signal: string): Promise<void> => {
270
- // Re-entrant: signal handlers can fire more than once on flaky
271
- // shells. Guard with a module-level flag.
272
- if (SHUTTING_DOWN) return;
273
- SHUTTING_DOWN = true;
274
- try {
275
- await server.close();
276
- } catch {
277
- // best-effort; we're exiting anyway.
278
- }
279
- // SIGTERM / SIGINT convention: exit(0) — orderly shutdown.
280
- process.exit(signal === 'SIGTERM' ? 0 : 0);
281
- };
282
-
283
- process.on('SIGINT', () => {
284
- void shutdown('SIGINT');
285
- });
286
- process.on('SIGTERM', () => {
287
- void shutdown('SIGTERM');
288
- });
289
-
290
- await server.connect(transport);
291
33
  }
292
34
 
293
- /** Re-entrancy guard for `shutdown()`. */
294
- let SHUTTING_DOWN = false;
295
-
296
- /**
297
- * Are we being invoked as a script? We compare the argv[1] file path's
298
- * basename to `server.ts` — test imports never match this because
299
- * `node --test tests/*.ts` sets argv[1] to the test runner entry, not
300
- * our file. A direct `node scripts/mcp-servers/gdd-mcp/server.ts`
301
- * invocation DOES match. The Windows-safe path normalization uses
302
- * `.replace(/\\/g, '/')` before the endsWith check.
303
- */
304
- function isMain(): boolean {
305
- const entry = process.argv[1];
306
- if (typeof entry !== 'string' || entry.length === 0) return false;
307
- return entry.replace(/\\/g, '/').endsWith('scripts/mcp-servers/gdd-mcp/server.ts');
308
- }
309
-
310
- if (isMain()) {
311
- runStdio().catch((err) => {
312
- const msg = err instanceof Error ? err.message : String(err);
313
- // eslint-disable-next-line no-console
314
- console.error(`[gdd-mcp] fatal: ${msg}`);
315
- process.exit(1);
316
- });
317
- }
35
+ export * from '../../../sdk/mcp/gdd-mcp/server.ts';