total-recall-brain 3.41.2 → 3.41.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 (69) hide show
  1. package/bin/total-recall.mjs +9 -3
  2. package/frontend/dist/assets/{index-BG9NkVUy.css → index-BIQmqDKP.css} +1 -1
  3. package/frontend/dist/assets/{index-BtuiZqwP.js → index-DFenQQEQ.js} +74 -79
  4. package/frontend/dist/index.html +2 -2
  5. package/frontend/dist/sw.js +8 -5
  6. package/frontend/src/api/auth.ts +0 -3
  7. package/frontend/src/components/plugins/PluginPanel.tsx +7 -3
  8. package/frontend/src/components/plugins/card/CardEngine.tsx +1 -1
  9. package/frontend/src/pages/LoginPage.tsx +1 -41
  10. package/frontend/src/pages/PluginsPage.tsx +14 -7
  11. package/package.json +2 -2
  12. package/scaffold/.agent/skills/plugin/scripts/record.mjs +6 -3
  13. package/scaffold/.agent/skills/total-recall/plugins/auth/cli.mjs +197 -31
  14. package/scaffold/.agent/skills/total-recall/plugins/auth/package-lock.json +4 -4
  15. package/scaffold/.agent/skills/total-recall/plugins/auth/package.json +1 -1
  16. package/scaffold/.agent/skills/total-recall/plugins/auth/plugin.json +8 -1
  17. package/scaffold/.agent/skills/total-recall/plugins/auth/ui/DESIGN.md +56 -8
  18. package/scaffold/.agent/skills/total-recall/plugins/auth/ui/card.js +1068 -204
  19. package/scaffold/.agent/skills/total-recall/plugins/auth/ui/panel.js +4 -2
  20. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/ui/DESIGN.md +32 -0
  21. package/scaffold/.agent/skills/total-recall/plugins/cli-agents/ui/card.js +436 -274
  22. package/scaffold/.agent/skills/total-recall/plugins/code-quality/package-lock.json +4 -4
  23. package/scaffold/.agent/skills/total-recall/plugins/code-quality/package.json +1 -1
  24. package/scaffold/.agent/skills/total-recall/plugins/code-quality/ui/DESIGN.md +16 -2
  25. package/scaffold/.agent/skills/total-recall/plugins/code-quality/ui/card.js +461 -242
  26. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/package-lock.json +4 -4
  27. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/package.json +1 -1
  28. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/plugin.json +1 -7
  29. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/DESIGN.md +17 -2
  30. package/scaffold/.agent/skills/total-recall/plugins/composable-cli/ui/card.js +426 -300
  31. package/scaffold/.agent/skills/total-recall/plugins/embeddings/package-lock.json +4 -4
  32. package/scaffold/.agent/skills/total-recall/plugins/embeddings/ui/DESIGN.md +32 -0
  33. package/scaffold/.agent/skills/total-recall/plugins/embeddings/ui/card.js +350 -283
  34. package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/package-lock.json +4 -4
  35. package/scaffold/.agent/skills/total-recall/plugins/git-sentinel/ui/card.js +31 -19
  36. package/scaffold/.agent/skills/total-recall/plugins/mesh/package-lock.json +4 -4
  37. package/scaffold/.agent/skills/total-recall/plugins/openwiki/package-lock.json +4 -4
  38. package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/DESIGN.md +16 -2
  39. package/scaffold/.agent/skills/total-recall/plugins/openwiki/ui/card.js +380 -298
  40. package/scaffold/.agent/skills/total-recall/plugins/security/cli.mjs +7 -1
  41. package/scaffold/.agent/skills/total-recall/plugins/security/package-lock.json +6 -6
  42. package/scaffold/.agent/skills/total-recall/plugins/security/package.json +1 -1
  43. package/scaffold/.agent/skills/total-recall/plugins/security/plugin.json +1 -1
  44. package/scaffold/.agent/skills/total-recall/plugins/security/src/scanner.mjs +17 -3
  45. package/scaffold/.agent/skills/total-recall/plugins/security/ui/DESIGN.md +38 -15
  46. package/scaffold/.agent/skills/total-recall/plugins/security/ui/card.js +60 -34
  47. package/src/cli/command.mjs +23 -18
  48. package/src/cli/daemon.mjs +23 -1
  49. package/src/cli/plugin/copies.mjs +8 -5
  50. package/src/cli/plugin/index.mjs +25 -2
  51. package/src/cli/rebuild.mjs +22 -0
  52. package/src/cli/reset-password.mjs +3 -2
  53. package/src/cli/skill.mjs +8 -2
  54. package/src/cli/status.mjs +25 -7
  55. package/src/core/automatic-brain-updates.mjs +9 -3
  56. package/src/core/brain-updates.mjs +63 -5
  57. package/src/core/global-brain-updates.mjs +21 -1
  58. package/src/core/mesh-brain-updates.mjs +5 -2
  59. package/src/core/meta-harness.mjs +16 -41
  60. package/src/core/plugin-runner.mjs +6 -1
  61. package/src/core/plugin-store.mjs +67 -1
  62. package/src/core/plugin-updates.mjs +1 -1
  63. package/src/core/portable-skills-maintenance.mjs +19 -0
  64. package/src/core/required-plugins.mjs +7 -0
  65. package/src/core/skills-registry.mjs +33 -3
  66. package/src/server/auth.mjs +41 -1
  67. package/src/server/memory-app.mjs +2 -1
  68. package/src/server/routes/auth.mjs +25 -1
  69. package/src/server/routes/plugins.mjs +17 -4
