@kage-core/kage-graph-mcp 1.1.37 → 1.2.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.37",
3
+ "version": "1.2.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
@@ -37,6 +37,7 @@
37
37
  risk: null,
38
38
  moduleHealth: null,
39
39
  graphInsights: null,
40
+ xray: null,
40
41
  workspace: null,
41
42
  sessions: null,
42
43
  replay: null,
@@ -182,6 +183,10 @@
182
183
  edgeCount: document.getElementById("edgeCount"),
183
184
  reviewCount: document.getElementById("reviewCount"),
184
185
  dashboardStats: document.getElementById("dashboardStats"),
186
+ repoXray: document.getElementById("repoXray"),
187
+ repoXrayStatus: document.getElementById("repoXrayStatus"),
188
+ repoXrayScript: document.getElementById("repoXrayScript"),
189
+ repoXrayLayers: document.getElementById("repoXrayLayers"),
185
190
  dashboardCharts: document.getElementById("dashboardCharts"),
186
191
  memoryStatus: document.getElementById("memoryStatus"),
187
192
  memoryStats: document.getElementById("memoryStats"),
@@ -242,9 +247,9 @@
242
247
  summary: "Find repo lore by file, feature, bug, command, or decision. Pick a packet to see linked code."
243
248
  },
244
249
  intel: {
245
- eyebrow: "kage://risks",
246
- title: "Risks",
247
- summary: "Files, owners, and modules to inspect before changes. Each card links into the graph."
250
+ eyebrow: "kage://before-edit",
251
+ title: "Before You Edit",
252
+ summary: "What can go wrong, why it matters, and the first safety step before an agent changes code."
248
253
  },
