@hegemonart/get-design-done 1.32.0 → 1.33.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 (49) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +57 -0
  4. package/NOTICE +43 -5
  5. package/README.md +13 -0
  6. package/package.json +4 -2
  7. package/reference/gdd-runtime-audit.md +111 -0
  8. package/reference/gdd-threat-model.md +336 -0
  9. package/reference/registry.json +14 -0
  10. package/reference/schemas/pressure-scenario.schema.json +69 -0
  11. package/scripts/lib/peer-cli/acp-client.cjs +9 -1
  12. package/scripts/lib/peer-cli/asp-client.cjs +10 -1
  13. package/scripts/lib/peer-cli/sanitize-env.cjs +198 -0
  14. package/scripts/lib/redact.cjs +20 -1
  15. package/scripts/lib/skill-behavior/runner.cjs +187 -0
  16. package/scripts/lib/skill-behavior/stub-invoker.cjs +95 -0
  17. package/scripts/lib/skill-behavior/telemetry.cjs +379 -0
  18. package/scripts/lib/transports/ws.cjs +67 -3
  19. package/sdk/mcp/gdd-state/schemas/add_blocker.schema.json +2 -0
  20. package/sdk/mcp/gdd-state/schemas/add_decision.schema.json +1 -0
  21. package/sdk/mcp/gdd-state/schemas/add_must_have.schema.json +1 -0
  22. package/sdk/mcp/gdd-state/schemas/checkpoint.schema.json +1 -0
  23. package/sdk/mcp/gdd-state/schemas/frontmatter_update.schema.json +1 -1
  24. package/sdk/mcp/gdd-state/schemas/get.schema.json +2 -1
  25. package/sdk/mcp/gdd-state/schemas/probe_connections.schema.json +2 -0
  26. package/sdk/mcp/gdd-state/schemas/resolve_blocker.schema.json +1 -0
  27. package/sdk/mcp/gdd-state/server.js +137 -48
  28. package/sdk/mcp/gdd-state/tools/add_blocker.ts +2 -0
  29. package/sdk/mcp/gdd-state/tools/add_decision.ts +2 -0
  30. package/sdk/mcp/gdd-state/tools/add_must_have.ts +2 -0
  31. package/sdk/mcp/gdd-state/tools/checkpoint.ts +2 -0
  32. package/sdk/mcp/gdd-state/tools/frontmatter_update.ts +2 -0
  33. package/sdk/mcp/gdd-state/tools/get.ts +2 -0
  34. package/sdk/mcp/gdd-state/tools/probe_connections.ts +2 -0
  35. package/sdk/mcp/gdd-state/tools/resolve_blocker.ts +2 -0
  36. package/sdk/mcp/gdd-state/tools/set_status.ts +2 -0
  37. package/sdk/mcp/gdd-state/tools/shared.ts +117 -7
  38. package/sdk/mcp/gdd-state/tools/transition_stage.ts +2 -0
  39. package/sdk/mcp/gdd-state/tools/update_progress.ts +2 -0
  40. package/scripts/lib/cli/index.ts +0 -29
  41. package/scripts/lib/error-classifier.cjs +0 -29
  42. package/scripts/lib/event-stream/index.ts +0 -29
  43. package/scripts/lib/gdd-errors/index.ts +0 -29
  44. package/scripts/lib/gdd-state/index.ts +0 -29
  45. package/scripts/lib/iteration-budget.cjs +0 -29
  46. package/scripts/lib/jittered-backoff.cjs +0 -29
  47. package/scripts/lib/lockfile.cjs +0 -29
  48. package/scripts/mcp-servers/gdd-mcp/server.ts +0 -35
  49. package/scripts/mcp-servers/gdd-state/server.ts +0 -34
@@ -1,8 +1,10 @@
1
1
  #!/usr/bin/env -S node --experimental-strip-types
2
2
  "use strict";
3
+ var __create = Object.create;
3
4
  var __defProp = Object.defineProperty;
4
5
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
6
  var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
6
8
  var __hasOwnProp = Object.prototype.hasOwnProperty;
7
9
  var __export = (target, all) => {
8
10
  for (var name12 in all)
@@ -16,6 +18,14 @@ var __copyProps = (to, from, except, desc) => {
16
18
  }
17
19
  return to;
18
20
  };