@@ -1,107 +1,101 @@
1
1
  /**
2
- * Command Fleet (cli-agents) — Multi-Agent Coordination & Node Inspection
3
- * Purpose-built domain interface: queries agent fleet status on mount, renders live
4
- * agent discovery matrix (Claude, DSH, Hermes, Antigravity, etc.), and exposes authentic
5
- * operational controls across mesh nodes.
2
+ * Command Fleet — Multi-Agent Coordination & Inspection Console
6
3
  *
7
- * Zero emojis, authentic commercial brand identity, touch-first ergonomics (>=44px).
4
+ * Bespoke operator interface for terminal coding agent fleet management.
5
+ * Displays real-time agent discovery matrix, mesh node targets, authentication
6
+ * postures, and official documentation states without template wrappers or terminal dumps.
8
7
  */
9
8
 
9
+ const DASH = '\u2014';
10
+
11
+ function escapeHtml(str) {
12
+ if (str === null || str === undefined) return '';
13
+ return String(str).replace(/[&<>"']/g, c => ({
14
+ '&': '&amp;',
15
+ '<': '&lt;',
16
+ '>': '&gt;',
17
+ '"': '&quot;',
18
+ "'": '&#39;'
19
+ }[c]));
20
+ }
21
+
10
22
  export class CommandFleetCard extends HTMLElement {
11
23
  constructor() {
12
24
  super();
13
- this.executing = false;
14
25
  this.agents = [];
15
- this.telemetry = {
16
- installedCount: '—',
17
- activeTarget: 'Local',
18
- docsStatus: 'Checking...'
19
- };
26
+ this.selectedAgent = null;
27
+ this.selectedNode = 'local';
28
+ this.nodes = [];
29
+ this.executing = false;
30
+ this.screens = [{ id: 'fleet', label: 'Command Fleet' }];
31
+ this.screen = 'fleet';
20
32
  }
21
33
 
22
34
  connectedCallback() {
23
- if (!this.shadowRoot) this.attachShadow({ mode: 'open' });
35
+ if (!this.shadowRoot) {
36
+ this.attachShadow({ mode: 'open' });
37
+ }
24
38
  this.render();
25
- this.refreshStatus();
39
+ this.refreshFleet();
26
40
  }
27
41
 
28
- async refreshStatus() {
42
+ async refreshFleet() {
43
+ if (!this.host) return;
29
44
  try {
30
- const res = await this.host?.run('status', ['--json']);
31
- let data = res;
32
- if (typeof res === 'string') {
33
- try { data = JSON.parse(res); } catch {}
34
- } else if (res && typeof res.output === 'string') {
35
- try { data = JSON.parse(res.output); } catch {}
45
+ const res = await this.host.run('catalog', ['--json']);
46
+ let parsed = [];
47
+ if (Array.isArray(res)) {
48
+ parsed = res;
49
+ } else if (res && typeof res === 'object' && Array.isArray(res.agents)) {
50
+ parsed = res.agents;
51
+ } else if (typeof res === 'string' && res.trim().startsWith('[')) {
52
+ parsed = JSON.parse(res);
36
53
  }
54
+ this.agents = parsed;
37
55
 
38
- if (data && typeof data === 'object') {
39
- this.agents = Array.isArray(data.agents) ? data.agents : [];
40
- const installed = this.agents.filter(a => a.installed).length;
41
- const total = this.agents.length;
42
-
43
- this.telemetry = {
44
- installedCount: `${installed}/${total} Active`,
45
- activeTarget: 'Host Mini',
46
- docsStatus: data.documentation?.ok ? 'Current' : 'Cached'
47
- };
48
- this.updateView();
56
+ if (this.agents.length > 0 && !this.selectedAgent) {
57
+ this.selectedAgent = this.agents[0].id || this.agents[0].name;
49
58
  }
59
+
60
+ // Also try to query mesh nodes
61
+ try {
62
+ const nodeRes = await this.host.run('nodes', ['--json']);
63
+ if (Array.isArray(nodeRes)) this.nodes = nodeRes;
64
+ else if (nodeRes && Array.isArray(nodeRes.nodes)) this.nodes = nodeRes.nodes;
65
+ } catch {
66
+ this.nodes = [];
67
+ }
68
+
69
+ this.render();
50
70
  } catch {
51
- this.telemetry = {
52
- installedCount: '3/9 Ready',
53
- activeTarget: 'Host',
54
- docsStatus: 'Ready'
55
- };
56
- this.updateView();
71
+ this.agents = [];
72
+ this.render();
57
73
  }
58
74
  }
59
75
 
60
- updateView() {
61
- const root = this.shadowRoot;
62
- if (!root) return;
76
+ async selectAgent(id) {
77
+ this.selectedAgent = id;
78
+ this.render();
79
+ }
63
80
 
64
- const countEl = root.querySelector('#stat-count');
65
- const targetEl = root.querySelector('#stat-target');
66
- const docsEl = root.querySelector('#stat-docs');
67
- if (countEl) countEl.textContent = this.telemetry.installedCount;
68
- if (targetEl) targetEl.textContent = this.telemetry.activeTarget;
69
- if (docsEl) docsEl.textContent = this.telemetry.docsStatus;
70
-
71
- const matrixEl = root.querySelector('#agent-matrix');
72
- if (matrixEl && this.agents.length > 0) {
73
- matrixEl.innerHTML = this.agents.map(agent => `
74
- <div class="agent-chip ${agent.installed ? 'installed' : 'uninstalled'}" title="${agent.name} ${agent.installed ? (agent.version || 'installed') : 'not installed'}">
75
- <span class="status-dot"></span>
76
- <span class="agent-name">${agent.name}</span>
77
- ${agent.installed && agent.version ? `<span class="agent-ver">${agent.version}</span>` : ''}
78
- </div>
79
- `).join('');
80
- }
81
+ async switchNode(nodeId) {
82
+ this.selectedNode = nodeId;
83
+ this.render();
84
+ await this.refreshFleet();
81
85
  }
82
86
 
83
- async execute(action, args = []) {
84
- const out = this.shadowRoot?.querySelector('#card-out');
85
- const actionLabel = action.toUpperCase();
86
- if (out) out.textContent = `[COMMAND FLEET] Running ${actionLabel}...`;
87
+ async refreshDocs() {
88
+ if (!this.host || this.executing) return;
87
89
  this.executing = true;
88
-
90
+ this.render();
89
91
  try {
90
- const res = await this.host?.run(action, args);
91
- let text = '';
92
- if (typeof res === 'object') {
93
- text = res.output || JSON.stringify(res, null, 2);
94
- } else {
95
- text = String(res || 'Operation completed.');
96
- }
97
- if (out) out.textContent = text.trim();
98
- if (['status', 'maintenance', 'docs-refresh'].includes(action)) {
99
- this.refreshStatus();
100
- }
101
- } catch (err) {
102
- if (out) out.textContent = `[FLEET ERROR] ${err.message}`;
92
+ await this.host.run('docs-ensure', ['--json']);
93
+ await this.refreshFleet();
94
+ } catch {
95
+ await this.refreshFleet();
103
96
  } finally {
104
97
  this.executing = false;
98
+ this.render();
105
99
  }
106
100
  }
107
101
 
@@ -109,302 +103,470 @@ export class CommandFleetCard extends HTMLElement {
109
103
  const root = this.shadowRoot;
110
104
  if (!root) return;
111
105
 
106
+ const installedAgents = this.agents.filter(a => a.installed !== false);
107
+ const active = this.agents.find(a => (a.id || a.name) === this.selectedAgent) || this.agents[0];
108
+
112
109
  root.innerHTML = `
113
110
  <style>
114
111
  :host {
112
+ --face-bg: #0c1427;
113
+ --face-surface: #1e293b;
114
+ --face-line: #1e293b;
115
+ --face-text: #f1f5f9;
116
+ --face-muted: #94a3b8;
117
+ --face-brand: #3b82f6;
118
+ --face-brand-hover: #60a5fa;
119
+ --face-accent: #38bdf8;
120
+ --face-ok: #10b981;
121
+ --face-warn: #f59e0b;
122
+ --face-danger: #ef4444;
123
+ --face-dark: #0c1427;
124
+
115
125
  display: block;
116
- font-family: 'Space Grotesk', -apple-system, sans-serif;
117
- background: #081d1a;
118
- color: #e6f4f1;
119
- border-radius: 12px;
120
- border: 1px solid #134e48;
121
- padding: 14px;
126
+ font-family: var(--font-sans, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif);
127
+ color: var(--text-primary, var(--face-text));
128
+ background: var(--bg-secondary, var(--face-bg));
129
+ border: 1px solid var(--border, var(--face-line));
130
+ border-radius: 8px;
131
+ padding: 16px;
122
132
  box-sizing: border-box;
123
- user-select: none;
133
+ overflow: hidden;
124
134
  }
125
135
 
126
- * { box-sizing: border-box; }
136
+ @media (prefers-reduced-motion: reduce) {
137
+ *, ::before, ::after {
138
+ animation-duration: 0.01ms !important;
139
+ animation-iteration-count: 1 !important;
140
+ transition-duration: 0.01ms !important;
141
+ }
142
+ }
127
143
 
128
- /* Telemetry Metric Cards */
129
- .telemetry-row {
130
- display: grid;
131
- grid-template-columns: repeat(3, 1fr);
132
- gap: 8px;
133
- margin-bottom: 12px;
144
+ /* Header Bar */
145
+ .header {
146
+ display: flex;
147
+ align-items: center;
148
+ justify-content: space-between;
149
+ flex-wrap: wrap;
150
+ gap: 12px;
151
+ padding-bottom: 14px;
152
+ border-bottom: 1px solid var(--border, var(--face-line));
153
+ margin-bottom: 16px;
134
154
  }
135
155
 
136
- .metric-card {
137
- background: rgba(19, 78, 72, 0.45);
138
- border: 1px solid rgba(20, 184, 166, 0.25);
139
- border-radius: 8px;
140
- padding: 8px 10px;
156
+ .brand-group {
141
157
  display: flex;
142
- flex-direction: column;
143
- gap: 2px;
158
+ align-items: center;
159
+ gap: 10px;
144
160
  }
145
161
 
146
- .metric-label {
147
- font-family: 'JetBrains Mono', monospace;
148
- font-size: 9px;
149
- font-weight: 700;
150
- color: #5eead4;
162
+ .wordmark-badge {
163
+ font-family: var(--font-mono, 'JetBrains Mono', monospace);
164
+ font-size: 13px;
165
+ font-weight: 800;
151
166
  letter-spacing: 0.05em;
152
- text-transform: uppercase;
167
+ color: var(--card-accent, var(--face-accent));
168
+ background: rgba(59, 130, 246, 0.12);
169
+ padding: 6px 10px;
170
+ border-radius: 6px;
171
+ border: 1px solid rgba(59, 130, 246, 0.3);
153
172
  }
154
173
 
155
- .metric-value {
156
- font-size: 13px;
174
+ .title-meta {
175
+ display: flex;
176
+ flex-direction: column;
177
+ }
178
+
179
+ .plugin-title {
180
+ font-size: 14px;
157
181
  font-weight: 700;
158
- color: #ffffff;
159
- font-family: 'JetBrains Mono', monospace;
160
- overflow: hidden;
161
- text-overflow: ellipsis;
162
- white-space: nowrap;
182
+ color: var(--text-primary, var(--face-text));
183
+ margin: 0;
163
184
  }
164
185
 
165
- /* Discovered Agents Matrix */
166
- .matrix-container {
167
- background: rgba(0, 0, 0, 0.35);
168
- border: 1px solid rgba(20, 184, 166, 0.2);
169
- border-radius: 8px;
170
- padding: 8px 10px;
171
- margin-bottom: 10px;
186
+ .plugin-subtitle {
187
+ font-size: 11px;
188
+ color: var(--text-secondary, var(--face-muted));
189
+ margin: 0;
172
190
  }
173
191
 
174
- .matrix-title {
175
- font-family: 'JetBrains Mono', monospace;
176
- font-size: 9px;
177
- color: #5eead4;
192
+ .status-chip {
193
+ display: flex;
194
+ align-items: center;
195
+ gap: 6px;
196
+ font-family: var(--font-mono, 'JetBrains Mono', monospace);
197
+ font-size: 11px;
198
+ font-weight: 700;
199
+ color: var(--face-ok);
200
+ background: rgba(16, 185, 129, 0.12);
201
+ border: 1px solid rgba(16, 185, 129, 0.3);
202
+ padding: 5px 12px;
203
+ border-radius: 6px;
178
204
  text-transform: uppercase;
179
- letter-spacing: 0.05em;
180
- margin-bottom: 6px;
181
205
  }
182
206
 
183
- .agent-chips {
207
+ /* Node Selector Bar */
208
+ .node-bar {
184
209
  display: flex;
210
+ align-items: center;
211
+ justify-content: space-between;
185
212
  flex-wrap: wrap;
186
- gap: 6px;
213
+ gap: 10px;
214
+ margin-bottom: 16px;
215
+ background: rgba(30, 41, 59, 0.4);
216
+ border: 1px solid var(--border, var(--face-line));
217
+ border-radius: 6px;
218
+ padding: 8px 12px;
187
219
  }
188
220
 
189
- .agent-chip {
221
+ .node-toggles {
190
222
  display: flex;
191
223
  align-items: center;
192
224
  gap: 6px;
193
- padding: 3px 8px;
194
- border-radius: 6px;
225
+ }
226
+
227
+ .btn-node {
228
+ min-height: 44px;
229
+ min-width: 44px;
230
+ padding: 6px 14px;
231
+ font-family: var(--font-mono, 'JetBrains Mono', monospace);
195
232
  font-size: 11px;
196
- font-family: 'JetBrains Mono', monospace;
197
- border: 1px solid var(--border, #134e48);
198
- background: rgba(255, 255, 255, 0.03);
199
- transition: all 0.15s ease;
233
+ font-weight: 600;
234
+ color: var(--text-secondary, var(--face-muted));
235
+ background: transparent;
236
+ border: 1px solid var(--border, var(--face-line));
237
+ border-radius: 5px;
238
+ cursor: pointer;
200
239
  }
201
240
 
202
- .agent-chip.installed {
203
- background: rgba(20, 184, 166, 0.15);
204
- border-color: rgba(20, 184, 166, 0.4);
205
- color: #ccfbf1;
241
+ .btn-node[data-active="true"] {
242
+ color: var(--text-primary, var(--face-text));
243
+ background: rgba(59, 130, 246, 0.2);
244
+ border-color: var(--card-accent, var(--face-brand));
206
245
  }
207
246
 
208
- .agent-chip.uninstalled {
209
- opacity: 0.55;
210
- color: #99f6e4;
247
+ .btn-docs-sync {
248
+ min-height: 44px;
249
+ padding: 8px 14px;
250
+ font-size: 11px;
251
+ font-weight: 700;
252
+ color: var(--face-dark);
253
+ background: var(--face-accent);
254
+ border: none;
255
+ border-radius: 5px;
256
+ cursor: pointer;
257
+ transition: background 0.15s ease;
211
258
  }
212
259
 
213
- .status-dot {
214
- width: 6px;
215
- height: 6px;
216
- border-radius: 50%;
217
- background: #64748b;
260
+ .btn-docs-sync:hover {
261
+ background: #7dd3fc;
218
262
  }
219
263
 
220
- .agent-chip.installed .status-dot {
221
- background: #14b8a6;
222
- box-shadow: 0 0 6px rgba(20, 184, 166, 0.8);
264
+ .btn-docs-sync:disabled {
265
+ opacity: 0.6;
266
+ cursor: not-allowed;
223
267
  }
224
268
 
225
- .agent-name {
226
- font-weight: 600;
269
+ /* Split Workspace */
270
+ .workspace-grid {
271
+ display: grid;
272
+ grid-template-columns: minmax(240px, 1.2fr) minmax(280px, 1.8fr);
273
+ gap: 14px;
274
+ margin-bottom: 16px;
227
275
  }
228
276
 
229
- .agent-ver {
230
- font-size: 9px;
231
- opacity: 0.75;
232
- background: rgba(0, 0, 0, 0.25);
233
- padding: 1px 4px;
234
- border-radius: 4px;
277
+ @media (max-width: 720px) {
278
+ .workspace-grid {
279
+ grid-template-columns: 1fr;
280
+ }
235
281
  }
236
282
 
237
- /* Action Buttons Grid (>=44px touch targets) */
238
- .actions-grid {
239
- display: grid;
240
- grid-template-columns: repeat(3, 1fr);
241
- gap: 6px;
242
- margin-bottom: 10px;
283
+ /* Agent Roster Matrix */
284
+ .roster-card {
285
+ border: 1px solid var(--border, var(--face-line));
286
+ border-radius: 6px;
287
+ background: rgba(18, 24, 38, 0.6);
288
+ padding: 10px;
289
+ display: flex;
290
+ flex-direction: column;
291
+ gap: 8px;
292
+ max-height: 360px;
293
+ overflow-y: auto;
243
294
  }
244
295
 
245
- .btn-action {
296
+ .agent-item {
246
297
  min-height: 44px;
247
- min-width: 44px;
248
- background: rgba(255, 255, 255, 0.04);
249
- border: 1px solid rgba(20, 184, 166, 0.25);
250
- border-radius: 8px;
251
- color: #e6f4f1;
252
- font-family: 'Space Grotesk', sans-serif;
253
- font-size: 11px;
254
- font-weight: 700;
255
- letter-spacing: 0.02em;
256
- cursor: pointer;
257
- touch-action: manipulation;
298
+ padding: 8px 12px;
258
299
  display: flex;
259
300
  align-items: center;
260
- justify-content: center;
261
- text-align: center;
262
- padding: 6px;
263
- transition: all 0.15s ease;
301
+ justify-content: space-between;
302
+ border: 1px solid transparent;
303
+ border-radius: 5px;
304
+ background: rgba(30, 41, 59, 0.3);
305
+ color: var(--text-primary, var(--face-text));
306
+ cursor: pointer;
307
+ text-align: left;
308
+ width: 100%;
309
+ box-sizing: border-box;
310
+ transition: background 0.15s ease;
264
311
  }
265
312
 
266
- .btn-action:hover {
267
- background: #0f766e;
268
- border-color: #14b8a6;
269
- color: #ffffff;
313
+ .agent-item:hover {
314
+ background: rgba(59, 130, 246, 0.12);
270
315
  }
271
316
 
272
- .btn-action:active {
273
- transform: scale(0.98);
317
+ .agent-item[data-selected="true"] {
318
+ background: rgba(59, 130, 246, 0.22);
319
+ border-color: var(--face-brand);
274
320
  }
275
321
 
276
- .btn-action.btn-primary {
277
- background: rgba(20, 184, 166, 0.2);
278
- border-color: #14b8a6;
279
- color: #2dd4bf;
322
+ .agent-meta-left {
323
+ display: flex;
324
+ flex-direction: column;
325
+ gap: 2px;
280
326
  }
281
327
 
282
- .btn-action.btn-primary:hover {
283
- background: #14b8a6;
284
- color: #042f2e;
328
+ .agent-name {
329
+ font-family: var(--font-mono, 'JetBrains Mono', monospace);
330
+ font-size: 12px;
331
+ font-weight: 700;
285
332
  }
286
333
 
287
- /* Terminal Console */
288
- .terminal-container {
289
- background: #041412;
290
- border: 1px solid rgba(20, 184, 166, 0.2);
291
- border-radius: 8px;
292
- padding: 8px 10px;
334
+ .agent-version {
335
+ font-size: 10px;
336
+ color: var(--text-secondary, var(--face-muted));
293
337
  }
294
338
 
295
- .terminal-header {
339
+ .agent-badges {
296
340
  display: flex;
297
- justify-content: space-between;
298
341
  align-items: center;
299
- margin-bottom: 4px;
300
- padding-bottom: 4px;
301
- border-bottom: 1px solid rgba(20, 184, 166, 0.1);
342
+ gap: 6px;
302
343
  }
303
344
 
304
- .terminal-label {
305
- font-family: 'JetBrains Mono', monospace;
345
+ .badge-status {
346
+ font-family: var(--font-mono, 'JetBrains Mono', monospace);
306
347
  font-size: 9px;
307
- color: #5eead4;
348
+ font-weight: 700;
349
+ padding: 2px 6px;
350
+ border-radius: 4px;
351
+ text-transform: uppercase;
352
+ }
353
+
354
+ .badge-online {
355
+ color: var(--face-ok);
356
+ background: rgba(16, 185, 129, 0.15);
357
+ }
358
+
359
+ .badge-offline {
360
+ color: var(--text-secondary, var(--face-muted));
361
+ background: rgba(148, 163, 184, 0.1);
362
+ }
363
+
364
+ .badge-auth-ok {
365
+ color: var(--face-accent);
366
+ background: rgba(56, 189, 248, 0.15);
367
+ }
368
+
369
+ .badge-auth-needed {
370
+ color: var(--face-warn);
371
+ background: rgba(245, 158, 11, 0.15);
372
+ }
373
+
374
+ /* Inspector Panel */
375
+ .inspector-panel {
376
+ border: 1px solid var(--border, var(--face-line));
377
+ border-radius: 6px;
378
+ background: rgba(18, 24, 38, 0.6);
379
+ padding: 14px;
380
+ display: flex;
381
+ flex-direction: column;
382
+ gap: 12px;
383
+ }
384
+
385
+ .inspector-title {
386
+ font-family: var(--font-mono, 'JetBrains Mono', monospace);
387
+ font-size: 14px;
388
+ font-weight: 700;
389
+ color: var(--text-primary, var(--face-text));
390
+ margin: 0;
391
+ padding-bottom: 8px;
392
+ border-bottom: 1px solid var(--border, var(--face-line));
393
+ }
394
+
395
+ .property-grid {
396
+ display: grid;
397
+ grid-template-columns: repeat(2, 1fr);
398
+ gap: 10px;
399
+ }
400
+
401
+ .property-box {
402
+ background: rgba(30, 41, 59, 0.4);
403
+ border: 1px solid var(--border, var(--face-line));
404
+ border-radius: 5px;
405
+ padding: 8px 10px;
406
+ display: flex;
407
+ flex-direction: column;
408
+ gap: 3px;
409
+ }
410
+
411
+ .property-label {
412
+ font-size: 10px;
413
+ color: var(--text-secondary, var(--face-muted));
308
414
  text-transform: uppercase;
309
415
  letter-spacing: 0.05em;
310
416
  }
311
417
 
312
- .terminal-copy {
313
- background: none;
314
- border: none;
315
- color: #2dd4bf;
316
- font-family: 'JetBrains Mono', monospace;
418
+ .property-val {
419
+ font-family: var(--font-mono, 'JetBrains Mono', monospace);
420
+ font-size: 12px;
421
+ font-weight: 700;
422
+ color: var(--text-primary, var(--face-text));
423
+ }
424
+
425
+ .permission-pills {
426
+ display: flex;
427
+ flex-wrap: wrap;
428
+ gap: 6px;
429
+ margin-top: 6px;
430
+ }
431
+
432
+ .pill-permission {
433
+ font-family: var(--font-mono, 'JetBrains Mono', monospace);
317
434
  font-size: 10px;
318
- cursor: pointer;
319
- padding: 2px 4px;
435
+ padding: 4px 8px;
320
436
  border-radius: 4px;
437
+ background: rgba(15, 23, 42, 0.8);
438
+ border: 1px solid var(--border, var(--face-line));
439
+ color: var(--text-secondary, var(--face-muted));
321
440
  }
322
441
 
323
- .terminal-copy:hover {
324
- background: rgba(20, 184, 166, 0.2);
442
+ .pill-active {
443
+ color: var(--face-ok);
444
+ border-color: rgba(16, 185, 129, 0.4);
445
+ background: rgba(16, 185, 129, 0.1);
325
446
  }
326
447
 
327
- .terminal-readout {
328
- font-family: 'JetBrains Mono', monospace;
329
- font-size: 11px;
330
- line-height: 1.4;
331
- color: #2dd4bf;
332
- min-height: 48px;
333
- max-height: 120px;
334
- overflow-y: auto;
335
- white-space: pre-wrap;
336
- word-break: break-all;
448
+ .empty-agents-box {
449
+ padding: 36px 16px;
450
+ text-align: center;
451
+ font-size: 12px;
452
+ color: var(--text-secondary, var(--face-muted));
453
+ }
454
+
455
+ /* Footer Telemetry */
456
+ .footer-bar {
457
+ display: flex;
458
+ justify-content: space-between;
459
+ align-items: center;
460
+ padding-top: 12px;
461
+ margin-top: 14px;
462
+ border-top: 1px solid var(--border, var(--face-line));
463
+ font-size: 10px;
464
+ color: var(--text-secondary, var(--face-muted));
337
465
  }
338
466
  </style>
339
467
 
340
- <!-- Live Telemetry -->
341
- <div class="telemetry-row">
342
- <div class="metric-card">
343
- <span class="metric-label">Harness Fleet</span>
344
- <span class="metric-value" id="stat-count">${this.telemetry.installedCount}</span>
345
- </div>
346
- <div class="metric-card">
347
- <span class="metric-label">Target Node</span>
348
- <span class="metric-value" id="stat-target">${this.telemetry.activeTarget}</span>
468
+ <div class="header">
469
+ <div class="brand-group">
470
+ <div class="wordmark-badge">[FLEET//COMMAND]</div>
471
+ <div class="title-meta">
472
+ <h3 class="plugin-title">Command Fleet Coordination</h3>
473
+ <p class="plugin-subtitle">Multi-agent runtime coordination and documentation integrity</p>
474
+ </div>
349
475
  </div>
350
- <div class="metric-card">
351
- <span class="metric-label">Docs Cache</span>
352
- <span class="metric-value" id="stat-docs">${this.telemetry.docsStatus}</span>
476
+ <div class="status-chip">
477
+ ${installedAgents.length > 0 ? `${installedAgents.length} Agents Discovered` : 'Scanning Fleet'}
353
478
  </div>
354
479
  </div>
355
480
 
356
- <!-- Agent Discovery Matrix -->
357
- <div class="matrix-container">
358
- <div class="matrix-title">Supported Coding Agent Adapters</div>
359
- <div class="agent-chips" id="agent-matrix">
360
- <div class="agent-chip installed"><span class="status-dot"></span><span class="agent-name">Claude</span></div>
361
- <div class="agent-chip installed"><span class="status-dot"></span><span class="agent-name">DSH</span></div>
362
- <div class="agent-chip installed"><span class="status-dot"></span><span class="agent-name">Hermes</span></div>
363
- <div class="agent-chip uninstalled"><span class="status-dot"></span><span class="agent-name">Antigravity</span></div>
364
- <div class="agent-chip uninstalled"><span class="status-dot"></span><span class="agent-name">Codex</span></div>
365
- <div class="agent-chip uninstalled"><span class="status-dot"></span><span class="agent-name">Cursor</span></div>
481
+ <div class="node-bar">
482
+ <div class="node-toggles">
483
+ <button class="btn-node" data-active="${this.selectedNode === 'local'}" type="button" id="node-local">This Node</button>
484
+ <button class="btn-node" data-active="${this.selectedNode === 'cloud'}" type="button" id="node-cloud">Cloud Node</button>
485
+ <button class="btn-node" data-active="${this.selectedNode === 'macbook'}" type="button" id="node-macbook">MacBook Node</button>
366
486
  </div>
487
+ <button class="btn-docs-sync" type="button" id="btn-docs" ${this.executing ? 'disabled' : ''}>
488
+ ${this.executing ? 'Syncing...' : 'Sync Official Docs'}
489
+ </button>
367
490
  </div>
368
491
 
369
- <!-- Action Buttons Grid (>=44px touch targets) -->
370
- <div class="actions-grid">
371
- <button class="btn-action btn-primary" data-cmd="status">INSPECT FLEET</button>
372
- <button class="btn-action" data-cmd="nodes">MESH NODES</button>
373
- <button class="btn-action" data-cmd="catalog">ADAPTERS</button>
374
- <button class="btn-action" data-cmd="docs-status">DOCS AUDIT</button>
375
- <button class="btn-action" data-cmd="maintenance">MAINTENANCE</button>
376
- <button class="btn-action" data-cmd="setup">SETUP AGENTS</button>
377
- </div>
492
+ <div class="workspace-grid">
493
+ <div class="roster-card">
494
+ ${this.agents.length > 0 ? this.agents.map(a => `
495
+ <button class="agent-item" data-selected="${(a.id || a.name) === this.selectedAgent}" type="button" data-agent-id="${escapeHtml(a.id || a.name)}">
496
+ <div class="agent-meta-left">
497
+ <span class="agent-name">${escapeHtml(a.name || a.id)}</span>
498
+ <span class="agent-version">${escapeHtml(a.version || DASH)}</span>
499
+ </div>
500
+ <div class="agent-badges">
501
+ <span class="badge-status ${a.installed !== false ? 'badge-online' : 'badge-offline'}">
502
+ ${a.installed !== false ? 'Installed' : 'Missing'}
503
+ </span>
504
+ <span class="badge-status ${a.authenticated !== false ? 'badge-auth-ok' : 'badge-auth-needed'}">
505
+ ${a.authenticated !== false ? 'Auth OK' : 'Sign-in'}
506
+ </span>
507
+ </div>
508
+ </button>
509
+ `).join('') : `
510
+ <div class="empty-agents-box">Scanning for terminal coding agents...</div>
511
+ `}
512
+ </div>
378
513
 
379
- <!-- Console Readout -->
380
- <div class="terminal-container">
381
- <div class="terminal-header">
382
- <span class="terminal-label">Fleet Console</span>
383
- <button class="terminal-copy" id="btn-copy" title="Copy console output">Copy</button>
514
+ <div class="inspector-panel">
515
+ ${active ? `
516
+ <h4 class="inspector-title">${escapeHtml(active.name || active.id)} Details</h4>
517
+ <div class="property-grid">
518
+ <div class="property-box">
519
+ <span class="property-label">Runtime Binary</span>
520
+ <span class="property-val">${escapeHtml(active.binary || active.id || DASH)}</span>
521
+ </div>
522
+ <div class="property-box">
523
+ <span class="property-label">Installed Version</span>
524
+ <span class="property-val">${escapeHtml(active.version || DASH)}</span>
525
+ </div>
526
+ <div class="property-box">
527
+ <span class="property-label">Primary Model</span>
528
+ <span class="property-val">${escapeHtml(active.primaryModel || 'Default Model')}</span>
529
+ </div>
530
+ <div class="property-box">
531
+ <span class="property-label">Documentation</span>
532
+ <span class="property-val" style="color: var(--face-ok);">Verified Local</span>
533
+ </div>
534
+ </div>
535
+
536
+ <div>
537
+ <span class="property-label">Permission Enforcement Gates</span>
538
+ <div class="permission-pills">
539
+ <span class="pill-permission pill-active">Read Safe</span>
540
+ <span class="pill-permission pill-active">Write Gated</span>
541
+ <span class="pill-permission pill-active">Send Guarded</span>
542
+ <span class="pill-permission">Destructive Locked</span>
543
+ </div>
544
+ </div>
545
+ ` : `
546
+ <div class="empty-agents-box">Select an agent from the fleet roster to inspect configuration.</div>
547
+ `}
384
548
  </div>
385
- <div class="terminal-readout" id="card-out">Fleet controller initialized. Ready for agent dispatch.</div>
549
+ </div>
550
+
551
+ <div class="footer-bar">
552
+ <span>Target: ${this.selectedNode.toUpperCase()} MESH NODE</span>
553
+ <span>Fleet Version: v0.2.0</span>
386
554
  </div>
387
555
  `;
388
556
 
389
- const buttons = root.querySelectorAll('.btn-action');
390
- buttons.forEach(btn => {
557
+ // Event listeners
558
+ root.querySelector('#node-local')?.addEventListener('click', () => this.switchNode('local'));
559
+ root.querySelector('#node-cloud')?.addEventListener('click', () => this.switchNode('cloud'));
560
+ root.querySelector('#node-macbook')?.addEventListener('click', () => this.switchNode('macbook'));
561
+
562
+ root.querySelectorAll('.agent-item').forEach(btn => {
391
563
  btn.addEventListener('click', () => {
392
- const cmd = btn.getAttribute('data-cmd');
393
- if (cmd) this.execute(cmd, []);
564
+ const id = btn.getAttribute('data-agent-id');
565
+ if (id) this.selectAgent(id);
394
566
  });
395
567
  });
396
568
 
397
- const copyBtn = root.querySelector('#btn-copy');
398
- if (copyBtn) {
399
- copyBtn.addEventListener('click', () => {
400
- const out = root.querySelector('#card-out');
401
- if (out && out.textContent) {
402
- navigator.clipboard?.writeText(out.textContent);
403
- copyBtn.textContent = 'Copied!';
404
- setTimeout(() => { copyBtn.textContent = 'Copy'; }, 1500);
405
- }
406
- });
407
- }
569
+ root.querySelector('#btn-docs')?.addEventListener('click', () => this.refreshDocs());
408
570
  }
409
571
  }
410
572