@plumpslabs/kuma 2.3.19 → 2.3.20
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@plumpslabs/kuma",
|
|
3
|
-
"version": "2.3.
|
|
3
|
+
"version": "2.3.20",
|
|
4
4
|
"description": "Safety-first context & orchestration engine for AI coding agents. MCP server with mandatory research pipeline, knowledge graph, impact analysis, decision memory, and safety guard — works with any MCP client.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -133,7 +133,9 @@ header .status .dot.off { background: var(--red); }
|
|
|
133
133
|
<div id="graph-container"></div>
|
|
134
134
|
<div id="search-box"><input id="node-search" type="text" placeholder="Search nodes..." /></div>
|
|
135
135
|
<div id="graph-controls">
|
|
136
|
+
<button onclick="resetGraphFocus()">Reset Focus</button>
|
|
136
137
|
<button onclick="if(network)network.fit()">Fit</button>
|
|
138
|
+
<button onclick="exportGraphPNG()">Export PNG</button>
|
|
137
139
|
<button onclick="if(network)network.startSimulation()">Restart</button>
|
|
138
140
|
<button onclick="if(network)network.stopSimulation()">Stop</button>
|
|
139
141
|
</div>
|
|
@@ -154,6 +156,26 @@ header .status .dot.off { background: var(--red); }
|
|
|
154
156
|
const API = "/api";
|
|
155
157
|
let network = null, nodesData = null, allNodes = null, allEdges = null;
|
|
156
158
|
|
|
159
|
+
function resetGraphFocus() {
|
|
160
|
+
if (!network) return;
|
|
161
|
+
network.body.data.nodes.forEach(function(n) {
|
|
162
|
+
network.body.data.nodes.update({ id: n.id, opacity: 1.0 });
|
|
163
|
+
});
|
|
164
|
+
document.getElementById("graph-tooltip").style.display = 'none';
|
|
165
|
+
var searchInput = document.getElementById("node-search");
|
|
166
|
+
if (searchInput) searchInput.value = "";
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function exportGraphPNG() {
|
|
170
|
+
if (!network) return;
|
|
171
|
+
var canvas = document.querySelector("#graph-container canvas");
|
|
172
|
+
if (!canvas) return;
|
|
173
|
+
var link = document.createElement("a");
|
|
174
|
+
link.download = "kuma-knowledge-graph.png";
|
|
175
|
+
link.href = canvas.toDataURL("image/png");
|
|
176
|
+
link.click();
|
|
177
|
+
}
|
|
178
|
+
|
|
157
179
|
async function init() {
|
|
158
180
|
var status = await fetch(API+"/status").then(function(r){return r.json()});
|
|
159
181
|
var dot = document.getElementById("status-dot");
|
|
@@ -204,6 +226,9 @@ function truncateLabel(name, maxLen) {
|
|
|
204
226
|
/** Calculate node size based on label length and type */
|
|
205
227
|
function calcNodeSize(name, type) {
|
|
206
228
|
var base = 14;
|
|
229
|
+
if (type === "feature_domain") base = 24;
|
|
230
|
+
if (type === "workflow") base = 18;
|
|
231
|
+
if (type === "cross_service_link") base = 16;
|
|
207
232
|
if (type === "function") base = 16;
|
|
208
233
|
if (type === "class") base = 18;
|
|
209
234
|
if (type === "module") base = 12;
|
|
@@ -212,7 +237,7 @@ function calcNodeSize(name, type) {
|
|
|
212
237
|
if (type === "test") base = 13;
|
|
213
238
|
// Gentle scaling for long labels
|
|
214
239
|
var len = name ? Math.min(name.length, 20) : 4;
|
|
215
|
-
return Math.min(
|
|
240
|
+
return Math.min(32, Math.max(base, base + len * 0.4));
|
|
216
241
|
}
|
|
217
242
|
|
|
218
243
|
|
|
@@ -225,9 +250,11 @@ function renderGraph(data) {
|
|
|
225
250
|
}
|
|
226
251
|
|
|
227
252
|
var types = [...new Set(allNodes.map(function(n){return n.type||'unknown'}))];
|
|
228
|
-
var colors = {
|
|
253
|
+
var colors = {feature_domain:'#ef4444',workflow:'#3b82f6',cross_service_link:'#10b981',
|
|
254
|
+
function:'#7c3aed',class:'#06b6d4',module:'#22c55e',
|
|
229
255
|
component:'#f97316',file:'#eab308',route:'#ec4899',test:'#14b8a6'};
|
|
230
|
-
var shapes = {
|
|
256
|
+
var shapes = {feature_domain:'star',workflow:'ellipse',cross_service_link:'box',
|
|
257
|
+
function:'box',class:'hexagon',module:'ellipse',
|
|
231
258
|
component:'diamond',file:'square',route:'triangle',test:'star'};
|
|
232
259
|
|
|
233
260
|
// Limit nodes for performance if > 200
|
|
@@ -261,16 +288,25 @@ function renderGraph(data) {
|
|
|
261
288
|
}).slice(0, 500);
|
|
262
289
|
|
|
263
290
|
var visEdges = new vis.DataSet(displayEdges.map(function(e){
|
|
291
|
+
var edgeColor = '#64748b';
|
|
292
|
+
var width = 1.5;
|
|
293
|
+
var dashes = false;
|
|
294
|
+
if (e.relation === 'flows_through') { edgeColor = '#3b82f6'; width = 2.5; }
|
|
295
|
+
if (e.relation === 'triggers') { edgeColor = '#ef4444'; width = 2; dashes = true; }
|
|
296
|
+
if (e.relation === 'syncs_with') { edgeColor = '#10b981'; width = 2; }
|
|
297
|
+
if (e.relation === 'contains') { edgeColor = '#475569'; width = 1; }
|
|
298
|
+
|
|
264
299
|
return {
|
|
265
300
|
from: e.source, to: e.target,
|
|
266
301
|
label: e.relation || '',
|
|
267
|
-
arrows: { to: { enabled: true, scaleFactor: 0.
|
|
268
|
-
color: { color:
|
|
269
|
-
font: { size: 10, color: '#
|
|
302
|
+
arrows: { to: { enabled: true, scaleFactor: 0.7, type: 'arrow' } },
|
|
303
|
+
color: { color: edgeColor, highlight: '#a855f7', hover: '#38bdf8', opacity: 0.85 },
|
|
304
|
+
font: { size: 10, color: '#94a3b8', strokeWidth: 0, background: 'transparent' },
|
|
305
|
+
dashes: dashes,
|
|
270
306
|
smooth: { type: 'continuous', roundness: 0.3 },
|
|
271
|
-
width:
|
|
272
|
-
hoverWidth:
|
|
273
|
-
selectionWidth:
|
|
307
|
+
width: width,
|
|
308
|
+
hoverWidth: 3,
|
|
309
|
+
selectionWidth: 3,
|
|
274
310
|
};
|
|
275
311
|
}));
|
|
276
312
|
|
|
@@ -315,11 +351,22 @@ function renderGraph(data) {
|
|
|
315
351
|
configure: { enabled: false },
|
|
316
352
|
});
|
|
317
353
|
|
|
318
|
-
// Click node → show tooltip with file path
|
|
354
|
+
// Click node → show tooltip with file path + highlight connected neighborhood
|
|
319
355
|
network.on('click', function(params) {
|
|
320
356
|
var tip = document.getElementById("graph-tooltip");
|
|
321
357
|
if (params.nodes.length) {
|
|
322
|
-
var
|
|
358
|
+
var selectedId = params.nodes[0];
|
|
359
|
+
var connectedNodeIds = new Set(network.getConnectedNodes(selectedId));
|
|
360
|
+
connectedNodeIds.add(selectedId);
|
|
361
|
+
|
|
362
|
+
network.body.data.nodes.forEach(function(n) {
|
|
363
|
+
network.body.data.nodes.update({
|
|
364
|
+
id: n.id,
|
|
365
|
+
opacity: connectedNodeIds.has(n.id) ? 1.0 : 0.15
|
|
366
|
+
});
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
var n = allNodes.find(function(x){return x.id===selectedId});
|
|
323
370
|
if (n) {
|
|
324
371
|
tip.style.display = 'block';
|
|
325
372
|
tip.innerHTML = '<b>' + n.name + '</b> <span style="color:var(--muted)">' + n.type + '</span>' +
|
|
@@ -337,6 +384,9 @@ function renderGraph(data) {
|
|
|
337
384
|
}
|
|
338
385
|
} else {
|
|
339
386
|
tip.style.display = 'none';
|
|
387
|
+
network.body.data.nodes.forEach(function(n) {
|
|
388
|
+
network.body.data.nodes.update({ id: n.id, opacity: 1.0 });
|
|
389
|
+
});
|
|
340
390
|
}
|
|
341
391
|
});
|
|
342
392
|
|