vibecheck-ai 2.0.2 → 5.0.1

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 (456) hide show
  1. package/bin/.generated +25 -0
  2. package/bin/_deprecations.js +463 -0
  3. package/bin/_router.js +46 -0
  4. package/bin/cli-hygiene.js +241 -0
  5. package/bin/dev/run-v2-torture.js +30 -0
  6. package/bin/registry.js +656 -0
  7. package/bin/runners/CLI_REFACTOR_SUMMARY.md +229 -0
  8. package/bin/runners/ENHANCEMENT_GUIDE.md +121 -0
  9. package/bin/runners/REPORT_AUDIT.md +64 -0
  10. package/bin/runners/cli-utils.js +1070 -0
  11. package/bin/runners/context/ai-task-decomposer.js +337 -0
  12. package/bin/runners/context/analyzer.js +513 -0
  13. package/bin/runners/context/api-contracts.js +427 -0
  14. package/bin/runners/context/context-diff.js +342 -0
  15. package/bin/runners/context/context-pruner.js +291 -0
  16. package/bin/runners/context/dependency-graph.js +414 -0
  17. package/bin/runners/context/generators/claude.js +107 -0
  18. package/bin/runners/context/generators/codex.js +108 -0
  19. package/bin/runners/context/generators/copilot.js +119 -0
  20. package/bin/runners/context/generators/cursor-enhanced.js +2525 -0
  21. package/bin/runners/context/generators/cursor.js +514 -0
  22. package/bin/runners/context/generators/mcp.js +169 -0
  23. package/bin/runners/context/generators/windsurf.js +180 -0
  24. package/bin/runners/context/git-context.js +304 -0
  25. package/bin/runners/context/index.js +1110 -0
  26. package/bin/runners/context/insights.js +173 -0
  27. package/bin/runners/context/mcp-server/generate-rules.js +337 -0
  28. package/bin/runners/context/mcp-server/index.js +1176 -0
  29. package/bin/runners/context/mcp-server/package.json +24 -0
  30. package/bin/runners/context/memory.js +200 -0
  31. package/bin/runners/context/monorepo.js +215 -0
  32. package/bin/runners/context/multi-repo-federation.js +404 -0
  33. package/bin/runners/context/patterns.js +253 -0
  34. package/bin/runners/context/proof-context.js +1264 -0
  35. package/bin/runners/context/security-scanner.js +541 -0
  36. package/bin/runners/context/semantic-search.js +350 -0
  37. package/bin/runners/context/shared.js +264 -0
  38. package/bin/runners/context/team-conventions.js +336 -0
  39. package/bin/runners/lib/__tests__/entitlements-v2.test.js +295 -0
  40. package/bin/runners/lib/agent-firewall/ai/false-positive-analyzer.js +474 -0
  41. package/bin/runners/lib/agent-firewall/change-packet/builder.js +488 -0
  42. package/bin/runners/lib/agent-firewall/change-packet/schema.json +228 -0
  43. package/bin/runners/lib/agent-firewall/change-packet/store.js +200 -0
  44. package/bin/runners/lib/agent-firewall/claims/claim-types.js +21 -0
  45. package/bin/runners/lib/agent-firewall/claims/extractor.js +303 -0
  46. package/bin/runners/lib/agent-firewall/claims/patterns.js +24 -0
  47. package/bin/runners/lib/agent-firewall/critic/index.js +151 -0
  48. package/bin/runners/lib/agent-firewall/critic/judge.js +432 -0
  49. package/bin/runners/lib/agent-firewall/critic/prompts.js +305 -0
  50. package/bin/runners/lib/agent-firewall/enforcement/gateway.js +1059 -0
  51. package/bin/runners/lib/agent-firewall/enforcement/index.js +98 -0
  52. package/bin/runners/lib/agent-firewall/enforcement/mode.js +318 -0
  53. package/bin/runners/lib/agent-firewall/enforcement/orchestrator.js +484 -0
  54. package/bin/runners/lib/agent-firewall/enforcement/proof-artifact.js +418 -0
  55. package/bin/runners/lib/agent-firewall/enforcement/schemas/change-event.schema.json +173 -0
  56. package/bin/runners/lib/agent-firewall/enforcement/schemas/intent.schema.json +181 -0
  57. package/bin/runners/lib/agent-firewall/enforcement/schemas/verdict.schema.json +222 -0
  58. package/bin/runners/lib/agent-firewall/enforcement/verdict-v2.js +333 -0
  59. package/bin/runners/lib/agent-firewall/evidence/auth-evidence.js +88 -0
  60. package/bin/runners/lib/agent-firewall/evidence/contract-evidence.js +75 -0
  61. package/bin/runners/lib/agent-firewall/evidence/env-evidence.js +127 -0
  62. package/bin/runners/lib/agent-firewall/evidence/resolver.js +102 -0
  63. package/bin/runners/lib/agent-firewall/evidence/route-evidence.js +213 -0
  64. package/bin/runners/lib/agent-firewall/evidence/side-effect-evidence.js +145 -0
  65. package/bin/runners/lib/agent-firewall/fs-hook/daemon.js +19 -0
  66. package/bin/runners/lib/agent-firewall/fs-hook/installer.js +87 -0
  67. package/bin/runners/lib/agent-firewall/fs-hook/watcher.js +184 -0
  68. package/bin/runners/lib/agent-firewall/git-hook/pre-commit.js +163 -0
  69. package/bin/runners/lib/agent-firewall/ide-extension/cursor.js +107 -0
  70. package/bin/runners/lib/agent-firewall/ide-extension/vscode.js +68 -0
  71. package/bin/runners/lib/agent-firewall/ide-extension/windsurf.js +66 -0
  72. package/bin/runners/lib/agent-firewall/index.js +200 -0
  73. package/bin/runners/lib/agent-firewall/integration/index.js +20 -0
  74. package/bin/runners/lib/agent-firewall/integration/ship-gate.js +437 -0
  75. package/bin/runners/lib/agent-firewall/intent/alignment-engine.js +634 -0
  76. package/bin/runners/lib/agent-firewall/intent/auto-detect.js +426 -0
  77. package/bin/runners/lib/agent-firewall/intent/index.js +102 -0
  78. package/bin/runners/lib/agent-firewall/intent/schema.js +352 -0
  79. package/bin/runners/lib/agent-firewall/intent/store.js +283 -0
  80. package/bin/runners/lib/agent-firewall/interception/fs-interceptor.js +502 -0
  81. package/bin/runners/lib/agent-firewall/interception/index.js +23 -0
  82. package/bin/runners/lib/agent-firewall/interceptor/base.js +308 -0
  83. package/bin/runners/lib/agent-firewall/interceptor/cursor.js +35 -0
  84. package/bin/runners/lib/agent-firewall/interceptor/vscode.js +35 -0
  85. package/bin/runners/lib/agent-firewall/interceptor/windsurf.js +34 -0
  86. package/bin/runners/lib/agent-firewall/lawbook/distributor.js +465 -0
  87. package/bin/runners/lib/agent-firewall/lawbook/evaluator.js +604 -0
  88. package/bin/runners/lib/agent-firewall/lawbook/index.js +304 -0
  89. package/bin/runners/lib/agent-firewall/lawbook/registry.js +514 -0
  90. package/bin/runners/lib/agent-firewall/lawbook/schema.js +420 -0
  91. package/bin/runners/lib/agent-firewall/logger.js +141 -0
  92. package/bin/runners/lib/agent-firewall/policy/default-policy.json +90 -0
  93. package/bin/runners/lib/agent-firewall/policy/engine.js +103 -0
  94. package/bin/runners/lib/agent-firewall/policy/loader.js +451 -0
  95. package/bin/runners/lib/agent-firewall/policy/rules/auth-drift.js +50 -0
  96. package/bin/runners/lib/agent-firewall/policy/rules/contract-drift.js +50 -0
  97. package/bin/runners/lib/agent-firewall/policy/rules/fake-success.js +79 -0
  98. package/bin/runners/lib/agent-firewall/policy/rules/ghost-env.js +227 -0
  99. package/bin/runners/lib/agent-firewall/policy/rules/ghost-route.js +191 -0
  100. package/bin/runners/lib/agent-firewall/policy/rules/scope.js +93 -0
  101. package/bin/runners/lib/agent-firewall/policy/rules/unsafe-side-effect.js +57 -0
  102. package/bin/runners/lib/agent-firewall/policy/schema.json +183 -0
  103. package/bin/runners/lib/agent-firewall/policy/verdict.js +54 -0
  104. package/bin/runners/lib/agent-firewall/proposal/extractor.js +394 -0
  105. package/bin/runners/lib/agent-firewall/proposal/index.js +212 -0
  106. package/bin/runners/lib/agent-firewall/proposal/schema.js +251 -0
  107. package/bin/runners/lib/agent-firewall/proposal/validator.js +386 -0
  108. package/bin/runners/lib/agent-firewall/reality/index.js +332 -0
  109. package/bin/runners/lib/agent-firewall/reality/state.js +625 -0
  110. package/bin/runners/lib/agent-firewall/reality/watcher.js +322 -0
  111. package/bin/runners/lib/agent-firewall/risk/index.js +173 -0
  112. package/bin/runners/lib/agent-firewall/risk/scorer.js +328 -0
  113. package/bin/runners/lib/agent-firewall/risk/thresholds.js +322 -0
  114. package/bin/runners/lib/agent-firewall/risk/vectors.js +421 -0
  115. package/bin/runners/lib/agent-firewall/session/collector.js +451 -0
  116. package/bin/runners/lib/agent-firewall/session/index.js +26 -0
  117. package/bin/runners/lib/agent-firewall/simulator/diff-simulator.js +472 -0
  118. package/bin/runners/lib/agent-firewall/simulator/import-resolver.js +346 -0
  119. package/bin/runners/lib/agent-firewall/simulator/index.js +181 -0
  120. package/bin/runners/lib/agent-firewall/simulator/route-validator.js +380 -0
  121. package/bin/runners/lib/agent-firewall/time-machine/incident-correlator.js +661 -0
  122. package/bin/runners/lib/agent-firewall/time-machine/index.js +267 -0
  123. package/bin/runners/lib/agent-firewall/time-machine/replay-engine.js +436 -0
  124. package/bin/runners/lib/agent-firewall/time-machine/state-reconstructor.js +490 -0
  125. package/bin/runners/lib/agent-firewall/time-machine/timeline-builder.js +530 -0
  126. package/bin/runners/lib/agent-firewall/truthpack/index.js +67 -0
  127. package/bin/runners/lib/agent-firewall/truthpack/loader.js +137 -0
  128. package/bin/runners/lib/agent-firewall/unblock/planner.js +337 -0
  129. package/bin/runners/lib/agent-firewall/utils/ignore-checker.js +118 -0
  130. package/bin/runners/lib/ai-bridge.js +416 -0
  131. package/bin/runners/lib/analysis-core.js +309 -0
  132. package/bin/runners/lib/analyzers.js +2500 -0
  133. package/bin/runners/lib/api-client.js +269 -0
  134. package/bin/runners/lib/approve-output.js +235 -0
  135. package/bin/runners/lib/artifact-envelope.js +540 -0
  136. package/bin/runners/lib/assets/vibecheck-logo.png +0 -0
  137. package/bin/runners/lib/audit-bridge.js +391 -0
  138. package/bin/runners/lib/auth-shared.js +977 -0
  139. package/bin/runners/lib/auth-truth.js +193 -0
  140. package/bin/runners/lib/auth.js +215 -0
  141. package/bin/runners/lib/authority-badge.js +425 -0
  142. package/bin/runners/lib/backup.js +62 -0
  143. package/bin/runners/lib/billing.js +107 -0
  144. package/bin/runners/lib/checkpoint.js +941 -0
  145. package/bin/runners/lib/claims.js +118 -0
  146. package/bin/runners/lib/classify-output.js +204 -0
  147. package/bin/runners/lib/cleanup/engine.js +571 -0
  148. package/bin/runners/lib/cleanup/index.js +53 -0
  149. package/bin/runners/lib/cleanup/output.js +375 -0
  150. package/bin/runners/lib/cleanup/rules.js +1060 -0
  151. package/bin/runners/lib/cli-output.js +400 -0
  152. package/bin/runners/lib/cli-ui.js +540 -0
  153. package/bin/runners/lib/compliance-bridge-new.js +0 -0
  154. package/bin/runners/lib/compliance-bridge.js +165 -0
  155. package/bin/runners/lib/contracts/auth-contract.js +202 -0
  156. package/bin/runners/lib/contracts/env-contract.js +181 -0
  157. package/bin/runners/lib/contracts/external-contract.js +206 -0
  158. package/bin/runners/lib/contracts/guard.js +168 -0
  159. package/bin/runners/lib/contracts/index.js +89 -0
  160. package/bin/runners/lib/contracts/plan-validator.js +311 -0
  161. package/bin/runners/lib/contracts/route-contract.js +199 -0
  162. package/bin/runners/lib/contracts.js +804 -0
  163. package/bin/runners/lib/default-config.js +127 -0
  164. package/bin/runners/lib/detect.js +89 -0
  165. package/bin/runners/lib/detectors-v2.js +622 -0
  166. package/bin/runners/lib/doctor/autofix.js +254 -0
  167. package/bin/runners/lib/doctor/diagnosis-receipt.js +454 -0
  168. package/bin/runners/lib/doctor/failure-signatures.js +526 -0
  169. package/bin/runners/lib/doctor/fix-script.js +336 -0
  170. package/bin/runners/lib/doctor/index.js +37 -0
  171. package/bin/runners/lib/doctor/modules/build-tools.js +453 -0
  172. package/bin/runners/lib/doctor/modules/dependencies.js +325 -0
  173. package/bin/runners/lib/doctor/modules/index.js +105 -0
  174. package/bin/runners/lib/doctor/modules/network.js +250 -0
  175. package/bin/runners/lib/doctor/modules/os-quirks.js +706 -0
  176. package/bin/runners/lib/doctor/modules/project.js +312 -0
  177. package/bin/runners/lib/doctor/modules/repo-integrity.js +485 -0
  178. package/bin/runners/lib/doctor/modules/runtime.js +224 -0
  179. package/bin/runners/lib/doctor/modules/security.js +350 -0
  180. package/bin/runners/lib/doctor/modules/system.js +213 -0
  181. package/bin/runners/lib/doctor/modules/vibecheck.js +394 -0
  182. package/bin/runners/lib/doctor/reporter.js +262 -0
  183. package/bin/runners/lib/doctor/safe-repair.js +384 -0
  184. package/bin/runners/lib/doctor/service.js +262 -0
  185. package/bin/runners/lib/doctor/types.js +113 -0
  186. package/bin/runners/lib/doctor/ui.js +263 -0
  187. package/bin/runners/lib/doctor-enhanced.js +233 -0
  188. package/bin/runners/lib/doctor-output.js +226 -0
  189. package/bin/runners/lib/doctor-v2.js +608 -0
  190. package/bin/runners/lib/drift.js +425 -0
  191. package/bin/runners/lib/enforcement.js +72 -0
  192. package/bin/runners/lib/engine/ast-cache.js +210 -0
  193. package/bin/runners/lib/engine/auth-extractor.js +211 -0
  194. package/bin/runners/lib/engine/billing-extractor.js +112 -0
  195. package/bin/runners/lib/engine/enforcement-extractor.js +100 -0
  196. package/bin/runners/lib/engine/env-extractor.js +207 -0
  197. package/bin/runners/lib/engine/express-extractor.js +208 -0
  198. package/bin/runners/lib/engine/extractors.js +849 -0
  199. package/bin/runners/lib/engine/index.js +207 -0
  200. package/bin/runners/lib/engine/repo-index.js +514 -0
  201. package/bin/runners/lib/engine/types.js +124 -0
  202. package/bin/runners/lib/engines/accessibility-engine.js +190 -0
  203. package/bin/runners/lib/engines/api-consistency-engine.js +162 -0
  204. package/bin/runners/lib/engines/ast-cache.js +99 -0
  205. package/bin/runners/lib/engines/attack-detector.js +1192 -0
  206. package/bin/runners/lib/engines/code-quality-engine.js +255 -0
  207. package/bin/runners/lib/engines/console-logs-engine.js +115 -0
  208. package/bin/runners/lib/engines/cross-file-analysis-engine.js +268 -0
  209. package/bin/runners/lib/engines/dead-code-engine.js +198 -0
  210. package/bin/runners/lib/engines/deprecated-api-engine.js +226 -0
  211. package/bin/runners/lib/engines/empty-catch-engine.js +150 -0
  212. package/bin/runners/lib/engines/file-filter.js +131 -0
  213. package/bin/runners/lib/engines/hardcoded-secrets-engine.js +251 -0
  214. package/bin/runners/lib/engines/mock-data-engine.js +272 -0
  215. package/bin/runners/lib/engines/parallel-processor.js +71 -0
  216. package/bin/runners/lib/engines/performance-issues-engine.js +265 -0
  217. package/bin/runners/lib/engines/security-vulnerabilities-engine.js +243 -0
  218. package/bin/runners/lib/engines/todo-fixme-engine.js +115 -0
  219. package/bin/runners/lib/engines/type-aware-engine.js +152 -0
  220. package/bin/runners/lib/engines/unsafe-regex-engine.js +225 -0
  221. package/bin/runners/lib/engines/vibecheck-engines/README.md +53 -0
  222. package/bin/runners/lib/engines/vibecheck-engines/index.js +15 -0
  223. package/bin/runners/lib/engines/vibecheck-engines/lib/ast-cache.js +164 -0
  224. package/bin/runners/lib/engines/vibecheck-engines/lib/code-quality-engine.js +291 -0
  225. package/bin/runners/lib/engines/vibecheck-engines/lib/console-logs-engine.js +83 -0
  226. package/bin/runners/lib/engines/vibecheck-engines/lib/dead-code-engine.js +198 -0
  227. package/bin/runners/lib/engines/vibecheck-engines/lib/deprecated-api-engine.js +275 -0
  228. package/bin/runners/lib/engines/vibecheck-engines/lib/empty-catch-engine.js +167 -0
  229. package/bin/runners/lib/engines/vibecheck-engines/lib/file-filter.js +217 -0
  230. package/bin/runners/lib/engines/vibecheck-engines/lib/hardcoded-secrets-engine.js +139 -0
  231. package/bin/runners/lib/engines/vibecheck-engines/lib/mock-data-engine.js +140 -0
  232. package/bin/runners/lib/engines/vibecheck-engines/lib/parallel-processor.js +164 -0
  233. package/bin/runners/lib/engines/vibecheck-engines/lib/performance-issues-engine.js +234 -0
  234. package/bin/runners/lib/engines/vibecheck-engines/lib/type-aware-engine.js +217 -0
  235. package/bin/runners/lib/engines/vibecheck-engines/lib/unsafe-regex-engine.js +78 -0
  236. package/bin/runners/lib/engines/vibecheck-engines/package.json +13 -0
  237. package/bin/runners/lib/enterprise-detect.js +603 -0
  238. package/bin/runners/lib/enterprise-init.js +942 -0
  239. package/bin/runners/lib/entitlements-v2.js +265 -0
  240. package/bin/runners/lib/entitlements.generated.js +0 -0
  241. package/bin/runners/lib/entitlements.js +340 -0
  242. package/bin/runners/lib/env-resolver.js +417 -0
  243. package/bin/runners/lib/env-template.js +66 -0
  244. package/bin/runners/lib/env.js +189 -0
  245. package/bin/runners/lib/error-handler.js +368 -0
  246. package/bin/runners/lib/error-messages.js +289 -0
  247. package/bin/runners/lib/evidence-pack.js +684 -0
  248. package/bin/runners/lib/exit-codes.js +275 -0
  249. package/bin/runners/lib/extractors/client-calls.js +990 -0
  250. package/bin/runners/lib/extractors/fastify-route-dump.js +573 -0
  251. package/bin/runners/lib/extractors/fastify-routes.js +426 -0
  252. package/bin/runners/lib/extractors/index.js +363 -0
  253. package/bin/runners/lib/extractors/next-routes.js +524 -0
  254. package/bin/runners/lib/extractors/proof-graph.js +431 -0
  255. package/bin/runners/lib/extractors/route-matcher.js +451 -0
  256. package/bin/runners/lib/extractors/truthpack-v2.js +377 -0
  257. package/bin/runners/lib/extractors/ui-bindings.js +547 -0
  258. package/bin/runners/lib/finding-id.js +69 -0
  259. package/bin/runners/lib/finding-sorter.js +89 -0
  260. package/bin/runners/lib/findings-schema.js +281 -0
  261. package/bin/runners/lib/fingerprint.js +377 -0
  262. package/bin/runners/lib/firewall-prompt.js +50 -0
  263. package/bin/runners/lib/fix-output.js +228 -0
  264. package/bin/runners/lib/global-flags.js +250 -0
  265. package/bin/runners/lib/graph/graph-builder.js +265 -0
  266. package/bin/runners/lib/graph/html-renderer.js +413 -0
  267. package/bin/runners/lib/graph/index.js +32 -0
  268. package/bin/runners/lib/graph/runtime-collector.js +215 -0
  269. package/bin/runners/lib/graph/static-extractor.js +518 -0
  270. package/bin/runners/lib/help-formatter.js +413 -0
  271. package/bin/runners/lib/html-proof-report.js +913 -0
  272. package/bin/runners/lib/html-report.js +650 -0
  273. package/bin/runners/lib/init-wizard.js +601 -0
  274. package/bin/runners/lib/interactive-menu.js +1496 -0
  275. package/bin/runners/lib/json-output.js +76 -0
  276. package/bin/runners/lib/llm.js +75 -0
  277. package/bin/runners/lib/logger.js +38 -0
  278. package/bin/runners/lib/meter.js +61 -0
  279. package/bin/runners/lib/missions/briefing.js +427 -0
  280. package/bin/runners/lib/missions/checkpoint.js +753 -0
  281. package/bin/runners/lib/missions/evidence.js +126 -0
  282. package/bin/runners/lib/missions/hardening.js +851 -0
  283. package/bin/runners/lib/missions/plan.js +648 -0
  284. package/bin/runners/lib/missions/safety-gates.js +645 -0
  285. package/bin/runners/lib/missions/schema.js +478 -0
  286. package/bin/runners/lib/missions/templates.js +317 -0
  287. package/bin/runners/lib/next-action.js +560 -0
  288. package/bin/runners/lib/packs/bundle.js +675 -0
  289. package/bin/runners/lib/packs/evidence-pack.js +671 -0
  290. package/bin/runners/lib/packs/pack-factory.js +837 -0
  291. package/bin/runners/lib/packs/permissions-pack.js +686 -0
  292. package/bin/runners/lib/packs/proof-graph-pack.js +779 -0
  293. package/bin/runners/lib/patch.js +40 -0
  294. package/bin/runners/lib/permissions/auth-model.js +213 -0
  295. package/bin/runners/lib/permissions/idor-prover.js +205 -0
  296. package/bin/runners/lib/permissions/index.js +45 -0
  297. package/bin/runners/lib/permissions/matrix-builder.js +198 -0
  298. package/bin/runners/lib/pkgjson.js +28 -0
  299. package/bin/runners/lib/policy.js +295 -0
  300. package/bin/runners/lib/polish/accessibility.js +62 -0
  301. package/bin/runners/lib/polish/analyzer.js +93 -0
  302. package/bin/runners/lib/polish/backend.js +87 -0
  303. package/bin/runners/lib/polish/configuration.js +83 -0
  304. package/bin/runners/lib/polish/documentation.js +83 -0
  305. package/bin/runners/lib/polish/frontend.js +817 -0
  306. package/bin/runners/lib/polish/index.js +27 -0
  307. package/bin/runners/lib/polish/infrastructure.js +80 -0
  308. package/bin/runners/lib/polish/internationalization.js +85 -0
  309. package/bin/runners/lib/polish/libraries.js +180 -0
  310. package/bin/runners/lib/polish/observability.js +75 -0
  311. package/bin/runners/lib/polish/performance.js +64 -0
  312. package/bin/runners/lib/polish/privacy.js +110 -0
  313. package/bin/runners/lib/polish/resilience.js +92 -0
  314. package/bin/runners/lib/polish/security.js +78 -0
  315. package/bin/runners/lib/polish/seo.js +71 -0
  316. package/bin/runners/lib/polish/styles.js +62 -0
  317. package/bin/runners/lib/polish/utils.js +104 -0
  318. package/bin/runners/lib/preflight.js +142 -0
  319. package/bin/runners/lib/prerequisites.js +149 -0
  320. package/bin/runners/lib/prove-output.js +220 -0
  321. package/bin/runners/lib/reality/correlation-detectors.js +359 -0
  322. package/bin/runners/lib/reality/index.js +318 -0
  323. package/bin/runners/lib/reality/request-hashing.js +416 -0
  324. package/bin/runners/lib/reality/request-mapper.js +453 -0
  325. package/bin/runners/lib/reality/safety-rails.js +463 -0
  326. package/bin/runners/lib/reality/semantic-snapshot.js +408 -0
  327. package/bin/runners/lib/reality/toast-detector.js +393 -0
  328. package/bin/runners/lib/reality-findings.js +84 -0
  329. package/bin/runners/lib/reality-output.js +231 -0
  330. package/bin/runners/lib/receipts.js +179 -0
  331. package/bin/runners/lib/redact.js +29 -0
  332. package/bin/runners/lib/replay/capsule-manager.js +154 -0
  333. package/bin/runners/lib/replay/index.js +263 -0
  334. package/bin/runners/lib/replay/player.js +348 -0
  335. package/bin/runners/lib/replay/recorder.js +331 -0
  336. package/bin/runners/lib/report-engine.js +626 -0
  337. package/bin/runners/lib/report-html.js +1233 -0
  338. package/bin/runners/lib/report-output.js +366 -0
  339. package/bin/runners/lib/report-templates.js +967 -0
  340. package/bin/runners/lib/report.js +135 -0
  341. package/bin/runners/lib/route-detection.js +1209 -0
  342. package/bin/runners/lib/route-truth.js +1322 -0
  343. package/bin/runners/lib/safelist/index.js +96 -0
  344. package/bin/runners/lib/safelist/integration.js +334 -0
  345. package/bin/runners/lib/safelist/matcher.js +696 -0
  346. package/bin/runners/lib/safelist/schema.js +948 -0
  347. package/bin/runners/lib/safelist/store.js +438 -0
  348. package/bin/runners/lib/sandbox/index.js +59 -0
  349. package/bin/runners/lib/sandbox/proof-chain.js +399 -0
  350. package/bin/runners/lib/sandbox/sandbox-runner.js +205 -0
  351. package/bin/runners/lib/sandbox/worktree.js +174 -0
  352. package/bin/runners/lib/scan-cache.js +330 -0
  353. package/bin/runners/lib/scan-output-schema.js +344 -0
  354. package/bin/runners/lib/scan-output.js +631 -0
  355. package/bin/runners/lib/scan-runner.js +135 -0
  356. package/bin/runners/lib/schema-validator.js +350 -0
  357. package/bin/runners/lib/schemas/ajv-validator.js +464 -0
  358. package/bin/runners/lib/schemas/contracts.schema.json +160 -0
  359. package/bin/runners/lib/schemas/error-envelope.schema.json +105 -0
  360. package/bin/runners/lib/schemas/finding-v3.schema.json +151 -0
  361. package/bin/runners/lib/schemas/finding.schema.json +100 -0
  362. package/bin/runners/lib/schemas/mission-pack.schema.json +206 -0
  363. package/bin/runners/lib/schemas/proof-graph.schema.json +176 -0
  364. package/bin/runners/lib/schemas/reality-report.schema.json +162 -0
  365. package/bin/runners/lib/schemas/report-artifact.schema.json +120 -0
  366. package/bin/runners/lib/schemas/run-request.schema.json +108 -0
  367. package/bin/runners/lib/schemas/share-pack.schema.json +180 -0
  368. package/bin/runners/lib/schemas/ship-manifest.schema.json +251 -0
  369. package/bin/runners/lib/schemas/ship-report.schema.json +117 -0
  370. package/bin/runners/lib/schemas/truthpack-v2.schema.json +303 -0
  371. package/bin/runners/lib/schemas/validator.js +465 -0
  372. package/bin/runners/lib/schemas/verdict.schema.json +140 -0
  373. package/bin/runners/lib/score-history.js +282 -0
  374. package/bin/runners/lib/security-bridge.js +249 -0
  375. package/bin/runners/lib/server-usage.js +513 -0
  376. package/bin/runners/lib/share-pack.js +239 -0
  377. package/bin/runners/lib/ship-gate.js +832 -0
  378. package/bin/runners/lib/ship-manifest.js +1153 -0
  379. package/bin/runners/lib/ship-output-enterprise.js +239 -0
  380. package/bin/runners/lib/ship-output.js +1128 -0
  381. package/bin/runners/lib/snippets.js +67 -0
  382. package/bin/runners/lib/status-output.js +340 -0
  383. package/bin/runners/lib/terminal-ui.js +356 -0
  384. package/bin/runners/lib/truth.js +1691 -0
  385. package/bin/runners/lib/ui.js +562 -0
  386. package/bin/runners/lib/unified-cli-output.js +947 -0
  387. package/bin/runners/lib/unified-output.js +197 -0
  388. package/bin/runners/lib/upsell.js +410 -0
  389. package/bin/runners/lib/usage.js +153 -0
  390. package/bin/runners/lib/validate-patch.js +156 -0
  391. package/bin/runners/lib/verdict-engine.js +628 -0
  392. package/bin/runners/lib/verification.js +345 -0
  393. package/bin/runners/lib/why-tree.js +650 -0
  394. package/bin/runners/reality/engine.js +917 -0
  395. package/bin/runners/reality/flows.js +122 -0
  396. package/bin/runners/reality/report.js +378 -0
  397. package/bin/runners/reality/session.js +193 -0
  398. package/bin/runners/runAIAgent.js +229 -0
  399. package/bin/runners/runAgent.d.ts +5 -0
  400. package/bin/runners/runAgent.js +161 -0
  401. package/bin/runners/runAllowlist.js +418 -0
  402. package/bin/runners/runApprove.js +320 -0
  403. package/bin/runners/runAudit.js +692 -0
  404. package/bin/runners/runAuth.js +731 -0
  405. package/bin/runners/runCI.js +353 -0
  406. package/bin/runners/runCheckpoint.js +530 -0
  407. package/bin/runners/runClassify.js +928 -0
  408. package/bin/runners/runCleanup.js +343 -0
  409. package/bin/runners/runContext.d.ts +4 -0
  410. package/bin/runners/runContext.js +175 -0
  411. package/bin/runners/runDoctor.js +877 -0
  412. package/bin/runners/runEvidencePack.js +362 -0
  413. package/bin/runners/runFirewall.d.ts +5 -0
  414. package/bin/runners/runFirewall.js +134 -0
  415. package/bin/runners/runFirewallHook.d.ts +5 -0
  416. package/bin/runners/runFirewallHook.js +56 -0
  417. package/bin/runners/runFix.js +1355 -0
  418. package/bin/runners/runForge.js +380 -0
  419. package/bin/runners/runGuard.js +262 -0
  420. package/bin/runners/runInit.js +1927 -0
  421. package/bin/runners/runIntent.js +906 -0
  422. package/bin/runners/runKickoff.js +878 -0
  423. package/bin/runners/runLabs.js +424 -0
  424. package/bin/runners/runLaunch.js +2000 -0
  425. package/bin/runners/runLink.js +785 -0
  426. package/bin/runners/runMcp.js +1875 -0
  427. package/bin/runners/runPacks.js +2089 -0
  428. package/bin/runners/runPolish.d.ts +4 -0
  429. package/bin/runners/runPolish.js +390 -0
  430. package/bin/runners/runPromptFirewall.js +211 -0
  431. package/bin/runners/runProve.js +1411 -0
  432. package/bin/runners/runQuickstart.js +531 -0
  433. package/bin/runners/runReality.js +2260 -0
  434. package/bin/runners/runReport.js +726 -0
  435. package/bin/runners/runRuntime.js +110 -0
  436. package/bin/runners/runSafelist.js +1190 -0
  437. package/bin/runners/runScan.js +688 -0
  438. package/bin/runners/runShield.js +1282 -0
  439. package/bin/runners/runShip.js +1660 -0
  440. package/bin/runners/runTruth.d.ts +5 -0
  441. package/bin/runners/runTruth.js +101 -0
  442. package/bin/runners/runValidate.js +179 -0
  443. package/bin/runners/runWatch.js +478 -0
  444. package/bin/runners/utils.js +360 -0
  445. package/bin/scan.js +617 -0
  446. package/bin/vibecheck.js +1617 -0
  447. package/dist/guardrail/index.d.ts +2405 -0
  448. package/dist/guardrail/index.js +9747 -0
  449. package/dist/guardrail/index.js.map +1 -0
  450. package/dist/scanner/index.d.ts +282 -0
  451. package/dist/scanner/index.js +3395 -0
  452. package/dist/scanner/index.js.map +1 -0
  453. package/package.json +123 -104
  454. package/README.md +0 -491
  455. package/dist/index.js +0 -99711
  456. package/dist/index.js.map +0 -1