249
254
  review: {
250
255
  eyebrow: "kage://review",
@@ -606,6 +611,7 @@
606
611
  var riskPath = params.get("risk");
607
612
  var moduleHealthPath = params.get("moduleHealth") || params.get("module-health");
608
613
  var graphInsightsPath = params.get("graphInsights") || params.get("graph-insights");
614
+ var xrayPath = params.get("xray") || params.get("repoXray") || params.get("repo-xray");
609
615
  var workspacePath = params.get("workspace");
610
616
  var sessionsPath = params.get("sessions");
611
617
  var replayPath = params.get("replay") || params.get("sessionReplay") || params.get("session-replay");
@@ -631,6 +637,7 @@
631
637
  if (!riskPath) riskPath = inferredRoot + "/reports/risk.json";
632
638
  if (!moduleHealthPath) moduleHealthPath = inferredRoot + "/reports/module-health.json";
633
639
  if (!graphInsightsPath) graphInsightsPath = inferredRoot + "/reports/graph-insights.json";
640
+ if (!xrayPath) xrayPath = inferredRoot + "/reports/xray.json";
634
641
  if (!workspacePath) workspacePath = inferredRoot + "/reports/workspace.json";
635
642
  if (!sessionsPath) sessionsPath = inferredRoot + "/reports/sessions.json";
636
643
  if (!replayPath) replayPath = inferredRoot + "/reports/replay.json";
@@ -657,6 +664,7 @@
657
664
  if (riskPath) jobs.push(fetchJson(riskPath).then(function (report) { state.reports.risk = report; }).catch(function () { state.reports.risk = null; }));
658
665
  if (moduleHealthPath) jobs.push(fetchJson(moduleHealthPath).then(function (report) { state.reports.moduleHealth = report; }).catch(function () { state.reports.moduleHealth = null; }));
659
666
  if (graphInsightsPath) jobs.push(fetchJson(graphInsightsPath).then(function (report) { state.reports.graphInsights = report; }).catch(function () { state.reports.graphInsights = null; }));
667
+ if (xrayPath) jobs.push(fetchJson(xrayPath).then(function (report) { state.reports.xray = report; }).catch(function () { state.reports.xray = null; }));
660
668
  if (workspacePath) jobs.push(fetchJson(workspacePath).then(function (report) { state.reports.workspace = report; }).catch(function () { state.reports.workspace = null; }));
661
669
  if (sessionsPath) jobs.push(fetchJson(sessionsPath).then(function (report) { state.reports.sessions = report; }).catch(function () { state.reports.sessions = null; }));
662
670
  if (replayPath) jobs.push(fetchJson(replayPath).then(function (report) { state.reports.replay = report; }).catch(function () { state.reports.replay = null; }));
@@ -705,6 +713,7 @@
705
713
  fetchJson("./data/kage/reports/decisions.json").catch(function () { return null; }),
706
714
  fetchJson("./data/kage/reports/module-health.json").catch(function () { return null; }),
707
715
  fetchJson("./data/kage/reports/graph-insights.json").catch(function () { return null; }),
716
+ fetchJson("./data/kage/reports/xray.json").catch(function () { return null; }),
708
717
  fetchJson("./data/kage/reports/workspace.json").catch(function () { return null; }),
709
718
  fetchJson("./data/kage/reports/sessions.json").catch(function () { return null; }),
710
719
  fetchJson("./data/kage/reports/replay.json").catch(function () { return null; }),
@@ -727,16 +736,17 @@
727
736
  state.reports.decisions = items[9];
728
737
  state.reports.moduleHealth = items[10];
729
738
  state.reports.graphInsights = items[11];
730
- state.reports.workspace = items[12];
731
- state.reports.sessions = items[13];
732
- state.reports.replay = items[14];
733
- state.reports.memoryAccess = items[15];
734
- state.reports.memoryAudit = items[16];
735
- state.reports.handoff = items[17];
736
- state.reports.lifecycle = items[18];
737
- state.reports.timeline = items[19];
738
- state.reports.lineage = items[20];
739
- state.reports.setup = items[21];
739
+ state.reports.xray = items[12];
740
+ state.reports.workspace = items[13];
741
+ state.reports.sessions = items[14];
742
+ state.reports.replay = items[15];
743
+ state.reports.memoryAccess = items[16];
744
+ state.reports.memoryAudit = items[17];
745
+ state.reports.handoff = items[18];
746
+ state.reports.lifecycle = items[19];
747
+ state.reports.timeline = items[20];
748
+ state.reports.lineage = items[21];
749
+ state.reports.setup = items[22];
740
750
  loadNormalizedGraph(merged, "Kage repo graph");
741
751
  setAutoLoad("Kage repo graph loaded", true);
742
752
  }).catch(function () {
@@ -2987,7 +2997,7 @@
2987
2997
  var reports = state.reports || {};
2988
2998
  var reportCount = Object.keys(reports).filter(function (key) { return reports[key]; }).length;
2989
2999
  var risk = reports.risk || {};
2990
- var riskTargets = Array.isArray(risk.targets) ? risk.targets : Object.keys(risk.targets || {});
3000
+ var riskTargets = userFacingRiskTargets(risk);
2991
3001
  var inboxCounts = state.inbox && state.inbox.counts ? state.inbox.counts : {};
2992
3002
  var handoff = memoryHandoffSummary(reports.handoff);
2993
3003
  var pendingReview = Number(firstNumber(inboxCounts.pending, memoryGraph.pending_packets, (state.pendingPackets || []).length, 0));
@@ -2995,14 +3005,14 @@
2995
3005
  var duplicateFlags = Number(firstNumber(inboxCounts.duplicates, memoryGraph.duplicate_candidate_pairs, 0));
2996
3006
  var missingContext = Number(firstNumber(inboxCounts.missing_context, 0));
2997
3007
  var ownerSilos = Array.isArray(risk.ownership_silos) ? risk.ownership_silos.length : 0;
2998
- var hotspots = Array.isArray(risk.global_hotspots) ? risk.global_hotspots.length : 0;
3008
+ var hotspots = userFacingRiskHotspots(risk).length;
2999
3009
  var readiness = handoff || dashboardReadiness(metrics, pendingReview, staleFlags, duplicateFlags, missingContext);
3000
3010
  var memoryCoverage = dashboardMemoryCoverage(reports, memoryCodeEdges, memoryGraph, memoryNodes);
3001
- var riskHealth = riskTargets.length || hotspots ? (riskTargets.length + hotspots) + " signals" : "No flags";
3011
+ var riskHealth = riskTargets.length || hotspots ? (riskTargets.length + hotspots) + " checks" : "No flags";
3002
3012
  var statRows = [
3003
3013
  ["Handoff", readiness.label, readiness.detail, readiness.status],
3004
3014
  ["Memory", memoryCoverage.label, memoryCoverage.detail, memoryCoverage.status],
3005
- ["Risk", riskHealth, riskTargets.length + " targets, " + ownerSilos + " ownership silos", riskTargets.length || ownerSilos || hotspots ? "warn" : "ok"]
3015
+ ["Before edit", riskHealth, riskTargets.length + " edit areas, " + ownerSilos + " ownership silos", riskTargets.length || ownerSilos || hotspots ? "warn" : "ok"]
3006
3016
  ];
3007
3017
  els.dashboardStats.textContent = "";
3008
3018
  statRows.forEach(function (row) {
@@ -3026,7 +3036,7 @@
3026
3036
  ["Coverage", codeGraph.indexer_coverage_percent != null ? codeGraph.indexer_coverage_percent + "%" : "not loaded"]
3027
3037
  ]);
3028
3038
  setDashboardRows("dashboardIntel", [
3029
- ["Risk targets", riskTargets.length || "none"],
3039
+ ["Edit checks", riskTargets.length || "none"],
3030
3040
  ["Ownership silos", ownerSilos || "none"],
3031
3041
  ["Decision coverage", reports.decisions && reports.decisions.coverage_percent != null ? reports.decisions.coverage_percent + "%" : "not loaded"]
3032
3042
  ]);
@@ -3042,6 +3052,7 @@
3042
3052
  ["Stale / duplicate", staleFlags + " / " + duplicateFlags],
3043
3053
  ["Missing context", missingContext || "none"]
3044
3054
  ]);
3055
+ renderRepoXray(reports.xray);
3045
3056
  renderDashboardCharts({
3046
3057
  metrics: metrics,
3047
3058
  reports: reports,
@@ -3061,6 +3072,61 @@
3061
3072
  });
3062
3073
  }
