@echomem/mcp 1.4.7 → 1.4.9

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 (46) hide show
  1. package/README.md +35 -9
  2. package/assets/canonical-scorer/README.md +18 -0
  3. package/assets/canonical-scorer/analyze-10-problems.mjs +857 -0
  4. package/assets/canonical-scorer/build-session-waste-dashboard.mjs +1628 -0
  5. package/assets/canonical-scorer/golden_anchors.mjs +83 -0
  6. package/assets/canonical-scorer/optimizable_detail.mjs +633 -0
  7. package/assets/hud/claude.svg +1 -0
  8. package/assets/hud/codex.svg +1 -0
  9. package/assets/hud/session-viewer.html +35 -0
  10. package/dist/city/chaos-to-clarity-pencil.html +582 -0
  11. package/dist/city/echo-ai-city-only.html +1126 -109
  12. package/dist/city/echo-ai-city-only.template.html +1126 -109
  13. package/dist/city/echo-face-cutout.png +0 -0
  14. package/dist/city/pencil-pie-generator.html +883 -0
  15. package/dist/city/pencil-webgl-landscape.html +1239 -0
  16. package/dist/city/spatial-fan-story.html +479 -0
  17. package/dist/codex-session-files.js +283 -0
  18. package/dist/codex-sync.js +7 -2
  19. package/dist/context-analysis/canonical-golden.js +47 -0
  20. package/dist/context-analysis/claude-native-canonical.js +1193 -0
  21. package/dist/context-analysis/vendored-canonical.js +793 -0
  22. package/dist/context-analysis/workspace-report.js +1838 -0
  23. package/dist/context-metrics/calculate.js +56 -0
  24. package/dist/context-metrics/model-limits.js +26 -0
  25. package/dist/context-metrics/types.js +1 -0
  26. package/dist/forensics-10-problems.js +7 -6
  27. package/dist/forensics.js +863 -132
  28. package/dist/hud/adapters.js +8 -4
  29. package/dist/hud/autostart.js +66 -0
  30. package/dist/hud/cli.js +31 -0
  31. package/dist/hud/electron-main.js +182 -19
  32. package/dist/hud/metric.js +13 -4
  33. package/dist/hud/monitor.js +171 -84
  34. package/dist/hud/preload.cjs +3 -0
  35. package/dist/hud/server.js +321 -4
  36. package/dist/hud/web.js +880 -270
  37. package/dist/index.js +122 -24
  38. package/dist/local-data-paths.js +87 -0
  39. package/dist/migrate.js +55 -29
  40. package/dist/report.js +101 -40
  41. package/dist/setup-page.js +4257 -245
  42. package/dist/setup-preview.js +245 -0
  43. package/dist/setup.js +786 -75
  44. package/dist/v1-contract.js +20 -2
  45. package/package.json +6 -4
  46. package/templates/echomem-recall.md +2 -2
