monomind 2.0.3 → 2.1.3

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 (113) hide show
  1. package/README.md +74 -92
  2. package/package.json +11 -7
  3. package/packages/@monomind/cli/.claude/helpers/control-start.cjs +13 -20
  4. package/packages/@monomind/cli/.claude/helpers/event-logger.cjs +109 -6
  5. package/packages/@monomind/cli/.claude/helpers/graphify-freshen.cjs +19 -17
  6. package/packages/@monomind/cli/.claude/helpers/handlers/agent-start-handler.cjs +60 -28
  7. package/packages/@monomind/cli/.claude/helpers/handlers/capture-handler.cjs +99 -28
  8. package/packages/@monomind/cli/.claude/helpers/handlers/edit-handler.cjs +27 -9
  9. package/packages/@monomind/cli/.claude/helpers/handlers/gates-handler.cjs +18 -9
  10. package/packages/@monomind/cli/.claude/helpers/handlers/loops-status-handler.cjs +1 -1
  11. package/packages/@monomind/cli/.claude/helpers/handlers/route-handler.cjs +33 -4
  12. package/packages/@monomind/cli/.claude/helpers/handlers/session-handler.cjs +75 -45
  13. package/packages/@monomind/cli/.claude/helpers/handlers/session-restore-handler.cjs +0 -12
  14. package/packages/@monomind/cli/.claude/helpers/handlers/task-handler.cjs +53 -22
  15. package/packages/@monomind/cli/.claude/helpers/hook-handler.cjs +75 -4
  16. package/packages/@monomind/cli/.claude/helpers/intelligence.cjs +94 -15
  17. package/packages/@monomind/cli/.claude/helpers/session.cjs +6 -1
  18. package/packages/@monomind/cli/.claude/helpers/statusline.cjs +10 -10
  19. package/packages/@monomind/cli/.claude/helpers/token-tracker.cjs +3 -3
  20. package/packages/@monomind/cli/.claude/helpers/utils/agent-registrations.cjs +41 -0
  21. package/packages/@monomind/cli/.claude/helpers/utils/fs-helpers.cjs +183 -0
  22. package/packages/@monomind/cli/.claude/helpers/utils/micro-agents.cjs +10 -6
  23. package/packages/@monomind/cli/.claude/helpers/utils/monograph.cjs +73 -2
  24. package/packages/@monomind/cli/.claude/helpers/utils/telemetry.cjs +35 -19
  25. package/packages/@monomind/cli/README.md +74 -92
  26. package/packages/@monomind/cli/bin/cli.js +10 -1
  27. package/packages/@monomind/cli/dist/src/browser/workflow/store.d.ts +2 -2
  28. package/packages/@monomind/cli/dist/src/commands/agent-ops.js +9 -15
  29. package/packages/@monomind/cli/dist/src/commands/browse.d.ts +1 -1
  30. package/packages/@monomind/cli/dist/src/commands/cleanup.js +20 -4
  31. package/packages/@monomind/cli/dist/src/commands/completions.js +66 -261
  32. package/packages/@monomind/cli/dist/src/commands/doctor-env-checks.js +5 -0
  33. package/packages/@monomind/cli/dist/src/commands/doctor.js +7 -2
  34. package/packages/@monomind/cli/dist/src/commands/hooks-workers.js +7 -0
  35. package/packages/@monomind/cli/dist/src/commands/index.js +3 -0
  36. package/packages/@monomind/cli/dist/src/commands/mcp.js +11 -6
  37. package/packages/@monomind/cli/dist/src/commands/memory-admin.js +12 -6
  38. package/packages/@monomind/cli/dist/src/commands/memory-transfer.js +6 -13
  39. package/packages/@monomind/cli/dist/src/commands/monograph.js +3 -3
  40. package/packages/@monomind/cli/dist/src/commands/org.d.ts +3 -1
  41. package/packages/@monomind/cli/dist/src/commands/org.js +321 -127
  42. package/packages/@monomind/cli/dist/src/commands/platforms.d.ts +1 -1
  43. package/packages/@monomind/cli/dist/src/commands/start.js +142 -37
  44. package/packages/@monomind/cli/dist/src/commands/tokens.js +21 -3
  45. package/packages/@monomind/cli/dist/src/config-adapter.js +37 -17
  46. package/packages/@monomind/cli/dist/src/index.js +64 -28
  47. package/packages/@monomind/cli/dist/src/init/claudemd-generator.js +44 -11
  48. package/packages/@monomind/cli/dist/src/init/executor.js +93 -55
  49. package/packages/@monomind/cli/dist/src/mcp-client.d.ts +8 -3
  50. package/packages/@monomind/cli/dist/src/mcp-client.js +37 -3
  51. package/packages/@monomind/cli/dist/src/mcp-server.js +8 -2
  52. package/packages/@monomind/cli/dist/src/mcp-tools/agent-tools.d.ts +20 -0
  53. package/packages/@monomind/cli/dist/src/mcp-tools/agent-tools.js +10 -5
  54. package/packages/@monomind/cli/dist/src/mcp-tools/auto-install.d.ts +8 -8
  55. package/packages/@monomind/cli/dist/src/mcp-tools/coherence/types.d.ts +31 -31
  56. package/packages/@monomind/cli/dist/src/mcp-tools/github-tools.js +30 -8
  57. package/packages/@monomind/cli/dist/src/mcp-tools/graphify-tools.js +11 -2
  58. package/packages/@monomind/cli/dist/src/mcp-tools/hive-mind-tools.js +56 -35
  59. package/packages/@monomind/cli/dist/src/mcp-tools/knowledge-tools.js +9 -1
  60. package/packages/@monomind/cli/dist/src/mcp-tools/monograph-compat.js +56 -14
  61. package/packages/@monomind/cli/dist/src/mcp-tools/monograph-tools.js +88 -41
  62. package/packages/@monomind/cli/dist/src/mcp-tools/quality/coverage-analysis/prioritize-gaps.d.ts +36 -36
  63. package/packages/@monomind/cli/dist/src/mcp-tools/quality/security-compliance/detect-secrets.d.ts +4 -4
  64. package/packages/@monomind/cli/dist/src/mcp-tools/swarm-tools.js +6 -4
  65. package/packages/@monomind/cli/dist/src/mcp-tools/system-tools.js +4 -4
  66. package/packages/@monomind/cli/dist/src/mcp-tools/types.d.ts +20 -0
  67. package/packages/@monomind/cli/dist/src/mcp-tools/types.js +36 -1
  68. package/packages/@monomind/cli/dist/src/memory/ewc-consolidation.d.ts +12 -0
  69. package/packages/@monomind/cli/dist/src/memory/hnsw-operations.d.ts +13 -1
  70. package/packages/@monomind/cli/dist/src/memory/hnsw-operations.js +66 -14
  71. package/packages/@monomind/cli/dist/src/memory/intelligence.js +99 -3
  72. package/packages/@monomind/cli/dist/src/memory/memory-bridge.d.ts +7 -0
  73. package/packages/@monomind/cli/dist/src/memory/memory-bridge.js +20 -0
  74. package/packages/@monomind/cli/dist/src/memory/sona-optimizer.d.ts +12 -0
  75. package/packages/@monomind/cli/dist/src/orgrt/broker.d.ts +26 -0
  76. package/packages/@monomind/cli/dist/src/orgrt/broker.js +73 -0
  77. package/packages/@monomind/cli/dist/src/orgrt/bus.d.ts +5 -1
  78. package/packages/@monomind/cli/dist/src/orgrt/bus.js +5 -1
  79. package/packages/@monomind/cli/dist/src/orgrt/daemon.d.ts +46 -0
  80. package/packages/@monomind/cli/dist/src/orgrt/daemon.js +201 -18
  81. package/packages/@monomind/cli/dist/src/orgrt/forwarder.d.ts +10 -5
  82. package/packages/@monomind/cli/dist/src/orgrt/forwarder.js +138 -8
  83. package/packages/@monomind/cli/dist/src/orgrt/inbox.d.ts +11 -0
  84. package/packages/@monomind/cli/dist/src/orgrt/inbox.js +56 -0
  85. package/packages/@monomind/cli/dist/src/orgrt/policy.d.ts +5 -1
  86. package/packages/@monomind/cli/dist/src/orgrt/policy.js +58 -5
  87. package/packages/@monomind/cli/dist/src/orgrt/provider.js +1 -0
  88. package/packages/@monomind/cli/dist/src/orgrt/scheduler.d.ts +13 -0
  89. package/packages/@monomind/cli/dist/src/orgrt/scheduler.js +48 -0
  90. package/packages/@monomind/cli/dist/src/orgrt/server.d.ts +9 -0
  91. package/packages/@monomind/cli/dist/src/orgrt/server.js +39 -0
  92. package/packages/@monomind/cli/dist/src/orgrt/session.d.ts +10 -1
  93. package/packages/@monomind/cli/dist/src/orgrt/session.js +24 -1
  94. package/packages/@monomind/cli/dist/src/orgrt/test-loop.d.ts +12 -0
  95. package/packages/@monomind/cli/dist/src/orgrt/test-loop.js +108 -0
  96. package/packages/@monomind/cli/dist/src/orgrt/types.d.ts +52 -52
  97. package/packages/@monomind/cli/dist/src/output.d.ts +29 -29
  98. package/packages/@monomind/cli/dist/src/output.js +22 -7
  99. package/packages/@monomind/cli/dist/src/parser.d.ts +32 -0
  100. package/packages/@monomind/cli/dist/src/parser.js +130 -5
  101. package/packages/@monomind/cli/dist/src/routing/embed-worker.js +13 -1
  102. package/packages/@monomind/cli/dist/src/routing/route-layer-factory.js +31 -0
  103. package/packages/@monomind/cli/dist/src/services/config-file-manager.d.ts +21 -0
  104. package/packages/@monomind/cli/dist/src/services/config-file-manager.js +60 -7
  105. package/packages/@monomind/cli/dist/src/services/crash-reporter.js +2 -1
  106. package/packages/@monomind/cli/dist/src/types.d.ts +2 -2
  107. package/packages/@monomind/cli/dist/src/ui/dashboard.html +243 -49
  108. package/packages/@monomind/cli/dist/src/ui/orgs-files.js +192 -0
  109. package/packages/@monomind/cli/dist/src/ui/orgs.html +79 -11
  110. package/packages/@monomind/cli/dist/src/ui/server.mjs +346 -139
  111. package/packages/@monomind/cli/dist/src/utils/input-guards.d.ts +9 -0
  112. package/packages/@monomind/cli/dist/src/utils/input-guards.js +39 -5
  113. package/packages/@monomind/cli/package.json +15 -15
