figjam-context-mcp 0.0.0-stage → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/.env.example +70 -0
  2. package/CHANGELOG.md +58 -0
  3. package/LICENSE +21 -0
  4. package/README.md +150 -2
  5. package/SECURITY.md +26 -0
  6. package/dist/index.d.ts +2 -0
  7. package/dist/index.js +20 -0
  8. package/dist/index.js.map +1 -0
  9. package/dist/lib/boardDiff.d.ts +55 -0
  10. package/dist/lib/boardDiff.js +285 -0
  11. package/dist/lib/boardDiff.js.map +1 -0
  12. package/dist/lib/cache.d.ts +9 -0
  13. package/dist/lib/cache.js +63 -0
  14. package/dist/lib/cache.js.map +1 -0
  15. package/dist/lib/connectorGraph.d.ts +32 -0
  16. package/dist/lib/connectorGraph.js +121 -0
  17. package/dist/lib/connectorGraph.js.map +1 -0
  18. package/dist/lib/docStructureMapper.d.ts +24 -0
  19. package/dist/lib/docStructureMapper.js +121 -0
  20. package/dist/lib/docStructureMapper.js.map +1 -0
  21. package/dist/lib/env.d.ts +2 -0
  22. package/dist/lib/env.js +25 -0
  23. package/dist/lib/env.js.map +1 -0
  24. package/dist/lib/evidence.d.ts +63 -0
  25. package/dist/lib/evidence.js +474 -0
  26. package/dist/lib/evidence.js.map +1 -0
  27. package/dist/lib/figmaApi.d.ts +28 -0
  28. package/dist/lib/figmaApi.js +180 -0
  29. package/dist/lib/figmaApi.js.map +1 -0
  30. package/dist/lib/llmClient.d.ts +65 -0
  31. package/dist/lib/llmClient.js +459 -0
  32. package/dist/lib/llmClient.js.map +1 -0
  33. package/dist/lib/llmOutputBudgets.d.ts +6 -0
  34. package/dist/lib/llmOutputBudgets.js +13 -0
  35. package/dist/lib/llmOutputBudgets.js.map +1 -0
  36. package/dist/lib/llmTimeouts.d.ts +12 -0
  37. package/dist/lib/llmTimeouts.js +22 -0
  38. package/dist/lib/llmTimeouts.js.map +1 -0
  39. package/dist/lib/modelRegistry.d.ts +12 -0
  40. package/dist/lib/modelRegistry.js +91 -0
  41. package/dist/lib/modelRegistry.js.map +1 -0
  42. package/dist/lib/nodeTree.d.ts +9 -0
  43. package/dist/lib/nodeTree.js +174 -0
  44. package/dist/lib/nodeTree.js.map +1 -0
  45. package/dist/lib/persistentCache.d.ts +32 -0
  46. package/dist/lib/persistentCache.js +301 -0
  47. package/dist/lib/persistentCache.js.map +1 -0
  48. package/dist/lib/spatialCluster.d.ts +58 -0
  49. package/dist/lib/spatialCluster.js +340 -0
  50. package/dist/lib/spatialCluster.js.map +1 -0
  51. package/dist/lib/visionInterpreter.d.ts +14 -0
  52. package/dist/lib/visionInterpreter.js +207 -0
  53. package/dist/lib/visionInterpreter.js.map +1 -0
  54. package/dist/schemas/answerFromBoard.d.ts +75 -0
  55. package/dist/schemas/answerFromBoard.js +28 -0
  56. package/dist/schemas/answerFromBoard.js.map +1 -0
  57. package/dist/schemas/common.d.ts +14 -0
  58. package/dist/schemas/common.js +59 -0
  59. package/dist/schemas/common.js.map +1 -0
  60. package/dist/schemas/diagnoseLlmConfig.d.ts +35 -0
  61. package/dist/schemas/diagnoseLlmConfig.js +20 -0
  62. package/dist/schemas/diagnoseLlmConfig.js.map +1 -0
  63. package/dist/schemas/diffBoard.d.ts +118 -0
  64. package/dist/schemas/diffBoard.js +61 -0
  65. package/dist/schemas/diffBoard.js.map +1 -0
  66. package/dist/schemas/getBoardContext.d.ts +255 -0
  67. package/dist/schemas/getBoardContext.js +77 -0
  68. package/dist/schemas/getBoardContext.js.map +1 -0
  69. package/dist/schemas/ingestBoard.d.ts +96 -0
  70. package/dist/schemas/ingestBoard.js +88 -0
  71. package/dist/schemas/ingestBoard.js.map +1 -0
  72. package/dist/server.d.ts +16 -0
  73. package/dist/server.js +131 -0
  74. package/dist/server.js.map +1 -0
  75. package/dist/tools/answerFromBoard.d.ts +9 -0
  76. package/dist/tools/answerFromBoard.js +243 -0
  77. package/dist/tools/answerFromBoard.js.map +1 -0
  78. package/dist/tools/diagnoseLlmConfig.d.ts +4 -0
  79. package/dist/tools/diagnoseLlmConfig.js +169 -0
  80. package/dist/tools/diagnoseLlmConfig.js.map +1 -0
  81. package/dist/tools/diffBoard.d.ts +11 -0
  82. package/dist/tools/diffBoard.js +49 -0
  83. package/dist/tools/diffBoard.js.map +1 -0
  84. package/dist/tools/getBoardContext.d.ts +3 -0
  85. package/dist/tools/getBoardContext.js +157 -0
  86. package/dist/tools/getBoardContext.js.map +1 -0
  87. package/dist/tools/ingestBoard.d.ts +8 -0
  88. package/dist/tools/ingestBoard.js +667 -0
  89. package/dist/tools/ingestBoard.js.map +1 -0
  90. package/dist/types.d.ts +145 -0
  91. package/dist/types.js +6 -0
  92. package/dist/types.js.map +1 -0
  93. package/docs/pipeline.gif +0 -0
  94. package/package.json +73 -4
  95. package/server.json +48 -0
