@vibecheckai/cli 3.5.0 → 3.5.2

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 (224) hide show
  1. package/bin/registry.js +214 -237
  2. package/bin/runners/cli-utils.js +33 -2
  3. package/bin/runners/context/analyzer.js +52 -1
  4. package/bin/runners/context/generators/cursor.js +2 -49
  5. package/bin/runners/context/git-context.js +3 -1
  6. package/bin/runners/context/team-conventions.js +33 -7
  7. package/bin/runners/lib/analysis-core.js +25 -5
  8. package/bin/runners/lib/analyzers.js +431 -481
  9. package/bin/runners/lib/default-config.js +127 -0
  10. package/bin/runners/lib/doctor/modules/security.js +3 -1
  11. package/bin/runners/lib/engine/ast-cache.js +210 -0
  12. package/bin/runners/lib/engine/auth-extractor.js +211 -0
  13. package/bin/runners/lib/engine/billing-extractor.js +112 -0
  14. package/bin/runners/lib/engine/enforcement-extractor.js +100 -0
  15. package/bin/runners/lib/engine/env-extractor.js +207 -0
  16. package/bin/runners/lib/engine/express-extractor.js +208 -0
  17. package/bin/runners/lib/engine/extractors.js +849 -0
  18. package/bin/runners/lib/engine/index.js +207 -0
  19. package/bin/runners/lib/engine/repo-index.js +514 -0
  20. package/bin/runners/lib/engine/types.js +124 -0
  21. package/bin/runners/lib/engines/accessibility-engine.js +18 -218
  22. package/bin/runners/lib/engines/api-consistency-engine.js +30 -335
  23. package/bin/runners/lib/engines/cross-file-analysis-engine.js +27 -292
  24. package/bin/runners/lib/engines/empty-catch-engine.js +17 -127
  25. package/bin/runners/lib/engines/mock-data-engine.js +10 -53
  26. package/bin/runners/lib/engines/performance-issues-engine.js +36 -176
  27. package/bin/runners/lib/engines/security-vulnerabilities-engine.js +54 -382
  28. package/bin/runners/lib/engines/type-aware-engine.js +39 -263
  29. package/bin/runners/lib/engines/vibecheck-engines/index.js +13 -122
  30. package/bin/runners/lib/engines/vibecheck-engines/lib/ast-cache.js +164 -0
  31. package/bin/runners/lib/engines/vibecheck-engines/lib/code-quality-engine.js +291 -0
  32. package/bin/runners/lib/engines/vibecheck-engines/lib/console-logs-engine.js +83 -0
  33. package/bin/runners/lib/engines/vibecheck-engines/lib/dead-code-engine.js +198 -0
  34. package/bin/runners/lib/engines/vibecheck-engines/lib/deprecated-api-engine.js +275 -0
  35. package/bin/runners/lib/engines/vibecheck-engines/lib/empty-catch-engine.js +167 -0
  36. package/bin/runners/lib/engines/vibecheck-engines/lib/file-filter.js +217 -0
  37. package/bin/runners/lib/engines/vibecheck-engines/lib/hardcoded-secrets-engine.js +73 -373
  38. package/bin/runners/lib/engines/vibecheck-engines/lib/mock-data-engine.js +140 -0
  39. package/bin/runners/lib/engines/vibecheck-engines/lib/parallel-processor.js +164 -0
  40. package/bin/runners/lib/engines/vibecheck-engines/lib/performance-issues-engine.js +234 -0
  41. package/bin/runners/lib/engines/vibecheck-engines/lib/type-aware-engine.js +217 -0
  42. package/bin/runners/lib/engines/vibecheck-engines/lib/unsafe-regex-engine.js +78 -0
  43. package/bin/runners/lib/entitlements-v2.js +73 -97
  44. package/bin/runners/lib/error-handler.js +44 -3
  45. package/bin/runners/lib/error-messages.js +289 -0
  46. package/bin/runners/lib/evidence-pack.js +7 -1
  47. package/bin/runners/lib/finding-id.js +69 -0
  48. package/bin/runners/lib/finding-sorter.js +89 -0
  49. package/bin/runners/lib/html-proof-report.js +700 -350
  50. package/bin/runners/lib/missions/plan.js +6 -46
  51. package/bin/runners/lib/missions/templates.js +0 -232
  52. package/bin/runners/lib/next-action.js +560 -0
  53. package/bin/runners/lib/prerequisites.js +149 -0
  54. package/bin/runners/lib/route-detection.js +137 -68
  55. package/bin/runners/lib/scan-output.js +91 -76
  56. package/bin/runners/lib/scan-runner.js +135 -0
  57. package/bin/runners/lib/schemas/ajv-validator.js +464 -0
  58. package/bin/runners/lib/schemas/error-envelope.schema.json +105 -0
  59. package/bin/runners/lib/schemas/finding-v3.schema.json +151 -0
  60. package/bin/runners/lib/schemas/report-artifact.schema.json +120 -0
  61. package/bin/runners/lib/schemas/run-request.schema.json +108 -0
  62. package/bin/runners/lib/schemas/validator.js +27 -0
  63. package/bin/runners/lib/schemas/verdict.schema.json +140 -0
  64. package/bin/runners/lib/ship-output-enterprise.js +23 -23
  65. package/bin/runners/lib/ship-output.js +75 -31
  66. package/bin/runners/lib/terminal-ui.js +6 -113
  67. package/bin/runners/lib/truth.js +351 -10
  68. package/bin/runners/lib/unified-cli-output.js +430 -603
  69. package/bin/runners/lib/unified-output.js +13 -9
  70. package/bin/runners/runAIAgent.js +10 -5
  71. package/bin/runners/runAgent.js +0 -3
  72. package/bin/runners/runAllowlist.js +389 -0
  73. package/bin/runners/runApprove.js +0 -33
  74. package/bin/runners/runAuth.js +73 -45
  75. package/bin/runners/runCheckpoint.js +51 -11
  76. package/bin/runners/runClassify.js +85 -21
  77. package/bin/runners/runContext.js +0 -3
  78. package/bin/runners/runDoctor.js +41 -28
  79. package/bin/runners/runEvidencePack.js +362 -0
  80. package/bin/runners/runFirewall.js +0 -3
  81. package/bin/runners/runFirewallHook.js +0 -3
  82. package/bin/runners/runFix.js +66 -76
  83. package/bin/runners/runGuard.js +18 -411
  84. package/bin/runners/runInit.js +113 -30
  85. package/bin/runners/runLabs.js +424 -0
  86. package/bin/runners/runMcp.js +19 -25
  87. package/bin/runners/runPolish.js +64 -240
  88. package/bin/runners/runPromptFirewall.js +12 -5
  89. package/bin/runners/runProve.js +57 -22
  90. package/bin/runners/runQuickstart.js +531 -0
  91. package/bin/runners/runReality.js +59 -68
  92. package/bin/runners/runReport.js +38 -33
  93. package/bin/runners/runRuntime.js +8 -5
  94. package/bin/runners/runScan.js +1413 -190
  95. package/bin/runners/runShip.js +113 -719
  96. package/bin/runners/runTruth.js +0 -3
  97. package/bin/runners/runValidate.js +13 -9
  98. package/bin/runners/runWatch.js +23 -14
  99. package/bin/scan.js +6 -1
  100. package/bin/vibecheck.js +204 -185
  101. package/mcp-server/deprecation-middleware.js +282 -0
  102. package/mcp-server/handlers/index.ts +15 -0
  103. package/mcp-server/handlers/tool-handler.ts +554 -0
  104. package/mcp-server/index-v1.js +698 -0
  105. package/mcp-server/index.js +210 -238
  106. package/mcp-server/lib/cache-wrapper.cjs +383 -0
  107. package/mcp-server/lib/error-envelope.js +138 -0
  108. package/mcp-server/lib/executor.ts +499 -0
  109. package/mcp-server/lib/index.ts +19 -0
  110. package/mcp-server/lib/rate-limiter.js +166 -0
  111. package/mcp-server/lib/sandbox.test.ts +519 -0
  112. package/mcp-server/lib/sandbox.ts +395 -0
  113. package/mcp-server/lib/types.ts +267 -0
  114. package/mcp-server/package.json +12 -3
  115. package/mcp-server/registry/tool-registry.js +794 -0
  116. package/mcp-server/registry/tools.json +605 -0
  117. package/mcp-server/registry.test.ts +334 -0
  118. package/mcp-server/tests/tier-gating.test.js +297 -0
  119. package/mcp-server/tier-auth.js +378 -45
  120. package/mcp-server/tools-v3.js +353 -442
  121. package/mcp-server/tsconfig.json +37 -0
  122. package/mcp-server/vibecheck-2.0-tools.js +14 -1
  123. package/package.json +1 -1
  124. package/bin/runners/lib/agent-firewall/learning/learning-engine.js +0 -849
  125. package/bin/runners/lib/audit-logger.js +0 -532
  126. package/bin/runners/lib/authority/authorities/architecture.js +0 -364
  127. package/bin/runners/lib/authority/authorities/compliance.js +0 -341
  128. package/bin/runners/lib/authority/authorities/human.js +0 -343
  129. package/bin/runners/lib/authority/authorities/quality.js +0 -420
  130. package/bin/runners/lib/authority/authorities/security.js +0 -228
  131. package/bin/runners/lib/authority/index.js +0 -293
  132. package/bin/runners/lib/bundle/bundle-intelligence.js +0 -846
  133. package/bin/runners/lib/cli-charts.js +0 -368
  134. package/bin/runners/lib/cli-config-display.js +0 -405
  135. package/bin/runners/lib/cli-demo.js +0 -275
  136. package/bin/runners/lib/cli-errors.js +0 -438
  137. package/bin/runners/lib/cli-help-formatter.js +0 -439
  138. package/bin/runners/lib/cli-interactive-menu.js +0 -509
  139. package/bin/runners/lib/cli-prompts.js +0 -441
  140. package/bin/runners/lib/cli-scan-cards.js +0 -362
  141. package/bin/runners/lib/compliance-reporter.js +0 -710
  142. package/bin/runners/lib/conductor/index.js +0 -671
  143. package/bin/runners/lib/easy/README.md +0 -123
  144. package/bin/runners/lib/easy/index.js +0 -140
  145. package/bin/runners/lib/easy/interactive-wizard.js +0 -788
  146. package/bin/runners/lib/easy/one-click-firewall.js +0 -564
  147. package/bin/runners/lib/easy/zero-config-reality.js +0 -714
  148. package/bin/runners/lib/engines/async-patterns-engine.js +0 -444
  149. package/bin/runners/lib/engines/bundle-size-engine.js +0 -433
  150. package/bin/runners/lib/engines/confidence-scoring.js +0 -276
  151. package/bin/runners/lib/engines/context-detection.js +0 -264
  152. package/bin/runners/lib/engines/database-patterns-engine.js +0 -429
  153. package/bin/runners/lib/engines/duplicate-code-engine.js +0 -354
  154. package/bin/runners/lib/engines/env-variables-engine.js +0 -458
  155. package/bin/runners/lib/engines/error-handling-engine.js +0 -437
  156. package/bin/runners/lib/engines/false-positive-prevention.js +0 -630
  157. package/bin/runners/lib/engines/framework-adapters/index.js +0 -607
  158. package/bin/runners/lib/engines/framework-detection.js +0 -508
  159. package/bin/runners/lib/engines/import-order-engine.js +0 -429
  160. package/bin/runners/lib/engines/naming-conventions-engine.js +0 -544
  161. package/bin/runners/lib/engines/noise-reduction-engine.js +0 -452
  162. package/bin/runners/lib/engines/orchestrator.js +0 -334
  163. package/bin/runners/lib/engines/react-patterns-engine.js +0 -457
  164. package/bin/runners/lib/engines/vibecheck-engines/lib/ai-hallucination-engine.js +0 -806
  165. package/bin/runners/lib/engines/vibecheck-engines/lib/smart-fix-engine.js +0 -577
  166. package/bin/runners/lib/engines/vibecheck-engines/lib/vibe-score-engine.js +0 -543
  167. package/bin/runners/lib/engines/vibecheck-engines.js +0 -514
  168. package/bin/runners/lib/enhanced-features/index.js +0 -305
  169. package/bin/runners/lib/enhanced-output.js +0 -631
  170. package/bin/runners/lib/enterprise.js +0 -300
  171. package/bin/runners/lib/firewall/command-validator.js +0 -351
  172. package/bin/runners/lib/firewall/config.js +0 -341
  173. package/bin/runners/lib/firewall/content-validator.js +0 -519
  174. package/bin/runners/lib/firewall/index.js +0 -101
  175. package/bin/runners/lib/firewall/path-validator.js +0 -256
  176. package/bin/runners/lib/intelligence/cross-repo-intelligence.js +0 -817
  177. package/bin/runners/lib/mcp-utils.js +0 -425
  178. package/bin/runners/lib/output/index.js +0 -1022
  179. package/bin/runners/lib/policy-engine.js +0 -652
  180. package/bin/runners/lib/polish/autofix/accessibility-fixes.js +0 -333
  181. package/bin/runners/lib/polish/autofix/async-handlers.js +0 -273
  182. package/bin/runners/lib/polish/autofix/dead-code.js +0 -280
  183. package/bin/runners/lib/polish/autofix/imports-optimizer.js +0 -344
  184. package/bin/runners/lib/polish/autofix/index.js +0 -200
  185. package/bin/runners/lib/polish/autofix/remove-consoles.js +0 -209
  186. package/bin/runners/lib/polish/autofix/strengthen-types.js +0 -245
  187. package/bin/runners/lib/polish/backend-checks.js +0 -148
  188. package/bin/runners/lib/polish/documentation-checks.js +0 -111
  189. package/bin/runners/lib/polish/frontend-checks.js +0 -168
  190. package/bin/runners/lib/polish/index.js +0 -71
  191. package/bin/runners/lib/polish/infrastructure-checks.js +0 -131
  192. package/bin/runners/lib/polish/library-detection.js +0 -175
  193. package/bin/runners/lib/polish/performance-checks.js +0 -100
  194. package/bin/runners/lib/polish/security-checks.js +0 -148
  195. package/bin/runners/lib/polish/utils.js +0 -203
  196. package/bin/runners/lib/prompt-builder.js +0 -540
  197. package/bin/runners/lib/proof-certificate.js +0 -634
  198. package/bin/runners/lib/reality/accessibility-audit.js +0 -946
  199. package/bin/runners/lib/reality/api-contract-validator.js +0 -1012
  200. package/bin/runners/lib/reality/chaos-engineering.js +0 -1084
  201. package/bin/runners/lib/reality/performance-tracker.js +0 -1077
  202. package/bin/runners/lib/reality/scenario-generator.js +0 -1404
  203. package/bin/runners/lib/reality/visual-regression.js +0 -852
  204. package/bin/runners/lib/reality-profiler.js +0 -717
  205. package/bin/runners/lib/replay/flight-recorder-viewer.js +0 -1160
  206. package/bin/runners/lib/review/ai-code-review.js +0 -832
  207. package/bin/runners/lib/rules/custom-rule-engine.js +0 -985
  208. package/bin/runners/lib/sbom-generator.js +0 -641
  209. package/bin/runners/lib/scan-output-enhanced.js +0 -512
  210. package/bin/runners/lib/security/owasp-scanner.js +0 -939
  211. package/bin/runners/lib/validators/contract-validator.js +0 -283
  212. package/bin/runners/lib/validators/dead-export-detector.js +0 -279
  213. package/bin/runners/lib/validators/dep-audit.js +0 -245
  214. package/bin/runners/lib/validators/env-validator.js +0 -319
  215. package/bin/runners/lib/validators/index.js +0 -120
  216. package/bin/runners/lib/validators/license-checker.js +0 -252
  217. package/bin/runners/lib/validators/route-validator.js +0 -290
  218. package/bin/runners/runAuthority.js +0 -528
  219. package/bin/runners/runConductor.js +0 -772
  220. package/bin/runners/runContainer.js +0 -366
  221. package/bin/runners/runEasy.js +0 -410
  222. package/bin/runners/runIaC.js +0 -372
  223. package/bin/runners/runVibe.js +0 -791
  224. package/mcp-server/tools.js +0 -495
