@vibecheckai/cli 3.1.6 → 3.2.0
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/README.md +27 -32
- package/bin/registry.js +208 -343
- package/bin/runners/context/generators/mcp.js +18 -0
- package/bin/runners/context/index.js +72 -4
- package/bin/runners/context/proof-context.js +293 -1
- package/bin/runners/context/security-scanner.js +311 -73
- package/bin/runners/lib/analyzers.js +607 -20
- package/bin/runners/lib/detectors-v2.js +172 -15
- package/bin/runners/lib/entitlements-v2.js +48 -1
- package/bin/runners/lib/evidence-pack.js +678 -0
- package/bin/runners/lib/html-proof-report.js +913 -0
- package/bin/runners/lib/missions/plan.js +231 -41
- package/bin/runners/lib/missions/templates.js +125 -0
- package/bin/runners/lib/scan-output.js +492 -253
- package/bin/runners/lib/ship-output.js +901 -641
- package/bin/runners/runCheckpoint.js +44 -3
- package/bin/runners/runContext.d.ts +4 -0
- package/bin/runners/runContext.js +2 -3
- package/bin/runners/runDoctor.js +11 -4
- package/bin/runners/runFix.js +51 -341
- package/bin/runners/runInit.js +37 -20
- package/bin/runners/runPolish.d.ts +4 -0
- package/bin/runners/runPolish.js +608 -29
- package/bin/runners/runProve.js +210 -25
- package/bin/runners/runReality.js +861 -107
- package/bin/runners/runScan.js +238 -4
- package/bin/runners/runShip.js +19 -3
- package/bin/runners/runWatch.js +25 -5
- package/bin/vibecheck.js +35 -47
- package/mcp-server/consolidated-tools.js +408 -42
- package/mcp-server/index.js +152 -15
- package/mcp-server/package.json +1 -1
- package/mcp-server/proof-tools.js +571 -0
- package/mcp-server/tier-auth.js +22 -19
- package/mcp-server/tools-v3.js +744 -0
- package/mcp-server/truth-firewall-tools.js +190 -4
- package/package.json +3 -1
- package/bin/runners/runBadge.js +0 -916
- package/bin/runners/runContracts.js +0 -105
- package/bin/runners/runCtx.js +0 -680
- package/bin/runners/runCtxDiff.js +0 -301
- package/bin/runners/runCtxGuard.js +0 -176
- package/bin/runners/runCtxSync.js +0 -116
- package/bin/runners/runExport.js +0 -93
- package/bin/runners/runGraph.js +0 -454
- package/bin/runners/runInstall.js +0 -273
- package/bin/runners/runLabs.js +0 -341
- package/bin/runners/runLaunch.js +0 -181
- package/bin/runners/runPR.js +0 -255
- package/bin/runners/runPermissions.js +0 -310
- package/bin/runners/runPreflight.js +0 -580
- package/bin/runners/runReplay.js +0 -499
- package/bin/runners/runSecurity.js +0 -92
- package/bin/runners/runShare.js +0 -212
- package/bin/runners/runStatus.js +0 -102
- package/bin/runners/runVerify.js +0 -272
package/bin/runners/runGraph.js
DELETED
|
@@ -1,454 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* vibecheck graph - Reality Proof Graph
|
|
3
|
-
*
|
|
4
|
-
* ═══════════════════════════════════════════════════════════════════════════════
|
|
5
|
-
* ENTERPRISE EDITION - World-Class Terminal Experience
|
|
6
|
-
* ═══════════════════════════════════════════════════════════════════════════════
|
|
7
|
-
*
|
|
8
|
-
* Builds and visualizes the complete causal chain:
|
|
9
|
-
* UI action → client function → network call → server route → handler → DB/external
|
|
10
|
-
*
|
|
11
|
-
* Identifies broken edges that indicate:
|
|
12
|
-
* - Routes referenced but don't exist
|
|
13
|
-
* - Routes that fail at runtime
|
|
14
|
-
* - UI showing success but server returning errors
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
"use strict";
|
|
18
|
-
|
|
19
|
-
const path = require("path");
|
|
20
|
-
const fs = require("fs");
|
|
21
|
-
const { buildTruthpack, loadTruthpack } = require("./lib/truth");
|
|
22
|
-
const {
|
|
23
|
-
extractStaticGraph,
|
|
24
|
-
buildProofGraph,
|
|
25
|
-
getFindingsFromGraph,
|
|
26
|
-
renderGraphHtml,
|
|
27
|
-
renderGraphMermaid,
|
|
28
|
-
generateFetchWrapper,
|
|
29
|
-
collectRequests,
|
|
30
|
-
buildRuntimeEdges,
|
|
31
|
-
mergeRuntimeResults
|
|
32
|
-
} = require("./lib/graph");
|
|
33
|
-
|
|
34
|
-
// Entitlements enforcement
|
|
35
|
-
const entitlements = require("./lib/entitlements-v2");
|
|
36
|
-
|
|
37
|
-
// ═══════════════════════════════════════════════════════════════════════════════
|
|
38
|
-
// ADVANCED TERMINAL - ANSI CODES & UTILITIES
|
|
39
|
-
// ═══════════════════════════════════════════════════════════════════════════════
|
|
40
|
-
|
|
41
|
-
const c = {
|
|
42
|
-
reset: '\x1b[0m',
|
|
43
|
-
bold: '\x1b[1m',
|
|
44
|
-
dim: '\x1b[2m',
|
|
45
|
-
italic: '\x1b[3m',
|
|
46
|
-
red: '\x1b[31m',
|
|
47
|
-
green: '\x1b[32m',
|
|
48
|
-
yellow: '\x1b[33m',
|
|
49
|
-
blue: '\x1b[34m',
|
|
50
|
-
magenta: '\x1b[35m',
|
|
51
|
-
cyan: '\x1b[36m',
|
|
52
|
-
white: '\x1b[37m',
|
|
53
|
-
gray: '\x1b[90m',
|
|
54
|
-
clearLine: '\x1b[2K',
|
|
55
|
-
hideCursor: '\x1b[?25l',
|
|
56
|
-
showCursor: '\x1b[?25h',
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
const rgb = (r, g, b) => `\x1b[38;2;${r};${g};${b}m`;
|
|
60
|
-
const bgRgb = (r, g, b) => `\x1b[48;2;${r};${g};${b}m`;
|
|
61
|
-
|
|
62
|
-
const colors = {
|
|
63
|
-
gradient1: rgb(255, 100, 150),
|
|
64
|
-
gradient2: rgb(255, 80, 170),
|
|
65
|
-
gradient3: rgb(255, 60, 190),
|
|
66
|
-
shipGreen: rgb(0, 255, 150),
|
|
67
|
-
warnAmber: rgb(255, 200, 0),
|
|
68
|
-
blockRed: rgb(255, 80, 80),
|
|
69
|
-
accent: rgb(255, 100, 150),
|
|
70
|
-
muted: rgb(120, 120, 140),
|
|
71
|
-
node: rgb(100, 200, 255),
|
|
72
|
-
edge: rgb(150, 255, 150),
|
|
73
|
-
broken: rgb(255, 100, 100),
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
// ═══════════════════════════════════════════════════════════════════════════════
|
|
77
|
-
// PREMIUM BANNER
|
|
78
|
-
// ═══════════════════════════════════════════════════════════════════════════════
|
|
79
|
-
|
|
80
|
-
const GRAPH_BANNER = `
|
|
81
|
-
${rgb(255, 100, 150)} ██████╗ ██████╗ █████╗ ██████╗ ██╗ ██╗${c.reset}
|
|
82
|
-
${rgb(255, 80, 170)} ██╔════╝ ██╔══██╗██╔══██╗██╔══██╗██║ ██║${c.reset}
|
|
83
|
-
${rgb(255, 60, 190)} ██║ ███╗██████╔╝███████║██████╔╝███████║${c.reset}
|
|
84
|
-
${rgb(255, 40, 210)} ██║ ██║██╔══██╗██╔══██║██╔═══╝ ██╔══██║${c.reset}
|
|
85
|
-
${rgb(255, 20, 230)} ╚██████╔╝██║ ██║██║ ██║██║ ██║ ██║${c.reset}
|
|
86
|
-
${rgb(255, 0, 255)} ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝${c.reset}
|
|
87
|
-
`;
|
|
88
|
-
|
|
89
|
-
const BANNER_FULL = `
|
|
90
|
-
${rgb(255, 100, 150)} ██╗ ██╗██╗██████╗ ███████╗ ██████╗██╗ ██╗███████╗ ██████╗██╗ ██╗${c.reset}
|
|
91
|
-
${rgb(255, 90, 160)} ██║ ██║██║██╔══██╗██╔════╝██╔════╝██║ ██║██╔════╝██╔════╝██║ ██╔╝${c.reset}
|
|
92
|
-
${rgb(255, 80, 170)} ██║ ██║██║██████╔╝█████╗ ██║ ███████║█████╗ ██║ █████╔╝ ${c.reset}
|
|
93
|
-
${rgb(255, 60, 190)} ╚██╗ ██╔╝██║██╔══██╗██╔══╝ ██║ ██╔══██║██╔══╝ ██║ ██╔═██╗ ${c.reset}
|
|
94
|
-
${rgb(255, 40, 210)} ╚████╔╝ ██║██████╔╝███████╗╚██████╗██║ ██║███████╗╚██████╗██║ ██╗${c.reset}
|
|
95
|
-
${rgb(255, 20, 230)} ╚═══╝ ╚═╝╚═════╝ ╚══════╝ ╚═════╝╚═╝ ╚═╝╚══════╝ ╚═════╝╚═╝ ╚═╝${c.reset}
|
|
96
|
-
|
|
97
|
-
${c.dim} ┌─────────────────────────────────────────────────────────────────────┐${c.reset}
|
|
98
|
-
${c.dim} │${c.reset} ${rgb(255, 100, 150)}📊${c.reset} ${c.bold}GRAPH${c.reset} ${c.dim}•${c.reset} ${rgb(200, 200, 200)}Proof Visualization${c.reset} ${c.dim}•${c.reset} ${rgb(150, 150, 150)}Causal Chain${c.reset} ${c.dim}•${c.reset} ${rgb(100, 100, 100)}Edge Analysis${c.reset} ${c.dim}│${c.reset}
|
|
99
|
-
${c.dim} └─────────────────────────────────────────────────────────────────────┘${c.reset}
|
|
100
|
-
`;
|
|
101
|
-
|
|
102
|
-
const ICONS = {
|
|
103
|
-
graph: '📊',
|
|
104
|
-
check: '✓',
|
|
105
|
-
cross: '✗',
|
|
106
|
-
warning: '⚠',
|
|
107
|
-
arrow: '→',
|
|
108
|
-
bullet: '•',
|
|
109
|
-
node: '◉',
|
|
110
|
-
edge: '─',
|
|
111
|
-
broken: '╳',
|
|
112
|
-
sparkle: '✨',
|
|
113
|
-
file: '📄',
|
|
114
|
-
folder: '📁',
|
|
115
|
-
view: '👁️',
|
|
116
|
-
};
|
|
117
|
-
|
|
118
|
-
const SPINNER_DOTS = ['⣾', '⣽', '⣻', '⢿', '⡿', '⣟', '⣯', '⣷'];
|
|
119
|
-
let spinnerIndex = 0;
|
|
120
|
-
let spinnerInterval = null;
|
|
121
|
-
let spinnerStartTime = null;
|
|
122
|
-
|
|
123
|
-
function formatDuration(ms) {
|
|
124
|
-
if (ms < 1000) return `${ms}ms`;
|
|
125
|
-
return `${(ms / 1000).toFixed(1)}s`;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
function startSpinner(message) {
|
|
129
|
-
spinnerStartTime = Date.now();
|
|
130
|
-
process.stdout.write(c.hideCursor);
|
|
131
|
-
spinnerInterval = setInterval(() => {
|
|
132
|
-
const elapsed = formatDuration(Date.now() - spinnerStartTime);
|
|
133
|
-
process.stdout.write(`\r${c.clearLine} ${colors.accent}${SPINNER_DOTS[spinnerIndex]}${c.reset} ${message} ${c.dim}${elapsed}${c.reset}`);
|
|
134
|
-
spinnerIndex = (spinnerIndex + 1) % SPINNER_DOTS.length;
|
|
135
|
-
}, 80);
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
function stopSpinner(message, success = true) {
|
|
139
|
-
if (spinnerInterval) {
|
|
140
|
-
clearInterval(spinnerInterval);
|
|
141
|
-
spinnerInterval = null;
|
|
142
|
-
}
|
|
143
|
-
const elapsed = spinnerStartTime ? formatDuration(Date.now() - spinnerStartTime) : '';
|
|
144
|
-
const icon = success ? `${colors.shipGreen}${ICONS.check}${c.reset}` : `${colors.blockRed}${ICONS.cross}${c.reset}`;
|
|
145
|
-
process.stdout.write(`\r${c.clearLine} ${icon} ${message} ${c.dim}${elapsed}${c.reset}\n`);
|
|
146
|
-
process.stdout.write(c.showCursor);
|
|
147
|
-
spinnerStartTime = null;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
function printDivider(char = '─', width = 69, color = c.dim) {
|
|
151
|
-
console.log(`${color} ${char.repeat(width)}${c.reset}`);
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
function printSection(title, icon = '◆') {
|
|
155
|
-
console.log();
|
|
156
|
-
console.log(` ${colors.accent}${icon}${c.reset} ${c.bold}${title}${c.reset}`);
|
|
157
|
-
printDivider();
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
function progressBar(percent, width = 25) {
|
|
161
|
-
const filled = Math.round((percent / 100) * width);
|
|
162
|
-
const empty = width - filled;
|
|
163
|
-
const color = percent >= 80 ? colors.shipGreen : percent >= 50 ? colors.warnAmber : colors.blockRed;
|
|
164
|
-
return `${color}${'█'.repeat(filled)}${c.dim}${'░'.repeat(empty)}${c.reset}`;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
function ensureDir(p) {
|
|
168
|
-
fs.mkdirSync(p, { recursive: true });
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
async function runGraph(args) {
|
|
172
|
-
const opts = parseArgs(args);
|
|
173
|
-
const startTime = Date.now();
|
|
174
|
-
|
|
175
|
-
if (opts.help) {
|
|
176
|
-
printHelp();
|
|
177
|
-
return 0;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
// TIER ENFORCEMENT: COMPLETE only
|
|
181
|
-
const access = await entitlements.enforce("graph", {
|
|
182
|
-
projectPath: opts.path || process.cwd(),
|
|
183
|
-
silent: false,
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
if (!access.allowed) {
|
|
187
|
-
console.log(`\n${c.yellow}Tip:${c.reset} The graph command requires COMPLETE tier for advanced proof graph visualization.`);
|
|
188
|
-
return entitlements.EXIT_FEATURE_NOT_ALLOWED;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
const root = path.resolve(opts.path || process.cwd());
|
|
192
|
-
const projectName = path.basename(root);
|
|
193
|
-
const outDir = path.join(root, ".vibecheck", "graph");
|
|
194
|
-
ensureDir(outDir);
|
|
195
|
-
|
|
196
|
-
// Print banner
|
|
197
|
-
console.log(BANNER_FULL);
|
|
198
|
-
console.log(` ${c.dim}Project:${c.reset} ${c.bold}${projectName}${c.reset}`);
|
|
199
|
-
console.log(` ${c.dim}Path:${c.reset} ${root}`);
|
|
200
|
-
console.log();
|
|
201
|
-
|
|
202
|
-
// Load or build truthpack
|
|
203
|
-
startSpinner('Loading truthpack...');
|
|
204
|
-
let truthpack = loadTruthpack(root);
|
|
205
|
-
if (!truthpack) {
|
|
206
|
-
stopSpinner('Truthpack not found, building...', true);
|
|
207
|
-
startSpinner('Building truthpack...');
|
|
208
|
-
truthpack = await buildTruthpack({ repoRoot: root, fastifyEntry: opts.fastifyEntry });
|
|
209
|
-
}
|
|
210
|
-
stopSpinner('Truthpack loaded', true);
|
|
211
|
-
|
|
212
|
-
// Extract static graph
|
|
213
|
-
startSpinner('Extracting static edges...');
|
|
214
|
-
const staticData = await extractStaticGraph(root, truthpack);
|
|
215
|
-
stopSpinner(`Extracted ${staticData.edges?.length || 0} edges`, true);
|
|
216
|
-
|
|
217
|
-
// Build proof graph
|
|
218
|
-
startSpinner('Building proof graph...');
|
|
219
|
-
let graph = buildProofGraph({
|
|
220
|
-
nodes: staticData.nodes,
|
|
221
|
-
edges: staticData.edges,
|
|
222
|
-
meta: { repoRoot: root }
|
|
223
|
-
});
|
|
224
|
-
stopSpinner(`Built graph with ${graph.nodes?.length || 0} nodes`, true);
|
|
225
|
-
|
|
226
|
-
// Runtime verification (optional)
|
|
227
|
-
if (opts.runtime && opts.url) {
|
|
228
|
-
startSpinner(`Collecting runtime edges from ${opts.url}...`);
|
|
229
|
-
|
|
230
|
-
try {
|
|
231
|
-
const runtimeEdges = await collectRuntimeEdges(opts.url, graph, opts);
|
|
232
|
-
graph = mergeRuntimeResults(graph, runtimeEdges);
|
|
233
|
-
stopSpinner('Runtime edges collected', true);
|
|
234
|
-
} catch (e) {
|
|
235
|
-
stopSpinner('Runtime collection failed', false);
|
|
236
|
-
console.log(` ${c.dim}${e.message}${c.reset}`);
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
// Get findings from broken edges
|
|
241
|
-
const findings = getFindingsFromGraph(graph);
|
|
242
|
-
|
|
243
|
-
// Write outputs
|
|
244
|
-
startSpinner('Writing outputs...');
|
|
245
|
-
fs.writeFileSync(path.join(outDir, "graph.json"), JSON.stringify(graph, null, 2), "utf8");
|
|
246
|
-
|
|
247
|
-
if (!opts.jsonOnly) {
|
|
248
|
-
const html = renderGraphHtml(graph);
|
|
249
|
-
fs.writeFileSync(path.join(outDir, "graph.html"), html, "utf8");
|
|
250
|
-
|
|
251
|
-
const mermaid = renderGraphMermaid(graph);
|
|
252
|
-
fs.writeFileSync(path.join(outDir, "graph.mmd"), mermaid, "utf8");
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
if (findings.length > 0) {
|
|
256
|
-
fs.writeFileSync(path.join(outDir, "broken-edges.json"), JSON.stringify(findings, null, 2), "utf8");
|
|
257
|
-
}
|
|
258
|
-
stopSpinner('Outputs written', true);
|
|
259
|
-
|
|
260
|
-
// Print summary
|
|
261
|
-
const duration = Date.now() - startTime;
|
|
262
|
-
const coverage = graph.coverage?.percent || 0;
|
|
263
|
-
|
|
264
|
-
printSection('GRAPH SUMMARY', ICONS.graph);
|
|
265
|
-
console.log();
|
|
266
|
-
console.log(` ${colors.node}${ICONS.node}${c.reset} ${c.bold}Nodes:${c.reset} ${graph.nodes?.length || 0}`);
|
|
267
|
-
console.log(` ${colors.edge}${ICONS.edge}${c.reset} ${c.bold}Valid edges:${c.reset} ${colors.shipGreen}${graph.edges?.length || 0}${c.reset}`);
|
|
268
|
-
console.log(` ${colors.broken}${ICONS.broken}${c.reset} ${c.bold}Broken edges:${c.reset} ${graph.brokenEdges?.length > 0 ? colors.blockRed : colors.shipGreen}${graph.brokenEdges?.length || 0}${c.reset}`);
|
|
269
|
-
console.log();
|
|
270
|
-
console.log(` ${c.bold}Coverage:${c.reset} ${progressBar(coverage)} ${coverage}%`);
|
|
271
|
-
console.log(` ${c.bold}Duration:${c.reset} ${formatDuration(duration)}`);
|
|
272
|
-
|
|
273
|
-
if (opts.broken || findings.length > 0) {
|
|
274
|
-
printSection('BROKEN EDGES', ICONS.broken);
|
|
275
|
-
console.log();
|
|
276
|
-
|
|
277
|
-
if (findings.length === 0) {
|
|
278
|
-
console.log(` ${colors.shipGreen}${ICONS.check}${c.reset} No broken edges found`);
|
|
279
|
-
} else {
|
|
280
|
-
for (const f of findings.slice(0, 10)) {
|
|
281
|
-
const icon = f.severity === "BLOCK" ? `${colors.blockRed}●` : `${colors.warnAmber}●`;
|
|
282
|
-
console.log(` ${icon}${c.reset} ${f.title}`);
|
|
283
|
-
}
|
|
284
|
-
if (findings.length > 10) {
|
|
285
|
-
console.log(` ${c.dim}... and ${findings.length - 10} more${c.reset}`);
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
printSection('OUTPUT', ICONS.folder);
|
|
291
|
-
console.log();
|
|
292
|
-
console.log(` ${ICONS.file} ${colors.accent}graph.json${c.reset} ${c.dim}Machine-readable graph${c.reset}`);
|
|
293
|
-
if (!opts.jsonOnly) {
|
|
294
|
-
console.log(` ${ICONS.view} ${colors.accent}graph.html${c.reset} ${c.dim}Interactive visualization${c.reset}`);
|
|
295
|
-
console.log(` ${ICONS.file} ${colors.accent}graph.mmd${c.reset} ${c.dim}Mermaid diagram${c.reset}`);
|
|
296
|
-
}
|
|
297
|
-
if (findings.length > 0) {
|
|
298
|
-
console.log(` ${ICONS.warning} ${colors.accent}broken-edges.json${c.reset} ${c.dim}Broken edges as findings${c.reset}`);
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
if (opts.view && !opts.jsonOnly) {
|
|
302
|
-
const htmlPath = path.join(outDir, "graph.html");
|
|
303
|
-
console.log();
|
|
304
|
-
console.log(` ${c.dim}Opening visualization...${c.reset}`);
|
|
305
|
-
|
|
306
|
-
const { exec } = require("child_process");
|
|
307
|
-
const cmd = process.platform === "win32" ? `start "" "${htmlPath}"` :
|
|
308
|
-
process.platform === "darwin" ? `open "${htmlPath}"` :
|
|
309
|
-
`xdg-open "${htmlPath}"`;
|
|
310
|
-
exec(cmd);
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
const blocks = findings.filter(f => f.severity === "BLOCK").length;
|
|
314
|
-
const warns = findings.filter(f => f.severity === "WARN").length;
|
|
315
|
-
|
|
316
|
-
// Verdict card
|
|
317
|
-
console.log();
|
|
318
|
-
const verdictColor = blocks ? colors.blockRed : warns ? colors.warnAmber : colors.shipGreen;
|
|
319
|
-
const verdictIcon = blocks ? '🛑' : warns ? '⚠️' : '✅';
|
|
320
|
-
const verdictText = blocks ? 'BLOCK' : warns ? 'WARN' : 'CLEAN';
|
|
321
|
-
console.log(` ${verdictColor}${c.bold}${verdictIcon} ${verdictText}${c.reset}`);
|
|
322
|
-
console.log();
|
|
323
|
-
|
|
324
|
-
return blocks ? 2 : warns ? 1 : 0;
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
async function collectRuntimeEdges(url, staticGraph, opts) {
|
|
328
|
-
let chromium;
|
|
329
|
-
try {
|
|
330
|
-
chromium = require("playwright").chromium;
|
|
331
|
-
} catch {
|
|
332
|
-
throw new Error("Playwright not installed. Run: npm i -D playwright");
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
const browser = await chromium.launch({ headless: !opts.headed });
|
|
336
|
-
const context = await browser.newContext();
|
|
337
|
-
const page = await context.newPage();
|
|
338
|
-
|
|
339
|
-
// Inject fetch wrapper
|
|
340
|
-
await page.addInitScript(generateFetchWrapper());
|
|
341
|
-
|
|
342
|
-
// Navigate and interact
|
|
343
|
-
await page.goto(url, { waitUntil: "domcontentloaded" });
|
|
344
|
-
await page.waitForLoadState("networkidle", { timeout: 10000 }).catch(() => {});
|
|
345
|
-
|
|
346
|
-
// Click some interactive elements to trigger requests
|
|
347
|
-
const buttons = await page.locator("button, a[href^='/'], [role='button']").all();
|
|
348
|
-
for (const btn of buttons.slice(0, 10)) {
|
|
349
|
-
try {
|
|
350
|
-
await btn.click({ timeout: 1000 });
|
|
351
|
-
await page.waitForTimeout(500);
|
|
352
|
-
} catch {
|
|
353
|
-
// Ignore click failures
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
// Collect requests
|
|
358
|
-
const requests = await collectRequests(page);
|
|
359
|
-
|
|
360
|
-
await browser.close();
|
|
361
|
-
|
|
362
|
-
// Build runtime edges
|
|
363
|
-
return buildRuntimeEdges(requests, staticGraph);
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
function parseArgs(args) {
|
|
367
|
-
const opts = {
|
|
368
|
-
path: process.cwd(),
|
|
369
|
-
url: null,
|
|
370
|
-
runtime: false,
|
|
371
|
-
view: false,
|
|
372
|
-
broken: false,
|
|
373
|
-
jsonOnly: false,
|
|
374
|
-
headed: false,
|
|
375
|
-
fastifyEntry: null,
|
|
376
|
-
help: false,
|
|
377
|
-
};
|
|
378
|
-
|
|
379
|
-
for (let i = 0; i < args.length; i++) {
|
|
380
|
-
const arg = args[i];
|
|
381
|
-
if (arg === "--build") continue; // Default behavior
|
|
382
|
-
else if (arg === "--runtime") opts.runtime = true;
|
|
383
|
-
else if (arg === "--url") opts.url = args[++i];
|
|
384
|
-
else if (arg === "--view") opts.view = true;
|
|
385
|
-
else if (arg === "--broken") opts.broken = true;
|
|
386
|
-
else if (arg === "--json") opts.jsonOnly = true;
|
|
387
|
-
else if (arg === "--headed") opts.headed = true;
|
|
388
|
-
else if (arg === "--fastify-entry") opts.fastifyEntry = args[++i];
|
|
389
|
-
else if (arg === "--path" || arg === "-p") opts.path = args[++i];
|
|
390
|
-
else if (arg === "--help" || arg === "-h") opts.help = true;
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
return opts;
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
function printHelp() {
|
|
397
|
-
console.log(BANNER_FULL);
|
|
398
|
-
console.log(`
|
|
399
|
-
${c.bold}Usage:${c.reset} vibecheck graph [options]
|
|
400
|
-
|
|
401
|
-
${c.bold}Reality Proof Graph${c.reset} — Visualize the complete causal chain from UI to database.
|
|
402
|
-
|
|
403
|
-
${c.bold}Modes:${c.reset}
|
|
404
|
-
${colors.accent}vibecheck graph${c.reset} ${c.dim}Build static graph${c.reset}
|
|
405
|
-
${colors.accent}vibecheck graph --runtime --url http://...${c.reset} ${c.dim}Add runtime edges${c.reset}
|
|
406
|
-
${colors.accent}vibecheck graph --view${c.reset} ${c.dim}Open visualization${c.reset}
|
|
407
|
-
${colors.accent}vibecheck graph --broken${c.reset} ${c.dim}List broken edges${c.reset}
|
|
408
|
-
|
|
409
|
-
${c.bold}Options:${c.reset}
|
|
410
|
-
${colors.accent}--build${c.reset} Build static graph ${c.dim}(default)${c.reset}
|
|
411
|
-
${colors.accent}--runtime${c.reset} Collect runtime edges via Playwright
|
|
412
|
-
${colors.accent}--url <url>${c.reset} Target URL for runtime collection
|
|
413
|
-
${colors.accent}--view${c.reset} Open interactive HTML visualization
|
|
414
|
-
${colors.accent}--broken${c.reset} Show only broken edges
|
|
415
|
-
${colors.accent}--json${c.reset} Output JSON only ${c.dim}(no HTML/Mermaid)${c.reset}
|
|
416
|
-
${colors.accent}--headed${c.reset} Run browser in headed mode
|
|
417
|
-
${colors.accent}--fastify-entry${c.reset} Fastify entry file ${c.dim}(e.g. src/server.ts)${c.reset}
|
|
418
|
-
${colors.accent}--path, -p${c.reset} Project path ${c.dim}(default: current directory)${c.reset}
|
|
419
|
-
${colors.accent}--help, -h${c.reset} Show this help
|
|
420
|
-
|
|
421
|
-
${c.bold}Graph Nodes:${c.reset}
|
|
422
|
-
${colors.node}◉${c.reset} UI Action ${c.dim}onClick, onSubmit, etc.${c.reset}
|
|
423
|
-
${colors.node}◉${c.reset} Client Function ${c.dim}Functions that make network calls${c.reset}
|
|
424
|
-
${colors.node}◉${c.reset} Network Call ${c.dim}fetch(), axios.*()${c.reset}
|
|
425
|
-
${colors.node}◉${c.reset} Server Route ${c.dim}Next API routes, Fastify routes${c.reset}
|
|
426
|
-
${colors.node}◉${c.reset} Handler ${c.dim}Route handler functions${c.reset}
|
|
427
|
-
${colors.node}◉${c.reset} DB Call ${c.dim}Prisma, raw SQL${c.reset}
|
|
428
|
-
${colors.node}◉${c.reset} External Call ${c.dim}Stripe, GitHub, etc.${c.reset}
|
|
429
|
-
|
|
430
|
-
${c.bold}Broken Edge Types:${c.reset}
|
|
431
|
-
${colors.blockRed}╳${c.reset} MissingRoute ${c.dim}Route referenced but doesn't exist${c.reset}
|
|
432
|
-
${colors.blockRed}╳${c.reset} RuntimeFailure ${c.dim}Route returns 4xx/5xx at runtime${c.reset}
|
|
433
|
-
${colors.blockRed}╳${c.reset} CausalContradiction ${c.dim}UI shows success but server returned error${c.reset}
|
|
434
|
-
|
|
435
|
-
${c.bold}Output:${c.reset}
|
|
436
|
-
${c.dim}.vibecheck/graph/${c.reset}
|
|
437
|
-
${colors.accent}graph.json${c.reset} ${c.dim}Machine-readable graph${c.reset}
|
|
438
|
-
${colors.accent}graph.html${c.reset} ${c.dim}Interactive D3 visualization${c.reset}
|
|
439
|
-
${colors.accent}graph.mmd${c.reset} ${c.dim}Mermaid diagram${c.reset}
|
|
440
|
-
${colors.accent}broken-edges.json${c.reset} ${c.dim}Broken edges as findings${c.reset}
|
|
441
|
-
|
|
442
|
-
${c.bold}Examples:${c.reset}
|
|
443
|
-
${c.dim}# Build + open visualization${c.reset}
|
|
444
|
-
vibecheck graph --view
|
|
445
|
-
|
|
446
|
-
${c.dim}# Runtime verification with Playwright${c.reset}
|
|
447
|
-
vibecheck graph --runtime --url http://localhost:3000 --view
|
|
448
|
-
|
|
449
|
-
${c.dim}# CI mode: just broken edges as JSON${c.reset}
|
|
450
|
-
vibecheck graph --broken --json
|
|
451
|
-
`);
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
module.exports = { runGraph };
|