@enigmax/dashboard 0.1.69 → 0.1.70

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 (2) hide show
  1. package/assets/index.html +94 -1
  2. package/package.json +1 -1
package/assets/index.html CHANGED
@@ -526,6 +526,7 @@
526
526
  <a href="#/settings" data-view="settings" class="tab">Settings</a>
527
527
  <a href="#/resources" data-view="resources" class="tab">Resources</a>
528
528
  <a href="#/recall" data-view="recall" class="tab">Recall</a>
529
+ <a href="#/codegraph" data-view="codegraph" class="tab">Code graph</a>
529
530
  <a href="#/packs" data-view="packs" class="tab">Packs</a>
530
531
  </nav>
531
532
  <div class="nav">
@@ -873,6 +874,19 @@
873
874
  <div id="recallList"></div>
874
875
  </section>
875
876
 
877
+ <section id="view-codegraph" style="display:none">
878
+ <div class="page-head" style="display:flex;align-items:flex-start;gap:16px">
879
+ <div style="flex:1">
880
+ <h1>Code graph</h1>
881
+ <p>Structural memory of your codebase - a knowledge graph of symbols, call chains, and routes. Enabling it registers the code-intelligence server in your agents so they can query the graph over MCP. Complements <a href="#/recall">Recall</a> (session memory).</p>
882
+ </div>
883
+ <button id="cgToggle" type="button" class="toggle" role="switch" aria-checked="false" aria-label="Codebase memory">off</button>
884
+ <button id="cgRefresh" type="button" class="iconbtn" title="Refresh" aria-label="Refresh"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M23 4v6h-6"/><path d="M1 20v-6h6"/><path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"/></svg></button>
885
+ </div>
886
+ <div id="cgNote" class="set-note"></div>
887
+ <div id="cgBody"><div class="empty" style="padding:12px 0">Loading...</div></div>
888
+ </section>
889
+
876
890
  <footer>
877
891
  Polling pauses automatically when this tab is hidden, and the server caches each snapshot - so an open dashboard costs almost nothing.
878
892
  Data source: <code>~/.enigma/ccr/stats.json</code> + <code>history.jsonl</code> - reset it with <code>enigma compress --clear</code>. Enigma <span id="ver"></span>
