@monoes/monomindcli 1.10.32 → 1.10.33
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.
|
@@ -6,7 +6,7 @@ module.exports = {
|
|
|
6
6
|
var intelligence = hCtx.intelligence;
|
|
7
7
|
var args = hCtx.args;
|
|
8
8
|
if (intelligence && intelligence.stats) {
|
|
9
|
-
await Promise.resolve(intelligence.stats(args.includes('--json')));
|
|
9
|
+
try { await Promise.resolve(intelligence.stats(args.includes('--json'))); } catch (e) { /* non-fatal */ }
|
|
10
10
|
} else {
|
|
11
11
|
console.log('[WARN] Intelligence module not available. Run session-restore first.');
|
|
12
12
|
}
|
|
@@ -1164,14 +1164,25 @@ function readMode() {
|
|
|
1164
1164
|
return 'full'; // default
|
|
1165
1165
|
}
|
|
1166
1166
|
|
|
1167
|
+
// ─── Testability export (when required as a module, not run as CLI) ──────────
|
|
1168
|
+
if (require.main !== module) {
|
|
1169
|
+
module.exports = {
|
|
1170
|
+
readJSON, safeStat, modelLabel,
|
|
1171
|
+
getSecurityStatus, getSwarmStatus, getADRStatus,
|
|
1172
|
+
getHooksStatus, getActiveAgent, getAgentDBStats,
|
|
1173
|
+
getLearningStats, getTestStats, getIntegrationStatus,
|
|
1174
|
+
generateJSON,
|
|
1175
|
+
};
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1167
1178
|
// ─── Main ───────────────────────────────────────────────────────
|
|
1168
|
-
if (process.argv.includes('--json')) {
|
|
1179
|
+
if (require.main === module && process.argv.includes('--json')) {
|
|
1169
1180
|
console.log(JSON.stringify(generateJSON(), null, 2));
|
|
1170
|
-
} else if (process.argv.includes('--compact')) {
|
|
1181
|
+
} else if (require.main === module && process.argv.includes('--compact')) {
|
|
1171
1182
|
console.log(JSON.stringify(generateJSON()));
|
|
1172
|
-
} else if (process.argv.includes('--single-line')) {
|
|
1183
|
+
} else if (require.main === module && process.argv.includes('--single-line')) {
|
|
1173
1184
|
console.log(generateStatusline());
|
|
1174
|
-
} else if (process.argv.includes('--toggle')) {
|
|
1185
|
+
} else if (require.main === module && process.argv.includes('--toggle')) {
|
|
1175
1186
|
// Toggle mode and print the new view
|
|
1176
1187
|
const current = readMode();
|
|
1177
1188
|
const next = current === 'compact' ? 'full' : 'compact';
|
|
@@ -1184,7 +1195,7 @@ if (process.argv.includes('--json')) {
|
|
|
1184
1195
|
} else {
|
|
1185
1196
|
console.log(generateDashboard());
|
|
1186
1197
|
}
|
|
1187
|
-
} else {
|
|
1198
|
+
} else if (require.main === module) {
|
|
1188
1199
|
// Default: respect mode state file
|
|
1189
1200
|
const mode = readMode();
|
|
1190
1201
|
if (mode === 'compact') {
|
|
@@ -74,7 +74,7 @@ html, body { height: 100%; background: var(--bg); color: var(--text-hi); font-fa
|
|
|
74
74
|
#feed-sess-nav { margin-left: auto; display: flex; gap: 6px; align-items: center; }
|
|
75
75
|
.sess-btn { font-size: 11px; color: var(--text-lo); background: var(--surface); border: 1px solid var(--border); border-radius: 4px; padding: 2px 8px; cursor: pointer; transition: color 0.1s; line-height: 1.4; }
|
|
76
76
|
.sess-btn:hover { color: var(--text-hi); }
|
|
77
|
-
#feed-scroll { flex: 1; overflow-y: auto; }
|
|
77
|
+
#feed-scroll { flex: 1; overflow-y: auto; min-width: 0; }
|
|
78
78
|
#feed-scroll::-webkit-scrollbar { width: 3px; }
|
|
79
79
|
#feed-scroll::-webkit-scrollbar-thumb { background: var(--border); border-radius: 2px; }
|
|
80
80
|
|
|
@@ -425,6 +425,85 @@ html, body { height: 100%; background: var(--bg); color: var(--text-hi); font-fa
|
|
|
425
425
|
.ph-mid { background:oklch(72% 0.18 75 / 0.15); color:oklch(78% 0.18 75); }
|
|
426
426
|
.ph-lo { background:oklch(60% 0.18 25 / 0.12); color:oklch(70% 0.18 25); }
|
|
427
427
|
|
|
428
|
+
/* ── ambient mode ────────────────────────────────────────── */
|
|
429
|
+
#app.ambient #sidebar,
|
|
430
|
+
#app.ambient #topbar,
|
|
431
|
+
#app.ambient #alerts-rail,
|
|
432
|
+
#app.ambient #feed-head,
|
|
433
|
+
#app.ambient #feed-time-filter,
|
|
434
|
+
#app.ambient #metrics-pane,
|
|
435
|
+
#app.ambient #replay-bar,
|
|
436
|
+
#app.ambient #feed-recap,
|
|
437
|
+
#app.ambient #feed-timeline { display:none !important; }
|
|
438
|
+
#app.ambient #main { background:var(--bg); }
|
|
439
|
+
#app.ambient #view-now { height:100vh; }
|
|
440
|
+
#app.ambient #feed-pane { border:none; }
|
|
441
|
+
#app.ambient .feed-entry { padding: 6px 22px; }
|
|
442
|
+
#app.ambient .feed-lbl { font-size:14px; }
|
|
443
|
+
#app-ambient-hint { display:none; position:fixed; bottom:16px; right:16px; font-size:11px; color:var(--text-xs); z-index:300; pointer-events:none; }
|
|
444
|
+
#app.ambient #app-ambient-hint { display:block; }
|
|
445
|
+
|
|
446
|
+
/* ── minimap scrubber ─────────────────────────────────────── */
|
|
447
|
+
#feed-minimap { position:absolute; top:0; right:0; width:8px; height:100%; z-index:10; cursor:pointer; }
|
|
448
|
+
#feed-minimap-track { position:absolute; inset:0; }
|
|
449
|
+
.mm-pip { position:absolute; right:1px; width:6px; border-radius:3px; min-height:3px; opacity:0.55; transition:opacity 0.1s; }
|
|
450
|
+
.mm-pip:hover { opacity:1; }
|
|
451
|
+
.mm-pip.mp-file { background:oklch(60% 0.12 220); }
|
|
452
|
+
.mm-pip.mp-bash { background:oklch(72% 0.18 75); }
|
|
453
|
+
.mm-pip.mp-agent { background:oklch(70% 0.15 300); }
|
|
454
|
+
.mm-pip.mp-mcp { background:oklch(65% 0.15 200); }
|
|
455
|
+
.mm-pip.mp-err { background:var(--red); opacity:0.8; }
|
|
456
|
+
.mm-pip.mp-user { background:var(--text-xs); }
|
|
457
|
+
.mm-pip.mp-other { background:var(--surface-hi); }
|
|
458
|
+
#mm-thumb { position:absolute; right:0; width:8px; background:oklch(100% 0 0 / 0.08); border-radius:4px; pointer-events:none; transition:top 0.05s; }
|
|
459
|
+
#feed-scroll-wrap { position:relative; flex:1; overflow:hidden; display:flex; }
|
|
460
|
+
#feed-scroll { flex:1; }
|
|
461
|
+
|
|
462
|
+
/* ── daily digest ─────────────────────────────────────────── */
|
|
463
|
+
#digest-card { display:none; flex-shrink:0; border-bottom:1px solid var(--border); background:oklch(13.5% 0.009 55); padding:10px 18px; }
|
|
464
|
+
#digest-card.show { display:block; }
|
|
465
|
+
.digest-row { display:flex; align-items:center; gap:6px; flex-wrap:wrap; }
|
|
466
|
+
.digest-title { font-size:11px; font-weight:600; letter-spacing:0.07em; text-transform:uppercase; color:var(--text-lo); margin-bottom:6px; display:flex; align-items:center; gap:8px; }
|
|
467
|
+
.digest-close { margin-left:auto; background:none; border:none; color:var(--text-xs); cursor:pointer; font-size:13px; line-height:1; padding:0; }
|
|
468
|
+
.digest-close:hover { color:var(--text-lo); }
|
|
469
|
+
.digest-stat { display:inline-flex; align-items:center; gap:4px; font-size:11px; padding:2px 8px; border-radius:8px; background:var(--surface-hi); color:var(--text-mid); white-space:nowrap; }
|
|
470
|
+
|
|
471
|
+
/* ── cost leaderboard ────────────────────────────────────── */
|
|
472
|
+
.lb-toggle { font-size:11px; color:var(--text-lo); background:transparent; border:1px solid transparent; border-radius:8px; padding:2px 9px; cursor:pointer; transition:color 0.1s, background 0.1s; font-family:var(--sans); }
|
|
473
|
+
.lb-toggle:hover { color:var(--text-hi); }
|
|
474
|
+
.lb-toggle.on { background:oklch(72% 0.18 75 / 0.1); color:var(--accent); border-color:oklch(72% 0.18 75 / 0.3); }
|
|
475
|
+
.lb-table { width:100%; border-collapse:collapse; margin-top:4px; }
|
|
476
|
+
.lb-table th { font-size:10px; letter-spacing:0.07em; text-transform:uppercase; color:var(--text-xs); padding:4px 6px; text-align:left; border-bottom:1px solid var(--border); }
|
|
477
|
+
.lb-table td { font-size:12px; padding:7px 6px; border-bottom:1px solid oklch(25% 0.008 55 / 0.5); color:var(--text-mid); vertical-align:top; cursor:pointer; }
|
|
478
|
+
.lb-table tr:hover td { background:var(--surface-hi); color:var(--text-hi); }
|
|
479
|
+
.lb-rank { font-family:var(--mono); color:var(--text-xs); width:22px; }
|
|
480
|
+
.lb-cost { font-family:var(--mono); color:oklch(78% 0.18 75); white-space:nowrap; }
|
|
481
|
+
.lb-dur { font-family:var(--mono); color:var(--text-lo); white-space:nowrap; }
|
|
482
|
+
.lb-prompt { max-width:260px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
|
|
483
|
+
|
|
484
|
+
/* ── session diff ─────────────────────────────────────────── */
|
|
485
|
+
.diff-toggle { font-size:11px; color:var(--text-lo); background:transparent; border:1px solid transparent; border-radius:8px; padding:2px 9px; cursor:pointer; transition:color 0.1s, background 0.1s; font-family:var(--sans); }
|
|
486
|
+
.diff-toggle:hover { color:var(--text-hi); }
|
|
487
|
+
.diff-toggle.on { background:oklch(60% 0.12 220 / 0.1); color:oklch(65% 0.12 220); border-color:oklch(60% 0.12 220 / 0.3); }
|
|
488
|
+
#diff-panel { display:none; border:1px solid var(--border); border-radius:8px; margin-bottom:16px; overflow:hidden; }
|
|
489
|
+
#diff-panel.show { display:block; }
|
|
490
|
+
.diff-header { display:flex; align-items:center; gap:10px; padding:8px 12px; border-bottom:1px solid var(--border); background:oklch(14% 0.009 55); }
|
|
491
|
+
.diff-title { font-size:11px; font-weight:600; letter-spacing:0.07em; text-transform:uppercase; color:var(--text-lo); flex:1; }
|
|
492
|
+
.diff-clear { background:none; border:none; color:var(--text-xs); cursor:pointer; font-size:12px; }
|
|
493
|
+
.diff-cols { display:grid; grid-template-columns:1fr 1fr; }
|
|
494
|
+
.diff-col { padding:10px 14px; }
|
|
495
|
+
.diff-col + .diff-col { border-left:1px solid var(--border); }
|
|
496
|
+
.diff-col-title { font-size:11px; font-weight:600; color:var(--text-mid); margin-bottom:8px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
|
|
497
|
+
.diff-row { display:flex; justify-content:space-between; gap:8px; padding:3px 0; border-bottom:1px solid oklch(25% 0.008 55 / 0.4); }
|
|
498
|
+
.diff-row:last-child { border-bottom:none; }
|
|
499
|
+
.diff-k { font-size:11px; color:var(--text-xs); }
|
|
500
|
+
.diff-v { font-size:11px; font-family:var(--mono); color:var(--text-mid); }
|
|
501
|
+
.diff-v.diff-hi { color:oklch(78% 0.18 75); }
|
|
502
|
+
.diff-v.diff-lo { color:var(--red); }
|
|
503
|
+
.diff-hint { font-size:11px; color:var(--text-xs); padding:8px 12px; text-align:center; }
|
|
504
|
+
.sess-row.diff-sel-a { background:oklch(60% 0.12 220 / 0.08); outline:1px solid oklch(60% 0.12 220 / 0.3); }
|
|
505
|
+
.sess-row.diff-sel-b { background:oklch(72% 0.18 75 / 0.06); outline:1px solid oklch(72% 0.18 75 / 0.3); }
|
|
506
|
+
|
|
428
507
|
/* ── budget cap ──────────────────────────────────────────── */
|
|
429
508
|
#budget-modal { display:none; position:fixed; inset:0; z-index:200; background:oklch(5% 0 0 / 0.6); align-items:center; justify-content:center; }
|
|
430
509
|
#budget-modal.open { display:flex; }
|
|
@@ -566,10 +645,20 @@ html, body { height: 100%; background: var(--bg); color: var(--text-hi); font-fa
|
|
|
566
645
|
<button class="tf-btn" data-tf="1h" onclick="setFeedTimeFilter('1h')">1h</button>
|
|
567
646
|
<button class="tf-btn" data-tf="6h" onclick="setFeedTimeFilter('6h')">6h</button>
|
|
568
647
|
<button class="tf-btn" data-tf="24h" onclick="setFeedTimeFilter('24h')">24h</button>
|
|
569
|
-
<span class="kb-hint"><kbd>J</kbd><kbd>K</kbd> navigate <kbd>↵</kbd> detail <kbd>/</kbd> find <kbd>G</kbd> live <kbd>⌘K</kbd> search</span>
|
|
648
|
+
<span class="kb-hint"><kbd>J</kbd><kbd>K</kbd> navigate <kbd>↵</kbd> detail <kbd>/</kbd> find <kbd>G</kbd> live <kbd>A</kbd> ambient <kbd>⌘K</kbd> search</span>
|
|
649
|
+
</div>
|
|
650
|
+
<div id="digest-card">
|
|
651
|
+
<div class="digest-title">Today's Digest <button class="digest-close" onclick="dismissDigest()" title="Dismiss">✕</button></div>
|
|
652
|
+
<div class="digest-row" id="digest-stats"></div>
|
|
570
653
|
</div>
|
|
571
|
-
<div id="feed-scroll">
|
|
572
|
-
<div id="feed-
|
|
654
|
+
<div id="feed-scroll-wrap">
|
|
655
|
+
<div id="feed-scroll">
|
|
656
|
+
<div id="feed-content"><div class="loading-txt">Loading activity…</div></div>
|
|
657
|
+
</div>
|
|
658
|
+
<div id="feed-minimap" title="Click to jump · events map">
|
|
659
|
+
<div id="feed-minimap-track"></div>
|
|
660
|
+
<div id="mm-thumb"></div>
|
|
661
|
+
</div>
|
|
573
662
|
</div>
|
|
574
663
|
</div>
|
|
575
664
|
|
|
@@ -617,11 +706,23 @@ html, body { height: 100%; background: var(--bg); color: var(--text-hi); font-fa
|
|
|
617
706
|
<!-- SESSIONS -->
|
|
618
707
|
<div class="view" id="view-sessions">
|
|
619
708
|
<div class="vscroll">
|
|
620
|
-
<div style="display:flex;align-items:
|
|
709
|
+
<div style="display:flex;align-items:center;gap:8px;margin-bottom:4px;flex-wrap:wrap">
|
|
621
710
|
<div class="pg-title" style="margin-bottom:0">Sessions</div>
|
|
622
711
|
<button id="sess-star-filter" onclick="toggleSessStarFilter()" title="Show only bookmarked sessions">☆ Starred</button>
|
|
712
|
+
<button class="lb-toggle" id="btn-leaderboard" onclick="toggleLeaderboard()" title="Cost leaderboard">⬆ Leaderboard</button>
|
|
713
|
+
<button class="diff-toggle" id="btn-diff" onclick="toggleDiffMode()" title="Compare two sessions">⇄ Compare</button>
|
|
623
714
|
</div>
|
|
624
715
|
<div class="pg-sub" id="sess-pg-sub">Recent Claude Code sessions for this project</div>
|
|
716
|
+
<div id="diff-panel">
|
|
717
|
+
<div class="diff-header"><span class="diff-title">Session Comparison</span><button class="diff-clear" onclick="clearDiff()" title="Clear">✕</button></div>
|
|
718
|
+
<div class="diff-hint" id="diff-hint">Click two sessions below to compare them</div>
|
|
719
|
+
<div class="diff-cols" id="diff-cols" style="display:none"></div>
|
|
720
|
+
</div>
|
|
721
|
+
<div id="lb-panel" style="display:none;margin-bottom:16px">
|
|
722
|
+
<table class="lb-table"><thead><tr>
|
|
723
|
+
<th class="lb-rank">#</th><th>Session</th><th class="lb-cost">Cost</th><th class="lb-dur">Duration</th>
|
|
724
|
+
</tr></thead><tbody id="lb-body"></tbody></table>
|
|
725
|
+
</div>
|
|
625
726
|
<div id="sess-content" class="sess-list"><div class="loading-txt">Loading…</div></div>
|
|
626
727
|
</div>
|
|
627
728
|
</div>
|
|
@@ -670,6 +771,7 @@ html, body { height: 100%; background: var(--bg); color: var(--text-hi); font-fa
|
|
|
670
771
|
|
|
671
772
|
</div><!-- /view-wrap -->
|
|
672
773
|
</div><!-- /main -->
|
|
774
|
+
<div id="app-ambient-hint">Press A to exit ambient mode</div>
|
|
673
775
|
</div><!-- /app -->
|
|
674
776
|
|
|
675
777
|
<!-- budget modal (fixed overlay, outside app) -->
|
|
@@ -978,7 +1080,8 @@ function renderFeedEvents(events, silent) {
|
|
|
978
1080
|
|
|
979
1081
|
// update timeline + breakdown with all original events (before time-filter)
|
|
980
1082
|
buildTimeline(filtered);
|
|
981
|
-
|
|
1083
|
+
buildBreakdownByName(filtered);
|
|
1084
|
+
buildMinimap(filtered);
|
|
982
1085
|
// session recap card
|
|
983
1086
|
buildRecap(filtered, allSessions[sessionIdx]);
|
|
984
1087
|
}
|
|
@@ -1287,8 +1390,8 @@ async function renderProjects() {
|
|
|
1287
1390
|
const el = document.getElementById('proj-content');
|
|
1288
1391
|
el.innerHTML = '<div class="loading-txt">Loading…</div>';
|
|
1289
1392
|
try {
|
|
1290
|
-
const data = await apiFetch('/api/
|
|
1291
|
-
allProjects = data?.
|
|
1393
|
+
const data = await apiFetch('/api/projects');
|
|
1394
|
+
allProjects = data?.projects || [];
|
|
1292
1395
|
document.getElementById('bdg-projects').textContent = allProjects.length || '—';
|
|
1293
1396
|
document.getElementById('proj-pg-sub').textContent =
|
|
1294
1397
|
allProjects.length + ' project' + (allProjects.length !== 1 ? 's' : '') + ' found';
|
|
@@ -1351,7 +1454,8 @@ async function renderSessions() {
|
|
|
1351
1454
|
el.innerHTML = '<div class="empty"><div class="empty-ico">☆</div><div>No bookmarked sessions</div></div>';
|
|
1352
1455
|
return;
|
|
1353
1456
|
}
|
|
1354
|
-
|
|
1457
|
+
const sessData = JSON.stringify(toShow).replace(/'/g, ''');
|
|
1458
|
+
el.innerHTML = toShow.map((s, idx) => {
|
|
1355
1459
|
const dur = s.totalDurationMs ? fmtDur(s.totalDurationMs) : '';
|
|
1356
1460
|
const msgs = s.totalMessages ? s.totalMessages + ' msg' : '';
|
|
1357
1461
|
const cost = typeof s.totalCost === 'number' ? '$' + s.totalCost.toFixed(2)
|
|
@@ -1360,7 +1464,8 @@ async function renderSessions() {
|
|
|
1360
1464
|
const summaries = (s.summaries || []).slice(0, 2).map(sm => { const t = typeof sm === 'string' ? sm : (sm.summary || sm.text || String(sm)); return `<span class="sr-tag">${esc(t.slice(0, 40))}</span>`; }).join('');
|
|
1361
1465
|
const autoTags = (allTags.sessionTags.get(s.id) || []).map(t => `<span class="sr-autotag">${esc(t)}</span>`).join('');
|
|
1362
1466
|
const isStarred = bookmarks.has(s.id);
|
|
1363
|
-
|
|
1467
|
+
const sData = JSON.stringify(s).replace(/'/g, ''');
|
|
1468
|
+
return `<div class="sess-row" data-sess-idx="${idx}" onclick="diffMode ? diffSelectSession(JSON.parse(this.dataset.sessData || '{}'), this) : jumpToSession('${esc(s.id)}')" data-sess-data='${sData}'>
|
|
1364
1469
|
<div class="sr-top">
|
|
1365
1470
|
<div class="sr-prompt">${esc(s.lastPrompt || s.id)}</div>
|
|
1366
1471
|
<div class="sr-time">${relTime(s.lastTs || s.mtime)}</div>
|
|
@@ -1375,6 +1480,8 @@ async function renderSessions() {
|
|
|
1375
1480
|
if (allTags.common.size > 1) {
|
|
1376
1481
|
el.innerHTML = buildTagFilterBar(toShow) + el.innerHTML;
|
|
1377
1482
|
}
|
|
1483
|
+
buildDigest();
|
|
1484
|
+
if (leaderboardOpen) renderLeaderboard();
|
|
1378
1485
|
} catch (err) {
|
|
1379
1486
|
el.innerHTML = '<div class="empty">Could not load sessions: ' + esc(err.message) + '</div>';
|
|
1380
1487
|
}
|
|
@@ -1492,9 +1599,9 @@ async function renderGlobalFeed() {
|
|
|
1492
1599
|
const el = document.getElementById('gf-content');
|
|
1493
1600
|
el.innerHTML = '<div class="loading-txt">Loading all projects…</div>';
|
|
1494
1601
|
try {
|
|
1495
|
-
// fetch project list
|
|
1496
|
-
const data = await apiFetch('/api/
|
|
1497
|
-
const projects = (data?.
|
|
1602
|
+
// fetch project list
|
|
1603
|
+
const data = await apiFetch('/api/projects');
|
|
1604
|
+
const projects = (data?.projects || []).slice(0, 8);
|
|
1498
1605
|
if (!projects.length) {
|
|
1499
1606
|
el.innerHTML = '<div class="empty"><div class="empty-ico">⊕</div><div>No projects found</div></div>';
|
|
1500
1607
|
return;
|
|
@@ -1695,6 +1802,256 @@ function healthClass(score) {
|
|
|
1695
1802
|
return 'ph-lo';
|
|
1696
1803
|
}
|
|
1697
1804
|
|
|
1805
|
+
// ── feature 7: tool call frequency chart (by name) ─────────
|
|
1806
|
+
function buildBreakdownByName(events) {
|
|
1807
|
+
const counts = {};
|
|
1808
|
+
for (const ev of events) {
|
|
1809
|
+
if (ev.kind !== 'tool') continue;
|
|
1810
|
+
const name = (ev.name || ev.cat || 'other').replace(/^mcp__.*$/, 'MCP').replace(/^m__.*$/, 'MCP');
|
|
1811
|
+
counts[name] = (counts[name] || 0) + 1;
|
|
1812
|
+
}
|
|
1813
|
+
const total = Object.values(counts).reduce((a, b) => a + b, 0);
|
|
1814
|
+
if (!total) {
|
|
1815
|
+
document.getElementById('m-breakdown').innerHTML =
|
|
1816
|
+
'<div class="m-group-title">Tool Usage</div><div class="loading-txt" style="padding:6px 0">—</div>';
|
|
1817
|
+
return;
|
|
1818
|
+
}
|
|
1819
|
+
const CAT_COLOR = { Bash:'oklch(72% 0.18 75)', Read:'oklch(60% 0.12 220)', Edit:'oklch(65% 0.15 160)', Write:'oklch(65% 0.15 160)', Agent:'oklch(70% 0.15 300)', Task:'oklch(65% 0.15 280)', MCP:'oklch(65% 0.15 200)', WebFetch:'oklch(60% 0.12 195)', WebSearch:'oklch(60% 0.12 195)' };
|
|
1820
|
+
const getColor = n => CAT_COLOR[n] || 'var(--text-xs)';
|
|
1821
|
+
const sorted = Object.entries(counts).sort((a, b) => b[1] - a[1]).slice(0, 8);
|
|
1822
|
+
const rows = sorted.map(([name, cnt]) => {
|
|
1823
|
+
const pct = Math.round(cnt / total * 100);
|
|
1824
|
+
return `<div class="tb-row">
|
|
1825
|
+
<div class="tb-lbl" style="width:54px" title="${esc(name)}">${esc(name.length > 8 ? name.slice(0,7)+'…' : name)}</div>
|
|
1826
|
+
<div class="tb-bar-wrap"><div class="tb-bar" style="width:${pct}%;background:${getColor(name)}"></div></div>
|
|
1827
|
+
<div class="tb-count">${cnt}</div>
|
|
1828
|
+
</div>`;
|
|
1829
|
+
}).join('');
|
|
1830
|
+
document.getElementById('m-breakdown').innerHTML =
|
|
1831
|
+
`<div class="m-group-title">Tool Usage <span style="font-size:10px;color:var(--text-xs);font-weight:400">${total} calls</span></div><div class="m-breakdown">${rows}</div>`;
|
|
1832
|
+
}
|
|
1833
|
+
|
|
1834
|
+
// ── feature 8: ambient mode ────────────────────────────────
|
|
1835
|
+
function toggleAmbient() {
|
|
1836
|
+
document.getElementById('app').classList.toggle('ambient');
|
|
1837
|
+
}
|
|
1838
|
+
|
|
1839
|
+
// ── feature 9: daily digest ────────────────────────────────
|
|
1840
|
+
const DIGEST_DISMISSED_KEY = 'mm-digest-dismissed';
|
|
1841
|
+
|
|
1842
|
+
function buildDigest() {
|
|
1843
|
+
const todayKey = new Date().toISOString().slice(0, 10);
|
|
1844
|
+
if (localStorage.getItem(DIGEST_DISMISSED_KEY) === todayKey) return;
|
|
1845
|
+
if (!allSessions.length) return;
|
|
1846
|
+
|
|
1847
|
+
const todayStart = new Date(); todayStart.setHours(0, 0, 0, 0);
|
|
1848
|
+
const todaySessions = allSessions.filter(s => {
|
|
1849
|
+
const t = s.lastTs || s.mtime;
|
|
1850
|
+
return t && new Date(typeof t === 'number' ? t : t).getTime() >= todayStart.getTime();
|
|
1851
|
+
});
|
|
1852
|
+
if (!todaySessions.length) return;
|
|
1853
|
+
|
|
1854
|
+
const totalCost = todaySessions.reduce((a, s) => a + (s.totalCost || 0), 0);
|
|
1855
|
+
const totalTools = todaySessions.reduce((a, s) => a + (s.toolCalls || 0), 0);
|
|
1856
|
+
const totalMsgs = todaySessions.reduce((a, s) => a + (s.userMessages || 0), 0);
|
|
1857
|
+
const longestMs = Math.max(...todaySessions.map(s => s.totalDurationMs || 0));
|
|
1858
|
+
|
|
1859
|
+
// gather top tools from today's sessions' tag keywords as themes
|
|
1860
|
+
const themes = [...new Set(todaySessions.flatMap(s => s._tags || []))].slice(0, 3);
|
|
1861
|
+
|
|
1862
|
+
const stats = [
|
|
1863
|
+
`${todaySessions.length} session${todaySessions.length > 1 ? 's' : ''}`,
|
|
1864
|
+
totalCost > 0 ? `$${totalCost.toFixed(2)} spent` : null,
|
|
1865
|
+
totalTools > 0 ? `${totalTools} tool calls` : null,
|
|
1866
|
+
totalMsgs > 0 ? `${totalMsgs} messages` : null,
|
|
1867
|
+
longestMs > 0 ? `${fmtDur(longestMs)} longest` : null,
|
|
1868
|
+
...themes.map(t => `#${t}`),
|
|
1869
|
+
].filter(Boolean);
|
|
1870
|
+
|
|
1871
|
+
document.getElementById('digest-stats').innerHTML =
|
|
1872
|
+
stats.map(s => `<span class="digest-stat">${esc(s)}</span>`).join('');
|
|
1873
|
+
document.getElementById('digest-card').classList.add('show');
|
|
1874
|
+
}
|
|
1875
|
+
|
|
1876
|
+
function dismissDigest() {
|
|
1877
|
+
const todayKey = new Date().toISOString().slice(0, 10);
|
|
1878
|
+
localStorage.setItem(DIGEST_DISMISSED_KEY, todayKey);
|
|
1879
|
+
document.getElementById('digest-card').classList.remove('show');
|
|
1880
|
+
}
|
|
1881
|
+
|
|
1882
|
+
// ── feature 10: minimap scrubber ──────────────────────────
|
|
1883
|
+
function buildMinimap(events) {
|
|
1884
|
+
const track = document.getElementById('feed-minimap-track');
|
|
1885
|
+
const thumb = document.getElementById('mm-thumb');
|
|
1886
|
+
const scroll = document.getElementById('feed-scroll');
|
|
1887
|
+
if (!track || !thumb || !scroll) return;
|
|
1888
|
+
|
|
1889
|
+
const tools = events.filter(ev => ev.kind === 'tool' || ev.kind === 'user');
|
|
1890
|
+
if (!tools.length) { track.innerHTML = ''; return; }
|
|
1891
|
+
|
|
1892
|
+
const CAT_CLS = { file:'mp-file', bash:'mp-bash', agent:'mp-agent', mcp:'mp-mcp', user:'mp-user' };
|
|
1893
|
+
const H = scroll.clientHeight || 400;
|
|
1894
|
+
const N = tools.length;
|
|
1895
|
+
track.innerHTML = tools.map((ev, i) => {
|
|
1896
|
+
const top = Math.round((i / N) * H);
|
|
1897
|
+
const h = Math.max(3, Math.round((1 / N) * H * 0.7));
|
|
1898
|
+
const cls = ev._errored ? 'mp-err' : (ev.kind === 'user' ? 'mp-user' : (CAT_CLS[ev.cat] || 'mp-other'));
|
|
1899
|
+
return `<div class="mm-pip ${cls}" style="top:${top}px;height:${h}px" onclick="minimapJump(${i},${N})"></div>`;
|
|
1900
|
+
}).join('');
|
|
1901
|
+
|
|
1902
|
+
// sync thumb
|
|
1903
|
+
const updateThumb = () => {
|
|
1904
|
+
const ratio = scroll.scrollHeight > H ? scroll.scrollTop / (scroll.scrollHeight - H) : 0;
|
|
1905
|
+
const thH = Math.max(20, Math.round(H * (H / Math.max(scroll.scrollHeight, H + 1))));
|
|
1906
|
+
thumb.style.height = thH + 'px';
|
|
1907
|
+
thumb.style.top = Math.round(ratio * (H - thH)) + 'px';
|
|
1908
|
+
};
|
|
1909
|
+
scroll.removeEventListener('scroll', scroll._mmListener || (() => {}));
|
|
1910
|
+
scroll._mmListener = updateThumb;
|
|
1911
|
+
scroll.addEventListener('scroll', scroll._mmListener);
|
|
1912
|
+
updateThumb();
|
|
1913
|
+
}
|
|
1914
|
+
|
|
1915
|
+
function minimapJump(idx, total) {
|
|
1916
|
+
const scroll = document.getElementById('feed-scroll');
|
|
1917
|
+
if (!scroll) return;
|
|
1918
|
+
scroll.scrollTop = Math.round((idx / total) * scroll.scrollHeight);
|
|
1919
|
+
}
|
|
1920
|
+
|
|
1921
|
+
// ── feature 11: cost leaderboard ──────────────────────────
|
|
1922
|
+
let leaderboardOpen = false;
|
|
1923
|
+
|
|
1924
|
+
function toggleLeaderboard() {
|
|
1925
|
+
leaderboardOpen = !leaderboardOpen;
|
|
1926
|
+
document.getElementById('btn-leaderboard').classList.toggle('on', leaderboardOpen);
|
|
1927
|
+
const panel = document.getElementById('lb-panel');
|
|
1928
|
+
panel.style.display = leaderboardOpen ? 'block' : 'none';
|
|
1929
|
+
if (leaderboardOpen) renderLeaderboard();
|
|
1930
|
+
}
|
|
1931
|
+
|
|
1932
|
+
function renderLeaderboard() {
|
|
1933
|
+
const sorted = [...allSessions]
|
|
1934
|
+
.filter(s => typeof s.totalCost === 'number' && s.totalCost > 0)
|
|
1935
|
+
.sort((a, b) => b.totalCost - a.totalCost)
|
|
1936
|
+
.slice(0, 15);
|
|
1937
|
+
const body = document.getElementById('lb-body');
|
|
1938
|
+
if (!sorted.length) { body.innerHTML = '<tr><td colspan="4" style="text-align:center;color:var(--text-xs);padding:12px">No cost data yet</td></tr>'; return; }
|
|
1939
|
+
body.innerHTML = sorted.map((s, i) => {
|
|
1940
|
+
const cost = '$' + s.totalCost.toFixed(2);
|
|
1941
|
+
const dur = s.totalDurationMs ? fmtDur(s.totalDurationMs) : '—';
|
|
1942
|
+
const prompt = s.lastPrompt || s.id;
|
|
1943
|
+
return `<tr onclick="jumpToSession('${esc(s.id)}')" title="${esc(prompt)}">
|
|
1944
|
+
<td class="lb-rank">${i + 1}</td>
|
|
1945
|
+
<td class="lb-prompt">${esc(prompt.slice(0, 60))}</td>
|
|
1946
|
+
<td class="lb-cost">${cost}</td>
|
|
1947
|
+
<td class="lb-dur">${dur}</td>
|
|
1948
|
+
</tr>`;
|
|
1949
|
+
}).join('');
|
|
1950
|
+
}
|
|
1951
|
+
|
|
1952
|
+
// ── feature 12: session diff ──────────────────────────────
|
|
1953
|
+
let diffMode = false;
|
|
1954
|
+
let diffSelA = null; let diffSelB = null;
|
|
1955
|
+
|
|
1956
|
+
function toggleDiffMode() {
|
|
1957
|
+
diffMode = !diffMode;
|
|
1958
|
+
document.getElementById('btn-diff').classList.toggle('on', diffMode);
|
|
1959
|
+
const panel = document.getElementById('diff-panel');
|
|
1960
|
+
panel.classList.toggle('show', diffMode);
|
|
1961
|
+
if (!diffMode) clearDiff();
|
|
1962
|
+
}
|
|
1963
|
+
|
|
1964
|
+
function clearDiff() {
|
|
1965
|
+
diffSelA = null; diffSelB = null;
|
|
1966
|
+
document.getElementById('diff-hint').style.display = '';
|
|
1967
|
+
document.getElementById('diff-cols').style.display = 'none';
|
|
1968
|
+
document.getElementById('diff-cols').innerHTML = '';
|
|
1969
|
+
document.querySelectorAll('.sess-row.diff-sel-a, .sess-row.diff-sel-b').forEach(el => {
|
|
1970
|
+
el.classList.remove('diff-sel-a', 'diff-sel-b');
|
|
1971
|
+
});
|
|
1972
|
+
}
|
|
1973
|
+
|
|
1974
|
+
function diffSelectSession(sess, el) {
|
|
1975
|
+
if (!diffMode) return;
|
|
1976
|
+
if (diffSelA && diffSelA.id === sess.id) {
|
|
1977
|
+
diffSelA = null;
|
|
1978
|
+
el.classList.remove('diff-sel-a');
|
|
1979
|
+
document.getElementById('diff-hint').style.display = '';
|
|
1980
|
+
document.getElementById('diff-cols').style.display = 'none';
|
|
1981
|
+
return;
|
|
1982
|
+
}
|
|
1983
|
+
if (diffSelB && diffSelB.id === sess.id) {
|
|
1984
|
+
diffSelB = null;
|
|
1985
|
+
el.classList.remove('diff-sel-b');
|
|
1986
|
+
renderDiff();
|
|
1987
|
+
return;
|
|
1988
|
+
}
|
|
1989
|
+
if (!diffSelA) {
|
|
1990
|
+
diffSelA = sess;
|
|
1991
|
+
el.classList.add('diff-sel-a');
|
|
1992
|
+
} else if (!diffSelB) {
|
|
1993
|
+
diffSelB = sess;
|
|
1994
|
+
el.classList.add('diff-sel-b');
|
|
1995
|
+
} else {
|
|
1996
|
+
// replace B with new selection
|
|
1997
|
+
document.querySelectorAll('.sess-row.diff-sel-b').forEach(e => e.classList.remove('diff-sel-b'));
|
|
1998
|
+
diffSelB = sess;
|
|
1999
|
+
el.classList.add('diff-sel-b');
|
|
2000
|
+
}
|
|
2001
|
+
if (diffSelA && diffSelB) {
|
|
2002
|
+
renderDiff();
|
|
2003
|
+
} else {
|
|
2004
|
+
document.getElementById('diff-hint').textContent = diffSelA ? 'Now click a second session to compare' : 'Click two sessions below to compare them';
|
|
2005
|
+
document.getElementById('diff-hint').style.display = '';
|
|
2006
|
+
document.getElementById('diff-cols').style.display = 'none';
|
|
2007
|
+
}
|
|
2008
|
+
}
|
|
2009
|
+
|
|
2010
|
+
function renderDiff() {
|
|
2011
|
+
if (!diffSelA || !diffSelB) return;
|
|
2012
|
+
document.getElementById('diff-hint').style.display = 'none';
|
|
2013
|
+
const cols = document.getElementById('diff-cols');
|
|
2014
|
+
cols.style.display = '';
|
|
2015
|
+
const fmt = (a, b, key, prefix = '') => {
|
|
2016
|
+
const va = a[key], vb = b[key];
|
|
2017
|
+
if (va == null && vb == null) return '';
|
|
2018
|
+
const fa = prefix + (va != null ? va : '—');
|
|
2019
|
+
const fb = prefix + (vb != null ? vb : '—');
|
|
2020
|
+
const hiA = va != null && vb != null && va > vb ? ' diff-hi' : (va != null && vb != null && va < vb ? ' diff-lo' : '');
|
|
2021
|
+
const hiB = va != null && vb != null && vb > va ? ' diff-hi' : (va != null && vb != null && vb < va ? ' diff-lo' : '');
|
|
2022
|
+
return [fa, hiA, fb, hiB];
|
|
2023
|
+
};
|
|
2024
|
+
|
|
2025
|
+
const diffCol = (s) => {
|
|
2026
|
+
const cost = typeof s.totalCost === 'number' ? '$' + s.totalCost.toFixed(2) : '—';
|
|
2027
|
+
const dur = s.totalDurationMs ? fmtDur(s.totalDurationMs) : '—';
|
|
2028
|
+
const tools = s.toolCalls != null ? s.toolCalls : '—';
|
|
2029
|
+
const msgs = s.userMessages != null ? s.userMessages : '—';
|
|
2030
|
+
const errs = s.errors != null ? s.errors : '—';
|
|
2031
|
+
const prompt = (s.lastPrompt || s.id || '').slice(0, 50);
|
|
2032
|
+
return { cost, dur, tools, msgs, errs, prompt, s };
|
|
2033
|
+
};
|
|
2034
|
+
|
|
2035
|
+
const a = diffCol(diffSelA), b = diffCol(diffSelB);
|
|
2036
|
+
const rows = [
|
|
2037
|
+
['Cost', a.cost, b.cost, typeof diffSelA.totalCost === 'number' && typeof diffSelB.totalCost === 'number' ? (diffSelA.totalCost > diffSelB.totalCost ? ['diff-hi','diff-lo'] : diffSelA.totalCost < diffSelB.totalCost ? ['diff-lo','diff-hi'] : ['','']) : ['','']],
|
|
2038
|
+
['Duration', a.dur, b.dur, ['','']],
|
|
2039
|
+
['Tool calls', a.tools, b.tools, typeof a.tools === 'number' && typeof b.tools === 'number' ? (a.tools > b.tools ? ['diff-hi','diff-lo'] : a.tools < b.tools ? ['diff-lo','diff-hi'] : ['','']) : ['','']],
|
|
2040
|
+
['Messages', a.msgs, b.msgs, ['','']],
|
|
2041
|
+
['Errors', a.errs, b.errs, ['','']],
|
|
2042
|
+
];
|
|
2043
|
+
|
|
2044
|
+
const colHtml = (idx) => `<div class="diff-col">
|
|
2045
|
+
<div class="diff-col-title">${esc(idx === 0 ? a.prompt : b.prompt)}</div>
|
|
2046
|
+
${rows.map(([k, va, vb, cls]) => `<div class="diff-row">
|
|
2047
|
+
<span class="diff-k">${k}</span>
|
|
2048
|
+
<span class="diff-v ${cls[idx]}">${esc(idx === 0 ? va : vb)}</span>
|
|
2049
|
+
</div>`).join('')}
|
|
2050
|
+
</div>`;
|
|
2051
|
+
|
|
2052
|
+
cols.innerHTML = colHtml(0) + colHtml(1);
|
|
2053
|
+
}
|
|
2054
|
+
|
|
1698
2055
|
// ── loops ──────────────────────────────────────────────────
|
|
1699
2056
|
async function renderLoops() {
|
|
1700
2057
|
const el = document.getElementById('loops-content');
|
|
@@ -2193,7 +2550,8 @@ document.addEventListener('keydown', e => {
|
|
|
2193
2550
|
if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return;
|
|
2194
2551
|
if (document.getElementById('cmd-palette').classList.contains('open')) return;
|
|
2195
2552
|
|
|
2196
|
-
if (e.key === 'Escape') { closeDetail(); closeCmdPalette(); }
|
|
2553
|
+
if (e.key === 'Escape') { closeDetail(); closeCmdPalette(); if (document.getElementById('app').classList.contains('ambient')) toggleAmbient(); }
|
|
2554
|
+
if (e.key === 'a' || e.key === 'A') { if (currentView === 'now') { e.preventDefault(); toggleAmbient(); } }
|
|
2197
2555
|
|
|
2198
2556
|
if (currentView === 'now') {
|
|
2199
2557
|
if (e.key === '/') { e.preventDefault(); toggleFeedSearch(); }
|
package/dist/src/ui/server.mjs
CHANGED
|
@@ -429,6 +429,40 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
429
429
|
return;
|
|
430
430
|
}
|
|
431
431
|
|
|
432
|
+
// ------------------------------------------------------- GET /api/projects
|
|
433
|
+
if (req.method === 'GET' && url === '/api/projects') {
|
|
434
|
+
try {
|
|
435
|
+
const projectsBase = path.join(os.homedir(), '.claude', 'projects');
|
|
436
|
+
let slugDirs = [];
|
|
437
|
+
try { slugDirs = fs.readdirSync(projectsBase, { withFileTypes: true }).filter(e => e.isDirectory()).map(e => e.name); } catch {}
|
|
438
|
+
const projects = slugDirs.map(slug => {
|
|
439
|
+
const projDir = path.join(projectsBase, slug);
|
|
440
|
+
// convert slug back to path: replace leading - and then each - that was /
|
|
441
|
+
const projPath = slug.replace(/-/g, '/');
|
|
442
|
+
const name = slug.split('-').filter(Boolean).pop() || slug;
|
|
443
|
+
let sessionCount = 0; let lastActivity = 0; let memoryCount = 0;
|
|
444
|
+
try {
|
|
445
|
+
const files = fs.readdirSync(projDir).filter(f => f.endsWith('.jsonl'));
|
|
446
|
+
sessionCount = files.length;
|
|
447
|
+
for (const f of files) {
|
|
448
|
+
try { const st = fs.statSync(path.join(projDir, f)); if (st.mtimeMs > lastActivity) lastActivity = st.mtimeMs; } catch {}
|
|
449
|
+
}
|
|
450
|
+
} catch {}
|
|
451
|
+
try {
|
|
452
|
+
const memDir = path.join(projDir, 'memory');
|
|
453
|
+
memoryCount = fs.readdirSync(memDir).filter(f => f.endsWith('.md') && f !== 'MEMORY.md').length;
|
|
454
|
+
} catch {}
|
|
455
|
+
return { slug, path: projPath, name, sessionCount, memoryCount, lastActivity: lastActivity || null };
|
|
456
|
+
}).filter(p => p.sessionCount > 0).sort((a, b) => (b.lastActivity || 0) - (a.lastActivity || 0));
|
|
457
|
+
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*', 'Cache-Control': 'no-cache' });
|
|
458
|
+
res.end(JSON.stringify({ projects }));
|
|
459
|
+
} catch (err) {
|
|
460
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
461
|
+
res.end(JSON.stringify({ error: err.message }));
|
|
462
|
+
}
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
|
|
432
466
|
// ------------------------------------------------------- GET /api/palace
|
|
433
467
|
if (req.method === 'GET' && url === '/api/palace') {
|
|
434
468
|
try {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@monoes/monomindcli",
|
|
3
|
-
"version": "1.10.
|
|
3
|
+
"version": "1.10.33",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Monomind CLI - Enterprise AI agent orchestration with 60+ specialized agents, swarm coordination, MCP server, self-learning hooks, and vector memory for Claude Code",
|
|
6
6
|
"main": "dist/src/index.js",
|