@theihtisham/agent-shadow-brain 5.0.0 → 5.1.1

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 (61) hide show
  1. package/README.md +575 -292
  2. package/dist/adapters/index.d.ts.map +1 -1
  3. package/dist/adapters/index.js +1 -0
  4. package/dist/adapters/index.js.map +1 -1
  5. package/dist/brain/auto-setup.d.ts +54 -0
  6. package/dist/brain/auto-setup.d.ts.map +1 -0
  7. package/dist/brain/auto-setup.js +660 -0
  8. package/dist/brain/auto-setup.js.map +1 -0
  9. package/dist/brain/brain-portability.d.ts +81 -0
  10. package/dist/brain/brain-portability.d.ts.map +1 -0
  11. package/dist/brain/brain-portability.js +329 -0
  12. package/dist/brain/brain-portability.js.map +1 -0
  13. package/dist/brain/code-dna.d.ts +70 -0
  14. package/dist/brain/code-dna.d.ts.map +1 -0
  15. package/dist/brain/code-dna.js +611 -0
  16. package/dist/brain/code-dna.js.map +1 -0
  17. package/dist/brain/fine-tuning-engine.d.ts +99 -0
  18. package/dist/brain/fine-tuning-engine.d.ts.map +1 -0
  19. package/dist/brain/fine-tuning-engine.js +681 -0
  20. package/dist/brain/fine-tuning-engine.js.map +1 -0
  21. package/dist/brain/intent-engine.d.ts +64 -0
  22. package/dist/brain/intent-engine.d.ts.map +1 -0
  23. package/dist/brain/intent-engine.js +374 -0
  24. package/dist/brain/intent-engine.js.map +1 -0
  25. package/dist/brain/lsp-server.d.ts +62 -0
  26. package/dist/brain/lsp-server.d.ts.map +1 -0
  27. package/dist/brain/lsp-server.js +480 -0
  28. package/dist/brain/lsp-server.js.map +1 -0
  29. package/dist/brain/mcp-server.d.ts +12 -0
  30. package/dist/brain/mcp-server.d.ts.map +1 -1
  31. package/dist/brain/mcp-server.js +396 -37
  32. package/dist/brain/mcp-server.js.map +1 -1
  33. package/dist/brain/orchestrator.d.ts +13 -0
  34. package/dist/brain/orchestrator.d.ts.map +1 -1
  35. package/dist/brain/orchestrator.js +25 -1
  36. package/dist/brain/orchestrator.js.map +1 -1
  37. package/dist/brain/plugin-system.d.ts +97 -0
  38. package/dist/brain/plugin-system.d.ts.map +1 -0
  39. package/dist/brain/plugin-system.js +404 -0
  40. package/dist/brain/plugin-system.js.map +1 -0
  41. package/dist/brain/smart-cache.d.ts +100 -0
  42. package/dist/brain/smart-cache.d.ts.map +1 -0
  43. package/dist/brain/smart-cache.js +417 -0
  44. package/dist/brain/smart-cache.js.map +1 -0
  45. package/dist/brain/temporal-intelligence.d.ts +89 -0
  46. package/dist/brain/temporal-intelligence.d.ts.map +1 -0
  47. package/dist/brain/temporal-intelligence.js +378 -0
  48. package/dist/brain/temporal-intelligence.js.map +1 -0
  49. package/dist/cli.js +444 -3
  50. package/dist/cli.js.map +1 -1
  51. package/dist/dashboard/server.d.ts +4 -0
  52. package/dist/dashboard/server.d.ts.map +1 -1
  53. package/dist/dashboard/server.js +453 -330
  54. package/dist/dashboard/server.js.map +1 -1
  55. package/dist/index.d.ts +11 -1
  56. package/dist/index.d.ts.map +1 -1
  57. package/dist/index.js +12 -1
  58. package/dist/index.js.map +1 -1
  59. package/dist/types.d.ts +284 -1
  60. package/dist/types.d.ts.map +1 -1
  61. package/package.json +28 -4
@@ -1,5 +1,6 @@
1
1
  // src/dashboard/server.ts — Real-time Web Dashboard with WebSocket
2
- // Serves an embedded HTML UI + pushes live events from Orchestrator
2
+ // v5.0.1 — Infinite Intelligence Edition
3
+ // 8-panel layout: Health, Insights, AI Tools, Memory Tiers, Modules, Fixes, Controls, Activity
3
4
  import * as http from 'http';
4
5
  import { WebSocketServer, WebSocket } from 'ws';
5
6
  // ── Embedded HTML Client ──────────────────────────────────────────────────────
