@vibecheckai/cli 3.2.5 → 3.3.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 (197) hide show
  1. package/bin/.generated +25 -25
  2. package/bin/dev/run-v2-torture.js +30 -30
  3. package/bin/registry.js +192 -5
  4. package/bin/runners/lib/__tests__/entitlements-v2.test.js +295 -295
  5. package/bin/runners/lib/agent-firewall/change-packet/builder.js +280 -6
  6. package/bin/runners/lib/agent-firewall/critic/index.js +151 -0
  7. package/bin/runners/lib/agent-firewall/critic/judge.js +432 -0
  8. package/bin/runners/lib/agent-firewall/critic/prompts.js +305 -0
  9. package/bin/runners/lib/agent-firewall/lawbook/distributor.js +465 -0
  10. package/bin/runners/lib/agent-firewall/lawbook/evaluator.js +604 -0
  11. package/bin/runners/lib/agent-firewall/lawbook/index.js +304 -0
  12. package/bin/runners/lib/agent-firewall/lawbook/registry.js +514 -0
  13. package/bin/runners/lib/agent-firewall/lawbook/schema.js +420 -0
  14. package/bin/runners/lib/agent-firewall/logger.js +141 -0
  15. package/bin/runners/lib/agent-firewall/policy/loader.js +312 -4
  16. package/bin/runners/lib/agent-firewall/policy/rules/ghost-env.js +113 -1
  17. package/bin/runners/lib/agent-firewall/policy/rules/ghost-route.js +133 -6
  18. package/bin/runners/lib/agent-firewall/proposal/extractor.js +394 -0
  19. package/bin/runners/lib/agent-firewall/proposal/index.js +212 -0
  20. package/bin/runners/lib/agent-firewall/proposal/schema.js +251 -0
  21. package/bin/runners/lib/agent-firewall/proposal/validator.js +386 -0
  22. package/bin/runners/lib/agent-firewall/reality/index.js +332 -0
  23. package/bin/runners/lib/agent-firewall/reality/state.js +625 -0
  24. package/bin/runners/lib/agent-firewall/reality/watcher.js +322 -0
  25. package/bin/runners/lib/agent-firewall/risk/index.js +173 -0
  26. package/bin/runners/lib/agent-firewall/risk/scorer.js +328 -0
  27. package/bin/runners/lib/agent-firewall/risk/thresholds.js +321 -0
  28. package/bin/runners/lib/agent-firewall/risk/vectors.js +421 -0
  29. package/bin/runners/lib/agent-firewall/simulator/diff-simulator.js +472 -0
  30. package/bin/runners/lib/agent-firewall/simulator/import-resolver.js +346 -0
  31. package/bin/runners/lib/agent-firewall/simulator/index.js +181 -0
  32. package/bin/runners/lib/agent-firewall/simulator/route-validator.js +380 -0
  33. package/bin/runners/lib/agent-firewall/time-machine/incident-correlator.js +661 -0
  34. package/bin/runners/lib/agent-firewall/time-machine/index.js +267 -0
  35. package/bin/runners/lib/agent-firewall/time-machine/replay-engine.js +436 -0
  36. package/bin/runners/lib/agent-firewall/time-machine/state-reconstructor.js +490 -0
  37. package/bin/runners/lib/agent-firewall/time-machine/timeline-builder.js +530 -0
  38. package/bin/runners/lib/analyzers.js +81 -18
  39. package/bin/runners/lib/api-client.js +269 -0
  40. package/bin/runners/lib/auth-truth.js +193 -193
  41. package/bin/runners/lib/authority-badge.js +425 -0
  42. package/bin/runners/lib/backup.js +62 -62
  43. package/bin/runners/lib/billing.js +107 -107
  44. package/bin/runners/lib/claims.js +118 -118
  45. package/bin/runners/lib/cli-output.js +7 -1
  46. package/bin/runners/lib/cli-ui.js +540 -540
  47. package/bin/runners/lib/contracts/auth-contract.js +202 -202
  48. package/bin/runners/lib/contracts/env-contract.js +181 -181
  49. package/bin/runners/lib/contracts/external-contract.js +206 -206
  50. package/bin/runners/lib/contracts/guard.js +168 -168
  51. package/bin/runners/lib/contracts/index.js +89 -89
  52. package/bin/runners/lib/contracts/plan-validator.js +311 -311
  53. package/bin/runners/lib/contracts/route-contract.js +199 -199
  54. package/bin/runners/lib/contracts.js +804 -804
  55. package/bin/runners/lib/detect.js +89 -89
  56. package/bin/runners/lib/doctor/autofix.js +254 -254
  57. package/bin/runners/lib/doctor/index.js +37 -37
  58. package/bin/runners/lib/doctor/modules/dependencies.js +325 -325
  59. package/bin/runners/lib/doctor/modules/index.js +46 -46
  60. package/bin/runners/lib/doctor/modules/network.js +250 -250
  61. package/bin/runners/lib/doctor/modules/project.js +312 -312
  62. package/bin/runners/lib/doctor/modules/runtime.js +224 -224
  63. package/bin/runners/lib/doctor/modules/security.js +348 -348
  64. package/bin/runners/lib/doctor/modules/system.js +213 -213
  65. package/bin/runners/lib/doctor/modules/vibecheck.js +394 -394
  66. package/bin/runners/lib/doctor/reporter.js +262 -262
  67. package/bin/runners/lib/doctor/service.js +262 -262
  68. package/bin/runners/lib/doctor/types.js +113 -113
  69. package/bin/runners/lib/doctor/ui.js +263 -263
  70. package/bin/runners/lib/doctor-v2.js +608 -608
  71. package/bin/runners/lib/drift.js +425 -425
  72. package/bin/runners/lib/enforcement.js +72 -72
  73. package/bin/runners/lib/enterprise-detect.js +603 -603
  74. package/bin/runners/lib/enterprise-init.js +942 -942
  75. package/bin/runners/lib/env-resolver.js +417 -417
  76. package/bin/runners/lib/env-template.js +66 -66
  77. package/bin/runners/lib/env.js +189 -189
  78. package/bin/runners/lib/error-handler.js +16 -9
  79. package/bin/runners/lib/exit-codes.js +275 -0
  80. package/bin/runners/lib/extractors/client-calls.js +990 -990
  81. package/bin/runners/lib/extractors/fastify-route-dump.js +573 -573
  82. package/bin/runners/lib/extractors/fastify-routes.js +426 -426
  83. package/bin/runners/lib/extractors/index.js +363 -363
  84. package/bin/runners/lib/extractors/next-routes.js +524 -524
  85. package/bin/runners/lib/extractors/proof-graph.js +431 -431
  86. package/bin/runners/lib/extractors/route-matcher.js +451 -451
  87. package/bin/runners/lib/extractors/truthpack-v2.js +377 -377
  88. package/bin/runners/lib/extractors/ui-bindings.js +547 -547
  89. package/bin/runners/lib/findings-schema.js +281 -281
  90. package/bin/runners/lib/firewall-prompt.js +50 -50
  91. package/bin/runners/lib/global-flags.js +37 -0
  92. package/bin/runners/lib/graph/graph-builder.js +265 -265
  93. package/bin/runners/lib/graph/html-renderer.js +413 -413
  94. package/bin/runners/lib/graph/index.js +32 -32
  95. package/bin/runners/lib/graph/runtime-collector.js +215 -215
  96. package/bin/runners/lib/graph/static-extractor.js +518 -518
  97. package/bin/runners/lib/help-formatter.js +413 -0
  98. package/bin/runners/lib/html-report.js +650 -650
  99. package/bin/runners/lib/llm.js +75 -75
  100. package/bin/runners/lib/logger.js +38 -0
  101. package/bin/runners/lib/meter.js +61 -61
  102. package/bin/runners/lib/missions/evidence.js +126 -126
  103. package/bin/runners/lib/patch.js +40 -40
  104. package/bin/runners/lib/permissions/auth-model.js +213 -213
  105. package/bin/runners/lib/permissions/idor-prover.js +205 -205
  106. package/bin/runners/lib/permissions/index.js +45 -45
  107. package/bin/runners/lib/permissions/matrix-builder.js +198 -198
  108. package/bin/runners/lib/pkgjson.js +28 -28
  109. package/bin/runners/lib/policy.js +295 -295
  110. package/bin/runners/lib/preflight.js +142 -142
  111. package/bin/runners/lib/reality/correlation-detectors.js +359 -359
  112. package/bin/runners/lib/reality/index.js +318 -318
  113. package/bin/runners/lib/reality/request-hashing.js +416 -416
  114. package/bin/runners/lib/reality/request-mapper.js +453 -453
  115. package/bin/runners/lib/reality/safety-rails.js +463 -463
  116. package/bin/runners/lib/reality/semantic-snapshot.js +408 -408
  117. package/bin/runners/lib/reality/toast-detector.js +393 -393
  118. package/bin/runners/lib/reality-findings.js +84 -84
  119. package/bin/runners/lib/receipts.js +179 -179
  120. package/bin/runners/lib/redact.js +29 -29
  121. package/bin/runners/lib/replay/capsule-manager.js +154 -154
  122. package/bin/runners/lib/replay/index.js +263 -263
  123. package/bin/runners/lib/replay/player.js +348 -348
  124. package/bin/runners/lib/replay/recorder.js +331 -331
  125. package/bin/runners/lib/report.js +135 -135
  126. package/bin/runners/lib/route-detection.js +1140 -1140
  127. package/bin/runners/lib/sandbox/index.js +59 -59
  128. package/bin/runners/lib/sandbox/proof-chain.js +399 -399
  129. package/bin/runners/lib/sandbox/sandbox-runner.js +205 -205
  130. package/bin/runners/lib/sandbox/worktree.js +174 -174
  131. package/bin/runners/lib/schema-validator.js +350 -350
  132. package/bin/runners/lib/schemas/contracts.schema.json +160 -160
  133. package/bin/runners/lib/schemas/finding.schema.json +100 -100
  134. package/bin/runners/lib/schemas/mission-pack.schema.json +206 -206
  135. package/bin/runners/lib/schemas/proof-graph.schema.json +176 -176
  136. package/bin/runners/lib/schemas/reality-report.schema.json +162 -162
  137. package/bin/runners/lib/schemas/share-pack.schema.json +180 -180
  138. package/bin/runners/lib/schemas/ship-report.schema.json +117 -117
  139. package/bin/runners/lib/schemas/truthpack-v2.schema.json +303 -303
  140. package/bin/runners/lib/schemas/validator.js +438 -438
  141. package/bin/runners/lib/score-history.js +282 -282
  142. package/bin/runners/lib/share-pack.js +239 -239
  143. package/bin/runners/lib/snippets.js +67 -67
  144. package/bin/runners/lib/unified-cli-output.js +604 -0
  145. package/bin/runners/lib/upsell.js +658 -510
  146. package/bin/runners/lib/usage.js +153 -153
  147. package/bin/runners/lib/validate-patch.js +156 -156
  148. package/bin/runners/lib/verdict-engine.js +628 -628
  149. package/bin/runners/reality/engine.js +917 -917
  150. package/bin/runners/reality/flows.js +122 -122
  151. package/bin/runners/reality/report.js +378 -378
  152. package/bin/runners/reality/session.js +193 -193
  153. package/bin/runners/runAgent.d.ts +5 -0
  154. package/bin/runners/runApprove.js +1200 -0
  155. package/bin/runners/runAuth.js +324 -95
  156. package/bin/runners/runCheckpoint.js +39 -21
  157. package/bin/runners/runClassify.js +859 -0
  158. package/bin/runners/runContext.js +136 -24
  159. package/bin/runners/runDoctor.js +108 -68
  160. package/bin/runners/runFirewall.d.ts +5 -0
  161. package/bin/runners/runFirewallHook.d.ts +5 -0
  162. package/bin/runners/runFix.js +6 -5
  163. package/bin/runners/runGuard.js +262 -168
  164. package/bin/runners/runInit.js +3 -2
  165. package/bin/runners/runMcp.js +130 -52
  166. package/bin/runners/runPolish.js +43 -20
  167. package/bin/runners/runProve.js +1 -2
  168. package/bin/runners/runReport.js +3 -2
  169. package/bin/runners/runScan.js +145 -44
  170. package/bin/runners/runShip.js +3 -4
  171. package/bin/runners/runTruth.d.ts +5 -0
  172. package/bin/runners/runValidate.js +19 -2
  173. package/bin/runners/runWatch.js +104 -53
  174. package/bin/vibecheck.js +106 -19
  175. package/mcp-server/HARDENING_SUMMARY.md +299 -0
  176. package/mcp-server/agent-firewall-interceptor.js +367 -31
  177. package/mcp-server/authority-tools.js +569 -0
  178. package/mcp-server/conductor/conflict-resolver.js +588 -0
  179. package/mcp-server/conductor/execution-planner.js +544 -0
  180. package/mcp-server/conductor/index.js +377 -0
  181. package/mcp-server/conductor/lock-manager.js +615 -0
  182. package/mcp-server/conductor/request-queue.js +550 -0
  183. package/mcp-server/conductor/session-manager.js +500 -0
  184. package/mcp-server/conductor/tools.js +510 -0
  185. package/mcp-server/index.js +1199 -208
  186. package/mcp-server/lib/api-client.cjs +305 -0
  187. package/mcp-server/lib/logger.cjs +30 -0
  188. package/mcp-server/logger.js +173 -0
  189. package/mcp-server/package.json +2 -2
  190. package/mcp-server/premium-tools.js +2 -2
  191. package/mcp-server/tier-auth.js +351 -136
  192. package/mcp-server/tools/index.js +72 -72
  193. package/mcp-server/truth-firewall-tools.js +145 -15
  194. package/mcp-server/vibecheck-tools.js +2 -2
  195. package/package.json +2 -3
  196. package/mcp-server/index.old.js +0 -4137
  197. package/mcp-server/package-lock.json +0 -165
