@vibecodeqa/cli 0.35.5 → 0.35.6
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/dist/monitor.js +57 -2
- package/package.json +1 -1
package/dist/monitor.js
CHANGED
|
@@ -13,6 +13,7 @@ import { watch, existsSync, mkdirSync, writeFileSync, readFileSync } from "node:
|
|
|
13
13
|
import { execFile } from "node:child_process";
|
|
14
14
|
import { fileURLToPath } from "node:url";
|
|
15
15
|
import { detectStack, detectWorkspace } from "./detect.js";
|
|
16
|
+
import { loadHistory } from "./history.js";
|
|
16
17
|
const VERSION = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf-8")).version;
|
|
17
18
|
const CLI_PATH = fileURLToPath(new URL("./cli.js", import.meta.url));
|
|
18
19
|
const DEFAULTS = {
|
|
@@ -199,6 +200,55 @@ function ConfigScreen({ monCfg, onSave, onClose, }) {
|
|
|
199
200
|
return (_jsxs(Text, { children: [_jsxs(Text, { color: selected ? "white" : "gray", children: [prefix, opt.label.padEnd(28)] }), _jsxs(Text, { color: "cyan", bold: true, children: ["\u25C0 ", String(opt.value).padStart(5), " \u25B6"] })] }, opt.key));
|
|
200
201
|
}), _jsx(Text, { dimColor: true, children: " " }), _jsx(Text, { dimColor: true, children: " \u2191\u2193 navigate \u00B7 Space toggle \u00B7 \u2190\u2192 adjust \u00B7 Shift+\u2190\u2192 \u00D710" }), _jsx(Text, { dimColor: true, children: " s save & close \u00B7 Esc cancel" })] }));
|
|
201
202
|
}
|
|
203
|
+
// ── Trends Screen ──
|
|
204
|
+
function sparkFull(values, width) {
|
|
205
|
+
if (values.length < 2)
|
|
206
|
+
return "";
|
|
207
|
+
const bars = " ▁▂▃▄▅▆▇█";
|
|
208
|
+
const min = Math.min(...values);
|
|
209
|
+
const max = Math.max(...values);
|
|
210
|
+
const range = max - min || 1;
|
|
211
|
+
// Resample to fit width
|
|
212
|
+
const sampled = [];
|
|
213
|
+
for (let i = 0; i < width; i++) {
|
|
214
|
+
const idx = Math.round((i / (width - 1)) * (values.length - 1));
|
|
215
|
+
sampled.push(values[idx]);
|
|
216
|
+
}
|
|
217
|
+
return sampled.map((v) => bars[Math.round(((v - min) / range) * 8)]).join("");
|
|
218
|
+
}
|
|
219
|
+
function TrendsScreen({ cwd, height, onClose }) {
|
|
220
|
+
const historyDir = join(cwd, ".vibe-check", "history");
|
|
221
|
+
const history = loadHistory(historyDir);
|
|
222
|
+
useInput((input, key) => {
|
|
223
|
+
if (key.escape || input === "t")
|
|
224
|
+
onClose();
|
|
225
|
+
});
|
|
226
|
+
if (history.length < 2) {
|
|
227
|
+
return (_jsxs(Box, { flexDirection: "column", height: height, paddingX: 1, children: [_jsx(Text, { bold: true, color: "magenta", children: " \u25C8 Trends" }), _jsx(Text, { dimColor: true, children: " Need at least 2 scans. Run the scanner a few more times." }), _jsx(Text, { dimColor: true, children: " Esc to go back" })] }));
|
|
228
|
+
}
|
|
229
|
+
const latest = history[history.length - 1];
|
|
230
|
+
const first = history[0];
|
|
231
|
+
const overallDelta = latest.score - first.score;
|
|
232
|
+
const scores = history.map((h) => h.score);
|
|
233
|
+
const chartWidth = 40;
|
|
234
|
+
// Get all check names from latest
|
|
235
|
+
const checkNames = [...latest.checkScores.keys()];
|
|
236
|
+
// Build per-check trends
|
|
237
|
+
const checkTrends = checkNames
|
|
238
|
+
.map((name) => {
|
|
239
|
+
const values = history.map((h) => h.checkScores.get(name) ?? 0).filter((v) => v > 0);
|
|
240
|
+
if (values.length < 2)
|
|
241
|
+
return null;
|
|
242
|
+
const current = values[values.length - 1];
|
|
243
|
+
const prev = values[0];
|
|
244
|
+
const delta = current - prev;
|
|
245
|
+
return { name, current, delta, spark: sparkFull(values, 20) };
|
|
246
|
+
})
|
|
247
|
+
.filter(Boolean)
|
|
248
|
+
.sort((a, b) => a.delta - b.delta);
|
|
249
|
+
const visibleChecks = Math.max(1, height - 10);
|
|
250
|
+
return (_jsxs(Box, { flexDirection: "column", height: height, paddingX: 1, children: [_jsxs(Text, { bold: true, color: "magenta", children: [" \u25C8 Trends \u2014 ", history.length, " scans"] }), _jsxs(Text, { dimColor: true, children: [" ", first.timestamp.split("T")[0], " \u2192 ", latest.timestamp.split("T")[0]] }), _jsx(Text, { children: " " }), _jsx(Text, { bold: true, children: " Overall Score" }), _jsxs(Text, { children: [_jsxs(Text, { color: "cyan", children: [" ", sparkFull(scores, chartWidth), " "] }), _jsxs(Text, { color: gc(latest.score >= 90 ? "A" : latest.score >= 75 ? "B" : "C"), bold: true, children: [" ", latest.score] }), _jsxs(Text, { color: overallDelta >= 0 ? "green" : "red", children: [" ", overallDelta >= 0 ? "+" : "", overallDelta] })] }), _jsx(Text, { children: " " }), _jsx(Text, { bold: true, children: " Per-Check (first \u2192 latest)" }), checkTrends.slice(0, visibleChecks).map((t) => (_jsxs(Text, { children: [_jsxs(Text, { children: [" ", t.name.slice(0, 14).padEnd(14), " "] }), _jsxs(Text, { color: "cyan", children: [t.spark, " "] }), _jsxs(Text, { color: gc(t.current >= 90 ? "A" : t.current >= 75 ? "B" : "C"), children: [String(t.current).padStart(3), " "] }), _jsxs(Text, { color: t.delta >= 0 ? "green" : "red", children: [t.delta >= 0 ? "+" : "", t.delta] })] }, t.name))), checkTrends.length > visibleChecks && _jsxs(Text, { dimColor: true, children: [" +", checkTrends.length - visibleChecks, " more"] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: " Esc back to dashboard" }) })] }));
|
|
251
|
+
}
|
|
202
252
|
// ── Main App ──
|
|
203
253
|
/** Load last scan from .vibe-check/report.json for instant display on startup. */
|
|
204
254
|
function loadCachedScan(cwd) {
|
|
@@ -308,7 +358,7 @@ function MonitorApp({ cwd }) {
|
|
|
308
358
|
// Keyboard — all input handled, nothing echoed
|
|
309
359
|
useInput((input, key) => {
|
|
310
360
|
if (key.escape) {
|
|
311
|
-
if (mode
|
|
361
|
+
if (mode !== "monitor") {
|
|
312
362
|
setMode("monitor");
|
|
313
363
|
return;
|
|
314
364
|
}
|
|
@@ -325,9 +375,14 @@ function MonitorApp({ cwd }) {
|
|
|
325
375
|
doScan();
|
|
326
376
|
if (input === "c")
|
|
327
377
|
setMode("config");
|
|
378
|
+
if (input === "t")
|
|
379
|
+
setMode("trends");
|
|
328
380
|
});
|
|
329
381
|
const proj = basename(cwd);
|
|
330
382
|
const p = monCfg.panels;
|
|
383
|
+
if (mode === "trends") {
|
|
384
|
+
return (_jsx(Box, { flexDirection: "column", height: rows, children: _jsx(TrendsScreen, { cwd: cwd, height: rows, onClose: () => setMode("monitor") }) }));
|
|
385
|
+
}
|
|
331
386
|
if (mode === "config") {
|
|
332
387
|
return (_jsx(Box, { flexDirection: "column", height: rows, justifyContent: "center", alignItems: "center", children: _jsx(ConfigScreen, { monCfg: monCfg, onSave: (cfg) => { setMonCfg(cfg); saveMonitorConfig(cwd, cfg); addLog("Settings saved", "info"); }, onClose: () => setMode("monitor") }) }));
|
|
333
388
|
}
|
|
@@ -342,7 +397,7 @@ function MonitorApp({ cwd }) {
|
|
|
342
397
|
const errorCount = state.checks.reduce((s, c) => s + c.issues.filter((i) => i.severity === "error").length, 0);
|
|
343
398
|
const warnCount = state.checks.reduce((s, c) => s + c.issues.filter((i) => i.severity === "warning").length, 0);
|
|
344
399
|
const activeCount = state.checks.filter((c) => !c.details.skipped && !c.details.comingSoon).length;
|
|
345
|
-
return (_jsxs(Box, { flexDirection: "column", height: rows, children: [_jsxs(Box, { paddingX: 1, justifyContent: "space-between", children: [_jsxs(Text, { children: [_jsx(Text, { color: "magenta", bold: true, children: "vcqa monitor" }), _jsxs(Text, { dimColor: true, children: [" v", VERSION] })] }), _jsxs(Text, { children: [_jsx(Text, { bold: true, children: proj }), _jsxs(Text, { dimColor: true, children: [" ", stack.language, "/", stack.framework, workspace.isMonorepo ? ` · ${workspace.tool}` : ""] })] }), _jsxs(Text, { children: [state.score > 0 && _jsxs(Text, { color: gc(state.grade), bold: true, children: [state.grade, " ", state.score, " "] }), _jsx(Text, { dimColor: true, children: state.scanning ? "⟳ scanning" : "● watching" })] })] }), _jsx(Box, { paddingX: 1, gap: 2, height: 1, children: state.scanCount > 0 && (_jsxs(_Fragment, { children: [_jsxs(Text, { children: [_jsx(Text, { dimColor: true, children: "E " }), _jsx(Text, { color: "red", bold: true, children: errorCount })] }), _jsxs(Text, { children: [_jsx(Text, { dimColor: true, children: "W " }), _jsx(Text, { color: "yellow", bold: true, children: warnCount })] }), _jsxs(Text, { children: [_jsx(Text, { dimColor: true, children: "checks " }), _jsx(Text, { bold: true, children: activeCount })] }), _jsxs(Text, { children: [_jsx(Text, { dimColor: true, children: "scan " }), _jsxs(Text, { children: [state.duration, "ms"] })] }), state.scores.length >= 2 && _jsx(Text, { color: "cyan", children: spark(state.scores) })] })) }), _jsxs(Box, { height: bodyRows, children: [sidebarVisible && (_jsxs(Box, { flexDirection: "column", width: 26, children: [p.score && _jsx(ScorePanel, { state: state, height: scoreH }), p.checks && _jsx(ChecksPanel, { checks: state.checks, height: checksH })] })), mainVisible && (_jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [p.activity && _jsx(ActivityPanel, { log: log, height: activityH }), p.issues && _jsx(IssuesPanel, { checks: state.checks, height: issuesH })] })), !sidebarVisible && !mainVisible && (_jsx(Box, { height: bodyRows, justifyContent: "center", alignItems: "center", children: _jsx(Text, { dimColor: true, children: "All panels hidden. Press c to configure." }) }))] }), _jsxs(Box, { paddingX: 1, justifyContent: "space-between", children: [_jsx(Text, { dimColor: true, children: "q/Esc quit \u00B7 r rescan \u00B7 c settings" }), monCfg.alertBelow > 0 && (_jsxs(Text, { dimColor: true, children: ["alert <", monCfg.alertBelow, " \u00B7 drop \u2265", monCfg.alertDrop] }))] })] }));
|
|
400
|
+
return (_jsxs(Box, { flexDirection: "column", height: rows, children: [_jsxs(Box, { paddingX: 1, justifyContent: "space-between", children: [_jsxs(Text, { children: [_jsx(Text, { color: "magenta", bold: true, children: "vcqa monitor" }), _jsxs(Text, { dimColor: true, children: [" v", VERSION] })] }), _jsxs(Text, { children: [_jsx(Text, { bold: true, children: proj }), _jsxs(Text, { dimColor: true, children: [" ", stack.language, "/", stack.framework, workspace.isMonorepo ? ` · ${workspace.tool}` : ""] })] }), _jsxs(Text, { children: [state.score > 0 && _jsxs(Text, { color: gc(state.grade), bold: true, children: [state.grade, " ", state.score, " "] }), _jsx(Text, { dimColor: true, children: state.scanning ? "⟳ scanning" : "● watching" })] })] }), _jsx(Box, { paddingX: 1, gap: 2, height: 1, children: state.scanCount > 0 && (_jsxs(_Fragment, { children: [_jsxs(Text, { children: [_jsx(Text, { dimColor: true, children: "E " }), _jsx(Text, { color: "red", bold: true, children: errorCount })] }), _jsxs(Text, { children: [_jsx(Text, { dimColor: true, children: "W " }), _jsx(Text, { color: "yellow", bold: true, children: warnCount })] }), _jsxs(Text, { children: [_jsx(Text, { dimColor: true, children: "checks " }), _jsx(Text, { bold: true, children: activeCount })] }), _jsxs(Text, { children: [_jsx(Text, { dimColor: true, children: "scan " }), _jsxs(Text, { children: [state.duration, "ms"] })] }), state.scores.length >= 2 && _jsx(Text, { color: "cyan", children: spark(state.scores) })] })) }), _jsxs(Box, { height: bodyRows, children: [sidebarVisible && (_jsxs(Box, { flexDirection: "column", width: 26, children: [p.score && _jsx(ScorePanel, { state: state, height: scoreH }), p.checks && _jsx(ChecksPanel, { checks: state.checks, height: checksH })] })), mainVisible && (_jsxs(Box, { flexDirection: "column", flexGrow: 1, children: [p.activity && _jsx(ActivityPanel, { log: log, height: activityH }), p.issues && _jsx(IssuesPanel, { checks: state.checks, height: issuesH })] })), !sidebarVisible && !mainVisible && (_jsx(Box, { height: bodyRows, justifyContent: "center", alignItems: "center", children: _jsx(Text, { dimColor: true, children: "All panels hidden. Press c to configure." }) }))] }), _jsxs(Box, { paddingX: 1, justifyContent: "space-between", children: [_jsx(Text, { dimColor: true, children: "q/Esc quit \u00B7 r rescan \u00B7 t trends \u00B7 c settings" }), monCfg.alertBelow > 0 && (_jsxs(Text, { dimColor: true, children: ["alert <", monCfg.alertBelow, " \u00B7 drop \u2265", monCfg.alertDrop] }))] })] }));
|
|
346
401
|
}
|
|
347
402
|
// ── Entry ──
|
|
348
403
|
export async function startMonitor(cwd) {
|