@@ -11,9 +11,6 @@ const fs = require("fs");
11
11
  const path = require("path");
12
12
  const { buildTruthpackV2 } = require("./lib/extractors/truthpack-v2");
13
13
 
14
- // Unified Output System
15
- const { output } = require("./lib/output/index.js");
16
-
17
14
  /**
18
15
  * Run truth command
19
16
  * @param {object} options - Command options
@@ -4,17 +4,21 @@
4
4
 
5
5
  const fs = require("fs");
6
6
  const path = require("path");
7
- const { buildTruthpack } = require("./lib/truth");
7
+ const { buildTruthpackSmart } = require("./lib/truth");
8
8
  const { routeMatches } = require("./lib/claims");
9
9
  const { EXIT } = require("./lib/exit-codes");
10
10
  const { formatSoftUpsell, formatWorkflowUpsell, PRICING_URL } = require("./lib/upsell");
11
11
  const { getApiKey } = require("./lib/auth");
12
12
 
13
- // Terminal UI - Import from shared module
14
- const { c } = require("./lib/terminal-ui");
15
-
16
- // Unified Output System
17
- const { output } = require("./lib/output/index.js");
13
+ const c = {
14
+ reset: "\x1b[0m",
15
+ bold: "\x1b[1m",
16
+ dim: "\x1b[2m",
17
+ green: "\x1b[32m",
18
+ yellow: "\x1b[33m",
19
+ red: "\x1b[31m",
20
+ cyan: "\x1b[36m",
21
+ };
18
22
 
19
23
  function parseArgs(args) {
20
24
  const opts = {
@@ -87,7 +91,7 @@ async function runValidate(args) {
87
91
 
88
92
  try {
89
93
  // Build truthpack for validation
90
- const truthpack = await buildTruthpack({ repoRoot: projectPath });
94
+ const truthpack = await buildTruthpackSmart({ repoRoot: projectPath });
91
95
 
92
96
  const issues = [];
93
97
 
@@ -133,7 +137,7 @@ async function runValidate(args) {
133
137
 
134
138
  // Output results
135
139
  const { key } = getApiKey();
136
- const currentTier = key ? "pro" : "free";
140
+ const currentTier = key ? "starter" : "free";
137
141
 
138
142
  if (opts.json) {
139
143
  console.log(JSON.stringify({ issues, valid: issues.length === 0 }, null, 2));
@@ -158,7 +162,7 @@ async function runValidate(args) {
158
162
  // Upsell for auto-fix
159
163
  if (currentTier === "free") {
160
164
  console.log(` ${c.dim}─────────────────────────────────────────────────────────────${c.reset}`);
161
- console.log(` ${c.magenta}★ PRO${c.reset}${c.dim}: auto-fix hallucinations with vibecheck fix${c.reset}`);
165
+ console.log(` ${c.cyan}★ STARTER${c.reset}${c.dim}: auto-fix hallucinations with vibecheck fix${c.reset}`);
162
166
  console.log(` ${c.dim} Upgrade → ${PRICING_URL}${c.reset}\n`);
163
167
  }
164
168
  }
@@ -19,15 +19,32 @@ const { EXIT } = require("./lib/exit-codes");
19
19
  const { shipCore } = require("./runShip");
20
20
 
21
21
  // ═══════════════════════════════════════════════════════════════════════════════
22
- // TERMINAL UI - Import from shared module
22
+ // ADVANCED TERMINAL - ANSI CODES & UTILITIES
23
23
  // ═══════════════════════════════════════════════════════════════════════════════
24
24
 
25
- const { c, rgb, bgRgb, icons: ICONS_BASE, Spinner } = require("./lib/terminal-ui");
25
+ const c = {
26
+ reset: '\x1b[0m',
27
+ bold: '\x1b[1m',
28
+ dim: '\x1b[2m',
29
+ italic: '\x1b[3m',
30
+ underline: '\x1b[4m',
31
+ red: '\x1b[31m',
32
+ green: '\x1b[32m',
33
+ yellow: '\x1b[33m',
34
+ blue: '\x1b[34m',
35
+ magenta: '\x1b[35m',
36
+ cyan: '\x1b[36m',
37
+ white: '\x1b[37m',
38
+ gray: '\x1b[90m',
39
+ clear: '\x1b[2J\x1b[H',
40
+ clearLine: '\x1b[2K',
41
+ hideCursor: '\x1b[?25l',
42
+ showCursor: '\x1b[?25h',
43
+ };
26
44
 
27
- // Unified Output System
28
- const { output } = require("./lib/output/index.js");
45
+ const rgb = (r, g, b) => `\x1b[38;2;${r};${g};${b}m`;
46
+ const bgRgb = (r, g, b) => `\x1b[48;2;${r};${g};${b}m`;
29
47
 
30
- // Extended color palette for watch command
31
48
  const colors = {
32
49
  gradient1: rgb(150, 100, 255),
33
50
  gradient2: rgb(130, 80, 255),
@@ -206,7 +223,7 @@ function printTopFindings(findings, max = 5) {
206
223
  const blockers = findings.filter(f => f.severity === "BLOCK").length;
207
224
  if (blockers > 0) {
208
225
  console.log();
209
- console.log(` ${colors.cyan}→${c.reset} ${c.dim}Fix these with${c.reset} ${colors.cyan}vibecheck fix${c.reset} ${c.dim}(PRO)${c.reset}`);
226
+ console.log(` ${colors.cyan}→${c.reset} ${c.dim}Fix these with${c.reset} ${colors.cyan}vibecheck fix${c.reset} ${c.dim}(STARTER)${c.reset}`);
210
227
  }
211
228
  console.log();
212
229
  }
@@ -321,14 +338,6 @@ async function runWatch(argsOrOpts = {}) {
321
338
  rawArgs = argsOrOpts;
322
339
  const { flags } = parseGlobalFlags(argsOrOpts);
323
340
  globalOpts = { ...globalOpts, ...flags };
324
-
325
- // Configure unified output mode
326
- output.setMode({
327
- json: globalOpts.json,
328
- quiet: globalOpts.quiet,
329
- ci: globalOpts.ci
330
- });
331
-
332
341
  if (globalOpts.help) {
333
342
  printHelp(globalOpts);
334
343
  return EXIT.SUCCESS;
package/bin/scan.js CHANGED
@@ -47,7 +47,12 @@ function getScanHistory(projectPath) {
47
47
  if (fs.existsSync(historyFile)) {
48
48
  return JSON.parse(fs.readFileSync(historyFile, "utf8"));
49
49
  }
50
- } catch (e) {}
50
+ } catch (e) {
51
+ // Corrupted or unreadable history file - log in debug mode and reset
52
+ if (process.env.DEBUG) {
53
+ console.warn(`[scan] Could not read scan history: ${e.message}`);
54
+ }
55
+ }
51
56
  return { scans: [], lastScore: null };
52
57
  }
53
58
 
package/bin/vibecheck.js CHANGED
@@ -800,10 +800,17 @@ ${c.bold}Installation:${c.reset}
800
800
  function printBanner() {
801
801
  const VERSION = getVersion();
802
802
  console.log(`
803
- ${c.dim}${sym.boxTopLeft}${sym.boxHorizontal.repeat(60)}${sym.boxTopRight}${c.reset}
804
- ${c.dim}${sym.boxVertical}${c.reset} ${gradient("VIBECHECK", [[0, 255, 255], [138, 43, 226], [255, 20, 147]])} ${c.dim}v${VERSION}${c.reset}${" ".repeat(60 - 13 - VERSION.length - 4)}${c.dim}${sym.boxVertical}${c.reset}
805
- ${c.dim}${sym.boxVertical}${c.reset} ${c.dim}Ship with confidence. Catch fake features before users do.${c.reset} ${c.dim}${sym.boxVertical}${c.reset}
806
- ${c.dim}${sym.boxBottomLeft}${sym.boxHorizontal.repeat(60)}${sym.boxBottomRight}${c.reset}
803
+ ${c.dim}${sym.boxTopLeft}${sym.boxHorizontal.repeat(64)}${sym.boxTopRight}${c.reset}
804
+ ${c.dim}${sym.boxVertical}${c.reset} ${gradient("VIBECHECK", [[0, 255, 255], [138, 43, 226], [255, 20, 147]])} ${c.dim}v${VERSION}${c.reset}${" ".repeat(64 - 13 - VERSION.length - 4)}${c.dim}${sym.boxVertical}${c.reset}
805
+ ${c.dim}${sym.boxVertical}${c.reset} ${c.dim}${sym.boxVertical}${c.reset}
806
+ ${c.dim}${sym.boxVertical}${c.reset} ${c.bold}Catch AI hallucinations before your users do.${c.reset} ${c.dim}${sym.boxVertical}${c.reset}
807
+ ${c.dim}${sym.boxVertical}${c.reset} ${c.dim}${sym.boxVertical}${c.reset}
808
+ ${c.dim}${sym.boxVertical}${c.reset} ${c.cyan}${sym.check}${c.reset} Detects routes that look real but don't work ${c.dim}${sym.boxVertical}${c.reset}
809
+ ${c.dim}${sym.boxVertical}${c.reset} ${c.cyan}${sym.check}${c.reset} Finds env vars used but never declared ${c.dim}${sym.boxVertical}${c.reset}
810
+ ${c.dim}${sym.boxVertical}${c.reset} ${c.cyan}${sym.check}${c.reset} Flags auth endpoints with no protection ${c.dim}${sym.boxVertical}${c.reset}
811
+ ${c.dim}${sym.boxVertical}${c.reset} ${c.dim}${sym.boxVertical}${c.reset}
812
+ ${c.dim}${sym.boxVertical}${c.reset} ${c.dim}SHIP = proof your code works. Not a vibe check.${c.reset} ${c.dim}${sym.boxVertical}${c.reset}
813
+ ${c.dim}${sym.boxBottomLeft}${sym.boxHorizontal.repeat(64)}${sym.boxBottomRight}${c.reset}
807
814
  `);
808
815
  }
809
816
 
@@ -883,74 +890,19 @@ function printHelp(showBanner = true) {
883
890
  reverseAliases[target].push(alias);
884
891
  }
885
892
 
886
- // Show beginner tip first
887
- console.log(`
888
- ${c.yellow}${sym.star} NEW TO VIBECHECK?${c.reset}
889
-
890
- Run ${c.cyan}${c.bold}vibecheck${c.reset} with no arguments to open the ${c.bold}interactive menu${c.reset}.
891
- Or follow the Quick Start guide below.
892
-
893
- ${c.dim}${sym.boxHorizontal.repeat(68)}${c.reset}
894
- `);
895
-
896
- // Quick Start section (most important for beginners)
897
- console.log(`${c.green}${sym.rocket} QUICK START${c.reset} ${c.dim}(Get running in 60 seconds)${c.reset}
898
-
899
- ${c.bold}${c.white}Step 1:${c.reset} ${c.cyan}vibecheck init${c.reset}
900
- ${c.dim}Set up vibecheck for your project${c.reset}
901
-
902
- ${c.bold}${c.white}Step 2:${c.reset} ${c.cyan}vibecheck scan${c.reset}
903
- ${c.dim}Analyze your codebase for issues${c.reset}
904
-
905
- ${c.bold}${c.white}Step 3:${c.reset} ${c.cyan}vibecheck ship${c.reset} ${c.magenta}[PRO]${c.reset}
906
- ${c.dim}Get final SHIP/WARN/BLOCK verdict${c.reset}
907
- `);
908
-
909
- // Pricing tiers (simple 2-tier)
910
- console.log(`${c.dim}${sym.boxHorizontal.repeat(68)}${c.reset}
911
-
912
- ${c.bold}PRICING${c.reset}
913
-
914
- ${c.green}${c.bold}FREE${c.reset} ${c.dim}$0${c.reset} Scan, Report, Context, Doctor
915
- ${c.magenta}${c.bold}PRO${c.reset} ${c.dim}$69/mo${c.reset} Ship verdicts, Auto-fix, Runtime proof, CI gates
916
- ${c.dim}→ ${c.cyan}https://vibecheckai.dev/pricing${c.reset}
917
- `);
918
-
919
- // Most-used commands
920
- console.log(`${c.dim}${sym.boxHorizontal.repeat(68)}${c.reset}
921
-
922
- ${c.bold}MOST USED COMMANDS${c.reset}
923
- `);
924
-
925
- const popularCommands = [
926
- { cmd: "scan", badge: "FREE", desc: "Analyze codebase for issues (routes, env, auth, mocks)" },
927
- { cmd: "ship", badge: "PRO", desc: "Get SHIP/WARN/BLOCK verdict with evidence" },
928
- { cmd: "fix", badge: "PRO", desc: "AI-powered auto-fix for detected issues" },
929
- { cmd: "doctor", badge: "FREE", desc: "Check environment and config health" },
930
- { cmd: "report", badge: "FREE", desc: "Generate HTML/JSON/SARIF reports" },
931
- { cmd: "prove", badge: "PRO", desc: "Runtime verification with Playwright" },
932
- ];
933
-
934
- for (const { cmd, badge, desc } of popularCommands) {
935
- const badgeStr = badge === "FREE"
936
- ? `${c.green}[FREE]${c.reset}`
937
- : `${c.magenta}[PRO]${c.reset}`;
938
- console.log(` ${c.cyan}${cmd.padEnd(10)}${c.reset} ${badgeStr} ${c.dim}${desc}${c.reset}`);
939
- }
940
-
941
- // Categories with all commands
893
+ // Categories ordered as specified
942
894
  const categoryOrder = ["setup", "analysis", "proof", "quality", "truth", "output", "ci", "automation", "account", "extras"];
943
895
  const categories = {
944
- setup: { name: "SETUP & CONFIG", color: c.yellow, icon: sym.gear },
896
+ setup: { name: "SETUP", color: c.yellow, icon: sym.gear },
945
897
  analysis: { name: "ANALYSIS", color: c.cyan, icon: sym.chart },
946
- proof: { name: "PROOF & VERIFICATION", color: c.green, icon: sym.shield },
898
+ proof: { name: "PROOF LOOP", color: c.green, icon: sym.shield },
947
899
  quality: { name: "QUALITY", color: c.brightGreen, icon: sym.star },
948
- truth: { name: "AI GUARDRAILS", color: c.magenta, icon: sym.lightning },
949
- output: { name: "OUTPUT & REPORTS", color: c.blue, icon: sym.file },
950
- ci: { name: "CI/CD", color: c.cyan, icon: sym.rocket },
900
+ truth: { name: "AI TRUTH", color: c.magenta, icon: sym.lightning },
901
+ output: { name: "OUTPUT", color: c.blue, icon: sym.file },
902
+ ci: { name: "CI & COLLABORATION", color: c.cyan, icon: sym.rocket },
951
903
  automation: { name: "AUTOMATION", color: c.brightBlue, icon: sym.fire },
952
- account: { name: "ACCOUNT", color: c.gray, icon: sym.key },
953
- extras: { name: "EXTRAS", color: c.gray, icon: sym.starEmpty },
904
+ account: { name: "ACCOUNT", color: c.dim, icon: sym.key },
905
+ extras: { name: "EXTRAS", color: c.dim, icon: sym.starEmpty },
954
906
  };
955
907
 
956
908
  // Group commands
@@ -961,74 +913,126 @@ ${c.bold}MOST USED COMMANDS${c.reset}
961
913
  grouped[cat].push({ cmd, ...def });
962
914
  }
963
915
 
964
- console.log(`
965
- ${c.dim}${sym.boxHorizontal.repeat(68)}${c.reset}
966
-
967
- ${c.bold}ALL COMMANDS${c.reset}
968
- `);
969
-
970
916
  // Print in order
971
917
  for (const catKey of categoryOrder) {
972
918
  const commands = grouped[catKey];
973
919
  if (!commands || commands.length === 0) continue;
974
920
 
975
921
  const cat = categories[catKey];
976
- console.log(` ${cat.color}${cat.icon} ${cat.name}${c.reset}`);
922
+ console.log(`\n${cat.color}${cat.icon} ${cat.name}${c.reset}\n`);
977
923
 
978
- for (const { cmd, description, tier } of commands) {
924
+ for (const { cmd, description, tier, caps } of commands) {
979
925
  // Tier badge (2-tier: FREE / PRO)
980
926
  let tierBadge = "";
981
- if (tier === "free") tierBadge = `${c.green}[FREE]${c.reset}`;
982
- else if (tier === "pro") tierBadge = `${c.magenta}[PRO]${c.reset}`;
927
+ if (tier === "free") tierBadge = `${c.green}[FREE]${c.reset} `;
928
+ else if (tier === "pro") tierBadge = `${c.magenta}[PRO]${c.reset} `;
983
929
 
984
- // Aliases
930
+ // Aliases (from reverseAliases map built earlier)
985
931
  const aliasList = reverseAliases[cmd] || [];
986
932
  const aliasStr = aliasList.length > 0
987
- ? `${c.dim}(${aliasList.slice(0, 2).join(", ")})${c.reset}`
933
+ ? `${c.dim}(${aliasList.slice(0, 3).join(", ")})${c.reset} `
988
934
  : "";
989
935
 
990
- console.log(` ${c.cyan}${cmd.padEnd(12)}${c.reset} ${tierBadge.padEnd(15)} ${description} ${aliasStr}`);
936
+ // Caps info
937
+ const capsStr = caps ? `${c.dim}(${caps})${c.reset}` : "";
938
+
939
+ console.log(` ${c.cyan}${cmd.padEnd(12)}${c.reset} ${aliasStr}${tierBadge}${description} ${capsStr}`);
991
940
  }
992
- console.log();
993
941
  }
994
942
 
995
- // Global options
996
- console.log(`${c.bold}GLOBAL OPTIONS${c.reset}
943
+ console.log(`
944
+ ${c.dim}${sym.boxHorizontal.repeat(64)}${c.reset}
945
+
946
+ ${c.green}${sym.star} PRICING TIERS${c.reset}
947
+
948
+ ${c.green}FREE${c.reset} ${c.dim}$0${c.reset} scan, ship, report, context, doctor, polish - full visibility
949
+ ${c.magenta}PRO${c.reset} ${c.dim}$69/mo${c.reset} + autofix, prove, reality, mcp, guard, ai-test, PR comments
950
+
951
+ ${c.green}${sym.rocket} QUICK START (2 minutes to first proof)${c.reset}
952
+
953
+ ${c.bold}Option 1: One command${c.reset}
954
+ ${c.cyan}vibecheck quickstart${c.reset} ${c.dim}Runs doctor → ctx → scan → ship → report${c.reset}
955
+
956
+ ${c.bold}Option 2: Step by step${c.reset}
957
+ ${c.bold}1.${c.reset} ${c.cyan}vibecheck init${c.reset} ${c.dim}Set up your project${c.reset}
958
+ ${c.bold}2.${c.reset} ${c.cyan}vibecheck scan${c.reset} ${c.dim}Analyze your codebase${c.reset}
959
+ ${c.bold}3.${c.reset} ${c.cyan}vibecheck ship${c.reset} ${c.dim}Get shipping verdict${c.reset}
960
+
961
+ ${c.green}${sym.lightning} COMMON WORKFLOWS${c.reset}
962
+
963
+ ${c.dim}# Quick health check${c.reset}
964
+ ${c.cyan}vibecheck doctor${c.reset}
965
+
966
+ ${c.dim}# Scan and auto-fix${c.reset}
967
+ ${c.cyan}vibecheck scan --autofix${c.reset}
968
+
969
+ ${c.dim}# Full proof with evidence pack${c.reset}
970
+ ${c.cyan}vibecheck prove --url http://localhost:3000 --bundle${c.reset}
971
+
972
+ ${c.bold}GLOBAL OPTIONS${c.reset}
997
973
 
998
974
  ${c.cyan}--help, -h${c.reset} Show help for any command
999
975
  ${c.cyan}--json${c.reset} Machine-readable JSON output
1000
976
  ${c.cyan}--quiet, -q${c.reset} Suppress non-essential output
1001
977
  ${c.cyan}--verbose${c.reset} Detailed output for debugging
1002
978
  ${c.cyan}--ci${c.reset} CI mode (quiet + no-banner)
979
+ ${c.cyan}--offline${c.reset} Run without API connection
1003
980
  ${c.cyan}--path, -p <dir>${c.reset} Run in specified directory
1004
- `);
1005
-
1006
- // Common workflows
1007
- console.log(`${c.bold}COMMON WORKFLOWS${c.reset}
1008
-
1009
- ${c.dim}# First-time setup${c.reset}
1010
- ${c.cyan}vibecheck init${c.reset}
1011
981
 
1012
- ${c.dim}# Daily development${c.reset}
1013
- ${c.cyan}vibecheck scan${c.reset}
982
+ ${c.bold}SHELL COMPLETIONS${c.reset}
1014
983
 
1015
- ${c.dim}# Before shipping${c.reset}
1016
- ${c.cyan}vibecheck ship${c.reset}
984
+ ${c.cyan}vibecheck completion bash${c.reset} ${c.dim}# Bash (add to ~/.bashrc)${c.reset}
985
+ ${c.cyan}vibecheck completion zsh${c.reset} ${c.dim}# Zsh (add to ~/.zshrc)${c.reset}
986
+ ${c.cyan}vibecheck completion fish${c.reset} ${c.dim}# Fish (save to completions)${c.reset}
1017
987
 
1018
- ${c.dim}# Auto-fix issues${c.reset}
1019
- ${c.cyan}vibecheck fix --apply${c.reset}
1020
-
1021
- ${c.dim}# CI/CD pipeline${c.reset}
1022
- ${c.cyan}vibecheck gate --strict${c.reset}
988
+ ${c.dim}${sym.boxHorizontal.repeat(64)}${c.reset}
989
+ ${c.dim}Run 'vibecheck <command> --help' for detailed command help.${c.reset}
990
+ ${c.dim}Documentation: https://docs.vibecheckai.dev${c.reset}
991
+ ${c.dim}Pricing: https://vibecheckai.dev/pricing${c.reset}
1023
992
  `);
993
+ }
1024
994
 
1025
- // Footer
1026
- console.log(`${c.dim}${sym.boxHorizontal.repeat(68)}${c.reset}
1027
- ${c.dim}Run ${c.cyan}vibecheck <command> --help${c.reset}${c.dim} for detailed command help.${c.reset}
1028
- ${c.dim}Interactive menu: ${c.cyan}vibecheck menu${c.reset}${c.dim} or just ${c.cyan}vibecheck${c.reset}
1029
- ${c.dim}Documentation: ${c.underline}https://docs.vibecheckai.dev${c.reset}
1030
- ${c.dim}Upgrade to Pro: ${c.underline}https://vibecheckai.dev/pricing${c.reset}
1031
- `);
995
+ // ═══════════════════════════════════════════════════════════════════════════════
996
+ // HELP VALIDATION
997
+ // ═══════════════════════════════════════════════════════════════════════════════
998
+
999
+ /**
1000
+ * Self-test function to verify help output matches actual commands
1001
+ * Run with: node bin/vibecheck.js --self-test
1002
+ */
1003
+ function validateHelpOutput() {
1004
+ const registry = getRegistry();
1005
+ const { COMMANDS, ALIAS_MAP } = registry;
1006
+
1007
+ // Extract commands from help text (simplified - just check registry)
1008
+ const actualCommands = Object.keys(COMMANDS);
1009
+ const actualAliases = Object.keys(ALIAS_MAP);
1010
+ const allActualCommands = [...actualCommands, ...actualAliases];
1011
+
1012
+ // Check for obvious issues
1013
+ const issues = [];
1014
+
1015
+ // Check that all commands have runners
1016
+ for (const [cmd, def] of Object.entries(COMMANDS)) {
1017
+ if (!def.runner) {
1018
+ issues.push(`Command '${cmd}' missing runner`);
1019
+ }
1020
+ }
1021
+
1022
+ // Check that aliases point to valid commands
1023
+ for (const [alias, target] of Object.entries(ALIAS_MAP)) {
1024
+ if (!COMMANDS[target]) {
1025
+ issues.push(`Alias '${alias}' points to non-existent command '${target}'`);
1026
+ }
1027
+ }
1028
+
1029
+ if (issues.length > 0) {
1030
+ console.error('Help output validation failed:');
1031
+ issues.forEach(issue => console.error(` - ${issue}`));
1032
+ return false;
1033
+ }
1034
+
1035
+ return true;
1032
1036
  }
