loom-spec 0.1.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/LICENSE +21 -0
- package/README.md +181 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +96 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/init.d.ts +5 -0
- package/dist/cli/init.js +69 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/cli/mcp.d.ts +4 -0
- package/dist/cli/mcp.js +17 -0
- package/dist/cli/mcp.js.map +1 -0
- package/dist/cli/validate.d.ts +5 -0
- package/dist/cli/validate.js +77 -0
- package/dist/cli/validate.js.map +1 -0
- package/dist/cli/view.d.ts +6 -0
- package/dist/cli/view.js +37 -0
- package/dist/cli/view.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/server.d.ts +4 -0
- package/dist/mcp/server.js +293 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/server/app.d.ts +9 -0
- package/dist/server/app.js +135 -0
- package/dist/server/app.js.map +1 -0
- package/dist/server/drift.d.ts +29 -0
- package/dist/server/drift.js +128 -0
- package/dist/server/drift.js.map +1 -0
- package/dist/server/fileOps.d.ts +13 -0
- package/dist/server/fileOps.js +56 -0
- package/dist/server/fileOps.js.map +1 -0
- package/dist/server/findLoomRoot.d.ts +9 -0
- package/dist/server/findLoomRoot.js +28 -0
- package/dist/server/findLoomRoot.js.map +1 -0
- package/dist/server/watch.d.ts +29 -0
- package/dist/server/watch.js +83 -0
- package/dist/server/watch.js.map +1 -0
- package/dist/types/diagram.d.ts +99 -0
- package/dist/types/diagram.js +7 -0
- package/dist/types/diagram.js.map +1 -0
- package/dist/types/node-types.d.ts +55 -0
- package/dist/types/node-types.js +7 -0
- package/dist/types/node-types.js.map +1 -0
- package/dist/validate.d.ts +11 -0
- package/dist/validate.js +47 -0
- package/dist/validate.js.map +1 -0
- package/dist/view/assets/index-Cst6HUW5.css +1 -0
- package/dist/view/assets/index-jlp2cU4j.js +205 -0
- package/dist/view/index.html +24 -0
- package/package.json +83 -0
- package/schema/diagram.schema.json +173 -0
- package/schema/node-types.schema.json +116 -0
- package/templates/.claude/skills/loom-spec/SKILL.md +278 -0
- package/templates/.loom/README.md +25 -0
- package/templates/.loom/diagrams/overview.flow.json +8 -0
- package/templates/.loom/node-types.json +56 -0
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import { listDiagrams, readDiagram, writeDiagram, readNodeTypes, } from "../server/fileOps.js";
|
|
5
|
+
import { validateDiagram } from "../validate.js";
|
|
6
|
+
import { runDriftCheck } from "../server/drift.js";
|
|
7
|
+
const STATUSES = ["planned", "implemented", "stale", "deprecated"];
|
|
8
|
+
const EDGE_KINDS = [
|
|
9
|
+
"request",
|
|
10
|
+
"event",
|
|
11
|
+
"data-read",
|
|
12
|
+
"data-write",
|
|
13
|
+
"signal",
|
|
14
|
+
"dependency",
|
|
15
|
+
"control",
|
|
16
|
+
];
|
|
17
|
+
const codeRefSchema = z.object({
|
|
18
|
+
path: z.string(),
|
|
19
|
+
symbol: z.string().optional(),
|
|
20
|
+
lines: z.string().optional(),
|
|
21
|
+
});
|
|
22
|
+
function uniqueNodeId(d, prefix) {
|
|
23
|
+
const ids = new Set(d.nodes.map((n) => n.id));
|
|
24
|
+
let i = 1;
|
|
25
|
+
while (ids.has(`${prefix}-${i}`))
|
|
26
|
+
i++;
|
|
27
|
+
return `${prefix}-${i}`;
|
|
28
|
+
}
|
|
29
|
+
function uniqueEdgeId(d) {
|
|
30
|
+
const ids = new Set(d.edges.map((e) => e.id));
|
|
31
|
+
let i = d.edges.length + 1;
|
|
32
|
+
while (ids.has(`e${i}`))
|
|
33
|
+
i++;
|
|
34
|
+
return `e${i}`;
|
|
35
|
+
}
|
|
36
|
+
function jsonText(obj) {
|
|
37
|
+
return { content: [{ type: "text", text: JSON.stringify(obj, null, 2) }] };
|
|
38
|
+
}
|
|
39
|
+
function errorText(msg, details) {
|
|
40
|
+
const text = details && details.length ? `${msg}\n - ${details.join("\n - ")}` : msg;
|
|
41
|
+
return { content: [{ type: "text", text }], isError: true };
|
|
42
|
+
}
|
|
43
|
+
async function readDiagramOrError(loomPath, id) {
|
|
44
|
+
try {
|
|
45
|
+
return { ok: true, diagram: await readDiagram(loomPath, id) };
|
|
46
|
+
}
|
|
47
|
+
catch (e) {
|
|
48
|
+
const code = e.code;
|
|
49
|
+
if (code === "ENOENT") {
|
|
50
|
+
return { ok: false, error: errorText(`diagram '${id}' not found`) };
|
|
51
|
+
}
|
|
52
|
+
return { ok: false, error: errorText(`read failed: ${e.message}`) };
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
async function persist(loomPath, id, d) {
|
|
56
|
+
const result = await validateDiagram(d);
|
|
57
|
+
if (!result.ok) {
|
|
58
|
+
return errorText("Validation failed:", result.errors);
|
|
59
|
+
}
|
|
60
|
+
await writeDiagram(loomPath, id, d);
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
export function createMcpServer(loomRoot) {
|
|
64
|
+
const server = new McpServer({ name: "loom-spec", version: "0.0.1" }, { instructions: `Use these tools to maintain the architecture spec in ${loomRoot.loomPath}. Prefer them over editing the JSON files directly — they validate against the schema before writing.` });
|
|
65
|
+
server.registerTool("loom_list_diagrams", {
|
|
66
|
+
title: "List diagrams",
|
|
67
|
+
description: "List all diagrams in the spec with title and node/edge counts.",
|
|
68
|
+
inputSchema: {},
|
|
69
|
+
}, async () => {
|
|
70
|
+
const summaries = await listDiagrams(loomRoot.loomPath);
|
|
71
|
+
return jsonText(summaries);
|
|
72
|
+
});
|
|
73
|
+
server.registerTool("loom_read_diagram", {
|
|
74
|
+
title: "Read diagram",
|
|
75
|
+
description: "Read a specific diagram's full JSON by id.",
|
|
76
|
+
inputSchema: { id: z.string().describe("Diagram id (e.g. 'overview')") },
|
|
77
|
+
}, async ({ id }) => {
|
|
78
|
+
const r = await readDiagramOrError(loomRoot.loomPath, id);
|
|
79
|
+
if (!r.ok)
|
|
80
|
+
return r.error;
|
|
81
|
+
return jsonText(r.diagram);
|
|
82
|
+
});
|
|
83
|
+
server.registerTool("loom_read_node_types", {
|
|
84
|
+
title: "Read node types",
|
|
85
|
+
description: "Read the project's node-types.json (available node-type vocabulary).",
|
|
86
|
+
inputSchema: {},
|
|
87
|
+
}, async () => {
|
|
88
|
+
const nt = await readNodeTypes(loomRoot.loomPath);
|
|
89
|
+
return jsonText(nt);
|
|
90
|
+
});
|
|
91
|
+
server.registerTool("loom_add_node", {
|
|
92
|
+
title: "Add node",
|
|
93
|
+
description: "Append a new node to a diagram. Returns the auto-generated id. The new node defaults to status='planned'. Position defaults to {200, 200}; the user can re-arrange in the UI.",
|
|
94
|
+
inputSchema: {
|
|
95
|
+
diagram: z.string().describe("Diagram id to add the node to"),
|
|
96
|
+
type: z.string().describe("Node type key (must exist in node-types.json)"),
|
|
97
|
+
label: z.string().describe("Display label"),
|
|
98
|
+
description: z.string().optional(),
|
|
99
|
+
position: z.object({ x: z.number(), y: z.number() }).optional(),
|
|
100
|
+
status: z.enum(STATUSES).default("planned"),
|
|
101
|
+
code_refs: z.array(codeRefSchema).optional(),
|
|
102
|
+
properties: z.record(z.string(), z.unknown()).optional(),
|
|
103
|
+
tags: z.array(z.string()).optional(),
|
|
104
|
+
id: z
|
|
105
|
+
.string()
|
|
106
|
+
.optional()
|
|
107
|
+
.describe("Optional explicit id (lowercase kebab). If omitted, generated as <type>-<n>."),
|
|
108
|
+
},
|
|
109
|
+
}, async ({ diagram, type, label, description, position, status, code_refs, properties, tags, id }) => {
|
|
110
|
+
const r = await readDiagramOrError(loomRoot.loomPath, diagram);
|
|
111
|
+
if (!r.ok)
|
|
112
|
+
return r.error;
|
|
113
|
+
const d = r.diagram;
|
|
114
|
+
const newId = id ?? uniqueNodeId(d, type);
|
|
115
|
+
if (d.nodes.some((n) => n.id === newId)) {
|
|
116
|
+
return errorText(`node with id '${newId}' already exists`);
|
|
117
|
+
}
|
|
118
|
+
const node = {
|
|
119
|
+
id: newId,
|
|
120
|
+
type,
|
|
121
|
+
label,
|
|
122
|
+
description,
|
|
123
|
+
position: position ?? { x: 200, y: 200 },
|
|
124
|
+
status,
|
|
125
|
+
code_refs: code_refs ?? [],
|
|
126
|
+
properties: properties ?? {},
|
|
127
|
+
tags,
|
|
128
|
+
};
|
|
129
|
+
d.nodes.push(node);
|
|
130
|
+
const err = await persist(loomRoot.loomPath, diagram, d);
|
|
131
|
+
if (err)
|
|
132
|
+
return err;
|
|
133
|
+
return jsonText({ ok: true, id: newId });
|
|
134
|
+
});
|
|
135
|
+
server.registerTool("loom_update_node", {
|
|
136
|
+
title: "Update node",
|
|
137
|
+
description: "Patch fields on an existing node. Only the fields you pass are changed. Use 'loom_mark_stale' as a shortcut for setting status='stale'.",
|
|
138
|
+
inputSchema: {
|
|
139
|
+
diagram: z.string(),
|
|
140
|
+
id: z.string().describe("Node id"),
|
|
141
|
+
patch: z
|
|
142
|
+
.object({
|
|
143
|
+
label: z.string().optional(),
|
|
144
|
+
description: z.string().nullable().optional(),
|
|
145
|
+
status: z.enum(STATUSES).optional(),
|
|
146
|
+
position: z.object({ x: z.number(), y: z.number() }).optional(),
|
|
147
|
+
code_refs: z.array(codeRefSchema).optional(),
|
|
148
|
+
properties: z.record(z.string(), z.unknown()).optional(),
|
|
149
|
+
tags: z.array(z.string()).optional(),
|
|
150
|
+
drill_down: z.string().nullable().optional(),
|
|
151
|
+
})
|
|
152
|
+
.describe("Fields to merge"),
|
|
153
|
+
},
|
|
154
|
+
}, async ({ diagram, id, patch }) => {
|
|
155
|
+
const r = await readDiagramOrError(loomRoot.loomPath, diagram);
|
|
156
|
+
if (!r.ok)
|
|
157
|
+
return r.error;
|
|
158
|
+
const d = r.diagram;
|
|
159
|
+
const idx = d.nodes.findIndex((n) => n.id === id);
|
|
160
|
+
if (idx < 0)
|
|
161
|
+
return errorText(`node '${id}' not found`);
|
|
162
|
+
const cleaned = {};
|
|
163
|
+
for (const [k, v] of Object.entries(patch)) {
|
|
164
|
+
if (v === null)
|
|
165
|
+
continue; // explicit null = clear; handled below
|
|
166
|
+
if (v !== undefined)
|
|
167
|
+
cleaned[k] = v;
|
|
168
|
+
}
|
|
169
|
+
// Honor explicit nulls as field clears
|
|
170
|
+
if (patch.description === null)
|
|
171
|
+
cleaned["description"] = undefined;
|
|
172
|
+
if (patch.drill_down === null)
|
|
173
|
+
cleaned["drill_down"] = undefined;
|
|
174
|
+
d.nodes[idx] = { ...d.nodes[idx], ...cleaned };
|
|
175
|
+
const err = await persist(loomRoot.loomPath, diagram, d);
|
|
176
|
+
if (err)
|
|
177
|
+
return err;
|
|
178
|
+
return jsonText({ ok: true });
|
|
179
|
+
});
|
|
180
|
+
server.registerTool("loom_mark_stale", {
|
|
181
|
+
title: "Mark node stale",
|
|
182
|
+
description: "Shortcut for setting a node's status to 'stale' (use when the underlying code no longer exists or has been replaced; humans review staleness rather than auto-delete).",
|
|
183
|
+
inputSchema: {
|
|
184
|
+
diagram: z.string(),
|
|
185
|
+
id: z.string(),
|
|
186
|
+
},
|
|
187
|
+
}, async ({ diagram, id }) => {
|
|
188
|
+
const r = await readDiagramOrError(loomRoot.loomPath, diagram);
|
|
189
|
+
if (!r.ok)
|
|
190
|
+
return r.error;
|
|
191
|
+
const d = r.diagram;
|
|
192
|
+
const idx = d.nodes.findIndex((n) => n.id === id);
|
|
193
|
+
if (idx < 0)
|
|
194
|
+
return errorText(`node '${id}' not found`);
|
|
195
|
+
d.nodes[idx] = { ...d.nodes[idx], status: "stale" };
|
|
196
|
+
const err = await persist(loomRoot.loomPath, diagram, d);
|
|
197
|
+
if (err)
|
|
198
|
+
return err;
|
|
199
|
+
return jsonText({ ok: true });
|
|
200
|
+
});
|
|
201
|
+
server.registerTool("loom_delete_node", {
|
|
202
|
+
title: "Delete node",
|
|
203
|
+
description: "Hard-delete a node and all edges that touch it. Prefer 'loom_mark_stale' for nodes whose code went away — deletion loses architectural history.",
|
|
204
|
+
inputSchema: {
|
|
205
|
+
diagram: z.string(),
|
|
206
|
+
id: z.string(),
|
|
207
|
+
},
|
|
208
|
+
}, async ({ diagram, id }) => {
|
|
209
|
+
const r = await readDiagramOrError(loomRoot.loomPath, diagram);
|
|
210
|
+
if (!r.ok)
|
|
211
|
+
return r.error;
|
|
212
|
+
const d = r.diagram;
|
|
213
|
+
const before = d.nodes.length;
|
|
214
|
+
d.nodes = d.nodes.filter((n) => n.id !== id);
|
|
215
|
+
if (d.nodes.length === before)
|
|
216
|
+
return errorText(`node '${id}' not found`);
|
|
217
|
+
d.edges = d.edges.filter((e) => {
|
|
218
|
+
const f = e.from.split(":")[0];
|
|
219
|
+
const t = e.to.split(":")[0];
|
|
220
|
+
return f !== id && t !== id;
|
|
221
|
+
});
|
|
222
|
+
const err = await persist(loomRoot.loomPath, diagram, d);
|
|
223
|
+
if (err)
|
|
224
|
+
return err;
|
|
225
|
+
return jsonText({ ok: true });
|
|
226
|
+
});
|
|
227
|
+
server.registerTool("loom_add_edge", {
|
|
228
|
+
title: "Add edge",
|
|
229
|
+
description: "Connect two nodes. Use 'node-id:port-name' syntax for typed ports.",
|
|
230
|
+
inputSchema: {
|
|
231
|
+
diagram: z.string(),
|
|
232
|
+
from: z.string().describe("Source node id (or 'node-id:port-name')"),
|
|
233
|
+
to: z.string().describe("Target node id (or 'node-id:port-name')"),
|
|
234
|
+
kind: z.enum(EDGE_KINDS),
|
|
235
|
+
label: z.string().optional(),
|
|
236
|
+
description: z.string().optional(),
|
|
237
|
+
},
|
|
238
|
+
}, async ({ diagram, from, to, kind, label, description }) => {
|
|
239
|
+
const r = await readDiagramOrError(loomRoot.loomPath, diagram);
|
|
240
|
+
if (!r.ok)
|
|
241
|
+
return r.error;
|
|
242
|
+
const d = r.diagram;
|
|
243
|
+
const edge = {
|
|
244
|
+
id: uniqueEdgeId(d),
|
|
245
|
+
from,
|
|
246
|
+
to,
|
|
247
|
+
kind,
|
|
248
|
+
label,
|
|
249
|
+
description,
|
|
250
|
+
};
|
|
251
|
+
d.edges.push(edge);
|
|
252
|
+
const err = await persist(loomRoot.loomPath, diagram, d);
|
|
253
|
+
if (err)
|
|
254
|
+
return err;
|
|
255
|
+
return jsonText({ ok: true, id: edge.id });
|
|
256
|
+
});
|
|
257
|
+
server.registerTool("loom_delete_edge", {
|
|
258
|
+
title: "Delete edge",
|
|
259
|
+
description: "Remove an edge by id.",
|
|
260
|
+
inputSchema: {
|
|
261
|
+
diagram: z.string(),
|
|
262
|
+
id: z.string(),
|
|
263
|
+
},
|
|
264
|
+
}, async ({ diagram, id }) => {
|
|
265
|
+
const r = await readDiagramOrError(loomRoot.loomPath, diagram);
|
|
266
|
+
if (!r.ok)
|
|
267
|
+
return r.error;
|
|
268
|
+
const d = r.diagram;
|
|
269
|
+
const before = d.edges.length;
|
|
270
|
+
d.edges = d.edges.filter((e) => e.id !== id);
|
|
271
|
+
if (d.edges.length === before)
|
|
272
|
+
return errorText(`edge '${id}' not found`);
|
|
273
|
+
const err = await persist(loomRoot.loomPath, diagram, d);
|
|
274
|
+
if (err)
|
|
275
|
+
return err;
|
|
276
|
+
return jsonText({ ok: true });
|
|
277
|
+
});
|
|
278
|
+
server.registerTool("loom_validate", {
|
|
279
|
+
title: "Validate spec",
|
|
280
|
+
description: "Run the full drift + schema check on every diagram. Reports schema errors, missing code-ref files, and missing symbols.",
|
|
281
|
+
inputSchema: {},
|
|
282
|
+
}, async () => {
|
|
283
|
+
const report = await runDriftCheck(loomRoot.rootPath, loomRoot.loomPath);
|
|
284
|
+
return jsonText(report);
|
|
285
|
+
});
|
|
286
|
+
return server;
|
|
287
|
+
}
|
|
288
|
+
export async function startMcpServer(loomRoot) {
|
|
289
|
+
const server = createMcpServer(loomRoot);
|
|
290
|
+
const transport = new StdioServerTransport();
|
|
291
|
+
await server.connect(transport);
|
|
292
|
+
}
|
|
293
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Hono } from "hono";
|
|
2
|
+
import type { LoomRoot } from "./findLoomRoot.js";
|
|
3
|
+
import type { LoomWatcher } from "./watch.js";
|
|
4
|
+
export interface AppOptions {
|
|
5
|
+
loomRoot: LoomRoot;
|
|
6
|
+
watcher: LoomWatcher;
|
|
7
|
+
serveSpaFrom?: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function createApp({ loomRoot, watcher, serveSpaFrom }: AppOptions): Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { Hono } from "hono";
|
|
2
|
+
import { cors } from "hono/cors";
|
|
3
|
+
import { streamSSE } from "hono/streaming";
|
|
4
|
+
import { serveStatic } from "@hono/node-server/serve-static";
|
|
5
|
+
import { listDiagrams, readDiagram, writeDiagram, readNodeTypes, } from "./fileOps.js";
|
|
6
|
+
import { validateDiagram, validateNodeTypes } from "../validate.js";
|
|
7
|
+
export function createApp({ loomRoot, watcher, serveSpaFrom }) {
|
|
8
|
+
const app = new Hono();
|
|
9
|
+
app.use("/api/*", cors());
|
|
10
|
+
app.get("/api/root", (c) => c.json({ rootPath: loomRoot.rootPath, loomPath: loomRoot.loomPath }));
|
|
11
|
+
app.get("/api/node-types", async (c) => {
|
|
12
|
+
try {
|
|
13
|
+
const data = await readNodeTypes(loomRoot.loomPath);
|
|
14
|
+
return c.json(data);
|
|
15
|
+
}
|
|
16
|
+
catch (e) {
|
|
17
|
+
return c.json({ error: e.message }, 500);
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
app.get("/api/diagrams", async (c) => {
|
|
21
|
+
try {
|
|
22
|
+
const summaries = await listDiagrams(loomRoot.loomPath);
|
|
23
|
+
return c.json(summaries);
|
|
24
|
+
}
|
|
25
|
+
catch (e) {
|
|
26
|
+
return c.json({ error: e.message }, 500);
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
app.get("/api/diagrams/:id", async (c) => {
|
|
30
|
+
const id = c.req.param("id");
|
|
31
|
+
try {
|
|
32
|
+
const data = await readDiagram(loomRoot.loomPath, id);
|
|
33
|
+
return c.json(data);
|
|
34
|
+
}
|
|
35
|
+
catch (e) {
|
|
36
|
+
const code = e.code;
|
|
37
|
+
if (code === "ENOENT")
|
|
38
|
+
return c.json({ error: "not found" }, 404);
|
|
39
|
+
return c.json({ error: e.message }, 500);
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
app.put("/api/diagrams/:id", async (c) => {
|
|
43
|
+
const id = c.req.param("id");
|
|
44
|
+
let body;
|
|
45
|
+
try {
|
|
46
|
+
body = await c.req.json();
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return c.json({ error: "invalid JSON" }, 400);
|
|
50
|
+
}
|
|
51
|
+
const result = await validateDiagram(body);
|
|
52
|
+
if (!result.ok) {
|
|
53
|
+
return c.json({ error: "validation failed", details: result.errors }, 422);
|
|
54
|
+
}
|
|
55
|
+
const diagram = body;
|
|
56
|
+
if (diagram.id !== id) {
|
|
57
|
+
return c.json({ error: `body id "${diagram.id}" does not match URL id "${id}"` }, 400);
|
|
58
|
+
}
|
|
59
|
+
try {
|
|
60
|
+
await writeDiagram(loomRoot.loomPath, id, body, (path) => watcher.markSelfWrite(path));
|
|
61
|
+
return c.json({ ok: true });
|
|
62
|
+
}
|
|
63
|
+
catch (e) {
|
|
64
|
+
return c.json({ error: e.message }, 500);
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
app.put("/api/node-types", async (c) => {
|
|
68
|
+
let body;
|
|
69
|
+
try {
|
|
70
|
+
body = await c.req.json();
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
return c.json({ error: "invalid JSON" }, 400);
|
|
74
|
+
}
|
|
75
|
+
const result = await validateNodeTypes(body);
|
|
76
|
+
if (!result.ok) {
|
|
77
|
+
return c.json({ error: "validation failed", details: result.errors }, 422);
|
|
78
|
+
}
|
|
79
|
+
return c.json({ error: "node-types editing is not enabled yet" }, 501);
|
|
80
|
+
});
|
|
81
|
+
// Server-Sent Events: live updates when files change externally
|
|
82
|
+
app.get("/api/events", (c) => streamSSE(c, async (stream) => {
|
|
83
|
+
let notify = null;
|
|
84
|
+
const queue = [];
|
|
85
|
+
const onChange = (event) => {
|
|
86
|
+
queue.push(event);
|
|
87
|
+
notify?.();
|
|
88
|
+
};
|
|
89
|
+
watcher.on("change", onChange);
|
|
90
|
+
// Initial "hello" so the client knows the stream is alive
|
|
91
|
+
await stream.writeSSE({ data: JSON.stringify({ type: "connected" }) });
|
|
92
|
+
// Heartbeat so proxies don't close the stream
|
|
93
|
+
const heartbeat = setInterval(() => {
|
|
94
|
+
stream
|
|
95
|
+
.writeSSE({ data: JSON.stringify({ type: "ping" }) })
|
|
96
|
+
.catch(() => { });
|
|
97
|
+
}, 25000);
|
|
98
|
+
stream.onAbort(() => {
|
|
99
|
+
watcher.off("change", onChange);
|
|
100
|
+
clearInterval(heartbeat);
|
|
101
|
+
notify?.();
|
|
102
|
+
});
|
|
103
|
+
try {
|
|
104
|
+
while (!stream.aborted) {
|
|
105
|
+
if (queue.length > 0) {
|
|
106
|
+
const event = queue.shift();
|
|
107
|
+
await stream.writeSSE({
|
|
108
|
+
event: "change",
|
|
109
|
+
data: JSON.stringify(event),
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
await new Promise((resolve) => {
|
|
114
|
+
notify = () => {
|
|
115
|
+
notify = null;
|
|
116
|
+
resolve();
|
|
117
|
+
};
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
finally {
|
|
123
|
+
watcher.off("change", onChange);
|
|
124
|
+
clearInterval(heartbeat);
|
|
125
|
+
}
|
|
126
|
+
}));
|
|
127
|
+
if (serveSpaFrom) {
|
|
128
|
+
app.use("/*", serveStatic({
|
|
129
|
+
root: serveSpaFrom,
|
|
130
|
+
rewriteRequestPath: (path) => (path === "/" ? "/index.html" : path),
|
|
131
|
+
}));
|
|
132
|
+
}
|
|
133
|
+
return app;
|
|
134
|
+
}
|
|
135
|
+
//# sourceMappingURL=app.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app.js","sourceRoot":"","sources":["../../src/server/app.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { CodeRef } from "../types/diagram.js";
|
|
2
|
+
export interface DriftFinding {
|
|
3
|
+
diagramId: string;
|
|
4
|
+
nodeId: string;
|
|
5
|
+
refIndex: number;
|
|
6
|
+
ref: CodeRef;
|
|
7
|
+
issue: "missing-file" | "missing-symbol" | "lines-out-of-range" | "invalid-lines";
|
|
8
|
+
detail?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface SchemaFinding {
|
|
11
|
+
diagramId: string;
|
|
12
|
+
errors: string[];
|
|
13
|
+
}
|
|
14
|
+
export interface DiagramReport {
|
|
15
|
+
diagramId: string;
|
|
16
|
+
title: string;
|
|
17
|
+
nodeCount: number;
|
|
18
|
+
edgeCount: number;
|
|
19
|
+
refsChecked: number;
|
|
20
|
+
staleNodes: number;
|
|
21
|
+
drift: DriftFinding[];
|
|
22
|
+
schemaErrors: string[];
|
|
23
|
+
}
|
|
24
|
+
export interface DriftReport {
|
|
25
|
+
perDiagram: DiagramReport[];
|
|
26
|
+
totalDrift: number;
|
|
27
|
+
totalSchemaErrors: number;
|
|
28
|
+
}
|
|
29
|
+
export declare function runDriftCheck(rootPath: string, loomPath: string): Promise<DriftReport>;
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { readFile, stat } from "node:fs/promises";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
import { listDiagrams, readDiagram } from "./fileOps.js";
|
|
4
|
+
import { validateDiagram } from "../validate.js";
|
|
5
|
+
const LINES_RE = /^(\d+)(?:-(\d+))?(?:,(\d+)(?:-(\d+))?)*$/;
|
|
6
|
+
function parseLineRange(spec) {
|
|
7
|
+
if (!LINES_RE.test(spec))
|
|
8
|
+
return null;
|
|
9
|
+
const ranges = [];
|
|
10
|
+
for (const part of spec.split(",")) {
|
|
11
|
+
const dash = part.indexOf("-");
|
|
12
|
+
if (dash === -1) {
|
|
13
|
+
const n = Number(part);
|
|
14
|
+
ranges.push([n, n]);
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
ranges.push([Number(part.slice(0, dash)), Number(part.slice(dash + 1))]);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return ranges;
|
|
21
|
+
}
|
|
22
|
+
async function checkRef(rootPath, ref) {
|
|
23
|
+
const abs = resolve(rootPath, ref.path);
|
|
24
|
+
// Path exists?
|
|
25
|
+
let contents;
|
|
26
|
+
try {
|
|
27
|
+
await stat(abs);
|
|
28
|
+
contents = await readFile(abs, "utf8");
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return { issue: "missing-file" };
|
|
32
|
+
}
|
|
33
|
+
// If symbol is set, grep for it.
|
|
34
|
+
if (ref.symbol) {
|
|
35
|
+
// Match as whole word; tolerate def/class/function/const prefixes implicitly
|
|
36
|
+
// by just looking for the identifier surrounded by non-word characters.
|
|
37
|
+
const symRe = new RegExp(`(?:^|[^\\w])${ref.symbol.replace(/[.*+?^${}()|[\\]\\\\]/g, "\\$&")}(?:[^\\w]|$)`, "m");
|
|
38
|
+
if (!symRe.test(contents)) {
|
|
39
|
+
return {
|
|
40
|
+
issue: "missing-symbol",
|
|
41
|
+
detail: `'${ref.symbol}' not found in ${ref.path}`,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
// If lines is set, validate
|
|
46
|
+
if (ref.lines) {
|
|
47
|
+
const ranges = parseLineRange(ref.lines);
|
|
48
|
+
if (!ranges) {
|
|
49
|
+
return { issue: "invalid-lines", detail: `bad format: '${ref.lines}'` };
|
|
50
|
+
}
|
|
51
|
+
const totalLines = contents.split("\n").length;
|
|
52
|
+
for (const [from, to] of ranges) {
|
|
53
|
+
if (from < 1 || to < from || to > totalLines) {
|
|
54
|
+
return {
|
|
55
|
+
issue: "lines-out-of-range",
|
|
56
|
+
detail: `${from}-${to} (file has ${totalLines} lines)`,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
export async function runDriftCheck(rootPath, loomPath) {
|
|
64
|
+
const summaries = await listDiagrams(loomPath);
|
|
65
|
+
const perDiagram = [];
|
|
66
|
+
let totalDrift = 0;
|
|
67
|
+
let totalSchemaErrors = 0;
|
|
68
|
+
for (const s of summaries) {
|
|
69
|
+
let diagram;
|
|
70
|
+
try {
|
|
71
|
+
diagram = await readDiagram(loomPath, s.id);
|
|
72
|
+
}
|
|
73
|
+
catch (e) {
|
|
74
|
+
perDiagram.push({
|
|
75
|
+
diagramId: s.id,
|
|
76
|
+
title: s.title,
|
|
77
|
+
nodeCount: 0,
|
|
78
|
+
edgeCount: 0,
|
|
79
|
+
refsChecked: 0,
|
|
80
|
+
staleNodes: 0,
|
|
81
|
+
drift: [],
|
|
82
|
+
schemaErrors: [`failed to read: ${e.message}`],
|
|
83
|
+
});
|
|
84
|
+
totalSchemaErrors++;
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
const schemaResult = await validateDiagram(diagram);
|
|
88
|
+
const schemaErrors = schemaResult.ok ? [] : schemaResult.errors;
|
|
89
|
+
totalSchemaErrors += schemaErrors.length;
|
|
90
|
+
const drift = [];
|
|
91
|
+
let refsChecked = 0;
|
|
92
|
+
let staleNodes = 0;
|
|
93
|
+
for (const node of diagram.nodes) {
|
|
94
|
+
if (node.status === "stale")
|
|
95
|
+
staleNodes++;
|
|
96
|
+
// Skip drift checking on nodes that are explicitly planned or deprecated;
|
|
97
|
+
// their code may legitimately not exist yet, or have moved on.
|
|
98
|
+
if (node.status === "planned" || node.status === "deprecated")
|
|
99
|
+
continue;
|
|
100
|
+
for (const [i, ref] of (node.code_refs ?? []).entries()) {
|
|
101
|
+
refsChecked++;
|
|
102
|
+
const finding = await checkRef(rootPath, ref);
|
|
103
|
+
if (finding) {
|
|
104
|
+
drift.push({
|
|
105
|
+
diagramId: s.id,
|
|
106
|
+
nodeId: node.id,
|
|
107
|
+
refIndex: i,
|
|
108
|
+
ref,
|
|
109
|
+
...finding,
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
totalDrift += drift.length;
|
|
115
|
+
perDiagram.push({
|
|
116
|
+
diagramId: s.id,
|
|
117
|
+
title: s.title,
|
|
118
|
+
nodeCount: diagram.nodes.length,
|
|
119
|
+
edgeCount: diagram.edges.length,
|
|
120
|
+
refsChecked,
|
|
121
|
+
staleNodes,
|
|
122
|
+
drift,
|
|
123
|
+
schemaErrors,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
return { perDiagram, totalDrift, totalSchemaErrors };
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=drift.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"drift.js","sourceRoot":"","sources":["../../src/server/drift.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { LoomDiagram } from "../types/diagram.js";
|
|
2
|
+
import type { LoomNodeTypes } from "../types/node-types.js";
|
|
3
|
+
export interface DiagramSummary {
|
|
4
|
+
id: string;
|
|
5
|
+
title: string;
|
|
6
|
+
description?: string;
|
|
7
|
+
nodeCount: number;
|
|
8
|
+
edgeCount: number;
|
|
9
|
+
}
|
|
10
|
+
export declare function listDiagrams(loomPath: string): Promise<DiagramSummary[]>;
|
|
11
|
+
export declare function readDiagram(loomPath: string, id: string): Promise<LoomDiagram>;
|
|
12
|
+
export declare function writeDiagram(loomPath: string, id: string, data: LoomDiagram, onWritten?: (path: string) => void): Promise<void>;
|
|
13
|
+
export declare function readNodeTypes(loomPath: string): Promise<LoomNodeTypes>;
|