@@ -0,0 +1,479 @@
1
+ <!doctype html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <title>The Shape of Noise — Spatial Fan Study</title>
7
+ <style>
8
+ :root {
9
+ --ink: #252720;
10
+ --muted-ink: #6f7067;
11
+ --paper: #f3f0e8;
12
+ --paper-light: #faf8f2;
13
+ --blue: #7f94bd;
14
+ --blue-dark: #5e7198;
15
+ --blue-light: #b8c5dc;
16
+ --line: rgba(37, 39, 32, 0.22);
17
+ }
18
+
19
+ * { box-sizing: border-box; }
20
+
21
+ html { background: #d9d6ce; }
22
+
23
+ body {
24
+ margin: 0;
25
+ min-height: 100vh;
26
+ color: var(--ink);
27
+ background:
28
+ radial-gradient(circle at 18% 8%, rgba(255,255,255,.72), transparent 28rem),
29
+ #d9d6ce;
30
+ font-family: Georgia, "Times New Roman", "Noto Serif SC", serif;
31
+ }
32
+
33
+ button { font: inherit; }
34
+
35
+ .page {
36
+ position: relative;
37
+ width: min(100%, 980px);
38
+ margin: 0 auto;
39
+ min-height: 100vh;
40
+ overflow: hidden;
41
+ background:
42
+ repeating-linear-gradient(7deg, transparent 0 8px, rgba(41,43,36,.018) 9px 10px),
43
+ radial-gradient(ellipse at 50% 0%, rgba(255,255,255,.78), transparent 55%),
44
+ var(--paper);
45
+ box-shadow: 0 20px 70px rgba(35, 37, 31, .16);
46
+ }
47
+
48
+ .page::after {
49
+ content: "";
50
+ position: absolute;
51
+ inset: 0;
52
+ pointer-events: none;
53
+ opacity: .15;
54
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='90' height='90' viewBox='0 0 90 90'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.8' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='.22'/%3E%3C/svg%3E");
55
+ mix-blend-mode: multiply;
56
+ }
57
+
58
+ header {
59
+ position: relative;
60
+ z-index: 2;
61
+ display: grid;
62
+ grid-template-columns: 1fr auto;
63
+ gap: 2rem;
64
+ align-items: end;
65
+ padding: 68px clamp(28px, 7vw, 76px) 28px;
66
+ }
67
+
68
+ .eyebrow,
69
+ .metric-label,
70
+ .step,
71
+ .legend,
72
+ .view-toggle {
73
+ font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
74
+ text-transform: uppercase;
75
+ letter-spacing: .15em;
76
+ }
77
+
78
+ .eyebrow {
79
+ margin: 0 0 10px;
80
+ color: var(--blue-dark);
81
+ font-size: 11px;
82
+ font-weight: 700;
83
+ }
84
+
85
+ h1 {
86
+ margin: 0;
87
+ max-width: 650px;
88
+ font-weight: 500;
89
+ font-size: clamp(40px, 7vw, 76px);
90
+ line-height: .88;
91
+ letter-spacing: -.055em;
92
+ }
93
+
94
+ .intro {
95
+ max-width: 270px;
96
+ margin: 0 0 4px;
97
+ color: var(--muted-ink);
98
+ font-size: 13px;
99
+ line-height: 1.65;
100
+ }
101
+
102
+ .view-toggle {
103
+ position: relative;
104
+ z-index: 3;
105
+ display: inline-flex;
106
+ gap: 8px;
107
+ align-items: center;
108
+ border: 1px solid rgba(37,39,32,.45);
109
+ border-radius: 999px;
110
+ margin: 2px clamp(28px, 7vw, 76px) 10px;
111
+ padding: 8px 12px;
112
+ color: var(--ink);
113
+ background: rgba(250,248,242,.6);
114
+ cursor: pointer;
115
+ font-size: 9px;
116
+ transition: background .2s ease, transform .2s ease;
117
+ }
118
+
119
+ .view-toggle:hover { background: var(--paper-light); transform: translateY(-1px); }
120
+ .view-toggle:focus-visible { outline: 2px solid var(--blue-dark); outline-offset: 3px; }
121
+
122
+ .view-toggle i {
123
+ width: 7px;
124
+ height: 7px;
125
+ border-radius: 50%;
126
+ background: var(--blue-dark);
127
+ box-shadow: 0 0 0 3px rgba(127,148,189,.2);
128
+ }
129
+
130
+ .story {
131
+ position: relative;
132
+ z-index: 1;
133
+ padding: 8px clamp(20px, 5vw, 54px) 76px;
134
+ }
135
+
136
+ .story-path {
137
+ position: absolute;
138
+ z-index: 0;
139
+ top: 0;
140
+ bottom: 80px;
141
+ left: 50%;
142
+ width: 118px;
143
+ transform: translateX(-50%);
144
+ opacity: .62;
145
+ pointer-events: none;
146
+ }
147
+
148
+ .story-path path {
149
+ fill: none;
150
+ stroke: var(--ink);
151
+ stroke-width: 1.15;
152
+ stroke-dasharray: 5 7;
153
+ vector-effect: non-scaling-stroke;
154
+ }
155
+
156
+ .scene {
157
+ --side: 0;
158
+ position: relative;
159
+ z-index: 1;
160
+ display: grid;
161
+ grid-template-columns: minmax(180px, .8fr) minmax(390px, 1.5fr);
162
+ min-height: 286px;
163
+ align-items: center;
164
+ border-top: 1px solid rgba(37,39,32,.2);
165
+ }
166
+
167
+ .scene:first-of-type { border-top-color: rgba(37,39,32,.42); }
168
+
169
+ .scene:nth-of-type(even) {
170
+ grid-template-columns: minmax(390px, 1.5fr) minmax(180px, .8fr);
171
+ }
172
+
173
+ .scene:nth-of-type(even) .copy { order: 2; text-align: right; }
174
+ .scene:nth-of-type(even) .landscape { order: 1; }
175
+
176
+ .copy { padding: 34px clamp(12px, 3vw, 30px); }
177
+
178
+ .step {
179
+ display: block;
180
+ margin-bottom: 16px;
181
+ color: var(--muted-ink);
182
+ font-size: 9px;
183
+ }
184
+
185
+ .value {
186
+ display: block;
187
+ font-size: clamp(46px, 7vw, 72px);
188
+ line-height: .8;
189
+ letter-spacing: -.06em;
190
+ }
191
+
192
+ .metric-label {
193
+ display: block;
194
+ margin-top: 14px;
195
+ font-size: 9px;
196
+ color: var(--muted-ink);
197
+ }
198
+
199
+ .landscape {
200
+ position: relative;
201
+ height: 258px;
202
+ perspective: 900px;
203
+ }
204
+
205
+ .landscape svg {
206
+ width: 100%;
207
+ height: 100%;
208
+ overflow: visible;
209
+ }
210
+
211
+ .terrain-line,
212
+ .contour,
213
+ .platform-line {
214
+ fill: none;
215
+ stroke: var(--ink);
216
+ stroke-linecap: round;
217
+ stroke-linejoin: round;
218
+ vector-effect: non-scaling-stroke;
219
+ }
220
+
221
+ .terrain-line { stroke-width: 1.25; }
222
+ .contour { stroke-width: .72; opacity: .34; }
223
+ .platform-line { stroke-width: 1.3; }
224
+
225
+ .hatch { stroke: var(--ink); stroke-width: .65; opacity: .34; }
226
+ .platform-side { fill: #dedbd2; stroke: var(--ink); stroke-width: 1.2; }
227
+ .platform-top { fill: #f8f6ef; stroke: var(--ink); stroke-width: 1.4; }
228
+ .fan-side { fill: var(--blue-dark); stroke: var(--ink); stroke-width: 1.15; }
229
+ .fan-top { fill: var(--blue); stroke: var(--ink); stroke-width: 1.35; }
230
+
231
+ .fan-group {
232
+ filter: drop-shadow(0 15px 11px rgba(37,39,32,.17));
233
+ transform-origin: center;
234
+ transition: transform .42s cubic-bezier(.2,.8,.2,1), filter .42s ease;
235
+ }
236
+
237
+ .scene:hover .fan-group {
238
+ transform: translateY(-7px);
239
+ filter: drop-shadow(0 22px 14px rgba(37,39,32,.22));
240
+ }
241
+
242
+ .fan-top {
243
+ transform-origin: center;
244
+ animation: settle .8s both cubic-bezier(.2,.8,.2,1);
245
+ animation-delay: calc(var(--i) * 100ms);
246
+ }
247
+
248
+ .node { fill: var(--ink); }
249
+ .node-halo { fill: none; stroke: var(--ink); opacity: .18; }
250
+ .sun-ray { stroke: var(--ink); stroke-width: .8; opacity: .65; }
251
+
252
+ .legend {
253
+ position: relative;
254
+ z-index: 2;
255
+ display: flex;
256
+ justify-content: space-between;
257
+ gap: 24px;
258
+ margin: 0 clamp(28px, 7vw, 76px);
259
+ padding: 18px 0 42px;
260
+ border-top: 1px solid rgba(37,39,32,.42);
261
+ color: var(--muted-ink);
262
+ font-size: 8px;
263
+ line-height: 1.65;
264
+ }
265
+
266
+ .legend strong { color: var(--ink); font-weight: 500; }
267
+
268
+ .page.elevation .fan-group { transform: translateY(-10px); filter: drop-shadow(0 26px 16px rgba(37,39,32,.24)); }
269
+ .page.elevation .platform-side { fill: #d5d1c8; }
270
+
271
+ @keyframes settle {
272
+ from { opacity: 0; transform: translateY(-16px); }
273
+ to { opacity: 1; transform: translateY(0); }
274
+ }
275
+
276
+ @media (max-width: 680px) {
277
+ header { grid-template-columns: 1fr; padding-top: 48px; }
278
+ h1 { max-width: 420px; }
279
+ .intro { max-width: 390px; }
280
+ .scene,
281
+ .scene:nth-of-type(even) {
282
+ grid-template-columns: 112px minmax(210px, 1fr);
283
+ min-height: 248px;
284
+ }
285
+ .scene:nth-of-type(even) .copy { order: 1; text-align: left; }
286
+ .scene:nth-of-type(even) .landscape { order: 2; }
287
+ .copy { padding-inline: 8px; }
288
+ .landscape { height: 220px; }
289
+ .story-path { left: 43%; }
290
+ .legend { flex-direction: column; }
291
+ }
292
+
293
+ @media (prefers-reduced-motion: reduce) {
294
+ *, *::before, *::after { animation: none !important; transition: none !important; }
295
+ }
296
+ </style>
297
+ </head>
298
+ <body>
299
+ <main class="page" id="page">
300
+ <header>
301
+ <div>
302
+ <p class="eyebrow">A topographic study · 01—04</p>
303
+ <h1>The shape<br>of noise.</h1>
304
+ </div>
305
+ <p class="intro">四个比例不再是平面的切片,而是沿途抬起的地形。阴影表达高度,实线确认边界,路径让数据获得时间。</p>
306
+ </header>
307
+
308
+ <button class="view-toggle" id="viewToggle" type="button" aria-pressed="false">
309
+ <i aria-hidden="true"></i><span>Lift the terrain</span>
310
+ </button>
311
+
312
+ <section class="story" aria-label="Noise ratio journey">
313
+ <svg class="story-path" viewBox="0 0 118 1120" preserveAspectRatio="none" aria-hidden="true">
314
+ <path d="M62 8 C 4 130, 110 208, 49 315 S 103 510, 54 614 S 6 816, 66 929 S 48 1058, 58 1112"/>
315
+ </svg>
316
+
317
+ <article class="scene" data-value="28">
318
+ <div class="copy">
319
+ <span class="step">Station 01</span>
320
+ <strong class="value">28%</strong>
321
+ <span class="metric-label">of noise · first ridge</span>
322
+ </div>
323
+ <div class="landscape" aria-label="28 percent three-dimensional fan region"></div>
324
+ </article>
325
+
326
+ <article class="scene" data-value="25">
327
+ <div class="copy">
328
+ <span class="step">Station 02</span>
329
+ <strong class="value">25%</strong>
330
+ <span class="metric-label">of noise · crossing</span>
331
+ </div>
332
+ <div class="landscape" aria-label="25 percent three-dimensional fan region"></div>
333
+ </article>
334
+
335
+ <article class="scene" data-value="23">
336
+ <div class="copy">
337
+ <span class="step">Station 03</span>
338
+ <strong class="value">23%</strong>
339
+ <span class="metric-label">of noise · lower valley</span>
340
+ </div>
341
+ <div class="landscape" aria-label="23 percent three-dimensional fan region"></div>
342
+ </article>
343
+
344
+ <article class="scene" data-value="17">
345
+ <div class="copy">
346
+ <span class="step">Station 04</span>
347
+ <strong class="value">17%</strong>
348
+ <span class="metric-label">of noise · clear horizon</span>
349
+ </div>
350
+ <div class="landscape" aria-label="17 percent three-dimensional fan region"></div>
351
+ </article>
352
+ </section>
353
+
354
+ <footer class="legend">
355
+ <span><strong>Solid line</strong><br>measured boundary</span>
356
+ <span><strong>Shadow</strong><br>spatial elevation</span>
357
+ <span><strong>Dashed path</strong><br>change through time</span>
358
+ <span>EchoMem · visual field study</span>
359
+ </footer>
360
+ </main>
361
+
362
+ <script>
363
+ const NS = "http://www.w3.org/2000/svg";
364
+ const scenes = [...document.querySelectorAll(".scene")];
365
+
366
+ function point(cx, cy, rx, ry, degrees) {
367
+ const radians = (degrees - 90) * Math.PI / 180;
368
+ return { x: cx + rx * Math.cos(radians), y: cy + ry * Math.sin(radians) };
369
+ }
370
+
371
+ function sectorPath(cx, cy, rx, ry, percent, yOffset = 0) {
372
+ const start = point(cx, cy + yOffset, rx, ry, 0);
373
+ const end = point(cx, cy + yOffset, rx, ry, percent * 3.6);
374
+ const largeArc = percent > 50 ? 1 : 0;
375
+ return `M ${cx} ${cy + yOffset} L ${start.x} ${start.y} A ${rx} ${ry} 0 ${largeArc} 1 ${end.x} ${end.y} Z`;
376
+ }
377
+
378
+ function el(name, attrs = {}) {
379
+ const node = document.createElementNS(NS, name);
380
+ Object.entries(attrs).forEach(([key, value]) => node.setAttribute(key, value));
381
+ return node;
382
+ }
383
+
384
+ const terrains = [
385
+ "M8 181 C58 169 80 121 129 133 C173 144 187 98 230 107 C282 118 304 70 355 88 C403 106 438 73 492 92",
386
+ "M8 160 C55 146 84 104 132 120 C179 137 205 171 248 151 C295 128 322 80 373 100 C418 118 445 97 492 112",
387
+ "M8 148 C53 125 90 143 124 158 C169 179 194 136 235 132 C282 128 314 164 356 153 C405 140 438 103 492 119",
388
+ "M8 169 C55 151 92 161 130 176 C175 194 209 170 248 154 C294 136 329 146 368 132 C414 115 451 82 492 96"
389
+ ];
390
+
391
+ function renderScene(scene, index) {
392
+ const percent = Number(scene.dataset.value);
393
+ const svg = el("svg", { viewBox: "0 0 500 250", role: "img" });
394
+ const title = el("title");
395
+ title.textContent = `${percent}% of noise shown as an elevated fan-shaped terrain`;
396
+ svg.append(title);
397
+
398
+ const defs = el("defs");
399
+ const fade = el("linearGradient", { id: `fade-${index}`, x1: "0", y1: "0", x2: "0", y2: "1" });
400
+ fade.append(el("stop", { offset: "0", "stop-color": "#c9c7bd", "stop-opacity": ".06" }));
401
+ fade.append(el("stop", { offset: "1", "stop-color": "#777970", "stop-opacity": ".22" }));
402
+ defs.append(fade);
403
+ svg.append(defs);
404
+
405
+ const terrainFill = el("path", {
406
+ d: `${terrains[index]} L492 232 L8 232 Z`,
407
+ fill: `url(#fade-${index})`
408
+ });
409
+ svg.append(terrainFill);
410
+ svg.append(el("path", { d: terrains[index], class: "terrain-line" }));
411
+
412
+ for (let c = 0; c < 6; c++) {
413
+ svg.append(el("path", {
414
+ d: terrains[index].replaceAll(/(\d+) (\d+)/g, (match, x, y) => `${x} ${Number(y) + 8 + c * 7}`),
415
+ class: "contour"
416
+ }));
417
+ }
418
+
419
+ const cx = index % 2 === 0 ? 306 : 211;
420
+ const cy = 132 + index * 3;
421
+ const rx = 88;
422
+ const ry = 43;
423
+ const depth = 18;
424
+
425
+ const fanEndTop = point(cx, cy - 7, rx, ry, percent * 3.6);
426
+ const fanEndBottom = point(cx, cy + depth - 7, rx, ry, percent * 3.6);
427
+ const fanStartTop = point(cx, cy - 7, rx, ry, 0);
428
+ const fanStartBottom = point(cx, cy + depth - 7, rx, ry, 0);
429
+
430
+ const platform = el("g", { class: "fan-group", style: `--i:${index}` });
431
+ platform.append(el("path", {
432
+ d: `M ${cx-rx} ${cy} A ${rx} ${ry} 0 0 0 ${cx+rx} ${cy} L ${cx+rx} ${cy+depth} A ${rx} ${ry} 0 0 1 ${cx-rx} ${cy+depth} Z`,
433
+ class: "platform-side"
434
+ }));
435
+ platform.append(el("ellipse", { cx, cy, rx, ry, class: "platform-top" }));
436
+
437
+ const visibleSide = `M ${cx} ${cy-7} L ${fanEndTop.x} ${fanEndTop.y} L ${fanEndBottom.x} ${fanEndBottom.y} L ${cx} ${cy+depth-7} Z`;
438
+ platform.append(el("path", { d: visibleSide, class: "fan-side" }));
439
+ platform.append(el("path", { d: sectorPath(cx, cy, rx, ry, percent, -7), class: "fan-top" }));
440
+
441
+ for (let h = 0; h < 9; h++) {
442
+ const x = cx - rx + 18 + h * 18;
443
+ platform.append(el("line", { x1: x, y1: cy + 8, x2: x - 7, y2: cy + depth + 7, class: "hatch" }));
444
+ }
445
+
446
+ platform.append(el("circle", { cx, cy: cy - 7, r: 4.1, class: "node" }));
447
+ platform.append(el("circle", { cx, cy: cy - 7, r: 11, class: "node-halo" }));
448
+ svg.append(platform);
449
+
450
+ const routeStartX = index % 2 === 0 ? 38 : 452;
451
+ const routeEndX = cx;
452
+ svg.append(el("path", {
453
+ d: `M${routeStartX} 218 C${(routeStartX+routeEndX)/2} 206, ${routeEndX-28} ${cy+66}, ${routeEndX} ${cy+19}`,
454
+ class: "platform-line",
455
+ "stroke-dasharray": "4 7"
456
+ }));
457
+
458
+ if (index === 3) {
459
+ svg.append(el("circle", { cx: 457, cy: 51, r: 10, fill: "none", stroke: "#252720", "stroke-width": "1.1" }));
460
+ [[457,28,457,17],[457,74,457,85],[434,51,423,51],[480,51,491,51],[441,35,433,27],[473,35,481,27]].forEach(v => {
461
+ svg.append(el("line", { x1:v[0], y1:v[1], x2:v[2], y2:v[3], class:"sun-ray" }));
462
+ });
463
+ }
464
+
465
+ scene.querySelector(".landscape").append(svg);
466
+ }
467
+
468
+ scenes.forEach(renderScene);
469
+
470
+ const page = document.getElementById("page");
471
+ const toggle = document.getElementById("viewToggle");
472
+ toggle.addEventListener("click", () => {
473
+ const lifted = page.classList.toggle("elevation");
474
+ toggle.setAttribute("aria-pressed", String(lifted));
475
+ toggle.querySelector("span").textContent = lifted ? "Settle the terrain" : "Lift the terrain";
476
+ });
477
+ </script>
478
+ </body>
479
+ </html>