@hegemonart/get-design-done 1.56.0 → 1.57.1

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.
@@ -6,8 +6,8 @@
6
6
  * resolve a cross-tree sibling via a fixed `__dirname`-relative `../../..`
7
7
  * jump, because that breaks the moment a file is copied/moved or the layout
8
8
  * shifts. Instead, walk UP from this file's directory until we find the GDD
9
- * package.json (identified by `name === 'get-design-done'`), and resolve all
10
- * in-repo siblings relative to that root.
9
+ * package.json (identified by `name === '@hegemonart/get-design-done'`), and
10
+ * resolve all in-repo siblings relative to that root.
11
11
  *
12
12
  * Even though these dashboard `.cjs` files are NOT esbuild-bundled (R8 — the
13
13
  * bin trampoline runs them directly so the Phase 53 __dirname-rewrite trap
@@ -23,7 +23,7 @@ let _cachedRoot = null;
23
23
 
24
24
  /**
25
25
  * Walk up from `startDir` looking for the GDD package root. The GDD root is
26
- * the first ancestor whose package.json declares `name: "get-design-done"`;
26
+ * the first ancestor whose package.json declares `name: "@hegemonart/get-design-done"`;
27
27
  * if no such marker is found (e.g. running from an unusual layout), fall back
28
28
  * to the FIRST ancestor that has any package.json, then to `startDir`.
29
29
  *
@@ -44,7 +44,7 @@ function findPackageRoot(startDir) {
44
44
  }
45
45
  if (pkg) {
46
46
  if (firstWithPkg === null) firstWithPkg = dir;
47
- if (pkg.name === 'get-design-done') return dir;
47
+ if (pkg.name === '@hegemonart/get-design-done') return dir;
48
48
  }
49
49
  const parent = path.dirname(dir);
50
50
  if (parent === dir) break;
@@ -33,15 +33,20 @@
33
33
  * (absent / unknown) -> default
34
34
  */
35
35
 
36
- /** Canonical action → color map (the only colors the Findings pane uses). */
36
+ /**
37
+ * Canonical action -> color map. Keys are lowercase (matching the emitter's
38
+ * suggested_action values from events.schema.json: allow/review/
39
+ * require_confirmation/block). The display label is separate from the key so
40
+ * the map can do a single case-insensitive lookup.
41
+ */
37
42
  const ACTION_COLOR = Object.freeze({
38
- Allow: 'green',
39
- Review: 'yellow',
40
- RequireConfirmation: 'orange',
41
- Block: 'red',
43
+ allow: 'green',
44
+ review: 'yellow',
45
+ require_confirmation: 'orange',
46
+ block: 'red',
42
47
  });
43
48
 
44
- /** The set of recognized suggested-action values (Phase 56 vocabulary). */
49
+ /** The set of recognized suggested-action values (Phase 56 vocabulary, lowercase). */
45
50
  const VALID_ACTIONS = Object.freeze(Object.keys(ACTION_COLOR));
46
51
 
47
52
  /** The blank placeholder row emitted pre-56 (or for malformed/absent input). */
@@ -66,34 +71,64 @@ function finiteOrNull(v) {
66
71
  }
67
72
 
68
73
  /**
69
- * Map a suggested_action to its display color. Unknown / absent -> 'default'.
74
+ * Canonicalize a suggested_action to its lowercase snake_case key for lookup.
75
+ * Handles the emitter's lowercase values (allow/review/require_confirmation/block)
76
+ * AND legacy CamelCase (Allow/Review/RequireConfirmation/Block) - the CamelCase
77
+ * 'RequireConfirmation' lowercases to 'requireconfirmation' with no separator, so
78
+ * map that explicitly to 'require_confirmation'. Returns null for non-strings.
79
+ * @param {*} action
80
+ * @returns {string|null}
81
+ */
82
+ function canonAction(action) {
83
+ if (typeof action !== 'string') return null;
84
+ let s = action.trim().toLowerCase().replace(/[\s-]+/g, '_');
85
+ if (s === 'requireconfirmation') s = 'require_confirmation';
86
+ return s;
87
+ }
88
+
89
+ /**
90
+ * Map a suggested_action to its display color. Case-insensitive so both
91
+ * 'allow' (emitter) and 'Allow' (legacy) resolve correctly. Unknown / absent -> 'default'.
70
92
  * @param {*} action
71
93
  * @returns {'green'|'yellow'|'orange'|'red'|'default'}
72
94
  */
73
95
  function colorForAction(action) {
74
- if (typeof action === 'string' && Object.prototype.hasOwnProperty.call(ACTION_COLOR, action)) {
75
- return ACTION_COLOR[action];
96
+ const canon = canonAction(action);
97
+ if (canon !== null && Object.prototype.hasOwnProperty.call(ACTION_COLOR, canon)) {
98
+ return ACTION_COLOR[canon];
76
99
  }
77
100
  return 'default';
78
101
  }
79
102
 
80
103
  /**
81
- * Surface the risk fields on ONE event/finding. Reads `risk_score`,
82
- * `confidence`, `suggested_action` WHEN PRESENT; otherwise returns the blank
83
- * placeholder. PURE; NEVER throws.
104
+ * Surface the risk fields on ONE event/finding. Accepts either a raw
105
+ * risk_assessment envelope (with a `.payload` sub-object) OR a bare payload
106
+ * object. When `.payload` is present it is used as the source of risk fields;
107
+ * otherwise `item` itself is inspected (bare-payload / legacy path).
84
108
  *
85
- * @param {*} item an event or finding (may be missing the risk fields pre-56)
109
+ * Reads `risk_score`, `confidence`, `suggested_action` WHEN PRESENT; otherwise
110
+ * returns the blank placeholder. PURE; NEVER throws.
111
+ *
112
+ * @param {*} item an event envelope or bare payload (may be missing risk fields pre-56)
86
113
  * @returns {{risk_score:number|null, confidence:number|null,
87
114
  * suggested_action:string|null, color:string}}
88
115
  */
89
116
  function surfaceRiskOne(item) {
90
117
  if (!item || typeof item !== 'object') return blankRow();
91
118
 
92
- const risk_score = finiteOrNull(item.risk_score);
93
- const confidence = finiteOrNull(item.confidence);
94
- const rawAction = item.suggested_action;
95
- const suggested_action =
96
- typeof rawAction === 'string' && VALID_ACTIONS.includes(rawAction) ? rawAction : null;
119
+ // Normalize: if the caller passed a full event envelope (with .payload), use the payload.
120
+ const src = (item.payload && typeof item.payload === 'object') ? item.payload : item;
121
+
122
+ const risk_score = finiteOrNull(src.risk_score);
123
+ const confidence = finiteOrNull(src.confidence);
124
+ const rawAction = src.suggested_action;
125
+ const canon = canonAction(rawAction);
126
+ const recognized =
127
+ canon !== null && Object.prototype.hasOwnProperty.call(ACTION_COLOR, canon);
128
+ // Preserve the action VERBATIM when recognized: the emitter sends lowercase
129
+ // (allow/review/require_confirmation/block), legacy callers may send CamelCase,
130
+ // and either is echoed back unchanged. Unrecognized / absent -> null.
131
+ const suggested_action = recognized ? rawAction : null;
97
132
 
98
133
  // Pre-56: when NONE of the risk fields are present, emit the blank placeholder
99
134
  // verbatim (color 'default') so the column reads as "not yet scored".
@@ -105,7 +140,7 @@ function surfaceRiskOne(item) {
105
140
  risk_score,
106
141
  confidence,
107
142
  suggested_action,
108
- color: colorForAction(suggested_action),
143
+ color: recognized ? ACTION_COLOR[canon] : 'default',
109
144
  };
110
145
  }
111
146
 
@@ -58,6 +58,10 @@ function tryRequire(relPath) {
58
58
  const designContextQuery = tryRequire('scripts/lib/design-context-query.cjs');
59
59
  const eventChain = tryRequire('scripts/lib/event-chain.cjs');
60
60
  const healthMirror = tryRequire('scripts/lib/health-mirror/index.cjs');
61
+ // Phase 57 (Round 3-E): state-store provides backendName() so the dashboard
62
+ // model can surface whether it read from SQLite or markdown. Soft-loaded so
63
+ // a missing module degrades without crashing. Never throws.
64
+ const stateStore = tryRequire('scripts/lib/state/state-store.cjs');
61
65
 
62
66
  // ---------------------------------------------------------------------------
63
67
  // .ts shared libs — dynamic import(pathToFileURL), memoized.
@@ -211,23 +215,54 @@ function scrapeEventsFile(eventsPath) {
211
215
 
212
216
  /**
213
217
  * Load STATE.md-derived fields: status, phase(stage), cycle, decisions[],
214
- * blockers[]. Tries the typed sdk/state read() first, then the file scrape.
218
+ * blockers[], backend. Tries the typed sdk/state read() first (which is
219
+ * already migration-active-aware for Phase 57: when BACKEND==='sqlite' and
220
+ * a sibling state.sqlite exists, the dual-write path ensures STATE.md is
221
+ * byte-equal with SQLite so read() returns the canonical view), then the
222
+ * file-scrape fallback. Never throws.
223
+ *
224
+ * The `backend` field reflects the active state-store backend:
225
+ * 'sqlite' — better-sqlite3 + FTS5 available and migration active
226
+ * 'markdown' — markdown floor (the universal default and CI surface)
215
227
  *
216
228
  * @param {string} root
217
229
  * @param {string[]} degraded
218
230
  * @returns {Promise<{status:string|null, phase:string|null, cycle:string|null,
219
- * decisions:Array, blockers:Array}>}
231
+ * decisions:Array, blockers:Array, backend:'sqlite'|'markdown'}>}
220
232
  */
221
233
  async function loadState(root, degraded) {
222
234
  const statePath = path.join(root, '.design', 'STATE.md');
223
- const empty = { status: null, phase: null, cycle: null, decisions: [], blockers: [] };
235
+ const empty = {
236
+ status: null, phase: null, cycle: null,
237
+ decisions: [], blockers: [],
238
+ backend: /** @type {'sqlite'|'markdown'} */ ('markdown'),
239
+ };
240
+
241
+ // Determine the active backend for this specific state path (Phase 57 R3-E).
242
+ // The migration-active gate is:
243
+ // BACKEND==='sqlite' AND existsSync(<statePath-sibling>/state.sqlite)
244
+ //
245
+ // We use state-store.backendName() to check the global probe result, then
246
+ // confirm by checking whether a sibling state.sqlite exists next to STATE.md.
247
+ // This mirrors the migrationActive() logic in sdk/state/index.ts exactly.
248
+ const globalBackend =
249
+ (stateStore && typeof stateStore.backendName === 'function')
250
+ ? /** @type {'sqlite'|'markdown'} */ (stateStore.backendName())
251
+ : 'markdown';
252
+ const sqliteSibling = path.join(root, '.design', 'state.sqlite');
253
+ const activeBackend = /** @type {'sqlite'|'markdown'} */ (
254
+ globalBackend === 'sqlite' && fs.existsSync(sqliteSibling) ? 'sqlite' : 'markdown'
255
+ );
224
256
 
225
257
  if (!fs.existsSync(statePath)) {
226
258
  degraded.push('state: .design/STATE.md not found');
227
- return empty;
259
+ return { ...empty, backend: activeBackend };
228
260
  }
229
261
 
230
262
  // 1) Typed lib read() — the in-process shared surface (R1).
263
+ // Phase 57: sdk/state read() is already migration-active-aware; when
264
+ // BACKEND==='sqlite' and state.sqlite sibling exists, STATE.md is kept
265
+ // byte-equal by the dual-write path, so no separate SQLite read needed.
231
266
  try {
232
267
  const stateMod = await importState();
233
268
  if (stateMod && typeof stateMod.read === 'function') {
@@ -239,6 +274,7 @@ async function loadState(root, degraded) {
239
274
  cycle: (parsed.frontmatter && parsed.frontmatter.cycle) || null,
240
275
  decisions: Array.isArray(parsed.decisions) ? parsed.decisions : [],
241
276
  blockers: Array.isArray(parsed.blockers) ? parsed.blockers : [],
277
+ backend: activeBackend,
242
278
  };
243
279
  }
244
280
  degraded.push('state: sdk/state import unavailable — using file scrape');
@@ -246,7 +282,7 @@ async function loadState(root, degraded) {
246
282
  degraded.push(`state: typed read failed (${errMsg(err)}) — using file scrape`);
247
283
  }
248
284
 
249
- // 2) File-scrape fallback.
285
+ // 2) File-scrape fallback (ultimate fallback; never throws).
250
286
  const scraped = scrapeStateFile(statePath);
251
287
  if (scraped) {
252
288
  return {
@@ -255,6 +291,7 @@ async function loadState(root, degraded) {
255
291
  cycle: scraped.cycle,
256
292
  decisions: scraped.decisions,
257
293
  blockers: scraped.blockers,
294
+ backend: 'markdown',
258
295
  };
259
296
  }
260
297
  degraded.push('state: scrape fallback failed');
@@ -508,6 +545,7 @@ function errMsg(err) {
508
545
  * sessions: Array,
509
546
  * degraded: string[],
510
547
  * root: string,
548
+ * backend: 'sqlite'|'markdown',
511
549
  * }>}
512
550
  */
513
551
  async function loadDashboardModel(opts = {}) {
@@ -564,6 +602,7 @@ async function loadDashboardModel(opts = {}) {
564
602
  sessions,
565
603
  degraded,
566
604
  root,
605
+ backend: stateRes.backend,
567
606
  };
568
607
  }
569
608
 
@@ -37,6 +37,21 @@ const readline = require('node:readline');
37
37
 
38
38
  const ansi = require('./ansi.cjs');
39
39
 
40
+ // Lazily require the risk-surface helper (same dep-free constraint as the data plane).
41
+ let _surfaceRisk = null;
42
+ function getSurfaceRisk() {
43
+ if (_surfaceRisk === null) {
44
+ try {
45
+ ({ surfaceRisk: _surfaceRisk } = require('../data/risk-surface.cjs'));
46
+ } catch {
47
+ // If the module is unavailable for any reason, fall back to a no-op that returns
48
+ // blank placeholder rows so the column still renders cleanly.
49
+ _surfaceRisk = () => ({ risk_score: null, confidence: null, suggested_action: null, color: 'default' });
50
+ }
51
+ }
52
+ return _surfaceRisk;
53
+ }
54
+
40
55
  // Lazily require the data plane so `renderFrame` (the pure path) can be imported + unit-tested
41
56
  // without paying for the data module's transitive requires. `run` resolves it on demand.
42
57
  let _loadDashboardModel = null;
@@ -351,11 +366,22 @@ function bodyFindings(model, inner, scroll) {
351
366
  if (tail.length === 0) {
352
367
  lines.push(' ' + ansi.color('no events', { dim: true }));
353
368
  } else {
369
+ const surfaceRisk = getSurfaceRisk();
354
370
  for (const ev of tail) {
355
371
  const name = (ev && (ev.event || ev.type || ev.kind)) || 'event';
356
- // Pre-Phase-56: risk/confidence are blank placeholders (D8).
372
+ // Phase-56+: surface risk/confidence from risk_assessment events.
373
+ // For pre-56 events that lack risk fields, surfaceRiskOne returns the blank placeholder.
374
+ const surfaced = (ev && ev.type === 'risk_assessment')
375
+ ? surfaceRisk(ev)
376
+ : { risk_score: null, confidence: null, suggested_action: null, color: 'default' };
377
+ const riskText = surfaced.risk_score !== null
378
+ ? ansi.color(surfaced.risk_score.toFixed(2), { fg: surfaced.color !== 'default' ? surfaced.color : undefined })
379
+ : ansi.color('·', { dim: true });
380
+ const confText = surfaced.confidence !== null
381
+ ? String(surfaced.confidence.toFixed(2))
382
+ : ansi.color('·', { dim: true });
357
383
  lines.push(ansi.columns(
358
- [String(name), ansi.color('·', { dim: true }), ansi.color('·', { dim: true })],
384
+ [String(name), riskText, confText],
359
385
  [Math.max(8, inner - 14), 5, 5],
360
386
  ));
361
387
  }
@@ -36,7 +36,7 @@ __export(server_exports, {
36
36
  });
37
37
  module.exports = __toCommonJS(server_exports);
38
38
  var import_node_fs5 = require("node:fs");
39
- var import_node_path3 = require("node:path");
39
+ var import_node_path4 = require("node:path");
40
40
  var import_server = require("@modelcontextprotocol/sdk/server/index.js");
41
41
  var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
42
42
  var import_types8 = require("@modelcontextprotocol/sdk/types.js");
@@ -196,6 +196,7 @@ __export(get_exports, {
196
196
 
197
197
  // sdk/state/index.ts
198
198
  var import_node_fs2 = require("node:fs");
199
+ var import_node_path = require("node:path");
199
200
 
200
201
  // sdk/state/lockfile.ts
201
202
  var import_node_fs = require("node:fs");
@@ -349,7 +350,10 @@ function getErrnoCode(err) {
349
350
  return void 0;
350
351
  }
351
352
  function sleep(ms) {
352
- return new Promise((resolve3) => setTimeout(resolve3, ms));
353
+ return new Promise((resolve4) => setTimeout(resolve4, ms));
354
+ }
355
+ async function acquireSqliteLock(sqlitePath, opts = {}) {
356
+ return acquire(sqlitePath, opts);
353
357
  }
354
358
 
355
359
  // sdk/state/parser.ts
@@ -1727,11 +1731,70 @@ function gateFor(from, to) {
1727
1731
  }
1728
1732
 
1729
1733
  // sdk/state/index.ts
1734
+ function _findPackageRoot(startDir) {
1735
+ let dir = (0, import_node_path.resolve)(startDir);
1736
+ let firstWithPkg = null;
1737
+ for (let i = 0; i < 12; i++) {
1738
+ const pkgPath = (0, import_node_path.join)(dir, "package.json");
1739
+ if ((0, import_node_fs2.existsSync)(pkgPath)) {
1740
+ try {
1741
+ const pkg = require(pkgPath);
1742
+ if (firstWithPkg === null) firstWithPkg = dir;
1743
+ if (pkg.name === "@hegemonart/get-design-done") return dir;
1744
+ } catch {
1745
+ if (firstWithPkg === null) firstWithPkg = dir;
1746
+ }
1747
+ }
1748
+ const parent = (0, import_node_path.dirname)(dir);
1749
+ if (parent === dir) break;
1750
+ dir = parent;
1751
+ }
1752
+ return firstWithPkg;
1753
+ }
1754
+ var _backendCache = null;
1755
+ function _loadBackend() {
1756
+ if (_backendCache !== null) return _backendCache === false ? null : _backendCache;
1757
+ try {
1758
+ const pkgRoot = _findPackageRoot(__dirname);
1759
+ if (pkgRoot === null) {
1760
+ _backendCache = false;
1761
+ return null;
1762
+ }
1763
+ const backendPath = (0, import_node_path.join)(pkgRoot, "scripts", "lib", "state", "state-backend.cjs");
1764
+ if (!(0, import_node_fs2.existsSync)(backendPath)) {
1765
+ _backendCache = false;
1766
+ return null;
1767
+ }
1768
+ _backendCache = require(backendPath);
1769
+ return _backendCache;
1770
+ } catch {
1771
+ _backendCache = false;
1772
+ return null;
1773
+ }
1774
+ }
1775
+ function migrationActive(statePath) {
1776
+ const backend = _loadBackend();
1777
+ if (backend === null || backend.BACKEND !== "sqlite") return false;
1778
+ const sqliteSibling = (0, import_node_path.join)((0, import_node_path.dirname)(statePath), "state.sqlite");
1779
+ if (!(0, import_node_fs2.existsSync)(sqliteSibling)) return false;
1780
+ try {
1781
+ if ((0, import_node_fs2.statSync)(sqliteSibling).isDirectory()) return false;
1782
+ } catch {
1783
+ return false;
1784
+ }
1785
+ return true;
1786
+ }
1730
1787
  async function read(path2) {
1731
1788
  const raw = (0, import_node_fs2.readFileSync)(path2, "utf8");
1732
1789
  return parse(raw).state;
1733
1790
  }
1734
1791
  async function mutate(path2, fn) {
1792
+ if (migrationActive(path2)) {
1793
+ return _mutateSqliteActive(path2, fn);
1794
+ }
1795
+ return _mutateMarkdown(path2, fn);
1796
+ }
1797
+ async function _mutateMarkdown(path2, fn) {
1735
1798
  const release = await acquire(path2);
1736
1799
  const tmpPath = `${path2}.tmp`;
1737
1800
  try {
@@ -1768,6 +1831,46 @@ async function mutate(path2, fn) {
1768
1831
  await release();
1769
1832
  }
1770
1833
  }
1834
+ async function _mutateSqliteActive(path2, fn) {
1835
+ const sqlitePath = (0, import_node_path.join)((0, import_node_path.dirname)(path2), "state.sqlite");
1836
+ const releaseSqliteLock = await acquireSqliteLock(sqlitePath);
1837
+ const release = await acquire(path2);
1838
+ const tmpPath = `${path2}.tmp`;
1839
+ try {
1840
+ const raw = (0, import_node_fs2.readFileSync)(path2, "utf8");
1841
+ const { state, raw_bodies, raw_frontmatter, block_gaps, line_ending } = parse(raw);
1842
+ const clone = structuredClone(state);
1843
+ const next = fn(clone);
1844
+ const out = serialize(next, {
1845
+ raw_frontmatter,
1846
+ raw_bodies,
1847
+ block_gaps,
1848
+ line_ending
1849
+ });
1850
+ (0, import_node_fs2.writeFileSync)(tmpPath, out, "utf8");
1851
+ try {
1852
+ (0, import_node_fs2.renameSync)(tmpPath, path2);
1853
+ } catch (err) {
1854
+ const code = typeof err === "object" && err !== null && "code" in err ? err.code : void 0;
1855
+ if (code === "EPERM" || code === "EBUSY") {
1856
+ await new Promise((r) => setTimeout(r, 50));
1857
+ (0, import_node_fs2.renameSync)(tmpPath, path2);
1858
+ } else {
1859
+ throw err;
1860
+ }
1861
+ }
1862
+ return next;
1863
+ } catch (err) {
1864
+ try {
1865
+ if ((0, import_node_fs2.existsSync)(tmpPath)) (0, import_node_fs2.unlinkSync)(tmpPath);
1866
+ } catch {
1867
+ }
1868
+ throw err;
1869
+ } finally {
1870
+ await release();
1871
+ await releaseSqliteLock();
1872
+ }
1873
+ }
1771
1874
  async function transition(path2, toStage) {
1772
1875
  const beforeMutate = await read(path2);
1773
1876
  const from = beforeMutate.position.stage;
@@ -1798,7 +1901,7 @@ async function transition(path2, toStage) {
1798
1901
  }
1799
1902
 
1800
1903
  // sdk/mcp/gdd-state/tools/shared.ts
1801
- var import_node_path2 = __toESM(require("node:path"));
1904
+ var import_node_path3 = __toESM(require("node:path"));
1802
1905
  var import_node_fs4 = require("node:fs");
1803
1906
  var import_node_module2 = require("node:module");
1804
1907
 
@@ -1848,13 +1951,13 @@ var EventBus = class extends import_node_events.EventEmitter {
1848
1951
 
1849
1952
  // sdk/event-stream/writer.ts
1850
1953
  var import_node_fs3 = require("node:fs");
1851
- var import_node_path = require("node:path");
1954
+ var import_node_path2 = require("node:path");
1852
1955
  var import_node_module = require("node:module");
1853
1956
  function _findRepoRoot() {
1854
1957
  let dir = process.cwd();
1855
1958
  for (let i = 0; i < 8; i++) {
1856
- if ((0, import_node_fs3.existsSync)((0, import_node_path.join)(dir, "package.json"))) return dir;
1857
- const parent = (0, import_node_path.dirname)(dir);
1959
+ if ((0, import_node_fs3.existsSync)((0, import_node_path2.join)(dir, "package.json"))) return dir;
1960
+ const parent = (0, import_node_path2.dirname)(dir);
1858
1961
  if (parent === dir) break;
1859
1962
  dir = parent;
1860
1963
  }
@@ -1863,16 +1966,16 @@ function _findRepoRoot() {
1863
1966
  var _redact;
1864
1967
  try {
1865
1968
  const _root = _findRepoRoot();
1866
- const _candidate = (0, import_node_path.resolve)(_root, "scripts/lib/redact.cjs");
1969
+ const _candidate = (0, import_node_path2.resolve)(_root, "scripts/lib/redact.cjs");
1867
1970
  if ((0, import_node_fs3.existsSync)(_candidate)) {
1868
- const _redactRequire = (0, import_node_module.createRequire)((0, import_node_path.join)(_root, "package.json"));
1971
+ const _redactRequire = (0, import_node_module.createRequire)((0, import_node_path2.join)(_root, "package.json"));
1869
1972
  const _mod = _redactRequire(_candidate);
1870
1973
  _redact = _mod.redact;
1871
1974
  } else {
1872
- const _altRoot = (0, import_node_path.resolve)(_root, "..", "..");
1873
- const _altCandidate = (0, import_node_path.resolve)(_altRoot, "scripts/lib/redact.cjs");
1975
+ const _altRoot = (0, import_node_path2.resolve)(_root, "..", "..");
1976
+ const _altCandidate = (0, import_node_path2.resolve)(_altRoot, "scripts/lib/redact.cjs");
1874
1977
  if ((0, import_node_fs3.existsSync)(_altCandidate)) {
1875
- const _altRequire = (0, import_node_module.createRequire)((0, import_node_path.join)(_altRoot, "package.json"));
1978
+ const _altRequire = (0, import_node_module.createRequire)((0, import_node_path2.join)(_altRoot, "package.json"));
1876
1979
  const _altMod = _altRequire(_altCandidate);
1877
1980
  _redact = _altMod.redact;
1878
1981
  } else {
@@ -1898,7 +2001,7 @@ var EventWriter = class {
1898
2001
  directoryEnsured = false;
1899
2002
  constructor(opts = {}) {
1900
2003
  const rawPath = opts.path ?? DEFAULT_EVENTS_PATH;
1901
- this.path = (0, import_node_path.isAbsolute)(rawPath) ? rawPath : (0, import_node_path.resolve)(process.cwd(), rawPath);
2004
+ this.path = (0, import_node_path2.isAbsolute)(rawPath) ? rawPath : (0, import_node_path2.resolve)(process.cwd(), rawPath);
1902
2005
  this.maxLineBytes = opts.maxLineBytes ?? DEFAULT_MAX_LINE_BYTES;
1903
2006
  }
1904
2007
  /**
@@ -1959,7 +2062,7 @@ var EventWriter = class {
1959
2062
  */
1960
2063
  ensureDirectory() {
1961
2064
  if (this.directoryEnsured) return;
1962
- (0, import_node_fs3.mkdirSync)((0, import_node_path.dirname)(this.path), { recursive: true });
2065
+ (0, import_node_fs3.mkdirSync)((0, import_node_path2.dirname)(this.path), { recursive: true });
1963
2066
  this.directoryEnsured = true;
1964
2067
  }
1965
2068
  };
@@ -2017,8 +2120,8 @@ function getSessionId() {
2017
2120
  function _findRepoRoot2() {
2018
2121
  let dir = process.cwd();
2019
2122
  for (let i = 0; i < 8; i++) {
2020
- if ((0, import_node_fs4.existsSync)(import_node_path2.default.join(dir, "package.json"))) return dir;
2021
- const parent = import_node_path2.default.dirname(dir);
2123
+ if ((0, import_node_fs4.existsSync)(import_node_path3.default.join(dir, "package.json"))) return dir;
2124
+ const parent = import_node_path3.default.dirname(dir);
2022
2125
  if (parent === dir) break;
2023
2126
  dir = parent;
2024
2127
  }
@@ -2027,9 +2130,9 @@ function _findRepoRoot2() {
2027
2130
  var _worktree = (() => {
2028
2131
  try {
2029
2132
  const root = _findRepoRoot2();
2030
- const candidate = import_node_path2.default.resolve(root, "scripts/lib/worktree-resolve.cjs");
2133
+ const candidate = import_node_path3.default.resolve(root, "scripts/lib/worktree-resolve.cjs");
2031
2134
  if (!(0, import_node_fs4.existsSync)(candidate)) return null;
2032
- const req = (0, import_node_module2.createRequire)(import_node_path2.default.join(root, "package.json"));
2135
+ const req = (0, import_node_module2.createRequire)(import_node_path3.default.join(root, "package.json"));
2033
2136
  return req(candidate);
2034
2137
  } catch {
2035
2138
  return null;
@@ -2042,20 +2145,20 @@ function resolveStatePath() {
2042
2145
  try {
2043
2146
  const designRoot = _worktree.resolveDesignRoot();
2044
2147
  if (_worktree.isWorktree()) {
2045
- _worktree.noticeOnce(import_node_path2.default.dirname(designRoot));
2148
+ _worktree.noticeOnce(import_node_path3.default.dirname(designRoot));
2046
2149
  }
2047
- return import_node_path2.default.join(designRoot, "STATE.md");
2150
+ return import_node_path3.default.join(designRoot, "STATE.md");
2048
2151
  } catch {
2049
2152
  }
2050
2153
  }
2051
2154
  return ".design/STATE.md";
2052
2155
  }
2053
- if (import_node_path2.default.isAbsolute(override)) {
2054
- return import_node_path2.default.resolve(override);
2156
+ if (import_node_path3.default.isAbsolute(override)) {
2157
+ return import_node_path3.default.resolve(override);
2055
2158
  }
2056
- const root = import_node_path2.default.resolve(process.cwd());
2057
- const resolved = import_node_path2.default.resolve(root, override);
2058
- const withSep = root.endsWith(import_node_path2.default.sep) ? root : root + import_node_path2.default.sep;
2159
+ const root = import_node_path3.default.resolve(process.cwd());
2160
+ const resolved = import_node_path3.default.resolve(root, override);
2161
+ const withSep = root.endsWith(import_node_path3.default.sep) ? root : root + import_node_path3.default.sep;
2059
2162
  if (resolved !== root && !resolved.startsWith(withSep)) {
2060
2163
  throwValidation(
2061
2164
  "STATE_PATH_ESCAPE",
@@ -2763,16 +2866,16 @@ var TOOL_COUNT = TOOL_MODULES.length;
2763
2866
  var SERVER_NAME = "gdd-state";
2764
2867
  var SERVER_VERSION = "1.20.0";
2765
2868
  function here() {
2766
- const expectedRel = (0, import_node_path3.join)("sdk", "mcp", "gdd-state");
2869
+ const expectedRel = (0, import_node_path4.join)("sdk", "mcp", "gdd-state");
2767
2870
  const entry = process.argv[1];
2768
2871
  if (typeof entry === "string" && entry.length > 0) {
2769
- const entryDir = (0, import_node_path3.dirname)((0, import_node_path3.resolve)(entry));
2770
- if ((0, import_node_fs5.existsSync)((0, import_node_path3.join)(entryDir, "tools", "index.ts"))) {
2872
+ const entryDir = (0, import_node_path4.dirname)((0, import_node_path4.resolve)(entry));
2873
+ if ((0, import_node_fs5.existsSync)((0, import_node_path4.join)(entryDir, "tools", "index.ts"))) {
2771
2874
  return entryDir;
2772
2875
  }
2773
2876
  }
2774
- const candidate = (0, import_node_path3.resolve)(process.cwd(), expectedRel);
2775
- if ((0, import_node_fs5.existsSync)((0, import_node_path3.join)(candidate, "tools", "index.ts"))) {
2877
+ const candidate = (0, import_node_path4.resolve)(process.cwd(), expectedRel);
2878
+ if ((0, import_node_fs5.existsSync)((0, import_node_path4.join)(candidate, "tools", "index.ts"))) {
2776
2879
  return candidate;
2777
2880
  }
2778
2881
  return candidate;
@@ -2780,7 +2883,7 @@ function here() {
2780
2883
  function loadTools() {
2781
2884
  const baseDir = here();
2782
2885
  return TOOL_MODULES.map((m) => {
2783
- const absPath = (0, import_node_path3.join)(baseDir, "tools", m.schemaPath);
2886
+ const absPath = (0, import_node_path4.join)(baseDir, "tools", m.schemaPath);
2784
2887
  const raw = (0, import_node_fs5.readFileSync)(absPath, "utf8");
2785
2888
  const parsed = JSON.parse(raw);
2786
2889
  const rawInput = parsed.properties?.input;
@@ -5,6 +5,14 @@
5
5
  // event. Optionally projects a subset of fields when `input.fields` is
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
+ //
9
+ // Phase 57 (Round 3-E): this tool already benefits from the SQLite read
10
+ // path transparently. `sdk/state/index.ts` `read()` delegates through the
11
+ // migration-active gate: when BACKEND==='sqlite' and a sibling state.sqlite
12
+ // exists the dual-write path keeps STATE.md byte-equal with SQLite state,
13
+ // so reading the on-disk STATE.md returns the canonical view regardless of
14
+ // which backend is active. No behavioral change is required here and the
15
+ // tool input/output SCHEMA IS UNCHANGED (no mcp-tools-manifest hash drift).
8
16
 
9
17
  import { read } from '../../../state/index.ts';
10
18
  import type { ParsedState } from '../../../state/types.ts';