1033
1037
 
1034
1038
  // ═══════════════════════════════════════════════════════════════════════════════
@@ -1057,6 +1061,50 @@ function formatError(error, config) {
1057
1061
  return lines.join("\n");
1058
1062
  }
1059
1063
 
1064
+ /**
1065
+ * Map error to standardized exit code
1066
+ * @param {Error} error - Error object
1067
+ * @param {boolean} json - Whether JSON output is requested
1068
+ * @returns {number} Exit code
1069
+ */
1070
+ function mapErrorToExitCode(error, json = false) {
1071
+ const EXIT_CODES = {
1072
+ 'AUTH_REQUIRED': 5,
1073
+ 'AUTH_FAILED': 6,
1074
+ 'TIER_REQUIRED': 7,
1075
+ 'RATE_LIMITED': 8,
1076
+ 'NOT_FOUND': 4,
1077
+ 'VALIDATION_ERROR': 3,
1078
+ 'USER_ERROR': 3,
1079
+ 'NETWORK_ERROR': 9,
1080
+ };
1081
+
1082
+ // Check error.code first
1083
+ if (error.code && EXIT_CODES[error.code]) {
1084
+ return EXIT_CODES[error.code];
1085
+ }
1086
+
1087
+ // Check error.exitCode
1088
+ if (error.exitCode !== undefined) {
1089
+ return error.exitCode;
1090
+ }
1091
+
1092
+ // Check error message for common patterns
1093
+ const message = error.message?.toLowerCase() || '';
1094
+ if (message.includes('not found') || message.includes('not exist')) {
1095
+ return 4; // NOT_FOUND
1096
+ }
1097
+ if (message.includes('auth') || message.includes('login')) {
1098
+ return 5; // AUTH_REQUIRED
1099
+ }
1100
+ if (message.includes('network') || message.includes('connection')) {
1101
+ return 9; // NETWORK_ERROR
1102
+ }
1103
+
1104
+ // Default to internal error
1105
+ return 10; // INTERNAL_ERROR
1106
+ }
1107
+
1060
1108
  // ═══════════════════════════════════════════════════════════════════════════════