21
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
22
+ // If the importer is in node compatibility mode or this is not an ESM
23
+ // file that has been converted to a CommonJS file using a Babel-
24
+ // compatible transform (i.e. "__esModule" has not been set), then set
25
+ // "default" to the CommonJS "module.exports" for node compatibility.
26
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
27
+ mod
28
+ ));
19
29
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
30
 
21
31
  // sdk/mcp/gdd-state/server.ts
@@ -26,7 +36,7 @@ __export(server_exports, {
26
36
  });
27
37
  module.exports = __toCommonJS(server_exports);
28
38
  var import_node_fs4 = require("node:fs");
29
- var import_node_path2 = require("node:path");
39
+ var import_node_path3 = require("node:path");
30
40
  var import_server = require("@modelcontextprotocol/sdk/server/index.js");
31
41
  var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
32
42
  var import_types8 = require("@modelcontextprotocol/sdk/types.js");
@@ -220,11 +230,11 @@ var DEFAULTS = {
220
230
  maxWaitMs: 5e3,
221
231
  pollMs: 50
222
232
  };
223
- async function acquire(path, opts = {}) {
233
+ async function acquire(path2, opts = {}) {
224
234
  const staleMs = opts.staleMs ?? DEFAULTS.staleMs;
225
235
  const maxWaitMs = opts.maxWaitMs ?? DEFAULTS.maxWaitMs;
226
236
  const pollMs = opts.pollMs ?? DEFAULTS.pollMs;
227
- const lockPath = `${path}.lock`;
237
+ const lockPath = `${path2}.lock`;
228
238
  const startedAt = Date.now();
229
239
  const payload = {
230
240
  pid: process.pid,
@@ -289,9 +299,9 @@ function makeRelease(lockPath) {
289
299
  }
290
300
  };
291
301
  }
292
- function readLockSafe(path) {
302
+ function readLockSafe(path2) {
293
303
  try {
294
- return (0, import_node_fs.readFileSync)(path, "utf8");
304
+ return (0, import_node_fs.readFileSync)(path2, "utf8");
295
305
  } catch (err) {
296
306
  const code = getErrnoCode(err);
297
307
  if (code === "ENOENT") return null;
@@ -1717,15 +1727,15 @@ function gateFor(from, to) {
1717
1727
  }
1718
1728
 
1719
1729
  // sdk/state/index.ts
1720
- async function read(path) {
1721
- const raw = (0, import_node_fs2.readFileSync)(path, "utf8");
1730
+ async function read(path2) {
1731
+ const raw = (0, import_node_fs2.readFileSync)(path2, "utf8");
1722
1732
  return parse(raw).state;
1723
1733
  }
1724
- async function mutate(path, fn) {
1725
- const release = await acquire(path);
1726
- const tmpPath = `${path}.tmp`;
1734
+ async function mutate(path2, fn) {
1735
+ const release = await acquire(path2);
1736
+ const tmpPath = `${path2}.tmp`;
1727
1737
  try {
1728
- const raw = (0, import_node_fs2.readFileSync)(path, "utf8");
1738
+ const raw = (0, import_node_fs2.readFileSync)(path2, "utf8");
1729
1739
  const { state, raw_bodies, raw_frontmatter, block_gaps, line_ending } = parse(raw);
1730
1740
  const clone = structuredClone(state);
1731
1741
  const next = fn(clone);
@@ -1737,12 +1747,12 @@ async function mutate(path, fn) {
1737
1747
  });
1738
1748
  (0, import_node_fs2.writeFileSync)(tmpPath, out, "utf8");
1739
1749
  try {
1740
- (0, import_node_fs2.renameSync)(tmpPath, path);
1750
+ (0, import_node_fs2.renameSync)(tmpPath, path2);
1741
1751
  } catch (err) {
1742
1752
  const code = typeof err === "object" && err !== null && "code" in err ? err.code : void 0;
1743
1753
  if (code === "EPERM" || code === "EBUSY") {
1744
1754
  await new Promise((r) => setTimeout(r, 50));
1745
- (0, import_node_fs2.renameSync)(tmpPath, path);
1755
+ (0, import_node_fs2.renameSync)(tmpPath, path2);
1746
1756
  } else {
1747
1757
  throw err;
1748
1758
  }
@@ -1758,8 +1768,8 @@ async function mutate(path, fn) {
1758
1768
  await release();
1759
1769
  }
1760
1770
  }
1761
- async function transition(path, toStage) {
1762
- const beforeMutate = await read(path);
1771
+ async function transition(path2, toStage) {
1772
+ const beforeMutate = await read(path2);
1763
1773
  const from = beforeMutate.position.stage;
1764
1774
  if (!isStage(from)) {
1765
1775
  throw new TransitionGateFailed(toStage, [
@@ -1777,7 +1787,7 @@ async function transition(path, toStage) {
1777
1787
  throw new TransitionGateFailed(toStage, gateResult.blockers);
1778
1788
  }
1779
1789
  const nowIso = (/* @__PURE__ */ new Date()).toISOString();
1780
- const nextState = await mutate(path, (s) => {
1790
+ const nextState = await mutate(path2, (s) => {
1781
1791
  s.frontmatter.stage = toStage;
1782
1792
  s.frontmatter.last_checkpoint = nowIso;
1783
1793
  s.position.stage = toStage;
@@ -1787,6 +1797,9 @@ async function transition(path, toStage) {
1787
1797
  return { pass: true, blockers: gateResult.blockers, state: nextState };
1788
1798
  }
1789
1799
 
1800
+ // sdk/mcp/gdd-state/tools/shared.ts
1801
+ var import_node_path2 = __toESM(require("node:path"));
1802
+
1790
1803
  // sdk/event-stream/index.ts
1791
1804
  var import_node_os2 = require("node:os");
1792
1805
 
@@ -2001,8 +2014,73 @@ function getSessionId() {
2001
2014
  }
2002
2015
  function resolveStatePath() {
2003
2016
  const override = process.env["GDD_STATE_PATH"];
2004
- if (typeof override === "string" && override.length > 0) return override;
2005
- return ".design/STATE.md";
2017
+ if (typeof override !== "string" || override.length === 0) {
2018
+ return ".design/STATE.md";
2019
+ }
2020
+ if (import_node_path2.default.isAbsolute(override)) {
2021
+ return import_node_path2.default.resolve(override);
2022
+ }
2023
+ const root = import_node_path2.default.resolve(process.cwd());
2024
+ const resolved = import_node_path2.default.resolve(root, override);
2025
+ const withSep = root.endsWith(import_node_path2.default.sep) ? root : root + import_node_path2.default.sep;
2026
+ if (resolved !== root && !resolved.startsWith(withSep)) {
2027
+ throwValidation(
2028
+ "STATE_PATH_ESCAPE",
2029
+ `GDD_STATE_PATH (relative) escapes the project boundary: ${override}`,
2030
+ { raw: override, resolved, root }
2031
+ );
2032
+ }
2033
+ return resolved;
2034
+ }
2035
+ var MAX_INPUT_BYTES = 64 * 1024;
2036
+ var MAX_STRING_LEN = 8192;
2037
+ var MAX_DEPTH = 32;
2038
+ function assertInputWithinLimits(input, opts) {
2039
+ const maxBytes = opts?.maxInputBytes ?? MAX_INPUT_BYTES;
2040
+ const maxStr = opts?.maxStringLen ?? MAX_STRING_LEN;
2041
+ const maxDepth = opts?.maxDepth ?? MAX_DEPTH;
2042
+ const walk = (node, depth) => {
2043
+ if (depth > maxDepth) {
2044
+ throwValidation(
2045
+ "INPUT_TOO_DEEP",
2046
+ `Input nesting exceeds the maximum depth of ${maxDepth}`,
2047
+ { maxDepth }
2048
+ );
2049
+ }
2050
+ if (typeof node === "string") {
2051
+ if (node.length > maxStr) {
2052
+ throwValidation(
2053
+ "INPUT_FIELD_TOO_LARGE",
2054
+ `A string field exceeds the maximum length of ${maxStr}`,
2055
+ { maxStringLen: maxStr, length: node.length }
2056
+ );
2057
+ }
2058
+ return;
2059
+ }
2060
+ if (Array.isArray(node)) {
2061
+ for (const item of node) walk(item, depth + 1);
2062
+ return;
2063
+ }
2064
+ if (node !== null && typeof node === "object") {
2065
+ for (const value of Object.values(node)) {
2066
+ walk(value, depth + 1);
2067
+ }
2068
+ }
2069
+ };
2070
+ walk(input, 0);
2071
+ let bytes;
2072
+ try {
2073
+ bytes = Buffer.byteLength(JSON.stringify(input) ?? "");
2074
+ } catch {
2075
+ throwValidation("INPUT_TOO_LARGE", "Input is not serializable JSON", {});
2076
+ }
2077
+ if (bytes > maxBytes) {
2078
+ throwValidation(
2079
+ "INPUT_TOO_LARGE",
2080
+ `Serialized input (${bytes} bytes) exceeds the maximum of ${maxBytes} bytes`,
2081
+ { maxInputBytes: maxBytes, bytes }
2082
+ );
2083
+ }
2006
2084
  }
2007
2085
  function isStageValue(value) {
2008
2086
  return value === "brief" || value === "explore" || value === "plan" || value === "design" || value === "verify";
@@ -2062,12 +2140,13 @@ function project(state, fields) {
2062
2140
  }
2063
2141
  async function handle(input) {
2064
2142
  try {
2143
+ assertInputWithinLimits(input);
2065
2144
  const typed = input ?? {};
2066
- const path = resolveStatePath();
2067
- const state = await read(path);
2145
+ const path2 = resolveStatePath();
2146
+ const state = await read(path2);
2068
2147
  const fields = Array.isArray(typed.fields) ? typed.fields : null;
2069
2148
  const stateOut = fields === null || fields.length === 0 ? state : project(state, fields);
2070
- return okResponse({ state: stateOut, path });
2149
+ return okResponse({ state: stateOut, path: path2 });
2071
2150
  } catch (err) {
2072
2151
  return errorResponse(err);
2073
2152
  }
@@ -2091,6 +2170,7 @@ var STATUSES = /* @__PURE__ */ new Set([
2091
2170
  ]);
2092
2171
  async function handle2(input) {
2093
2172
  try {
2173
+ assertInputWithinLimits(input);
2094
2174
  const typed = input ?? {};
2095
2175
  if (typed.task_progress === void 0 && typed.status === void 0) {
2096
2176
  throwValidation(
@@ -2112,9 +2192,9 @@ async function handle2(input) {
2112
2192
  `status "${typed.status}" is not one of initialized/in_progress/completed/blocked`
2113
2193
  );
2114
2194
  }
2115
- const path = resolveStatePath();
2195
+ const path2 = resolveStatePath();
2116
2196
  const diff = {};
2117
- const after = await mutate(path, (s) => {
2197
+ const after = await mutate(path2, (s) => {
2118
2198
  if (typed.task_progress !== void 0) {
2119
2199
  diff["task_progress"] = {
2120
2200
  before: s.position.task_progress,
@@ -2146,6 +2226,7 @@ var name3 = "gdd_state__transition_stage";
2146
2226
  var schemaPath3 = "../schemas/transition_stage.schema.json";
2147
2227
  async function handle3(input) {
2148
2228
  try {
2229
+ assertInputWithinLimits(input);
2149
2230
  const typed = input ?? {};
2150
2231
  if (!isStage(typed.to)) {
2151
2232
  throwValidation(
@@ -2153,11 +2234,11 @@ async function handle3(input) {
2153
2234
  `to "${String(typed.to)}" is not a recognized Stage`
2154
2235
  );
2155
2236
  }
2156
- const path = resolveStatePath();
2157
- const before = await read(path);
2237
+ const path2 = resolveStatePath();
2238
+ const before = await read(path2);
2158
2239
  const fromValue = before.position.stage;
2159
2240
  try {
2160
- const result = await transition(path, typed.to);
2241
+ const result = await transition(path2, typed.to);
2161
2242
  const fromNarrow = isStage(fromValue) ? fromValue : typed.to;
2162
2243
  emitStateTransition(fromNarrow, typed.to, true, [], result.state);
2163
2244
  return okResponse({
@@ -2198,6 +2279,7 @@ function today() {
2198
2279
  }
2199
2280
  async function handle4(input) {
2200
2281
  try {
2282
+ assertInputWithinLimits(input);
2201
2283
  const typed = input ?? {};
2202
2284
  if (typeof typed.text !== "string" || typed.text.length === 0) {
2203
2285
  throwValidation("MISSING_FIELD", "add_blocker requires a non-empty text");
@@ -2208,10 +2290,10 @@ async function handle4(input) {
2208
2290
  `date "${typed.date}" must be YYYY-MM-DD`
2209
2291
  );
2210
2292
  }
2211
- const path = resolveStatePath();
2293
+ const path2 = resolveStatePath();
2212
2294
  let appended = null;
2213
2295
  let countAfter = 0;
2214
- const after = await mutate(path, (s) => {
2296
+ const after = await mutate(path2, (s) => {
2215
2297
  const blocker = {
2216
2298
  stage: typed.stage ?? s.position.stage ?? "",
2217
2299
  date: typed.date ?? today(),
@@ -2243,6 +2325,7 @@ var name5 = "gdd_state__resolve_blocker";
2243
2325
  var schemaPath5 = "../schemas/resolve_blocker.schema.json";
2244
2326
  async function handle5(input) {
2245
2327
  try {
2328
+ assertInputWithinLimits(input);
2246
2329
  const typed = input ?? {};
2247
2330
  const hasIndex = typeof typed.index === "number";
2248
2331
  const hasText = typeof typed.text === "string" && typed.text.length > 0;
@@ -2255,10 +2338,10 @@ async function handle5(input) {
2255
2338
  if (hasIndex && typed.index < 0) {
2256
2339
  throwValidation("INDEX_NEGATIVE", "index must be >= 0");
2257
2340
  }
2258
- const path = resolveStatePath();
2341
+ const path2 = resolveStatePath();
2259
2342
  let removed = null;
2260
2343
  let countAfter = 0;
2261
- const after = await mutate(path, (s) => {
2344
+ const after = await mutate(path2, (s) => {
2262
2345
  if (hasIndex) {
2263
2346
  const idx = typed.index;
2264
2347
  if (idx >= s.blockers.length) {
@@ -2319,6 +2402,7 @@ function nextDecisionId(existing) {
2319
2402
  }
2320
2403
  async function handle6(input) {
2321
2404
  try {
2405
+ assertInputWithinLimits(input);
2322
2406
  const typed = input ?? {};
2323
2407
  if (typeof typed.text !== "string" || typed.text.length === 0) {
2324
2408
  throwValidation("MISSING_FIELD", "add_decision requires a non-empty text");
@@ -2332,10 +2416,10 @@ async function handle6(input) {
2332
2416
  if (typed.id !== void 0 && !ID_RE.test(typed.id)) {
2333
2417
  throwValidation("ID_FORMAT", `id "${typed.id}" must match D-<digits>`);
2334
2418
  }
2335
- const path = resolveStatePath();
2419
+ const path2 = resolveStatePath();
2336
2420
  let appended = null;
2337
2421
  let countAfter = 0;
2338
- const after = await mutate(path, (s) => {
2422
+ const after = await mutate(path2, (s) => {
2339
2423
  const decision = {
2340
2424
  id: typed.id ?? nextDecisionId(s.decisions),
2341
2425
  text: typed.text,
@@ -2379,6 +2463,7 @@ function nextMustHaveId(existing) {
2379
2463
  }
2380
2464
  async function handle7(input) {
2381
2465
  try {
2466
+ assertInputWithinLimits(input);
2382
2467
  const typed = input ?? {};
2383
2468
  if (typeof typed.text !== "string" || typed.text.length === 0) {
2384
2469
  throwValidation(
@@ -2395,11 +2480,11 @@ async function handle7(input) {
2395
2480
  if (typed.id !== void 0 && !ID_RE2.test(typed.id)) {
2396
2481
  throwValidation("ID_FORMAT", `id "${typed.id}" must match M-<digits>`);
2397
2482
  }
2398
- const path = resolveStatePath();
2483
+ const path2 = resolveStatePath();
2399
2484
  let written = null;
2400
2485
  let action = "insert";
2401
2486
  let countAfter = 0;
2402
- const after = await mutate(path, (s) => {
2487
+ const after = await mutate(path2, (s) => {
2403
2488
  const mh = {
2404
2489
  id: typed.id ?? nextMustHaveId(s.must_haves),
2405
2490
  text: typed.text,
@@ -2448,6 +2533,7 @@ var STATUSES2 = /* @__PURE__ */ new Set([
2448
2533
  ]);
2449
2534
  async function handle8(input) {
2450
2535
  try {
2536
+ assertInputWithinLimits(input);
2451
2537
  const typed = input ?? {};
2452
2538
  if (typeof typed.status !== "string" || !STATUSES2.has(typed.status)) {
2453
2539
  throwValidation(
@@ -2455,9 +2541,9 @@ async function handle8(input) {
2455
2541
  `status "${String(typed.status)}" is not one of initialized/in_progress/completed/blocked`
2456
2542
  );
2457
2543
  }
2458
- const path = resolveStatePath();
2544
+ const path2 = resolveStatePath();
2459
2545
  const diff = {};
2460
- const after = await mutate(path, (s) => {
2546
+ const after = await mutate(path2, (s) => {
2461
2547
  diff["status"] = { before: s.position.status, after: typed.status };
2462
2548
  s.position.status = typed.status;
2463
2549
  return s;
@@ -2480,14 +2566,15 @@ var name9 = "gdd_state__checkpoint";
2480
2566
  var schemaPath9 = "../schemas/checkpoint.schema.json";
2481
2567
  async function handle9(input) {
2482
2568
  try {
2569
+ assertInputWithinLimits(input);
2483
2570
  const typed = input ?? {};
2484
2571
  if (typed.label !== void 0 && (typeof typed.label !== "string" || typed.label.length === 0)) {
2485
2572
  throwValidation("LABEL_FORMAT", "label must be a non-empty string");
2486
2573
  }
2487
- const path = resolveStatePath();
2574
+ const path2 = resolveStatePath();
2488
2575
  const nowIso = (/* @__PURE__ */ new Date()).toISOString();
2489
2576
  let timestampKey = "";
2490
- const after = await mutate(path, (s) => {
2577
+ const after = await mutate(path2, (s) => {
2491
2578
  s.frontmatter.last_checkpoint = nowIso;
2492
2579
  const stage = typeof s.position.stage === "string" ? s.position.stage : "";
2493
2580
  timestampKey = typed.label !== void 0 ? `${typed.label}_at` : stage.length > 0 ? `${stage}_checkpoint_at` : "checkpoint_at";
@@ -2516,6 +2603,7 @@ var name10 = "gdd_state__probe_connections";
2516
2603
  var schemaPath10 = "../schemas/probe_connections.schema.json";
2517
2604
  async function handle10(input) {
2518
2605
  try {
2606
+ assertInputWithinLimits(input);
2519
2607
  const typed = input ?? {};
2520
2608
  if (!Array.isArray(typed.probe_results) || typed.probe_results.length === 0) {
2521
2609
  throwValidation(
@@ -2537,10 +2625,10 @@ async function handle10(input) {
2537
2625
  );
2538
2626
  }
2539
2627
  }
2540
- const path = resolveStatePath();
2628
+ const path2 = resolveStatePath();
2541
2629
  const updated = [];
2542
2630
  const diff = {};
2543
- const after = await mutate(path, (s) => {
2631
+ const after = await mutate(path2, (s) => {
2544
2632
  for (const p of typed.probe_results) {
2545
2633
  const before = Object.prototype.hasOwnProperty.call(s.connections, p.name) ? s.connections[p.name] : null;
2546
2634
  s.connections[p.name] = p.status;
@@ -2575,6 +2663,7 @@ function isScalar(v) {
2575
2663
  }
2576
2664
  async function handle11(input) {
2577
2665
  try {
2666
+ assertInputWithinLimits(input);
2578
2667
  const typed = input ?? {};
2579
2668
  if (typed.patch === void 0 || typed.patch === null || typeof typed.patch !== "object") {
2580
2669
  throwValidation(
@@ -2603,9 +2692,9 @@ async function handle11(input) {
2603
2692
  );
2604
2693
  }
2605
2694
  }
2606
- const path = resolveStatePath();
2695
+ const path2 = resolveStatePath();
2607
2696
  const diff = {};
2608
- const after = await mutate(path, (s) => {
2697
+ const after = await mutate(path2, (s) => {
2609
2698
  for (const k of keys) {
2610
2699
  const before = s.frontmatter[k];
2611
2700
  const value = typed.patch[k];
@@ -2641,16 +2730,16 @@ var TOOL_COUNT = TOOL_MODULES.length;
2641
2730
  var SERVER_NAME = "gdd-state";
2642
2731
  var SERVER_VERSION = "1.20.0";
2643
2732
  function here() {
2644
- const expectedRel = (0, import_node_path2.join)("sdk", "mcp", "gdd-state");
2733
+ const expectedRel = (0, import_node_path3.join)("sdk", "mcp", "gdd-state");
2645
2734
  const entry = process.argv[1];
2646
2735
  if (typeof entry === "string" && entry.length > 0) {
2647
- const entryDir = (0, import_node_path2.dirname)((0, import_node_path2.resolve)(entry));
2648
- if ((0, import_node_fs4.existsSync)((0, import_node_path2.join)(entryDir, "tools", "index.ts"))) {
2736
+ const entryDir = (0, import_node_path3.dirname)((0, import_node_path3.resolve)(entry));
2737
+ if ((0, import_node_fs4.existsSync)((0, import_node_path3.join)(entryDir, "tools", "index.ts"))) {
2649
2738
  return entryDir;
2650
2739
  }
2651
2740
  }
2652
- const candidate = (0, import_node_path2.resolve)(process.cwd(), expectedRel);
2653
- if ((0, import_node_fs4.existsSync)((0, import_node_path2.join)(candidate, "tools", "index.ts"))) {
2741
+ const candidate = (0, import_node_path3.resolve)(process.cwd(), expectedRel);
2742
+ if ((0, import_node_fs4.existsSync)((0, import_node_path3.join)(candidate, "tools", "index.ts"))) {
2654
2743
  return candidate;
2655
2744
  }
2656
2745
  return candidate;
@@ -2658,7 +2747,7 @@ function here() {
2658
2747
  function loadTools() {
2659
2748
  const baseDir = here();
2660
2749
  return TOOL_MODULES.map((m) => {
2661
- const absPath = (0, import_node_path2.join)(baseDir, "tools", m.schemaPath);
2750
+ const absPath = (0, import_node_path3.join)(baseDir, "tools", m.schemaPath);
2662
2751
  const raw = (0, import_node_fs4.readFileSync)(absPath, "utf8");
2663
2752
  const parsed = JSON.parse(raw);
2664
2753
  const rawInput = parsed.properties?.input;
@@ -8,6 +8,7 @@
8
8
  import { mutate } from '../../../state/index.ts';
9
9
  import type { Blocker } from '../../../state/types.ts';
10
10
  import {
11
+ assertInputWithinLimits,
11
12
  emitStateMutation,
12
13
  errorResponse,
13
14
  okResponse,
@@ -34,6 +35,7 @@ function today(): string {
34
35
 
35
36
  export async function handle(input: unknown): Promise<ToolResponse> {
36
37
  try {
38
+ assertInputWithinLimits(input);
37
39
  const typed = (input ?? {}) as AddBlockerInput;
38
40
  if (typeof typed.text !== 'string' || typed.text.length === 0) {
39
41
  throwValidation('MISSING_FIELD', 'add_blocker requires a non-empty text');
@@ -8,6 +8,7 @@
8
8
  import { mutate } from '../../../state/index.ts';
9
9
  import type { Decision } from '../../../state/types.ts';
10
10
  import {
11
+ assertInputWithinLimits,
11
12
  emitStateMutation,
12
13
  errorResponse,
13
14
  okResponse,
@@ -46,6 +47,7 @@ function nextDecisionId(existing: Decision[]): string {
46
47
 
47
48
  export async function handle(input: unknown): Promise<ToolResponse> {
48
49
  try {
50
+ assertInputWithinLimits(input);
49
51
  const typed = (input ?? {}) as AddDecisionInput;
50
52
  if (typeof typed.text !== 'string' || typed.text.length === 0) {
51
53
  throwValidation('MISSING_FIELD', 'add_decision requires a non-empty text');
@@ -12,6 +12,7 @@
12
12
  import { mutate } from '../../../state/index.ts';
13
13
  import type { MustHave } from '../../../state/types.ts';
14
14
  import {
15
+ assertInputWithinLimits,
15
16
  emitStateMutation,
16
17
  errorResponse,
17
18
  okResponse,
@@ -46,6 +47,7 @@ function nextMustHaveId(existing: MustHave[]): string {
46
47
 
47
48
  export async function handle(input: unknown): Promise<ToolResponse> {
48
49
  try {
50
+ assertInputWithinLimits(input);
49
51
  const typed = (input ?? {}) as AddMustHaveInput;
50
52
  if (typeof typed.text !== 'string' || typed.text.length === 0) {
51
53
  throwValidation(
@@ -8,6 +8,7 @@
8
8
 
9
9
  import { mutate } from '../../../state/index.ts';
10
10
  import {
11
+ assertInputWithinLimits,
11
12
  emitStateMutation,
12
13
  errorResponse,
13
14
  okResponse,
@@ -25,6 +26,7 @@ export interface CheckpointInput {
25
26
 
26
27
  export async function handle(input: unknown): Promise<ToolResponse> {
27
28
  try {
29
+ assertInputWithinLimits(input);
28
30
  const typed = (input ?? {}) as CheckpointInput;
29
31
  if (
30
32
  typed.label !== undefined &&
@@ -10,6 +10,7 @@
10
10
 
11
11
  import { mutate } from '../../../state/index.ts';
12
12
  import {
13
+ assertInputWithinLimits,
13
14
  emitStateMutation,
14
15
  errorResponse,
15
16
  okResponse,
@@ -38,6 +39,7 @@ function isScalar(v: unknown): v is string | number | boolean {
38
39
 
39
40
  export async function handle(input: unknown): Promise<ToolResponse> {
40
41
  try {
42
+ assertInputWithinLimits(input);
41
43
  const typed = (input ?? {}) as FrontmatterUpdateInput;
42
44
  if (
43
45
  typed.patch === undefined ||
@@ -9,6 +9,7 @@
9
9
  import { read } from '../../../state/index.ts';
10
10
  import type { ParsedState } from '../../../state/types.ts';
11
11
  import {
12
+ assertInputWithinLimits,
12
13
  errorResponse,
13
14
  okResponse,
14
15
  resolveStatePath,
@@ -38,6 +39,7 @@ function project(state: ParsedState, fields: string[]): Record<string, unknown>
38
39
 
39
40
  export async function handle(input: unknown): Promise<ToolResponse> {
40
41
  try {
42
+ assertInputWithinLimits(input);
41
43
  const typed = (input ?? {}) as GetInput;
42
44
  const path = resolveStatePath();
43
45
  const state = await read(path);
@@ -11,6 +11,7 @@ import {
11
11
  type ConnectionStatus,
12
12
  } from '../../../state/types.ts';
13
13
  import {
14
+ assertInputWithinLimits,
14
15
  emitStateMutation,
15
16
  errorResponse,
16
17
  okResponse,
@@ -28,6 +29,7 @@ export interface ProbeConnectionsInput {
28
29
 
29
30
  export async function handle(input: unknown): Promise<ToolResponse> {
30
31
  try {
32
+ assertInputWithinLimits(input);
31
33
  const typed = (input ?? {}) as ProbeConnectionsInput;
32
34
  if (!Array.isArray(typed.probe_results) || typed.probe_results.length === 0) {
33
35
  throwValidation(
@@ -9,6 +9,7 @@
9
9
  import { mutate } from '../../../state/index.ts';
10
10
  import type { Blocker } from '../../../state/types.ts';
11
11
  import {
12
+ assertInputWithinLimits,
12
13
  emitStateMutation,
13
14
  errorResponse,
14
15
  okResponse,
@@ -28,6 +29,7 @@ export interface ResolveBlockerInput {
28
29
 
29
30
  export async function handle(input: unknown): Promise<ToolResponse> {
30
31
  try {
32
+ assertInputWithinLimits(input);
31
33
  const typed = (input ?? {}) as ResolveBlockerInput;
32
34
  const hasIndex = typeof typed.index === 'number';
33
35
  const hasText = typeof typed.text === 'string' && typed.text.length > 0;
@@ -7,6 +7,7 @@
7
7
 
8
8
  import { mutate } from '../../../state/index.ts';
9
9
  import {
10
+ assertInputWithinLimits,
10
11
  emitStateMutation,
11
12
  errorResponse,
12
13
  okResponse,
@@ -31,6 +32,7 @@ const STATUSES = new Set([
31
32
 
32
33
  export async function handle(input: unknown): Promise<ToolResponse> {
33
34
  try {
35
+ assertInputWithinLimits(input);
34
36
  const typed = (input ?? {}) as SetStatusInput;
35
37
  if (typeof typed.status !== 'string' || !STATUSES.has(typed.status)) {
36
38
  throwValidation(