@@ -6,6 +6,7 @@
6
6
  */
7
7
 
8
8
  const path = require("path");
9
+ const fs = require("fs");
9
10
  const http = require("http");
10
11
  const { URL } = require("url");
11
12
 
@@ -14,6 +15,8 @@ const { buildTruthpack, writeTruthpack } = require("./lib/truth");
14
15
  const { shipCore } = require("./runShip");
15
16
  const { generateRunId } = require("./lib/cli-output");
16
17
  const { enforceLimit, enforceFeature, trackUsage } = require("./lib/entitlements");
18
+ const { EXIT } = require("./lib/exit-codes");
19
+ const { parseGlobalFlags, shouldSuppressOutput, isJsonMode } = require("./lib/global-flags");
17
20
 
18
21
  // MCP Server class
19
22
  class VibecheckMCPServer {
@@ -642,8 +645,14 @@ class VibecheckMCPServer {
642
645
  });
643
646
 
644
647
  this.server.on("error", err => {
645
- console.error("MCP Server failed to start:", err);
646
- process.exit(1);
648
+ console.error("MCP Server failed to start:", err.message);
649
+ if (err.code === "EADDRINUSE") {
650
+ console.error(` Port ${this.port} is already in use. Try a different port with --port`);
651
+ } else if (err.code === "EACCES") {
652
+ console.error(` Permission denied for port ${this.port}. Use a port > 1024`);
653
+ }
654
+ // Emit error event for caller to handle
655
+ this.emit?.("error", err);
647
656
  });