@@ -0,0 +1,1617 @@
1
+ #!/usr/bin/env node
2
+ // bin/vibecheck.js - World-Class CLI (Refactored)
3
+ // ═══════════════════════════════════════════════════════════════════════════════
4
+ // VibeCheck - Proves your app is real
5
+ // Ship with confidence. Catch fake features before your users do.
6
+ // ═══════════════════════════════════════════════════════════════════════════════
7
+
8
+ "use strict";
9
+
10
+ const { performance } = require("perf_hooks");
11
+ const STARTUP_TIME = performance.now();
12
+
13
+ // ═══════════════════════════════════════════════════════════════════════════════
14
+ // LAZY LOADING - Defer all requires until needed for fast startup
15
+ // ═══════════════════════════════════════════════════════════════════════════════
16
+ const lazyModules = {};
17
+
18
+ function lazy(name, loader) {
19
+ return () => {
20
+ if (!lazyModules[name]) {
21
+ lazyModules[name] = loader();
22
+ }
23
+ return lazyModules[name];
24
+ };
25
+ }
26
+
27
+ const getFs = lazy("fs", () => require("fs"));
28
+ const getPath = lazy("path", () => require("path"));
29
+ const getOs = lazy("os", () => require("os"));
30
+ const getCrypto = lazy("crypto", () => require("crypto"));
31
+ const getHttps = lazy("https", () => require("https"));
32
+ const getReadline = lazy("readline", () => require("readline"));
33
+
34
+ // ═══════════════════════════════════════════════════════════════════════════════
35
+ // VERSION & METADATA (lazy loaded)
36
+ // ═══════════════════════════════════════════════════════════════════════════════
37
+ let _version = null;
38
+ function getVersion() {
39
+ if (_version) return _version;
40
+ try {
41
+ const fs = getFs();
42
+ const path = getPath();
43
+ const pkgPath = path.join(__dirname, "..", "package.json");
44
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
45
+ _version = pkg.version || "0.0.0";
46
+ } catch {
47
+ _version = "0.0.0";
48
+ }
49
+ return _version;
50
+ }
51
+
52
+ const CLI_NAME = "vibecheck";
53
+ const CONFIG_FILE = ".vibecheckrc";
54
+ const UPDATE_CHECK_INTERVAL = 24 * 60 * 60 * 1000; // 24 hours
55
+
56
+ // Cache/state paths (computed lazily)
57
+ let _cacheDir = null;
58
+ let _stateFile = null;
59
+ function getCacheDir() {
60
+ if (!_cacheDir) {
61
+ const os = getOs();
62
+ const path = getPath();
63
+ _cacheDir = path.join(os.homedir(), ".vibecheck");
64
+ }
65
+ return _cacheDir;
66
+ }
67
+ function getStateFile() {
68
+ if (!_stateFile) {
69
+ const path = getPath();
70
+ _stateFile = path.join(getCacheDir(), "state.json");
71
+ }
72
+ return _stateFile;
73
+ }
74
+
75
+ // ═══════════════════════════════════════════════════════════════════════════════
76
+ // ANSI STYLES - Premium terminal styling with gradient support
77
+ // ═══════════════════════════════════════════════════════════════════════════════
78
+ const SUPPORTS_COLOR = process.stdout.isTTY && !process.env.NO_COLOR;
79
+ const SUPPORTS_TRUECOLOR = SUPPORTS_COLOR && (
80
+ process.env.COLORTERM === "truecolor" ||
81
+ process.env.TERM_PROGRAM === "iTerm.app" ||
82
+ process.env.TERM_PROGRAM === "Apple_Terminal" ||
83
+ process.env.WT_SESSION
84
+ );
85
+
86
+ const c = SUPPORTS_COLOR ? {
87
+ reset: "\x1b[0m",
88
+ bold: "\x1b[1m",
89
+ dim: "\x1b[2m",
90
+ italic: "\x1b[3m",
91
+ underline: "\x1b[4m",
92
+ blink: "\x1b[5m",
93
+ inverse: "\x1b[7m",
94
+ hidden: "\x1b[8m",
95
+ strikethrough: "\x1b[9m",
96
+ black: "\x1b[30m",
97
+ red: "\x1b[31m",
98
+ green: "\x1b[32m",
99
+ yellow: "\x1b[33m",
100
+ blue: "\x1b[34m",
101
+ magenta: "\x1b[35m",
102
+ cyan: "\x1b[36m",
103
+ white: "\x1b[37m",
104
+ gray: "\x1b[90m",
105
+ brightRed: "\x1b[91m",
106
+ brightGreen: "\x1b[92m",
107
+ brightYellow: "\x1b[93m",
108
+ brightBlue: "\x1b[94m",
109
+ brightMagenta: "\x1b[95m",
110
+ brightCyan: "\x1b[96m",
111
+ brightWhite: "\x1b[97m",
112
+ bgRed: "\x1b[41m",
113
+ bgGreen: "\x1b[42m",
114
+ bgYellow: "\x1b[43m",
115
+ bgBlue: "\x1b[44m",
116
+ bgMagenta: "\x1b[45m",
117
+ bgCyan: "\x1b[46m",
118
+ bgWhite: "\x1b[47m",
119
+ bgGray: "\x1b[100m",
120
+ rgb: (r, g, b) => SUPPORTS_TRUECOLOR ? `\x1b[38;2;${r};${g};${b}m` : "",
121
+ bgRgb: (r, g, b) => SUPPORTS_TRUECOLOR ? `\x1b[48;2;${r};${g};${b}m` : "",
122
+ } : Object.fromEntries([
123
+ "reset", "bold", "dim", "italic", "underline", "blink", "inverse", "hidden", "strikethrough",
124
+ "black", "red", "green", "yellow", "blue", "magenta", "cyan", "white", "gray",
125
+ "brightRed", "brightGreen", "brightYellow", "brightBlue", "brightMagenta", "brightCyan", "brightWhite",
126
+ "bgRed", "bgGreen", "bgYellow", "bgBlue", "bgMagenta", "bgCyan", "bgWhite", "bgGray"
127
+ ].map(k => [k, ""]));
128
+
129
+ // Add no-op rgb functions for non-color mode
130
+ if (!SUPPORTS_COLOR) {
131
+ c.rgb = () => "";
132
+ c.bgRgb = () => "";
133
+ }
134
+
135
+ function gradient(text, colors = [[0, 255, 255], [255, 0, 255], [255, 255, 0]]) {
136
+ if (!SUPPORTS_TRUECOLOR) return `${c.cyan}${text}${c.reset}`;
137
+ const chars = [...text];
138
+ const len = chars.length;
139
+ if (len === 0) return text;
140
+ return chars.map((char, i) => {
141
+ const t = i / Math.max(len - 1, 1);
142
+ const segmentLen = colors.length - 1;
143
+ const segment = Math.min(Math.floor(t * segmentLen), segmentLen - 1);
144
+ const localT = (t * segmentLen) - segment;
145
+ const c1 = colors[segment];
146
+ const c2 = colors[segment + 1] || c1;
147
+ const r = Math.round(c1[0] + (c2[0] - c1[0]) * localT);
148
+ const g = Math.round(c1[1] + (c2[1] - c1[1]) * localT);
149
+ const b = Math.round(c1[2] + (c2[2] - c1[2]) * localT);
150
+ return `${c.rgb(r, g, b)}${char}`;
151
+ }).join("") + c.reset;
152
+ }
153
+
154
+ // ═══════════════════════════════════════════════════════════════════════════════
155
+ // UNICODE SYMBOLS
156
+ // ═══════════════════════════════════════════════════════════════════════════════
157
+ const SUPPORTS_UNICODE = process.platform !== "win32" || process.env.WT_SESSION || process.env.TERM_PROGRAM;
158
+
159
+ const sym = SUPPORTS_UNICODE ? {
160
+ success: "✓", error: "✗", warning: "⚠", info: "ℹ", pending: "○", active: "●",
161
+ spinner: ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"],
162
+ progress: ["░", "▒", "▓", "█"],
163
+ arrow: "→", arrowRight: "▸", arrowDown: "▾", bullet: "•",
164
+ star: "★", starEmpty: "☆", heart: "♥", shield: "🛡️", rocket: "🚀",
165
+ fire: "🔥", sparkles: "✨", check: "☑", box: "☐", lock: "🔒",
166
+ key: "🔑", lightning: "⚡", clock: "⏱", folder: "📁", file: "📄",
167
+ gear: "⚙", chart: "📊",
168
+ boxTopLeft: "╭", boxTopRight: "╮", boxBottomLeft: "╰", boxBottomRight: "╯",
169
+ boxHorizontal: "─", boxVertical: "│", boxCross: "┼",
170
+ boxHorizontalDown: "┬", boxHorizontalUp: "┴",
171
+ boxVerticalRight: "├", boxVerticalLeft: "┤",
172
+ dblBoxTopLeft: "╔", dblBoxTopRight: "╗", dblBoxBottomLeft: "╚", dblBoxBottomRight: "╝",
173
+ dblBoxHorizontal: "═", dblBoxVertical: "║",
174
+ } : {
175
+ success: "√", error: "×", warning: "!", info: "i", pending: "o", active: "*",
176
+ spinner: ["-", "\\", "|", "/"], progress: [".", ":", "#", "#"],
177
+ arrow: "->", arrowRight: ">", arrowDown: "v", bullet: "*",
178
+ star: "*", starEmpty: "o", heart: "<3", shield: "[#]", rocket: ">>",
179
+ fire: "(!)", sparkles: "*", check: "[x]", box: "[ ]", lock: "[L]",
180
+ key: "[K]", lightning: "!", clock: "[T]", folder: "[D]", file: "[F]",
181
+ gear: "[G]", chart: "[C]",
182
+ boxTopLeft: "+", boxTopRight: "+", boxBottomLeft: "+", boxBottomRight: "+",
183
+ boxHorizontal: "-", boxVertical: "|", boxCross: "+",
184
+ boxHorizontalDown: "+", boxHorizontalUp: "+",
185
+ boxVerticalRight: "+", boxVerticalLeft: "+",
186
+ dblBoxTopLeft: "+", dblBoxTopRight: "+", dblBoxBottomLeft: "+", dblBoxBottomRight: "+",
187
+ dblBoxHorizontal: "=", dblBoxVertical: "||",
188
+ };
189
+
190
+ // ═══════════════════════════════════════════════════════════════════════════════
191
+ // CI DETECTION
192
+ // ═══════════════════════════════════════════════════════════════════════════════
193
+ function isCI() {
194
+ return !!(
195
+ process.env.CI ||
196
+ process.env.CONTINUOUS_INTEGRATION ||
197
+ process.env.RAILWAY_ENVIRONMENT ||
198
+ process.env.VERCEL ||
199
+ process.env.NETLIFY ||
200
+ process.env.GITHUB_ACTIONS ||
201
+ process.env.GITLAB_CI ||
202
+ process.env.CIRCLECI ||
203
+ process.env.TRAVIS ||
204
+ process.env.BUILDKITE ||
205
+ process.env.RENDER ||
206
+ process.env.HEROKU ||
207
+ process.env.CODEBUILD_BUILD_ID ||
208
+ process.env.JENKINS_URL ||
209
+ process.env.TEAMCITY_VERSION ||
210
+ process.env.TF_BUILD ||
211
+ !process.stdin.isTTY
212
+ );
213
+ }
214
+
215
+ // ═══════════════════════════════════════════════════════════════════════════════
216
+ // SPINNER CLASS
217
+ // ═══════════════════════════════════════════════════════════════════════════════
218
+ class Spinner {
219
+ constructor(text = "") {
220
+ this.text = text;
221
+ this.frame = 0;
222
+ this.interval = null;
223
+ this.stream = process.stderr;
224
+ this.isCI = isCI();
225
+ }
226
+
227
+ start(text) {
228
+ if (text) this.text = text;
229
+ if (this.isCI) {
230
+ this.stream.write(`${c.dim}${sym.pending}${c.reset} ${this.text}\n`);
231
+ return this;
232
+ }
233
+ this.interval = setInterval(() => {
234
+ const spinner = sym.spinner[this.frame % sym.spinner.length];
235
+ this.stream.write(`\r${c.cyan}${spinner}${c.reset} ${this.text}`);
236
+ this.frame++;
237
+ }, 80);
238
+ return this;
239
+ }
240
+
241
+ update(text) {
242
+ this.text = text;
243
+ if (this.isCI) this.stream.write(` ${c.dim}${sym.arrowRight}${c.reset} ${text}\n`);
244
+ return this;
245
+ }
246
+
247
+ succeed(text) {
248
+ this.stop();
249
+ this.stream.write(`\r${c.green}${sym.success}${c.reset} ${text || this.text}\n`);
250
+ return this;
251
+ }
252
+
253
+ fail(text) {
254
+ this.stop();
255
+ this.stream.write(`\r${c.red}${sym.error}${c.reset} ${text || this.text}\n`);
256
+ return this;
257
+ }
258
+
259
+ warn(text) {
260
+ this.stop();
261
+ this.stream.write(`\r${c.yellow}${sym.warning}${c.reset} ${text || this.text}\n`);
262
+ return this;
263
+ }
264
+
265
+ info(text) {
266
+ this.stop();
267
+ this.stream.write(`\r${c.blue}${sym.info}${c.reset} ${text || this.text}\n`);
268
+ return this;
269
+ }
270
+
271
+ stop() {
272
+ if (this.interval) {
273
+ clearInterval(this.interval);
274
+ this.interval = null;
275
+ this.stream.write("\r\x1b[K");
276
+ }
277
+ return this;
278
+ }
279
+ }
280
+
281
+ // ═══════════════════════════════════════════════════════════════════════════════
282
+ // STATE MANAGEMENT (lazy file I/O)
283
+ // ═══════════════════════════════════════════════════════════════════════════════
284
+ function ensureCacheDir() {
285
+ const fs = getFs();
286
+ const cacheDir = getCacheDir();
287
+ if (!fs.existsSync(cacheDir)) {
288
+ fs.mkdirSync(cacheDir, { recursive: true });
289
+ }
290
+ }
291
+
292
+ function loadState() {
293
+ try {
294
+ const fs = getFs();
295
+ const stateFile = getStateFile();
296
+ if (fs.existsSync(stateFile)) {
297
+ return JSON.parse(fs.readFileSync(stateFile, "utf-8"));
298
+ }
299
+ } catch {}
300
+ return {
301
+ firstRun: Date.now(),
302
+ lastRun: null,
303
+ runCount: 0,
304
+ lastUpdateCheck: null,
305
+ latestVersion: null,
306
+ commandHistory: [],
307
+ favorites: [],
308
+ };
309
+ }
310
+
311
+ function saveState(state) {
312
+ try {
313
+ const fs = getFs();
314
+ ensureCacheDir();
315
+ fs.writeFileSync(getStateFile(), JSON.stringify(state, null, 2));
316
+ } catch {}
317
+ }
318
+
319
+ function loadConfig() {
320
+ const fs = getFs();
321
+ const path = getPath();
322
+
323
+ const config = {
324
+ debug: false,
325
+ verbose: false,
326
+ quiet: false,
327
+ color: true,
328
+ analytics: true,
329
+ updateCheck: true,
330
+ timeout: 30000,
331
+ maxRetries: 3,
332
+ noBanner: false,
333
+ };
334
+
335
+ const projectConfigPath = path.join(process.cwd(), CONFIG_FILE);
336
+ if (fs.existsSync(projectConfigPath)) {
337
+ try {
338
+ Object.assign(config, JSON.parse(fs.readFileSync(projectConfigPath, "utf-8")));
339
+ } catch {}
340
+ }
341
+
342
+ // Environment overrides
343
+ if (process.env.VIBECHECK_DEBUG === "true") config.debug = true;
344
+ if (process.env.VIBECHECK_VERBOSE === "true") config.verbose = true;
345
+ if (process.env.VIBECHECK_QUIET === "true") config.quiet = true;
346
+ if (process.env.VIBECHECK_NO_BANNER === "true") config.noBanner = true;
347
+ if (process.env.NO_COLOR || process.env.VIBECHECK_NO_COLOR) config.color = false;
348
+
349
+ return config;
350
+ }
351
+
352
+ // ═══════════════════════════════════════════════════════════════════════════════
353
+ // UPDATE CHECKER - Actually implemented
354
+ // ═══════════════════════════════════════════════════════════════════════════════
355
+ async function checkForUpdates(state, config) {
356
+ if (!config.updateCheck) return null;
357
+ if (isCI()) return null;
358
+
359
+ const now = Date.now();
360
+ if (state.lastUpdateCheck && (now - state.lastUpdateCheck) < UPDATE_CHECK_INTERVAL) {
361
+ // Use cached version if recent
362
+ if (state.latestVersion && state.latestVersion !== getVersion()) {
363
+ return state.latestVersion;
364
+ }
365
+ return null;
366
+ }
367
+
368
+ // Async check - don't block CLI startup
369
+ return new Promise((resolve) => {
370
+ const https = getHttps();
371
+ const timeout = setTimeout(() => resolve(null), 3000); // 3s timeout
372
+
373
+ const req = https.get(
374
+ "https://registry.npmjs.org/@vibecheck/cli/latest",
375
+ { headers: { "Accept": "application/json", "User-Agent": `vibecheck-cli/${getVersion()}` } },
376
+ (res) => {
377
+ let data = "";
378
+ res.on("data", (chunk) => { data += chunk; });
379
+ res.on("end", () => {
380
+ clearTimeout(timeout);
381
+ try {
382
+ const pkg = JSON.parse(data);
383
+ const latest = pkg.version;
384
+ state.lastUpdateCheck = now;
385
+ state.latestVersion = latest;
386
+ saveState(state);
387
+
388
+ if (latest && latest !== getVersion()) {
389
+ resolve(latest);
390
+ } else {
391
+ resolve(null);
392
+ }
393
+ } catch {
394
+ resolve(null);
395
+ }
396
+ });
397
+ }
398
+ );
399
+
400
+ req.on("error", () => {
401
+ clearTimeout(timeout);
402
+ resolve(null);
403
+ });
404
+
405
+ req.end();
406
+ });
407
+ }
408
+
409
+ function printUpdateNotice(latestVersion) {
410
+ const currentVersion = getVersion();
411
+ console.log();
412
+ console.log(`${c.yellow}${sym.sparkles}${c.reset} Update available: ${c.dim}${currentVersion}${c.reset} ${sym.arrow} ${c.green}${latestVersion}${c.reset}`);
413
+ console.log(` Run ${c.cyan}npm update -g @vibecheck/cli${c.reset} to update`);
414
+ console.log();
415
+ }
416
+
417
+ // ═══════════════════════════════════════════════════════════════════════════════
418
+ // FUZZY MATCHING
419
+ // ═══════════════════════════════════════════════════════════════════════════════
420
+ function levenshtein(a, b) {
421
+ const matrix = [];
422
+ for (let i = 0; i <= b.length; i++) matrix[i] = [i];
423
+ for (let j = 0; j <= a.length; j++) matrix[0][j] = j;
424
+ for (let i = 1; i <= b.length; i++) {
425
+ for (let j = 1; j <= a.length; j++) {
426
+ if (b.charAt(i - 1) === a.charAt(j - 1)) {
427
+ matrix[i][j] = matrix[i - 1][j - 1];
428
+ } else {
429
+ matrix[i][j] = Math.min(
430
+ matrix[i - 1][j - 1] + 1,
431
+ matrix[i][j - 1] + 1,
432
+ matrix[i - 1][j] + 1
433
+ );
434
+ }
435
+ }
436
+ }
437
+ return matrix[b.length][a.length];
438
+ }
439
+
440
+ function findSimilarCommands(input, commands, maxDistance = 3) {
441
+ const matches = [];
442
+ for (const cmd of commands) {
443
+ const distance = levenshtein(input.toLowerCase(), cmd.toLowerCase());
444
+ if (distance <= maxDistance) {
445
+ matches.push({ cmd, distance });
446
+ }
447
+ if (cmd.toLowerCase().startsWith(input.toLowerCase()) && input.length >= 2) {
448
+ matches.push({ cmd, distance: 0.5 });
449
+ }
450
+ }
451
+ return [...new Set(matches.sort((a, b) => a.distance - b.distance).map(m => m.cmd))].slice(0, 3);
452
+ }
453
+
454
+ // ═══════════════════════════════════════════════════════════════════════════════
455
+ // COMMAND REGISTRY (lazy loaded)
456
+ // ═══════════════════════════════════════════════════════════════════════════════
457
+ let _registry = null;
458
+ function getRegistry() {
459
+ if (!_registry) {
460
+ _registry = require("./registry");
461
+ }
462
+ return _registry;
463
+ }
464
+
465
+ function getRunner(cmd) {
466
+ const registry = getRegistry();
467
+ return registry.getRunner(cmd, { red: c.red, reset: c.reset, errorSymbol: sym.error });
468
+ }
469
+
470
+ // ═══════════════════════════════════════════════════════════════════════════════
471
+ // ENTITLEMENTS & UPSELL (lazy loaded)
472
+ // ═══════════════════════════════════════════════════════════════════════════════
473
+ let _entitlements = null;
474
+ let _upsell = null;
475
+
476
+ function getEntitlements() {
477
+ if (!_entitlements) {
478
+ _entitlements = require("./runners/lib/entitlements-v2");
479
+ }
480
+ return _entitlements;
481
+ }
482
+
483
+ function getUpsell() {
484
+ if (!_upsell) {
485
+ _upsell = require("./runners/lib/upsell");
486
+ }
487
+ return _upsell;
488
+ }
489
+
490
+ // ═══════════════════════════════════════════════════════════════════════════════
491
+ // CLI OUTPUT UTILITIES (lazy loaded)
492
+ // ═══════════════════════════════════════════════════════════════════════════════
493
+ let _cliOutput = null;
494
+ function getCliOutput() {
495
+ if (!_cliOutput) {
496
+ _cliOutput = require("./runners/lib/cli-output");
497
+ }
498
+ return _cliOutput;
499
+ }
500
+
501
+ // ═══════════════════════════════════════════════════════════════════════════════
502
+ // AUTH MODULE (lazy loaded)
503
+ // ═══════════════════════════════════════════════════════════════════════════════
504
+ let _authModule = null;
505
+ function getAuthModule() {
506
+ if (!_authModule) {
507
+ _authModule = require("./runners/lib/auth");
508
+ }
509
+ return _authModule;
510
+ }
511
+
512
+ // ═══════════════════════════════════════════════════════════════════════════════
513
+ // ACCESS CONTROL
514
+ // ═══════════════════════════════════════════════════════════════════════════════
515
+ async function checkCommandAccess(cmd, args, authInfo) {
516
+ const registry = getRegistry();
517
+ const def = registry.COMMANDS[cmd];
518
+ if (!def) return { allowed: true };
519
+
520
+ const entitlements = getEntitlements();
521
+ const result = await entitlements.enforce(cmd, {
522
+ apiKey: authInfo?.key,
523
+ projectPath: process.cwd(),
524
+ silent: true,
525
+ });
526
+
527
+ if (result.allowed) {
528
+ return {
529
+ allowed: true,
530
+ tier: result.tier,
531
+ downgrade: result.downgrade,
532
+ limits: result.limits,
533
+ caps: result.caps,
534
+ };
535
+ }
536
+
537
+ const upsell = getUpsell();
538
+ return {
539
+ allowed: false,
540
+ tier: result.tier,
541
+ requiredTier: result.requiredTier,
542
+ exitCode: result.exitCode,
543
+ reason: upsell.formatDenied(cmd, {
544
+ currentTier: result.tier,
545
+ requiredTier: result.requiredTier,
546
+ }),
547
+ };
548
+ }
549
+
550
+ // ═══════════════════════════════════════════════════════════════════════════════
551
+ // SHELL COMPLETIONS
552
+ // ═══════════════════════════════════════════════════════════════════════════════
553
+ function generateBashCompletion() {
554
+ const registry = getRegistry();
555
+ const commands = Object.keys(registry.COMMANDS);
556
+ const aliases = Object.keys(registry.ALIAS_MAP);
557
+ const allCmds = [...commands, ...aliases].join(" ");
558
+
559
+ return `# vibecheck bash completion
560
+ # Add to ~/.bashrc or ~/.bash_profile:
561
+ # eval "$(vibecheck completion bash)"
562
+
563
+ _vibecheck_completions() {
564
+ local cur="\${COMP_WORDS[COMP_CWORD]}"
565
+ local prev="\${COMP_WORDS[COMP_CWORD-1]}"
566
+
567
+ # Main commands
568
+ local commands="${allCmds}"
569
+
570
+ # Global flags
571
+ local global_flags="--help --version --json --ci --quiet --verbose --debug --path --output --no-banner"
572
+
573
+ case "\${prev}" in
574
+ vibecheck|vc)
575
+ COMPREPLY=($(compgen -W "\${commands} \${global_flags}" -- "\${cur}"))
576
+ return 0
577
+ ;;
578
+ --path|--output|-p|-o)
579
+ COMPREPLY=($(compgen -d -- "\${cur}"))
580
+ return 0
581
+ ;;
582
+ init)
583
+ COMPREPLY=($(compgen -W "--local --connect --quick --enterprise --ci --soc2 --hipaa --gdpr --team --mcp" -- "\${cur}"))
584
+ return 0
585
+ ;;
586
+ scan)
587
+ COMPREPLY=($(compgen -W "--fix --autofix --json --strict --path" -- "\${cur}"))
588
+ return 0
589
+ ;;
590
+ report)
591
+ COMPREPLY=($(compgen -W "--format --output html md json sarif csv" -- "\${cur}"))
592
+ return 0
593
+ ;;
594
+ completion)
595
+ COMPREPLY=($(compgen -W "bash zsh fish" -- "\${cur}"))
596
+ return 0
597
+ ;;
598
+ esac
599
+
600
+ if [[ "\${cur}" == -* ]]; then
601
+ COMPREPLY=($(compgen -W "\${global_flags}" -- "\${cur}"))
602
+ fi
603
+ }
604
+
605
+ complete -F _vibecheck_completions vibecheck
606
+ complete -F _vibecheck_completions vc
607
+ `;
608
+ }
609
+
610
+ function generateZshCompletion() {
611
+ const registry = getRegistry();
612
+ const commands = Object.entries(registry.COMMANDS);
613
+
614
+ let cmdList = commands.map(([cmd, def]) => {
615
+ const desc = (def.description || "").replace(/'/g, "\\'").replace(/"/g, '\\"');
616
+ return ` '${cmd}:${desc}'`;
617
+ }).join(" \\\n");
618
+
619
+ return `#compdef vibecheck vc
620
+ # vibecheck zsh completion
621
+ # Add to ~/.zshrc:
622
+ # eval "$(vibecheck completion zsh)"
623
+
624
+ _vibecheck() {
625
+ local -a commands
626
+ commands=(
627
+ ${cmdList}
628
+ )
629
+
630
+ local -a global_opts
631
+ global_opts=(
632
+ '--help[Show help]'
633
+ '--version[Show version]'
634
+ '--json[Output as JSON]'
635
+ '--ci[CI mode]'
636
+ '--quiet[Suppress output]'
637
+ '--verbose[Verbose output]'
638
+ '--debug[Debug mode]'
639
+ '--path[Project path]:directory:_directories'
640
+ '--output[Output directory]:directory:_directories'
641
+ '--no-banner[Hide banner]'
642
+ )
643
+
644
+ _arguments -C \\
645
+ "1: :->command" \\
646
+ "*::arg:->args"
647
+
648
+ case "\$state" in
649
+ command)
650
+ _describe -t commands 'vibecheck commands' commands
651
+ _describe -t options 'options' global_opts
652
+ ;;
653
+ args)
654
+ case "\$words[1]" in
655
+ init)
656
+ _arguments \\
657
+ '--local[Local setup only]' \\
658
+ '--connect[GitHub integration]' \\
659
+ '--quick[Quick setup]' \\
660
+ '--enterprise[Enterprise mode]' \\
661
+ '--ci[Setup CI/CD]' \\
662
+ '--soc2[SOC2 compliance]' \\
663
+ '--hipaa[HIPAA compliance]'
664
+ ;;
665
+ scan)
666
+ _arguments \\
667
+ '--fix[Apply fixes]' \\
668
+ '--autofix[Auto-fix issues]' \\
669
+ '--json[JSON output]' \\
670
+ '--strict[Strict mode]'
671
+ ;;
672
+ report)
673
+ _arguments \\
674
+ '--format[Output format]:format:(html md json sarif csv)' \\
675
+ '--output[Output file]:file:_files'
676
+ ;;
677
+ completion)
678
+ _arguments '1:shell:(bash zsh fish)'
679
+ ;;
680
+ esac
681
+ ;;
682
+ esac
683
+ }
684
+
685
+ compdef _vibecheck vibecheck
686
+ compdef _vibecheck vc
687
+ `;
688
+ }
689
+
690
+ function generateFishCompletion() {
691
+ const registry = getRegistry();
692
+ const commands = Object.entries(registry.COMMANDS);
693
+
694
+ let completions = `# vibecheck fish completion
695
+ # Add to ~/.config/fish/completions/vibecheck.fish
696
+ # Or run: vibecheck completion fish > ~/.config/fish/completions/vibecheck.fish
697
+
698
+ # Disable file completion by default
699
+ complete -c vibecheck -f
700
+ complete -c vc -f
701
+
702
+ # Global flags
703
+ complete -c vibecheck -l help -d 'Show help'
704
+ complete -c vibecheck -l version -d 'Show version'
705
+ complete -c vibecheck -l json -d 'Output as JSON'
706
+ complete -c vibecheck -l ci -d 'CI mode'
707
+ complete -c vibecheck -l quiet -d 'Suppress output'
708
+ complete -c vibecheck -l verbose -d 'Verbose output'
709
+ complete -c vibecheck -l debug -d 'Debug mode'
710
+ complete -c vibecheck -l path -d 'Project path' -r -a '(__fish_complete_directories)'
711
+ complete -c vibecheck -l output -d 'Output directory' -r -a '(__fish_complete_directories)'
712
+ complete -c vibecheck -l no-banner -d 'Hide banner'
713
+
714
+ # Commands
715
+ `;
716
+
717
+ for (const [cmd, def] of commands) {
718
+ const desc = (def.description || "").replace(/'/g, "\\'");
719
+ completions += `complete -c vibecheck -n '__fish_use_subcommand' -a '${cmd}' -d '${desc}'\n`;
720
+ completions += `complete -c vc -n '__fish_use_subcommand' -a '${cmd}' -d '${desc}'\n`;
721
+ }
722
+
723
+ // Add aliases
724
+ for (const [alias, target] of Object.entries(registry.ALIAS_MAP)) {
725
+ const def = registry.COMMANDS[target];
726
+ if (def) {
727
+ const desc = `Alias for ${target}`;
728
+ completions += `complete -c vibecheck -n '__fish_use_subcommand' -a '${alias}' -d '${desc}'\n`;
729
+ completions += `complete -c vc -n '__fish_use_subcommand' -a '${alias}' -d '${desc}'\n`;
730
+ }
731
+ }
732
+
733
+ completions += `
734
+ # init subcommand options
735
+ complete -c vibecheck -n '__fish_seen_subcommand_from init' -l local -d 'Local setup only'
736
+ complete -c vibecheck -n '__fish_seen_subcommand_from init' -l connect -d 'GitHub integration'
737
+ complete -c vibecheck -n '__fish_seen_subcommand_from init' -l quick -d 'Quick setup'
738
+ complete -c vibecheck -n '__fish_seen_subcommand_from init' -l enterprise -d 'Enterprise mode'
739
+
740
+ # scan subcommand options
741
+ complete -c vibecheck -n '__fish_seen_subcommand_from scan' -l fix -d 'Apply fixes'
742
+ complete -c vibecheck -n '__fish_seen_subcommand_from scan' -l autofix -d 'Auto-fix issues'
743
+ complete -c vibecheck -n '__fish_seen_subcommand_from scan' -l strict -d 'Strict mode'
744
+
745
+ # report subcommand options
746
+ complete -c vibecheck -n '__fish_seen_subcommand_from report' -l format -d 'Output format' -r -a 'html md json sarif csv'
747
+
748
+ # completion subcommand
749
+ complete -c vibecheck -n '__fish_seen_subcommand_from completion' -a 'bash zsh fish' -d 'Shell type'
750
+ `;
751
+
752
+ return completions;
753
+ }
754
+
755
+ function runCompletion(args) {
756
+ const shell = args[0];
757
+
758
+ if (!shell || shell === "--help" || shell === "-h") {
759
+ console.log(`
760
+ ${c.bold}Usage:${c.reset} vibecheck completion <shell>
761
+
762
+ ${c.bold}Shells:${c.reset}
763
+ bash Generate bash completions
764
+ zsh Generate zsh completions
765
+ fish Generate fish completions
766
+
767
+ ${c.bold}Installation:${c.reset}
768
+ ${c.dim}# Bash (add to ~/.bashrc)${c.reset}
769
+ eval "$(vibecheck completion bash)"
770
+
771
+ ${c.dim}# Zsh (add to ~/.zshrc)${c.reset}
772
+ eval "$(vibecheck completion zsh)"
773
+
774
+ ${c.dim}# Fish${c.reset}
775
+ vibecheck completion fish > ~/.config/fish/completions/vibecheck.fish
776
+ `);
777
+ return 0;
778
+ }
779
+
780
+ switch (shell.toLowerCase()) {
781
+ case "bash":
782
+ console.log(generateBashCompletion());
783
+ return 0;
784
+ case "zsh":
785
+ console.log(generateZshCompletion());
786
+ return 0;
787
+ case "fish":
788
+ console.log(generateFishCompletion());
789
+ return 0;
790
+ default:
791
+ console.error(`${c.red}${sym.error}${c.reset} Unknown shell: ${shell}`);
792
+ console.error(`Supported shells: bash, zsh, fish`);
793
+ return 1;
794
+ }
795
+ }
796
+
797
+ // ═══════════════════════════════════════════════════════════════════════════════
798
+ // HELP SYSTEM - World-Class CLI Experience
799
+ // ═══════════════════════════════════════════════════════════════════════════════
800
+ function printBanner() {
801
+ const VERSION = getVersion();
802
+ console.log(`
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}
814
+ `);
815
+ }
816
+
817
+ /**
818
+ * Print command-specific help with rich examples
819
+ */
820
+ function printCommandHelp(cmd) {
821
+ const registry = getRegistry();
822
+ const def = registry.COMMANDS[cmd];
823
+ if (!def) return false;
824
+
825
+ // Build reverse alias map
826
+ const reverseAliases = {};
827
+ for (const [alias, target] of Object.entries(registry.ALIAS_MAP)) {
828
+ if (!reverseAliases[target]) reverseAliases[target] = [];
829
+ reverseAliases[target].push(alias);
830
+ }
831
+
832
+ const aliases = reverseAliases[cmd] || [];
833
+
834
+ // Tier badge (2-tier: FREE / PRO)
835
+ const tierBadge = def.tier === "free" ? `${c.green}[FREE]${c.reset}` :
836
+ def.tier === "pro" ? `${c.magenta}[PRO]${c.reset}` : "";
837
+
838
+ console.log(`
839
+ ${c.bold}${sym.arrowRight} vibecheck ${cmd}${c.reset} ${tierBadge}
840
+ ${aliases.length > 0 ? `${c.dim}Aliases: ${aliases.join(", ")}${c.reset}` : ""}
841
+
842
+ ${def.longDescription || def.description}
843
+ `);
844
+
845
+ // Examples
846
+ if (def.examples && def.examples.length > 0) {
847
+ console.log(` ${c.bold}${sym.star} EXAMPLES${c.reset}\n`);
848
+ for (const ex of def.examples) {
849
+ const exTier = ex.tier === "pro" ? `${c.magenta}[PRO]${c.reset} ` : "";
850
+ console.log(` ${c.dim}#${c.reset} ${ex.description} ${exTier}`);
851
+ console.log(` ${c.cyan}${ex.command}${c.reset}`);
852
+ console.log();
853
+ }
854
+ }
855
+
856
+ // Related commands
857
+ if (def.related && def.related.length > 0) {
858
+ console.log(` ${c.bold}${sym.arrowRight} RELATED COMMANDS${c.reset}\n`);
859
+ for (const relCmd of def.related) {
860
+ const relDef = registry.COMMANDS[relCmd];
861
+ if (relDef) {
862
+ const relTier = relDef.tier === "pro" ? `${c.magenta}[PRO]${c.reset} ` : "";
863
+ console.log(` ${c.cyan}vibecheck ${relCmd}${c.reset} ${relTier}${c.dim}${relDef.description}${c.reset}`);
864
+ }
865
+ }
866
+ console.log();
867
+ }
868
+
869
+ // Documentation link
870
+ if (def.docsUrl) {
871
+ console.log(` ${c.dim}${sym.boxHorizontal.repeat(56)}${c.reset}`);
872
+ console.log(` ${c.dim}Documentation: ${c.underline}${def.docsUrl}${c.reset}`);
873
+ }
874
+
875
+ console.log(` ${c.dim}Run 'vibecheck --help' for all commands.${c.reset}\n`);
876
+
877
+ return true;
878
+ }
879
+
880
+ function printHelp(showBanner = true) {
881
+ if (showBanner) printBanner();
882
+
883
+ const registry = getRegistry();
884
+ const { COMMANDS } = registry;
885
+
886
+ // Categories — clean story: Start → Core → Evidence → Automation
887
+ const categoryOrder = ["start", "core", "output", "verify", "enforce", "automation"];
888
+ const categories = {
889
+ start: { name: "GETTING STARTED", color: c.yellow, icon: sym.rocket },
890
+ core: { name: "CORE LOOP", color: c.green, icon: sym.shield },
891
+ output: { name: "EVIDENCE", color: c.blue, icon: sym.file },
892
+ verify: { name: "VERIFICATION", color: c.cyan, icon: sym.check },
893
+ enforce: { name: "ENFORCEMENT", color: c.magenta, icon: sym.lock },
894
+ automation: { name: "AUTOMATION", color: c.brightBlue, icon: sym.gear },
895
+ };
896
+
897
+ // Group commands by category
898
+ const grouped = {};
899
+ for (const [cmd, def] of Object.entries(COMMANDS)) {
900
+ const cat = def.category || "automation";
901
+ if (!grouped[cat]) grouped[cat] = [];
902
+ grouped[cat].push({ cmd, ...def });
903
+ }
904
+
905
+ // Print each category
906
+ for (const catKey of categoryOrder) {
907
+ const commands = grouped[catKey];
908
+ if (!commands || commands.length === 0) continue;
909
+
910
+ const cat = categories[catKey];
911
+ console.log(`\n${cat.color}${cat.icon} ${cat.name}${c.reset}\n`);
912
+
913
+ for (const { cmd, description, tier, subcommands } of commands) {
914
+ // Tier badge
915
+ const tierBadge = tier === "pro" ? `${c.magenta}[PRO]${c.reset} ` : `${c.green}[FREE]${c.reset} `;
916
+
917
+ // Subcommand hint
918
+ let subHint = "";
919
+ if (subcommands && subcommands.length > 0) {
920
+ const subNames = subcommands.map(s => s.name).join(", ");
921
+ subHint = ` ${c.dim}(${subNames})${c.reset}`;
922
+ }
923
+
924
+ console.log(` ${c.cyan}${cmd.padEnd(12)}${c.reset} ${tierBadge}${description}${subHint}`);
925
+ }
926
+ }
927
+
928
+ console.log(`
929
+ ${c.dim}${sym.boxHorizontal.repeat(64)}${c.reset}
930
+
931
+ ${c.green}${sym.star} PRODUCT STORY${c.reset}
932
+
933
+ ${c.bold}Scan${c.reset} ${c.dim}→${c.reset} ${c.bold}Ship${c.reset} ${c.dim}→${c.reset} ${c.bold}Certify${c.reset} ${c.dim}That's it. Three steps to proven code.${c.reset}
934
+
935
+ ${c.green}${sym.rocket} QUICK START${c.reset}
936
+
937
+ ${c.bold}One command:${c.reset}
938
+ ${c.cyan}vibecheck kickoff${c.reset} ${c.dim}60-second first run experience${c.reset}
939
+
940
+ ${c.bold}Step by step:${c.reset}
941
+ ${c.bold}1.${c.reset} ${c.cyan}vibecheck scan${c.reset} ${c.dim}Find what's broken${c.reset}
942
+ ${c.bold}2.${c.reset} ${c.cyan}vibecheck fix --apply${c.reset} ${c.dim}Fix it with AI${c.reset}
943
+ ${c.bold}3.${c.reset} ${c.cyan}vibecheck ship${c.reset} ${c.dim}Get verdict: SHIP / WARN / BLOCK${c.reset}
944
+
945
+ ${c.green}${sym.lightning} COMMON WORKFLOWS${c.reset}
946
+
947
+ ${c.cyan}vibecheck scan secrets${c.reset} ${c.dim}Scan for leaked secrets${c.reset}
948
+ ${c.cyan}vibecheck firewall on${c.reset} ${c.dim}Enable agent firewall${c.reset}
949
+ ${c.cyan}vibecheck certify --badge${c.reset} ${c.dim}Full proof + badge${c.reset}
950
+ ${c.cyan}vibecheck report sarif${c.reset} ${c.dim}SARIF for GitHub CI${c.reset}
951
+ ${c.cyan}vibecheck ship preflight${c.reset} ${c.dim}Pre-release checklist${c.reset}
952
+
953
+ ${c.green}${sym.star} PRICING${c.reset}
954
+
955
+ ${c.green}FREE${c.reset} ${c.dim}$0${c.reset} kickoff, scan, report, ci, config, doctor
956
+ ${c.magenta}PRO${c.reset} ${c.dim}$49/mo${c.reset} + ship, fix, certify, reality, firewall, mcp
957
+
958
+ ${c.bold}GLOBAL OPTIONS${c.reset}
959
+
960
+ ${c.cyan}--help, -h${c.reset} Show help for any command
961
+ ${c.cyan}--json${c.reset} Machine-readable JSON output
962
+ ${c.cyan}--quiet, -q${c.reset} Suppress non-essential output
963
+ ${c.cyan}--verbose${c.reset} Detailed output for debugging
964
+ ${c.cyan}--ci${c.reset} CI mode (quiet + no-banner)
965
+ ${c.cyan}--offline${c.reset} Run without API connection
966
+ ${c.cyan}--path, -p <dir>${c.reset} Run in specified directory
967
+
968
+ ${c.dim}${sym.boxHorizontal.repeat(64)}${c.reset}
969
+ ${c.dim}Run 'vibecheck <command> --help' for subcommands and options.${c.reset}
970
+ ${c.dim}Docs: https://docs.vibecheckai.dev${c.reset}
971
+ `);
972
+ }
973
+
974
+ // ═══════════════════════════════════════════════════════════════════════════════
975
+ // HELP VALIDATION
976
+ // ═══════════════════════════════════════════════════════════════════════════════
977
+
978
+ /**
979
+ * Self-test function to verify help output matches actual commands
980
+ * Run with: node bin/vibecheck.js --self-test
981
+ */
982
+ function validateHelpOutput() {
983
+ const registry = getRegistry();
984
+ const { COMMANDS, ALIAS_MAP } = registry;
985
+
986
+ // Extract commands from help text (simplified - just check registry)
987
+ const actualCommands = Object.keys(COMMANDS);
988
+ const actualAliases = Object.keys(ALIAS_MAP);
989
+ const allActualCommands = [...actualCommands, ...actualAliases];
990
+
991
+ // Check for obvious issues
992
+ const issues = [];
993
+
994
+ // Check that all commands have runners
995
+ for (const [cmd, def] of Object.entries(COMMANDS)) {
996
+ if (!def.runner) {
997
+ issues.push(`Command '${cmd}' missing runner`);
998
+ }
999
+ }
1000
+
1001
+ // Check that aliases point to valid commands
1002
+ for (const [alias, target] of Object.entries(ALIAS_MAP)) {
1003
+ if (!COMMANDS[target]) {
1004
+ issues.push(`Alias '${alias}' points to non-existent command '${target}'`);
1005
+ }
1006
+ }
1007
+
1008
+ if (issues.length > 0) {
1009
+ console.error('Help output validation failed:');
1010
+ issues.forEach(issue => console.error(` - ${issue}`));
1011
+ return false;
1012
+ }
1013
+
1014
+ return true;
1015
+ }
1016
+
1017
+ // ═══════════════════════════════════════════════════════════════════════════════
1018
+ // HELPER FUNCTIONS
1019
+ // ═══════════════════════════════════════════════════════════════════════════════
1020
+ function getArgValue(args, flags) {
1021
+ for (const flag of flags) {
1022
+ const idx = args.indexOf(flag);
1023
+ if (idx !== -1 && idx < args.length - 1) return args[idx + 1];
1024
+ }
1025
+ return undefined;
1026
+ }
1027
+
1028
+ function hasFlag(args, flags) {
1029
+ for (const flag of flags) {
1030
+ if (args.includes(flag)) return true;
1031
+ }
1032
+ return false;
1033
+ }
1034
+
1035
+ function formatError(error, config) {
1036
+ const lines = [`${c.red}${sym.error} Error:${c.reset} ${error.message}`];
1037
+ if (config.debug && error.stack) {
1038
+ lines.push("", `${c.dim}Stack trace:${c.reset}`, c.dim + error.stack.split("\n").slice(1).join("\n") + c.reset);
1039
+ }
1040
+ return lines.join("\n");
1041
+ }
1042
+
1043
+ /**
1044
+ * Map error to standardized exit code
1045
+ * @param {Error} error - Error object
1046
+ * @param {boolean} json - Whether JSON output is requested
1047
+ * @returns {number} Exit code
1048
+ */
1049
+ function mapErrorToExitCode(error, json = false) {
1050
+ const EXIT_CODES = {
1051
+ 'AUTH_REQUIRED': 5,
1052
+ 'AUTH_FAILED': 6,
1053
+ 'TIER_REQUIRED': 7,
1054
+ 'RATE_LIMITED': 8,
1055
+ 'NOT_FOUND': 4,
1056
+ 'VALIDATION_ERROR': 3,
1057
+ 'USER_ERROR': 3,
1058
+ 'NETWORK_ERROR': 9,
1059
+ };
1060
+
1061
+ // Check error.code first
1062
+ if (error.code && EXIT_CODES[error.code]) {
1063
+ return EXIT_CODES[error.code];
1064
+ }
1065
+
1066
+ // Check error.exitCode
1067
+ if (error.exitCode !== undefined) {
1068
+ return error.exitCode;
1069
+ }
1070
+
1071
+ // Check error message for common patterns
1072
+ const message = error.message?.toLowerCase() || '';
1073
+ if (message.includes('not found') || message.includes('not exist')) {
1074
+ return 4; // NOT_FOUND
1075
+ }
1076
+ if (message.includes('auth') || message.includes('login')) {
1077
+ return 5; // AUTH_REQUIRED
1078
+ }
1079
+ if (message.includes('network') || message.includes('connection')) {
1080
+ return 9; // NETWORK_ERROR
1081
+ }
1082
+
1083
+ // Default to internal error
1084
+ return 10; // INTERNAL_ERROR
1085
+ }
1086
+
1087
+ // ═══════════════════════════════════════════════════════════════════════════════
1088
+ // FLAG PARSING
1089
+ // ═══════════════════════════════════════════════════════════════════════════════
1090
+ function parseGlobalFlags(rawArgs) {
1091
+ const flags = {
1092
+ help: false,
1093
+ version: false,
1094
+ json: false,
1095
+ ci: false,
1096
+ quiet: false,
1097
+ verbose: false,
1098
+ debug: false,
1099
+ strict: false,
1100
+ noBanner: false,
1101
+ offline: false,
1102
+ path: process.cwd(),
1103
+ output: null,
1104
+ };
1105
+
1106
+ const cleanArgs = [];
1107
+ let i = 0;
1108
+
1109
+ while (i < rawArgs.length) {
1110
+ const arg = rawArgs[i];
1111
+
1112
+ switch (arg) {
1113
+ case "--help":
1114
+ case "-h":
1115
+ flags.help = true;
1116
+ break;
1117
+ case "--version":
1118
+ case "-v":
1119
+ flags.version = true;
1120
+ break;
1121
+ case "--json":
1122
+ flags.json = true;
1123
+ break;
1124
+ case "--ci":
1125
+ flags.ci = true;
1126
+ break;
1127
+ case "--quiet":
1128
+ case "-q":
1129
+ flags.quiet = true;
1130
+ break;
1131
+ case "--verbose":
1132
+ flags.verbose = true;
1133
+ break;
1134
+ case "--debug":
1135
+ flags.debug = true;
1136
+ break;
1137
+ case "--strict":
1138
+ flags.strict = true;
1139
+ break;
1140
+ case "--no-banner":
1141
+ flags.noBanner = true;
1142
+ break;
1143
+ case "--offline":
1144
+ case "--local":
1145
+ flags.offline = true;
1146
+ break;
1147
+ case "--path":
1148
+ case "-p":
1149
+ flags.path = rawArgs[++i] || process.cwd();
1150
+ break;
1151
+ case "--output":
1152
+ case "-o":
1153
+ flags.output = rawArgs[++i] || null;
1154
+ break;
1155
+ default:
1156
+ if (arg.startsWith("--path=")) {
1157
+ flags.path = arg.split("=")[1];
1158
+ } else if (arg.startsWith("--output=")) {
1159
+ flags.output = arg.split("=")[1];
1160
+ } else {
1161
+ cleanArgs.push(arg);
1162
+ }
1163
+ }
1164
+ i++;
1165
+ }
1166
+
1167
+ return { flags, cleanArgs };
1168
+ }
1169
+
1170
+ // ═══════════════════════════════════════════════════════════════════════════════
1171
+ // MAIN ENTRY POINT
1172
+ // ═══════════════════════════════════════════════════════════════════════════════
1173
+ async function main() {
1174
+ const startTime = performance.now();
1175
+ const rawArgs = process.argv.slice(2);
1176
+
1177
+ // Parse global flags
1178
+ const { flags: globalFlags, cleanArgs } = parseGlobalFlags(rawArgs);
1179
+
1180
+ // Handle version (fast path - no config loading)
1181
+ if (globalFlags.version) {
1182
+ console.log(`vibecheck v${getVersion()}`);
1183
+ process.exit(0);
1184
+ }
1185
+
1186
+ // Handle self-test flag (for CI validation)
1187
+ if (cleanArgs.includes('--self-test')) {
1188
+ const registry = getRegistry();
1189
+ if (!validateHelpOutput()) {
1190
+ process.exit(1);
1191
+ }
1192
+ console.log('Self-test passed: Help output matches command registry');
1193
+ process.exit(0);
1194
+ }
1195
+
1196
+ // Load config and state
1197
+ const config = loadConfig();
1198
+ const state = loadState();
1199
+
1200
+ // Apply flag overrides to config
1201
+ if (globalFlags.debug) config.debug = true;
1202
+ if (globalFlags.verbose) config.verbose = true;
1203
+ if (globalFlags.quiet) config.quiet = true;
1204
+ if (globalFlags.noBanner) config.noBanner = true;
1205
+ if (globalFlags.ci) {
1206
+ config.quiet = true;
1207
+ config.noBanner = true;
1208
+ }
1209
+
1210
+ // Handle no command
1211
+ if (!cleanArgs[0]) {
1212
+ printHelp(!config.noBanner);
1213
+ process.exit(0);
1214
+ }
1215
+
1216
+ // Handle global help
1217
+ if (globalFlags.help && !cleanArgs[0]) {
1218
+ printHelp(!config.noBanner);
1219
+ process.exit(0);
1220
+ }
1221
+
1222
+ // Handle completion command (special - doesn't need registry loaded normally)
1223
+ if (cleanArgs[0] === "completion") {
1224
+ process.exit(runCompletion(cleanArgs.slice(1)));
1225
+ }
1226
+
1227
+ // Load registry for command resolution
1228
+ const registry = getRegistry();
1229
+ const { COMMANDS, ALIAS_MAP, ALL_COMMANDS, HIDDEN_COMMANDS, SUBCOMMAND_MAP, DEPRECATION_MAP } = registry;
1230
+
1231
+ let cmd = cleanArgs[0];
1232
+ const originalCmd = cmd; // Save original for deprecation warning
1233
+ let subcommandRunner = null; // Will be set if a subcommand route matches
1234
+
1235
+ // ═══════════════════════════════════════════════════════════════════════════════
1236
+ // COMMAND RESOLUTION (3-pass)
1237
+ // 1. Exact match in 12 top-level COMMANDS
1238
+ // 2. Alias resolution (old names → new 12)
1239
+ // 3. Hidden command fallback (power-user / legacy)
1240
+ // ═══════════════════════════════════════════════════════════════════════════════
1241
+ let resolvedViaAlias = false;
1242
+ let resolvedViaHidden = false;
1243
+
1244
+ if (!COMMANDS[cmd]) {
1245
+ if (ALIAS_MAP[cmd]) {
1246
+ resolvedViaAlias = true;
1247
+ cmd = ALIAS_MAP[cmd];
1248
+ } else if (HIDDEN_COMMANDS && HIDDEN_COMMANDS[cmd]) {
1249
+ resolvedViaHidden = true;
1250
+ // Hidden commands run directly — skip subcommand logic
1251
+ }
1252
+ }
1253
+
1254
+ let cmdArgs = cleanArgs.slice(1);
1255
+
1256
+ // ═══════════════════════════════════════════════════════════════════════════════
1257
+ // SUBCOMMAND ROUTING
1258
+ // If the first arg matches a known subcommand, route to its dedicated runner.
1259
+ // e.g. "vibecheck scan secrets" → SUBCOMMAND_MAP["scan:secrets"]
1260
+ // "vibecheck ship preflight" → SUBCOMMAND_MAP["ship:preflight"]
1261
+ // "vibecheck firewall on" → SUBCOMMAND_MAP["firewall:on"]
1262
+ // ═══════════════════════════════════════════════════════════════════════════════
1263
+ if (COMMANDS[cmd] && cmdArgs.length > 0 && !resolvedViaHidden) {
1264
+ const subKey = `${cmd}:${cmdArgs[0]}`;
1265
+ if (SUBCOMMAND_MAP[subKey]) {
1266
+ try {
1267
+ const subDef = SUBCOMMAND_MAP[subKey];
1268
+ subcommandRunner = subDef.runner();
1269
+ const prependArgs = subDef.prependArgs || [];
1270
+ // Drop the subcommand name from args, prepend any mapped args
1271
+ cmdArgs = [...prependArgs, ...cmdArgs.slice(1)];
1272
+ } catch (e) {
1273
+ // Fall through to default runner if subcommand runner fails to load
1274
+ subcommandRunner = null;
1275
+ }
1276
+ }
1277
+ }
1278
+
1279
+ // ═══════════════════════════════════════════════════════════════════════════════
1280
+ // SPECIAL ALIAS ROUTING
1281
+ // When old top-level commands that are now subcommands are used via alias,
1282
+ // route them to the correct subcommand runner.
1283
+ // e.g. "vibecheck launch" → alias to "ship" → route to ship:preflight runner
1284
+ // "vibecheck login" → alias to "config" → route to config:auth runner
1285
+ // ═══════════════════════════════════════════════════════════════════════════════
1286
+ if (resolvedViaAlias && !subcommandRunner) {
1287
+ const aliasRoutes = {
1288
+ // Auth aliases → config:auth
1289
+ "login": "config:auth", "logout": "config:auth", "whoami": "config:auth",
1290
+ "me": "config:auth", "signin": "config:auth", "signout": "config:auth",
1291
+ // launch → ship:preflight
1292
+ "launch": "ship:preflight", "gate": "ship:preflight",
1293
+ "ci-gate": "ship:preflight", "preflight": "ship:preflight", "prelaunch": "ship:preflight",
1294
+ // shield/guard → firewall (default runner is fine)
1295
+ // prove/verify/seal/badge → certify subcommands
1296
+ "prove": "certify:prove", "verify": "certify:verify",
1297
+ "seal": "certify:seal", "badge": "certify:seal", "attest": "certify:seal",
1298
+ "truth": "certify:truth", "truthpack": "certify:truth",
1299
+ // safelist/allowlist → config:safelist
1300
+ "safelist": "config:safelist", "allowlist": "config:safelist",
1301
+ "al": "config:safelist", "suppress": "config:safelist",
1302
+ // context → config:context
1303
+ "context": "config:context", "ctx": "config:context",
1304
+ // forge → firewall:rules
1305
+ "rules": "firewall:rules", "ai-rules": "firewall:rules",
1306
+ "mdc": "firewall:rules", "brain": "firewall:rules",
1307
+ // intent/approve → firewall subcommands
1308
+ "intent": "firewall:intent", "approve": "firewall:approve",
1309
+ // checkpoint shortcuts → fix:checkpoint
1310
+ "checkpoint": "fix:checkpoint", "cp": "fix:checkpoint",
1311
+ "snap": "fix:checkpoint", "snapshot": "fix:checkpoint",
1312
+ "timemachine": "fix:checkpoint", "rollback": "fix:checkpoint",
1313
+ // polish → fix:polish
1314
+ "prod": "fix:polish", "final": "fix:polish",
1315
+ // missions → fix:missions
1316
+ "missions": "fix:missions",
1317
+ // packs → report (default runner)
1318
+ // evidence-pack → report:evidence
1319
+ "evidence-pack": "report:evidence",
1320
+ "permissions-pack": "report:permissions",
1321
+ "proof-graph": "report:graph",
1322
+ };
1323
+
1324
+ const subKey = aliasRoutes[originalCmd];
1325
+ if (subKey && SUBCOMMAND_MAP[subKey]) {
1326
+ try {
1327
+ const subDef = SUBCOMMAND_MAP[subKey];
1328
+ subcommandRunner = subDef.runner();
1329
+ const prependArgs = subDef.prependArgs || [];
1330
+ // For auth aliases, prepend the original command as subcommand
1331
+ const authAliases = ['login', 'logout', 'whoami', 'me', 'signin', 'signout'];
1332
+ if (authAliases.includes(originalCmd)) {
1333
+ const mapped = originalCmd === 'signin' ? 'login' : originalCmd === 'signout' ? 'logout' : originalCmd;
1334
+ cmdArgs = [mapped, ...cmdArgs];
1335
+ }
1336
+ cmdArgs = [...prependArgs, ...cmdArgs];
1337
+ } catch (e) {
1338
+ subcommandRunner = null;
1339
+ }
1340
+ }
1341
+ }
1342
+
1343
+ // ═══════════════════════════════════════════════════════════════════════════════
1344
+ // DEPRECATION WARNING
1345
+ // Show one-line hint when user uses an old command name
1346
+ // ═══════════════════════════════════════════════════════════════════════════════
1347
+ if (resolvedViaAlias && DEPRECATION_MAP && DEPRECATION_MAP[originalCmd]) {
1348
+ const dep = DEPRECATION_MAP[originalCmd];
1349
+ if (!globalFlags.json && !globalFlags.quiet) {
1350
+ console.log(`${c.yellow}${sym.warning}${c.reset} ${c.dim}${dep.message} — use '${c.cyan}vibecheck ${dep.target}${c.reset}${c.dim}' instead${c.reset}`);
1351
+ }
1352
+ }
1353
+
1354
+ // Handle command-specific help (vibecheck <cmd> --help)
1355
+ if (globalFlags.help && cmd && (COMMANDS[cmd] || (HIDDEN_COMMANDS && HIDDEN_COMMANDS[cmd]))) {
1356
+ // Try our rich help first, then fall back to runner's --help
1357
+ if (COMMANDS[cmd] && printCommandHelp(cmd)) {
1358
+ process.exit(0);
1359
+ }
1360
+ }
1361
+
1362
+ // Pass --help to runner if specified with command
1363
+ if (globalFlags.help) cmdArgs = ["--help", ...cmdArgs];
1364
+
1365
+ // Pass standard flags to runners
1366
+ if (globalFlags.json) cmdArgs.push("--json");
1367
+ if (globalFlags.ci) cmdArgs.push("--ci");
1368
+ if (globalFlags.noBanner) cmdArgs.push("--no-banner");
1369
+ if (globalFlags.quiet) cmdArgs.push("--quiet");
1370
+ if (globalFlags.path && globalFlags.path !== process.cwd()) {
1371
+ cmdArgs.push("--path", globalFlags.path);
1372
+ }
1373
+ if (globalFlags.output) {
1374
+ cmdArgs.push("--output", globalFlags.output);
1375
+ }
1376
+ if (globalFlags.verbose) cmdArgs.push("--verbose");
1377
+ if (globalFlags.strict) cmdArgs.push("--strict");
1378
+ if (globalFlags.offline) cmdArgs.push("--offline");
1379
+
1380
+ // Unknown command
1381
+ if (!COMMANDS[cmd] && !resolvedViaHidden) {
1382
+ const suggestions = findSimilarCommands(cmd, [...Object.keys(COMMANDS)]);
1383
+ console.log(`\n${c.red}${sym.error}${c.reset} Unknown command: ${c.yellow}${originalCmd}${c.reset}`);
1384
+
1385
+ if (suggestions.length > 0) {
1386
+ console.log(`\n${c.dim}Did you mean:${c.reset}`);
1387
+ suggestions.forEach((s) => {
1388
+ const def = COMMANDS[s];
1389
+ console.log(` ${c.cyan}vibecheck ${s}${c.reset} ${c.dim}${def?.description || ""}${c.reset}`);
1390
+ });
1391
+ }
1392
+ console.log(`\n${c.dim}Run 'vibecheck --help' for available commands.${c.reset}\n`);
1393
+ process.exit(4);
1394
+ }
1395
+
1396
+ // Generate runId
1397
+ const cliOutput = getCliOutput();
1398
+ const runId = cliOutput.generateRunId();
1399
+ const runStart = new Date().toISOString();
1400
+
1401
+ const cmdDef = COMMANDS[cmd] || (HIDDEN_COMMANDS && HIDDEN_COMMANDS[cmd]) || {};
1402
+ let authInfo = { key: null };
1403
+
1404
+ // Check for offline/dev mode (via flag or env var)
1405
+ // SECURITY: VIBECHECK_DEV_PRO only works in non-production to prevent auth bypass
1406
+ const isDevProAllowed = process.env.NODE_ENV !== 'production' &&
1407
+ process.env.CI !== 'true' &&
1408
+ process.env.CI !== '1' &&
1409
+ process.env.VIBECHECK_DEV_PRO === '1';
1410
+ const isOffline = globalFlags.offline ||
1411
+ process.env.VIBECHECK_OFFLINE === '1' ||
1412
+ process.env.VIBECHECK_LOCAL === '1' ||
1413
+ isDevProAllowed;
1414
+
1415
+ // Auth check (unless skipAuth or offline mode)
1416
+ // Auth-exempt: config (has auth subcommand), doctor, kickoff, plus direct auth aliases
1417
+ const authExemptCommands = ['auth', 'config', 'login', 'logout', 'whoami', 'me', 'signin', 'signout', 'help', '--help', '-h', 'version', '--version', 'doctor', 'kickoff'];
1418
+ const needsAuth = !authExemptCommands.includes(cmd) && !cmdDef.skipAuth && !isOffline;
1419
+
1420
+ if (needsAuth) {
1421
+ const auth = getAuthModule();
1422
+ const { key } = auth.getApiKey();
1423
+ authInfo.key = key;
1424
+
1425
+ // If no API key, prompt for login
1426
+ if (!key) {
1427
+ console.log(`\n ${c.red}❌ Authentication Required${c.reset}`);
1428
+ console.log(` Please log in to use vibecheck.\n`);
1429
+ console.log(` ${c.cyan}Options:${c.reset}`);
1430
+ console.log(` 1. Press ${c.yellow}Enter${c.reset} to open browser and sign up`);
1431
+ console.log(` 2. Visit ${c.underline}https://vibecheckai.dev${c.reset} directly`);
1432
+ console.log(` 3. Run ${c.cyan}vibecheck login${c.reset} after getting your API key\n`);
1433
+
1434
+ // Wait for Enter key
1435
+ const readline = getReadline();
1436
+ readline.createInterface({
1437
+ input: process.stdin,
1438
+ output: process.stdout
1439
+ }).question('', async () => {
1440
+ try {
1441
+ const https = getHttps();
1442
+ const url = 'https://vibecheckai.dev';
1443
+ console.log(`\n Opening ${c.underline}${url}${c.reset} in your browser...\n`);
1444
+
1445
+ // Try to open browser
1446
+ const start = process.platform === 'darwin' ? 'open' :
1447
+ process.platform === 'win32' ? 'start' : 'xdg-open';
1448
+ require('child_process').exec(`${start} ${url}`);
1449
+ } catch (e) {
1450
+ console.log(` Could not open browser. Please visit: ${c.underline}https://vibecheckai.dev${c.reset}`);
1451
+ }
1452
+ process.exit(0);
1453
+ });
1454
+
1455
+ // Keep process alive
1456
+ return new Promise(() => {});
1457
+ }
1458
+
1459
+ const access = await checkCommandAccess(cmd, cmdArgs, authInfo);
1460
+
1461
+ if (!access.allowed) {
1462
+ console.log(access.reason);
1463
+
1464
+ // Show upgrade prompt if tier insufficient
1465
+ if (access.upgradeUrl) {
1466
+ console.log(`\n ${c.cyan}Upgrade:${c.reset} ${access.upgradeUrl}`);
1467
+ }
1468
+
1469
+ return 1;
1470
+ }
1471
+
1472
+ authInfo.access = access;
1473
+ } else if (isOffline) {
1474
+ // Offline mode - provide basic access info
1475
+ // Suppress message if JSON/SARIF output is requested (check both global flags and command args)
1476
+ const hasJsonOutput = globalFlags.json || cmdArgs.includes('--json') || cmdArgs.includes('--sarif');
1477
+ if (!config.quiet && !config.noBanner && !hasJsonOutput) {
1478
+ console.log(`${c.cyan}${sym.arrowRight} OFFLINE${c.reset} ${c.dim}mode - local scanning without API${c.reset}`);
1479
+ }
1480
+ authInfo.access = {
1481
+ allowed: true,
1482
+ tier: 'free',
1483
+ caps: { maxFiles: 100, maxDepth: 3 },
1484
+ offline: true
1485
+ };
1486
+ }
1487
+
1488
+ // Update state
1489
+ state.runCount++;
1490
+ state.lastRun = Date.now();
1491
+ state.commandHistory = [...(state.commandHistory || []).slice(-99), { cmd, timestamp: Date.now() }];
1492
+ saveState(state);
1493
+
1494
+ // Check for updates (async, non-blocking for startup)
1495
+ let updatePromise = null;
1496
+ if (!config.quiet && !config.noBanner && !isCI()) {
1497
+ updatePromise = checkForUpdates(state, config);
1498
+ }
1499
+
1500
+ let exitCode = 0;
1501
+
1502
+ try {
1503
+ // Use subcommand runner if resolved, otherwise default command runner
1504
+ const runner = subcommandRunner || getRunner(cmd);
1505
+ if (!runner) {
1506
+ console.error(`${c.red}${sym.error}${c.reset} Failed to load runner for: ${cmd}`);
1507
+ process.exit(4); // NOT_FOUND
1508
+ }
1509
+
1510
+ const context = {
1511
+ repoRoot: globalFlags.path,
1512
+ config,
1513
+ state,
1514
+ authInfo,
1515
+ version: getVersion(),
1516
+ isCI: isCI(),
1517
+ runId,
1518
+ runStart,
1519
+ };
1520
+
1521
+ // Execute command - all commands use consistent runner signature
1522
+ const result = await runner(cmdArgs, context);
1523
+
1524
+ // Ensure result has standardized exit code
1525
+ if (typeof result === 'object' && result.exitCode !== undefined) {
1526
+ exitCode = result.exitCode;
1527
+ } else if (typeof result === 'number') {
1528
+ exitCode = result;
1529
+ } else {
1530
+ exitCode = 0; // SUCCESS
1531
+ }
1532
+ } catch (error) {
1533
+ // Map errors to exit codes
1534
+ exitCode = mapErrorToExitCode(error, globalFlags.json);
1535
+
1536
+ if (globalFlags.json) {
1537
+ console.log(JSON.stringify({
1538
+ success: false,
1539
+ error: {
1540
+ code: error.code || 'INTERNAL_ERROR',
1541
+ message: error.message,
1542
+ },
1543
+ exitCode,
1544
+ }, null, 2));
1545
+ } else {
1546
+ console.error(formatError(error, config));
1547
+ }
1548
+ }
1549
+
1550
+ // Create receipt for paid commands
1551
+ if (cmdDef.tier !== "free" && runId) {
1552
+ try {
1553
+ const { createManifestAndReceipt } = require("./runners/lib/receipts");
1554
+ await createManifestAndReceipt({
1555
+ runId,
1556
+ command: cmd,
1557
+ args: cmdArgs,
1558
+ tier: authInfo.access?.tier || "free",
1559
+ exitCode,
1560
+ startTime: runStart,
1561
+ endTime: new Date().toISOString(),
1562
+ projectPath: globalFlags.path,
1563
+ });
1564
+ } catch (e) {
1565
+ if (config.debug) console.error(`Failed to create receipt: ${e.message}`);
1566
+ }
1567
+ }
1568
+
1569
+ // Show update notice after command (if available)
1570
+ if (updatePromise) {
1571
+ const latestVersion = await updatePromise;
1572
+ if (latestVersion) {
1573
+ printUpdateNotice(latestVersion);
1574
+ }
1575
+ }
1576
+
1577
+ // Debug timing
1578
+ if (config.debug) {
1579
+ console.log(`\n${c.dim}${sym.clock} Total: ${(performance.now() - startTime).toFixed(0)}ms${c.reset}`);
1580
+ }
1581
+
1582
+ process.exit(exitCode);
1583
+ }
1584
+
1585
+ // ═══════════════════════════════════════════════════════════════════════════════
1586
+ // GRACEFUL SHUTDOWN
1587
+ // ═══════════════════════════════════════════════════════════════════════════════
1588
+ process.on("SIGINT", () => {
1589
+ console.log(`\n${c.yellow}${sym.warning}${c.reset} Interrupted`);
1590
+ process.exit(130);
1591
+ });
1592
+
1593
+ process.on("SIGTERM", () => {
1594
+ console.log(`\n${c.yellow}${sym.warning}${c.reset} Terminated`);
1595
+ process.exit(143);
1596
+ });
1597
+
1598
+ process.on("uncaughtException", (error) => {
1599
+ console.error(`\n${c.red}${sym.error} Uncaught Exception:${c.reset} ${error.message}`);
1600
+ if (process.env.VIBECHECK_DEBUG === "true") {
1601
+ console.error(c.dim + error.stack + c.reset);
1602
+ }
1603
+ process.exit(1);
1604
+ });
1605
+
1606
+ process.on("unhandledRejection", (reason) => {
1607
+ console.error(`\n${c.red}${sym.error} Unhandled Rejection:${c.reset} ${reason}`);
1608
+ process.exit(1);
1609
+ });
1610
+
1611
+ // ═══════════════════════════════════════════════════════════════════════════════
1612
+ // RUN
1613
+ // ═══════════════════════════════════════════════════════════════════════════════
1614
+ main().catch((error) => {
1615
+ console.error(`\n${c.red}${sym.error} Fatal:${c.reset} ${error.message}`);
1616
+ process.exit(1);
1617
+ });