3063
3074
 
3075
+ function renderRepoXray(report) {
3076
+ if (!els.repoXray || !els.repoXrayLayers) return;
3077
+ if (!report || !Array.isArray(report.layers)) {
3078
+ if (els.repoXrayStatus) els.repoXrayStatus.textContent = "not loaded";
3079
+ if (els.repoXrayScript) els.repoXrayScript.textContent = "Open with `kage viewer --project <repo>` after refresh to see the first-use repo map.";
3080
+ els.repoXrayLayers.textContent = "";
3081
+ return;
3082
+ }
3083
+ var layers = report.layers.filter(function (layer) { return Array.isArray(layer.items) && layer.items.length; });
3084
+ if (els.repoXrayStatus) els.repoXrayStatus.textContent = layers.length ? layers.length + " layers" : "empty";
3085
+ if (els.repoXrayScript) {
3086
+ var script = Array.isArray(report.first_use_script) ? report.first_use_script : [];
3087
+ els.repoXrayScript.textContent = script[0] || report.summary || "Kage mapped the repo structure.";
3088
+ }
3089
+ els.repoXrayLayers.textContent = "";
3090
+ if (!layers.length) {
3091
+ els.repoXrayLayers.className = "repo-xray-layers details-empty";
3092
+ els.repoXrayLayers.textContent = "No X-Ray layers have items yet. Run kage refresh so code graph, risk, tests, and memory links are current.";
3093
+ return;
3094
+ }
3095
+ els.repoXrayLayers.className = "repo-xray-layers";
3096
+ layers.slice(0, 6).forEach(function (layer) {
3097
+ var card = document.createElement("article");
3098
+ card.className = "repo-xray-layer";
3099
+ card.innerHTML = [
3100
+ "<div class=\"repo-xray-layer-head\"><div><strong></strong><span></span></div><em></em></div>",
3101
+ "<div class=\"repo-xray-items\"></div>"
3102
+ ].join("");
3103
+ card.querySelector("strong").textContent = layer.title || layer.id || "Layer";
3104
+ card.querySelector("span").textContent = layer.summary || "";
3105
+ card.querySelector("em").textContent = String(layer.items.length || 0);
3106
+ var list = card.querySelector(".repo-xray-items");
3107
+ layer.items.slice(0, 3).forEach(function (item) {
3108
+ var button = document.createElement("button");
3109
+ button.type = "button";
3110
+ button.className = classNames("repo-xray-item", item.status && "repo-xray-item-" + safeCssName(item.status));
3111
+ button.innerHTML = [
3112
+ "<span><strong></strong><em></em></span>",
3113
+ "<i></i>"
3114
+ ].join("");
3115
+ button.querySelector("strong").textContent = item.label || item.path || "signal";
3116
+ button.querySelector("em").textContent = Array.isArray(item.evidence) && item.evidence.length ? item.evidence.slice(0, 2).join("; ") : item.action || "";
3117
+ button.querySelector("i").style.width = clamp(Number(item.strength || 0), 4, 100) + "%";
3118
+ if (item.path) {
3119
+ button.title = "Focus " + item.path + " in the graph";
3120
+ button.addEventListener("click", function () {
3121
+ focusGraphPath(item.path);
3122
+ });
3123
+ }
3124
+ list.appendChild(button);
3125
+ });
3126
+ els.repoXrayLayers.appendChild(card);
3127
+ });
3128
+ }
3129
+
3064
3130
  function renderDashboardCharts(data) {
3065
3131
  if (!els.dashboardCharts) return;
3066
3132
  var approvedPackets = Number(firstNumber(data.memoryGraph.approved_packets, data.memoryNodes, 0));
@@ -3190,11 +3256,11 @@
3190
3256
  ], blockers ? "Resolve Review before handing work to another agent." : "Memory is clean for handoff.", blockers ? "warn" : "ok"));
3191
3257
  }
3192
3258
  tailCards.push(
3193
- metricBars("Change risk", riskSignals ? riskSignals + " signals" : "none", [
3194
- { label: "Targets", value: data.riskTargets.length, score: Math.min(100, data.riskTargets.length * 18), status: data.riskTargets.length ? "warn" : "ok" },
3259
+ metricBars("Before edit", riskSignals ? riskSignals + " checks" : "none", [
3260
+ { label: "Edit areas", value: data.riskTargets.length, score: Math.min(100, data.riskTargets.length * 18), status: data.riskTargets.length ? "warn" : "ok" },
3195
3261
  { label: "Silos", value: data.ownerSilos, score: Math.min(100, data.ownerSilos * 18), status: data.ownerSilos ? "warn" : "ok" },
3196
3262
  { label: "Hotspots", value: data.hotspots, score: Math.min(100, data.hotspots * 18), status: data.hotspots ? "danger" : "ok" }
3197
- ], riskSignals ? "Open Intel or Owners before editing risky files." : "No loaded risk flags.", riskSignals ? "warn" : "ok")
3263
+ ], riskSignals ? "Open Before Edit and do the listed safety step before changing code." : "No loaded risk flags.", riskSignals ? "warn" : "ok")
3198
3264
  );
