@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.
Files changed (56) hide show
  1. package/README.md +27 -32
  2. package/bin/registry.js +208 -343
  3. package/bin/runners/context/generators/mcp.js +18 -0
  4. package/bin/runners/context/index.js +72 -4
  5. package/bin/runners/context/proof-context.js +293 -1
  6. package/bin/runners/context/security-scanner.js +311 -73
  7. package/bin/runners/lib/analyzers.js +607 -20
  8. package/bin/runners/lib/detectors-v2.js +172 -15
  9. package/bin/runners/lib/entitlements-v2.js +48 -1
  10. package/bin/runners/lib/evidence-pack.js +678 -0
  11. package/bin/runners/lib/html-proof-report.js +913 -0
  12. package/bin/runners/lib/missions/plan.js +231 -41
  13. package/bin/runners/lib/missions/templates.js +125 -0
  14. package/bin/runners/lib/scan-output.js +492 -253
  15. package/bin/runners/lib/ship-output.js +901 -641
  16. package/bin/runners/runCheckpoint.js +44 -3
  17. package/bin/runners/runContext.d.ts +4 -0
  18. package/bin/runners/runContext.js +2 -3
  19. package/bin/runners/runDoctor.js +11 -4
  20. package/bin/runners/runFix.js +51 -341
  21. package/bin/runners/runInit.js +37 -20
  22. package/bin/runners/runPolish.d.ts +4 -0
  23. package/bin/runners/runPolish.js +608 -29
  24. package/bin/runners/runProve.js +210 -25
  25. package/bin/runners/runReality.js +861 -107
  26. package/bin/runners/runScan.js +238 -4
  27. package/bin/runners/runShip.js +19 -3
  28. package/bin/runners/runWatch.js +25 -5
  29. package/bin/vibecheck.js +35 -47
  30. package/mcp-server/consolidated-tools.js +408 -42
  31. package/mcp-server/index.js +152 -15
  32. package/mcp-server/package.json +1 -1
  33. package/mcp-server/proof-tools.js +571 -0
  34. package/mcp-server/tier-auth.js +22 -19
  35. package/mcp-server/tools-v3.js +744 -0
  36. package/mcp-server/truth-firewall-tools.js +190 -4
  37. package/package.json +3 -1
  38. package/bin/runners/runBadge.js +0 -916
  39. package/bin/runners/runContracts.js +0 -105
  40. package/bin/runners/runCtx.js +0 -680
  41. package/bin/runners/runCtxDiff.js +0 -301
  42. package/bin/runners/runCtxGuard.js +0 -176
  43. package/bin/runners/runCtxSync.js +0 -116
  44. package/bin/runners/runExport.js +0 -93
  45. package/bin/runners/runGraph.js +0 -454
  46. package/bin/runners/runInstall.js +0 -273
  47. package/bin/runners/runLabs.js +0 -341
  48. package/bin/runners/runLaunch.js +0 -181
  49. package/bin/runners/runPR.js +0 -255
  50. package/bin/runners/runPermissions.js +0 -310
  51. package/bin/runners/runPreflight.js +0 -580
  52. package/bin/runners/runReplay.js +0 -499
  53. package/bin/runners/runSecurity.js +0 -92
  54. package/bin/runners/runShare.js +0 -212
  55. package/bin/runners/runStatus.js +0 -102
  56. package/bin/runners/runVerify.js +0 -272
@@ -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 };