@vibecheckai/cli 3.1.8 → 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 (36) hide show
  1. package/bin/registry.js +106 -116
  2. package/bin/runners/context/generators/mcp.js +18 -0
  3. package/bin/runners/context/index.js +72 -4
  4. package/bin/runners/context/proof-context.js +293 -1
  5. package/bin/runners/context/security-scanner.js +311 -73
  6. package/bin/runners/lib/analyzers.js +607 -20
  7. package/bin/runners/lib/detectors-v2.js +172 -15
  8. package/bin/runners/lib/entitlements-v2.js +48 -1
  9. package/bin/runners/lib/evidence-pack.js +678 -0
  10. package/bin/runners/lib/html-proof-report.js +913 -0
  11. package/bin/runners/lib/missions/plan.js +231 -41
  12. package/bin/runners/lib/missions/templates.js +125 -0
  13. package/bin/runners/lib/scan-output.js +492 -253
  14. package/bin/runners/lib/ship-output.js +901 -641
  15. package/bin/runners/runCheckpoint.js +44 -3
  16. package/bin/runners/runContext.d.ts +4 -0
  17. package/bin/runners/runDoctor.js +10 -2
  18. package/bin/runners/runFix.js +51 -341
  19. package/bin/runners/runInit.js +11 -0
  20. package/bin/runners/runPolish.d.ts +4 -0
  21. package/bin/runners/runPolish.js +608 -29
  22. package/bin/runners/runProve.js +210 -25
  23. package/bin/runners/runReality.js +846 -101
  24. package/bin/runners/runScan.js +238 -4
  25. package/bin/runners/runShip.js +19 -3
  26. package/bin/runners/runWatch.js +14 -1
  27. package/bin/vibecheck.js +32 -2
  28. package/mcp-server/consolidated-tools.js +408 -42
  29. package/mcp-server/index.js +152 -15
  30. package/mcp-server/proof-tools.js +571 -0
  31. package/mcp-server/tier-auth.js +22 -19
  32. package/mcp-server/tools-v3.js +744 -0
  33. package/mcp-server/truth-firewall-tools.js +190 -4
  34. package/package.json +3 -1
  35. package/bin/runners/runInstall.js +0 -281
  36. package/bin/runners/runLabs.js +0 -341
@@ -673,28 +673,44 @@ function printHelp(showBanner = true) {
673
673
  console.log(BANNER_FULL);
674
674
  }
