@kage-core/kage-graph-mcp 1.1.38 → 1.3.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kage-core/kage-graph-mcp",
3
- "version": "1.1.38",
3
+ "version": "1.3.0",
4
4
  "description": "Local-first repo memory, code graph, and recall MCP server for coding agents",
5
5
  "main": "dist/index.js",
6
6
  "files": [
package/viewer/app.js CHANGED
@@ -47,7 +47,9 @@
47
47
  lifecycle: null,
48
48
  timeline: null,
49
49
  lineage: null,
50
- setup: null
50
+ setup: null,
51
+ trust: null,
52
+ suppressed: null
51
53
  },
52
54
  pendingPackets: [],
53
55
  reviewText: "",
@@ -183,6 +185,8 @@
183
185
  edgeCount: document.getElementById("edgeCount"),
184
186
  reviewCount: document.getElementById("reviewCount"),
185
187
  dashboardStats: document.getElementById("dashboardStats"),
188
+ trustHero: document.getElementById("trustHero"),
189
+ suppressionShelf: document.getElementById("suppressionShelf"),
186
190
  repoXray: document.getElementById("repoXray"),
187
191
  repoXrayStatus: document.getElementById("repoXrayStatus"),
188
192
  repoXrayScript: document.getElementById("repoXrayScript"),
@@ -233,8 +237,8 @@
233
237
  var PAGE_META = {
234
238
  overview: {
235
239
  eyebrow: "kage://overview",
236
- title: "Repo dashboard",
237
- summary: "What is safe to change next, what needs attention, and what is ready to hand off."
240
+ title: "Repository overview",
241
+ summary: "Whether this repo's agent memory can be trusted, what needs review, and what's ready to hand off."
238
242
  },
239
243
  graph: {
240
244
  eyebrow: "kage://graph",
@@ -622,6 +626,8 @@
622
626
  var timelinePath = params.get("timeline") || params.get("memoryTimeline") || params.get("memory-timeline");
623
627
  var lineagePath = params.get("lineage") || params.get("memoryLineage") || params.get("memory-lineage");
624
628
  var setupPath = params.get("setup") || params.get("setupDoctor") || params.get("setup-doctor");
629
+ var trustPath = params.get("trust") || params.get("trustBenchmark") || params.get("trust-benchmark");
630
+ var suppressedPath = params.get("suppressed") || params.get("suppressedMemory") || params.get("suppressed-memory");
625
631
  var inferredRoot = inferMemoryRoot(graphPaths[0] || "");
626
632
  if (!inboxPath && inferredRoot) inboxPath = inferredRoot + "/inbox.json";
627
633
  if (!reviewPath && inferredRoot) reviewPath = inferredRoot + "/review/memory-review.md";
@@ -648,6 +654,8 @@
648
654
  if (!timelinePath) timelinePath = inferredRoot + "/reports/timeline.json";
649
655
  if (!lineagePath) lineagePath = inferredRoot + "/reports/lineage.json";
650
656
  if (!setupPath) setupPath = inferredRoot + "/reports/setup.json";
657
+ if (!trustPath) trustPath = inferredRoot + "/reports/trust.json";
658
+ if (!suppressedPath) suppressedPath = inferredRoot + "/reports/suppressed.json";
651
659
  }
652
660
  var jobs = [];
653
661
  if (metricsPath) jobs.push(fetchJson(metricsPath).then(function (metrics) { state.metrics = metrics; }));
@@ -675,6 +683,8 @@
675
683
  if (timelinePath) jobs.push(fetchJson(timelinePath).then(function (report) { state.reports.timeline = report; }).catch(function () { state.reports.timeline = null; }));
676
684
  if (lineagePath) jobs.push(fetchJson(lineagePath).then(function (report) { state.reports.lineage = report; }).catch(function () { state.reports.lineage = null; }));
677
685
  if (setupPath) jobs.push(fetchJson(setupPath).then(function (report) { state.reports.setup = report; }).catch(function () { state.reports.setup = null; }));
686
+ if (trustPath) jobs.push(fetchJson(trustPath).then(function (report) { state.reports.trust = report; }).catch(function () { state.reports.trust = null; }));
687
+ if (suppressedPath) jobs.push(fetchJson(suppressedPath).then(function (report) { state.reports.suppressed = report; }).catch(function () { state.reports.suppressed = null; }));
678
688
  if (!graphPaths.length && !jobs.length) {
679
689
  loadHostedDefault();
680
690
  return;
@@ -723,7 +733,9 @@
723
733
  fetchJson("./data/kage/reports/lifecycle.json").catch(function () { return null; }),
724
734
  fetchJson("./data/kage/reports/timeline.json").catch(function () { return null; }),
725
735
  fetchJson("./data/kage/reports/lineage.json").catch(function () { return null; }),
726
- fetchJson("./data/kage/reports/setup.json").catch(function () { return null; })
736
+ fetchJson("./data/kage/reports/setup.json").catch(function () { return null; }),
737
+ fetchJson("./data/kage/reports/trust.json").catch(function () { return null; }),
738
+ fetchJson("./data/kage/reports/suppressed.json").catch(function () { return null; })
727
739
  ]).then(function (items) {
728
740
  var merged = mergeNormalizedGraphs([normalizeGraph(items[0]), normalizeGraph(items[1])]);
729
741
  state.metrics = items[2];
@@ -747,6 +759,8 @@
747
759
  state.reports.timeline = items[20];
748
760
  state.reports.lineage = items[21];
749
761
  state.reports.setup = items[22];
762
+ state.reports.trust = items[23];
763
+ state.reports.suppressed = items[24];
750
764
  loadNormalizedGraph(merged, "Kage repo graph");
751
765
  setAutoLoad("Kage repo graph loaded", true);
752
766
  }).catch(function () {
@@ -2983,6 +2997,75 @@
2983
2997
  ].forEach(function (card) { els.debugOverview.appendChild(card); });
2984
2998
  }
