@hegemonart/get-design-done 1.33.0 → 1.33.6
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 +49 -0
- package/README.md +4 -0
- package/SKILL.md +1 -0
- package/agents/design-authority-watcher.md +4 -0
- package/connections/connections.md +2 -0
- package/connections/openrouter.md +86 -0
- package/hooks/budget-enforcer.ts +103 -0
- package/package.json +5 -2
- package/reference/gdd-runtime-audit.md +111 -0
- package/reference/gdd-threat-model.md +399 -0
- package/reference/openrouter-tier-mapping.md +98 -0
- package/reference/prices.openrouter.md +26 -0
- package/reference/registry.json +28 -0
- package/scripts/lib/authority-watcher/index.cjs +147 -0
- package/scripts/lib/budget-enforcer.cjs +16 -0
- package/scripts/lib/openrouter/catalog-fetcher.cjs +326 -0
- package/scripts/lib/peer-cli/acp-client.cjs +9 -1
- package/scripts/lib/peer-cli/asp-client.cjs +10 -1
- package/scripts/lib/peer-cli/sanitize-env.cjs +198 -0
- package/scripts/lib/redact.cjs +20 -1
- package/scripts/lib/tier-resolver-openrouter.cjs +343 -0
- package/scripts/lib/transports/ws.cjs +67 -3
- package/sdk/event-stream/types.ts +24 -2
- package/sdk/mcp/gdd-state/schemas/add_blocker.schema.json +2 -0
- package/sdk/mcp/gdd-state/schemas/add_decision.schema.json +1 -0
- package/sdk/mcp/gdd-state/schemas/add_must_have.schema.json +1 -0
- package/sdk/mcp/gdd-state/schemas/checkpoint.schema.json +1 -0
- package/sdk/mcp/gdd-state/schemas/frontmatter_update.schema.json +1 -1
- package/sdk/mcp/gdd-state/schemas/get.schema.json +2 -1
- package/sdk/mcp/gdd-state/schemas/probe_connections.schema.json +2 -0
- package/sdk/mcp/gdd-state/schemas/resolve_blocker.schema.json +1 -0
- package/sdk/mcp/gdd-state/server.js +137 -48
- package/sdk/mcp/gdd-state/tools/add_blocker.ts +2 -0
- package/sdk/mcp/gdd-state/tools/add_decision.ts +2 -0
- package/sdk/mcp/gdd-state/tools/add_must_have.ts +2 -0
- package/sdk/mcp/gdd-state/tools/checkpoint.ts +2 -0
- package/sdk/mcp/gdd-state/tools/frontmatter_update.ts +2 -0
- package/sdk/mcp/gdd-state/tools/get.ts +2 -0
- package/sdk/mcp/gdd-state/tools/probe_connections.ts +2 -0
- package/sdk/mcp/gdd-state/tools/resolve_blocker.ts +2 -0
- package/sdk/mcp/gdd-state/tools/set_status.ts +2 -0
- package/sdk/mcp/gdd-state/tools/shared.ts +117 -7
- package/sdk/mcp/gdd-state/tools/transition_stage.ts +2 -0
- package/sdk/mcp/gdd-state/tools/update_progress.ts +2 -0
- package/skills/openrouter-status/SKILL.md +86 -0
|
@@ -146,10 +146,32 @@ export type ParallelismVerdictEvent = BaseEvent & {
|
|
|
146
146
|
payload: { task_ids: string[]; verdict: 'parallel' | 'sequential'; reason: string };
|
|
147
147
|
};
|
|
148
148
|
|
|
149
|
-
/**
|
|
149
|
+
/**
|
|
150
|
+
* Phase 10.1 cost-telemetry event-stream sink.
|
|
151
|
+
*
|
|
152
|
+
* Phase 33.6 / Plan 33.6-03 (SC#6) extension — additive/back-compat: the
|
|
153
|
+
* payload gains an OPTIONAL `provider?: string`, set to `'openrouter'` when the
|
|
154
|
+
* model for this cost row was resolved via the OpenRouter tier-resolver adapter
|
|
155
|
+
* (`scripts/lib/tier-resolver-openrouter.cjs`). Absent on every pre-33.6 event
|
|
156
|
+
* (and on native-resolution rows) — exactly the same additive discipline as the
|
|
157
|
+
* Phase-27 `runtime_role`/`peer_id` extension documented above. The cost-row
|
|
158
|
+
* emit site that threads it is
|
|
159
|
+
* `scripts/lib/budget-enforcer.cjs#buildCostEventPayload`.
|
|
160
|
+
*/
|
|
150
161
|
export type CostUpdateEvent = BaseEvent & {
|
|
151
162
|
type: 'cost.update';
|
|
152
|
-
payload: {
|
|
163
|
+
payload: {
|
|
164
|
+
agent: string;
|
|
165
|
+
tier: string;
|
|
166
|
+
usd: number;
|
|
167
|
+
tokens_in: number;
|
|
168
|
+
tokens_out: number;
|
|
169
|
+
/**
|
|
170
|
+
* Phase 33.6 SC#6. Set to `'openrouter'` when the model was resolved via the
|
|
171
|
+
* OpenRouter adapter; omitted otherwise (native-resolution + pre-33.6 rows).
|
|
172
|
+
*/
|
|
173
|
+
provider?: string;
|
|
174
|
+
};
|
|
153
175
|
};
|
|
154
176
|
|
|
155
177
|
/** Rate-guard / backoff stream (Plan 20-10, 20-11). */
|
|
@@ -15,10 +15,12 @@
|
|
|
15
15
|
"text": {
|
|
16
16
|
"type": "string",
|
|
17
17
|
"minLength": 1,
|
|
18
|
+
"maxLength": 8192,
|
|
18
19
|
"description": "Human-readable blocker description."
|
|
19
20
|
},
|
|
20
21
|
"stage": {
|
|
21
22
|
"type": "string",
|
|
23
|
+
"maxLength": 64,
|
|
22
24
|
"description": "Optional. Defaults to <position>.stage."
|
|
23
25
|
},
|
|
24
26
|
"date": {
|
|
@@ -13,7 +13,8 @@
|
|
|
13
13
|
"properties": {
|
|
14
14
|
"fields": {
|
|
15
15
|
"type": "array",
|
|
16
|
-
"
|
|
16
|
+
"maxItems": 64,
|
|
17
|
+
"items": { "type": "string", "minLength": 1, "maxLength": 256 },
|
|
17
18
|
"description": "Optional projection. When present, limit data.state to these top-level keys. Unknown keys are ignored (no error)."
|
|
18
19
|
}
|
|
19
20
|
}
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
"probe_results": {
|
|
16
16
|
"type": "array",
|
|
17
17
|
"minItems": 1,
|
|
18
|
+
"maxItems": 256,
|
|
18
19
|
"items": {
|
|
19
20
|
"type": "object",
|
|
20
21
|
"additionalProperties": false,
|
|
@@ -23,6 +24,7 @@
|
|
|
23
24
|
"name": {
|
|
24
25
|
"type": "string",
|
|
25
26
|
"minLength": 1,
|
|
27
|
+
"maxLength": 256,
|
|
26
28
|
"description": "Connection name (e.g. \"figma\", \"refero\")."
|
|
27
29
|
},
|
|
28
30
|
"status": {
|
|
@@ -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
|
|
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(
|
|
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 = `${
|
|
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(
|
|
302
|
+
function readLockSafe(path2) {
|
|
293
303
|
try {
|
|
294
|
-
return (0, import_node_fs.readFileSync)(
|
|
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(
|
|
1721
|
-
const raw = (0, import_node_fs2.readFileSync)(
|
|
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(
|
|
1725
|
-
const release = await acquire(
|
|
1726
|
-
const tmpPath = `${
|
|
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)(
|
|
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,
|
|
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,
|
|
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(
|
|
1762
|
-
const beforeMutate = await read(
|
|
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(
|
|
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
|
|
2005
|
-
|
|
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
|
|
2067
|
-
const state = await read(
|
|
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
|
|
2195
|
+
const path2 = resolveStatePath();
|
|
2116
2196
|
const diff = {};
|
|
2117
|
-
const after = await mutate(
|
|
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
|
|
2157
|
-
const before = await read(
|
|
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(
|
|
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
|
|
2293
|
+
const path2 = resolveStatePath();
|
|
2212
2294
|
let appended = null;
|
|
2213
2295
|
let countAfter = 0;
|
|
2214
|
-
const after = await mutate(
|
|
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
|
|
2341
|
+
const path2 = resolveStatePath();
|
|
2259
2342
|
let removed = null;
|
|
2260
2343
|
let countAfter = 0;
|
|
2261
|
-
const after = await mutate(
|
|
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
|
|
2419
|
+
const path2 = resolveStatePath();
|
|
2336
2420
|
let appended = null;
|
|
2337
2421
|
let countAfter = 0;
|
|
2338
|
-
const after = await mutate(
|
|
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
|
|
2483
|
+
const path2 = resolveStatePath();
|
|
2399
2484
|
let written = null;
|
|
2400
2485
|
let action = "insert";
|
|
2401
2486
|
let countAfter = 0;
|
|
2402
|
-
const after = await mutate(
|
|
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
|
|
2544
|
+
const path2 = resolveStatePath();
|
|
2459
2545
|
const diff = {};
|
|
2460
|
-
const after = await mutate(
|
|
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
|
|
2574
|
+
const path2 = resolveStatePath();
|
|
2488
2575
|
const nowIso = (/* @__PURE__ */ new Date()).toISOString();
|
|
2489
2576
|
let timestampKey = "";
|
|
2490
|
-
const after = await mutate(
|
|
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
|
|
2628
|
+
const path2 = resolveStatePath();
|
|
2541
2629
|
const updated = [];
|
|
2542
2630
|
const diff = {};
|
|
2543
|
-
const after = await mutate(
|
|
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
|
|
2695
|
+
const path2 = resolveStatePath();
|
|
2607
2696
|
const diff = {};
|
|
2608
|
-
const after = await mutate(
|
|
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,
|
|
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,
|
|
2648
|
-
if ((0, import_node_fs4.existsSync)((0,
|
|
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,
|
|
2653
|
-
if ((0, import_node_fs4.existsSync)((0,
|
|
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,
|
|
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);
|