675
675
  console.log(`
676
- ${c.bold}Usage:${c.reset} vibecheck prove [options]
676
+ ${c.bold}Usage:${c.reset} vibecheck prove --url <url> [options]
677
677
 
678
- ${c.bold}One Command Reality Proof${c.reset} — Make it real or keep fixing until it is.
678
+ ${c.bold}One Command Reality Proof${c.reset} — Undeniable evidence that your app works.
679
+
680
+ Produces a shareable proof bundle with ${c.bold}video${c.reset} + ${c.bold}network evidence${c.reset} showing
681
+ real user flows hitting real APIs. Drop it into CI and instantly trust the verdict.
682
+
683
+ ${c.bold}What You Get:${c.reset}
684
+ ${colors.shipGreen}🎬${c.reset} Video recordings of browser sessions
685
+ ${colors.shipGreen}📡${c.reset} Network traffic capture (HAR files)
686
+ ${colors.shipGreen}📊${c.reset} Playwright traces (viewable at trace.playwright.dev)
687
+ ${colors.shipGreen}📄${c.reset} Beautiful HTML proof report
679
688
 
680
689
  ${c.bold}Pipeline:${c.reset}
681
690
  ${colors.step1}1. CTX${c.reset} Refresh truthpack (ground truth)
682
- ${colors.step2}2. REALITY${c.reset} Runtime UI proof ${c.dim}(if --url provided)${c.reset}
691
+ ${colors.step2}2. REALITY${c.reset} Runtime UI proof with video + network capture
683
692
  ${colors.step3}3. SHIP${c.reset} Static + runtime verdict
684
693
  ${colors.step4}4. FIX${c.reset} Auto-fix if BLOCK ${c.dim}(up to N rounds)${c.reset}
685
694
  ${colors.step5}5. VERIFY${c.reset} Re-run to confirm SHIP
686
695
 
687
696
  ${c.bold}Options:${c.reset}
688
- ${colors.accent}--url, -u <url>${c.reset} Base URL for runtime testing
697
+ ${colors.accent}--url, -u <url>${c.reset} Base URL for runtime testing ${c.dim}(required)${c.reset}
689
698
  ${colors.accent}--auth <email:pass>${c.reset} Login credentials for auth verification
690
699
  ${colors.accent}--storage-state <path>${c.reset} Playwright session state file
691
- ${colors.accent}--fastify-entry <path>${c.reset} Fastify entry file for route extraction
700
+ ${colors.accent}--stability-runs <n>${c.reset} Multiple runs for flakiness ${c.dim}(default: 2)${c.reset}
692
701
  ${colors.accent}--max-fix-rounds <n>${c.reset} Max auto-fix attempts ${c.dim}(default: 3)${c.reset}
693
702
  ${colors.accent}--skip-reality${c.reset} Skip runtime crawling (static only)
694
703
  ${colors.accent}--skip-fix${c.reset} Don't auto-fix, just diagnose
695
704
  ${colors.accent}--headed${c.reset} Run browser in headed mode
696
- ${colors.accent}--danger${c.reset} Allow clicking destructive elements
697
- ${colors.accent}--help, -h${c.reset} Show this help
705
+ ${colors.accent}--ci${c.reset} CI-friendly output (GitHub Actions)
706
+
707
+ ${c.bold}Recording Options:${c.reset} ${c.dim}(all enabled by default)${c.reset}
708
+ ${colors.accent}--no-video${c.reset} Disable video recording
709
+ ${colors.accent}--no-trace${c.reset} Disable Playwright traces
710
+ ${colors.accent}--no-har${c.reset} Disable HAR network capture
711
+
712
+ ${c.bold}Evidence Pack:${c.reset}
713
+ ${colors.accent}--bundle${c.reset} Generate shareable evidence pack (zip with videos, traces, screenshots)
698
714
 
699
715
  ${c.bold}Exit Codes:${c.reset}
700
716
  ${colors.shipGreen}0${c.reset} SHIP — Proved real, ready to deploy
@@ -702,17 +718,26 @@ function printHelp(showBanner = true) {
702
718
  ${colors.blockRed}2${c.reset} BLOCK — Blockers found, not proved
703
719
 
704
720
  ${c.bold}Examples:${c.reset}
705
- ${c.dim}# Full proof with runtime testing${c.reset}
721
+ ${c.dim}# Full proof with video + network evidence${c.reset}
706
722
  vibecheck prove --url http://localhost:3000
707
723
 
708
- ${c.dim}# With authentication${c.reset}
724
+ ${c.dim}# With authentication (two-pass verification)${c.reset}
709
725
  vibecheck prove --url http://localhost:3000 --auth user@test.com:pass
710
726
 
711
- ${c.dim}# Static only (no runtime)${c.reset}
712
- vibecheck prove --skip-reality
727
+ ${c.dim}# CI integration (GitHub Actions)${c.reset}
728
+ npx vibecheck prove --url \$APP_URL --ci
729
+
730
+ ${c.dim}# Quick check (no recordings)${c.reset}
731
+ vibecheck prove --url http://localhost:3000 --no-video --no-trace
713
732
 
714
- ${c.dim}# More fix attempts${c.reset}
715
- vibecheck prove --url http://localhost:3000 --max-fix-rounds 5
733
+ ${c.bold}CI Integration:${c.reset}
734
+ ${c.dim}# Add to .github/workflows/test.yml:${c.reset}
735
+ - name: Reality Proof
736
+ run: npx vibecheck prove --url \${{ env.APP_URL }} --ci
737
+ - uses: actions/upload-artifact@v4
738
+ with:
739
+ name: proof-report
740
+ path: .vibecheck/prove/*/proof-report.html
716
741
  `);