1061
1109
  // FLAG PARSING
1062
1110
  // ═══════════════════════════════════════════════════════════════════════════════
@@ -1156,6 +1204,16 @@ async function main() {
1156
1204
  process.exit(0);
1157
1205
  }
1158
1206
 
1207
+ // Handle self-test flag (for CI validation)
1208
+ if (cleanArgs.includes('--self-test')) {
1209
+ const registry = getRegistry();
1210
+ if (!validateHelpOutput()) {
1211
+ process.exit(1);
1212
+ }
1213
+ console.log('Self-test passed: Help output matches command registry');
1214
+ process.exit(0);
1215
+ }
1216
+
1159
1217
  // Load config and state
1160
1218
  const config = loadConfig();
1161
1219
  const state = loadState();
@@ -1170,57 +1228,10 @@ async function main() {
1170
1228
  config.noBanner = true;
1171
1229
  }
1172
1230
 
1173
- // ═══════════════════════════════════════════════════════════════════════════
1174
- // EASY MODE - Super simple commands for non-technical users
1175
- // ═══════════════════════════════════════════════════════════════════════════
1176
- const easyModeCommands = ['test', 'protect', 'status'];
1177
- const firstArg = cleanArgs[0]?.toLowerCase();
1178
-
1179
- // Check if this is an easy mode command or URL
1180
- const isEasyCommand = easyModeCommands.includes(firstArg);
1181
- const isDirectUrl = firstArg && (firstArg.startsWith('http') || firstArg.includes('localhost'));
1182
-
1183
- if (isEasyCommand || isDirectUrl) {
1184
- try {
1185
- const { main: runEasy } = require("./runners/runEasy");
1186
- await runEasy();
1187
- return;
1188
- } catch (err) {
1189
- if (config.debug) console.error(`Easy mode error: ${err.message}`);
1190
- // Fall through to normal handling
1191
- }
1192
- }
1193
-
1194
- // Handle no command - show EASY wizard for beginners (much friendlier)
1231
+ // Handle no command
1195
1232
  if (!cleanArgs[0]) {
1196
- // In TTY mode, show the EASY wizard for new users
1197
- if (process.stdout.isTTY && !config.quiet && !config.ci) {
1198
- try {
1199
- // Try easy wizard first (friendlier for new users)
1200
- const { Wizard } = require("./runners/lib/easy/interactive-wizard");
1201
- const wizard = new Wizard();
1202
- await wizard.start();
1203
- return;
1204
- } catch {
1205
- // Fall back to original interactive menu
1206
- try {
1207
- const { runInteractiveMenu } = require("./runners/lib/cli-interactive-menu");
1208
- runInteractiveMenu().then(() => process.exit(0)).catch(() => {
1209
- printHelp(!config.noBanner);
1210
- process.exit(0);
1211
- });
1212
- return; // Let async menu run
1213
- } catch {
1214
- // Fall back to help if menu module not available
1215
- printHelp(!config.noBanner);
1216
- process.exit(0);
1217
- }
1218
- }
1219
- } else {
1220
- // Non-TTY or quiet mode: show help
1221
- printHelp(!config.noBanner);
1222
- process.exit(0);
1223
- }
1233
+ printHelp(!config.noBanner);
1234
+ process.exit(0);
1224
1235
  }