3199
3265
  cards.concat(tailCards).slice(0, 3).forEach(function (card) { els.dashboardCharts.appendChild(card); });
3200
3266
  }
@@ -4373,9 +4439,8 @@
4373
4439
  var handoff = state.reports && state.reports.handoff;
4374
4440
  var handoffItems = handoff && Array.isArray(handoff.items) ? handoff.items : [];
4375
4441
  var counts = inbox && inbox.counts ? inbox.counts : {};
4376
- var openCount = reviewOpenCount(counts, packets, inboxItems);
4377
- if (handoff && handoff.totals) openCount = Number(firstNumber(handoff.totals.open_items, openCount));
4378
- els.reviewCount.textContent = String(openCount);
4442
+ var openCount = reviewOpenCount(counts, packets);
4443
+ els.reviewCount.textContent = openCount ? String(openCount) : "clear";
4379
4444
  els.reviewList.textContent = "";
4380
4445
  if (els.reviewOverview) renderReviewOverview(inbox, packets, inboxItems, handoff);
4381
4446
  renderMemoryHandoff(handoff, handoffItems);
@@ -4412,7 +4477,7 @@
4412
4477
  summary.querySelector(".review-summary").textContent = Array.isArray(inbox.recommendations) && inbox.recommendations.length
4413
4478
  ? inbox.recommendations.slice(0, 2).join(" ")
4414
4479
  : "No inbox recommendations.";
4415
- summary.querySelector(".review-risks").textContent = openCount ? "Resolve inbox items before merge" : "Ready for handoff";
4480
+ summary.querySelector(".review-risks").textContent = openCount ? "Inbox needs review before merge" : "Inbox: clear";
4416
4481
  els.reviewList.appendChild(summary);
4417
4482
  }