@@ -0,0 +1,192 @@
1
+ // packages/@monomind/cli/dist/src/ui/orgs-files.js
2
+ // Files tab + diff-view feature, split out of orgs.html to keep that file under
3
+ // the CLAUDE.md 500-line guideline. Loaded as a classic <script> right where this
4
+ // code used to live inline — shares the same global scope as orgs.html's main
5
+ // script (chatSessions, orgSessionMatch, esc, currentTab, loadChatSessions,
6
+ // viewArtifact are all defined there and referenced here by closure).
7
+
8
+ // ── Files tab: every asset produced by this org, grouped by path with full
9
+ // version history (not deduped) so successive writes to the same file can be
10
+ // diffed against each other, latest-touched file first ──
11
+ let filesGroups = [];
12
+
13
+ function collectOrgArtifacts() {
14
+ const bySessions = chatSessions.filter(orgSessionMatch);
15
+ const byPath = new Map(); // path -> [{ art, ts, from }]
16
+ bySessions.forEach(s => (s.events || []).forEach(ev => {
17
+ if (ev.type !== 'org:artifact') return;
18
+ const art = ev.artifact || (ev.path ? { path: ev.path, label: ev.label, mimeType: ev.mimeType } : null);
19
+ if (!art || !art.path) return;
20
+ if (!byPath.has(art.path)) byPath.set(art.path, []);
21
+ byPath.get(art.path).push({ art, ts: ev.ts || 0, from: ev.from || '' });
22
+ }));
23
+ const groups = [];
24
+ byPath.forEach((versions, path) => {
25
+ versions.sort((a, b) => a.ts - b.ts);
26
+ groups.push({ path, versions, latest: versions[versions.length - 1] });
27
+ });
28
+ groups.sort((a, b) => b.latest.ts - a.latest.ts);
29
+ return groups;
30
+ }
31
+
32
+ function mkFileCard(group) {
33
+ const { art, ts, from } = group.latest;
34
+ const el = document.createElement('div');
35
+ el.className = 'file-card';
36
+ const isText = (art.mimeType || '').startsWith('text/') || (art.mimeType || '') === 'application/json';
37
+ const labelRaw = art.label || (art.path || 'file').split('/').pop();
38
+ const timeStr = ts ? new Date(ts).toLocaleString([], { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' }) : '';
39
+ const meta = [from, art.path ? art.path.split('/').slice(-3).join('/') : null, timeStr].filter(Boolean).join(' · ');
40
+ const vBadge = group.versions.length > 1 ? `<span class="fc-vbadge">${group.versions.length}v</span>` : '';
41
+ const diffable = group.versions.filter(v => typeof v.art.content === 'string').length >= 2;
42
+ el.innerHTML = `
43
+ <div class="fc-icon">📄</div>
44
+ <div class="fc-body">
45
+ <div class="fc-name">${esc(labelRaw)} ${vBadge}</div>
46
+ <div class="fc-meta">${esc(meta)}</div>
47
+ </div>
48
+ ${diffable ? `<button class="fc-diff" onclick="openDiffPanel(${JSON.stringify(art.path)})">Diff</button>` : ''}
49
+ ${isText && art.path
50
+ ? `<button class="fc-view" onclick="viewArtifact(${JSON.stringify(art.path)},${JSON.stringify(labelRaw)})">View</button>`
51
+ : `<span class="fc-binary">Binary</span>`}
52
+ `;
53
+ return el;
54
+ }
55
+
56
+ function paintFilesGrid() {
57
+ const grid = document.getElementById('files-grid');
58
+ const empty = document.getElementById('files-empty');
59
+ if (!grid) return;
60
+ filesGroups = collectOrgArtifacts();
61
+ renderFilesGridFromGroups();
62
+ }
63
+
64
+ /** Re-renders the grid from the current in-memory `filesGroups` — no rescan.
65
+ * Split out of paintFilesGrid() so applyArtifactEvent() can repaint after an
66
+ * O(groups) incremental update instead of forcing a full O(all-events) rescan. */
67
+ function renderFilesGridFromGroups() {
68
+ const grid = document.getElementById('files-grid');
69
+ const empty = document.getElementById('files-empty');
70
+ if (!grid) return;
71
+ grid.innerHTML = '';
72
+ if (!filesGroups.length) { if (empty) empty.style.display = 'block'; return; }
73
+ if (empty) empty.style.display = 'none';
74
+ filesGroups.forEach(group => grid.appendChild(mkFileCard(group)));
75
+ }
76
+
77
+ /** Incrementally folds a single new org:artifact event into `filesGroups` —
78
+ * O(groups) to re-sort by latest-touched, not O(all sessions' all events)
79
+ * like collectOrgArtifacts(). Mirrors collectOrgArtifacts()'s grouping rules. */
80
+ function applyArtifactEvent(ev) {
81
+ const art = ev.artifact || (ev.path ? { path: ev.path, label: ev.label, mimeType: ev.mimeType } : null);
82
+ if (!art || !art.path) return;
83
+ const entry = { art, ts: ev.ts || 0, from: ev.from || '' };
84
+ let group = filesGroups.find(g => g.path === art.path);
85
+ if (!group) {
86
+ group = { path: art.path, versions: [], latest: entry };
87
+ filesGroups.push(group);
88
+ }
89
+ group.versions.push(entry);
90
+ group.versions.sort((a, b) => a.ts - b.ts);
91
+ group.latest = group.versions[group.versions.length - 1];
92
+ filesGroups.sort((a, b) => b.latest.ts - a.latest.ts);
93
+ renderFilesGridFromGroups();
94
+ }
95
+
96
+ function renderFilesTab() {
97
+ paintFilesGrid(); // paint immediately with whatever's cached
98
+ loadChatSessions().then(() => { if (currentTab === 'files') paintFilesGrid(); });
99
+ }
100
+
101
+ // ── Diff view: compare two captured versions of the same file ──
102
+ window.openDiffPanel = function(path) {
103
+ const group = filesGroups.find(g => g.path === path);
104
+ if (!group) return;
105
+ const versions = group.versions.filter(v => typeof v.art.content === 'string');
106
+ if (versions.length < 2) return;
107
+ const label = group.latest.art.label || path.split('/').pop();
108
+ renderDiffPanel(label, versions);
109
+ };
110
+
111
+ function renderDiffPanel(label, versions) {
112
+ let panel = document.getElementById('artifact-panel');
113
+ if (!panel) {
114
+ panel = document.createElement('div');
115
+ panel.id = 'artifact-panel';
116
+ panel.style.cssText = 'position:fixed;top:0;right:0;width:640px;height:100vh;background:#0a0a14;border-left:1px solid #333;z-index:1000;display:flex;flex-direction:column;overflow:hidden';
117
+ document.body.appendChild(panel);
118
+ }
119
+ const optLabel = v => `${new Date(v.ts).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit' })} · ${esc(v.from || '?')}`;
120
+ const opts = versions.map((v, i) => `<option value="${i}">${optLabel(v)}</option>`).join('');
121
+ panel.innerHTML = `
122
+ <div style="padding:12px 14px;border-bottom:1px solid #1a1a2a;display:flex;align-items:center;gap:10px">
123
+ <span style="font-size:13px;font-weight:700;color:#ccc;flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">${esc(label)} — diff</span>
124
+ <button onclick="document.getElementById('artifact-panel').remove()" style="background:none;border:none;color:#888;font-size:16px;cursor:pointer">✕</button>
125
+ </div>
126
+ <div style="padding:8px 14px;border-bottom:1px solid #1a1a2a;display:flex;gap:8px;align-items:center;font-size:10px;color:#888">
127
+ <select id="diff-from" style="background:#151520;color:#ccc;border:1px solid #333;border-radius:3px;padding:2px 6px;font-size:10px">${opts}</select>
128
+ <span>→</span>
129
+ <select id="diff-to" style="background:#151520;color:#ccc;border:1px solid #333;border-radius:3px;padding:2px 6px;font-size:10px">${opts}</select>
130
+ </div>
131
+ <div id="diff-body" style="flex:1;overflow:auto;font-size:11px;line-height:1.6;font-family:var(--mono,ui-monospace,monospace)"></div>
132
+ `;
133
+ panel.style.display = 'flex';
134
+ const fromSel = document.getElementById('diff-from');
135
+ const toSel = document.getElementById('diff-to');
136
+ fromSel.value = String(versions.length - 2);
137
+ toSel.value = String(versions.length - 1);
138
+ const recompute = () => renderDiffBody(versions[Number(fromSel.value)].art.content, versions[Number(toSel.value)].art.content);
139
+ fromSel.onchange = recompute;
140
+ toSel.onchange = recompute;
141
+ recompute();
142
+ }
143
+
144
+ function renderDiffBody(oldText, newText) {
145
+ const body = document.getElementById('diff-body');
146
+ if (!body) return;
147
+ if (oldText === newText) {
148
+ body.innerHTML = '<div style="padding:14px;color:#666">No differences between these two versions.</div>';
149
+ return;
150
+ }
151
+ const rows = diffLines(oldText, newText);
152
+ if (!rows) {
153
+ body.innerHTML = '<div style="padding:14px;color:#666">File too large to diff line-by-line.</div>';
154
+ return;
155
+ }
156
+ body.innerHTML = rows.map(r => {
157
+ const style = r.type === 'add' ? 'background:#0d2818;color:#7ee787'
158
+ : r.type === 'del' ? 'background:#2d0d10;color:#ffa198' : 'color:#8b949e';
159
+ const prefix = r.type === 'add' ? '+' : r.type === 'del' ? '-' : ' ';
160
+ return `<div style="${style};padding:1px 12px;white-space:pre-wrap;word-break:break-word">${esc(prefix + ' ' + r.text)}</div>`;
161
+ }).join('');
162
+ }
163
+
164
+ /** Line-level LCS diff. Returns null (caller shows a fallback message) rather
165
+ * than hanging the tab if both files are unexpectedly huge — the O(n*m) DP
166
+ * table would otherwise blow past what's reasonable to allocate client-side.
167
+ * Bounds n and m individually, not just their product: a lopsided pair (e.g.
168
+ * n=1, m=4000000 — one huge file diffed against a near-empty one) passes the
169
+ * product check but still allocates a 4M-row DP table, one row per line of A. */
170
+ function diffLines(a, b) {
171
+ const A = a.split('\n'), B = b.split('\n');
172
+ const n = A.length, m = B.length;
173
+ const DIFF_MAX_LINES = 20000;
174
+ if (n > DIFF_MAX_LINES || m > DIFF_MAX_LINES || n * m > 4000000) return null;
175
+ const dp = new Array(n + 1);
176
+ for (let i = 0; i <= n; i++) dp[i] = new Int32Array(m + 1);
177
+ for (let i = n - 1; i >= 0; i--) {
178
+ for (let j = m - 1; j >= 0; j--) {
179
+ dp[i][j] = A[i] === B[j] ? dp[i + 1][j + 1] + 1 : Math.max(dp[i + 1][j], dp[i][j + 1]);
180
+ }
181
+ }
182
+ const out = [];
183
+ let i = 0, j = 0;
184
+ while (i < n && j < m) {
185
+ if (A[i] === B[j]) { out.push({ type: 'ctx', text: A[i] }); i++; j++; }
186
+ else if (dp[i + 1][j] >= dp[i][j + 1]) { out.push({ type: 'del', text: A[i] }); i++; }
187
+ else { out.push({ type: 'add', text: B[j] }); j++; }
188
+ }
189
+ while (i < n) { out.push({ type: 'del', text: A[i] }); i++; }
190
+ while (j < m) { out.push({ type: 'add', text: B[j] }); j++; }
191
+ return out;
192
+ }
@@ -383,6 +383,46 @@ html, body {
383
383
  content: '—'; color: oklch(36% 0.008 186 / 0.5); flex-shrink: 0; margin-top: 1px;
384
384
  }
385
385
 
386
+ /* ── Files tab ──────────────────────────────────────────────────── */
387
+ #files-pane { padding: 20px; }
388
+ .files-grid {
389
+ display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
390
+ gap: 10px;
391
+ }
392
+ .file-card {
393
+ background: var(--bg-panel);
394
+ border: 1px solid var(--border); border-radius: 3px;
395
+ padding: 10px 12px;
396
+ display: flex; align-items: center; gap: 10px;
397
+ transition: border-color 0.12s;
398
+ }
399
+ .file-card:hover { border-color: var(--teal-dim); }
400
+ .fc-icon { font-size: 18px; flex-shrink: 0; }
401
+ .fc-body { flex: 1; min-width: 0; }
402
+ .fc-name {
403
+ font-size: 12px; color: var(--text); font-weight: 500;
404
+ white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
405
+ }
406
+ .fc-meta {
407
+ font-size: 9px; color: var(--dim); margin-top: 2px;
408
+ white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
409
+ }
410
+ .fc-view {
411
+ flex-shrink: 0; background: #1a3a5a; color: #5d9fd9; border: 1px solid #2980b944;
412
+ border-radius: 4px; padding: 4px 10px; font-size: 10px; font-weight: 700; cursor: pointer;
413
+ }
414
+ .fc-view:hover { background: #204a70; }
415
+ .fc-diff {
416
+ flex-shrink: 0; background: #2a1a3a; color: #b98dd9; border: 1px solid #8e44ad44;
417
+ border-radius: 4px; padding: 4px 10px; font-size: 10px; font-weight: 700; cursor: pointer;
418
+ }
419
+ .fc-diff:hover { background: #3a2050; }
420
+ .fc-vbadge {
421
+ font-size: 8px; font-weight: 700; color: var(--dim); border: 1px solid var(--border);
422
+ border-radius: 2px; padding: 1px 5px; margin-left: 4px; letter-spacing: 0.5px;
423
+ }
424
+ .fc-binary { flex-shrink: 0; font-size: 9px; color: var(--dim); }
425
+
386
426
  /* ── Activity / event log ───────────────────────────────────────── */
387
427
  #activity-pane { padding: 20px; }
388
428
  #activity-log { display: flex; flex-direction: column; gap: 0; }
@@ -819,6 +859,7 @@ html, body {
819
859
  <button class="tab-btn" onclick="switchTab('roles')">ROLES</button>
820
860
  <button class="tab-btn" onclick="switchTab('activity')">ACTIVITY</button>
821
861
  <button class="tab-btn" onclick="switchTab('health')">HEALTH</button>
862
+ <button class="tab-btn" onclick="switchTab('files')">FILES</button>
822
863
  <button class="tab-btn" onclick="switchTab('chat')">CHAT</button>
823
864
  </div>
824
865
 
@@ -937,6 +978,14 @@ html, body {
937
978
  </div>
938
979
  </div>
939
980
 
981
+ <!-- Files tab: every asset any agent in this org has produced, across all runs -->
982
+ <div class="tab-pane" id="tab-files">
983
+ <div id="files-pane">
984
+ <div id="files-empty" class="act-empty" style="display:none">No files produced yet.</div>
985
+ <div id="files-grid" class="files-grid"></div>
986
+ </div>
987
+ </div>
988
+
940
989
  <!-- Chat tab -->
941
990
  <div class="tab-pane" id="tab-chat">
942
991
  <div id="chat-pane">
@@ -1081,9 +1130,19 @@ window.switchTab = function(tab) {
1081
1130
  const mainBody = document.getElementById('main-body');
1082
1131
  if (mainBody) mainBody.classList.toggle('chat-active', tab === 'chat');
1083
1132
  if (tab === 'chat') renderChatTab();
1133
+ else if (tab === 'files') renderFilesTab();
1084
1134
  else if (orgDetailData) renderTab(tab);
1085
1135
  };
1086
1136
 
1137
+ // Files tab + diff view — extracted to orgs-files.js to keep this file under
1138
+ // the CLAUDE.md 500-line-per-file guideline. Loaded as a classic script below,
1139
+ // sharing this script's global scope (function declarations here call into it,
1140
+ // e.g. renderFilesTab() from renderTab() above and applyArtifactEvent() from
1141
+ // handleMmEvent() below).
1142
+ </script>
1143
+ <script src="orgs-files.js"></script>
1144
+ <script>
1145
+
1087
1146
  // ── Render all orgs in sidebar ────────────────────────────────────
1088
1147
  function renderSidebar() {
1089
1148
  const list = document.getElementById('sb-list');
@@ -1689,8 +1748,13 @@ function orgSessionMatch(s) {
1689
1748
  const orgLc = selectedOrg.toLowerCase();
1690
1749
  const prompt = (s.prompt || '').toLowerCase();
1691
1750
  if (prompt.includes('running org: ' + orgLc) || prompt.includes('org: ' + orgLc)) return true;
1692
- // Also check events for an org field
1693
- return (s.events || []).some(ev => (ev.org || '').toLowerCase() === orgLc);
1751
+ // Only genuine org-run events (org:comms, org:artifact, org:start, ...) count.
1752
+ // agent:bash / agent:edit / etc. also carry an `org` field (ambient "which
1753
+ // org was active while this Claude Code session ran" tagging) — matching on
1754
+ // those too would pull an unrelated dev session's entire tool-call history
1755
+ // into this org's Chat/Files tabs whenever that org happened to be running
1756
+ // at the same time.
1757
+ return (s.events || []).some(ev => (ev.type || '').startsWith('org:') && (ev.org || '').toLowerCase() === orgLc);
1694
1758
  }
1695
1759
 
1696
1760
  function populateChatSelect() {
@@ -1834,21 +1898,21 @@ function appendChatEvent(ev, animate) {
1834
1898
  } else if (ev.type === 'agent:spawn') {
1835
1899
  const col = dc(ev.domain);
1836
1900
  const name = ev.agentType || ev.from || ev.agent || 'agent';
1837
- el = mkAgent(name, `spawned: ${esc(ev.task||'task')}`, ts, col, 'spawn');
1901
+ el = mkAgent(name, `spawned: ${ev.task||'task'}`, ts, col, 'spawn');
1838
1902
  } else if (ev.type === 'agent:complete') {
1839
1903
  const name = ev.agentType || ev.role || ev.from || 'agent';
1840
1904
  const result = (ev.result || '').slice(0, 300);
1841
1905
  const cost = ev.cost_usd ? ` · $${parseFloat(ev.cost_usd).toFixed(4)}` : '';
1842
1906
  const tools = ev.toolCalls?.length ? ` · ${ev.toolCalls.slice(0,4).join(', ')}` : '';
1843
- el = mkAgent(name, `${esc(result) || 'done'}${esc(tools)}${esc(cost)}`, ts, dc(ev.domain), 'complete');
1907
+ el = mkAgent(name, `${result || 'done'}${tools}${cost}`, ts, dc(ev.domain), 'complete');
1844
1908
  } else if (ev.type === 'org:comms') {
1845
- el = mkIntercom(ev.from || 'boss', ev.to || 'agent', esc(ev.msg || ev.message || ''), ts);
1909
+ el = mkIntercom(ev.from || 'boss', ev.to || 'agent', ev.msg || ev.message || '', ts);
1846
1910
  } else if (ev.type === 'agent:message') {
1847
- el = mkAgent(ev.agent||'agent', esc(ev.text||''), ts, dc(ev.domain), null);
1911
+ el = mkAgent(ev.agent||'agent', ev.text||'', ts, dc(ev.domain), null);
1848
1912
  } else if (ev.type === 'intercom') {
1849
- el = mkIntercom(ev.from, ev.to, esc(ev.msg||''), ts);
1913
+ el = mkIntercom(ev.from, ev.to, ev.msg||'', ts);
1850
1914
  } else if (ev.type === 'user:message') {
1851
- el = mkUser(esc(ev.text||ev.msg||''), ts);
1915
+ el = mkUser(ev.text||ev.msg||'', ts);
1852
1916
  } else if (ev.type === 'loop:start') {
1853
1917
  el = mkSys(`🔁 Loop: ${esc(ev.command||'')} (${ev.repeat||'?'} runs)`, ts);
1854
1918
  } else if (ev.type === 'loop:complete') {
@@ -1876,13 +1940,13 @@ function mkAgent(name, text, ts, color, typeTag) {
1876
1940
  el.className = 'cmsg cagent';
1877
1941
  const tagStyle = color ? `style="border-color:${color}44;color:${color}"` : '';
1878
1942
  const ttag = typeTag ? `<span class="cevtype">${esc(typeTag)}</span>` : '';
1879
- el.innerHTML = `<div class="cmsg-meta"><span class="ctag" ${tagStyle}>${esc(name)}</span>${ttag}<span class="cts">${ts}</span></div><div class="cbubble">${text}</div>`;
1943
+ el.innerHTML = `<div class="cmsg-meta"><span class="ctag" ${tagStyle}>${esc(name)}</span>${ttag}<span class="cts">${ts}</span></div><div class="cbubble">${esc(text)}</div>`;
1880
1944
  return el;
1881
1945
  }
1882
1946
  function mkIntercom(from, to, text, ts) {
1883
1947
  const el = document.createElement('div');
1884
1948
  el.className = 'cmsg cic';
1885
- el.innerHTML = `<div class="cmsg-meta"><span class="ctag sender">${esc(from||'?')}</span><span class="cic-arrow">→</span><span class="ctag receiver">${esc(to||'?')}</span><span class="cevtype">intercom</span><span class="cts">${ts}</span></div><div class="cbubble">${text}</div>`;
1949
+ el.innerHTML = `<div class="cmsg-meta"><span class="ctag sender">${esc(from||'?')}</span><span class="cic-arrow">→</span><span class="ctag receiver">${esc(to||'?')}</span><span class="cevtype">intercom</span><span class="cts">${ts}</span></div><div class="cbubble">${esc(text)}</div>`;
1886
1950
  return el;
1887
1951
  }
1888
1952
  function mkArtifact(art, ts) {
@@ -1901,7 +1965,7 @@ function mkArtifact(art, ts) {
1901
1965
  function mkUser(text, ts) {
1902
1966
  const el = document.createElement('div');
1903
1967
  el.className = 'cmsg cuser';
1904
- el.innerHTML = `<div class="cmsg-meta" style="justify-content:flex-end"><span class="cts">${ts}</span><span class="ctag" style="border-color:oklch(68% 0.18 250 / 0.35);color:oklch(68% 0.14 250)">you</span></div><div class="cbubble">${text}</div>`;
1968
+ el.innerHTML = `<div class="cmsg-meta" style="justify-content:flex-end"><span class="cts">${ts}</span><span class="ctag" style="border-color:oklch(68% 0.18 250 / 0.35);color:oklch(68% 0.14 250)">you</span></div><div class="cbubble">${esc(text)}</div>`;
1905
1969
  return el;
1906
1970
  }
1907
1971
 
@@ -2042,6 +2106,10 @@ function handleMmEvent(ev) {
2042
2106
  appendChatEvent(ev, true);
2043
2107
  chatScrollFeed();
2044
2108
  }
2109
+ // Live-update the Files tab when a new asset lands while it's open — incremental,
2110
+ // not a rescan of every session's every event (that's collectOrgArtifacts()'s job,
2111
+ // reserved for tab-open / initial load in renderFilesTab()).
2112
+ if (ev.type === 'org:artifact' && currentTab === 'files') applyArtifactEvent(ev);
2045
2113
  }
2046
2114
  }
2047
2115