@@ -0,0 +1,285 @@
1
+ import { STRUCTURAL_TYPES } from "./nodeTree.js";
2
+ import { hashClusterNodes } from "./persistentCache.js";
3
+ import { connectorArrow } from "./connectorGraph.js";
4
+ /** Minimum member overlap (relative to the smaller cluster) to treat two clusters as the same. */
5
+ const CLUSTER_MATCH_THRESHOLD = 0.5;
6
+ /** Max entries rendered per section in summaryText. */
7
+ const SUMMARY_SECTION_LIMIT = 8;
8
+ export function diffBoards(baseline, current) {
9
+ const baselineNodes = contentNodesById(baseline);
10
+ const currentNodes = contentNodesById(current);
11
+ const addedNodeIds = [...currentNodes.keys()].filter((id) => !baselineNodes.has(id));
12
+ const removedNodeIds = [...baselineNodes.keys()].filter((id) => !currentNodes.has(id));
13
+ const editedNodeIds = [...currentNodes.keys()].filter((id) => {
14
+ const before = baselineNodes.get(id);
15
+ return before !== undefined && nodeContentChanged(before, currentNodes.get(id));
16
+ });
17
+ const clusterDiff = diffClusters(baseline, current);
18
+ const connectionDiff = diffConnections(baseline, current);
19
+ const tableCellChanges = diffTableCells(baselineNodes, currentNodes);
20
+ const stats = {
21
+ addedNodes: addedNodeIds.length,
22
+ removedNodes: removedNodeIds.length,
23
+ editedNodes: editedNodeIds.length,
24
+ newClusters: clusterDiff.newClusters.length,
25
+ removedClusters: clusterDiff.removedClusters.length,
26
+ modifiedClusters: clusterDiff.modifiedClusters.length,
27
+ unchangedClusters: clusterDiff.unchangedClusters,
28
+ addedConnections: connectionDiff.added.length,
29
+ removedConnections: connectionDiff.removed.length,
30
+ };
31
+ return {
32
+ stats,
33
+ newClusters: clusterDiff.newClusters,
34
+ removedClusters: clusterDiff.removedClusters,
35
+ modifiedClusters: clusterDiff.modifiedClusters,
36
+ addedConnections: connectionDiff.added,
37
+ removedConnections: connectionDiff.removed,
38
+ summaryText: buildSummaryText(baseline, current, stats, clusterDiff, connectionDiff),
39
+ tableCellChanges,
40
+ };
41
+ }
42
+ /** Leaf content nodes: everything except structural containers and connectors. */
43
+ function contentNodesById(board) {
44
+ const map = new Map();
45
+ for (const node of board.nodes) {
46
+ if (!STRUCTURAL_TYPES.has(node.type) && node.type !== "CONNECTOR") {
47
+ map.set(node.id, node);
48
+ }
49
+ }
50
+ return map;
51
+ }
52
+ function nodeContentChanged(before, after) {
53
+ return (before.text ?? "") !== (after.text ?? "") ||
54
+ before.imageRef !== after.imageRef || before.name !== after.name || before.type !== after.type ||
55
+ before.rotation !== after.rotation ||
56
+ JSON.stringify(before.table?.cells) !== JSON.stringify(after.table?.cells) ||
57
+ (before.contentFingerprint !== undefined && after.contentFingerprint !== undefined &&
58
+ before.contentFingerprint !== after.contentFingerprint);
59
+ }
60
+ function diffTableCells(beforeNodes, afterNodes) {
61
+ const changes = [];
62
+ const tableIds = new Set([
63
+ ...[...beforeNodes.values()].filter((node) => node.table).map((node) => node.id),
64
+ ...[...afterNodes.values()].filter((node) => node.table).map((node) => node.id),
65
+ ]);
66
+ for (const tableNodeId of tableIds) {
67
+ const before = new Map((beforeNodes.get(tableNodeId)?.table?.cells ?? []).map((cell) => [cell.id, cell]));
68
+ const after = new Map((afterNodes.get(tableNodeId)?.table?.cells ?? []).map((cell) => [cell.id, cell]));
69
+ for (const cellId of new Set([...before.keys(), ...after.keys()])) {
70
+ const previous = before.get(cellId);
71
+ const current = after.get(cellId);
72
+ if (previous && current && previous.text === current.text && previous.row === current.row &&
73
+ previous.column === current.column)
74
+ continue;
75
+ changes.push({
76
+ tableNodeId, cellId,
77
+ changeType: !previous ? "added" : !current ? "removed" : "modified",
78
+ previousText: previous?.text, currentText: current?.text,
79
+ previousRow: previous?.row, previousColumn: previous?.column,
80
+ currentRow: current?.row, currentColumn: current?.column,
81
+ });
82
+ }
83
+ }
84
+ return changes;
85
+ }
86
+ /**
87
+ * Greedy best-overlap matching: cluster pairs are ranked by shared member
88
+ * count and matched one-to-one while the overlap covers at least half of
89
+ * the smaller cluster. Matched pairs with identical content hashes count as
90
+ * unchanged; everything unmatched is new (current side) or removed
91
+ * (baseline side).
92
+ */
93
+ function diffClusters(baseline, current) {
94
+ const baselineNodesById = new Map(baseline.nodes.map((node) => [node.id, node]));
95
+ const currentNodesById = new Map(current.nodes.map((node) => [node.id, node]));
96
+ const pairs = [];
97
+ const previousByMember = new Map();
98
+ for (const prev of baseline.clusters) {
99
+ for (const id of new Set(prev.nodeIds)) {
100
+ const owners = previousByMember.get(id) ?? [];
101
+ owners.push(prev);
102
+ previousByMember.set(id, owners);
103
+ }
104
+ }
105
+ for (const cur of current.clusters) {
106
+ const overlaps = new Map();
107
+ for (const id of cur.nodeIds) {
108
+ for (const prev of previousByMember.get(id) ?? [])
109
+ overlaps.set(prev, (overlaps.get(prev) ?? 0) + 1);
110
+ }
111
+ for (const [prev, overlap] of overlaps) {
112
+ if (overlap / Math.min(prev.nodeIds.length, cur.nodeIds.length) >= CLUSTER_MATCH_THRESHOLD)
113
+ pairs.push({ prev, cur, overlap });
114
+ }
115
+ }
116
+ pairs.sort((a, b) => b.overlap - a.overlap);
117
+ const matchedPrev = new Set();
118
+ const matchedCur = new Set();
119
+ const modifiedClusters = [];
120
+ let unchangedClusters = 0;
121
+ for (const { prev, cur } of pairs) {
122
+ if (matchedPrev.has(prev.id) || matchedCur.has(cur.id)) {
123
+ continue;
124
+ }
125
+ matchedPrev.add(prev.id);
126
+ matchedCur.add(cur.id);
127
+ const prevMembers = membersOf(prev, baselineNodesById);
128
+ const curMembers = membersOf(cur, currentNodesById);
129
+ if (hashClusterNodes(prevMembers) === hashClusterNodes(curMembers)) {
130
+ unchangedClusters++;
131
+ continue;
132
+ }
133
+ const prevIds = new Set(prev.nodeIds);
134
+ const curIds = new Set(cur.nodeIds);
135
+ const sharedIds = cur.nodeIds.filter((id) => prevIds.has(id));
136
+ modifiedClusters.push({
137
+ label: cur.label,
138
+ previousLabel: prev.label,
139
+ addedNodeCount: cur.nodeIds.filter((id) => !prevIds.has(id)).length,
140
+ removedNodeCount: prev.nodeIds.filter((id) => !curIds.has(id)).length,
141
+ editedNodeCount: sharedIds.filter((id) => {
142
+ const before = baselineNodesById.get(id);
143
+ const after = currentNodesById.get(id);
144
+ return before !== undefined && after !== undefined && nodeContentChanged(before, after);
145
+ }).length,
146
+ });
147
+ }
148
+ return {
149
+ newClusters: current.clusters
150
+ .filter((cluster) => !matchedCur.has(cluster.id))
151
+ .map((cluster) => ({ label: cluster.label, summary: cluster.summary })),
152
+ removedClusters: baseline.clusters
153
+ .filter((cluster) => !matchedPrev.has(cluster.id))
154
+ .map((cluster) => ({ label: cluster.label, summary: cluster.summary })),
155
+ modifiedClusters,
156
+ unchangedClusters,
157
+ };
158
+ }
159
+ function membersOf(cluster, nodesById) {
160
+ return cluster.nodeIds
161
+ .map((id) => nodesById.get(id))
162
+ .filter((node) => node !== undefined);
163
+ }
164
+ /**
165
+ * Diffs connector edges as a multiset of (from, to, label) tuples.
166
+ *
167
+ * Connector IDs are deliberately ignored: replacing an arrow without changing
168
+ * its meaning is not a semantic board change. Multiplicity still matters,
169
+ * though — removing one of two parallel arrows must be reported.
170
+ */
171
+ function diffConnections(baseline, current) {
172
+ const baselineEdges = groupEdgesByMeaning(baseline.connectorEdges ?? []);
173
+ const currentEdges = groupEdgesByMeaning(current.connectorEdges ?? []);
174
+ const keys = new Set([...baselineEdges.keys(), ...currentEdges.keys()]);
175
+ const added = [];
176
+ const removed = [];
177
+ const baselineLabel = edgeFormatter(baseline);
178
+ const currentLabel = edgeFormatter(current);
179
+ for (const key of keys) {
180
+ const before = baselineEdges.get(key) ?? [];
181
+ const after = currentEdges.get(key) ?? [];
182
+ for (let index = before.length; index < after.length; index++) {
183
+ added.push(currentLabel(after[index]));
184
+ }
185
+ for (let index = after.length; index < before.length; index++) {
186
+ removed.push(baselineLabel(before[index]));
187
+ }
188
+ }
189
+ return { added, removed };
190
+ }
191
+ function groupEdgesByMeaning(edges) {
192
+ const grouped = new Map();
193
+ for (const edge of edges) {
194
+ const direction = edge.direction ?? "forward";
195
+ const endpoints = direction === "reverse"
196
+ ? [edge.toNodeId, edge.fromNodeId]
197
+ : direction === "bidirectional" || direction === "undirected"
198
+ ? [edge.fromNodeId, edge.toNodeId].sort()
199
+ : [edge.fromNodeId, edge.toNodeId];
200
+ const key = JSON.stringify([endpoints, direction === "reverse" ? "forward" : direction, edge.label ?? ""]);
201
+ const matches = grouped.get(key) ?? [];
202
+ matches.push(edge);
203
+ grouped.set(key, matches);
204
+ }
205
+ return grouped;
206
+ }
207
+ /** Renders one edge with the owning clusters' labels (from its own snapshot). */
208
+ function edgeFormatter(board) {
209
+ const owner = new Map();
210
+ const nodes = new Map(board.nodes.map(node => [node.id, node]));
211
+ for (const cluster of board.clusters)
212
+ for (const id of cluster.nodeIds)
213
+ owner.set(id, cluster);
214
+ return edge => {
215
+ const internal = owner.has(edge.fromNodeId) && owner.get(edge.fromNodeId) === owner.get(edge.toNodeId);
216
+ const label = (id) => {
217
+ const cluster = owner.get(id);
218
+ if (cluster && !internal)
219
+ return cluster.label;
220
+ const node = nodes.get(id);
221
+ const text = node?.text?.trim();
222
+ return text ? truncate(text, 40) : node?.name ?? id;
223
+ };
224
+ return `"${label(edge.fromNodeId)}" ${connectorArrow(edge)} "${label(edge.toNodeId)}"${edge.label ? ` — "${edge.label}"` : ""}`;
225
+ };
226
+ }
227
+ function buildSummaryText(baseline, current, stats, clusters, connections) {
228
+ const lines = [
229
+ `FigJam board ${current.fileKey} — changes from ${timestamp(baseline.createdAt)} to ${timestamp(current.createdAt)}:`,
230
+ "",
231
+ ];
232
+ if (stats.addedNodes + stats.removedNodes + stats.editedNodes === 0 &&
233
+ stats.newClusters + stats.removedClusters + stats.modifiedClusters === 0 &&
234
+ stats.addedConnections + stats.removedConnections === 0) {
235
+ lines.push("No changes — the board content is identical.");
236
+ return lines.join("\n");
237
+ }
238
+ if (clusters.newClusters.length > 0) {
239
+ lines.push(`New clusters (${clusters.newClusters.length}):`);
240
+ lines.push(...capped(clusters.newClusters, (c) => `- "${c.label}": ${firstSentence(c.summary)}`));
241
+ }
242
+ if (clusters.removedClusters.length > 0) {
243
+ lines.push(`Removed clusters (${clusters.removedClusters.length}):`);
244
+ lines.push(...capped(clusters.removedClusters, (c) => `- "${c.label}"`));
245
+ }
246
+ if (clusters.modifiedClusters.length > 0) {
247
+ lines.push(`Modified clusters (${clusters.modifiedClusters.length}):`);
248
+ lines.push(...capped(clusters.modifiedClusters, (c) => {
249
+ const renamed = c.previousLabel !== c.label ? ` (was "${c.previousLabel}")` : "";
250
+ const parts = [
251
+ c.addedNodeCount > 0 ? `+${c.addedNodeCount} nodes` : "",
252
+ c.removedNodeCount > 0 ? `-${c.removedNodeCount} nodes` : "",
253
+ c.editedNodeCount > 0 ? `${c.editedNodeCount} edited` : "",
254
+ ].filter(Boolean);
255
+ return `- "${c.label}"${renamed}: ${parts.join(", ") || "reorganized"}`;
256
+ }));
257
+ }
258
+ if (connections.added.length > 0 || connections.removed.length > 0) {
259
+ lines.push(`Connections: +${connections.added.length} / -${connections.removed.length}`);
260
+ lines.push(...capped(connections.added, (line) => `- new: ${line}`));
261
+ lines.push(...capped(connections.removed, (line) => `- removed: ${line}`));
262
+ }
263
+ lines.push(`Nodes: +${stats.addedNodes} added / -${stats.removedNodes} removed / ${stats.editedNodes} edited.`);
264
+ if (stats.unchangedClusters > 0) {
265
+ lines.push(`Unchanged clusters: ${stats.unchangedClusters}.`);
266
+ }
267
+ return lines.join("\n");
268
+ }
269
+ function capped(items, render) {
270
+ const lines = items.slice(0, SUMMARY_SECTION_LIMIT).map(render);
271
+ if (items.length > SUMMARY_SECTION_LIMIT) {
272
+ lines.push(`- … and ${items.length - SUMMARY_SECTION_LIMIT} more`);
273
+ }
274
+ return lines;
275
+ }
276
+ function timestamp(ms) {
277
+ return new Date(ms).toISOString().replace(/\.\d{3}Z$/, "Z");
278
+ }
279
+ function firstSentence(text) {
280
+ return text.split(/(?<=[.!?])\s+/)[0]?.trim() ?? "";
281
+ }
282
+ function truncate(text, maxLength) {
283
+ return text.length <= maxLength ? text : `${text.slice(0, maxLength - 1)}…`;
284
+ }
285
+ //# sourceMappingURL=boardDiff.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"boardDiff.js","sourceRoot":"","sources":["../../src/lib/boardDiff.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type { BoardData } from "../types.js";
2
+ export declare function setBoard(boardId: string, data: BoardData): void;
3
+ export declare function getBoard(boardId: string): BoardData | undefined;
4
+ /**
5
+ * Memory-first read with persistent fallback: after a restart the in-memory
6
+ * map is empty, but the last finished ingest is still on disk — load it,
7
+ * mark its cluster summaries as cache-sourced, and re-seed the memory map.
8
+ */
9
+ export declare function getBoardOrRestore(boardId: string, snapshotId?: string): Promise<BoardData | undefined>;
@@ -0,0 +1,63 @@
1
+ import { readIntEnv } from "./env.js";
2
+ import { readLatestBoard, readBoardSnapshot, hasLegacyBoard } from "./persistentCache.js";
3
+ /**
4
+ * Simple in-memory store, keyed by boardId. setBoard writes/overwrites an
5
+ * entry produced by ingest_board; getBoard reads it back for
6
+ * get_board_context / answer_from_board. getBoardOrRestore additionally
7
+ * falls back to the persistent cache, so ingested boards survive server
8
+ * restarts without a manual re-ingest.
9
+ */
10
+ const MAX_CACHED_BOARDS = readIntEnv("FIGJAM_MCP_MEMORY_CACHE_MAX_BOARDS", 10, 1);
11
+ const boards = new Map();
12
+ export function setBoard(boardId, data) {
13
+ // Map preserves insertion order. Re-inserting an existing board moves it to
14
+ // the most-recently-used end before the oldest entries are evicted.
15
+ boards.delete(boardId);
16
+ boards.set(boardId, data);
17
+ while (boards.size > MAX_CACHED_BOARDS) {
18
+ const oldestBoardId = boards.keys().next().value;
19
+ if (oldestBoardId === undefined) {
20
+ break;
21
+ }
22
+ boards.delete(oldestBoardId);
23
+ }
24
+ }
25
+ export function getBoard(boardId) {
26
+ const board = boards.get(boardId);
27
+ if (!board) {
28
+ return undefined;
29
+ }
30
+ boards.delete(boardId);
31
+ boards.set(boardId, board);
32
+ return board;
33
+ }
34
+ /**
35
+ * Memory-first read with persistent fallback: after a restart the in-memory
36
+ * map is empty, but the last finished ingest is still on disk — load it,
37
+ * mark its cluster summaries as cache-sourced, and re-seed the memory map.
38
+ */
39
+ export async function getBoardOrRestore(boardId, snapshotId) {
40
+ const inMemory = getBoard(boardId);
41
+ if (inMemory && (!snapshotId || inMemory.snapshotId === snapshotId)) {
42
+ return inMemory;
43
+ }
44
+ const persisted = snapshotId ? await readBoardSnapshot(boardId, snapshotId) : await readLatestBoard(boardId);
45
+ if (!persisted) {
46
+ if (snapshotId)
47
+ throw new Error("Snapshot is no longer available. Start a new context query against the latest ingest.");
48
+ if (await hasLegacyBoard(boardId))
49
+ throw new Error("This board uses cache v3. Run ingest_board again to capture complete sources for cache v4; old files are preserved.");
50
+ return undefined;
51
+ }
52
+ const restored = {
53
+ ...persisted,
54
+ clusters: persisted.clusters.map((cluster) => ({
55
+ ...cluster,
56
+ cacheHit: true,
57
+ })),
58
+ };
59
+ if (!snapshotId)
60
+ setBoard(boardId, restored);
61
+ return restored;
62
+ }
63
+ //# sourceMappingURL=cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache.js","sourceRoot":"","sources":["../../src/lib/cache.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACtC,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE1F;;;;;;GAMG;AACH,MAAM,iBAAiB,GAAG,UAAU,CAAC,oCAAoC,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;AAClF,MAAM,MAAM,GAAG,IAAI,GAAG,EAAqB,CAAC;AAE5C,MAAM,UAAU,QAAQ,CAAC,OAAe,EAAE,IAAe;IACvD,4EAA4E;IAC5E,oEAAoE;IACpE,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACvB,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAC1B,OAAO,MAAM,CAAC,IAAI,GAAG,iBAAiB,EAAE,CAAC;QACvC,MAAM,aAAa,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,KAA2B,CAAC;QACvE,IAAI,aAAa,KAAK,SAAS,EAAE,CAAC;YAChC,MAAM;QACR,CAAC;QACD,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC;IAC/B,CAAC;AACH,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,OAAe;IACtC,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IAClC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACvB,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IAC3B,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,OAAe,EAAE,UAAmB;IAC1E,MAAM,QAAQ,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC;IACnC,IAAI,QAAQ,IAAI,CAAC,CAAC,UAAU,IAAI,QAAQ,CAAC,UAAU,KAAK,UAAU,CAAC,EAAE,CAAC;QACpE,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,MAAM,SAAS,GAAG,UAAU,CAAC,CAAC,CAAC,MAAM,iBAAiB,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,MAAM,eAAe,CAAC,OAAO,CAAC,CAAC;IAC7G,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,IAAI,UAAU;YAAE,MAAM,IAAI,KAAK,CAAC,uFAAuF,CAAC,CAAC;QACzH,IAAI,MAAM,cAAc,CAAC,OAAO,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,qHAAqH,CAAC,CAAC;QAC1K,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,MAAM,QAAQ,GAAc;QAC1B,GAAG,SAAS;QACZ,QAAQ,EAAE,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;YAC7C,GAAG,OAAO;YACV,QAAQ,EAAE,IAAI;SACf,CAAC,CAAC;KACJ,CAAC;IACF,IAAI,CAAC,UAAU;QAAE,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IAC7C,OAAO,QAAQ,CAAC;AAClB,CAAC"}
@@ -0,0 +1,32 @@
1
+ import type { Cluster, ClusterRelation, ConnectorEdge, NormalizedNode, RefinedCluster } from "../types.js";
2
+ /**
3
+ * Connector arrows are excluded from geometric clustering (they deliberately
4
+ * span between groups), but they carry the board's semantic structure:
5
+ * "A leads to B". This module extracts them as a graph and aggregates them
6
+ * to cluster-level relations that get_board_context / answer_from_board can
7
+ * hand to the LLM as relationship context.
8
+ */
9
+ /**
10
+ * Pulls all fully attached connector arrows out of the flattened node list.
11
+ * Connectors dangling in empty space (missing either endpoint) are skipped —
12
+ * without both endpoints there is no relation to express.
13
+ */
14
+ export declare function extractConnectorEdges(nodes: NormalizedNode[]): ConnectorEdge[];
15
+ export declare function connectorArrow(edge: ConnectorEdge): string;
16
+ /** Node-level evidence retains internal arrows that the cluster overview omits. */
17
+ export declare function formatNodeConnections(edges: ConnectorEdge[], nodes: NormalizedNode[], selectedNodeIds?: ReadonlySet<string>): string[];
18
+ /**
19
+ * Aggregates node-level connector edges into directed cluster-to-cluster
20
+ * relations. Internal edges remain available in the node graph and evidence;
21
+ * this overview only renders connections between clusters. Parallel edges
22
+ * between the same cluster pair are merged, collecting their unique labels. Sorted by
23
+ * edgeCount so the strongest relations come first.
24
+ */
25
+ export declare function buildClusterRelations(edges: ConnectorEdge[], clusters: Cluster[]): ClusterRelation[];
26
+ /**
27
+ * Renders cluster relations as compact human/LLM-readable lines, e.g.
28
+ * `"User interviews" → "Problem framing" — "informs" (2 connectors)`.
29
+ * Relations pointing at clusters missing from `clusters` (e.g. filtered out
30
+ * by a topic) are skipped.
31
+ */
32
+ export declare function formatClusterRelations(relations: ClusterRelation[], clusters: RefinedCluster[]): string[];
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Connector arrows are excluded from geometric clustering (they deliberately
3
+ * span between groups), but they carry the board's semantic structure:
4
+ * "A leads to B". This module extracts them as a graph and aggregates them
5
+ * to cluster-level relations that get_board_context / answer_from_board can
6
+ * hand to the LLM as relationship context.
7
+ */
8
+ /**
9
+ * Pulls all fully attached connector arrows out of the flattened node list.
10
+ * Connectors dangling in empty space (missing either endpoint) are skipped —
11
+ * without both endpoints there is no relation to express.
12
+ */
13
+ export function extractConnectorEdges(nodes) {
14
+ // Canvas endpoints are coordinate anchors, not claims about a document or page.
15
+ const nonContentIds = new Set(nodes
16
+ .filter(node => ["DOCUMENT", "CANVAS", "PAGE"].includes(node.type))
17
+ .map(node => node.id));
18
+ // Normalization retains page ancestry, but omits the outer document itself.
19
+ for (const node of nodes) {
20
+ if ((node.type === "CANVAS" || node.type === "PAGE") && node.parentId)
21
+ nonContentIds.add(node.parentId);
22
+ }
23
+ return nodes
24
+ .filter((node) => node.type === "CONNECTOR" && Boolean(node.connectorStartId) && Boolean(node.connectorEndId)
25
+ && !nonContentIds.has(node.connectorStartId) && !nonContentIds.has(node.connectorEndId))
26
+ .map((node) => ({
27
+ connectorId: node.id,
28
+ fromNodeId: node.connectorStartId,
29
+ toNodeId: node.connectorEndId,
30
+ label: node.text?.trim() || undefined,
31
+ direction: connectorDirection(node),
32
+ }));
33
+ }
34
+ function connectorDirection(node) {
35
+ // Historical fixtures/caches lack cap metadata and used start → end.
36
+ if (node.connectorStartArrowhead === undefined && node.connectorEndArrowhead === undefined) {
37
+ return "forward";
38
+ }
39
+ const hasArrow = (cap) => Boolean(cap?.startsWith("ARROW_") || cap === "TRIANGLE_FILLED" || cap === "LINE_ARROW");
40
+ const start = hasArrow(node.connectorStartArrowhead);
41
+ const end = hasArrow(node.connectorEndArrowhead);
42
+ return start && end ? "bidirectional" : start ? "reverse" : end ? "forward" : "undirected";
43
+ }
44
+ export function connectorArrow(edge) {
45
+ return edge.direction === "reverse" ? "←" : edge.direction === "bidirectional" ? "↔" :
46
+ edge.direction === "undirected" ? "—" : "→";
47
+ }
48
+ /** Node-level evidence retains internal arrows that the cluster overview omits. */
49
+ export function formatNodeConnections(edges, nodes, selectedNodeIds) {
50
+ const byId = new Map(nodes.map((node) => [node.id, node]));
51
+ const label = (id) => {
52
+ const node = byId.get(id);
53
+ const text = node?.text?.trim() || node?.name || id;
54
+ return `${id} (${text.length > 120 ? `${text.slice(0, 119)}…` : text})`;
55
+ };
56
+ return edges
57
+ .filter((edge) => !selectedNodeIds || selectedNodeIds.has(edge.fromNodeId) || selectedNodeIds.has(edge.toNodeId))
58
+ .map((edge) => `${label(edge.fromNodeId)} ${connectorArrow(edge)} ${label(edge.toNodeId)}${edge.label ? ` — ${edge.label}` : ""}`);
59
+ }
60
+ /**
61
+ * Aggregates node-level connector edges into directed cluster-to-cluster
62
+ * relations. Internal edges remain available in the node graph and evidence;
63
+ * this overview only renders connections between clusters. Parallel edges
64
+ * between the same cluster pair are merged, collecting their unique labels. Sorted by
65
+ * edgeCount so the strongest relations come first.
66
+ */
67
+ export function buildClusterRelations(edges, clusters) {
68
+ const clusterOfNode = new Map();
69
+ for (const cluster of clusters) {
70
+ for (const nodeId of cluster.nodeIds) {
71
+ clusterOfNode.set(nodeId, cluster.id);
72
+ }
73
+ }
74
+ const relations = new Map();
75
+ for (const edge of edges) {
76
+ if (edge.direction === "undirected")
77
+ continue;
78
+ const pairs = edge.direction === "reverse"
79
+ ? [[edge.toNodeId, edge.fromNodeId]]
80
+ : edge.direction === "bidirectional"
81
+ ? [[edge.fromNodeId, edge.toNodeId], [edge.toNodeId, edge.fromNodeId]]
82
+ : [[edge.fromNodeId, edge.toNodeId]];
83
+ for (const [fromId, toId] of pairs) {
84
+ const from = clusterOfNode.get(fromId);
85
+ const to = clusterOfNode.get(toId);
86
+ if (!from || !to || from === to) {
87
+ continue;
88
+ }
89
+ const key = `${from}->${to}`;
90
+ const relation = relations.get(key) ?? {
91
+ fromClusterId: from,
92
+ toClusterId: to,
93
+ labels: [],
94
+ edgeCount: 0,
95
+ };
96
+ relation.edgeCount++;
97
+ if (edge.label && !relation.labels.includes(edge.label)) {
98
+ relation.labels.push(edge.label);
99
+ }
100
+ relations.set(key, relation);
101
+ }
102
+ }
103
+ return [...relations.values()].sort((a, b) => b.edgeCount - a.edgeCount);
104
+ }
105
+ /**
106
+ * Renders cluster relations as compact human/LLM-readable lines, e.g.
107
+ * `"User interviews" → "Problem framing" — "informs" (2 connectors)`.
108
+ * Relations pointing at clusters missing from `clusters` (e.g. filtered out
109
+ * by a topic) are skipped.
110
+ */
111
+ export function formatClusterRelations(relations, clusters) {
112
+ const labelOf = new Map(clusters.map((cluster) => [cluster.id, cluster.label]));
113
+ return relations
114
+ .filter((rel) => labelOf.has(rel.fromClusterId) && labelOf.has(rel.toClusterId))
115
+ .map((rel) => {
116
+ const labels = rel.labels.length > 0 ? ` — "${rel.labels.join('", "')}"` : "";
117
+ const count = rel.edgeCount > 1 ? ` (${rel.edgeCount} connectors)` : "";
118
+ return `- "${labelOf.get(rel.fromClusterId)}" → "${labelOf.get(rel.toClusterId)}"${labels}${count}`;
119
+ });
120
+ }
121
+ //# sourceMappingURL=connectorGraph.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connectorGraph.js","sourceRoot":"","sources":["../../src/lib/connectorGraph.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ import type { DocStructureHint, RefinedCluster } from "../types.js";
2
+ /**
3
+ * Maps clusters onto the phases of a documentation framework — deterministic
4
+ * keyword scoring against each cluster's label and summary, no extra LLM
5
+ * call. The label/summary text produced by the vision/text step is
6
+ * descriptive enough for that, and the "unclear" fallback keeps mismatches
7
+ * honest.
8
+ *
9
+ * Built-in frameworks live in FRAMEWORKS; callers can also pass a free-form
10
+ * phase list (customPhases on ingest_board), whose keywords are derived from
11
+ * the phase names themselves.
12
+ */
13
+ export interface PhaseDefinition {
14
+ name: string;
15
+ /** Lowercase keyword signals matched against label (2×) and summary (1×). */
16
+ keywords: string[];
17
+ }
18
+ /**
19
+ * Assigns each cluster a phase from the active framework. Order of
20
+ * precedence: customPhases (free-form names) > built-in framework for the
21
+ * hint > no-op for "freeform". Clusters with no keyword hits — or a tie
22
+ * between phases — get "unclear".
23
+ */
24
+ export declare function mapClustersToPhases(clusters: RefinedCluster[], hint: DocStructureHint, customPhases?: string[]): RefinedCluster[];