717
742
  }
718
743
 
@@ -738,6 +763,7 @@ async function runProve(argsOrOpts = {}, context = {}) {
738
763
  // Extract runId from context or generate new one
739
764
  const runId = context.runId || generateRunId();
740
765
  const startTime = context.startTime || new Date().toISOString();
766
+ const executionStart = Date.now();
741
767
 
742
768
  // Handle array args from CLI
743
769
  if (Array.isArray(argsOrOpts)) {
@@ -767,6 +793,14 @@ async function runProve(argsOrOpts = {}, context = {}) {
767
793
  skipFix: cleanArgs.includes("--skip-fix"),
768
794
  headed: cleanArgs.includes("--headed"),
769
795
  danger: cleanArgs.includes("--danger"),
796
+ // Recording options - enabled by default for "undeniable proof"
797
+ noVideo: cleanArgs.includes("--no-video"),
798
+ noTrace: cleanArgs.includes("--no-trace"),
799
+ noHar: cleanArgs.includes("--no-har"),
800
+ stabilityRuns: parseInt(getArg(["--stability-runs"]) || "2", 10),
801
+ flakyThreshold: parseFloat(getArg(["--flaky-threshold"]) || "0.66"),
802
+ maxPages: parseInt(getArg(["--max-pages"]) || "18", 10),
803
+ evidencePack: cleanArgs.includes("--evidence-pack") || cleanArgs.includes("--bundle"),
770
804
  output: getArg(["--output", "-o"]),
771
805
  };
772
806
  }
@@ -787,9 +821,16 @@ async function runProve(argsOrOpts = {}, context = {}) {
787
821
  maxPages = 18,
788
822
  maxDepth = 2,
789
823
  timeoutMs = 15000,
824
+ // Recording enabled by default for undeniable proof
825
+ noVideo = false,
826
+ noTrace = false,
827
+ noHar = false,
828
+ stabilityRuns = 2,
829
+ flakyThreshold = 0.66,
790
830
  json = false,
791
831
  output = null,
792
- ci = false
832
+ ci = false,
833
+ evidencePack = false
793
834
  } = argsOrOpts;
794
835
 
795
836
  const root = repoRoot || process.cwd();
@@ -882,9 +923,10 @@ async function runProve(argsOrOpts = {}, context = {}) {
882
923
  printStepHeader(2, url);
883
924
  pipelineStatus[1] = 'running';
884
925
 
885
- startSpinner('Running reality check...', colors.step2);
926
+ startSpinner('Running reality check with video + network capture...', colors.step2);
886
927
 
887
928
  try {
929
+ // Enable all recording by default for undeniable proof
888
930
  await runReality({
889
931
  repoRoot: root,
890
932
  url,
@@ -895,7 +937,14 @@ async function runProve(argsOrOpts = {}, context = {}) {
895
937
  maxPages,
896
938
  maxDepth,
897
939
  danger,
898
- timeoutMs
940
+ timeoutMs,
941
+ // Recording enabled by default for "prove" - undeniable evidence
942
+ recordVideo: !noVideo,
943
+ recordTrace: !noTrace,
944
+ recordHar: !noHar,
945
+ // Multiple stability runs for flakiness reduction
946
+ stabilityRuns: stabilityRuns,
947
+ flakyThreshold: 0.66
899
948
  });
900
949
 
901
950
  // Read reality report for timeline
@@ -1119,7 +1168,7 @@ async function runProve(argsOrOpts = {}, context = {}) {
1119
1168
  printStepHeader(5);
1120
1169
  pipelineStatus[4] = 'running';
1121
1170
 
1122
- startSpinner('Running final verification...', colors.step5);
1171
+ startSpinner('Running final verification with recording...', colors.step5);
1123
1172
 
1124
1173
  try {
1125
1174
  await runReality({
@@ -1132,7 +1181,12 @@ async function runProve(argsOrOpts = {}, context = {}) {
1132
1181
  maxPages,
1133
1182
  maxDepth,
1134
1183
  danger,
1135
- timeoutMs
1184
+ timeoutMs,
1185
+ // Recording for final proof
1186
+ recordVideo: !noVideo,
1187
+ recordTrace: !noTrace,
1188
+ recordHar: !noHar,
1189
+ stabilityRuns: 1 // Single run for verification
1136
1190
  });
1137
1191
 
1138
1192
  shipResult = await shipCore({ repoRoot: root, fastifyEntry, noWrite: false });
@@ -1162,26 +1216,61 @@ async function runProve(argsOrOpts = {}, context = {}) {
1162
1216
  finalVerdict = shipResult.verdict;
1163
1217
 
1164
1218
  // ═══════════════════════════════════════════════════════════════════════════
1165
- // SUMMARY
1219
+ // SUMMARY & HTML PROOF REPORT
1166
1220
  // ═══════════════════════════════════════════════════════════════════════════
1167
1221
  const duration = Date.now() - executionStart;
1168
1222
  const durationStr = formatDuration(duration);
1169
1223
 
1224
+ // Load reality report for evidence
1225
+ let realityReport = null;
1226
+ const realityPath = path.join(root, ".vibecheck", "reality", "last_reality.json");
1227
+ try { realityReport = JSON.parse(fs.readFileSync(realityPath, "utf8")); } catch {}
1228
+
1170
1229
  const report = {
1171
1230
  meta: {
1172
1231
  startedAt: new Date(executionStart).toISOString(),
1173
1232
  finishedAt: new Date().toISOString(),
1174
1233
  durationMs: duration,
1175
1234
  url: url || null,
1176
- fixRounds: fixRound
1235
+ fixRounds: fixRound,
1236
+ stabilityRuns: stabilityRuns,
1237
+ recordingEnabled: { video: !noVideo, trace: !noTrace, har: !noHar }
1177
1238
  },
1178
1239
  timeline,
1179
1240
  finalVerdict,
1180
- finalFindings: shipResult.report?.findings?.length || 0
1241
+ finalFindings: shipResult.report?.findings?.length || 0,
1242
+ // Include reality evidence
1243
+ coverage: realityReport?.coverage || null,
1244
+ artifacts: realityReport?.artifacts || null,
1245
+ findings: realityReport?.findings || shipResult.report?.findings || []
1181
1246
  };
1182
1247
 
1183
1248
  fs.writeFileSync(path.join(outDir, "prove_report.json"), JSON.stringify(report, null, 2), "utf8");
1184
1249
 
1250
+ // Generate HTML proof report
1251
+ let htmlReportPath = null;
1252
+ try {
1253
+ const { generateHtmlProofReport } = require("./lib/html-proof-report");
1254
+ const htmlContent = generateHtmlProofReport({
1255
+ verdict: finalVerdict,
1256
+ projectName: projectName,
1257
+ url: url,
1258
+ startedAt: new Date(executionStart).toISOString(),
1259
+ finishedAt: new Date().toISOString(),
1260
+ durationMs: duration,
1261
+ findings: report.findings,
1262
+ coverage: report.coverage,
1263
+ passes: realityReport?.passes || {},
1264
+ artifacts: report.artifacts,
1265
+ meta: report.meta,
1266
+ stabilityRuns: stabilityRuns
1267
+ });
1268
+ htmlReportPath = path.join(outDir, "proof-report.html");
1269
+ fs.writeFileSync(htmlReportPath, htmlContent, "utf8");
1270
+ } catch (e) {
1271
+ // HTML report generation failed - non-fatal
1272
+ }
1273
+
1185
1274
  // Also save a stable pointer
1186
1275
  const latestPath = path.join(root, ".vibecheck", "prove", "last_prove.json");
1187
1276
  ensureDir(path.dirname(latestPath));
@@ -1191,6 +1280,50 @@ async function runProve(argsOrOpts = {}, context = {}) {
1191
1280
  saveArtifact(runId, "prove-report", report);
1192
1281
  saveArtifact(runId, "timeline", timeline);
1193
1282
 
1283
+ // Generate evidence pack if requested
1284
+ let evidencePackPath = null;
1285
+ if (evidencePack) {
1286
+ try {
1287
+ // Lazy load evidence pack module
1288
+ const evidencePackModule = require("../../packages/cli/src/evidence/evidence-pack");
1289
+ const { generateEvidencePack, generateHtmlViewer } = evidencePackModule.default || evidencePackModule;
1290
+
1291
+ if (!json && !ci) {
1292
+ console.log();
1293
+ console.log(` ${colors.accent}${ICONS.doc}${c.reset} Generating evidence pack...`);
1294
+ }
1295
+
1296
+ const result = await generateEvidencePack({
1297
+ projectPath: root,
1298
+ sourceReport: latestPath,
1299
+ sourceType: 'prove',
1300
+ includeVideos: !noVideo,
1301
+ includeTraces: !noTrace,
1302
+ includeHar: !noHar,
1303
+ includeScreenshots: true
1304
+ });
1305
+
1306
+ evidencePackPath = result.packPath;
1307
+
1308
+ // Generate HTML viewer
1309
+ const htmlViewerModule = require("../../packages/cli/src/evidence/html-viewer");
1310
+ const htmlViewer = htmlViewerModule.default || htmlViewerModule;
1311
+ await htmlViewer.generateHtmlViewer({
1312
+ packPath: evidencePackPath,
1313
+ embedMedia: false,
1314
+ title: `Evidence Pack - ${projectName}`
1315
+ });
1316
+
1317
+ if (!json && !ci) {
1318
+ console.log(` ${colors.success}${ICONS.check}${c.reset} Evidence pack generated: ${colors.accent}${path.relative(root, evidencePackPath)}${c.reset}`);
1319
+ }
1320
+ } catch (e) {
1321
+ if (!json && !ci) {
1322
+ console.log(` ${colors.warning}${ICONS.warning}${c.reset} Evidence pack generation failed: ${e.message}`);
1323
+ }
1324
+ }
1325
+ }
1326
+
1194
1327
  // JSON output mode
1195
1328
  if (json) {
1196
1329
  const output = createJsonOutput({
@@ -1228,10 +1361,41 @@ async function runProve(argsOrOpts = {}, context = {}) {
1228
1361
  printTimelineSummary(timeline);
1229
1362
 
1230
1363
  // Report links
1231
- printSection('REPORTS', ICONS.doc);
1364
+ printSection('PROOF ARTIFACTS', ICONS.doc);
1232
1365
  console.log();
1233
- console.log(` ${colors.accent}${outDir}/prove_report.json${c.reset}`);
1234
- console.log(` ${c.dim}${path.join(root, '.vibecheck', 'prove', 'last_prove.json')}${c.reset}`);
1366
+
1367
+ // Highlight the HTML proof report
1368
+ if (htmlReportPath) {
1369
+ console.log(` ${colors.shipGreen}${ICONS.sparkle}${c.reset} ${c.bold}HTML Proof Report:${c.reset}`);
1370
+ console.log(` ${colors.accent}${path.relative(root, htmlReportPath)}${c.reset}`);
1371
+ console.log(` ${c.dim}↳ Open in browser to see video evidence${c.reset}`);
1372
+ console.log();
1373
+ }
1374
+
1375
+ // Show video/trace artifacts if available
1376
+ if (report.artifacts) {
1377
+ if (report.artifacts.videos?.anon || report.artifacts.videos?.auth) {
1378
+ console.log(` ${c.dim}🎬 Videos:${c.reset} ${report.artifacts.videos?.directory || 'N/A'}`);
1379
+ }
1380
+ if (report.artifacts.traces?.anon || report.artifacts.traces?.auth) {
1381
+ console.log(` ${c.dim}📊 Traces:${c.reset} ${report.artifacts.traces?.directory || 'N/A'}`);
1382
+ console.log(` ${c.dim}↳ Upload to trace.playwright.dev for debugging${c.reset}`);
1383
+ }
1384
+ if (report.artifacts.har?.directory) {
1385
+ console.log(` ${c.dim}📡 HAR:${c.reset} ${report.artifacts.har?.directory || 'N/A'}`);
1386
+ }
1387
+ console.log();
1388
+ }
1389
+
1390
+ console.log(` ${c.dim}JSON Report:${c.reset} ${path.relative(root, path.join(outDir, 'prove_report.json'))}`);
1391
+ console.log();
1392
+
1393
+ // CI Integration tip
1394
+ printSection('CI INTEGRATION', ICONS.lightning);
1395
+ console.log();
1396
+ console.log(` ${c.dim}Add to your CI pipeline:${c.reset}`);
1397
+ console.log(` ${colors.accent}npx vibecheck prove --url \$APP_URL --ci${c.reset}`);
1398
+ console.log(` ${c.dim}Exit codes: 0=SHIP, 1=WARN, 2=BLOCK${c.reset}`);
1235
1399
  console.log();
1236
1400
 
1237
1401
  // Next steps if not proved
@@ -1247,10 +1411,31 @@ async function runProve(argsOrOpts = {}, context = {}) {
1247
1411
  console.log();
1248
1412
  }
1249
1413
  } else if (ci) {
1250
- // CI mode - minimal output
1414
+ // CI mode - structured output for easy parsing
1415
+ console.log(`::group::vibecheck prove summary`);
1251
1416
  console.log(`VERDICT=${finalVerdict}`);
1252
1417
  console.log(`DURATION=${duration}ms`);
1253
1418
  console.log(`FIX_ROUNDS=${fixRound}`);
1419
+ console.log(`BLOCKERS=${report.findings.filter(f => f.severity === 'BLOCK').length}`);
1420
+ console.log(`WARNINGS=${report.findings.filter(f => f.severity === 'WARN').length}`);
1421
+ console.log(`COVERAGE=${report.coverage?.percent || 0}%`);
1422
+ if (htmlReportPath) {
1423
+ console.log(`HTML_REPORT=${path.relative(root, htmlReportPath)}`);
1424
+ }
1425
+ console.log(`JSON_REPORT=${path.relative(root, path.join(outDir, 'prove_report.json'))}`);
1426
+ if (evidencePackPath) {
1427
+ console.log(`EVIDENCE_PACK=${path.relative(root, evidencePackPath)}`);
1428
+ }
1429
+ console.log(`::endgroup::`);
1430
+
1431
+ // GitHub Actions annotations
1432
+ if (finalVerdict === 'BLOCK') {
1433
+ console.log(`::error title=Reality Check Failed::${report.findings.filter(f => f.severity === 'BLOCK').length} blockers found`);
1434
+ } else if (finalVerdict === 'WARN') {
1435
+ console.log(`::warning title=Reality Check Passed with Warnings::${report.findings.filter(f => f.severity === 'WARN').length} warnings found`);
1436
+ } else {
1437
+ console.log(`::notice title=Reality Check Passed::App verified with video + network evidence`);
1438
+ }
1254
1439
  }
1255
1440
 
1256
1441
  return verdictToExitCode(finalVerdict);