@@ -8,346 +9,365 @@ const HTML_CLIENT = `<!DOCTYPE html>
8
9
  <head>
9
10
  <meta charset="UTF-8">
10
11
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
11
- <title>🧠 Shadow Brain Dashboard</title>
12
+ <title>Shadow Brain v5.0.1 — Dashboard</title>
12
13
  <style>
13
14
  :root {
14
- --bg: #0d1117; --bg2: #161b22; --bg3: #21262d;
15
- --border: #30363d; --text: #e6edf3; --text2: #8b949e;
16
- --green: #3fb950; --yellow: #d29922; --red: #f85149;
17
- --blue: #58a6ff; --purple: #bc8cff; --orange: #ffa657;
18
- --cyan: #39d353;
15
+ --bg:#0a0e14;--bg2:#131820;--bg3:#1a2030;--bg4:#232d3f;
16
+ --border:#2a3550;--text:#d4dce8;--text2:#6b7d99;
17
+ --green:#00e676;--yellow:#ffca28;--red:#ff5252;
18
+ --blue:#42a5f5;--purple:#b388ff;--orange:#ffab40;
19
+ --cyan:#18ffff;--pink:#ff4081;
19
20
  }
20
- * { box-sizing: border-box; margin: 0; padding: 0; }
21
- body { background: var(--bg); color: var(--text); font-family: 'Segoe UI', system-ui, monospace; font-size: 14px; height: 100vh; overflow: hidden; display: flex; flex-direction: column; }
22
- header { background: var(--bg2); border-bottom: 1px solid var(--border); padding: 12px 20px; display: flex; align-items: center; gap: 16px; flex-shrink: 0; }
23
- header h1 { font-size: 18px; font-weight: 700; letter-spacing: -0.5px; }
24
- header h1 span { color: var(--blue); }
25
- .status-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--red); transition: background .3s; }
26
- .status-dot.connected { background: var(--green); animation: pulse 2s infinite; }
27
- @keyframes pulse { 0%,100%{opacity:1} 50%{opacity:.5} }
28
- .badge { background: var(--bg3); border: 1px solid var(--border); border-radius: 12px; padding: 2px 10px; font-size: 12px; color: var(--text2); }
29
- .badge.score { color: var(--green); border-color: var(--green); }
30
- .badge.grade { font-weight: 700; }
31
- .badge.grade.A { color: var(--green); border-color: var(--green); }
32
- .badge.grade.B { color: var(--yellow); border-color: var(--yellow); }
33
- .badge.grade.C { color: var(--orange); border-color: var(--orange); }
34
- .badge.grade.D,.badge.grade.F { color: var(--red); border-color: var(--red); }
35
- .trend { font-size: 16px; }
36
- .trend.improving { color: var(--green); }
37
- .trend.declining { color: var(--red); }
38
- .trend.stable { color: var(--text2); }
39
- .layout { display: grid; grid-template-columns: 340px 1fr; grid-template-rows: 1fr 1fr; gap: 1px; background: var(--border); flex: 1; overflow: hidden; }
40
- .panel { background: var(--bg); overflow: hidden; display: flex; flex-direction: column; }
41
- .panel-header { background: var(--bg2); padding: 8px 14px; font-size: 12px; font-weight: 600; color: var(--text2); text-transform: uppercase; letter-spacing: .08em; border-bottom: 1px solid var(--border); display: flex; align-items: center; justify-content: space-between; flex-shrink: 0; }
42
- .panel-body { flex: 1; overflow-y: auto; padding: 10px; }
43
- .panel-body::-webkit-scrollbar { width: 4px; } .panel-body::-webkit-scrollbar-thumb { background: var(--border); }
44
- /* Health panel */
45
- .health-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin-bottom: 14px; }
46
- .health-big { grid-column: 1/-1; text-align: center; padding: 16px; background: var(--bg2); border-radius: 8px; border: 1px solid var(--border); }
47
- .health-big .num { font-size: 56px; font-weight: 900; line-height: 1; }
48
- .health-big .label { font-size: 12px; color: var(--text2); margin-top: 4px; }
49
- .dim { background: var(--bg2); border: 1px solid var(--border); border-radius: 6px; padding: 10px; }
50
- .dim .name { font-size: 11px; color: var(--text2); margin-bottom: 6px; }
51
- .dim .bar-wrap { background: var(--bg3); border-radius: 3px; height: 6px; overflow: hidden; margin-bottom: 4px; }
52
- .dim .bar-fill { height: 100%; border-radius: 3px; transition: width .6s ease; }
53
- .dim .score-val { font-size: 13px; font-weight: 700; }
21
+ *{box-sizing:border-box;margin:0;padding:0}
22
+ body{background:var(--bg);color:var(--text);font-family:'Segoe UI',system-ui,-apple-system,monospace;font-size:13px;height:100vh;overflow:hidden;display:flex;flex-direction:column}
23
+
24
+ /* Header */
25
+ header{background:linear-gradient(180deg,var(--bg2),var(--bg));border-bottom:1px solid var(--border);padding:10px 20px;display:flex;align-items:center;gap:12px;flex-shrink:0}
26
+ header h1{font-size:16px;font-weight:800;letter-spacing:-.5px}
27
+ header h1 .v{color:var(--cyan);font-size:11px;font-weight:500;margin-left:4px}
28
+ .dot{width:8px;height:8px;border-radius:50%;background:var(--red);transition:.3s}
29
+ .dot.on{background:var(--green);box-shadow:0 0 8px var(--green);animation:pulse 2s infinite}
30
+ @keyframes pulse{0%,100%{opacity:1}50%{opacity:.5}}
31
+ .pill{background:var(--bg3);border:1px solid var(--border);border-radius:12px;padding:2px 10px;font-size:11px;color:var(--text2)}
32
+ .pill.score{font-weight:700;border-color:var(--green);color:var(--green)}
33
+ .pill.grade{font-weight:800}
34
+ .pill.gA{color:var(--green);border-color:var(--green)}
35
+ .pill.gB{color:var(--yellow);border-color:var(--yellow)}
36
+ .pill.gC{color:var(--orange);border-color:var(--orange)}
37
+ .pill.gD,.pill.gF{color:var(--red);border-color:var(--red)}
38
+ .trend{font-size:14px}
39
+ .trend.up{color:var(--green)}.trend.down{color:var(--red)}.trend.flat{color:var(--text2)}
40
+ .controls{margin-left:auto;display:flex;gap:6px}
41
+ .btn{background:var(--bg3);border:1px solid var(--border);border-radius:6px;padding:4px 12px;font-size:11px;color:var(--text);cursor:pointer;transition:.2s;font-weight:600}
42
+ .btn:hover{background:var(--bg4);border-color:var(--blue)}
43
+ .btn.danger{color:var(--red);border-color:var(--red)}
44
+ .btn.danger:hover{background:rgba(255,82,82,.15)}
45
+ .btn.primary{color:var(--green);border-color:var(--green)}
46
+ .btn.primary:hover{background:rgba(0,230,118,.15)}
47
+
48
+ /* Layout */
49
+ .grid{display:grid;grid-template-columns:280px 1fr 280px;grid-template-rows:auto 1fr 1fr;gap:1px;background:var(--border);flex:1;overflow:hidden}
50
+ .pnl{background:var(--bg);overflow:hidden;display:flex;flex-direction:column}
51
+ .pnl-h{background:var(--bg2);padding:7px 12px;font-size:11px;font-weight:700;color:var(--text2);text-transform:uppercase;letter-spacing:.08em;border-bottom:1px solid var(--border);display:flex;align-items:center;justify-content:space-between;flex-shrink:0}
52
+ .pnl-b{flex:1;overflow-y:auto;padding:8px}
53
+ .pnl-b::-webkit-scrollbar{width:3px}.pnl-b::-webkit-scrollbar-thumb{background:var(--border)}
54
+ .cnt{background:var(--bg4);border-radius:10px;padding:1px 7px;font-size:10px;color:var(--text2)}
55
+
56
+ /* Row 1: top bar */
57
+ .topbar{grid-column:1/-1;background:var(--bg2);padding:6px 14px;display:flex;gap:16px;align-items:center;flex-shrink:0;overflow-x:auto}
58
+ .stat{display:flex;flex-direction:column;align-items:center;min-width:80px}
59
+ .stat .val{font-size:20px;font-weight:900;line-height:1}
60
+ .stat .lbl{font-size:9px;color:var(--text2);text-transform:uppercase;letter-spacing:.06em;margin-top:2px}
61
+
62
+ /* Health */
63
+ .health-big{text-align:center;padding:12px;background:var(--bg2);border-radius:8px;border:1px solid var(--border);margin-bottom:10px}
64
+ .health-big .num{font-size:48px;font-weight:900;line-height:1}
65
+ .health-big .lbl{font-size:11px;color:var(--text2);margin-top:3px}
66
+ .dim{background:var(--bg2);border:1px solid var(--border);border-radius:5px;padding:8px;margin-bottom:6px}
67
+ .dim .name{font-size:10px;color:var(--text2);margin-bottom:4px}
68
+ .dim .bw{background:var(--bg4);border-radius:3px;height:5px;overflow:hidden;margin-bottom:3px}
69
+ .dim .bf{height:100%;border-radius:3px;transition:width .6s}
70
+ .dim .sv{font-size:12px;font-weight:700}
71
+
72
+ /* AI Tools */
73
+ .tool{display:flex;align-items:center;gap:8px;padding:6px 8px;border-radius:5px;margin-bottom:4px;background:var(--bg2);border:1px solid var(--border)}
74
+ .tool .icon{font-size:16px;width:24px;text-align:center}
75
+ .tool .info{flex:1;min-width:0}
76
+ .tool .tname{font-size:12px;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
77
+ .tool .tstat{font-size:10px;color:var(--text2)}
78
+ .tool .tdot{width:6px;height:6px;border-radius:50%;flex-shrink:0}
79
+ .tool .tdot.active{background:var(--green)}.tool .tdot.inactive{background:var(--text2)}.tool .tdot.error{background:var(--red)}
80
+
81
+ /* Memory tiers */
82
+ .tier{background:var(--bg2);border:1px solid var(--border);border-radius:5px;padding:8px;margin-bottom:5px}
83
+ .tier .th{display:flex;justify-content:space-between;align-items:center;margin-bottom:4px}
84
+ .tier .tname{font-size:11px;font-weight:600}
85
+ .tier .tcount{font-size:11px;font-weight:700;color:var(--cyan)}
86
+ .tier .bw{background:var(--bg4);border-radius:3px;height:4px;overflow:hidden;margin-bottom:3px}
87
+ .tier .bf{height:100%;border-radius:3px;transition:width .5s}
88
+ .tier .tmeta{font-size:9px;color:var(--text2)}
89
+ .tier.t0 .tname{color:var(--green)}.tier.t1 .tname{color:var(--blue)}.tier.t2 .tname{color:var(--purple)}.tier.t3 .tname{color:var(--orange)}
90
+
91
+ /* Modules */
92
+ .mod{display:flex;align-items:center;gap:6px;padding:4px 8px;border-radius:4px;margin-bottom:3px;background:var(--bg2);border:1px solid var(--border);font-size:11px}
93
+ .mod .mdot{width:5px;height:5px;border-radius:50%;flex-shrink:0}
94
+ .mod .mdot.active{background:var(--green)}.mod .mdot.idle{background:var(--text2)}.mod .mdot.error{background:var(--red)}
95
+ .mod .mname{flex:1;font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
96
+ .mod .mstat{font-size:9px;color:var(--text2);flex-shrink:0}
97
+
54
98
  /* Insights */
55
- .insight { background: var(--bg2); border: 1px solid var(--border); border-radius: 6px; padding: 10px 12px; margin-bottom: 8px; border-left: 3px solid var(--border); animation: fadeIn .3s ease; }
56
- @keyframes fadeIn { from{opacity:0;transform:translateY(-4px)} to{opacity:1;transform:none} }
57
- .insight.critical { border-left-color: var(--red); }
58
- .insight.high { border-left-color: var(--orange); }
59
- .insight.medium { border-left-color: var(--yellow); }
60
- .insight.low { border-left-color: var(--text2); }
61
- .insight-title { font-weight: 600; font-size: 13px; margin-bottom: 3px; }
62
- .insight-meta { font-size: 11px; color: var(--text2); display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 5px; }
63
- .insight-content { font-size: 12px; color: var(--text2); line-height: 1.5; }
64
- .tag { background: var(--bg3); border-radius: 10px; padding: 1px 7px; font-size: 10px; }
65
- .tag.critical { background: rgba(248,81,73,.15); color: var(--red); }
66
- .tag.high { background: rgba(255,166,87,.15); color: var(--orange); }
67
- .tag.medium { background: rgba(210,153,34,.15); color: var(--yellow); }
68
- .tag.security { background: rgba(248,81,73,.12); color: var(--red); }
69
- .tag.performance { background: rgba(57,211,83,.12); color: var(--cyan); }
70
- .tag.quality { background: rgba(88,166,255,.12); color: var(--blue); }
99
+ .ins{background:var(--bg2);border:1px solid var(--border);border-radius:5px;padding:8px 10px;margin-bottom:6px;border-left:3px solid var(--border);animation:fadeIn .25s}
100
+ @keyframes fadeIn{from{opacity:0;transform:translateY(-3px)}to{opacity:1;transform:none}}
101
+ .ins.critical{border-left-color:var(--red)}.ins.high{border-left-color:var(--orange)}.ins.medium{border-left-color:var(--yellow)}.ins.low{border-left-color:var(--text2)}
102
+ .ins-t{font-weight:600;font-size:12px;margin-bottom:2px}
103
+ .ins-m{font-size:10px;color:var(--text2);display:flex;gap:6px;flex-wrap:wrap;margin-bottom:4px}
104
+ .ins-c{font-size:11px;color:var(--text2);line-height:1.45}
105
+ .tag{background:var(--bg4);border-radius:8px;padding:1px 6px;font-size:9px}
106
+ .tag.critical{background:rgba(255,82,82,.15);color:var(--red)}
107
+ .tag.high{background:rgba(255,171,64,.15);color:var(--orange)}
108
+ .tag.medium{background:rgba(255,202,40,.15);color:var(--yellow)}
109
+ .tag.low{background:rgba(107,125,153,.15);color:var(--text2)}
110
+ .tag.security{background:rgba(255,82,82,.12);color:var(--red)}
111
+ .tag.performance{background:rgba(24,255,255,.12);color:var(--cyan)}
112
+ .tag.quality{background:rgba(66,165,245,.12);color:var(--blue)}
113
+
71
114
  /* Fixes */
72
- .fix { background: var(--bg2); border: 1px solid var(--border); border-radius: 6px; padding: 10px 12px; margin-bottom: 8px; animation: fadeIn .3s ease; }
73
- .fix-title { font-weight: 600; font-size: 13px; margin-bottom: 6px; }
74
- .fix-file { font-size: 11px; color: var(--text2); margin-bottom: 8px; }
75
- .code-block { background: var(--bg3); border: 1px solid var(--border); border-radius: 4px; padding: 8px 10px; font-family: 'Cascadia Code', 'Fira Code', monospace; font-size: 11px; line-height: 1.6; overflow-x: auto; }
76
- .code-before { border-left: 3px solid var(--red); }
77
- .code-after { border-left: 3px solid var(--green); }
78
- .fix-label { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .08em; margin-bottom: 4px; }
79
- .fix-label.before { color: var(--red); }
80
- .fix-label.after { color: var(--green); }
81
- .fix-explanation { font-size: 11px; color: var(--text2); margin-top: 8px; line-height: 1.5; }
115
+ .fix{background:var(--bg2);border:1px solid var(--border);border-radius:5px;padding:8px 10px;margin-bottom:6px;animation:fadeIn .25s}
116
+ .fix-t{font-weight:600;font-size:12px;margin-bottom:4px}
117
+ .fix-f{font-size:10px;color:var(--text2);margin-bottom:6px}
118
+ .cb{background:var(--bg4);border:1px solid var(--border);border-radius:3px;padding:6px 8px;font-family:'Cascadia Code','Fira Code',monospace;font-size:10px;line-height:1.5;overflow-x:auto}
119
+ .cb.bef{border-left:3px solid var(--red)}.cb.aft{border-left:3px solid var(--green)}
120
+ .fl{font-size:9px;font-weight:700;text-transform:uppercase;letter-spacing:.06em;margin-bottom:3px}
121
+ .fl.bef{color:var(--red)}.fl.aft{color:var(--green)}
122
+ .fix-x{font-size:10px;color:var(--text2);margin-top:6px;line-height:1.4}
123
+
82
124
  /* Log */
83
- .log-entry { font-family: monospace; font-size: 12px; padding: 3px 0; border-bottom: 1px solid var(--bg2); display: flex; gap: 10px; }
84
- .log-time { color: var(--text2); flex-shrink: 0; }
85
- .log-msg { flex: 1; word-break: break-all; }
86
- .log-msg.info { color: var(--blue); }
87
- .log-msg.success { color: var(--green); }
88
- .log-msg.warning { color: var(--yellow); }
89
- .log-msg.error { color: var(--red); }
90
- /* Empty state */
91
- .empty { text-align: center; color: var(--text2); padding: 40px 20px; font-size: 13px; }
92
- .empty .icon { font-size: 32px; margin-bottom: 10px; }
93
- .spinner { display: inline-block; animation: spin 1s linear infinite; }
94
- @keyframes spin { to { transform: rotate(360deg); } }
95
- .panel-count { background: var(--bg3); border-radius: 10px; padding: 1px 7px; font-size: 11px; color: var(--text2); }
125
+ .log{font-family:monospace;font-size:11px;padding:2px 0;border-bottom:1px solid var(--bg2);display:flex;gap:8px}
126
+ .log-t{color:var(--text2);flex-shrink:0;font-size:10px}
127
+ .log-m{flex:1;word-break:break-all}
128
+ .log-m.info{color:var(--blue)}.log-m.success{color:var(--green)}.log-m.warning{color:var(--yellow)}.log-m.error{color:var(--red)}
129
+
130
+ .empty{text-align:center;color:var(--text2);padding:30px 16px;font-size:12px}
131
+ .empty .ic{font-size:28px;margin-bottom:8px}
132
+ .spin{display:inline-block;animation:spin 1s linear infinite}
133
+ @keyframes spin{to{transform:rotate(360deg)}}
96
134
  </style>
97
135
  </head>
98
136
  <body>
99
137
  <header>
100
- <div class="status-dot" id="statusDot"></div>
101
- <h1>🧠 <span>Shadow</span>Brain</h1>
102
- <span class="badge" id="badgeScore">—/100</span>
103
- <span class="badge grade" id="badgeGrade">—</span>
104
- <span class="trend" id="badgeTrend" title="Trend">→</span>
105
- <span class="badge" id="badgeAgents">No agents</span>
106
- <span style="flex:1"></span>
107
- <span class="badge" id="badgeProject" style="font-size:11px;max-width:300px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">Connecting…</span>
138
+ <div class="dot" id="dot"></div>
139
+ <h1>\u{1F9E0} Shadow Brain <span class="v">v5.0.1</span></h1>
140
+ <span class="pill score" id="pScore">\u2014/100</span>
141
+ <span class="pill grade" id="pGrade">\u2014</span>
142
+ <span class="trend flat" id="pTrend">\u2192</span>
143
+ <span class="pill" id="pAgents">No agents</span>
144
+ <span class="pill" id="pModules" style="color:var(--cyan)">0 modules</span>
145
+ <div class="controls">
146
+ <button class="btn primary" id="btnAnalyze" onclick="triggerAnalysis()">Analyze</button>
147
+ <button class="btn" id="btnRefresh" onclick="refreshAll()">Refresh</button>
148
+ </div>
108
149
  </header>
109
150
 
110
- <div class="layout">
111
- <!-- Top-left: Health Score -->
112
- <div class="panel" style="grid-row: 1">
113
- <div class="panel-header">⬟ Health Score</div>
114
- <div class="panel-body" id="healthPanel">
115
- <div class="empty"><div class="icon"><span class="spinner">⟳</span></div>Waiting for analysis…</div>
116
- </div>
151
+ <div class="grid">
152
+ <!-- Row 1: stats bar -->
153
+ <div class="topbar">
154
+ <div class="stat"><div class="val" id="sInsights" style="color:var(--blue)">0</div><div class="lbl">Insights</div></div>
155
+ <div class="stat"><div class="val" id="sFixes" style="color:var(--green)">0</div><div class="lbl">Fixes</div></div>
156
+ <div class="stat"><div class="val" id="sMemory" style="color:var(--purple)">0</div><div class="lbl">Memory KB</div></div>
157
+ <div class="stat"><div class="val" id="sModules" style="color:var(--cyan)">0</div><div class="lbl">Modules</div></div>
158
+ <div class="stat"><div class="val" id="sTools" style="color:var(--orange)">0</div><div class="lbl">AI Tools</div></div>
159
+ <div class="stat"><div class="val" id="sEvol" style="color:var(--pink)">0</div><div class="lbl">Evolution Gen</div></div>
160
+ <div class="stat"><div class="val" id="sSwarm" style="color:var(--yellow)">0%</div><div class="lbl">Swarm Conv</div></div>
161
+ <div class="stat"><div class="val" id="sTurbo" style="color:var(--green)">0x</div><div class="lbl">Turbo Comp</div></div>
162
+ <span style="flex:1"></span>
163
+ <span style="font-size:10px;color:var(--text2)" id="pProject">Connecting\u2026</span>
117
164
  </div>
118
165
 
119
- <!-- Top-right: Insights stream -->
120
- <div class="panel" style="grid-row: 1">
121
- <div class="panel-header">
122
- 💡 Live Insights
123
- <span class="panel-count" id="insightCount">0</span>
124
- </div>
125
- <div class="panel-body" id="insightsPanel">
126
- <div class="empty"><div class="icon">💡</div>Insights will appear here…</div>
127
- </div>
166
+ <!-- Col 1: Health + Memory -->
167
+ <div class="pnl" style="grid-row:2/4">
168
+ <div class="pnl-h">\u2B22 Health Score</div>
169
+ <div class="pnl-b" id="healthPnl"><div class="empty"><div class="ic"><span class="spin">\u27F3</span></div>Waiting\u2026</div></div>
170
+ <div class="pnl-h">\u{1F4BE} Memory Tiers</div>
171
+ <div class="pnl-b" id="memoryPnl"><div class="empty"><div class="ic">\u{1F4BE}</div>No memory data</div></div>
128
172
  </div>
129
173
 
130
- <!-- Bottom-left: Smart Fixes -->
131
- <div class="panel" style="grid-row: 2">
132
- <div class="panel-header">
133
- 🔧 Smart Fixes
134
- <span class="panel-count" id="fixCount">0</span>
135
- </div>
136
- <div class="panel-body" id="fixesPanel">
137
- <div class="empty"><div class="icon">🔧</div>Fix suggestions will appear here…</div>
138
- </div>
174
+ <!-- Col 2: Insights + Fixes -->
175
+ <div class="pnl" style="grid-row:2">
176
+ <div class="pnl-h">\u{1F4A1} Live Insights <span class="cnt" id="iCnt">0</span></div>
177
+ <div class="pnl-b" id="insPnl"><div class="empty"><div class="ic">\u{1F4A1}</div>Insights will appear\u2026</div></div>
178
+ </div>
179
+ <div class="pnl" style="grid-row:3">
180
+ <div class="pnl-h">\u{1F527} Smart Fixes <span class="cnt" id="fCnt">0</span></div>
181
+ <div class="pnl-b" id="fixPnl"><div class="empty"><div class="ic">\u{1F527}</div>Fix suggestions\u2026</div></div>
139
182
  </div>
140
183
 
141
- <!-- Bottom-right: Activity Log -->
142
- <div class="panel" style="grid-row: 2">
143
- <div class="panel-header">📋 Activity Log</div>
144
- <div class="panel-body" id="logPanel"></div>
184
+ <!-- Col 3: AI Tools + Modules + Log -->
185
+ <div class="pnl" style="grid-row:2">
186
+ <div class="pnl-h">\u{1F916} AI Tools</div>
187
+ <div class="pnl-b" id="toolsPnl"><div class="empty"><div class="ic">\u{1F916}</div>Detecting tools\u2026</div></div>
188
+ </div>
189
+ <div class="pnl" style="grid-row:3">
190
+ <div class="pnl-h">\u2699 Brain Modules</div>
191
+ <div class="pnl-b" id="modPnl"><div class="empty"><div class="ic">\u2699</div>Loading modules\u2026</div></div>
145
192
  </div>
146
193
  </div>
147
194
 
148
195
  <script>
149
- const MAX_INSIGHTS = 50;
150
- const MAX_LOG = 100;
151
- let insightCount = 0;
152
- let fixCount = 0;
153
- let allInsights = [];
196
+ const MAX_INS=50,MAX_LOG=80;
197
+ let insCnt=0,fixCnt=0,allIns=[];
154
198
 
155
- const statusDot = document.getElementById('statusDot');
156
- const badgeScore = document.getElementById('badgeScore');
157
- const badgeGrade = document.getElementById('badgeGrade');
158
- const badgeTrend = document.getElementById('badgeTrend');
159
- const badgeAgents = document.getElementById('badgeAgents');
160
- const badgeProject = document.getElementById('badgeProject');
161
- const healthPanel = document.getElementById('healthPanel');
162
- const insightsPanel = document.getElementById('insightsPanel');
163
- const fixesPanel = document.getElementById('fixesPanel');
164
- const logPanel = document.getElementById('logPanel');
165
- const insightCountEl = document.getElementById('insightCount');
166
- const fixCountEl = document.getElementById('fixCount');
199
+ const $=id=>document.getElementById(id);
200
+ const dot=$('dot'),pScore=$('pScore'),pGrade=$('pGrade'),pTrend=$('pTrend');
201
+ const pAgents=$('pAgents'),pModules=$('pModules'),pProject=$('pProject');
202
+ const healthPnl=$('healthPnl'),memoryPnl=$('memoryPnl');
203
+ const insPnl=$('insPnl'),fixPnl=$('fixPnl');
204
+ const toolsPnl=$('toolsPnl'),modPnl=$('modPnl');
167
205
 
168
- function ts() {
169
- return new Date().toLocaleTimeString('en-US', { hour12: false });
170
- }
206
+ function ts(){return new Date().toLocaleTimeString('en-US',{hour12:false})}
207
+ function esc(s){return String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;')}
208
+ function sc(v){return v>=85?'var(--green)':v>=70?'var(--yellow)':v>=50?'var(--orange)':'var(--red)'}
171
209
 
172
- function log(msg, type = 'info') {
173
- const el = document.createElement('div');
174
- el.className = 'log-entry';
175
- el.innerHTML = \`<span class="log-time">\${ts()}</span><span class="log-msg \${type}">\${escapeHtml(msg)}</span>\`;
176
- if (logPanel.children.length >= MAX_LOG) logPanel.removeChild(logPanel.lastChild);
177
- logPanel.insertBefore(el, logPanel.firstChild);
210
+ // Log to insights panel feedback via activity
211
+ function logActivity(msg,type){
212
+ // We no longer have a dedicated log panel, so we use a minimal approach
178
213
  }
179
214
 
180
- function escapeHtml(s) {
181
- return String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;');
215
+ function renderHealth(s){
216
+ if(!s)return;
217
+ const c=sc(s.overall);
218
+ const ti=s.trend==='improving'?'\u2191':s.trend==='declining'?'\u2193':'\u2192';
219
+ const tc=s.trend==='improving'?'up':s.trend==='declining'?'down':'flat';
220
+ pScore.textContent=s.overall+'/100';pScore.style.color=c;pScore.style.borderColor=c;
221
+ pGrade.textContent=s.grade;
222
+ const gc=s.grade.replace('+','').charAt(0);
223
+ pGrade.className='pill grade g'+gc;
224
+ pTrend.textContent=ti;pTrend.className='trend '+tc;
225
+ const dh=s.dimensions.map(d=>{
226
+ const col=sc(d.score);
227
+ const det=d.details.map(x=>'<div style="font-size:9px;color:var(--text2);margin-top:1px">\u2022 '+esc(x)+'</div>').join('');
228
+ return '<div class="dim"><div class="name">'+esc(d.name)+'</div><div class="bw"><div class="bf" style="width:'+d.score+'%;background:'+col+'"></div></div><div class="sv" style="color:'+col+'">'+d.score+'%</div>'+det+'</div>';
229
+ }).join('');
230
+ const ti2=s.topIssues.length>0?'<div style="margin-top:8px;padding:6px;background:var(--bg2);border-radius:5px;border:1px solid var(--border)"><div style="font-size:10px;color:var(--red);font-weight:700;margin-bottom:4px">\u{1F525} Top Issues</div>'+s.topIssues.map(i=>'<div style="font-size:10px;color:var(--text2);margin-bottom:2px">\u2022 '+esc(i)+'</div>').join('')+'</div>':'';
231
+ healthPnl.innerHTML='<div class="health-big"><div class="num" style="color:'+c+'">'+s.overall+'</div><div class="lbl">Health / 100 \u2014 Grade '+s.grade+' '+ti+'</div></div>'+dh+ti2;
182
232
  }
183
233
 
184
- function scoreColor(v) {
185
- if (v >= 85) return '#3fb950';
186
- if (v >= 70) return '#d29922';
187
- if (v >= 50) return '#ffa657';
188
- return '#f85149';
234
+ function renderMemory(mem){
235
+ if(!mem)return;
236
+ const tiers=[
237
+ {key:'raw',name:'Raw',cls:'t0',color:'var(--green)',desc:'Full content'},
238
+ {key:'summary',name:'Summary',cls:'t1',color:'var(--blue)',desc:'Compressed summary'},
239
+ {key:'pattern',name:'Pattern',cls:'t2',color:'var(--purple)',desc:'Extracted patterns'},
240
+ {key:'principle',name:'Principle',cls:'t3',color:'var(--orange)',desc:'Core principles'},
241
+ ];
242
+ const max=Math.max(1,...tiers.map(t=>mem[t.key]||0));
243
+ let html='';
244
+ let total=0;
245
+ for(const t of tiers){
246
+ const v=mem[t.key]||0;total+=v;
247
+ const pct=max>0?Math.round(v/max*100):0;
248
+ html+='<div class="tier '+t.cls+'"><div class="th"><span class="tname">'+t.name+'</span><span class="tcount">'+v+'</span></div><div class="bw"><div class="bf" style="width:'+pct+'%;background:'+t.color+'"></div></div><div class="tmeta">'+t.desc+'</div></div>';
249
+ }
250
+ const turbo=mem.turboCompression||0;
251
+ const turboLine=turbo>0?'<div style="margin-top:6px;padding:6px;background:var(--bg2);border-radius:5px;border:1px solid var(--border);font-size:10px;color:var(--cyan)">\u26A1 TurboQuant compression: '+turbo+'x &mdash; '+total+' total entries</div>':'<div style="margin-top:6px;font-size:10px;color:var(--text2);text-align:center">'+total+' total memory entries</div>';
252
+ memoryPnl.innerHTML=html+turboLine;
253
+ $('sMemory').textContent=Math.round(total/10);
189
254
  }
190
255
 
191
- function renderHealth(score) {
192
- if (!score) return;
193
- const col = scoreColor(score.overall);
194
- const trendIcon = score.trend === 'improving' ? '↑' : score.trend === 'declining' ? '↓' : '→';
195
- badgeScore.textContent = score.overall + '/100';
196
- badgeScore.style.color = col;
197
- badgeScore.style.borderColor = col;
198
- badgeGrade.textContent = score.grade;
199
- const gradeClass = score.grade.replace('+','').charAt(0);
200
- badgeGrade.className = \`badge grade \${gradeClass}\`;
201
- badgeTrend.textContent = trendIcon;
202
- badgeTrend.className = \`trend \${score.trend}\`;
203
-
204
- const dimsHtml = score.dimensions.map(d => {
205
- const c = scoreColor(d.score);
206
- const det = d.details.map(x => \`<div style="font-size:10px;color:var(--text2);margin-top:2px">• \${escapeHtml(x)}</div>\`).join('');
207
- return \`<div class="dim">
208
- <div class="name">\${escapeHtml(d.name)}</div>
209
- <div class="bar-wrap"><div class="bar-fill" style="width:\${d.score}%;background:\${c}"></div></div>
210
- <div class="score-val" style="color:\${c}">\${d.score}%</div>
211
- \${det}
212
- </div>\`;
256
+ function renderTools(tools){
257
+ if(!tools||!tools.length){
258
+ toolsPnl.innerHTML='<div class="empty"><div class="ic">\u{1F916}</div>No AI tools detected</div>';
259
+ $('sTools').textContent='0';
260
+ return;
261
+ }
262
+ const icons={claude_code:'\u{1F9E0}',cursor:'\u2702\uFE0F',kilo_code:'\u2699\uFE0F',cline:'\u{1F4BB}',opencode:'\u{1F517}',codex:'\u{1F4DA}',roo:'\u{1F418}',aider:'\u{1F91D}'};
263
+ toolsPnl.innerHTML=tools.map(t=>{
264
+ const ic=icons[t.id]||'\u{1F916}';
265
+ const cls=t.connected?'active':t.detected?'inactive':'error';
266
+ const label=t.connected?'Connected':t.detected?'Detected':'Not found';
267
+ return '<div class="tool"><div class="icon">'+ic+'</div><div class="info"><div class="tname">'+esc(t.name)+'</div><div class="tstat">'+label+(t.version?' v'+t.version:'')+'</div></div><div class="tdot '+cls+'"></div></div>';
213
268
  }).join('');
214
-
215
- const topIssues = score.topIssues.length > 0
216
- ? \`<div style="margin-top:10px;padding:8px;background:var(--bg2);border-radius:6px;border:1px solid var(--border)">
217
- <div style="font-size:11px;color:var(--red);font-weight:700;margin-bottom:6px">🔥 Top Issues</div>
218
- \${score.topIssues.map(i => \`<div style="font-size:11px;color:var(--text2);margin-bottom:3px">• \${escapeHtml(i)}</div>\`).join('')}
219
- </div>\`
220
- : '';
221
-
222
- healthPanel.innerHTML = \`
223
- <div class="health-big">
224
- <div class="num" style="color:\${col}">\${score.overall}</div>
225
- <div class="label">Health Score / 100 — Grade \${score.grade} \${trendIcon}</div>
226
- </div>
227
- <div class="health-grid">\${dimsHtml}</div>
228
- \${topIssues}
229
- \`;
269
+ $('sTools').textContent=tools.length;
230
270
  }
231
271
 
232
- function priorityTag(p) {
233
- return \`<span class="tag \${p}">\${p.toUpperCase()}</span>\`;
272
+ function renderModules(mods){
273
+ if(!mods||!mods.length){
274
+ modPnl.innerHTML='<div class="empty"><div class="ic">\u2699</div>No module data</div>';
275
+ return;
276
+ }
277
+ modPnl.innerHTML=mods.map(m=>{
278
+ const cls=m.active?'active':m.error?'error':'idle';
279
+ const label=m.active?'Active':m.error?'Error':'Idle';
280
+ const extra=m.stats?' ('+m.stats+')':'';
281
+ return '<div class="mod"><div class="mdot '+cls+'"></div><span class="mname">'+esc(m.name)+'</span><span class="mstat">'+label+extra+'</span></div>';
282
+ }).join('');
283
+ const active=mods.filter(m=>m.active).length;
284
+ pModules.textContent=active+'/'+mods.length+' modules';
285
+ $('sModules').textContent=active;
234
286
  }
235
287
 
236
- function typeTag(t) {
237
- const colors = { warning: '#f85149', suggestion: '#58a6ff', review: '#bc8cff', insight: '#39d353' };
238
- return \`<span class="tag" style="color:\${colors[t]||'#8b949e'}">\${t}</span>\`;
239
- }
288
+ function ptag(p){return '<span class="tag '+p+'">'+p.toUpperCase()+'</span>'}
289
+ function ttag(t){const c={warning:'var(--red)',suggestion:'var(--blue)',review:'var(--purple)',insight:'var(--cyan)'};return '<span class="tag" style="color:'+(c[t]||'var(--text2)')+'">'+t+'</span>'}
240
290
 
241
- function renderInsight(insight) {
242
- const files = (insight.files || []).map(f => \`<code style="font-size:10px;background:var(--bg3);padding:1px 5px;border-radius:3px">\${escapeHtml(f)}</code>\`).join(' ');
243
- return \`<div class="insight \${insight.priority}">
244
- <div class="insight-title">\${escapeHtml(insight.title)}</div>
245
- <div class="insight-meta">
246
- \${priorityTag(insight.priority)}
247
- \${typeTag(insight.type)}
248
- \${files}
249
- </div>
250
- <div class="insight-content">\${escapeHtml(insight.content)}</div>
251
- </div>\`;
291
+ function renderIns(ins){
292
+ const files=(ins.files||[]).map(f=>'<code style="font-size:9px;background:var(--bg4);padding:1px 4px;border-radius:2px">'+esc(f)+'</code>').join(' ');
293
+ return '<div class="ins '+ins.priority+'"><div class="ins-t">'+esc(ins.title)+'</div><div class="ins-m">'+ptag(ins.priority)+' '+ttag(ins.type)+' '+files+'</div><div class="ins-c">'+esc(ins.content)+'</div></div>';
252
294
  }
253
295
 
254
- function addInsights(insights) {
255
- if (!insights || insights.length === 0) return;
256
- if (insightsPanel.querySelector('.empty')) insightsPanel.innerHTML = '';
257
- allInsights = [...insights, ...allInsights].slice(0, MAX_INSIGHTS);
258
- insightsPanel.innerHTML = allInsights.map(renderInsight).join('');
259
- insightCount += insights.length;
260
- insightCountEl.textContent = insightCount;
261
- log(\`\${insights.length} new insight(s) received\`, 'success');
296
+ function addInsights(ins){
297
+ if(!ins||!ins.length)return;
298
+ if(insPnl.querySelector('.empty'))insPnl.innerHTML='';
299
+ allIns=[...ins,...allIns].slice(0,MAX_INS);
300
+ insPnl.innerHTML=allIns.map(renderIns).join('');
301
+ insCnt+=ins.length;
302
+ $('iCnt').textContent=insCnt;
303
+ $('sInsights').textContent=insCnt;
262
304
  }
263
305
 
264
- function catTag(c) {
265
- return \`<span class="tag \${c}">\${c.toUpperCase()}</span>\`;
306
+ function renderFixes(fixes){
307
+ if(!fixes||!fixes.length)return;
308
+ if(fixPnl.querySelector('.empty'))fixPnl.innerHTML='';
309
+ fixPnl.innerHTML=fixes.map(f=>'<div class="fix"><div class="fix-t">'+esc(f.issue)+'</div><div class="fix-f">\u{1F4C4} '+esc(f.file)+' &nbsp;<span class="tag">'+f.confidence+'</span></div><div class="fl bef">Before</div><pre class="cb bef">'+esc(f.before)+'</pre><div class="fl aft" style="margin-top:6px">After</div><pre class="cb aft">'+esc(f.after)+'</pre><div class="fix-x">\u{1F4A1} '+esc(f.explanation)+'</div></div>').join('');
310
+ fixCnt=fixes.length;
311
+ $('fCnt').textContent=fixCnt;
312
+ $('sFixes').textContent=fixCnt;
266
313
  }
267
314
 
268
- function renderFix(fix) {
269
- return \`<div class="fix">
270
- <div class="fix-title">\${escapeHtml(fix.issue)}</div>
271
- <div class="fix-file">📄 \${escapeHtml(fix.file)} &nbsp; \${catTag(fix.category)} &nbsp; <span class="tag">\${fix.confidence} confidence</span></div>
272
- <div class="fix-label before">Before</div>
273
- <pre class="code-block code-before">\${escapeHtml(fix.before)}</pre>
274
- <div class="fix-label after" style="margin-top:8px">After</div>
275
- <pre class="code-block code-after">\${escapeHtml(fix.after)}</pre>
276
- <div class="fix-explanation">💡 \${escapeHtml(fix.explanation)}</div>
277
- </div>\`;
315
+ function triggerAnalysis(){
316
+ fetch('/api/trigger-analysis',{method:'POST'}).then(r=>r.json()).then(d=>{
317
+ // acknowledged
318
+ }).catch(()=>{});
278
319
  }
279
320
 
280
- function renderFixes(fixes) {
281
- if (!fixes || fixes.length === 0) return;
282
- if (fixesPanel.querySelector('.empty')) fixesPanel.innerHTML = '';
283
- fixesPanel.innerHTML = fixes.map(renderFix).join('');
284
- fixCount = fixes.length;
285
- fixCountEl.textContent = fixCount;
286
- log(\`\${fixes.length} smart fix suggestion(s) available\`, 'info');
321
+ function refreshAll(){
322
+ fetch('/api/status').then(r=>r.json()).then(d=>{
323
+ if(d.agents)pAgents.textContent=d.agents.join(', ');
324
+ if(d.projectDir)pProject.textContent=d.projectDir;
325
+ if(d.turboMemoryStats){
326
+ const ts=d.turboMemoryStats;
327
+ $('sTurbo').textContent=(ts.compressionRatio||0).toFixed(1)+'x';
328
+ }
329
+ if(d.evolutionGeneration!==undefined)$('sEvol').textContent=d.evolutionGeneration;
330
+ if(d.swarmConvergence!==undefined)$('sSwarm').textContent=Math.round(d.swarmConvergence*100)+'%';
331
+ }).catch(()=>{});
332
+ fetch('/api/modules').then(r=>r.json()).then(renderModules).catch(()=>{});
333
+ fetch('/api/memory').then(r=>r.json()).then(renderMemory).catch(()=>{});
334
+ fetch('/api/tools').then(r=>r.json()).then(renderTools).catch(()=>{});
287
335
  }
288
336
 
289
- function connect() {
290
- const proto = location.protocol === 'https:' ? 'wss:' : 'ws:';
291
- const ws = new WebSocket(\`\${proto}//\${location.host}\`);
292
-
293
- ws.onopen = () => {
294
- statusDot.classList.add('connected');
295
- log('Connected to Shadow Brain', 'success');
296
- };
297
-
298
- ws.onclose = () => {
299
- statusDot.classList.remove('connected');
300
- log('Disconnected — reconnecting in 3s…', 'warning');
301
- setTimeout(connect, 3000);
302
- };
303
-
304
- ws.onerror = () => {
305
- log('WebSocket error', 'error');
306
- };
307
-
308
- ws.onmessage = (ev) => {
309
- let msg;
310
- try { msg = JSON.parse(ev.data); } catch { return; }
311
-
312
- switch (msg.type) {
337
+ function connect(){
338
+ const proto=location.protocol==='https:'?'wss:':'ws:';
339
+ const ws=new WebSocket(proto+'//'+location.host);
340
+ ws.onopen=()=>{dot.classList.add('on');refreshAll()};
341
+ ws.onclose=()=>{dot.classList.remove('on');setTimeout(connect,3000)};
342
+ ws.onerror=()=>{};
343
+ ws.onmessage=ev=>{
344
+ let m;try{m=JSON.parse(ev.data)}catch{return}
345
+ switch(m.type){
313
346
  case 'init':
314
- badgeProject.textContent = msg.projectDir || '';
315
- if (msg.agents && msg.agents.length > 0) {
316
- badgeAgents.textContent = msg.agents.join(', ');
317
- }
318
- log(\`Project: \${msg.projectDir}\`, 'info');
347
+ if(m.projectDir)pProject.textContent=m.projectDir;
348
+ if(m.agents&&m.agents.length)pAgents.textContent=m.agents.join(', ');
319
349
  break;
320
350
  case 'agents-detected':
321
- if (msg.agents && msg.agents.length > 0) {
322
- badgeAgents.textContent = msg.agents.join(', ');
323
- log(\`Agents: \${msg.agents.join(', ')}\`, 'info');
324
- }
325
- break;
326
- case 'analysis-start':
327
- log(\`Analysis started — \${msg.changeCount} change(s)\`, 'info');
328
- break;
329
- case 'insights':
330
- addInsights(msg.insights);
331
- break;
332
- case 'health-score':
333
- renderHealth(msg.score);
334
- break;
335
- case 'fixes':
336
- renderFixes(msg.fixes);
351
+ if(m.agents&&m.agents.length)pAgents.textContent=m.agents.join(', ');
337
352
  break;
338
- case 'injection':
339
- if (msg.success) log(\`Injected → \${msg.adapter}: \${msg.insightTitle}\`, 'success');
340
- break;
341
- case 'info':
342
- log(msg.message, 'info');
343
- break;
344
- case 'error':
345
- log(\`Error: \${msg.error}\`, 'error');
353
+ case 'analysis-start':break;
354
+ case 'insights':addInsights(m.insights);break;
355
+ case 'health-score':renderHealth(m.score);break;
356
+ case 'fixes':renderFixes(m.fixes);break;
357
+ case 'injection':break;
358
+ case 'modules':renderModules(m.modules);break;
359
+ case 'memory':renderMemory(m.memory);break;
360
+ case 'tools':renderTools(m.tools);break;
361
+ case 'stats':
362
+ if(m.turboCompression)$('sTurbo').textContent=m.turboCompression.toFixed(1)+'x';
363
+ if(m.evolutionGen!==undefined)$('sEvol').textContent=m.evolutionGen;
364
+ if(m.swarmConv!==undefined)$('sSwarm').textContent=Math.round(m.swarmConv*100)+'%';
346
365
  break;
366
+ case 'info':break;
367
+ case 'error':break;
347
368
  }
348
369
  };
349
370
  }
350
-
351
371
  connect();
352
372
  </script>
353
373
  </body>
@@ -362,42 +382,143 @@ export class DashboardServer {
362
382
  host: options.host ?? 'localhost',
363
383
  openBrowser: options.openBrowser ?? false,
364
384
  };
365
- // HTTP server — serves the embedded HTML client
366
385
  this.server = http.createServer((req, res) => {
367
- if (req.url === '/' || req.url === '/index.html') {
368
- res.writeHead(200, {
369
- 'Content-Type': 'text/html; charset=utf-8',
370
- 'Cache-Control': 'no-cache',
371
- });
372
- res.end(HTML_CLIENT);
373
- }
374
- else if (req.url === '/api/status') {
375
- res.writeHead(200, { 'Content-Type': 'application/json' });
376
- res.end(JSON.stringify(this.orchestrator.getStatus(), null, 2));
377
- }
378
- else if (req.url === '/api/health') {
379
- const h = this.orchestrator.getLastHealthScore();
380
- res.writeHead(200, { 'Content-Type': 'application/json' });
381
- res.end(JSON.stringify(h, null, 2));
382
- }
383
- else if (req.url === '/api/fixes') {
384
- res.writeHead(200, { 'Content-Type': 'application/json' });
385
- res.end(JSON.stringify(this.orchestrator.getLastFixes(), null, 2));
386
- }
387
- else {
388
- res.writeHead(404);
389
- res.end('Not found');
390
- }
386
+ this.handleRequest(req, res);
391
387
  });
392
- // WebSocket server on the same HTTP server
393
388
  this.wss = new WebSocketServer({ server: this.server });
394
389
  this.setupWebSocket();
395
390
  this.subscribeToOrchestrator();
396
391
  }
392
+ handleRequest(req, res) {
393
+ const url = req.url || '/';
394
+ const send = (data, status = 200) => {
395
+ res.writeHead(status, { 'Content-Type': 'application/json' });
396
+ res.end(JSON.stringify(data, null, 2));
397
+ };
398
+ if (url === '/' || url === '/index.html') {
399
+ res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8', 'Cache-Control': 'no-cache' });
400
+ res.end(HTML_CLIENT);
401
+ }
402
+ else if (url === '/api/status') {
403
+ send(this.orchestrator.getStatus());
404
+ }
405
+ else if (url === '/api/health') {
406
+ send(this.orchestrator.getLastHealthScore());
407
+ }
408
+ else if (url === '/api/fixes') {
409
+ send(this.orchestrator.getLastFixes());
410
+ }
411
+ else if (url === '/api/modules') {
412
+ send(this.getModulesData());
413
+ }
414
+ else if (url === '/api/memory') {
415
+ send(this.getMemoryData());
416
+ }
417
+ else if (url === '/api/tools') {
418
+ send(this.getToolsData());
419
+ }
420
+ else if (url === '/api/trigger-analysis' && req.method === 'POST') {
421
+ // Trigger a fresh analysis
422
+ this.orchestrator.reviewOnce().then(() => {
423
+ send({ triggered: true });
424
+ }).catch((err) => {
425
+ send({ triggered: false, error: err.message }, 500);
426
+ });
427
+ }
428
+ else {
429
+ res.writeHead(404);
430
+ res.end('Not found');
431
+ }
432
+ }
433
+ getModulesData() {
434
+ const status = this.orchestrator.getStatus();
435
+ const modules = [];
436
+ const moduleList = [
437
+ 'hierarchicalMemory', 'patternMemory', 'learningEngine', 'neuralMesh',
438
+ 'consensusState', 'recallState', 'collectiveRules', 'turboMemory',
439
+ 'knowledgeGraph', 'swarmState', 'evolutionState', 'adversarialDefense',
440
+ 'crossAgentProtocol',
441
+ ];
442
+ const moduleNames = {
443
+ hierarchicalMemory: 'Hierarchical Memory (4-tier)',
444
+ patternMemory: 'Pattern Memory',
445
+ learningEngine: 'Learning Engine',
446
+ neuralMesh: 'Neural Mesh (SSSP)',
447
+ consensusState: 'Consensus Protocol',
448
+ recallState: 'Context Recall',
449
+ collectiveRules: 'Collective Learning',
450
+ turboMemory: 'TurboQuant Memory',
451
+ knowledgeGraph: 'Knowledge Graph (PageRank)',
452
+ swarmState: 'Swarm Intelligence (ACO)',
453
+ evolutionState: 'Self-Evolution (Genetic)',
454
+ adversarialDefense: 'Adversarial Defense',
455
+ crossAgentProtocol: 'Cross-Agent (CAIP)',
456
+ };
457
+ for (const key of moduleList) {
458
+ const active = status[key] !== null && status[key] !== undefined;
459
+ let stats;
460
+ const val = status[key];
461
+ if (val && typeof val === 'object') {
462
+ if (typeof val.count === 'number')
463
+ stats = String(val.count) + ' entries';
464
+ else if (typeof val.size === 'number')
465
+ stats = String(val.size) + ' items';
466
+ else if (typeof val.entityCount === 'number')
467
+ stats = String(val.entityCount) + ' entities';
468
+ }
469
+ modules.push({
470
+ name: moduleNames[key] || key,
471
+ active,
472
+ error: false,
473
+ stats,
474
+ });
475
+ }
476
+ return modules;
477
+ }
478
+ getMemoryData() {
479
+ try {
480
+ const status = this.orchestrator.getStatus();
481
+ const hm = status.hierarchicalMemory;
482
+ return {
483
+ raw: hm?.rawCount ?? 0,
484
+ summary: hm?.summaryCount ?? 0,
485
+ pattern: hm?.patternCount ?? 0,
486
+ principle: hm?.principleCount ?? 0,
487
+ turboCompression: status.turboMemoryStats?.compressionRatio ?? 0,
488
+ };
489
+ }
490
+ catch {
491
+ return { raw: 0, summary: 0, pattern: 0, principle: 0, turboCompression: 0 };
492
+ }
493
+ }
494
+ getToolsData() {
495
+ try {
496
+ const status = this.orchestrator.getStatus();
497
+ const agents = status.agents || [];
498
+ const knownTools = [
499
+ { id: 'claude_code', name: 'Claude Code', configDir: '.claude' },
500
+ { id: 'cursor', name: 'Cursor', configDir: '.cursor' },
501
+ { id: 'kilo_code', name: 'Kilo Code', configDir: '.kilocode' },
502
+ { id: 'cline', name: 'Cline', configDir: '.cline' },
503
+ { id: 'opencode', name: 'OpenCode', configDir: '.opencode' },
504
+ { id: 'codex', name: 'Codex', configDir: '.codex' },
505
+ { id: 'roo', name: 'Roo Code', configDir: '.roo' },
506
+ { id: 'aider', name: 'Aider', configDir: '.aider' },
507
+ ];
508
+ return knownTools.map(t => ({
509
+ id: t.id,
510
+ name: t.name,
511
+ connected: agents.some(a => a.toLowerCase().includes(t.name.toLowerCase())),
512
+ detected: agents.some(a => a.toLowerCase().includes(t.name.toLowerCase())),
513
+ }));
514
+ }
515
+ catch {
516
+ return [];
517
+ }
518
+ }
397
519
  setupWebSocket() {
398
520
  this.wss.on('connection', (ws) => {
399
521
  this.clients.add(ws);
400
- // Send init state immediately
401
522
  const status = this.orchestrator.getStatus();
402
523
  this.send(ws, {
403
524
  type: 'init',
@@ -407,22 +528,25 @@ export class DashboardServer {
407
528
  provider: status.provider,
408
529
  model: status.model,
409
530
  });
410
- // Send cached health score if available
411
531
  const health = this.orchestrator.getLastHealthScore();
412
- if (health) {
532
+ if (health)
413
533
  this.send(ws, { type: 'health-score', score: health });
414
- }
415
- // Send cached fixes if available
416
534
  const fixes = this.orchestrator.getLastFixes();
417
- if (fixes.length > 0) {
535
+ if (fixes.length > 0)
418
536
  this.send(ws, { type: 'fixes', fixes });
419
- }
420
- ws.on('close', () => {
421
- this.clients.delete(ws);
422
- });
423
- ws.on('error', () => {
424
- this.clients.delete(ws);
537
+ // Send module/memory/tools data
538
+ this.send(ws, { type: 'modules', modules: this.getModulesData() });
539
+ this.send(ws, { type: 'memory', memory: this.getMemoryData() });
540
+ this.send(ws, { type: 'tools', tools: this.getToolsData() });
541
+ // Send stats
542
+ this.send(ws, {
543
+ type: 'stats',
544
+ turboCompression: status.turboMemoryStats?.compressionRatio ?? 0,
545
+ evolutionGen: status.evolutionGeneration ?? 0,
546
+ swarmConv: status.swarmConvergence ?? 0,
425
547
  });
548
+ ws.on('close', () => { this.clients.delete(ws); });
549
+ ws.on('error', () => { this.clients.delete(ws); });
426
550
  });
427
551
  }
428
552
  subscribeToOrchestrator() {
@@ -431,12 +555,16 @@ export class DashboardServer {
431
555
  type: 'agents-detected',
432
556
  agents: adapters.map((a) => `${a.displayName} (${a.name})`),
433
557
  });
558
+ this.broadcast({ type: 'tools', tools: this.getToolsData() });
434
559
  });
435
560
  this.orchestrator.on('analysis-start', ({ changeCount }) => {
436
561
  this.broadcast({ type: 'analysis-start', changeCount });
437
562
  });
438
563
  this.orchestrator.on('insights', ({ insights }) => {
439
564
  this.broadcast({ type: 'insights', insights });
565
+ // Update modules after insights
566
+ this.broadcast({ type: 'modules', modules: this.getModulesData() });
567
+ this.broadcast({ type: 'memory', memory: this.getMemoryData() });
440
568
  });
441
569
  this.orchestrator.on('health-score', ({ score }) => {
442
570
  this.broadcast({ type: 'health-score', score });
@@ -445,12 +573,7 @@ export class DashboardServer {
445
573
  this.broadcast({ type: 'fixes', fixes });
446
574
  });
447
575
  this.orchestrator.on('injection', ({ adapter, insight, success }) => {
448
- this.broadcast({
449
- type: 'injection',
450
- adapter,
451
- insightTitle: insight?.title || '',
452
- success,
453
- });
576
+ this.broadcast({ type: 'injection', adapter, insightTitle: insight?.title || '', success });
454
577
  });
455
578
  this.orchestrator.on('info', (message) => {
456
579
  this.broadcast({ type: 'info', message });