@echomem/mcp 1.4.8 → 1.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/README.md +23 -3
- package/assets/canonical-scorer/README.md +18 -0
- package/assets/canonical-scorer/analyze-10-problems.mjs +857 -0
- package/assets/canonical-scorer/build-session-waste-dashboard.mjs +1628 -0
- package/assets/canonical-scorer/golden_anchors.mjs +83 -0
- package/assets/canonical-scorer/optimizable_detail.mjs +633 -0
- package/assets/hud/github.svg +1 -0
- package/dist/city/README.md +9 -0
- package/dist/city/echo-ai-city-only.html +1067 -107
- package/dist/codex-session-files.js +283 -0
- package/dist/codex-sync.js +7 -2
- package/dist/context-analysis/canonical-golden.js +47 -0
- package/dist/context-analysis/claude-canonical-adapter.js +315 -0
- package/dist/context-analysis/claude-native-canonical.js +1216 -0
- package/dist/context-analysis/vendored-canonical.js +793 -0
- package/dist/context-analysis/workspace-report.js +1838 -0
- package/dist/context-metrics/calculate.js +43 -0
- package/dist/context-metrics/estimator.js +45 -0
- package/dist/context-metrics/ledger.js +507 -0
- package/dist/context-metrics/model-limits.js +26 -0
- package/dist/context-metrics/parse-claude.js +227 -0
- package/dist/context-metrics/parse-codex.js +276 -0
- package/dist/context-metrics/types.js +1 -0
- package/dist/forensics-10-problems.js +7 -6
- package/dist/forensics.js +863 -132
- package/dist/hud/adapters.js +77 -202
- package/dist/hud/cli.js +0 -0
- package/dist/hud/efficiency.js +447 -0
- package/dist/hud/electron-main.js +3 -2
- package/dist/hud/fs.js +14 -0
- package/dist/hud/metric.js +17 -102
- package/dist/hud/monitor.js +136 -28
- package/dist/hud/render.js +4 -3
- package/dist/hud/server.js +30 -0
- package/dist/hud/web.js +622 -353
- package/dist/index.js +7 -3
- package/dist/local-data-paths.js +87 -0
- package/dist/migrate.js +37 -29
- package/dist/report.js +101 -40
- package/dist/setup-page/client-core.js +475 -0
- package/dist/setup-page/client-extraction.js +550 -0
- package/dist/setup-page/client-lifecycle.js +116 -0
- package/dist/setup-page/client-report-audit.js +818 -0
- package/dist/setup-page/client-report-city.js +204 -0
- package/dist/setup-page/client-report.js +6 -0
- package/dist/setup-page/client.js +15 -0
- package/dist/setup-page/document.js +37 -0
- package/dist/setup-page/styles-city-report.js +880 -0
- package/dist/setup-page/styles-context-audit.js +470 -0
- package/dist/setup-page/styles-extraction.js +821 -0
- package/dist/setup-page/styles-foundation.js +231 -0
- package/dist/setup-page/styles.js +11 -0
- package/dist/setup-page.js +15 -2538
- package/dist/setup-preview.js +245 -0
- package/dist/setup.js +456 -44
- package/package.json +8 -7
- package/templates/echomem-recall.md +2 -2
- package/dist/city/10-problems-report.html +0 -649
- package/dist/city/_live.html +0 -37
- package/dist/city/_serve.mjs +0 -45
- package/dist/city/card-data.json +0 -15
- package/dist/city/city-data.json +0 -248
- package/dist/city/echo-ai-city-only.template.html +0 -1272
- package/dist/city/generate-echo-city-only.mjs +0 -112
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
// Generated from the prior inline setup page. This content is emitted into one browser IIFE/style tag.
|
|
2
|
+
export const SETUP_PAGE_CLIENT_REPORT_CITY = String.raw ` /* ---------- fullscreen city modal: 80% viewport, dirty/clean, legend, stats ---------- */
|
|
3
|
+
function cityComparisonTo(mode) {
|
|
4
|
+
var frame = document.getElementById("city-report-frame");
|
|
5
|
+
if (!frame || !frame.contentWindow) return;
|
|
6
|
+
try { frame.contentWindow.postMessage({ type: "echomem:city-comparison", mode: mode }, location.origin); } catch (_) {}
|
|
7
|
+
}
|
|
8
|
+
function fillCityFullData() {
|
|
9
|
+
var legend = document.getElementById("cfc-legend");
|
|
10
|
+
var stats = document.getElementById("cfc-stats");
|
|
11
|
+
var r = report || {};
|
|
12
|
+
var gs = gsClean(r.canonicalGoldenStandard);
|
|
13
|
+
var s = (gs && gs.summary) || {};
|
|
14
|
+
var projection = gsBillingProjection(r, gs);
|
|
15
|
+
var cov = r.activeSessionCoverage || {};
|
|
16
|
+
var sr = (gs && gs.sourceReports) || {};
|
|
17
|
+
var codexShare = Number(sr.codex && sr.codex.summary && sr.codex.summary.officialInputTokens || 0);
|
|
18
|
+
var claudeShare = Number(sr.claudeCode && sr.claudeCode.summary && sr.claudeCode.summary.officialInputTokens || 0);
|
|
19
|
+
var basis = "of input tokens";
|
|
20
|
+
if (!codexShare && !claudeShare) {
|
|
21
|
+
codexShare = Number(cov.codex || 0);
|
|
22
|
+
claudeShare = Number(cov.claudeCode || 0);
|
|
23
|
+
basis = "of sessions";
|
|
24
|
+
}
|
|
25
|
+
var totalShare = codexShare + claudeShare;
|
|
26
|
+
var codexPct = totalShare ? Math.round((codexShare / totalShare) * 100) : 0;
|
|
27
|
+
var claudePct = totalShare ? 100 - codexPct : 0;
|
|
28
|
+
if (legend) legend.innerHTML =
|
|
29
|
+
'<span class="cfc-side"><i class="cfc-swatch" style="background:#10a37f"></i><img src="/hud-assets/codex.svg" alt="" />Codex <b>' + codexPct + '%</b></span>' +
|
|
30
|
+
'<span class="cfc-side"><i class="cfc-swatch" style="background:#d97757"></i><img src="/hud-assets/claude.svg" alt="" />Claude Code <b>' + claudePct + '%</b></span>' +
|
|
31
|
+
'<span style="color:var(--echo-ink-faint)">' + basis + '</span>';
|
|
32
|
+
var wh = r.userWorkingHours || {};
|
|
33
|
+
var items = [
|
|
34
|
+
["Sessions", number(Number(s.sessionsAnalyzed || 0) || (Number(cov.codex || 0) + Number(cov.claudeCode || 0)))],
|
|
35
|
+
["Repos analyzed", number(Number(s.reposAnalyzed || 0) || Number(r.scale && r.scale.repoCount || 0))],
|
|
36
|
+
["Turns analyzed", number(s.turnsAnalyzed)],
|
|
37
|
+
["Provider input", big(projection.billedInputTokens)],
|
|
38
|
+
["Projected noise", Math.round(gs ? gsDisplayWastePct(gs) : 0) + "% · " + big(projection.projectedWasteTokens)],
|
|
39
|
+
["Attention hours", number(Math.round(Number(wh.userAttentionHours || 0))) + "h"]
|
|
40
|
+
];
|
|
41
|
+
if (stats) stats.innerHTML = items.map(function (it) {
|
|
42
|
+
return '<div class="cfc-stat"><span>' + it[0] + '</span><strong>' + it[1] + '</strong></div>';
|
|
43
|
+
}).join("");
|
|
44
|
+
}
|
|
45
|
+
function bindCityFullscreen() {
|
|
46
|
+
var btn = document.getElementById("city-full-btn");
|
|
47
|
+
var chrome = document.getElementById("city-full-chrome");
|
|
48
|
+
var backdrop = document.getElementById("city-full-backdrop");
|
|
49
|
+
if (!btn || !chrome || !backdrop) return;
|
|
50
|
+
function setState(mode) {
|
|
51
|
+
cityComparisonTo(mode);
|
|
52
|
+
Array.prototype.forEach.call(chrome.querySelectorAll("[data-city-state]"), function (b) {
|
|
53
|
+
b.classList.toggle("on", b.getAttribute("data-city-state") === mode);
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
function open() {
|
|
57
|
+
fillCityFullData();
|
|
58
|
+
document.body.classList.add("cityFull");
|
|
59
|
+
chrome.hidden = false;
|
|
60
|
+
backdrop.hidden = false;
|
|
61
|
+
}
|
|
62
|
+
function close() {
|
|
63
|
+
document.body.classList.remove("cityFull");
|
|
64
|
+
chrome.hidden = true;
|
|
65
|
+
backdrop.hidden = true;
|
|
66
|
+
setState("without");
|
|
67
|
+
}
|
|
68
|
+
btn.onclick = open;
|
|
69
|
+
backdrop.onclick = close;
|
|
70
|
+
chrome.querySelector(".cfc-close").onclick = close;
|
|
71
|
+
Array.prototype.forEach.call(chrome.querySelectorAll("[data-city-state]"), function (b) {
|
|
72
|
+
b.onclick = function () { setState(b.getAttribute("data-city-state")); };
|
|
73
|
+
});
|
|
74
|
+
Array.prototype.forEach.call(chrome.querySelectorAll("[data-city-metric]"), function (b) {
|
|
75
|
+
b.onclick = function () {
|
|
76
|
+
var frame = document.getElementById("city-report-frame");
|
|
77
|
+
if (frame && frame.contentWindow) {
|
|
78
|
+
try { frame.contentWindow.postMessage({ type: "echomem:city-metric", metric: b.getAttribute("data-city-metric") }, location.origin); } catch (_) {}
|
|
79
|
+
}
|
|
80
|
+
Array.prototype.forEach.call(chrome.querySelectorAll("[data-city-metric]"), function (x) {
|
|
81
|
+
x.classList.toggle("on", x === b);
|
|
82
|
+
});
|
|
83
|
+
};
|
|
84
|
+
});
|
|
85
|
+
document.addEventListener("keydown", function (e) {
|
|
86
|
+
if (e.key === "Escape" && document.body.classList.contains("cityFull")) close();
|
|
87
|
+
});
|
|
88
|
+
if (params.get("cityfull") === "1") open();
|
|
89
|
+
}
|
|
90
|
+
function sendReportToCity() {
|
|
91
|
+
if (!reportEnvelope) return;
|
|
92
|
+
var frame = document.getElementById("city-report-frame");
|
|
93
|
+
if (!frame || !frame.contentWindow) return;
|
|
94
|
+
try {
|
|
95
|
+
frame.contentWindow.postMessage({ type: "echomem:setup-report", nonce: nonce, envelope: reportEnvelope }, location.origin);
|
|
96
|
+
} catch (_) {}
|
|
97
|
+
}
|
|
98
|
+
function scheduleReportToCity() {
|
|
99
|
+
sendReportToCity();
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/* ---------- report shell (parent owns report state; city renders it) ---------- */
|
|
103
|
+
// The report page (full-width city hero + detail sections below) is mounted ONCE. The city iframe
|
|
104
|
+
// shows its OWN in-canvas loading state while it scans, then fades into the rendered report — one
|
|
105
|
+
// surface, no separate loading page, no reload. The parent only fills the detail sections on /report.
|
|
106
|
+
function mountReportShell() {
|
|
107
|
+
setCityMode(true);
|
|
108
|
+
app.className = "";
|
|
109
|
+
app.innerHTML =
|
|
110
|
+
'<header class="reportTop">' +
|
|
111
|
+
'<span class="yeahLogo"><i>Yeah!</i><b>Echo</b></span>' +
|
|
112
|
+
'<nav class="navCenter">' +
|
|
113
|
+
'<span class="navPill">' +
|
|
114
|
+
'<span class="navTitle"><img class="navTitleFace" src="/hud-assets/echo-face-cutout.png" alt="" />Agent Doctor</span>' +
|
|
115
|
+
'<span class="navDates" id="report-period"></span>' +
|
|
116
|
+
'</span>' +
|
|
117
|
+
'<span class="navLocal">' +
|
|
118
|
+
'<svg viewBox="0 0 12 12" fill="none" aria-hidden="true"><rect x="2" y="5" width="8" height="5.5" rx="1.2" stroke="currentColor" stroke-width="1.3"/><path d="M4 5V3.8a2 2 0 0 1 4 0V5" stroke="currentColor" stroke-width="1.3"/></svg>' +
|
|
119
|
+
'Local Only' +
|
|
120
|
+
'</span>' +
|
|
121
|
+
'</nav>' +
|
|
122
|
+
'<button class="navConnect" data-connect-echo><span class="navDot"></span>Start Now' +
|
|
123
|
+
'<svg viewBox="0 0 12 12" fill="none" aria-hidden="true"><path d="M3.5 8.5 8.5 3.5M4.5 3.5h4v4" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>' +
|
|
124
|
+
'</button>' +
|
|
125
|
+
'</header>' +
|
|
126
|
+
'<section class="cityHero"><div class="cityFigureRow">' +
|
|
127
|
+
'<figure class="cityFig">' +
|
|
128
|
+
'<div class="cityShell" id="city-shell">' +
|
|
129
|
+
'<iframe id="city-report-frame" title="AI Coding City" src="/city/echo-ai-city-only.html?setup=1&figure=1&nonce=' + encodeURIComponent(nonce) + (previewState ? '&preview=1' : '') + '"></iframe>' +
|
|
130
|
+
'</div>' +
|
|
131
|
+
'<button class="cityFullBtn" id="city-full-btn" type="button">' +
|
|
132
|
+
'<svg viewBox="0 0 12 12" fill="none" aria-hidden="true"><path d="M7.5 1.5h3v3M4.5 10.5h-3v-3M10.5 1.5 7 5M1.5 10.5 5 7" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"/></svg>' +
|
|
133
|
+
'Enter' +
|
|
134
|
+
'</button>' +
|
|
135
|
+
'</figure>' +
|
|
136
|
+
'<div class="cityNote" id="city-fieldnote"></div>' +
|
|
137
|
+
'</div></section>' +
|
|
138
|
+
'<div class="cityFullBackdrop" id="city-full-backdrop" hidden></div>' +
|
|
139
|
+
'<div class="cityFullChrome" id="city-full-chrome" hidden>' +
|
|
140
|
+
'<div class="cfc-bar">' +
|
|
141
|
+
'<div class="cfc-left">' +
|
|
142
|
+
'<div class="cfc-toggle" role="group" aria-label="City state">' +
|
|
143
|
+
'<button type="button" data-city-state="without" class="on"><i class="cfc-dot"></i>Dirty — today</button>' +
|
|
144
|
+
'<button type="button" data-city-state="with"><img class="cfc-face" src="/hud-assets/echo-face-cutout.png" alt="" />Clean — with Echo</button>' +
|
|
145
|
+
'</div>' +
|
|
146
|
+
'<div class="cfc-toggle" role="group" aria-label="City metric">' +
|
|
147
|
+
'<button type="button" data-city-metric="time" class="on">Time</button>' +
|
|
148
|
+
'<button type="button" data-city-metric="tokens">Tokens</button>' +
|
|
149
|
+
'<button type="button" data-city-metric="cost">API $</button>' +
|
|
150
|
+
'</div>' +
|
|
151
|
+
'</div>' +
|
|
152
|
+
'<div class="cfc-legend" id="cfc-legend"></div>' +
|
|
153
|
+
'<button class="cfc-close" type="button" aria-label="Exit fullscreen">×</button>' +
|
|
154
|
+
'</div>' +
|
|
155
|
+
'<div class="cfc-stats" id="cfc-stats"></div>' +
|
|
156
|
+
'</div>' +
|
|
157
|
+
'<div id="setup-details" class="detailsWrap"></div>';
|
|
158
|
+
bindCityFieldnote();
|
|
159
|
+
bindConnect();
|
|
160
|
+
bindCityFullscreen();
|
|
161
|
+
var cityFrame = document.getElementById("city-report-frame");
|
|
162
|
+
if (cityFrame) cityFrame.addEventListener("load", sendReportToCity);
|
|
163
|
+
reportMounted = true;
|
|
164
|
+
}
|
|
165
|
+
function resetReportSurface() {
|
|
166
|
+
reportMounted = false;
|
|
167
|
+
document.body.classList.remove("cityFull");
|
|
168
|
+
setScanMode(false);
|
|
169
|
+
setCityMode(false);
|
|
170
|
+
}
|
|
171
|
+
function renderLoading() {
|
|
172
|
+
var progress = reportEnvelope && reportEnvelope.kind === "scanning" ? (reportEnvelope.progress || {}) : {};
|
|
173
|
+
var scanned = Math.max(0, Number(progress.scanned || 0));
|
|
174
|
+
var total = Math.max(0, Number(progress.total || 0));
|
|
175
|
+
var pct = total > 0 ? Math.min(100, Math.round((scanned / total) * 100)) : 0;
|
|
176
|
+
var elapsed = Number(progress.stageElapsedMs || 0);
|
|
177
|
+
var unit = progress.stage === "reading-transcripts" ? "transcript files" : "sessions";
|
|
178
|
+
var stage = progress.label || "Starting local scan";
|
|
179
|
+
var caption = total > 0
|
|
180
|
+
? stage + " " + number(scanned) + " / " + number(total) + " " + unit + (elapsed > 0 ? " · " + Math.round(elapsed / 1000) + "s" : "")
|
|
181
|
+
: stage + "…";
|
|
182
|
+
var fill = document.getElementById("scan-load-fill");
|
|
183
|
+
if (app.className === "scanLoading" && fill) {
|
|
184
|
+
fill.style.width = pct + "%";
|
|
185
|
+
fill.parentElement.setAttribute("aria-valuenow", String(pct));
|
|
186
|
+
var existingCaption = document.getElementById("scan-load-caption");
|
|
187
|
+
if (existingCaption) existingCaption.textContent = caption;
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
setScanMode(true);
|
|
191
|
+
setHead("Reading your local coding history", "Local scan");
|
|
192
|
+
app.className = "scanLoading";
|
|
193
|
+
app.innerHTML =
|
|
194
|
+
'<div class="scanLoader" aria-live="polite">' +
|
|
195
|
+
'<p class="scanEyebrow">Local scan</p>' +
|
|
196
|
+
'<h2>Reading your local coding history</h2>' +
|
|
197
|
+
'<div class="scanProgress" role="progressbar" aria-label="Local scan progress" aria-valuemin="0" aria-valuemax="100" aria-valuenow="' + pct + '">' +
|
|
198
|
+
'<i id="scan-load-fill" class="scanProgressFill" style="width:' + pct + '%"></i>' +
|
|
199
|
+
'</div>' +
|
|
200
|
+
'<p id="scan-load-caption" class="scanCaption">' + esc(caption) + '</p>' +
|
|
201
|
+
'</div>';
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
`;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { SETUP_PAGE_CLIENT_REPORT_CITY } from "./client-report-city.js";
|
|
2
|
+
import { SETUP_PAGE_CLIENT_REPORT_AUDIT } from "./client-report-audit.js";
|
|
3
|
+
export const SETUP_PAGE_CLIENT_REPORT = [
|
|
4
|
+
SETUP_PAGE_CLIENT_REPORT_CITY,
|
|
5
|
+
SETUP_PAGE_CLIENT_REPORT_AUDIT,
|
|
6
|
+
].join("\n");
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { SETUP_PAGE_CLIENT_CORE } from "./client-core.js";
|
|
2
|
+
import { SETUP_PAGE_CLIENT_REPORT } from "./client-report.js";
|
|
3
|
+
import { SETUP_PAGE_CLIENT_EXTRACTION } from "./client-extraction.js";
|
|
4
|
+
import { SETUP_PAGE_CLIENT_LIFECYCLE } from "./client-lifecycle.js";
|
|
5
|
+
export function renderSetupPageClient(previewState, startupScript) {
|
|
6
|
+
const body = [
|
|
7
|
+
SETUP_PAGE_CLIENT_CORE,
|
|
8
|
+
SETUP_PAGE_CLIENT_REPORT,
|
|
9
|
+
SETUP_PAGE_CLIENT_EXTRACTION,
|
|
10
|
+
SETUP_PAGE_CLIENT_LIFECYCLE,
|
|
11
|
+
].join("\n")
|
|
12
|
+
.replace("__ECHO_SETUP_PREVIEW_STATE__", JSON.stringify(previewState))
|
|
13
|
+
.replace("__ECHO_SETUP_STARTUP__", startupScript);
|
|
14
|
+
return ` <script>\n (function () {\n${body} })();\n </script>`;
|
|
15
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { SETUP_PAGE_STYLES } from "./styles.js";
|
|
2
|
+
import { renderSetupPageClient } from "./client.js";
|
|
3
|
+
const SETUP_PAGE_BODY = String.raw `
|
|
4
|
+
</head>
|
|
5
|
+
<body>
|
|
6
|
+
<main>
|
|
7
|
+
<header>
|
|
8
|
+
<div>
|
|
9
|
+
<p class="kicker">EchoMem device setup</p>
|
|
10
|
+
<h1 id="title">Turn history into memory</h1>
|
|
11
|
+
</div>
|
|
12
|
+
<div class="pill" id="status">Local</div>
|
|
13
|
+
</header>
|
|
14
|
+
<section id="app" class="state">Starting local scan...</section>
|
|
15
|
+
</main>
|
|
16
|
+
<dialog id="echo-note" class="echo-note-dlg">
|
|
17
|
+
<button class="nclose" aria-label="Close">×</button>
|
|
18
|
+
<p class="nk" id="echo-note-k"></p>
|
|
19
|
+
<h2 class="nt" id="echo-note-t"></h2>
|
|
20
|
+
<div class="nb" id="echo-note-b"></div>
|
|
21
|
+
</dialog>
|
|
22
|
+
`;
|
|
23
|
+
export function renderSetupPageDocument(options) {
|
|
24
|
+
return `<!doctype html>
|
|
25
|
+
<html lang="en">
|
|
26
|
+
<head>
|
|
27
|
+
<meta charset="utf-8" />
|
|
28
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
29
|
+
<title>EchoMem Device Setup</title>
|
|
30
|
+
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
31
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
32
|
+
<link href="https://fonts.googleapis.com/css2?family=Caveat:wght@500;700&display=swap" rel="stylesheet" />
|
|
33
|
+
<style>
|
|
34
|
+
${SETUP_PAGE_STYLES} </style>${SETUP_PAGE_BODY}${renderSetupPageClient(options.previewState, options.startupScript)}
|
|
35
|
+
</body>
|
|
36
|
+
</html>`;
|
|
37
|
+
}
|