@hegemonart/get-design-done 1.55.0 → 1.57.0
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.
- package/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +90 -0
- package/README.md +6 -0
- package/SKILL.md +2 -0
- package/agents/design-fixer.md +16 -0
- package/dist/claude-code/.claude/skills/override/SKILL.md +86 -0
- package/dist/claude-code/.claude/skills/state/SKILL.md +106 -0
- package/hooks/gdd-decision-injector.js +58 -0
- package/hooks/gdd-fact-force.js +434 -0
- package/hooks/gdd-risk-gate.js +406 -0
- package/hooks/hooks.json +18 -0
- package/package.json +1 -1
- package/reference/schemas/events.schema.json +61 -1
- package/reference/skill-graph.md +3 -1
- package/scripts/lib/manifest/skills.json +16 -0
- package/scripts/lib/risk/calibration.cjs +385 -0
- package/scripts/lib/risk/compute-risk.cjs +229 -0
- package/scripts/lib/risk/consumers.cjs +211 -0
- package/scripts/lib/risk/override.cjs +87 -0
- package/scripts/lib/risk/route.cjs +59 -0
- package/scripts/lib/risk/tables.cjs +221 -0
- package/scripts/lib/state/migrate-to-sqlite.cjs +664 -0
- package/scripts/lib/state/query-surface.cjs +391 -0
- package/scripts/lib/state/render-markdown.cjs +717 -0
- package/scripts/lib/state/state-backend.cjs +345 -0
- package/scripts/lib/state/state-store.cjs +735 -0
- package/sdk/cli/index.js +193 -96
- package/sdk/dashboard/data/source.cjs +44 -5
- package/sdk/mcp/gdd-state/server.js +127 -30
- package/sdk/mcp/gdd-state/tools/get.ts +8 -0
- package/sdk/state/index.ts +267 -13
- package/sdk/state/lockfile.ts +48 -0
- package/sdk/state/schema.sql +218 -0
- package/skills/override/SKILL.md +86 -0
- package/skills/state/SKILL.md +106 -0
|
@@ -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
|
|
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 = {
|
|
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
|
|
|
@@ -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
|
|
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((
|
|
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,64 @@ 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
|
+
return (0, import_node_fs2.existsSync)(sqliteSibling);
|
|
1780
|
+
}
|
|
1730
1781
|
async function read(path2) {
|
|
1731
1782
|
const raw = (0, import_node_fs2.readFileSync)(path2, "utf8");
|
|
1732
1783
|
return parse(raw).state;
|
|
1733
1784
|
}
|
|
1734
1785
|
async function mutate(path2, fn) {
|
|
1786
|
+
if (migrationActive(path2)) {
|
|
1787
|
+
return _mutateSqliteActive(path2, fn);
|
|
1788
|
+
}
|
|
1789
|
+
return _mutateMarkdown(path2, fn);
|
|
1790
|
+
}
|
|
1791
|
+
async function _mutateMarkdown(path2, fn) {
|
|
1735
1792
|
const release = await acquire(path2);
|
|
1736
1793
|
const tmpPath = `${path2}.tmp`;
|
|
1737
1794
|
try {
|
|
@@ -1768,6 +1825,46 @@ async function mutate(path2, fn) {
|
|
|
1768
1825
|
await release();
|
|
1769
1826
|
}
|
|
1770
1827
|
}
|
|
1828
|
+
async function _mutateSqliteActive(path2, fn) {
|
|
1829
|
+
const sqlitePath = (0, import_node_path.join)((0, import_node_path.dirname)(path2), "state.sqlite");
|
|
1830
|
+
const releaseSqliteLock = await acquireSqliteLock(sqlitePath);
|
|
1831
|
+
const release = await acquire(path2);
|
|
1832
|
+
const tmpPath = `${path2}.tmp`;
|
|
1833
|
+
try {
|
|
1834
|
+
const raw = (0, import_node_fs2.readFileSync)(path2, "utf8");
|
|
1835
|
+
const { state, raw_bodies, raw_frontmatter, block_gaps, line_ending } = parse(raw);
|
|
1836
|
+
const clone = structuredClone(state);
|
|
1837
|
+
const next = fn(clone);
|
|
1838
|
+
const out = serialize(next, {
|
|
1839
|
+
raw_frontmatter,
|
|
1840
|
+
raw_bodies,
|
|
1841
|
+
block_gaps,
|
|
1842
|
+
line_ending
|
|
1843
|
+
});
|
|
1844
|
+
(0, import_node_fs2.writeFileSync)(tmpPath, out, "utf8");
|
|
1845
|
+
try {
|
|
1846
|
+
(0, import_node_fs2.renameSync)(tmpPath, path2);
|
|
1847
|
+
} catch (err) {
|
|
1848
|
+
const code = typeof err === "object" && err !== null && "code" in err ? err.code : void 0;
|
|
1849
|
+
if (code === "EPERM" || code === "EBUSY") {
|
|
1850
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
1851
|
+
(0, import_node_fs2.renameSync)(tmpPath, path2);
|
|
1852
|
+
} else {
|
|
1853
|
+
throw err;
|
|
1854
|
+
}
|
|
1855
|
+
}
|
|
1856
|
+
return next;
|
|
1857
|
+
} catch (err) {
|
|
1858
|
+
try {
|
|
1859
|
+
if ((0, import_node_fs2.existsSync)(tmpPath)) (0, import_node_fs2.unlinkSync)(tmpPath);
|
|
1860
|
+
} catch {
|
|
1861
|
+
}
|
|
1862
|
+
throw err;
|
|
1863
|
+
} finally {
|
|
1864
|
+
await release();
|
|
1865
|
+
await releaseSqliteLock();
|
|
1866
|
+
}
|
|
1867
|
+
}
|
|
1771
1868
|
async function transition(path2, toStage) {
|
|
1772
1869
|
const beforeMutate = await read(path2);
|
|
1773
1870
|
const from = beforeMutate.position.stage;
|
|
@@ -1798,7 +1895,7 @@ async function transition(path2, toStage) {
|
|
|
1798
1895
|
}
|
|
1799
1896
|
|
|
1800
1897
|
// sdk/mcp/gdd-state/tools/shared.ts
|
|
1801
|
-
var
|
|
1898
|
+
var import_node_path3 = __toESM(require("node:path"));
|
|
1802
1899
|
var import_node_fs4 = require("node:fs");
|
|
1803
1900
|
var import_node_module2 = require("node:module");
|
|
1804
1901
|
|
|
@@ -1848,13 +1945,13 @@ var EventBus = class extends import_node_events.EventEmitter {
|
|
|
1848
1945
|
|
|
1849
1946
|
// sdk/event-stream/writer.ts
|
|
1850
1947
|
var import_node_fs3 = require("node:fs");
|
|
1851
|
-
var
|
|
1948
|
+
var import_node_path2 = require("node:path");
|
|
1852
1949
|
var import_node_module = require("node:module");
|
|
1853
1950
|
function _findRepoRoot() {
|
|
1854
1951
|
let dir = process.cwd();
|
|
1855
1952
|
for (let i = 0; i < 8; i++) {
|
|
1856
|
-
if ((0, import_node_fs3.existsSync)((0,
|
|
1857
|
-
const parent = (0,
|
|
1953
|
+
if ((0, import_node_fs3.existsSync)((0, import_node_path2.join)(dir, "package.json"))) return dir;
|
|
1954
|
+
const parent = (0, import_node_path2.dirname)(dir);
|
|
1858
1955
|
if (parent === dir) break;
|
|
1859
1956
|
dir = parent;
|
|
1860
1957
|
}
|
|
@@ -1863,16 +1960,16 @@ function _findRepoRoot() {
|
|
|
1863
1960
|
var _redact;
|
|
1864
1961
|
try {
|
|
1865
1962
|
const _root = _findRepoRoot();
|
|
1866
|
-
const _candidate = (0,
|
|
1963
|
+
const _candidate = (0, import_node_path2.resolve)(_root, "scripts/lib/redact.cjs");
|
|
1867
1964
|
if ((0, import_node_fs3.existsSync)(_candidate)) {
|
|
1868
|
-
const _redactRequire = (0, import_node_module.createRequire)((0,
|
|
1965
|
+
const _redactRequire = (0, import_node_module.createRequire)((0, import_node_path2.join)(_root, "package.json"));
|
|
1869
1966
|
const _mod = _redactRequire(_candidate);
|
|
1870
1967
|
_redact = _mod.redact;
|
|
1871
1968
|
} else {
|
|
1872
|
-
const _altRoot = (0,
|
|
1873
|
-
const _altCandidate = (0,
|
|
1969
|
+
const _altRoot = (0, import_node_path2.resolve)(_root, "..", "..");
|
|
1970
|
+
const _altCandidate = (0, import_node_path2.resolve)(_altRoot, "scripts/lib/redact.cjs");
|
|
1874
1971
|
if ((0, import_node_fs3.existsSync)(_altCandidate)) {
|
|
1875
|
-
const _altRequire = (0, import_node_module.createRequire)((0,
|
|
1972
|
+
const _altRequire = (0, import_node_module.createRequire)((0, import_node_path2.join)(_altRoot, "package.json"));
|
|
1876
1973
|
const _altMod = _altRequire(_altCandidate);
|
|
1877
1974
|
_redact = _altMod.redact;
|
|
1878
1975
|
} else {
|
|
@@ -1898,7 +1995,7 @@ var EventWriter = class {
|
|
|
1898
1995
|
directoryEnsured = false;
|
|
1899
1996
|
constructor(opts = {}) {
|
|
1900
1997
|
const rawPath = opts.path ?? DEFAULT_EVENTS_PATH;
|
|
1901
|
-
this.path = (0,
|
|
1998
|
+
this.path = (0, import_node_path2.isAbsolute)(rawPath) ? rawPath : (0, import_node_path2.resolve)(process.cwd(), rawPath);
|
|
1902
1999
|
this.maxLineBytes = opts.maxLineBytes ?? DEFAULT_MAX_LINE_BYTES;
|
|
1903
2000
|
}
|
|
1904
2001
|
/**
|
|
@@ -1959,7 +2056,7 @@ var EventWriter = class {
|
|
|
1959
2056
|
*/
|
|
1960
2057
|
ensureDirectory() {
|
|
1961
2058
|
if (this.directoryEnsured) return;
|
|
1962
|
-
(0, import_node_fs3.mkdirSync)((0,
|
|
2059
|
+
(0, import_node_fs3.mkdirSync)((0, import_node_path2.dirname)(this.path), { recursive: true });
|
|
1963
2060
|
this.directoryEnsured = true;
|
|
1964
2061
|
}
|
|
1965
2062
|
};
|
|
@@ -2017,8 +2114,8 @@ function getSessionId() {
|
|
|
2017
2114
|
function _findRepoRoot2() {
|
|
2018
2115
|
let dir = process.cwd();
|
|
2019
2116
|
for (let i = 0; i < 8; i++) {
|
|
2020
|
-
if ((0, import_node_fs4.existsSync)(
|
|
2021
|
-
const parent =
|
|
2117
|
+
if ((0, import_node_fs4.existsSync)(import_node_path3.default.join(dir, "package.json"))) return dir;
|
|
2118
|
+
const parent = import_node_path3.default.dirname(dir);
|
|
2022
2119
|
if (parent === dir) break;
|
|
2023
2120
|
dir = parent;
|
|
2024
2121
|
}
|
|
@@ -2027,9 +2124,9 @@ function _findRepoRoot2() {
|
|
|
2027
2124
|
var _worktree = (() => {
|
|
2028
2125
|
try {
|
|
2029
2126
|
const root = _findRepoRoot2();
|
|
2030
|
-
const candidate =
|
|
2127
|
+
const candidate = import_node_path3.default.resolve(root, "scripts/lib/worktree-resolve.cjs");
|
|
2031
2128
|
if (!(0, import_node_fs4.existsSync)(candidate)) return null;
|
|
2032
|
-
const req = (0, import_node_module2.createRequire)(
|
|
2129
|
+
const req = (0, import_node_module2.createRequire)(import_node_path3.default.join(root, "package.json"));
|
|
2033
2130
|
return req(candidate);
|
|
2034
2131
|
} catch {
|
|
2035
2132
|
return null;
|
|
@@ -2042,20 +2139,20 @@ function resolveStatePath() {
|
|
|
2042
2139
|
try {
|
|
2043
2140
|
const designRoot = _worktree.resolveDesignRoot();
|
|
2044
2141
|
if (_worktree.isWorktree()) {
|
|
2045
|
-
_worktree.noticeOnce(
|
|
2142
|
+
_worktree.noticeOnce(import_node_path3.default.dirname(designRoot));
|
|
2046
2143
|
}
|
|
2047
|
-
return
|
|
2144
|
+
return import_node_path3.default.join(designRoot, "STATE.md");
|
|
2048
2145
|
} catch {
|
|
2049
2146
|
}
|
|
2050
2147
|
}
|
|
2051
2148
|
return ".design/STATE.md";
|
|
2052
2149
|
}
|
|
2053
|
-
if (
|
|
2054
|
-
return
|
|
2150
|
+
if (import_node_path3.default.isAbsolute(override)) {
|
|
2151
|
+
return import_node_path3.default.resolve(override);
|
|
2055
2152
|
}
|
|
2056
|
-
const root =
|
|
2057
|
-
const resolved =
|
|
2058
|
-
const withSep = root.endsWith(
|
|
2153
|
+
const root = import_node_path3.default.resolve(process.cwd());
|
|
2154
|
+
const resolved = import_node_path3.default.resolve(root, override);
|
|
2155
|
+
const withSep = root.endsWith(import_node_path3.default.sep) ? root : root + import_node_path3.default.sep;
|
|
2059
2156
|
if (resolved !== root && !resolved.startsWith(withSep)) {
|
|
2060
2157
|
throwValidation(
|
|
2061
2158
|
"STATE_PATH_ESCAPE",
|
|
@@ -2763,16 +2860,16 @@ var TOOL_COUNT = TOOL_MODULES.length;
|
|
|
2763
2860
|
var SERVER_NAME = "gdd-state";
|
|
2764
2861
|
var SERVER_VERSION = "1.20.0";
|
|
2765
2862
|
function here() {
|
|
2766
|
-
const expectedRel = (0,
|
|
2863
|
+
const expectedRel = (0, import_node_path4.join)("sdk", "mcp", "gdd-state");
|
|
2767
2864
|
const entry = process.argv[1];
|
|
2768
2865
|
if (typeof entry === "string" && entry.length > 0) {
|
|
2769
|
-
const entryDir = (0,
|
|
2770
|
-
if ((0, import_node_fs5.existsSync)((0,
|
|
2866
|
+
const entryDir = (0, import_node_path4.dirname)((0, import_node_path4.resolve)(entry));
|
|
2867
|
+
if ((0, import_node_fs5.existsSync)((0, import_node_path4.join)(entryDir, "tools", "index.ts"))) {
|
|
2771
2868
|
return entryDir;
|
|
2772
2869
|
}
|
|
2773
2870
|
}
|
|
2774
|
-
const candidate = (0,
|
|
2775
|
-
if ((0, import_node_fs5.existsSync)((0,
|
|
2871
|
+
const candidate = (0, import_node_path4.resolve)(process.cwd(), expectedRel);
|
|
2872
|
+
if ((0, import_node_fs5.existsSync)((0, import_node_path4.join)(candidate, "tools", "index.ts"))) {
|
|
2776
2873
|
return candidate;
|
|
2777
2874
|
}
|
|
2778
2875
|
return candidate;
|
|
@@ -2780,7 +2877,7 @@ function here() {
|
|
|
2780
2877
|
function loadTools() {
|
|
2781
2878
|
const baseDir = here();
|
|
2782
2879
|
return TOOL_MODULES.map((m) => {
|
|
2783
|
-
const absPath = (0,
|
|
2880
|
+
const absPath = (0, import_node_path4.join)(baseDir, "tools", m.schemaPath);
|
|
2784
2881
|
const raw = (0, import_node_fs5.readFileSync)(absPath, "utf8");
|
|
2785
2882
|
const parsed = JSON.parse(raw);
|
|
2786
2883
|
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';
|