@mneme-ai/xray 2.179.0 → 2.180.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 +1 -1
- package/public/card.js +31 -6
- package/public/index.html +15 -0
- package/public/report.html +15 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mneme-ai/xray",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.180.0",
|
|
4
4
|
"description": "Mneme Repo X-Ray — a signed, raw-free, deterministic X-Ray of any repo. Every number is reproducible from git/AST/metadata and sealed with an offline-verifiable NOTARY receipt. No source code ever leaves the machine; no LLM guesses anything.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
package/public/card.js
CHANGED
|
@@ -136,11 +136,15 @@
|
|
|
136
136
|
const nodeSvg = nodes.map((n) => { const R = n.r, col = riskColor(n.risk), showLabel = n.i < 9 || n.risk >= 0.65; const lbl = esc(base(n.file)).slice(0, 24) + (n.ownerPct >= 0.5 ? ` · ${Math.round(n.ownerPct * 100)}%` : ""); const lw = lbl.length * 6.4 + 14, ly = n.y + R + 7;
|
|
137
137
|
return `<g><circle cx="${n.x}" cy="${n.y}" r="${(R + 11).toFixed(1)}" fill="${col}" opacity="0.12"/><circle cx="${n.x}" cy="${n.y}" r="${R.toFixed(1)}" fill="${col}" opacity="0.95"/><circle cx="${(n.x - R * 0.3).toFixed(1)}" cy="${(n.y - R * 0.32).toFixed(1)}" r="${(R * 0.52).toFixed(1)}" fill="#ffffff" opacity="0.22"/><circle cx="${n.x}" cy="${n.y}" r="${R.toFixed(1)}" fill="none" stroke="#ffffff" stroke-width="1.6" opacity="0.7"/>${showLabel ? `<g><rect x="${(n.x - lw / 2).toFixed(1)}" y="${ly.toFixed(1)}" width="${lw.toFixed(1)}" height="19" rx="9.5" fill="#ffffff" opacity="0.92" stroke="#ececef"/><text x="${n.x}" y="${(ly + 13).toFixed(1)}" text-anchor="middle" font-size="11.5" fill="#33333b" font-family="ui-monospace,Menlo,monospace">${lbl}</text></g>` : ""}</g>`; }).join("");
|
|
138
138
|
const owned = nodes.filter((n) => n.risk >= 0.6).length;
|
|
139
|
+
// TOP key-person risks as WORDS (the part a CEO/dev reads) — concrete + actionable
|
|
140
|
+
const topRisk = nodes.filter((n) => n.risk >= 0.5).slice(0, 6);
|
|
141
|
+
const riskList = topRisk.length ? `<div class="rmlist"><div class="rmlt">⚠️ Top key-person risks — fix these first</div>${topRisk.map((n) => `<div class="rmli"><span class="rmdot" style="background:${riskColor(n.risk)}"></span><b>${esc(base(n.file))}</b><span class="rmwhy">one author owns <b>${Math.round(n.ownerPct * 100)}%</b> of its history — add a reviewer / write docs before they leave</span></div>`).join("")}</div>` : `<div class="rmlist rmok">✓ No single-owner files — knowledge is well spread across the team.</div>`;
|
|
139
142
|
return `<div class="riskmap">
|
|
140
|
-
<div class="rmhead">🗺 Risk map — <b>
|
|
141
|
-
<div class="rmsub"
|
|
142
|
-
<svg class="rmsvg" viewBox="0 0 ${W} ${H}" preserveAspectRatio="xMidYMid meet" role="img" aria-label="repository risk map">${edgeSvg}${nodeSvg}</svg>
|
|
143
|
-
<div class="rmleg"><span><i style="background:#16a34a"></i>shared /
|
|
143
|
+
<div class="rmhead">🗺 Risk map — <b>who holds the keys, and what breaks if they leave</b></div>
|
|
144
|
+
<div class="rmsub"><b>How to read:</b> each circle is a file · <b>red</b> = only one person knows it (key-person risk) · <b>bigger</b> = changes more often · <b>lines</b> = files that always change together. Click the map to enlarge. Every value is verbatim from the signed report — no AI guessed it.</div>
|
|
145
|
+
<div class="rmsvgwrap rmzoom" title="click to enlarge"><svg class="rmsvg" viewBox="0 0 ${W} ${H}" preserveAspectRatio="xMidYMid meet" role="img" aria-label="repository risk map">${edgeSvg}${nodeSvg}</svg><span class="rmexpand">⤢ enlarge</span></div>
|
|
146
|
+
<div class="rmleg"><span><i style="background:#16a34a"></i>shared / safe</span><span><i style="background:#d97706"></i>concentrated</span><span><i style="background:#e11d48"></i>single-owner</span><span><i class="dash"></i>hidden cross-dir coupling</span><span class="rmsummary">${nodes.length} files · ${edges.length} links${owned ? ` · <b style="color:#be123c">${owned} single-owner</b>` : ""}</span></div>
|
|
147
|
+
${riskList}
|
|
144
148
|
</div>`;
|
|
145
149
|
}
|
|
146
150
|
|
|
@@ -190,7 +194,7 @@
|
|
|
190
194
|
<div class="membrane">
|
|
191
195
|
<div class="mp"><span class="mpk">① CAPABILITY</span><span class="mpv">${s.signalsRun} deterministic signals · ${(sec.filesScanned || 0).toLocaleString()} files scanned</span></div>
|
|
192
196
|
<div class="mp"><span class="mpk">② ATTENTION</span><span class="mpv">${tri.length ? `${tri.length} signal(s) need attention` : `all signals clear`}</span></div>
|
|
193
|
-
<div class="mp"><span class="mpk">③
|
|
197
|
+
<div class="mp"><span class="mpk">③ METHOD</span><span class="mpv"><span class="hdot"></span><b>No AI in the math</b> — 0 numbers can be hallucinated because none are generated by an LLM; every figure is computed from git/code, re-runnable${verified ? " + signed" : ""}</span></div>
|
|
194
198
|
</div>
|
|
195
199
|
<div class="trustbar"><span class="htext"><b>Every figure is computed from git, code & package metadata — not one guessed by an AI.</b> Re-run this commit → identical numbers${verified ? ` · <b>signed</b>, verifies offline with the embedded public key` : ""}.</span></div>
|
|
196
200
|
${tri.length ? `<div class="triage">
|
|
@@ -212,5 +216,26 @@
|
|
|
212
216
|
</div>`;
|
|
213
217
|
}
|
|
214
218
|
|
|
215
|
-
g.MnemeXRay = { xrayCardHTML, gradeClass: gC, esc };
|
|
219
|
+
g.MnemeXRay = { xrayCardHTML, gradeClass: gC, esc, riskMapHTML };
|
|
220
|
+
|
|
221
|
+
// CLICK-TO-ENLARGE the risk map — one delegated listener (works on every page that
|
|
222
|
+
// loads card.js, every browser, mobile). Builds a responsive modal on first open.
|
|
223
|
+
if (typeof document !== "undefined" && !g.__rmZoomBound) {
|
|
224
|
+
g.__rmZoomBound = true;
|
|
225
|
+
const close = () => { const m = document.getElementById("rmmodal"); if (m) m.style.display = "none"; };
|
|
226
|
+
document.addEventListener("click", (ev) => {
|
|
227
|
+
const t = ev.target;
|
|
228
|
+
if (t && t.closest && t.closest(".rmmodal-close")) { close(); return; }
|
|
229
|
+
if (t && t.id === "rmmodal") { close(); return; }
|
|
230
|
+
const z = t && t.closest && t.closest(".rmzoom");
|
|
231
|
+
if (z) {
|
|
232
|
+
const svg = z.querySelector("svg"); if (!svg) return;
|
|
233
|
+
let m = document.getElementById("rmmodal");
|
|
234
|
+
if (!m) { m = document.createElement("div"); m.id = "rmmodal"; m.className = "rmmodal"; document.body.appendChild(m); }
|
|
235
|
+
m.innerHTML = `<div class="rmmodal-box"><div class="rmmodal-bar"><b>🗺 Risk map</b> — red = single-owner (key-person risk) · size = churn · line = files that change together<button class="rmmodal-close" aria-label="close">✕</button></div><div class="rmmodal-svg">${svg.outerHTML}</div></div>`;
|
|
236
|
+
m.style.display = "flex";
|
|
237
|
+
}
|
|
238
|
+
});
|
|
239
|
+
document.addEventListener("keydown", (ev) => { if (ev.key === "Escape") close(); });
|
|
240
|
+
}
|
|
216
241
|
})(window);
|
package/public/index.html
CHANGED
|
@@ -96,6 +96,21 @@
|
|
|
96
96
|
.riskmap .rmleg span{display:inline-flex;align-items:center;gap:6px}
|
|
97
97
|
.riskmap .rmleg i{width:11px;height:11px;border-radius:50%;display:inline-block}
|
|
98
98
|
.riskmap .rmleg i.dash{width:18px;height:0;border-radius:0;border-top:2px dashed #e11d48}
|
|
99
|
+
.riskmap .rmsummary{margin-left:auto;color:#8b8f98}
|
|
100
|
+
.riskmap .rmsvgwrap{position:relative;cursor:zoom-in}
|
|
101
|
+
.riskmap .rmexpand{position:absolute;top:10px;right:12px;font-size:11px;color:#5b6068;background:#fff;border:1px solid #ececef;border-radius:8px;padding:3px 9px;opacity:.85}
|
|
102
|
+
.riskmap .rmlist{margin-top:14px;border-top:1px solid #eef0f2;padding-top:12px}
|
|
103
|
+
.riskmap .rmlt{font-size:12.5px;font-weight:680;color:#be123c;margin-bottom:8px}
|
|
104
|
+
.riskmap .rmok{color:#15803d;font-weight:560;font-size:13px;border:0;padding-top:0}
|
|
105
|
+
.riskmap .rmli{display:flex;align-items:baseline;gap:9px;padding:6px 0;font-size:13px;flex-wrap:wrap}
|
|
106
|
+
.riskmap .rmdot{width:10px;height:10px;border-radius:50%;flex-shrink:0;align-self:center}
|
|
107
|
+
.riskmap .rmli b{color:#0b0b0f;font-family:ui-monospace,Menlo,monospace}
|
|
108
|
+
.riskmap .rmwhy{color:#5b6068}
|
|
109
|
+
.rmmodal{display:none;position:fixed;inset:0;z-index:9999;background:rgba(11,11,20,.62);align-items:center;justify-content:center;padding:16px}
|
|
110
|
+
.rmmodal-box{background:#fff;border-radius:16px;max-width:1100px;width:100%;max-height:92vh;overflow:auto;box-shadow:0 24px 80px rgba(0,0,0,.4)}
|
|
111
|
+
.rmmodal-bar{display:flex;align-items:center;gap:10px;padding:14px 18px;border-bottom:1px solid #eef0f2;font-size:13px;color:#5b6068;position:sticky;top:0;background:#fff}
|
|
112
|
+
.rmmodal-close{margin-left:auto;width:34px;height:34px;border:1px solid #ececef;background:#fff;border-radius:9px;cursor:pointer;font-size:15px;flex-shrink:0}
|
|
113
|
+
.rmmodal-svg{padding:14px}.rmmodal-svg svg{width:100%;height:auto}
|
|
99
114
|
.trustbar{display:flex;align-items:center;gap:14px;padding:13px 30px;background:#f0fdf4;border-bottom:1px solid #dcfce7;flex-wrap:wrap}
|
|
100
115
|
.hgauge{display:inline-flex;align-items:center;gap:8px;font-weight:680;color:#15803d;font-size:14px;white-space:nowrap}
|
|
101
116
|
.hdot{width:10px;height:10px;border-radius:50%;background:#16a34a;box-shadow:0 0 0 4px rgba(22,163,74,.16)}
|
package/public/report.html
CHANGED
|
@@ -49,6 +49,21 @@
|
|
|
49
49
|
.riskmap .rmleg span{display:inline-flex;align-items:center;gap:6px}
|
|
50
50
|
.riskmap .rmleg i{width:11px;height:11px;border-radius:50%;display:inline-block}
|
|
51
51
|
.riskmap .rmleg i.dash{width:18px;height:0;border-radius:0;border-top:2px dashed #e11d48}
|
|
52
|
+
.riskmap .rmsummary{margin-left:auto;color:#8b8f98}
|
|
53
|
+
.riskmap .rmsvgwrap{position:relative;cursor:zoom-in}
|
|
54
|
+
.riskmap .rmexpand{position:absolute;top:10px;right:12px;font-size:11px;color:#5b6068;background:#fff;border:1px solid #ececef;border-radius:8px;padding:3px 9px;opacity:.85}
|
|
55
|
+
.riskmap .rmlist{margin-top:14px;border-top:1px solid #eef0f2;padding-top:12px}
|
|
56
|
+
.riskmap .rmlt{font-size:12.5px;font-weight:680;color:#be123c;margin-bottom:8px}
|
|
57
|
+
.riskmap .rmok{color:#15803d;font-weight:560;font-size:13px;border:0;padding-top:0}
|
|
58
|
+
.riskmap .rmli{display:flex;align-items:baseline;gap:9px;padding:6px 0;font-size:13px;flex-wrap:wrap}
|
|
59
|
+
.riskmap .rmdot{width:10px;height:10px;border-radius:50%;flex-shrink:0;align-self:center}
|
|
60
|
+
.riskmap .rmli b{color:#0b0b0f;font-family:ui-monospace,Menlo,monospace}
|
|
61
|
+
.riskmap .rmwhy{color:#5b6068}
|
|
62
|
+
.rmmodal{display:none;position:fixed;inset:0;z-index:9999;background:rgba(11,11,20,.62);align-items:center;justify-content:center;padding:16px}
|
|
63
|
+
.rmmodal-box{background:#fff;border-radius:16px;max-width:1100px;width:100%;max-height:92vh;overflow:auto;box-shadow:0 24px 80px rgba(0,0,0,.4)}
|
|
64
|
+
.rmmodal-bar{display:flex;align-items:center;gap:10px;padding:14px 18px;border-bottom:1px solid #eef0f2;font-size:13px;color:#5b6068;position:sticky;top:0;background:#fff}
|
|
65
|
+
.rmmodal-close{margin-left:auto;width:34px;height:34px;border:1px solid #ececef;background:#fff;border-radius:9px;cursor:pointer;font-size:15px;flex-shrink:0}
|
|
66
|
+
.rmmodal-svg{padding:14px}.rmmodal-svg svg{width:100%;height:auto}
|
|
52
67
|
.trustbar{display:flex;align-items:center;gap:14px;padding:13px 30px;background:#f0fdf4;border-bottom:1px solid #dcfce7;flex-wrap:wrap}
|
|
53
68
|
.hgauge{display:inline-flex;align-items:center;gap:8px;font-weight:680;color:#15803d;font-size:14px}
|
|
54
69
|
.hdot{width:10px;height:10px;border-radius:50%;background:#16a34a;box-shadow:0 0 0 4px rgba(22,163,74,.16)}
|