4418
4483
  inboxItems.slice(0, 8).forEach(function (entry) {
@@ -4466,22 +4531,24 @@
4466
4531
  var stale = Number(firstNumber(counts.stale, 0));
4467
4532
  var duplicates = Number(firstNumber(counts.duplicates, 0));
4468
4533
  var missingContext = Number(firstNumber(counts.missing_context, 0));
4469
- var blockers = reviewOpenCount(counts, packets, inboxItems);
4470
- if (handoff && handoff.totals) blockers = Number(firstNumber(handoff.totals.open_items, blockers));
4534
+ var inboxBlockers = reviewOpenCount(counts, packets);
4535
+ var handoffReviews = handoffReviewCount(handoff);
4536
+ var blockers = handoff ? handoffReviews : inboxBlockers;
4471
4537
  var mutations = handoff && handoff.totals ? Number(firstNumber(handoff.totals.recent_mutations, 0)) : 0;
4472
4538
  els.reviewOverview.appendChild(metricDonut(
4473
4539
  "Handoff readiness",
4474
4540
  blockers ? 0 : 100,
4475
- blockers ? blockers + " memory handoff item(s) need attention" : "No pending, stale, duplicate, or missing-context memory",
4541
+ blockers ? blockers + " handoff review item(s) need attention" : "No pending, stale, or duplicate memory",
4476
4542
  blockers ? "Resolve the handoff queue before trusting branch memory." : "Ready to hand work to another agent or teammate.",
4477
4543
  blockers ? "warn" : "ok"
4478
4544
  ));
4479
- els.reviewOverview.appendChild(metricBars(handoff ? "Handoff queue" : "Inbox breakdown", blockers ? blockers + " open" : "clear", [
4545
+ els.reviewOverview.appendChild(metricBars(handoff ? "Inbox and handoff" : "Inbox breakdown", inboxBlockers ? inboxBlockers + " inbox blocker(s)" : "Inbox: clear", [
4546
+ { label: "Handoff review", value: handoffReviews || "none", score: Math.min(100, handoffReviews * 24), status: handoffReviews ? "warn" : "ok" },
4480
4547
  { label: "Pending", value: pending, score: Math.min(100, pending * 24), status: pending ? "warn" : "ok" },
4481
4548
  { label: "Stale", value: stale, score: Math.min(100, stale * 24), status: stale ? "warn" : "ok" },
4482
4549
  { label: "Duplicates", value: duplicates, score: Math.min(100, duplicates * 24), status: duplicates ? "warn" : "ok" },
4483
4550
  { label: "Mutations", value: mutations, score: Math.min(100, mutations * 16), status: mutations ? "ok" : "warn" }
4484
- ], handoff ? (handoff.summary || "Combined inbox, lifecycle, audit, timeline, and lineage.") : "These are the review metrics that should block merge or handoff.", blockers ? "warn" : "ok"));
4551
+ ], handoff ? (handoff.summary || "Handoff review combines lifecycle, audit, timeline, and lineage.") : "These inbox metrics should block merge or handoff.", blockers ? "warn" : "ok"));
4485
4552
  }
4486
4553
 
4487
4554
  function renderMemoryHandoff(handoff, items) {
@@ -4537,14 +4604,15 @@
4537
4604
  return item;
4538
4605
  }
4539
4606
 
4540
- function reviewOpenCount(counts, packets, inboxItems) {
4607
+ function handoffReviewCount(handoff) {
4608
+ return handoff && handoff.totals ? Number(firstNumber(handoff.totals.open_items, 0)) : 0;
4609
+ }
4610
+
4611
+ function reviewOpenCount(counts, packets) {
4541
4612
  var pending = Number(firstNumber(counts && counts.pending, packets && packets.length, 0));
4542
4613
  var stale = Number(firstNumber(counts && counts.stale, 0));
4543
4614
  var duplicates = Number(firstNumber(counts && counts.duplicates, 0));
4544
- var missingContext = Number(firstNumber(counts && counts.missing_context, 0));
4545
- var counted = pending + stale + duplicates + missingContext;
4546
- if (counted) return counted;
4547
- return Array.isArray(inboxItems) ? inboxItems.length : 0;
4615
+ return pending + stale + duplicates;
4548
4616
  }
4549
4617
 
4550
4618
  function renderProof() {
@@ -4716,12 +4784,12 @@
4716
4784
  els.intelligenceStatus.textContent = cards.length ? cards.length + " loaded" : "not loaded";
4717
4785
  if (!cards.length) {
4718
4786
  els.intelligenceList.className = "intelligence-list details-empty";
4719
- els.intelligenceList.textContent = "No repo intelligence reports loaded. Launch with `kage viewer --project <repo>` to load risk, module health, graph insights, and workspace reports.";
4787
+ els.intelligenceList.textContent = "No before-edit reports loaded. Launch with `kage viewer --project <repo>` to load risk, module health, graph insights, and workspace reports.";
4720
4788
  return;
4721
4789
  }
4722
4790
  els.intelligenceList.className = "intelligence-list";
4723
4791
  var normalizedCards = normalizeIntelCards(cards);
4724
- var riskFirst = new Set(["Change Risk", "Module Health", "Decision Memory", "Graph Insights", "Memory Quality"]);
4792
+ var riskFirst = new Set(["Before You Edit", "Change Risk", "Module Health", "Decision Memory", "Graph Insights", "Memory Quality"]);
4725
4793
  var primaryCards = normalizedCards.filter(function (card) { return riskFirst.has(card.title); }).slice(0, 3);
4726
4794
  if (!primaryCards.length) primaryCards = normalizedCards.slice(0, 3);
4727
4795
  primaryCards.forEach(function (card) {
@@ -4741,11 +4809,11 @@
4741
4809
  item.querySelector(".intel-highlight").textContent = card.highlight || card.summary || "";
4742
4810
  item.querySelector(".intel-action span").textContent = card.action || "Review this signal before changing related code.";
4743
4811
  var list = item.querySelector("ul");
4744
- card.rows.slice(0, 2).forEach(function (row) {
4812
+ card.rows.slice(0, Number(card.rowLimit || (card.title === "Before You Edit" ? 3 : 2))).forEach(function (row) {
4745
4813
  var li = document.createElement("li");
4746
4814
  li.innerHTML = "<strong></strong> <span></span>";
4747
4815
  li.querySelector("strong").textContent = row[0];
4748
- li.querySelector("span").textContent = trimIntelText(row[1], 92);
4816
+ li.querySelector("span").textContent = card.title === "Before You Edit" ? row[1] : trimIntelText(row[1], 92);
4749
4817
  list.appendChild(li);
4750
4818
  });
4751
4819
  els.intelligenceList.appendChild(item);
@@ -4817,6 +4885,7 @@
4817
4885
 
4818
4886
  function intelligenceSectionPriority(section) {
4819
4887
  var title = String(section && section.title || "").toLowerCase();
4888
+ if (title.indexOf("before you edit") !== -1 || title.indexOf("checklist") !== -1) return 0;
4820
4889
  if (title.indexOf("blast") !== -1 || title.indexOf("risk") !== -1) return 0;
4821
4890
  if (title.indexOf("onboarding") !== -1 || title.indexOf("decision") !== -1) return 1;
4822
4891
  if (title.indexOf("module") !== -1 || title.indexOf("health") !== -1) return 2;
@@ -4824,6 +4893,81 @@
4824
4893
  return 4;
4825
4894
  }
4826
4895
 
4896
+ function riskTargetList(risk) {
4897
+ if (!risk) return [];
4898
+ return Array.isArray(risk.targets)
4899
+ ? risk.targets
4900
+ : Object.keys(risk.targets || {}).map(function (key) { return risk.targets[key]; }).filter(Boolean);
4901
+ }
4902
+
4903
+ function isGeneratedMemoryPathValue(path) {
4904
+ var normalized = String(path || "").replace(/\\/g, "/").replace(/^\.\//, "").replace(/^\/+/, "");
4905
+ return normalized.indexOf(".agent_memory/") === 0 ||
4906
+ normalized.indexOf("agent_memory/") === 0 ||
4907
+ normalized.indexOf("/.agent_memory/") !== -1 ||
4908
+ normalized.indexOf("/agent_memory/") !== -1;
4909
+ }
4910
+
4911
+ function isUserFacingRiskPath(path) {
4912
+ var normalized = String(path || "").replace(/\\/g, "/").replace(/^\.\//, "").replace(/^\/+/, "");
4913
+ if (!normalized || normalized === "." || normalized === "root") return false;
4914
+ if (isGeneratedMemoryPathValue(normalized)) return false;
4915
+ if (normalized.indexOf("node_modules/") !== -1 || normalized.indexOf("dist/") === 0 || normalized.indexOf("build/") === 0) return false;
4916
+ return true;
4917
+ }
4918
+
4919
+ function userFacingRiskTargets(risk) {
4920
+ return riskTargetList(risk).filter(function (target) {
4921
+ return target && isUserFacingRiskPath(target.target);
4922
+ });
4923
+ }
4924
+
4925
+ function userFacingRiskHotspots(risk) {
4926
+ return Array.isArray(risk && risk.global_hotspots)
4927
+ ? risk.global_hotspots.filter(function (hotspot) { return hotspot && isUserFacingRiskPath(hotspot.file_path); })
4928
+ : [];
4929
+ }
4930
+
4931
+ function riskExplanation(item) {
4932
+ var dependents = Number(item && item.dependents_count || 0);
4933
+ var commits = Number(item && item.git && item.git.commit_count_90d || 0);
4934
+ var owner = item && item.git ? shortContributor(item.git.primary_owner || "") : "";
4935
+ if (item && item.test_gap) {
4936
+ return {
4937
+ why: "changes here are not protected by a direct test signal",
4938
+ action: "find or add the closest verification before handoff"
4939
+ };
4940
+ }
4941
+ if (dependents >= 5) {
4942
+ return {
4943
+ why: "many other files depend on this path",
4944
+ action: "inspect dependents and run the full related test suite"
4945
+ };
4946
+ }
4947
+ if (item && item.risk_type === "single-owner") {
4948
+ return {
4949
+ why: owner ? "most history belongs to " + owner : "ownership is concentrated in one person",
4950
+ action: "route review to the owner or write down assumptions before editing"
4951
+ };
4952
+ }
4953
+ if (commits >= 20 || item && item.risk_type === "churn-heavy") {
4954
+ return {
4955
+ why: "this area changed " + commits + " time(s) in the last 90 days",
4956
+ action: "recall recent memory and keep the change narrow"
4957
+ };
4958
+ }
4959
+ if (dependents > 0) {
4960
+ return {
4961
+ why: dependents + " file(s) depend on this path",
4962
+ action: "check the dependent paths before editing"
4963
+ };
4964
+ }
4965
+ return {
4966
+ why: item && item.risk_type ? "Kage sees a " + item.risk_type.replace(/-/g, " ") + " signal" : "Kage sees a local change-risk signal",
4967
+ action: "state the expected behavior and verify it after editing"
4968
+ };
4969
+ }
4970
+
4827
4971
  function buildIntelligenceSections(reports) {
4828
4972
  var sections = [];
4829
4973
  var contributors = reports.contributors;
@@ -5012,13 +5156,14 @@
5012
5156
  }
5013
5157
 
5014
5158
  if (risk) {
5015
- var targets = Array.isArray(risk.targets) ? risk.targets : Object.keys(risk.targets || {}).map(function (key) { return risk.targets[key]; });
5016
- var hotspots = Array.isArray(risk.global_hotspots) ? risk.global_hotspots : [];
5159
+ var targets = userFacingRiskTargets(risk);
5160
+ var hotspots = userFacingRiskHotspots(risk);
5017
5161
  var riskRows = targets.slice(0, 6).map(function (item) {
5162
+ var explanation = riskExplanation(item);
5018
5163
  return {
5019
5164
  label: item.target,
5020
- value: item.risk_type || "risk",
5021
- meta: item.risk_summary || "",
5165
+ value: explanation.why,
5166
+ meta: "Do first: " + explanation.action,
5022
5167
  score: Math.round(Number(item.hotspot_score || 0) * 100) || Math.min(100, Number(item.dependents_count || 0) * 12 + (item.test_gap ? 24 : 0)),
5023
5168
  status: item.test_gap || item.risk_type === "single-owner" ? "warn" : "",
5024
5169
  path: item.target,
@@ -5026,8 +5171,8 @@
5026
5171
  }).concat(hotspots.slice(0, 4).map(function (hotspot) {
5027
5172
  return {
5028
5173
  label: hotspot.file_path,
5029
- value: Math.round(Number(hotspot.hotspot_score || 0) * 100) + "% hot",
5030
- meta: (hotspot.commit_count_90d || 0) + " commits in 90d, owner " + shortContributor(hotspot.primary_owner || "unknown"),
5174
+ value: "changed " + (hotspot.commit_count_90d || 0) + " time(s) in 90 days",
5175
+ meta: "Do first: recall recent repo memory and run the closest tests after editing.",
5031
5176
  score: Math.round(Number(hotspot.hotspot_score || 0) * 100),
5032
5177
  status: "danger",
5033
5178
  path: hotspot.file_path,
@@ -5035,10 +5180,10 @@
5035
5180
  }));
5036
5181
  if (riskRows.length) {
5037
5182
  sections.push({
5038
- title: "Blast Radius",
5039
- kicker: "change impact",
5040
- stat: riskRows.length + " signals",
5041
- summary: "Action: review tests, owners, and dependents before editing these targets.",
5183
+ title: "Before You Edit Checklist",
5184
+ kicker: "why risky / do first",
5185
+ stat: riskRows.length + " checks",
5186
+ summary: "Each row explains what can go wrong and the first safety step before an agent edits it.",
5042
5187
  rows: riskRows,
5043
5188
  limit: 10,
5044
5189
  });
@@ -5100,16 +5245,24 @@
5100
5245
  }
5101
5246
  var risk = reports.risk;
5102
5247
  if (risk) {
5103
- var targets = Array.isArray(risk.targets) ? risk.targets : Object.keys(risk.targets || {}).map(function (key) { return risk.targets[key]; });
5248
+ var targets = userFacingRiskTargets(risk);
5104
5249
  var silos = Array.isArray(risk.ownership_silos) ? risk.ownership_silos : [];
5250
+ var hotspots = userFacingRiskHotspots(risk);
5251
+ var riskRows = targets.slice(0, 4).map(function (item) {
5252
+ var explanation = riskExplanation(item);
5253
+ return [item.target || "edit area", "Why: " + explanation.why + " Do first: " + explanation.action];
5254
+ }).concat(targets.length ? [] : hotspots.slice(0, 2).map(function (hotspot) {
5255
+ return [hotspot.file_path || "hotspot", "Why: changed " + (hotspot.commit_count_90d || 0) + " time(s) in 90 days. Do first: recall recent memory and run closest tests after editing."];
5256
+ }));
5105
5257
  cards.push({
5106
- title: "Change Risk",
5107
- kicker: "blast radius",
5108
- summary: risk.summary || "Local risk report from code graph and git history.",
5109
- rows: targets.slice(0, 5).map(function (item) {
5110
- return [item.target || "target", item.risk_summary || [item.risk_type, item.dependents_count != null ? item.dependents_count + " dependents" : ""].filter(Boolean).join(", ")];
5111
- }).concat(targets.length ? [] : [["Hotspots", Array.isArray(risk.global_hotspots) ? risk.global_hotspots.length + " global" : "none"]])
5112
- .concat(silos.length ? [["Silos", silos.length + " ownership concentration(s)"]] : [])
5258
+ title: "Before You Edit",
5259
+ kicker: "risk checklist",
5260
+ summary: riskRows.length
5261
+ ? "These are places where an agent is more likely to break behavior, miss tests, or rely on unstated knowledge."
5262
+ : "No user-facing risky edit areas were found in the loaded report.",
5263
+ rowLimit: 3,
5264
+ rows: (riskRows.length ? riskRows : [["Risk", "No code-level risk checks loaded"]])
5265
+ .concat(silos.length ? [["Reviewer signal", silos.length + " ownership concentration(s)"]] : [])
5113
5266
  });
5114
5267
  }
5115
5268
  var contributors = reports.contributors;
@@ -5253,13 +5406,13 @@
5253
5406
  normalized.metricLabel = "memory-code links";
5254
5407
  normalized.highlight = "Shows whether saved repo knowledge is tied to actual files, symbols, routes, and tests.";
5255
5408
  normalized.action = "If this is low, capture memory with concrete paths so agents can recall it during edits.";
5256
- } else if (card.title === "Change Risk") {
5257
- var siloText = row("Silos");
5409
+ } else if (card.title === "Before You Edit" || card.title === "Change Risk") {
5410
+ var siloText = row("Reviewer signal") || row("Silos");
5258
5411
  var siloMatch = siloText && String(siloText).match(/\d+/);
5259
- normalized.metric = siloMatch ? siloMatch[0] + " silos" : ((card.rows || []).length + " signals");
5260
- normalized.metricLabel = "risk signals";
5261
- normalized.highlight = "Flags files with blast radius, test gaps, or ownership concentration.";
5262
- normalized.action = "Use these rows to pick tests and reviewers before touching risky files.";
5412
+ normalized.metric = (card.rows || []).length + " checks";
5413
+ normalized.metricLabel = siloMatch ? siloMatch[0] + " reviewer silo(s)" : "pre-edit checklist";
5414
+ normalized.highlight = card.summary || "Shows what can go wrong before an agent edits a file.";
5415
+ normalized.action = "Start with the first row: read why it is risky, then do the listed safety step before editing.";
5263
5416
  } else if (card.title === "Contributors") {
5264
5417
  normalized.metric = (card.rows || []).length + " profiles";
5265
5418
  normalized.metricLabel = "review routing";
package/viewer/data.html CHANGED
@@ -21,7 +21,7 @@
21
21
  <a class="viewer-section active" href="./" data-viewer-page="overview" aria-current="page">Overview</a>
22
22
  <a class="viewer-section" href="./graph.html" data-viewer-page="graph">Graph</a>
23
23
  <a class="viewer-section" href="./memory.html" data-viewer-page="memory">Memory</a>
24
- <a class="viewer-section" href="./intel.html" data-viewer-page="intel">Risks</a>
24
+ <a class="viewer-section" href="./intel.html" data-viewer-page="intel">Before You Edit</a>
25
25
  <a class="viewer-section" href="./review.html" data-viewer-page="review">Review</a>
26
26
  </nav>
27
27
  <nav class="sidebar-secondary" aria-label="Diagnostics">
@@ -70,10 +70,10 @@
70
70
  <a href="./graph.html" data-viewer-page="graph">Explore graph</a>
71
71
  </article>
72
72
  <article class="dashboard-card primary" id="dashboardIntel">
73
- <div class="dashboard-card-head"><span>Risks</span><strong>reports</strong></div>
73
+ <div class="dashboard-card-head"><span>Before You Edit</span><strong>checklist</strong></div>
74
74
  <h3>What needs attention</h3>
75
75
  <ul></ul>
76
- <a href="./intel.html" data-viewer-page="intel">Open risks</a>
76
+ <a href="./intel.html" data-viewer-page="intel">Open edit checklist</a>
77
77
  </article>
78
78
  <article class="dashboard-card primary" id="dashboardReview">
79
79
  <div class="dashboard-card-head"><span>Review</span><strong>handoff</strong></div>
@@ -259,9 +259,9 @@
259
259
  <div id="proofList" class="proof-list"></div>
260
260
  </section>
261
261
 
262
- <section class="intelligence-panel" aria-label="Risks and reports">
262
+ <section class="intelligence-panel" aria-label="Before edit risks and reports">
263
263
  <div class="panel-heading">
264
- <h2>Risks</h2>
264
+ <h2>Before You Edit</h2>
265
265
  <span id="intelligenceStatus">reports</span>
266
266
  </div>
267
267
  <div id="intelligenceList" class="intelligence-list"></div>
@@ -273,7 +273,7 @@
273
273
  <span id="entityCount">0</span>
274
274
  </div>
275
275
  <div id="debugOverview" class="debug-overview"></div>
276
- <div class="debug-guide">Use artifacts when the graph or recall output looks wrong. Normal repo work starts from Overview, Graph, Memory, Risks, or Review.</div>
276
+ <div class="debug-guide">Use artifacts when the graph or recall output looks wrong. Normal repo work starts from Overview, Graph, Memory, Before You Edit, or Review.</div>
277
277
  <div id="entityList" class="list"></div>
278
278
  </section>
279
279
 
package/viewer/graph.html CHANGED
@@ -21,7 +21,7 @@
21
21
  <a class="viewer-section active" href="./" data-viewer-page="overview" aria-current="page">Overview</a>
22
22
  <a class="viewer-section" href="./graph.html" data-viewer-page="graph">Graph</a>
23
23
  <a class="viewer-section" href="./memory.html" data-viewer-page="memory">Memory</a>
24
- <a class="viewer-section" href="./intel.html" data-viewer-page="intel">Risks</a>
24
+ <a class="viewer-section" href="./intel.html" data-viewer-page="intel">Before You Edit</a>
25
25
  <a class="viewer-section" href="./review.html" data-viewer-page="review">Review</a>
26
26
  </nav>
27
27
  <nav class="sidebar-secondary" aria-label="Diagnostics">
@@ -70,10 +70,10 @@
70
70
  <a href="./graph.html" data-viewer-page="graph">Explore graph</a>
71
71
  </article>
72
72
  <article class="dashboard-card primary" id="dashboardIntel">
73
- <div class="dashboard-card-head"><span>Risks</span><strong>reports</strong></div>
73
+ <div class="dashboard-card-head"><span>Before You Edit</span><strong>checklist</strong></div>
74
74
  <h3>What needs attention</h3>
75
75
  <ul></ul>
76
- <a href="./intel.html" data-viewer-page="intel">Open risks</a>
76
+ <a href="./intel.html" data-viewer-page="intel">Open edit checklist</a>
77
77
  </article>
78
78
  <article class="dashboard-card primary" id="dashboardReview">
79
79
  <div class="dashboard-card-head"><span>Review</span><strong>handoff</strong></div>
@@ -259,9 +259,9 @@
259
259
  <div id="proofList" class="proof-list"></div>
260
260
  </section>
261
261
 
262
- <section class="intelligence-panel" aria-label="Risks and reports">
262
+ <section class="intelligence-panel" aria-label="Before edit risks and reports">
263
263
  <div class="panel-heading">
264
- <h2>Risks</h2>
264
+ <h2>Before You Edit</h2>
265
265
  <span id="intelligenceStatus">reports</span>
266
266
  </div>
267
267
  <div id="intelligenceList" class="intelligence-list"></div>
@@ -273,7 +273,7 @@
273
273
  <span id="entityCount">0</span>
274
274
  </div>
275
275
  <div id="debugOverview" class="debug-overview"></div>
276
- <div class="debug-guide">Use artifacts when the graph or recall output looks wrong. Normal repo work starts from Overview, Graph, Memory, Risks, or Review.</div>
276
+ <div class="debug-guide">Use artifacts when the graph or recall output looks wrong. Normal repo work starts from Overview, Graph, Memory, Before You Edit, or Review.</div>
277
277
  <div id="entityList" class="list"></div>
278
278
  </section>
279
279