@plumpslabs/kuma 2.4.9 → 2.4.10
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.4.
|
|
3
|
+
"version": "2.4.10",
|
|
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",
|
|
@@ -182,11 +182,31 @@ async function getDashboardData() {
|
|
|
182
182
|
const pkg = JSON.parse(fs.readFileSync(rootPkgPath, "utf-8"));
|
|
183
183
|
const pnpmWs = path.join(root, "pnpm-workspace.yaml");
|
|
184
184
|
const isWs = Boolean(pkg.workspaces || fs.existsSync(pnpmWs));
|
|
185
|
+
const packages = [];
|
|
186
|
+
if (isWs) {
|
|
187
|
+
for (const dirName of ["packages", "apps", "libs", "services", "crates"]) {
|
|
188
|
+
const dirPath = path.join(root, dirName);
|
|
189
|
+
if (fs.existsSync(dirPath) && fs.statSync(dirPath).isDirectory()) {
|
|
190
|
+
for (const sub of fs.readdirSync(dirPath)) {
|
|
191
|
+
const subPkg = path.join(dirPath, sub, "package.json");
|
|
192
|
+
if (fs.existsSync(subPkg)) {
|
|
193
|
+
try {
|
|
194
|
+
const sp = JSON.parse(fs.readFileSync(subPkg, "utf-8"));
|
|
195
|
+
packages.push({ name: sp.name || sub, path: `${dirName}/${sub}` });
|
|
196
|
+
} catch {
|
|
197
|
+
packages.push({ name: sub, path: `${dirName}/${sub}` });
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
185
204
|
workspace = {
|
|
186
205
|
isWorkspace: isWs,
|
|
187
206
|
name: pkg.name || path.basename(root),
|
|
188
207
|
version: pkg.version || "1.0.0",
|
|
189
|
-
type: isWs ? fs.existsSync(pnpmWs) ? "pnpm" : "npm/yarn" : "single-package"
|
|
208
|
+
type: isWs ? fs.existsSync(pnpmWs) ? "pnpm" : "npm/yarn" : "single-package",
|
|
209
|
+
packages
|
|
190
210
|
};
|
|
191
211
|
}
|
|
192
212
|
} catch {
|
|
@@ -440,10 +460,13 @@ try {
|
|
|
440
460
|
} catch {
|
|
441
461
|
}
|
|
442
462
|
app.get("/", (c) => {
|
|
443
|
-
|
|
463
|
+
try {
|
|
464
|
+
const html = readFileSync(htmlPath, "utf-8");
|
|
465
|
+
return c.html(html);
|
|
466
|
+
} catch {
|
|
467
|
+
if (htmlCache) return c.html(htmlCache);
|
|
444
468
|
return c.text("index.html not found. Run from packages/ide/studio/", 500);
|
|
445
469
|
}
|
|
446
|
-
return c.html(htmlCache);
|
|
447
470
|
});
|
|
448
471
|
serve({ fetch: app.fetch, port: PORT }, (info) => {
|
|
449
472
|
const dbPath = findKumaDb();
|
|
@@ -322,10 +322,47 @@ header {
|
|
|
322
322
|
color: var(--muted);
|
|
323
323
|
border-radius: 4px;
|
|
324
324
|
font-size: 10px;
|
|
325
|
-
padding:
|
|
325
|
+
padding: 3px 8px;
|
|
326
326
|
cursor: pointer;
|
|
327
|
+
display: inline-flex;
|
|
328
|
+
align-items: center;
|
|
329
|
+
gap: 4px;
|
|
330
|
+
transition: all 0.2s ease;
|
|
327
331
|
}
|
|
328
332
|
.btn-copy-workaround:hover { color: #fff; border-color: var(--amber); }
|
|
333
|
+
.btn-copy-workaround.copied {
|
|
334
|
+
background: rgba(16, 185, 129, 0.15);
|
|
335
|
+
border-color: var(--emerald);
|
|
336
|
+
color: #34d399;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
#kuma-toast {
|
|
340
|
+
position: fixed;
|
|
341
|
+
bottom: 24px;
|
|
342
|
+
right: 24px;
|
|
343
|
+
background: #1e293b;
|
|
344
|
+
color: #f8fafc;
|
|
345
|
+
border: 1px solid #334155;
|
|
346
|
+
border-left: 4px solid var(--emerald);
|
|
347
|
+
padding: 10px 16px;
|
|
348
|
+
border-radius: 6px;
|
|
349
|
+
box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.5);
|
|
350
|
+
display: flex;
|
|
351
|
+
align-items: center;
|
|
352
|
+
gap: 8px;
|
|
353
|
+
font-size: 12px;
|
|
354
|
+
font-weight: 500;
|
|
355
|
+
z-index: 9999;
|
|
356
|
+
opacity: 0;
|
|
357
|
+
transform: translateY(12px);
|
|
358
|
+
transition: opacity 0.25s ease, transform 0.25s ease;
|
|
359
|
+
pointer-events: none;
|
|
360
|
+
}
|
|
361
|
+
#kuma-toast.show {
|
|
362
|
+
opacity: 1;
|
|
363
|
+
transform: translateY(0);
|
|
364
|
+
pointer-events: auto;
|
|
365
|
+
}
|
|
329
366
|
|
|
330
367
|
.gotcha-footer {
|
|
331
368
|
font-size: 11px;
|
|
@@ -699,11 +736,14 @@ async function fetchDashboard() {
|
|
|
699
736
|
}
|
|
700
737
|
|
|
701
738
|
function renderAll(data) {
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
739
|
+
if (!data) return;
|
|
740
|
+
try { renderGotchas(data.gotchas || []); } catch (e) { console.error("renderGotchas error:", e); }
|
|
741
|
+
try { renderFlows(data.flows || []); } catch (e) { console.error("renderFlows error:", e); }
|
|
742
|
+
try { renderWorkspace(data.workspace, data.nodes || []); } catch (e) { console.error("renderWorkspace error:", e); }
|
|
743
|
+
try { renderHealth(data); } catch (e) { console.error("renderHealth error:", e); }
|
|
744
|
+
if (window.lucide && window.lucide.createIcons) {
|
|
745
|
+
try { window.lucide.createIcons(); } catch {}
|
|
746
|
+
}
|
|
707
747
|
}
|
|
708
748
|
|
|
709
749
|
function setStatusFilter(status) {
|
|
@@ -756,8 +796,10 @@ function renderGotchaCards(list) {
|
|
|
756
796
|
${g.workaround ? `
|
|
757
797
|
<div class="gotcha-workaround">
|
|
758
798
|
<strong>Workaround:</strong>
|
|
759
|
-
|
|
760
|
-
<button class="btn-copy-workaround" onclick="
|
|
799
|
+
<div class="workaround-text" style="margin-top:4px">${escapeHtml(g.workaround)}</div>
|
|
800
|
+
<button class="btn-copy-workaround" onclick="copyWorkaround(this)" title="Copy workaround to clipboard">
|
|
801
|
+
<i data-lucide="copy" style="width:11px;height:11px"></i> Copy
|
|
802
|
+
</button>
|
|
761
803
|
</div>
|
|
762
804
|
` : ''}
|
|
763
805
|
<div class="gotcha-footer">
|
|
@@ -771,23 +813,25 @@ function renderGotchaCards(list) {
|
|
|
771
813
|
}
|
|
772
814
|
|
|
773
815
|
function renderFlows(flows) {
|
|
774
|
-
document.getElementById("badge-flow-count")
|
|
816
|
+
var countBadge = document.getElementById("badge-flow-count");
|
|
817
|
+
if (countBadge) countBadge.textContent = flows ? flows.length : 0;
|
|
775
818
|
var container = document.getElementById("flow-cards-container");
|
|
776
|
-
if (!
|
|
819
|
+
if (!container) return;
|
|
820
|
+
if (!flows || !flows.length) {
|
|
777
821
|
container.innerHTML = '<div class="empty-placeholder"><i data-lucide="git-branch"></i><div>No domain sequence flows recorded yet.</div></div>';
|
|
778
822
|
if (window.lucide && window.lucide.createIcons) window.lucide.createIcons();
|
|
779
823
|
return;
|
|
780
824
|
}
|
|
781
825
|
container.innerHTML = flows.map(function(f) {
|
|
782
826
|
var meta = {};
|
|
783
|
-
try { meta = typeof f.metadata === "string" ? JSON.parse(f.metadata) : f.metadata; } catch {}
|
|
784
|
-
var hops = meta.hops
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
827
|
+
try { meta = typeof f.metadata === "string" ? JSON.parse(f.metadata) : (f.metadata || {}); } catch {}
|
|
828
|
+
var hops = Array.isArray(meta.hops) ? meta.hops : [];
|
|
829
|
+
var hopsCount = hops.length || (typeof meta.hops === "number" ? meta.hops : (meta.hopsCount || 0));
|
|
830
|
+
var gotchas = Array.isArray(meta.gotchas) ? meta.gotchas : [];
|
|
831
|
+
|
|
832
|
+
var hopsChainHtml = '';
|
|
833
|
+
if (hops.length > 0) {
|
|
834
|
+
hopsChainHtml = `
|
|
791
835
|
<div class="flow-hops-chain">
|
|
792
836
|
${hops.map(function(h, idx) {
|
|
793
837
|
var toLabel = typeof h === "object" ? (h.to || "") : String(h);
|
|
@@ -799,7 +843,21 @@ function renderFlows(flows) {
|
|
|
799
843
|
return `${firstHopPrefix}<span class="hop-node">${escapeHtml(toLabel)}${rel}</span>${idx < hops.length - 1 ? '<span class="hop-arrow">→</span>' : ''}`;
|
|
800
844
|
}).join("")}
|
|
801
845
|
</div>
|
|
802
|
-
|
|
846
|
+
`;
|
|
847
|
+
} else if (hopsCount > 0) {
|
|
848
|
+
hopsChainHtml = `<div style="font-size:12px;color:var(--muted);padding:4px 0">${hopsCount} sequence hop(s) recorded.</div>`;
|
|
849
|
+
} else {
|
|
850
|
+
hopsChainHtml = `<div style="font-size:12px;color:var(--muted);padding:4px 0">No hops recorded for this flow.</div>`;
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
return `
|
|
854
|
+
<div class="flow-card">
|
|
855
|
+
<div class="flow-card-header">
|
|
856
|
+
<div class="flow-anchor"><i data-lucide="anchor" style="width:16px;height:16px"></i> ${escapeHtml(f.name || "Flow")}</div>
|
|
857
|
+
<span style="font-size:11px;color:var(--muted)">${hopsCount} hop(s)</span>
|
|
858
|
+
</div>
|
|
859
|
+
${hopsChainHtml}
|
|
860
|
+
${gotchas.length ? `<div style="font-size:11px;color:var(--amber);margin-top:6px">⚠️ Linked Gotchas: ${gotchas.map(escapeHtml).join(", ")}</div>` : ''}
|
|
803
861
|
</div>
|
|
804
862
|
`;
|
|
805
863
|
}).join("");
|
|
@@ -808,14 +866,29 @@ function renderFlows(flows) {
|
|
|
808
866
|
|
|
809
867
|
function renderWorkspace(ws, nodes) {
|
|
810
868
|
var container = document.getElementById("ws-topology-info");
|
|
869
|
+
if (!container) return;
|
|
811
870
|
if (!ws) {
|
|
812
871
|
container.innerHTML = "<div>Single package workspace.</div>";
|
|
813
872
|
return;
|
|
814
873
|
}
|
|
874
|
+
var nodesLen = (nodes && nodes.length) ? nodes.length : 0;
|
|
875
|
+
var pkgHtml = '';
|
|
876
|
+
if (ws.packages && Array.isArray(ws.packages) && ws.packages.length > 0) {
|
|
877
|
+
pkgHtml = `
|
|
878
|
+
<div style="margin-top:10px"><strong>Monorepo Packages (${ws.packages.length}):</strong></div>
|
|
879
|
+
<div style="margin-top:6px;display:flex;flex-wrap:wrap;gap:6px">
|
|
880
|
+
${ws.packages.map(function(p) {
|
|
881
|
+
var name = typeof p === 'string' ? p : (p.name || p.path || '');
|
|
882
|
+
return `<span class="badge-pkg" style="background:#1e293b;border:1px solid #334155;padding:2px 8px;border-radius:4px;font-size:11px">📦 ${escapeHtml(name)}</span>`;
|
|
883
|
+
}).join("")}
|
|
884
|
+
</div>
|
|
885
|
+
`;
|
|
886
|
+
}
|
|
815
887
|
container.innerHTML = `
|
|
816
888
|
<div style="margin-bottom:8px"><strong>Workspace Type:</strong> <span class="badge-project">${escapeHtml(ws.type || "standard")}</span></div>
|
|
817
889
|
<div style="margin-bottom:8px"><strong>Project Root:</strong> <code>${escapeHtml(ws.name || "root")}</code> (v${escapeHtml(ws.version || "1.0.0")})</div>
|
|
818
|
-
|
|
890
|
+
${pkgHtml}
|
|
891
|
+
<div style="margin-top:12px;color:var(--muted)">Total indexed knowledge entities: <strong>${nodesLen}</strong></div>
|
|
819
892
|
`;
|
|
820
893
|
}
|
|
821
894
|
|
|
@@ -865,13 +938,16 @@ function renderHealth(data) {
|
|
|
865
938
|
|
|
866
939
|
var tbody = document.querySelector("#sessions-table tbody");
|
|
867
940
|
var sessions = eff.recentSessions || [];
|
|
941
|
+
if (!tbody) return;
|
|
868
942
|
if (!sessions.length) {
|
|
869
943
|
tbody.innerHTML = '<tr><td colspan="6" style="text-align:center;color:var(--muted)">No session records logged yet.</td></tr>';
|
|
870
944
|
} else {
|
|
871
945
|
tbody.innerHTML = sessions.map(function(s) {
|
|
946
|
+
var dateObj = s.startedAt ? new Date(s.startedAt < 1e11 ? s.startedAt * 1000 : s.startedAt) : null;
|
|
947
|
+
var timeStr = dateObj && !isNaN(dateObj.getTime()) ? dateObj.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }) : "Recent";
|
|
872
948
|
return `
|
|
873
949
|
<tr>
|
|
874
|
-
<td>${
|
|
950
|
+
<td>${timeStr}</td>
|
|
875
951
|
<td>${escapeHtml(s.goal || "General development")}</td>
|
|
876
952
|
<td>${s.toolCalls}</td>
|
|
877
953
|
<td>${s.edits}</td>
|
|
@@ -883,6 +959,7 @@ function renderHealth(data) {
|
|
|
883
959
|
}
|
|
884
960
|
|
|
885
961
|
var staleBox = document.getElementById("staleness-container");
|
|
962
|
+
if (!staleBox) return;
|
|
886
963
|
if (data.staleness && data.staleness.missing && data.staleness.missing.length) {
|
|
887
964
|
staleBox.innerHTML = `
|
|
888
965
|
<div style="color:var(--rose);margin-bottom:8px">⚠️ Detected ${data.staleness.missing.length} reference(s) to deleted files:</div>
|
|
@@ -961,10 +1038,76 @@ async function resetDb() {
|
|
|
961
1038
|
}
|
|
962
1039
|
}
|
|
963
1040
|
|
|
964
|
-
function
|
|
965
|
-
|
|
966
|
-
|
|
1041
|
+
function showToast(msg) {
|
|
1042
|
+
var toast = document.getElementById("kuma-toast");
|
|
1043
|
+
if (!toast) {
|
|
1044
|
+
toast = document.createElement("div");
|
|
1045
|
+
toast.id = "kuma-toast";
|
|
1046
|
+
document.body.appendChild(toast);
|
|
967
1047
|
}
|
|
1048
|
+
toast.innerHTML = `<span style="color:var(--emerald);font-size:14px">✓</span> <span>${escapeHtml(msg || "Copied to clipboard!")}</span>`;
|
|
1049
|
+
toast.classList.add("show");
|
|
1050
|
+
clearTimeout(window.__kumaToastTimer);
|
|
1051
|
+
window.__kumaToastTimer = setTimeout(function() {
|
|
1052
|
+
toast.classList.remove("show");
|
|
1053
|
+
}, 2500);
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
function fallbackCopy(text) {
|
|
1057
|
+
var textarea = document.createElement("textarea");
|
|
1058
|
+
textarea.value = text;
|
|
1059
|
+
textarea.style.position = "fixed";
|
|
1060
|
+
textarea.style.top = "-9999px";
|
|
1061
|
+
textarea.style.left = "-9999px";
|
|
1062
|
+
document.body.appendChild(textarea);
|
|
1063
|
+
textarea.focus();
|
|
1064
|
+
textarea.select();
|
|
1065
|
+
try {
|
|
1066
|
+
document.execCommand("copy");
|
|
1067
|
+
} catch (err) {
|
|
1068
|
+
console.error("execCommand copy error:", err);
|
|
1069
|
+
}
|
|
1070
|
+
document.body.removeChild(textarea);
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
async function copyText(str, btn, toastMsg) {
|
|
1074
|
+
if (!str) return;
|
|
1075
|
+
var success = false;
|
|
1076
|
+
if (navigator.clipboard && window.isSecureContext) {
|
|
1077
|
+
try {
|
|
1078
|
+
await navigator.clipboard.writeText(str);
|
|
1079
|
+
success = true;
|
|
1080
|
+
} catch {
|
|
1081
|
+
fallbackCopy(str);
|
|
1082
|
+
success = true;
|
|
1083
|
+
}
|
|
1084
|
+
} else {
|
|
1085
|
+
fallbackCopy(str);
|
|
1086
|
+
success = true;
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
if (success) {
|
|
1090
|
+
showToast(toastMsg || "Copied to clipboard!");
|
|
1091
|
+
if (btn) {
|
|
1092
|
+
var origHtml = btn.innerHTML;
|
|
1093
|
+
btn.classList.add("copied");
|
|
1094
|
+
btn.innerHTML = '<span>Copied! ✓</span>';
|
|
1095
|
+
setTimeout(function() {
|
|
1096
|
+
btn.classList.remove("copied");
|
|
1097
|
+
btn.innerHTML = origHtml;
|
|
1098
|
+
}, 2000);
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
function copyWorkaround(btn) {
|
|
1104
|
+
if (!btn) return;
|
|
1105
|
+
var parent = btn.closest(".gotcha-workaround");
|
|
1106
|
+
if (!parent) return;
|
|
1107
|
+
var textEl = parent.querySelector(".workaround-text");
|
|
1108
|
+
var text = textEl ? textEl.innerText.trim() : "";
|
|
1109
|
+
if (!text) return;
|
|
1110
|
+
copyText(text, btn, "Workaround copied to clipboard!");
|
|
968
1111
|
}
|
|
969
1112
|
|
|
970
1113
|
function escapeHtml(str) {
|
package/plugin.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://antigravity.google/schemas/v1/plugin.json",
|
|
3
3
|
"name": "kuma",
|
|
4
|
-
"version": "2.4.
|
|
4
|
+
"version": "2.4.10",
|
|
5
5
|
"description": "Safety-first context & orchestration engine for AI coding agents. Shadow memory, gotcha lifecycle, blast radius impact analysis, and test verification.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "plumpslabs",
|