1225
1236
 
1226
1237
  // Handle global help
@@ -1228,28 +1239,6 @@ async function main() {
1228
1239
  printHelp(!config.noBanner);
1229
1240
  process.exit(0);
1230
1241
  }
1231
-
1232
- // Handle explicit menu command
1233
- if (cleanArgs[0] === "menu") {
1234
- if (process.stdout.isTTY) {
1235
- try {
1236
- const { runInteractiveMenu } = require("./runners/lib/cli-interactive-menu");
1237
- runInteractiveMenu().then(() => process.exit(0)).catch((err) => {
1238
- console.error(`Menu error: ${err.message}`);
1239
- process.exit(1);
1240
- });
1241
- return;
1242
- } catch (err) {
1243
- console.error(`${c.red}Error:${c.reset} Interactive menu not available: ${err.message}`);
1244
- console.log(`${c.dim}Use 'vibecheck --help' to see available commands.${c.reset}`);
1245
- process.exit(1);
1246
- }
1247
- } else {
1248
- console.error(`${c.yellow}Warning:${c.reset} Interactive menu requires a terminal.`);
1249
- console.log(`${c.dim}Use 'vibecheck --help' to see available commands.${c.reset}`);
1250
- process.exit(1);
1251
- }
1252
- }
1253
1242
 