2985
2999
 
3000
+ function renderTrustHero(trust) {
3001
+ if (!els.trustHero) return;
3002
+ var metrics = (trust && trust.metrics) || {};
3003
+ var score = trust && typeof trust.trust_score === "number" ? trust.trust_score : null;
3004
+ var status = score == null ? "idle" : (score >= 90 ? "ok" : score >= 70 ? "warn" : "alert");
3005
+ var bars = [
3006
+ ["Hallucinated citations rejected", metrics.hallucinated_citation_rejection_rate],
3007
+ ["Stale memory excluded from recall", metrics.stale_memory_exclusion_rate],
3008
+ ["Live memory grounded to code", metrics.live_grounding_rate]
3009
+ ];
3010
+ var note = score == null
3011
+ ? "Run <code>kage benchmark --trust</code> to score this repo."
3012
+ : (status === "ok"
3013
+ ? "Verified — agents recall only memory that is grounded and current."
3014
+ : "Some memory needs review before agents should trust it.");
3015
+ var barsHtml = bars.map(function (bar) {
3016
+ var raw = bar[1];
3017
+ var has = !(raw === null || raw === undefined || isNaN(raw));
3018
+ var pct = has ? Math.max(0, Math.min(100, Number(raw))) : 0;
3019
+ return '<div class="trust-bar">'
3020
+ + '<span class="trust-bar-label"></span>'
3021
+ + '<span class="trust-bar-track"><i style="width:' + pct + '%"></i></span>'
3022
+ + '<b class="trust-bar-value">' + (has ? pct + "%" : "—") + '</b>'
3023
+ + '</div>';
3024
+ }).join("");
3025
+ els.trustHero.setAttribute("data-status", status);
3026
+ els.trustHero.innerHTML =
3027
+ '<div class="trust-hero-score">'
3028
+ + '<span class="trust-hero-eyebrow">Memory Trust</span>'
3029
+ + '<div class="trust-hero-number"><strong>' + (score == null ? "—" : score) + '</strong><span>/100</span></div>'
3030
+ + '<p class="trust-hero-note">' + note + '</p>'
3031
+ + '</div>'
3032
+ + '<div class="trust-hero-bars">' + barsHtml + '</div>';
3033
+ // Set labels via textContent to avoid any HTML injection from labels.
3034
+ var labelEls = els.trustHero.querySelectorAll(".trust-bar-label");
3035
+ for (var i = 0; i < labelEls.length; i += 1) labelEls[i].textContent = bars[i][0];
3036
+ }
3037
+
3038
+ function renderSuppressionShelf(report) {
3039
+ if (!els.suppressionShelf) return;
3040
+ var items = (report && report.items) || [];
3041
+ if (!items.length) {
3042
+ els.suppressionShelf.innerHTML = "";
3043
+ els.suppressionShelf.hidden = true;
3044
+ return;
3045
+ }
3046
+ els.suppressionShelf.hidden = false;
3047
+ var plural = items.length === 1 ? "memory is" : "memories are";
3048
+ els.suppressionShelf.innerHTML =
3049
+ '<div class="suppression-head">'
3050
+ + '<div><span class="suppression-eyebrow">Withheld from recall</span>'
3051
+ + '<h2>' + items.length + ' ' + plural + ' being withheld from your agents</h2></div>'
3052
+ + '<strong class="suppression-flag">Needs review</strong>'
3053
+ + '</div>'
3054
+ + '<p class="suppression-sub">Recall is hiding these because their cited evidence was deleted or expired. Verify, update, or supersede each before agents should trust them.</p>'
3055
+ + '<div class="suppression-list">'
3056
+ + items.slice(0, 8).map(function () {
3057
+ return '<div class="suppression-item"><span class="suppression-item-title"></span><span class="suppression-item-reason"></span></div>';
3058
+ }).join("")
3059
+ + '</div>'
3060
+ + (items.length > 8 ? '<p class="suppression-more">+ ' + (items.length - 8) + ' more — run <code>kage suppressed</code></p>' : '');
3061
+ var titleEls = els.suppressionShelf.querySelectorAll(".suppression-item-title");
3062
+ var reasonEls = els.suppressionShelf.querySelectorAll(".suppression-item-reason");
3063
+ for (var i = 0; i < titleEls.length; i += 1) {
3064
+ titleEls[i].textContent = items[i].title;
3065
+ reasonEls[i].textContent = items[i].reason;
3066
+ }
3067
+ }
3068
+
2986
3069
  function renderDashboard() {
2987
3070
  if (!els.dashboardStats) return;
2988
3071
  var metrics = state.metrics || {};
@@ -3009,6 +3092,8 @@
3009
3092
  var readiness = handoff || dashboardReadiness(metrics, pendingReview, staleFlags, duplicateFlags, missingContext);
3010
3093
  var memoryCoverage = dashboardMemoryCoverage(reports, memoryCodeEdges, memoryGraph, memoryNodes);
3011
3094
  var riskHealth = riskTargets.length || hotspots ? (riskTargets.length + hotspots) + " checks" : "No flags";
3095
+ renderTrustHero(reports.trust);
3096
+ renderSuppressionShelf(reports.suppressed);
3012
3097
  var statRows = [
3013
3098
  ["Handoff", readiness.label, readiness.detail, readiness.status],
3014
3099
  ["Memory", memoryCoverage.label, memoryCoverage.detail, memoryCoverage.status],
@@ -3547,6 +3632,10 @@
3547
3632
  return state.entities.filter(function (entity) { return entity.type === type; }).length;
3548
3633
  }
3549
3634
 
3635
+ function numOrDash(value) {
3636
+ return (value === null || value === undefined || isNaN(value)) ? "—" : value;
3637
+ }
3638
+
3550
3639
  function formatDashboardValue(value) {
3551
3640
  if (typeof value === "number" && Number.isFinite(value)) return value.toLocaleString();
3552
3641
  return String(value == null ? "n/a" : value);
package/viewer/index.html CHANGED
@@ -4,7 +4,7 @@
4
4
  <meta charset="utf-8">
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1">
6
6
  <title>Kage viewer</title>
7
- <link rel="stylesheet" href="./styles.css?v=37">
7
+ <link rel="stylesheet" href="./styles.css?v=39">
8
8
  </head>
9
9
  <body>
10
10
  <div class="viewer-shell">
@@ -54,16 +54,18 @@
54
54
 
55
55
  <main class="layout">
56
56
  <section class="dashboard-panel" aria-label="Kage repo dashboard">
57
+ <div id="trustHero" class="trust-hero" aria-label="Memory trust score"></div>
58
+ <div id="suppressionShelf" class="suppression-shelf" aria-label="Memory withheld from recall" hidden></div>
57
59
  <div id="dashboardStats" class="dashboard-stats" aria-label="Repo dashboard stats"></div>
58
- <section id="repoXray" class="repo-xray" aria-label="Repo X-Ray">
60
+ <section id="repoXray" class="repo-xray" aria-label="Repository map">
59
61
  <div class="repo-xray-head">
60
62
  <div>
61
- <span>Repo X-Ray</span>
62
- <h2>Show me what you understand</h2>
63
+ <span>Repository map</span>
64
+ <h2>What Kage understands about this repo</h2>
63
65
  </div>
64
66
  <strong id="repoXrayStatus">waiting</strong>
65
67
  </div>
66
- <p id="repoXrayScript">Kage will map entry points, core files, risk, tests, and memory overlays when the X-Ray report is loaded.</p>
68
+ <p id="repoXrayScript">Entry points, core modules, change risk, tests, and memory overlays mapped from the code graph.</p>
67
69
  <div id="repoXrayLayers" class="repo-xray-layers"></div>
68
70
  </section>
69
71
  <div id="dashboardCharts" class="dashboard-charts" aria-label="Repo health charts"></div>
@@ -302,6 +304,6 @@
302
304
  </section>
303
305
  </div>
304
306
 
305
- <script src="./app.js?v=50"></script>
307
+ <script src="./app.js?v=53"></script>
306
308
  </body>
307
309
  </html>
package/viewer/styles.css CHANGED
@@ -424,6 +424,103 @@ body.viewer-page-data .graph-panel {
424
424
  white-space: nowrap;
425
425
  }
426
426
 
427
+ .trust-hero {
428
+ display: grid;
429
+ grid-template-columns: minmax(170px, 250px) minmax(0, 1fr);
430
+ gap: 30px;
431
+ align-items: center;
432
+ margin-bottom: 18px;
433
+ padding: 24px 28px;
434
+ border: 1px solid rgba(65, 255, 143, 0.24);
435
+ border-radius: 14px;
436
+ background:
437
+ radial-gradient(circle at 0 0, rgba(65, 255, 143, 0.12), transparent 380px),
438
+ linear-gradient(135deg, rgba(8, 17, 13, 0.96), rgba(10, 16, 22, 0.82));
439
+ box-shadow: var(--shadow);
440
+ }
441
+ .trust-hero[data-status="warn"] { border-color: rgba(255, 209, 102, 0.34); }
442
+ .trust-hero[data-status="alert"] { border-color: rgba(255, 107, 107, 0.38); }
443
+ .trust-hero[data-status="idle"] { border-color: var(--line); }
444
+ .trust-hero-eyebrow {
445
+ display: block;
446
+ color: var(--terminal-dim);
447
+ font-size: 11px;
448
+ font-weight: 800;
449
+ letter-spacing: 0.14em;
450
+ text-transform: uppercase;
451
+ }
452
+ .trust-hero-number { display: flex; align-items: baseline; gap: 5px; margin-top: 8px; }
453
+ .trust-hero-number strong {
454
+ font-size: 60px;
455
+ line-height: 1;
456
+ color: var(--terminal-strong);
457
+ text-shadow: 0 0 24px rgba(65, 255, 143, 0.38);
458
+ }
459
+ .trust-hero[data-status="warn"] .trust-hero-number strong { color: var(--warn); text-shadow: 0 0 24px rgba(255, 209, 102, 0.32); }
460
+ .trust-hero[data-status="alert"] .trust-hero-number strong { color: var(--danger); text-shadow: 0 0 24px rgba(255, 107, 107, 0.32); }
461
+ .trust-hero[data-status="idle"] .trust-hero-number strong { color: var(--muted); text-shadow: none; }
462
+ .trust-hero-number span { font-size: 19px; color: var(--muted); }
463
+ .trust-hero-note { margin: 12px 0 0; color: var(--muted); font-size: 12.5px; line-height: 1.45; max-width: 250px; }
464
+ .trust-hero-note code { color: var(--accent); background: var(--accent-soft); padding: 1px 6px; border-radius: 5px; font-size: 11.5px; }
465
+ .trust-hero-bars { display: grid; gap: 15px; }
466
+ .trust-bar {
467
+ display: grid;
468
+ grid-template-columns: minmax(0, 1fr) 52px;
469
+ grid-template-areas: "label value" "track value";
470
+ align-items: center;
471
+ gap: 4px 14px;
472
+ }
473
+ .trust-bar-label { grid-area: label; color: var(--text); font-size: 13px; }
474
+ .trust-bar-track { grid-area: track; height: 8px; border-radius: 999px; background: rgba(215, 249, 223, 0.07); overflow: hidden; }
475
+ .trust-bar-track i { display: block; height: 100%; border-radius: 999px; background: linear-gradient(90deg, var(--brand), var(--accent)); box-shadow: 0 0 12px rgba(65, 255, 143, 0.3); transition: width 0.5s ease; }
476
+ .trust-bar-value { grid-area: value; text-align: right; color: var(--terminal-strong); font-size: 15px; font-weight: 800; }
477
+ @media (max-width: 860px) {
478
+ .trust-hero { grid-template-columns: 1fr; gap: 20px; }
479
+ .trust-hero-note { max-width: none; }
480
+ }
481
+
482
+ .suppression-shelf {
483
+ margin-bottom: 18px;
484
+ padding: 20px 24px;
485
+ border: 1px solid rgba(255, 209, 102, 0.34);
486
+ border-radius: 14px;
487
+ background:
488
+ radial-gradient(circle at 0 0, rgba(255, 209, 102, 0.08), transparent 360px),
489
+ linear-gradient(135deg, rgba(24, 18, 8, 0.92), rgba(16, 14, 10, 0.82));
490
+ }
491
+ .suppression-shelf[hidden] { display: none; }
492
+ .suppression-head { display: flex; align-items: start; justify-content: space-between; gap: 16px; }
493
+ .suppression-eyebrow { display: block; color: var(--warn); font-size: 11px; font-weight: 800; letter-spacing: 0.14em; text-transform: uppercase; }
494
+ .suppression-head h2 { margin-top: 5px; color: var(--text); text-shadow: none; font-size: 17px; }
495
+ .suppression-flag {
496
+ flex: none;
497
+ color: var(--warn);
498
+ font-size: 11px;
499
+ font-weight: 800;
500
+ text-transform: uppercase;
501
+ letter-spacing: 0.08em;
502
+ padding: 5px 10px;
503
+ border: 1px solid rgba(255, 209, 102, 0.4);
504
+ border-radius: 999px;
505
+ background: var(--warn-soft);
506
+ }
507
+ .suppression-sub { margin: 8px 0 14px; color: var(--muted); font-size: 12.5px; line-height: 1.45; max-width: 760px; }
508
+ .suppression-list { display: grid; gap: 8px; }
509
+ .suppression-item {
510
+ display: grid;
511
+ grid-template-columns: minmax(0, 1fr) auto;
512
+ align-items: center;
513
+ gap: 16px;
514
+ padding: 11px 14px;
515
+ border: 1px solid rgba(255, 209, 102, 0.16);
516
+ border-radius: 9px;
517
+ background: rgba(8, 17, 13, 0.5);
518
+ }
519
+ .suppression-item-title { color: var(--text); font-size: 13.5px; font-weight: 650; overflow-wrap: anywhere; }
520
+ .suppression-item-reason { color: var(--warn); font-size: 12px; text-align: right; opacity: 0.92; }
521
+ .suppression-more { margin: 12px 0 0; color: var(--muted); font-size: 12px; }
522
+ .suppression-more code { color: var(--accent); background: var(--accent-soft); padding: 1px 5px; border-radius: 5px; }
523
+
427
524
  .dashboard-stats {
428
525
  display: grid;
429
526
  grid-template-columns: repeat(3, minmax(0, 1fr));
@@ -431,8 +528,8 @@ body.viewer-page-data .graph-panel {
431
528
  margin-bottom: 22px;
432
529
  }
433
530
  .dashboard-stat {
434
- min-height: 142px;
435
- padding: 22px;
531
+ min-height: 120px;
532
+ padding: 20px 22px;
436
533
  border: 1px solid rgba(65, 255, 143, 0.12);
437
534
  border-radius: 10px;
438
535
  background: rgba(8, 17, 13, 0.82);