@@ -3156,6 +3170,7 @@ export {
3156
3170
  const guard = (sec.guardProtects || []).map((p) => '<span class="tag">' + esc(p) + "</span>").join("");
3157
3171
  el.innerHTML =
3158
3172
  item("Context compression (MCP) · measured", boolPill(s.compress))
3173
+ + item("Codebase memory / code graph (MCP)", boolPill(s.codeGraph))
3159
3174
  + item("Real tool-usage stats · measured", boolPill(s.usageStats))
3160
3175
  + item("Claude Code proxy · experimental", boolPill(s.proxy))
3161
3176
  + item("Live usage windows · real", boolPill(s.usageApi))
@@ -3713,7 +3728,7 @@ export {
3713
3728
 
3714
3729
  // --- hash routing between the subpages ---
3715
3730
  // Single source of truth for the subpage ids (savings is the default/fallback).
3716
- const VIEWS = ["savings", "usage", "accounts", "skills", "settings", "resources", "recall", "packs"];
3731
+ const VIEWS = ["savings", "usage", "accounts", "skills", "settings", "resources", "recall", "codegraph", "packs"];
3717
3732
  // Views that apply to a single project; the rest are global/machine-level only.
3718
3733
  const PROJECT_VIEWS = ["settings", "skills"];
3719
3734
  let activeView = "savings";
@@ -3734,6 +3749,7 @@ export {
3734
3749
  else if (v === "usage") { loadProviderStatus(); if (!usageAccountsLoaded) { usageAccountsLoaded = true; loadUsageAccounts(); } }
3735
3750
  else if (v === "resources") loadResources();
3736
3751
  else if (v === "recall") loadRecall();
3752
+ else if (v === "codegraph") loadCodeGraph();
3737
3753
  else if (v === "packs") loadPacks();
3738
3754
  // The chart was sized while its view may have been hidden; nudge it on return.
3739
3755
  else if (v === "savings") { loadSystems(true); if (chart) { try { applyRange(); } catch { /* not ready */ } } }
@@ -3857,6 +3873,82 @@ export {
3857
3873
  });
3858
3874
  }
3859
3875
 
3876
+ // --- code graph subpage (structural code memory) ---
3877
+ let cgProject = null;
3878
+ // A single architecture/schema field, rendered defensively (shape varies by tool version).
3879
+ function cgItem(x) {
3880
+ if (x && typeof x === "object") return String(x.name || x.label || x.path || x.id || JSON.stringify(x)).slice(0, 60);
3881
+ return String(x);
3882
+ }
3883
+ function cgVal(v) {
3884
+ if (Array.isArray(v)) {
3885
+ if (!v.length) return '<span class="tag">none</span>';
3886
+ return v.slice(0, 12).map((x) => '<span class="tag">' + esc(cgItem(x)) + "</span>").join(" ")
3887
+ + (v.length > 12 ? ' <span class="set-hint">+' + (v.length - 12) + " more</span>" : "");
3888
+ }
3889
+ if (v && typeof v === "object") return '<span class="set-hint">' + esc(Object.keys(v).length + " field(s)") + "</span>";
3890
+ return esc(String(v));
3891
+ }
3892
+ function cgSection(title, obj) {
3893
+ const entries = obj && typeof obj === "object" ? Object.entries(obj) : [];
3894
+ if (!entries.length) return "";
3895
+ return '<div class="panel"><div class="set-cat-h">' + esc(title) + "</div>"
3896
+ + entries.map(([k, v]) => '<div class="set-row"><div class="set-meta"><div class="set-name">' + esc(k) + '</div><div class="set-hint">' + cgVal(v) + "</div></div></div>").join("")
3897
+ + "</div>";
3898
+ }
3899
+ function renderCodeGraph(view) {
3900
+ view = view || {};
3901
+ const on = !!view.enabled;
3902
+ const t = $("cgToggle");
3903
+ t.classList.toggle("on", on); t.setAttribute("aria-checked", on ? "true" : "false"); t.textContent = on ? "on" : "off";
3904
+ if (!view.available) {
3905
+ $("cgBody").innerHTML = '<div class="panel"><div class="set-cat-h">Not installed</div>'
3906
+ + '<div class="set-hint" style="padding:6px 0">The code-intelligence engine is not available yet. When enabled, your agent fetches it automatically on first use (needs <code>npx</code> on PATH). Then tell your agent "Index this project".</div></div>';
3907
+ return;
3908
+ }
3909
+ const projects = view.projects || [];
3910
+ const projHtml = projects.length
3911
+ ? projects.map((p) => {
3912
+ const name = p.name || p.root || "";
3913
+ const sel = name === view.selected;
3914
+ return '<div class="set-row"><div class="set-meta"><div class="set-name">' + esc(name)
3915
+ + (sel ? ' <span class="tag" style="color:var(--ok)">viewing</span>' : "")
3916
+ + '</div><div class="set-hint">' + esc(p.root || "") + '</div></div>'
3917
+ + '<div class="set-ctl"><button type="button" class="toggle' + (sel ? " on" : "") + '" data-cg-project="' + esc(name) + '">View</button></div></div>';
3918
+ }).join("")
3919
+ : '<div class="empty" style="padding:12px 0">No projects indexed yet. In your agent, say "Index this project".</div>';
3920
+ let html = '<div class="panel"><div class="set-cat-h">Indexed projects <small>' + projects.length + "</small></div>" + projHtml + "</div>";
3921
+ if (view.architecture) html += cgSection("Architecture" + (view.selected ? " - " + view.selected : ""), view.architecture);
3922
+ if (view.schema) html += cgSection("Graph schema", view.schema);
3923
+ $("cgBody").innerHTML = html;
3924
+ }
3925
+ async function loadCodeGraph() {
3926
+ $("cgNote").textContent = "";
3927
+ try {
3928
+ const q = cgProject ? "?project=" + encodeURIComponent(cgProject) : "";
3929
+ renderCodeGraph(await (await fetch("/api/codegraph" + q, { cache: "no-store" })).json());
3930
+ } catch { $("cgBody").innerHTML = '<div class="empty" style="padding:12px 0">Code graph unavailable.</div>'; }
3931
+ }
3932
+ async function cgToggleAction() {
3933
+ const turnOn = $("cgToggle").getAttribute("aria-checked") !== "true";
3934
+ setNote("cgNote", "Saving...");
3935
+ try {
3936
+ const out = await (await fetch("/api/codegraph", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ op: "toggle", on: turnOn, project: cgProject || undefined }) })).json();
3937
+ if (out.view) renderCodeGraph(out.view);
3938
+ setNote("cgNote", out.ok ? (turnOn ? "Codebase memory enabled - registered in your agents (restart them to load it)." : "Codebase memory disabled.") : (out.error || "Action failed."), out.ok ? "ok" : "err");
3939
+ } catch { setNote("cgNote", "Could not reach the server.", "err"); }
3940
+ }
3941
+ function wireCodeGraph() {
3942
+ $("cgRefresh").addEventListener("click", loadCodeGraph);
3943
+ $("cgToggle").addEventListener("click", cgToggleAction);
3944
+ $("view-codegraph").addEventListener("click", (e) => {
3945
+ const b = e.target.closest("button[data-cg-project]");
3946
+ if (!b) return;
3947
+ cgProject = b.dataset.cgProject;
3948
+ loadCodeGraph();
3949
+ });
3950
+ }
3951
+
3860
3952
  // --- recall subpage (local session memory; reads your own transcripts) ---
3861
3953
  const RECALL_TYPES = ["bugfix", "feature", "refactor", "change", "discovery", "decision", "security"];
3862
3954
  let recallProjSig = "";
@@ -4425,6 +4517,7 @@ export {
4425
4517
  wireUsage();
4426
4518
  wireResources();
4427
4519
  wireRecall();
4520
+ wireCodeGraph();
4428
4521
  wirePacks();
4429
4522
  wireScope();
4430
4523
  enhanceAll(); // upgrade every static <select> into the shared pretty dropdown
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@enigmax/dashboard",
3
- "version": "0.1.69",
3
+ "version": "0.1.70",
4
4
  "description": "Local browser dashboard UI for enigma: the static page and chart assets enigma serves on its loopback dashboard (savings, real tool usage, in-browser settings). Installed on demand by enigma-cli; not a runtime dependency.",
5
5
  "type": "module",
6
6
  "files": [