648
657
  }
649
658
 
@@ -658,54 +667,77 @@ class VibecheckMCPServer {
658
667
  // CLI handler
659
668
  async function runMcp(args) {
660
669
  const opts = parseArgs(args);
670
+ const { flags: globalFlags } = parseGlobalFlags(args);
671
+ const quiet = shouldSuppressOutput(globalFlags);
672
+ const json = isJsonMode(globalFlags);
661
673
 
662
674
  // Check if we're in free tier and only showing help/config
663
675
  const isFreeTier = process.env.VIBECHECK_TIER === "free" || !process.env.VIBECHECK_API_KEY;
664
676
  const isHelpOrConfig = opts.help || opts.printConfig || opts.status || opts.test;
665
677
 
666
678
  if (isFreeTier && !isHelpOrConfig) {
667
- // This will be handled by entitlements system
668
- // But we can show a helpful message
669
- console.log("\n🔌 MCP Server requires STARTER plan or higher");
670
- console.log("Use --help to see available options or");
671
- console.log("Upgrade: https://vibecheckai.dev/pricing\n");
672
- return 3; // EXIT_FEATURE_NOT_ALLOWED
679
+ if (json) {
680
+ console.log(JSON.stringify({ success: false, error: "MCP Server requires STARTER plan or higher" }));
681
+ } else if (!quiet) {
682
+ console.log("\n🔌 MCP Server requires STARTER plan or higher");
683
+ console.log("Use --help to see available options or");
684
+ console.log("Upgrade: https://vibecheckai.dev/pricing\n");
685
+ }
686
+ return EXIT.TIER_REQUIRED;
673
687
  }
674
688
 
675
689
  if (opts.help) {
676
690
  printHelp();
677
- return 0;
691
+ return EXIT.SUCCESS;
678
692
  }
679
693
 
680
694
  if (opts.printConfig) {
681
695
  printClientConfig(opts);
682
- return 0;
696
+ return EXIT.SUCCESS;
683
697
  }
684
698
 
685
699
  if (opts.status) {
686
700
  // Check server status
687
701
  try {
688
- const response = await fetch(`http://${opts.host}:${opts.port}/status`);
702
+ const host = opts.host || "127.0.0.1";
703
+ const port = opts.port || 3000;
704
+ const response = await fetch(`http://${host}:${port}/status`);
689
705
  const status = await response.json();
690
706
  console.log(JSON.stringify(status, null, 2));
691
- return 0;
707
+ return EXIT.SUCCESS;
692
708
  } catch (err) {
693
- console.error("Server not running or not reachable");
694
- return 1;
709
+ if (json) {
710
+ console.log(JSON.stringify({ success: false, error: "Server not running or not reachable" }));
711
+ } else {
712
+ console.error("Server not running or not reachable");
713
+ console.error(" Start the server with: vibecheck mcp");
714
+ }
715
+ return EXIT.NETWORK_ERROR;
695
716
  }
696
717
  }
697
718
 
698
719
  if (opts.test) {
699
720
  // Test connection
700
721
  try {
701
- const response = await fetch(`http://${opts.host}:${opts.port}/tools`);
722
+ const host = opts.host || "127.0.0.1";
723
+ const port = opts.port || 3000;
724
+ const response = await fetch(`http://${host}:${port}/tools`);
702
725
  const tools = await response.json();
703
- console.log("✅ Connection successful");
704
- console.log(`📋 Available tools: ${tools.tools.length}`);
705
- return 0;
726
+ if (json) {
727
+ console.log(JSON.stringify({ success: true, toolCount: tools.tools?.length || 0 }));
728
+ } else {
729
+ console.log("✅ Connection successful");
730
+ console.log(`📋 Available tools: ${tools.tools?.length || 0}`);
731
+ }
732
+ return EXIT.SUCCESS;
706
733
  } catch (err) {
707
- console.error("❌ Connection failed:", err.message);
708
- return 1;
734
+ if (json) {
735
+ console.log(JSON.stringify({ success: false, error: err.message }));
736
+ } else {
737
+ console.error("❌ Connection failed:", err.message);
738
+ console.error(" Ensure the MCP server is running");
739
+ }
740
+ return EXIT.NETWORK_ERROR;
709
741
  }
710
742
  }
711
743
 
@@ -713,42 +745,88 @@ async function runMcp(args) {
713
745
  let config = {};
714
746
  if (opts.config) {
715
747
  const configPath = path.resolve(opts.config);
716
- if (require("fs").existsSync(configPath)) {
717
- config = JSON.parse(require("fs").readFileSync(configPath, "utf8"));
718
- } else {
719
- console.error(`Config file not found: ${configPath}`);
720
- return 1;
748
+ if (!fs.existsSync(configPath)) {
749
+ if (json) {
750
+ console.log(JSON.stringify({ success: false, error: `Config file not found: ${configPath}` }));
751
+ } else {
752
+ console.error(`Config file not found: ${configPath}`);
753
+ }
754
+ return EXIT.NOT_FOUND;
755
+ }
756
+ try {
757
+ config = JSON.parse(fs.readFileSync(configPath, "utf8"));
758
+ } catch (parseErr) {
759
+ if (json) {
760
+ console.log(JSON.stringify({ success: false, error: `Invalid JSON in config: ${parseErr.message}` }));
761
+ } else {
762
+ console.error(`Invalid JSON in config file: ${parseErr.message}`);
763
+ }
764
+ return EXIT.USER_ERROR;
721
765
  }
722
766
  }
723
767
 
724
- // Create and start server
725
- const server = new VibecheckMCPServer({
726
- host: opts.host || config.server?.host || "127.0.0.1",
727
- port: opts.port || config.server?.port || 3000,
728
- allowRemote: opts.allowRemote || config.server?.allowRemote || false,
729
- requireApiKey: opts.requireApiKey !== false,
730
- apiKey: opts.apiKey || config.auth?.apiKey,
731
- logLevel: opts.logLevel || config.logging?.level || "info",
732
- auditLog: opts.auditLog || config.logging?.auditFile
733
- });
734
-
735
- server.start();
736
-
737
- // Handle shutdown
738
- process.on("SIGINT", () => {
739
- console.log("\nShutting down MCP server...");
740
- server.stop();
741
- process.exit(0);
742
- });
743
-
744
- process.on("SIGTERM", () => {
745
- console.log("\nShutting down MCP server...");
746
- server.stop();
747
- process.exit(0);
748
- });
768
+ // Validate port
769
+ const port = opts.port || config.server?.port || 3000;
770
+ if (isNaN(port) || port < 1 || port > 65535) {
771
+ if (json) {
772
+ console.log(JSON.stringify({ success: false, error: `Invalid port: ${port}` }));
773
+ } else {
774
+ console.error(`Invalid port: ${port}`);
775
+ console.error(" Port must be a number between 1 and 65535");
776
+ }
777
+ return EXIT.USER_ERROR;
778
+ }
749
779
 
750
- // Keep process alive
751
- return new Promise(() => {});
780
+ try {
781
+ // Create and start server
782
+ const server = new VibecheckMCPServer({
783
+ host: opts.host || config.server?.host || "127.0.0.1",
784
+ port: port,
785
+ allowRemote: opts.allowRemote || config.server?.allowRemote || false,
786
+ requireApiKey: opts.requireApiKey !== false,
787
+ apiKey: opts.apiKey || config.auth?.apiKey,
788
+ logLevel: opts.logLevel || config.logging?.level || "info",
789
+ auditLog: opts.auditLog || config.logging?.auditFile
790
+ });
791
+
792
+ // Track if server started successfully
793
+ let serverStarted = false;
794
+ let serverError = null;
795
+
796
+ // Listen for server errors before starting
797
+ server.server?.on?.("error", (err) => {
798
+ serverError = err;
799
+ });
800
+
801
+ server.start();
802
+ serverStarted = true;
803
+
804
+ // Handle shutdown gracefully
805
+ const cleanup = () => {
806
+ if (!quiet) console.log("\nShutting down MCP server...");
807
+ server.stop();
808
+ };
809
+
810
+ process.on("SIGINT", () => {
811
+ cleanup();
812
+ process.exit(EXIT.SUCCESS);
813
+ });
814
+
815
+ process.on("SIGTERM", () => {
816
+ cleanup();
817
+ process.exit(EXIT.SUCCESS);
818
+ });
819
+
820
+ // Keep process alive
821
+ return new Promise(() => {});
822
+ } catch (error) {
823
+ if (json) {
824
+ console.log(JSON.stringify({ success: false, error: error.message }));
825
+ } else {
826
+ console.error(`Failed to start MCP server: ${error.message}`);
827
+ }
828
+ return EXIT.INTERNAL_ERROR;
829
+ }
752
830
  }
753
831
 
754
832
  // Argument parsing
@@ -24,6 +24,8 @@
24
24
 
25
25
  const fs = require("fs");
26
26
  const path = require("path");
27
+ const { EXIT } = require("./lib/exit-codes");
28
+ const { parseGlobalFlags, shouldSuppressOutput, isJsonMode } = require("./lib/global-flags");
27
29
 
28
30
  // ═══════════════════════════════════════════════════════════════════════════════
29
31
  // TERMINAL STYLING
@@ -2856,26 +2858,35 @@ function getCategoryIcon(category) {
2856
2858
 
2857
2859
  async function runPolish(args) {
2858
2860
  const opts = parseArgs(args);
2861
+ const { flags: globalFlags } = parseGlobalFlags(args);
2862
+ const quiet = shouldSuppressOutput(globalFlags);
2863
+ const json = isJsonMode(globalFlags) || opts.json;
2859
2864
 
2860
2865
  if (opts.help) {
2861
2866
  printHelp();
2862
- return 0;
2867
+ return EXIT.SUCCESS;
2863
2868
  }
2864
2869
 
2865
2870
  const projectPath = path.resolve(opts.path);
2866
2871
 
2867
2872
  // Verify project exists
2868
2873
  if (!await pathExists(projectPath)) {
2869
- console.error(`${c.red}${icons.cross} Project path does not exist: ${projectPath}${c.reset}`);
2870
- return 1;
2874
+ if (json) {
2875
+ console.log(JSON.stringify({ success: false, error: `Project path does not exist: ${projectPath}` }));
2876
+ } else {
2877
+ console.error(`${c.red}${icons.cross} Project path does not exist: ${projectPath}${c.reset}`);
2878
+ console.error(` Verify the path and try again.`);
2879
+ }
2880
+ return EXIT.NOT_FOUND;
2871
2881
  }
2872
2882
 
2873
- // JSON mode
2874
- if (opts.json) {
2875
- const report = await analyzeProject(projectPath, opts);
2876
- console.log(JSON.stringify(report, null, 2));
2877
- return report.critical > 0 ? 1 : 0;
2878
- }
2883
+ try {
2884
+ // JSON mode
2885
+ if (json) {
2886
+ const report = await analyzeProject(projectPath, opts);
2887
+ console.log(JSON.stringify(report, null, 2));
2888
+ return report.critical > 0 ? EXIT.BLOCKING : EXIT.SUCCESS;
2889
+ }
2879
2890
 
2880
2891
  // Interactive mode
2881
2892
  console.log(`
@@ -2914,8 +2925,10 @@ ${c.bold}╔══════════════════════
2914
2925
  console.log(` ${icons.critical} ${report.critical} critical ${icons.high} ${report.high} high ${icons.medium} ${report.medium} medium ${icons.low} ${report.low} low\n`);
2915
2926
 
2916
2927
  if (report.issues.length === 0) {
2917
- console.log(`\n${c.green}${c.bold}${icons.check} Perfect!${c.reset} No polish issues found. Your project is production-ready! ${icons.rocket}\n`);
2918
- return 0;
2928
+ if (!quiet) {
2929
+ console.log(`\n${c.green}${c.bold}${icons.check} Perfect!${c.reset} No polish issues found. Your project is production-ready! ${icons.rocket}\n`);
2930
+ }
2931
+ return EXIT.SUCCESS;
2919
2932
  }
2920
2933
 
2921
2934
  // Group by category
@@ -3016,16 +3029,26 @@ ${c.bold}╔══════════════════════
3016
3029
  }
3017
3030
 
3018
3031
  // Next steps
3019
- console.log(`${c.bold}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${c.reset}`);
3020
- console.log(`${c.bold}${icons.rocket} NEXT STEPS${c.reset}`);
3021
- console.log(`${c.bold}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${c.reset}\n`);
3022
- console.log(` 1. Fix ${c.red}critical${c.reset} and ${c.yellow}high${c.reset} severity issues first`);
3023
- console.log(` 2. Use ${c.cyan}vibecheck polish --prompts${c.reset} to get AI-ready fixes`);
3024
- console.log(` 3. Copy prompts to your AI assistant (Cursor, Copilot, Claude)`);
3025
- console.log(` 4. Re-run ${c.cyan}vibecheck polish${c.reset} to verify fixes`);
3026
- console.log(` 5. Run ${c.cyan}vibecheck ship${c.reset} when score is 80+\n`);
3032
+ if (!quiet) {
3033
+ console.log(`${c.bold}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${c.reset}`);
3034
+ console.log(`${c.bold}${icons.rocket} NEXT STEPS${c.reset}`);
3035
+ console.log(`${c.bold}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${c.reset}\n`);
3036
+ console.log(` 1. Fix ${c.red}critical${c.reset} and ${c.yellow}high${c.reset} severity issues first`);
3037
+ console.log(` 2. Use ${c.cyan}vibecheck polish --prompts${c.reset} to get AI-ready fixes`);
3038
+ console.log(` 3. Copy prompts to your AI assistant (Cursor, Copilot, Claude)`);
3039
+ console.log(` 4. Re-run ${c.cyan}vibecheck polish${c.reset} to verify fixes`);
3040
+ console.log(` 5. Run ${c.cyan}vibecheck ship${c.reset} when score is 80+\n`);
3041
+ }
3027
3042
 
3028
- return report.critical > 0 ? 1 : 0;
3043
+ return report.critical > 0 ? EXIT.BLOCKING : EXIT.SUCCESS;
3044
+ } catch (error) {
3045
+ if (json) {
3046
+ console.log(JSON.stringify({ success: false, error: error.message }));
3047
+ } else {
3048
+ console.error(`${c.red}${icons.cross} Polish analysis failed: ${error.message}${c.reset}`);
3049
+ }
3050
+ return EXIT.INTERNAL_ERROR;
3051
+ }
3029
3052
  }
3030
3053
 
3031
3054
  module.exports = { runPolish };
@@ -26,10 +26,9 @@ const {
26
26
  generateRunId,
27
27
  createJsonOutput,
28
28
  writeJsonOutput,
29
- exitCodeToVerdict,
30
- verdictToExitCode,
31
29
  saveArtifact
32
30
  } = require("./lib/cli-output");
31
+ const { EXIT, verdictToExitCode, exitCodeToVerdict } = require("./lib/exit-codes");
33
32
  const { parseGlobalFlags, shouldShowBanner } = require("./lib/global-flags");
34
33
  const upsell = require("./lib/upsell");
35
34
 
@@ -18,6 +18,7 @@
18
18
  const path = require("path");
19
19
  const fs = require("fs");
20
20
  const { parseGlobalFlags, shouldShowBanner } = require("./lib/global-flags");
21
+ const { EXIT } = require("./lib/exit-codes");
21
22
 
22
23
  // Entitlements enforcement
23
24
  let entitlements;
@@ -264,11 +265,11 @@ async function runReport(args) {
264
265
  default:
265
266
  spinner.fail(`Unknown format: ${format}`);
266
267
  console.log(` ${ansi.dim}Supported: html, md, json, sarif, csv, pdf${ansi.reset}`);
267
- return 1;
268
+ return EXIT.USER_ERROR;
268
269
  }
269
270
  } catch (err) {
270
271
  spinner.fail(`Failed to generate report: ${err.message}`);
271
- return 1;
272
+ return EXIT.INTERNAL_ERROR;
272
273
  }
273
274
 
274
275
  // Determine output path
@@ -18,6 +18,14 @@ const { withErrorHandling, createUserError } = require("./lib/error-handler");
18
18
  const { enforceLimit, trackUsage } = require("./lib/entitlements");
19
19
  const { emitScanStart, emitScanComplete } = require("./lib/audit-bridge");
20
20
  const { parseGlobalFlags, shouldShowBanner } = require("./lib/global-flags");
21
+ const {
22
+ createScan,
23
+ updateScanProgress,
24
+ submitScanResults,
25
+ reportScanError,
26
+ isApiAvailable
27
+ } = require("./lib/api-client");
28
+ const { EXIT, verdictToExitCode } = require("./lib/exit-codes");
21
29
 
22
30
  // ═══════════════════════════════════════════════════════════════════════════════
23
31
  // ENHANCED TERMINAL UI & OUTPUT MODULES
@@ -163,57 +171,75 @@ function printHelp(showBanner = true) {
163
171
  console.log(BANNER);
164
172
  }
165
173
  console.log(`
166
- ${ansi.bold}Usage:${ansi.reset} vibecheck scan ${ansi.dim}(s)${ansi.reset} [path] [options]
174
+ ${ansi.bold}USAGE${ansi.reset}
175
+ ${colors.accent}vibecheck scan${ansi.reset} [path] [options]
167
176
 
168
- ${ansi.bold}Aliases:${ansi.reset} ${ansi.dim}s${ansi.reset}
169
-
170
- ${ansi.bold}Scan Modes:${ansi.reset}
171
- ${colors.accent}(default)${ansi.reset} Layer 1: AST static analysis ${ansi.dim}(fast)${ansi.reset}
172
- ${colors.accent}--truth, -t${ansi.reset} Layer 1+2: Include build manifest verification ${ansi.dim}(CI/ship)${ansi.reset}
173
- ${colors.accent}--reality, -r${ansi.reset} Layer 1+2+3: Include Playwright runtime proof ${ansi.dim}(full)${ansi.reset}
174
- ${colors.accent}--reality-sniff${ansi.reset} Include Reality Sniff AI artifact detection ${ansi.dim}(recommended)${ansi.reset}
175
-
176
- ${ansi.bold}Fix Mode:${ansi.reset}
177
- ${colors.accent}--autofix, -f${ansi.reset} Apply safe fixes + generate AI missions ${ansi.rgb(0, 200, 255)}[STARTER]${ansi.reset}
178
-
179
- ${ansi.bold}Allowlist (suppress false positives):${ansi.reset}
180
- ${colors.accent}--allowlist list${ansi.reset} Show all allowlist entries
181
- ${colors.accent}--allowlist add${ansi.reset} Add entry to allowlist
182
- ${ansi.dim}--id <finding-id>${ansi.reset} Finding ID to allowlist
183
- ${ansi.dim}--pattern <regex>${ansi.reset} Pattern to match
184
- ${ansi.dim}--reason <text>${ansi.reset} Reason (required)
185
- ${ansi.dim}--scope <global|file|line>${ansi.reset} Scope (default: global)
186
- ${ansi.dim}--expires <days>${ansi.reset} Auto-expire after N days
187
- ${colors.accent}--allowlist remove --id <id>${ansi.reset} Remove entry from allowlist
188
- ${colors.accent}--allowlist check --id <id>${ansi.reset} Check if finding is allowlisted
189
-
190
- ${ansi.bold}Options:${ansi.reset}
191
- ${colors.accent}--url, -u${ansi.reset} Base URL for reality testing (e.g., http://localhost:3000)
192
- ${colors.accent}--verbose, -v${ansi.reset} Show detailed progress
193
- ${colors.accent}--json${ansi.reset} Output results as JSON
194
- ${colors.accent}--sarif${ansi.reset} Output in SARIF format (GitHub code scanning)
195
- ${colors.accent}--no-save${ansi.reset} Don't save results to .vibecheck/results/
196
- ${colors.accent}--help, -h${ansi.reset} Show this help
197
-
198
- ${ansi.bold}Examples:${ansi.reset}
199
- ${ansi.dim}# Quick scan (AST only)${ansi.reset}
177
+ ${ansi.dim}Aliases: s, check${ansi.reset}
178
+
179
+ The core analysis engine. Scans your codebase for route integrity issues,
180
+ security vulnerabilities, code quality problems, and more.
181
+
182
+ ${ansi.bold}SCAN LAYERS${ansi.reset}
183
+ ${colors.accent}(default)${ansi.reset} Layer 1: AST static analysis ${ansi.dim}(fast, ~2s)${ansi.reset}
184
+ ${colors.accent}--truth, -t${ansi.reset} Layer 1+2: + build manifest verification ${ansi.dim}(CI/ship)${ansi.reset}
185
+ ${colors.accent}--reality, -r${ansi.reset} Layer 1+2+3: + Playwright runtime proof ${ansi.dim}[PRO]${ansi.reset}
186
+ ${colors.accent}--reality-sniff${ansi.reset} Include Reality Sniff AI artifact detection
187
+
188
+ ${ansi.bold}FIX MODE${ansi.reset} ${ansi.cyan}[STARTER]${ansi.reset}
189
+ ${colors.accent}--autofix, -f${ansi.reset} Generate AI fix missions for detected issues
190
+
191
+ ${ansi.bold}BASELINE TRACKING${ansi.reset}
192
+ ${colors.accent}--baseline${ansi.reset} Compare against previous scan ${ansi.dim}(default: on)${ansi.reset}
193
+ ${colors.accent}--no-baseline${ansi.reset} Skip baseline comparison
194
+ ${colors.accent}--update-baseline${ansi.reset} Save current findings as new baseline
195
+
196
+ ${ansi.bold}ALLOWLIST (suppress false positives)${ansi.reset}
197
+ ${colors.accent}--allowlist list${ansi.reset} Show all suppressed findings
198
+ ${colors.accent}--allowlist add --id <id> --reason "..."${ansi.reset}
199
+ ${colors.accent}--allowlist remove --id <id>${ansi.reset} Remove suppression
200
+ ${colors.accent}--allowlist check --id <id>${ansi.reset} Check if suppressed
201
+
202
+ ${ansi.bold}OUTPUT OPTIONS${ansi.reset}
203
+ ${colors.accent}--json${ansi.reset} Output as JSON ${ansi.dim}(machine-readable)${ansi.reset}
204
+ ${colors.accent}--sarif${ansi.reset} SARIF format ${ansi.dim}(GitHub code scanning)${ansi.reset}
205
+ ${colors.accent}--no-save${ansi.reset} Don't save results to .vibecheck/results/
206
+ ${colors.accent}--verbose, -v${ansi.reset} Show detailed progress
207
+
208
+ ${ansi.bold}GLOBAL OPTIONS${ansi.reset}
209
+ ${colors.accent}--path, -p <dir>${ansi.reset} Run in specified directory
210
+ ${colors.accent}--quiet, -q${ansi.reset} Suppress non-essential output
211
+ ${colors.accent}--ci${ansi.reset} CI mode ${ansi.dim}(quiet + no-banner)${ansi.reset}
212
+ ${colors.accent}--help, -h${ansi.reset} Show this help
213
+
214
+ ${ansi.bold}💡 EXAMPLES${ansi.reset}
215
+
216
+ ${ansi.dim}# Quick scan (most common)${ansi.reset}
200
217
  vibecheck scan
201
218
 
202
- ${ansi.dim}# Scan + autofix with missions${ansi.reset}
219
+ ${ansi.dim}# Scan with AI fix missions${ansi.reset}
203
220
  vibecheck scan --autofix
204
221
 
205
- ${ansi.dim}# Add false positive to allowlist${ansi.reset}
206
- vibecheck scan --allowlist add --id R_DEAD_abc123 --reason "Known toggle"
222
+ ${ansi.dim}# Suppress a false positive${ansi.reset}
223
+ vibecheck scan --allowlist add --id R_DEAD_abc123 --reason "Feature toggle"
207
224
 
208
- ${ansi.dim}# List allowlist entries${ansi.reset}
209
- vibecheck scan --allowlist list
225
+ ${ansi.dim}# CI pipeline (JSON output, strict)${ansi.reset}
226
+ vibecheck scan --ci --json > results.json
210
227
 
211
- ${ansi.dim}# Full proof with Playwright${ansi.reset}
228
+ ${ansi.dim}# Full reality proof (requires running app)${ansi.reset}
212
229
  vibecheck scan --reality --url http://localhost:3000
213
230
 
214
- ${ansi.bold}Output:${ansi.reset}
215
- Results saved to: .vibecheck/results/latest.json
216
- Missions saved to: .vibecheck/missions/ ${ansi.dim}(with --autofix)${ansi.reset}
231
+ ${ansi.bold}📄 OUTPUT${ansi.reset}
232
+ Results: .vibecheck/results/latest.json
233
+ Missions: .vibecheck/missions/ ${ansi.dim}(with --autofix)${ansi.reset}
234
+ History: .vibecheck/results/history/
235
+
236
+ ${ansi.bold}🔗 RELATED COMMANDS${ansi.reset}
237
+ ${colors.accent}vibecheck ship${ansi.reset} Get final SHIP/WARN/BLOCK verdict
238
+ ${colors.accent}vibecheck fix${ansi.reset} Apply AI-generated fixes ${ansi.cyan}[STARTER]${ansi.reset}
239
+ ${colors.accent}vibecheck prove${ansi.reset} Full proof pipeline with evidence ${ansi.magenta}[PRO]${ansi.reset}
240
+
241
+ ${ansi.dim}─────────────────────────────────────────────────────────────${ansi.reset}
242
+ ${ansi.dim}Documentation: https://docs.vibecheckai.dev/cli/scan${ansi.reset}
217
243
  `);
218
244
  }
219
245
 
@@ -703,6 +729,27 @@ async function runScan(args) {
703
729
  emitScanStart(projectPath, args);
704
730
  const projectName = path.basename(projectPath);
705
731
 
732
+ // Initialize API integration
733
+ let apiScan = null;
734
+ let apiConnected = false;
735
+
736
+ // Try to connect to API for dashboard integration
737
+ try {
738
+ apiConnected = await isApiAvailable();
739
+ if (apiConnected) {
740
+ // Create scan record in dashboard
741
+ apiScan = await createScan({
742
+ localPath: projectPath,
743
+ branch: opts.branch || 'main',
744
+ enableLLM: opts.llm || false,
745
+ });
746
+ console.log(`${colors.info}📡${ansi.reset} Connected to dashboard (Scan ID: ${apiScan.scanId})`);
747
+ }
748
+ } catch (err) {
749
+ // API connection is optional, continue without it
750
+ console.log(`${colors.dim}ℹ${ansi.reset} Dashboard integration unavailable`);
751
+ }
752
+
706
753
  // Validate project path
707
754
  if (!fs.existsSync(projectPath)) {
708
755
  throw createUserError(`Project path does not exist: ${projectPath}`, "ValidationError");
@@ -733,7 +780,7 @@ async function runScan(args) {
733
780
  console.log(` ${colors.warning}⚠${ansi.reset} ${ansi.bold}Reality layer requires --url${ansi.reset}`);
734
781
  console.log(` ${ansi.dim}Example: vibecheck scan --reality --url http://localhost:3000${ansi.reset}`);
735
782
  console.log();
736
- return 1;
783
+ return EXIT.USER_ERROR;
737
784
  }
738
785
 
739
786
  // Initialize spinner outside try block for error handling
@@ -1228,6 +1275,28 @@ async function runScan(args) {
1228
1275
  durationMs: timings.total,
1229
1276
  });
1230
1277
 
1278
+ // Submit results to dashboard if connected
1279
+ if (apiConnected && apiScan) {
1280
+ try {
1281
+ await submitScanResults(apiScan.scanId, {
1282
+ verdict: verdict.verdict,
1283
+ score: report.score?.overall || 0,
1284
+ findings: report.findings || [],
1285
+ filesScanned: report.stats?.filesScanned || 0,
1286
+ linesScanned: report.stats?.linesScanned || 0,
1287
+ durationMs: timings.total,
1288
+ metadata: {
1289
+ layers,
1290
+ profile: opts.profile,
1291
+ version: require('../../package.json').version,
1292
+ },
1293
+ });
1294
+ console.log(`${colors.success}✓${ansi.reset} Results sent to dashboard`);
1295
+ } catch (err) {
1296
+ console.log(`${colors.warning}⚠${ansi.reset} Failed to send results to dashboard: ${err.message}`);
1297
+ }
1298
+ }
1299
+
1231
1300
  return getExitCodeFromUnified ? getExitCodeFromUnified(verdict) : getExitCode(verdict);
1232
1301
  } else {
1233
1302
  // Legacy fallback output when unified output system isn't available
@@ -1316,8 +1385,30 @@ async function runScan(args) {
1316
1385
  issueCount: criticalCount + warningCount,
1317
1386
  durationMs: timings.total,
1318
1387
  });
1388
+
1389
+ // Submit results to dashboard if connected
1390
+ if (apiConnected && apiScan) {
1391
+ try {
1392
+ await submitScanResults(apiScan.scanId, {
1393
+ verdict,
1394
+ score: verdict === 'SHIP' ? 100 : verdict === 'WARN' ? 70 : 40,
1395
+ findings: normalizedLegacyFindings,
1396
+ filesScanned: result.stats?.filesScanned || 0,
1397
+ linesScanned: result.stats?.linesScanned || 0,
1398
+ durationMs: timings.total,
1399
+ metadata: {
1400
+ layers,
1401
+ profile: opts.profile,
1402
+ version: require('../../package.json').version,
1403
+ },
1404
+ });
1405
+ console.log(`${colors.success}✓${ansi.reset} Results sent to dashboard`);
1406
+ } catch (err) {
1407
+ console.log(`${colors.warning}⚠${ansi.reset} Failed to send results to dashboard: ${err.message}`);
1408
+ }
1409
+ }
1319
1410
 
1320
- return verdict === 'SHIP' ? 0 : verdict === 'WARN' ? 1 : 2;
1411
+ return verdictToExitCode(verdict);
1321
1412
  }
1322
1413
 
1323
1414
  } catch (error) {
@@ -1334,6 +1425,16 @@ async function runScan(args) {
1334
1425
  errorMessage: error.message,
1335
1426
  durationMs: Date.now() - startTime,
1336
1427
  });
1428
+
1429
+ // Report error to dashboard if connected
1430
+ if (apiConnected && apiScan) {
1431
+ try {
1432
+ await reportScanError(apiScan.scanId, error);
1433
+ console.log(`${colors.info}📡${ansi.reset} Error reported to dashboard`);
1434
+ } catch (err) {
1435
+ console.log(`${colors.warning}⚠${ansi.reset} Failed to report error to dashboard: ${err.message}`);
1436
+ }
1437
+ }
1337
1438
 
1338
1439
  return exitCode;
1339
1440
  }