1254
1243
  // Handle completion command (special - doesn't need registry loaded normally)
1255
1244
  if (cleanArgs[0] === "completion") {
@@ -1318,10 +1307,16 @@ async function main() {
1318
1307
  const cmdDef = COMMANDS[cmd];
1319
1308
  let authInfo = { key: null };
1320
1309
 
1321
- // Check for offline mode (via flag or env var)
1310
+ // Check for offline/dev mode (via flag or env var)
1311
+ // SECURITY: VIBECHECK_DEV_PRO only works in non-production to prevent auth bypass
1312
+ const isDevProAllowed = process.env.NODE_ENV !== 'production' &&
1313
+ process.env.CI !== 'true' &&
1314
+ process.env.CI !== '1' &&
1315
+ process.env.VIBECHECK_DEV_PRO === '1';
1322
1316
  const isOffline = globalFlags.offline ||
1323
1317
  process.env.VIBECHECK_OFFLINE === '1' ||
1324
- process.env.VIBECHECK_LOCAL === '1';
1318
+ process.env.VIBECHECK_LOCAL === '1' ||
1319
+ isDevProAllowed;
1325
1320
 
1326
1321
  // Auth check (unless skipAuth or offline mode)
1327
1322
  // Only allow login, logout, whoami, and help without auth
@@ -1383,7 +1378,9 @@ async function main() {
1383
1378
  authInfo.access = access;
1384
1379
  } else if (isOffline) {
1385
1380
  // Offline mode - provide basic access info
1386
- if (!config.quiet && !config.noBanner) {
1381
+ // Suppress message if JSON/SARIF output is requested (check both global flags and command args)
1382
+ const hasJsonOutput = globalFlags.json || cmdArgs.includes('--json') || cmdArgs.includes('--sarif');
1383
+ if (!config.quiet && !config.noBanner && !hasJsonOutput) {
1387
1384
  console.log(`${c.cyan}${sym.arrowRight} OFFLINE${c.reset} ${c.dim}mode - local scanning without API${c.reset}`);
1388
1385
  }
1389
1386
  authInfo.access = {
@@ -1412,7 +1409,7 @@ async function main() {
1412
1409
  const runner = getRunner(cmd);
1413
1410
  if (!runner) {
1414
1411
  console.error(`${c.red}${sym.error}${c.reset} Failed to load runner for: ${cmd}`);
1415
- process.exit(1);
1412
+ process.exit(4); // NOT_FOUND
1416
1413
  }
1417
1414
 
1418
1415
  const context = {
@@ -1427,10 +1424,32 @@ async function main() {
1427
1424
  };
1428
1425
 
1429
1426
  // Execute command - all commands use consistent runner signature
1430
- exitCode = await runner(cmdArgs, context);
1427
+ const result = await runner(cmdArgs, context);
1428
+
1429
+ // Ensure result has standardized exit code
1430
+ if (typeof result === 'object' && result.exitCode !== undefined) {
1431
+ exitCode = result.exitCode;
1432
+ } else if (typeof result === 'number') {
1433
+ exitCode = result;
1434
+ } else {
1435
+ exitCode = 0; // SUCCESS
1436
+ }
1431
1437
  } catch (error) {
1432
- console.error(formatError(error, config));
1433
- exitCode = 1;
1438
+ // Map errors to exit codes
1439
+ exitCode = mapErrorToExitCode(error, globalFlags.json);
1440
+
1441
+ if (globalFlags.json) {
1442
+ console.log(JSON.stringify({
1443
+ success: false,
1444
+ error: {
1445
+ code: error.code || 'INTERNAL_ERROR',
1446
+ message: error.message,
1447
+ },
1448
+ exitCode,
1449
+ }, null, 2));
1450
+ } else {
1451
+ console.error(formatError(error, config));
1452
+ }
1434
1453
  }
1435
1454
 
1436
1455
  // Create receipt for paid commands