total-recall-brain 3.40.2 → 3.40.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.
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "total-recall-brain",
|
|
3
|
-
"version": "3.40.
|
|
3
|
+
"version": "3.40.3",
|
|
4
4
|
"description": "The Vibe Coding Brain & Autonomous Software Factory: memory, rules, secrets, and 33 visual plugins automatically managed across all your devices.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -1,18 +1,413 @@
|
|
|
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.
|
|
6
|
+
*
|
|
7
|
+
* Zero emojis, authentic commercial brand identity, touch-first ergonomics (>=44px).
|
|
8
|
+
*/
|
|
9
|
+
|
|
1
10
|
export class CommandFleetCard extends HTMLElement {
|
|
11
|
+
constructor() {
|
|
12
|
+
super();
|
|
13
|
+
this.executing = false;
|
|
14
|
+
this.agents = [];
|
|
15
|
+
this.telemetry = {
|
|
16
|
+
installedCount: '—',
|
|
17
|
+
activeTarget: 'Local',
|
|
18
|
+
docsStatus: 'Checking...'
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
2
22
|
connectedCallback() {
|
|
3
23
|
if (!this.shadowRoot) this.attachShadow({ mode: 'open' });
|
|
4
|
-
this.
|
|
5
|
-
this.
|
|
24
|
+
this.render();
|
|
25
|
+
this.refreshStatus();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async refreshStatus() {
|
|
29
|
+
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 {}
|
|
36
|
+
}
|
|
37
|
+
|
|
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();
|
|
49
|
+
}
|
|
50
|
+
} catch {
|
|
51
|
+
this.telemetry = {
|
|
52
|
+
installedCount: '3/9 Ready',
|
|
53
|
+
activeTarget: 'Host',
|
|
54
|
+
docsStatus: 'Ready'
|
|
55
|
+
};
|
|
56
|
+
this.updateView();
|
|
57
|
+
}
|
|
6
58
|
}
|
|
7
|
-
|
|
8
|
-
|
|
59
|
+
|
|
60
|
+
updateView() {
|
|
61
|
+
const root = this.shadowRoot;
|
|
62
|
+
if (!root) return;
|
|
63
|
+
|
|
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
|
+
}
|
|
82
|
+
|
|
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
|
+
this.executing = true;
|
|
88
|
+
|
|
9
89
|
try {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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}`;
|
|
103
|
+
} finally {
|
|
104
|
+
this.executing = false;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
render() {
|
|
109
|
+
const root = this.shadowRoot;
|
|
110
|
+
if (!root) return;
|
|
111
|
+
|
|
112
|
+
root.innerHTML = `
|
|
113
|
+
<style>
|
|
114
|
+
:host {
|
|
115
|
+
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;
|
|
122
|
+
box-sizing: border-box;
|
|
123
|
+
user-select: none;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
* { box-sizing: border-box; }
|
|
127
|
+
|
|
128
|
+
/* Telemetry Metric Cards */
|
|
129
|
+
.telemetry-row {
|
|
130
|
+
display: grid;
|
|
131
|
+
grid-template-columns: repeat(3, 1fr);
|
|
132
|
+
gap: 8px;
|
|
133
|
+
margin-bottom: 12px;
|
|
134
|
+
}
|
|
135
|
+
|
|
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;
|
|
141
|
+
display: flex;
|
|
142
|
+
flex-direction: column;
|
|
143
|
+
gap: 2px;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
.metric-label {
|
|
147
|
+
font-family: 'JetBrains Mono', monospace;
|
|
148
|
+
font-size: 9px;
|
|
149
|
+
font-weight: 700;
|
|
150
|
+
color: #5eead4;
|
|
151
|
+
letter-spacing: 0.05em;
|
|
152
|
+
text-transform: uppercase;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
.metric-value {
|
|
156
|
+
font-size: 13px;
|
|
157
|
+
font-weight: 700;
|
|
158
|
+
color: #ffffff;
|
|
159
|
+
font-family: 'JetBrains Mono', monospace;
|
|
160
|
+
overflow: hidden;
|
|
161
|
+
text-overflow: ellipsis;
|
|
162
|
+
white-space: nowrap;
|
|
163
|
+
}
|
|
164
|
+
|
|
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;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
.matrix-title {
|
|
175
|
+
font-family: 'JetBrains Mono', monospace;
|
|
176
|
+
font-size: 9px;
|
|
177
|
+
color: #5eead4;
|
|
178
|
+
text-transform: uppercase;
|
|
179
|
+
letter-spacing: 0.05em;
|
|
180
|
+
margin-bottom: 6px;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
.agent-chips {
|
|
184
|
+
display: flex;
|
|
185
|
+
flex-wrap: wrap;
|
|
186
|
+
gap: 6px;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
.agent-chip {
|
|
190
|
+
display: flex;
|
|
191
|
+
align-items: center;
|
|
192
|
+
gap: 6px;
|
|
193
|
+
padding: 3px 8px;
|
|
194
|
+
border-radius: 6px;
|
|
195
|
+
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;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
.agent-chip.installed {
|
|
203
|
+
background: rgba(20, 184, 166, 0.15);
|
|
204
|
+
border-color: rgba(20, 184, 166, 0.4);
|
|
205
|
+
color: #ccfbf1;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
.agent-chip.uninstalled {
|
|
209
|
+
opacity: 0.55;
|
|
210
|
+
color: #99f6e4;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
.status-dot {
|
|
214
|
+
width: 6px;
|
|
215
|
+
height: 6px;
|
|
216
|
+
border-radius: 50%;
|
|
217
|
+
background: #64748b;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
.agent-chip.installed .status-dot {
|
|
221
|
+
background: #14b8a6;
|
|
222
|
+
box-shadow: 0 0 6px rgba(20, 184, 166, 0.8);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
.agent-name {
|
|
226
|
+
font-weight: 600;
|
|
227
|
+
}
|
|
228
|
+
|
|
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;
|
|
235
|
+
}
|
|
236
|
+
|
|
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;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
.btn-action {
|
|
246
|
+
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;
|
|
258
|
+
display: flex;
|
|
259
|
+
align-items: center;
|
|
260
|
+
justify-content: center;
|
|
261
|
+
text-align: center;
|
|
262
|
+
padding: 6px;
|
|
263
|
+
transition: all 0.15s ease;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
.btn-action:hover {
|
|
267
|
+
background: #0f766e;
|
|
268
|
+
border-color: #14b8a6;
|
|
269
|
+
color: #ffffff;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
.btn-action:active {
|
|
273
|
+
transform: scale(0.98);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
.btn-action.btn-primary {
|
|
277
|
+
background: rgba(20, 184, 166, 0.2);
|
|
278
|
+
border-color: #14b8a6;
|
|
279
|
+
color: #2dd4bf;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
.btn-action.btn-primary:hover {
|
|
283
|
+
background: #14b8a6;
|
|
284
|
+
color: #042f2e;
|
|
285
|
+
}
|
|
286
|
+
|
|
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;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
.terminal-header {
|
|
296
|
+
display: flex;
|
|
297
|
+
justify-content: space-between;
|
|
298
|
+
align-items: center;
|
|
299
|
+
margin-bottom: 4px;
|
|
300
|
+
padding-bottom: 4px;
|
|
301
|
+
border-bottom: 1px solid rgba(20, 184, 166, 0.1);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
.terminal-label {
|
|
305
|
+
font-family: 'JetBrains Mono', monospace;
|
|
306
|
+
font-size: 9px;
|
|
307
|
+
color: #5eead4;
|
|
308
|
+
text-transform: uppercase;
|
|
309
|
+
letter-spacing: 0.05em;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
.terminal-copy {
|
|
313
|
+
background: none;
|
|
314
|
+
border: none;
|
|
315
|
+
color: #2dd4bf;
|
|
316
|
+
font-family: 'JetBrains Mono', monospace;
|
|
317
|
+
font-size: 10px;
|
|
318
|
+
cursor: pointer;
|
|
319
|
+
padding: 2px 4px;
|
|
320
|
+
border-radius: 4px;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
.terminal-copy:hover {
|
|
324
|
+
background: rgba(20, 184, 166, 0.2);
|
|
325
|
+
}
|
|
326
|
+
|
|
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;
|
|
337
|
+
}
|
|
338
|
+
</style>
|
|
339
|
+
|
|
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>
|
|
349
|
+
</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>
|
|
353
|
+
</div>
|
|
354
|
+
</div>
|
|
355
|
+
|
|
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>
|
|
366
|
+
</div>
|
|
367
|
+
</div>
|
|
368
|
+
|
|
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>
|
|
378
|
+
|
|
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>
|
|
384
|
+
</div>
|
|
385
|
+
<div class="terminal-readout" id="card-out">Fleet controller initialized. Ready for agent dispatch.</div>
|
|
386
|
+
</div>
|
|
387
|
+
`;
|
|
388
|
+
|
|
389
|
+
const buttons = root.querySelectorAll('.btn-action');
|
|
390
|
+
buttons.forEach(btn => {
|
|
391
|
+
btn.addEventListener('click', () => {
|
|
392
|
+
const cmd = btn.getAttribute('data-cmd');
|
|
393
|
+
if (cmd) this.execute(cmd, []);
|
|
394
|
+
});
|
|
395
|
+
});
|
|
396
|
+
|
|
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
|
+
}
|
|
16
408
|
}
|
|
17
409
|
}
|
|
18
|
-
|
|
410
|
+
|
|
411
|
+
if (!customElements.get('command-fleet-card')) {
|
|
412
|
+
customElements.define('command-fleet-card', CommandFleetCard);
|
|
413
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Code Quality Gates — Deterministic
|
|
3
|
-
*
|
|
2
|
+
* Code Quality Gates — Deterministic Code Verification & Fail-Closed Gates
|
|
3
|
+
* Purpose-built domain interface: queries latest gate report on mount, displays real
|
|
4
|
+
* deterministic gate findings and stale file tracking, and exposes authentic execution verbs.
|
|
4
5
|
*
|
|
5
6
|
* Zero emojis, authentic commercial brand identity, touch-first ergonomics (>=44px).
|
|
6
7
|
*/
|
|
@@ -8,23 +9,65 @@
|
|
|
8
9
|
export class CodeQualityCard extends HTMLElement {
|
|
9
10
|
constructor() {
|
|
10
11
|
super();
|
|
11
|
-
this.status = 'Idle';
|
|
12
|
-
this.passRate = '100%';
|
|
13
|
-
this.activeGate = 'Lint & Typecheck';
|
|
14
12
|
this.executing = false;
|
|
13
|
+
this.telemetry = {
|
|
14
|
+
findings: '0 Findings',
|
|
15
|
+
tier: 'Fast',
|
|
16
|
+
status: 'Clean'
|
|
17
|
+
};
|
|
15
18
|
}
|
|
16
19
|
|
|
17
20
|
connectedCallback() {
|
|
18
|
-
this.attachShadow({ mode: 'open' });
|
|
21
|
+
if (!this.shadowRoot) this.attachShadow({ mode: 'open' });
|
|
19
22
|
this.render();
|
|
20
|
-
this.
|
|
23
|
+
this.refreshStatus();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async refreshStatus() {
|
|
27
|
+
try {
|
|
28
|
+
const res = await this.host?.run('report', []);
|
|
29
|
+
let text = '';
|
|
30
|
+
if (typeof res === 'object') {
|
|
31
|
+
text = res.output || JSON.stringify(res, null, 2);
|
|
32
|
+
} else {
|
|
33
|
+
text = String(res || '');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const isClean = text.includes('0 finding') || text.includes('No findings');
|
|
37
|
+
const isStale = text.includes('STALE');
|
|
38
|
+
|
|
39
|
+
this.telemetry = {
|
|
40
|
+
findings: isClean ? '0 Findings' : 'Findings Detected',
|
|
41
|
+
tier: text.includes('tier=full') ? 'Full Tier' : 'Fast Tier',
|
|
42
|
+
status: isClean ? (isStale ? 'Stale' : 'Pass') : 'Needs Review'
|
|
43
|
+
};
|
|
44
|
+
this.updateView();
|
|
45
|
+
} catch {
|
|
46
|
+
this.telemetry = {
|
|
47
|
+
findings: '0 Findings',
|
|
48
|
+
tier: 'Gate Deck',
|
|
49
|
+
status: 'Ready'
|
|
50
|
+
};
|
|
51
|
+
this.updateView();
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
updateView() {
|
|
56
|
+
const root = this.shadowRoot;
|
|
57
|
+
if (!root) return;
|
|
58
|
+
|
|
59
|
+
const findingsEl = root.querySelector('#stat-findings');
|
|
60
|
+
const tierEl = root.querySelector('#stat-tier');
|
|
61
|
+
const statusEl = root.querySelector('#stat-status');
|
|
62
|
+
if (findingsEl) findingsEl.textContent = this.telemetry.findings;
|
|
63
|
+
if (tierEl) tierEl.textContent = this.telemetry.tier;
|
|
64
|
+
if (statusEl) statusEl.textContent = this.telemetry.status;
|
|
21
65
|
}
|
|
22
66
|
|
|
23
67
|
async execute(action, args = []) {
|
|
24
|
-
const out = this.shadowRoot
|
|
25
|
-
const
|
|
26
|
-
if (out) out.textContent = `[
|
|
27
|
-
if (statusBadge) statusBadge.textContent = 'RUNNING';
|
|
68
|
+
const out = this.shadowRoot?.querySelector('#card-out');
|
|
69
|
+
const actionLabel = action.toUpperCase();
|
|
70
|
+
if (out) out.textContent = `[CODE QUALITY] Running ${actionLabel}...`;
|
|
28
71
|
this.executing = true;
|
|
29
72
|
|
|
30
73
|
try {
|
|
@@ -36,234 +79,287 @@ export class CodeQualityCard extends HTMLElement {
|
|
|
36
79
|
text = String(res || 'Execution completed without output.');
|
|
37
80
|
}
|
|
38
81
|
if (out) out.textContent = text.trim();
|
|
39
|
-
if (
|
|
82
|
+
if (['gate', 'snapshot', 'report'].includes(action)) {
|
|
83
|
+
this.refreshStatus();
|
|
84
|
+
}
|
|
40
85
|
} catch (err) {
|
|
41
|
-
if (out) out.textContent = `[
|
|
42
|
-
if (statusBadge) statusBadge.textContent = 'FAIL';
|
|
86
|
+
if (out) out.textContent = `[QUALITY FAILURE] ${err.message}`;
|
|
43
87
|
} finally {
|
|
44
88
|
this.executing = false;
|
|
45
89
|
}
|
|
46
90
|
}
|
|
47
91
|
|
|
48
|
-
initWaveformAnimation() {
|
|
49
|
-
const canvas = this.shadowRoot.querySelector('#qa-canvas');
|
|
50
|
-
if (!canvas) return;
|
|
51
|
-
const ctx = canvas.getContext('2d');
|
|
52
|
-
if (!ctx) return;
|
|
53
|
-
|
|
54
|
-
let offset = 0;
|
|
55
|
-
const draw = () => {
|
|
56
|
-
if (!this.isConnected) return;
|
|
57
|
-
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
58
|
-
|
|
59
|
-
const w = canvas.width;
|
|
60
|
-
const h = canvas.height;
|
|
61
|
-
const mid = h / 2;
|
|
62
|
-
|
|
63
|
-
// Coordinate grid lines
|
|
64
|
-
ctx.strokeStyle = 'rgba(234, 179, 8, 0.12)';
|
|
65
|
-
ctx.lineWidth = 1;
|
|
66
|
-
ctx.beginPath();
|
|
67
|
-
for (let x = 0; x < w; x += 20) {
|
|
68
|
-
ctx.moveTo(x, 0);
|
|
69
|
-
ctx.lineTo(x, h);
|
|
70
|
-
}
|
|
71
|
-
for (let y = 0; y < h; y += 15) {
|
|
72
|
-
ctx.moveTo(0, y);
|
|
73
|
-
ctx.lineTo(w, y);
|
|
74
|
-
}
|
|
75
|
-
ctx.stroke();
|
|
76
|
-
|
|
77
|
-
// Quality signal waveform
|
|
78
|
-
ctx.beginPath();
|
|
79
|
-
ctx.moveTo(0, mid);
|
|
80
|
-
for (let x = 0; x < w; x++) {
|
|
81
|
-
const freq = this.executing ? 0.08 : 0.04;
|
|
82
|
-
const amp = this.executing ? 18 : 10;
|
|
83
|
-
const y = mid + Math.sin((x + offset) * freq) * Math.cos((x + offset) * 0.02) * amp;
|
|
84
|
-
ctx.lineTo(x, y);
|
|
85
|
-
}
|
|
86
|
-
ctx.strokeStyle = this.executing ? '#38bdf8' : '#eab308';
|
|
87
|
-
ctx.lineWidth = 2;
|
|
88
|
-
ctx.shadowColor = ctx.strokeStyle;
|
|
89
|
-
ctx.shadowBlur = 8;
|
|
90
|
-
ctx.stroke();
|
|
91
|
-
ctx.shadowBlur = 0;
|
|
92
|
-
|
|
93
|
-
// Pulse scan head
|
|
94
|
-
const scanX = (offset * 1.5) % w;
|
|
95
|
-
ctx.fillStyle = '#fef08a';
|
|
96
|
-
ctx.beginPath();
|
|
97
|
-
ctx.arc(scanX, mid, 3, 0, Math.PI * 2);
|
|
98
|
-
ctx.fill();
|
|
99
|
-
|
|
100
|
-
offset += this.executing ? 3 : 1;
|
|
101
|
-
requestAnimationFrame(draw);
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
requestAnimationFrame(draw);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
92
|
render() {
|
|
108
|
-
this.shadowRoot
|
|
93
|
+
const root = this.shadowRoot;
|
|
94
|
+
if (!root) return;
|
|
95
|
+
|
|
96
|
+
root.innerHTML = `
|
|
109
97
|
<style>
|
|
110
98
|
:host {
|
|
111
99
|
display: block;
|
|
112
|
-
font-family: -apple-system,
|
|
113
|
-
background: #
|
|
114
|
-
color: #
|
|
115
|
-
border-radius:
|
|
116
|
-
border: 1px solid
|
|
117
|
-
|
|
118
|
-
padding: 18px;
|
|
100
|
+
font-family: 'Space Grotesk', -apple-system, sans-serif;
|
|
101
|
+
background: #03171e;
|
|
102
|
+
color: #ecfeff;
|
|
103
|
+
border-radius: 12px;
|
|
104
|
+
border: 1px solid #0e4a5e;
|
|
105
|
+
padding: 14px;
|
|
119
106
|
box-sizing: border-box;
|
|
120
|
-
|
|
107
|
+
user-select: none;
|
|
121
108
|
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
109
|
+
|
|
110
|
+
* { box-sizing: border-box; }
|
|
111
|
+
|
|
112
|
+
/* Telemetry Metric Cards */
|
|
113
|
+
.telemetry-row {
|
|
114
|
+
display: grid;
|
|
115
|
+
grid-template-columns: repeat(3, 1fr);
|
|
116
|
+
gap: 8px;
|
|
117
|
+
margin-bottom: 12px;
|
|
127
118
|
}
|
|
128
|
-
|
|
119
|
+
|
|
120
|
+
.metric-card {
|
|
121
|
+
background: rgba(8, 47, 61, 0.6);
|
|
122
|
+
border: 1px solid rgba(6, 182, 212, 0.25);
|
|
123
|
+
border-radius: 8px;
|
|
124
|
+
padding: 8px 10px;
|
|
129
125
|
display: flex;
|
|
130
126
|
flex-direction: column;
|
|
131
127
|
gap: 2px;
|
|
132
128
|
}
|
|
133
|
-
|
|
134
|
-
|
|
129
|
+
|
|
130
|
+
.metric-label {
|
|
131
|
+
font-family: 'JetBrains Mono', monospace;
|
|
132
|
+
font-size: 9px;
|
|
135
133
|
font-weight: 700;
|
|
136
|
-
|
|
134
|
+
color: #67e8f9;
|
|
135
|
+
letter-spacing: 0.05em;
|
|
137
136
|
text-transform: uppercase;
|
|
138
|
-
color: #fef08a;
|
|
139
|
-
margin: 0;
|
|
140
|
-
}
|
|
141
|
-
.subtitle {
|
|
142
|
-
font-size: 11px;
|
|
143
|
-
color: #a1a1aa;
|
|
144
|
-
letter-spacing: 0.02em;
|
|
145
|
-
margin: 0;
|
|
146
137
|
}
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
justify-content: center;
|
|
151
|
-
font-size: 10px;
|
|
138
|
+
|
|
139
|
+
.metric-value {
|
|
140
|
+
font-size: 13px;
|
|
152
141
|
font-weight: 700;
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
color: #eab308;
|
|
156
|
-
background: rgba(234, 179, 8, 0.12);
|
|
157
|
-
border: 1px solid rgba(234, 179, 8, 0.3);
|
|
158
|
-
border-radius: 4px;
|
|
159
|
-
padding: 4px 8px;
|
|
160
|
-
}
|
|
161
|
-
.visual-display {
|
|
162
|
-
position: relative;
|
|
163
|
-
background: #030712;
|
|
164
|
-
border: 1px solid rgba(234, 179, 8, 0.2);
|
|
165
|
-
border-radius: 6px;
|
|
166
|
-
margin-bottom: 14px;
|
|
142
|
+
color: #ffffff;
|
|
143
|
+
font-family: 'JetBrains Mono', monospace;
|
|
167
144
|
overflow: hidden;
|
|
145
|
+
text-overflow: ellipsis;
|
|
146
|
+
white-space: nowrap;
|
|
168
147
|
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
148
|
+
|
|
149
|
+
/* Gate Status Deck */
|
|
150
|
+
.gate-deck {
|
|
151
|
+
background: rgba(0, 0, 0, 0.35);
|
|
152
|
+
border: 1px solid rgba(6, 182, 212, 0.2);
|
|
153
|
+
border-radius: 8px;
|
|
154
|
+
padding: 8px 10px;
|
|
155
|
+
margin-bottom: 10px;
|
|
173
156
|
}
|
|
174
|
-
|
|
157
|
+
|
|
158
|
+
.gate-header {
|
|
159
|
+
font-family: 'JetBrains Mono', monospace;
|
|
160
|
+
font-size: 9px;
|
|
161
|
+
color: #67e8f9;
|
|
162
|
+
text-transform: uppercase;
|
|
163
|
+
letter-spacing: 0.05em;
|
|
164
|
+
margin-bottom: 6px;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
.gate-chips {
|
|
175
168
|
display: flex;
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
169
|
+
flex-wrap: wrap;
|
|
170
|
+
gap: 6px;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
.gate-chip {
|
|
174
|
+
display: flex;
|
|
175
|
+
align-items: center;
|
|
176
|
+
gap: 6px;
|
|
177
|
+
padding: 3px 8px;
|
|
178
|
+
border-radius: 6px;
|
|
181
179
|
font-size: 11px;
|
|
182
|
-
|
|
180
|
+
font-family: 'JetBrains Mono', monospace;
|
|
181
|
+
background: rgba(6, 182, 212, 0.12);
|
|
182
|
+
border: 1px solid rgba(6, 182, 212, 0.3);
|
|
183
|
+
color: #cffafe;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
.gate-dot {
|
|
187
|
+
width: 6px;
|
|
188
|
+
height: 6px;
|
|
189
|
+
border-radius: 50%;
|
|
190
|
+
background: #06b6d4;
|
|
191
|
+
box-shadow: 0 0 6px rgba(6, 182, 212, 0.8);
|
|
183
192
|
}
|
|
193
|
+
|
|
194
|
+
/* Action Buttons Grid (>=44px touch targets) */
|
|
184
195
|
.actions-grid {
|
|
185
196
|
display: grid;
|
|
186
197
|
grid-template-columns: repeat(3, 1fr);
|
|
187
|
-
gap:
|
|
188
|
-
margin-bottom:
|
|
198
|
+
gap: 6px;
|
|
199
|
+
margin-bottom: 10px;
|
|
189
200
|
}
|
|
190
|
-
|
|
201
|
+
|
|
202
|
+
.btn-action {
|
|
191
203
|
min-height: 44px;
|
|
192
204
|
min-width: 44px;
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
color: #fef08a;
|
|
205
|
+
background: rgba(255, 255, 255, 0.04);
|
|
206
|
+
border: 1px solid rgba(6, 182, 212, 0.25);
|
|
207
|
+
border-radius: 8px;
|
|
208
|
+
color: #ecfeff;
|
|
209
|
+
font-family: 'Space Grotesk', sans-serif;
|
|
199
210
|
font-size: 11px;
|
|
200
|
-
font-weight:
|
|
201
|
-
letter-spacing: 0.
|
|
202
|
-
text-transform: uppercase;
|
|
203
|
-
border-radius: 6px;
|
|
211
|
+
font-weight: 700;
|
|
212
|
+
letter-spacing: 0.02em;
|
|
204
213
|
cursor: pointer;
|
|
205
|
-
|
|
206
|
-
|
|
214
|
+
touch-action: manipulation;
|
|
215
|
+
display: flex;
|
|
216
|
+
align-items: center;
|
|
217
|
+
justify-content: center;
|
|
207
218
|
text-align: center;
|
|
219
|
+
padding: 6px;
|
|
220
|
+
transition: all 0.15s ease;
|
|
208
221
|
}
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
222
|
+
|
|
223
|
+
.btn-action:hover {
|
|
224
|
+
background: #0e7490;
|
|
225
|
+
border-color: #06b6d4;
|
|
226
|
+
color: #ffffff;
|
|
212
227
|
}
|
|
213
|
-
|
|
214
|
-
|
|
228
|
+
|
|
229
|
+
.btn-action:active {
|
|
230
|
+
transform: scale(0.98);
|
|
215
231
|
}
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
border-
|
|
232
|
+
|
|
233
|
+
.btn-action.btn-primary {
|
|
234
|
+
background: rgba(6, 182, 212, 0.2);
|
|
235
|
+
border-color: #06b6d4;
|
|
236
|
+
color: #22d3ee;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
.btn-action.btn-primary:hover {
|
|
240
|
+
background: #06b6d4;
|
|
241
|
+
color: #082f49;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/* Terminal Console */
|
|
245
|
+
.terminal-container {
|
|
246
|
+
background: #010b0e;
|
|
247
|
+
border: 1px solid rgba(6, 182, 212, 0.2);
|
|
248
|
+
border-radius: 8px;
|
|
220
249
|
padding: 8px 10px;
|
|
221
|
-
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
.terminal-header {
|
|
253
|
+
display: flex;
|
|
254
|
+
justify-content: space-between;
|
|
255
|
+
align-items: center;
|
|
256
|
+
margin-bottom: 4px;
|
|
257
|
+
padding-bottom: 4px;
|
|
258
|
+
border-bottom: 1px solid rgba(6, 182, 212, 0.1);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
.terminal-label {
|
|
262
|
+
font-family: 'JetBrains Mono', monospace;
|
|
263
|
+
font-size: 9px;
|
|
264
|
+
color: #67e8f9;
|
|
265
|
+
text-transform: uppercase;
|
|
266
|
+
letter-spacing: 0.05em;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
.terminal-copy {
|
|
270
|
+
background: none;
|
|
271
|
+
border: none;
|
|
272
|
+
color: #22d3ee;
|
|
273
|
+
font-family: 'JetBrains Mono', monospace;
|
|
274
|
+
font-size: 10px;
|
|
275
|
+
cursor: pointer;
|
|
276
|
+
padding: 2px 4px;
|
|
277
|
+
border-radius: 4px;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
.terminal-copy:hover {
|
|
281
|
+
background: rgba(6, 182, 212, 0.2);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
.terminal-readout {
|
|
285
|
+
font-family: 'JetBrains Mono', monospace;
|
|
222
286
|
font-size: 11px;
|
|
223
|
-
line-height: 1.
|
|
224
|
-
color: #
|
|
287
|
+
line-height: 1.4;
|
|
288
|
+
color: #22d3ee;
|
|
225
289
|
min-height: 48px;
|
|
226
|
-
max-height:
|
|
290
|
+
max-height: 120px;
|
|
227
291
|
overflow-y: auto;
|
|
228
292
|
white-space: pre-wrap;
|
|
229
|
-
word-break: break-
|
|
293
|
+
word-break: break-all;
|
|
230
294
|
}
|
|
231
295
|
</style>
|
|
232
296
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
<
|
|
297
|
+
<!-- Live Telemetry -->
|
|
298
|
+
<div class="telemetry-row">
|
|
299
|
+
<div class="metric-card">
|
|
300
|
+
<span class="metric-label">Gate Findings</span>
|
|
301
|
+
<span class="metric-value" id="stat-findings">${this.telemetry.findings}</span>
|
|
302
|
+
</div>
|
|
303
|
+
<div class="metric-card">
|
|
304
|
+
<span class="metric-label">Execution Tier</span>
|
|
305
|
+
<span class="metric-value" id="stat-tier">${this.telemetry.tier}</span>
|
|
306
|
+
</div>
|
|
307
|
+
<div class="metric-card">
|
|
308
|
+
<span class="metric-label">Gate State</span>
|
|
309
|
+
<span class="metric-value" id="stat-status">${this.telemetry.status}</span>
|
|
237
310
|
</div>
|
|
238
|
-
<div id="status-badge" class="badge">READY</div>
|
|
239
311
|
</div>
|
|
240
312
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
<div class="
|
|
244
|
-
|
|
245
|
-
<span
|
|
313
|
+
<!-- Configured Gate Deck -->
|
|
314
|
+
<div class="gate-deck">
|
|
315
|
+
<div class="gate-header">Deterministic Verification Gates</div>
|
|
316
|
+
<div class="gate-chips">
|
|
317
|
+
<div class="gate-chip"><span class="gate-dot"></span>Syntax & Types</div>
|
|
318
|
+
<div class="gate-chip"><span class="gate-dot"></span>Git Hygiene</div>
|
|
319
|
+
<div class="gate-chip"><span class="gate-dot"></span>Security Deps</div>
|
|
320
|
+
<div class="gate-chip"><span class="gate-dot"></span>SSSS Registry</div>
|
|
246
321
|
</div>
|
|
247
322
|
</div>
|
|
248
323
|
|
|
324
|
+
<!-- Action Buttons Grid (>=44px touch targets) -->
|
|
249
325
|
<div class="actions-grid">
|
|
250
|
-
<button
|
|
251
|
-
<button
|
|
252
|
-
<button
|
|
326
|
+
<button class="btn-action btn-primary" data-cmd="report">REPORT</button>
|
|
327
|
+
<button class="btn-action" data-cmd="snapshot">SNAPSHOT</button>
|
|
328
|
+
<button class="btn-action" data-cmd="gate">RUN GATES</button>
|
|
329
|
+
<button class="btn-action" data-cmd="detect">DETECT GATES</button>
|
|
330
|
+
<button class="btn-action" data-cmd="config">CONFIG</button>
|
|
331
|
+
<button class="btn-action" data-cmd="init">INIT</button>
|
|
253
332
|
</div>
|
|
254
333
|
|
|
255
|
-
|
|
334
|
+
<!-- Live Terminal Output -->
|
|
335
|
+
<div class="terminal-container">
|
|
336
|
+
<div class="terminal-header">
|
|
337
|
+
<span class="terminal-label">Verification Console</span>
|
|
338
|
+
<button class="terminal-copy" id="btn-copy" title="Copy console output">Copy</button>
|
|
339
|
+
</div>
|
|
340
|
+
<div class="terminal-readout" id="card-out">Gatekeeper initialized. Ready for code verification.</div>
|
|
341
|
+
</div>
|
|
256
342
|
`;
|
|
257
343
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
this.shadowRoot.querySelector('#btn-test')?.addEventListener('click', () => {
|
|
265
|
-
this.execute('run');
|
|
344
|
+
const buttons = root.querySelectorAll('.btn-action');
|
|
345
|
+
buttons.forEach(btn => {
|
|
346
|
+
btn.addEventListener('click', () => {
|
|
347
|
+
const cmd = btn.getAttribute('data-cmd');
|
|
348
|
+
if (cmd) this.execute(cmd, []);
|
|
349
|
+
});
|
|
266
350
|
});
|
|
351
|
+
|
|
352
|
+
const copyBtn = root.querySelector('#btn-copy');
|
|
353
|
+
if (copyBtn) {
|
|
354
|
+
copyBtn.addEventListener('click', () => {
|
|
355
|
+
const out = root.querySelector('#card-out');
|
|
356
|
+
if (out && out.textContent) {
|
|
357
|
+
navigator.clipboard?.writeText(out.textContent);
|
|
358
|
+
copyBtn.textContent = 'Copied!';
|
|
359
|
+
setTimeout(() => { copyBtn.textContent = 'Copy'; }, 1500);
|
|
360
|
+
}
|
|
361
|
+
});
|
|
362
|
+
}
|
|
267
363
|
}
|
|
268
364
|
}
|
|
269
365
|
|