vibecheck-ai 2.0.1 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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 +451 -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,2000 @@
1
+ /**
2
+ * vibecheck launch - Pre-Release Validation Wizard
3
+ *
4
+ * ═══════════════════════════════════════════════════════════════════════════════
5
+ * THE LAST 10 MINUTES BEFORE RELEASE
6
+ * ═══════════════════════════════════════════════════════════════════════════════
7
+ *
8
+ * One command to validate everything:
9
+ * • Build & Compilation
10
+ * • Environment Wiring
11
+ * • Route Integrity
12
+ * • Auth Configuration
13
+ * • Integration Health
14
+ * • Security Posture
15
+ *
16
+ * @module bin/runners/runLaunch
17
+ * @version 2.0.0
18
+ */
19
+
20
+ "use strict";
21
+
22
+ const path = require("path");
23
+ const fs = require("fs");
24
+ const readline = require("readline");
25
+ const crypto = require("crypto");
26
+ const { execSync, spawn } = require("child_process");
27
+
28
+ // ═══════════════════════════════════════════════════════════════════════════════
29
+ // LAZY IMPORTS
30
+ // ═══════════════════════════════════════════════════════════════════════════════
31
+
32
+ let _globalFlags = null;
33
+ let _exitCodes = null;
34
+ let _errorHandler = null;
35
+ let _cliOutput = null;
36
+
37
+ function getGlobalFlags() {
38
+ if (!_globalFlags) _globalFlags = require("./lib/global-flags");
39
+ return _globalFlags;
40
+ }
41
+
42
+ function getExitCodes() {
43
+ if (!_exitCodes) _exitCodes = require("./lib/exit-codes");
44
+ return _exitCodes;
45
+ }
46
+
47
+ function getErrorHandler() {
48
+ if (!_errorHandler) _errorHandler = require("./lib/error-handler");
49
+ return _errorHandler;
50
+ }
51
+
52
+ function getCliOutput() {
53
+ if (!_cliOutput) _cliOutput = require("./lib/unified-cli-output");
54
+ return _cliOutput;
55
+ }
56
+
57
+ // ═══════════════════════════════════════════════════════════════════════════════
58
+ // CONSTANTS
59
+ // ═══════════════════════════════════════════════════════════════════════════════
60
+
61
+ const LAUNCH_VERSION = "2.0.0";
62
+ const DEFAULT_TIMEOUT = 600000; // 10 minutes
63
+ const CHECK_TIMEOUT = 60000; // 1 minute per check
64
+
65
+ const PHASES = {
66
+ build: {
67
+ name: "Build Validation",
68
+ description: "TypeScript compilation, artifacts, bundle analysis",
69
+ icon: "🏗️",
70
+ estimatedTime: "~2 min",
71
+ },
72
+ env: {
73
+ name: "Environment Wiring",
74
+ description: "Variables, secrets, URL validation",
75
+ icon: "🌍",
76
+ estimatedTime: "~1 min",
77
+ },
78
+ routes: {
79
+ name: "Route Integrity",
80
+ description: "Registration, handlers, contract drift",
81
+ icon: "🛤️",
82
+ estimatedTime: "~2 min",
83
+ },
84
+ auth: {
85
+ name: "Auth Configuration",
86
+ description: "Middleware, protection, CORS, rate limiting",
87
+ icon: "🔐",
88
+ estimatedTime: "~1 min",
89
+ },
90
+ integrations: {
91
+ name: "Integration Health",
92
+ description: "Database, Redis, billing, webhooks",
93
+ icon: "🔗",
94
+ estimatedTime: "~2 min",
95
+ },
96
+ security: {
97
+ name: "Security Posture",
98
+ description: "Secrets scan, dependency audit, mock detection",
99
+ icon: "🛡️",
100
+ estimatedTime: "~2 min",
101
+ },
102
+ };
103
+
104
+ const PHASE_ORDER = ["build", "env", "routes", "auth", "integrations", "security"];
105
+
106
+ // Secret patterns (critical - must not be in code)
107
+ const SECRET_PATTERNS = [
108
+ { name: "Stripe Live Key", pattern: /sk_live_[a-zA-Z0-9]{24,}/g, severity: "BLOCK" },
109
+ { name: "AWS Access Key", pattern: /AKIA[A-Z0-9]{16}/g, severity: "BLOCK" },
110
+ { name: "Private Key", pattern: /-----BEGIN\s+(RSA|EC|DSA|OPENSSH)?\s*PRIVATE KEY-----/g, severity: "BLOCK" },
111
+ { name: "GitHub Token", pattern: /ghp_[a-zA-Z0-9]{36}/g, severity: "BLOCK" },
112
+ { name: "Generic API Key", pattern: /['"]?api[_-]?key['"]?\s*[:=]\s*['"][a-zA-Z0-9_-]{20,}['"]/gi, severity: "WARN" },
113
+ { name: "Password Assignment", pattern: /['"]?password['"]?\s*[:=]\s*['"][^'"]{8,}['"]/gi, severity: "WARN" },
114
+ { name: "JWT Secret Hardcoded", pattern: /jwt[_-]?secret\s*[:=]\s*['"][^'"]+['"]/gi, severity: "BLOCK" },
115
+ ];
116
+
117
+ // Mock data patterns
118
+ const MOCK_PATTERNS = [
119
+ { name: "Demo User", pattern: /demo@|test@example|user@test|admin@localhost/gi },
120
+ { name: "Placeholder Email", pattern: /your[_-]?email@|example\.com/gi },
121
+ { name: "Fake Data", pattern: /FAKE_|MOCK_|PLACEHOLDER_|TODO_REPLACE/gi },
122
+ { name: "Lorem Ipsum", pattern: /lorem\s+ipsum/gi },
123
+ { name: "Test Mode Flag", pattern: /isTest\s*[:=]\s*true|testMode\s*[:=]\s*true/gi },
124
+ ];
125
+
126
+ // Auth patterns to detect
127
+ const AUTH_PATTERNS = {
128
+ middleware: [
129
+ /authMiddleware/,
130
+ /authenticate\s*\(/,
131
+ /requireAuth/,
132
+ /isAuthenticated/,
133
+ /verifyToken/,
134
+ /passport\.authenticate/,
135
+ ],
136
+ hashBeforeLookup: [
137
+ /hashApiKey/,
138
+ /hash.*before.*lookup/i,
139
+ /createHash.*apiKey/i,
140
+ /sha256.*apiKey/i,
141
+ ],
142
+ rateLimiting: [
143
+ /rateLimit/i,
144
+ /rate.*limit/i,
145
+ /express-rate-limit/,
146
+ /@fastify\/rate-limit/,
147
+ /throttle/i,
148
+ ],
149
+ cors: [
150
+ /cors\s*\(/,
151
+ /ALLOWED_ORIGINS/,
152
+ /Access-Control-Allow-Origin/,
153
+ ],
154
+ };
155
+
156
+ // Files to scan for security issues
157
+ const SECURITY_PATHS = [
158
+ "src/middleware",
159
+ "src/auth",
160
+ "src/routes/auth",
161
+ "apps/api/src/middleware",
162
+ "apps/api/src/routes/auth",
163
+ "apps/api/src/services/auth",
164
+ "apps/api/src/services/api-key",
165
+ "apps/api/src/services/encryption",
166
+ ];
167
+
168
+ // ═══════════════════════════════════════════════════════════════════════════════
169
+ // ANSI STYLING
170
+ // ═══════════════════════════════════════════════════════════════════════════════
171
+
172
+ const SUPPORTS_COLOR = process.stdout.isTTY && !process.env.NO_COLOR;
173
+
174
+ const c = SUPPORTS_COLOR ? {
175
+ reset: "\x1b[0m",
176
+ bold: "\x1b[1m",
177
+ dim: "\x1b[2m",
178
+ red: "\x1b[31m",
179
+ green: "\x1b[32m",
180
+ yellow: "\x1b[33m",
181
+ blue: "\x1b[34m",
182
+ magenta: "\x1b[35m",
183
+ cyan: "\x1b[36m",
184
+ white: "\x1b[37m",
185
+ gray: "\x1b[90m",
186
+ bgGreen: "\x1b[42m",
187
+ bgYellow: "\x1b[43m",
188
+ bgRed: "\x1b[41m",
189
+ bgCyan: "\x1b[46m",
190
+ bgMagenta: "\x1b[45m",
191
+ } : Object.fromEntries([
192
+ "reset", "bold", "dim", "red", "green", "yellow", "blue",
193
+ "magenta", "cyan", "white", "gray", "bgGreen", "bgYellow", "bgRed", "bgCyan", "bgMagenta"
194
+ ].map(k => [k, ""]));
195
+
196
+ const sym = {
197
+ check: "✓",
198
+ cross: "✗",
199
+ warning: "⚠",
200
+ arrow: "→",
201
+ rocket: "🚀",
202
+ block: "🚫",
203
+ spinner: ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"],
204
+ box: {
205
+ topLeft: "╔", topRight: "╗", bottomLeft: "╚", bottomRight: "╝",
206
+ horizontal: "═", vertical: "║",
207
+ teeRight: "╠", teeLeft: "╣",
208
+ },
209
+ };
210
+
211
+ // ═══════════════════════════════════════════════════════════════════════════════
212
+ // BANNER (uses unified CLI output)
213
+ // ═══════════════════════════════════════════════════════════════════════════════
214
+
215
+ function printUnifiedBanner(projectRoot, opts) {
216
+ const cli = getCliOutput();
217
+ const projectName = path.basename(projectRoot);
218
+
219
+ // Determine integrity based on environment
220
+ const isProduction = process.env.NODE_ENV === "production";
221
+ const integrity = isProduction ? "PRODUCTION" : "DEVELOPMENT";
222
+
223
+ console.log(cli.renderFullHeader("launch", {
224
+ version: LAUNCH_VERSION,
225
+ tier: "PRO",
226
+ integrity,
227
+ target: projectRoot,
228
+ sessionId: cli.generateSessionId(),
229
+ }));
230
+ }
231
+
232
+ // ═══════════════════════════════════════════════════════════════════════════════
233
+ // UTILITIES
234
+ // ═══════════════════════════════════════════════════════════════════════════════
235
+
236
+ /**
237
+ * Recursively find files matching extensions
238
+ */
239
+ function findFiles(dir, extensions, ignore = []) {
240
+ const results = [];
241
+ if (!fs.existsSync(dir)) return results;
242
+
243
+ const defaultIgnore = ["node_modules", ".git", "dist", "build", ".next", "coverage", "__pycache__"];
244
+ const ignoreSet = new Set([...defaultIgnore, ...ignore]);
245
+
246
+ function walk(currentDir) {
247
+ try {
248
+ const entries = fs.readdirSync(currentDir, { withFileTypes: true });
249
+ for (const entry of entries) {
250
+ if (ignoreSet.has(entry.name)) continue;
251
+
252
+ const fullPath = path.join(currentDir, entry.name);
253
+ if (entry.isDirectory()) {
254
+ walk(fullPath);
255
+ } else if (entry.isFile()) {
256
+ const ext = path.extname(entry.name);
257
+ if (extensions.includes(ext)) {
258
+ results.push(fullPath);
259
+ }
260
+ }
261
+ }
262
+ } catch {
263
+ // Permission denied or other errors - skip
264
+ }
265
+ }
266
+
267
+ walk(dir);
268
+ return results;
269
+ }
270
+
271
+ /**
272
+ * Scan file content for patterns
273
+ */
274
+ function scanFileForPatterns(filePath, patterns) {
275
+ const findings = [];
276
+ try {
277
+ const content = fs.readFileSync(filePath, "utf-8");
278
+ const lines = content.split("\n");
279
+
280
+ for (const { name, pattern, severity } of patterns) {
281
+ // Reset regex lastIndex for global patterns
282
+ if (pattern.global) pattern.lastIndex = 0;
283
+
284
+ let lineNum = 0;
285
+ for (const line of lines) {
286
+ lineNum++;
287
+ // Reset for each line
288
+ if (pattern.global) pattern.lastIndex = 0;
289
+
290
+ if (pattern.test(line)) {
291
+ findings.push({
292
+ pattern: name,
293
+ file: filePath,
294
+ line: lineNum,
295
+ content: line.trim().substring(0, 100),
296
+ severity: severity || "WARN",
297
+ });
298
+ }
299
+ }
300
+ }
301
+ } catch {
302
+ // File read error - skip
303
+ }
304
+ return findings;
305
+ }
306
+
307
+ /**
308
+ * Check if any pattern matches in a file
309
+ */
310
+ function fileContainsPatterns(filePath, patterns) {
311
+ try {
312
+ const content = fs.readFileSync(filePath, "utf-8");
313
+ return patterns.some(p => p.test(content));
314
+ } catch {
315
+ return false;
316
+ }
317
+ }
318
+
319
+ /**
320
+ * Get git info for audit trail
321
+ */
322
+ function getGitInfo(projectRoot) {
323
+ try {
324
+ const commit = execSync("git rev-parse HEAD", { cwd: projectRoot, stdio: "pipe" }).toString().trim();
325
+ const branch = execSync("git rev-parse --abbrev-ref HEAD", { cwd: projectRoot, stdio: "pipe" }).toString().trim();
326
+ const status = execSync("git status --porcelain", { cwd: projectRoot, stdio: "pipe" }).toString().trim();
327
+ return {
328
+ commit: commit.substring(0, 12),
329
+ branch,
330
+ dirty: status.length > 0,
331
+ };
332
+ } catch {
333
+ return { commit: "unknown", branch: "unknown", dirty: false };
334
+ }
335
+ }
336
+
337
+ /**
338
+ * Load environment from .env files
339
+ */
340
+ function loadEnvFiles(projectRoot) {
341
+ const env = { ...process.env };
342
+ const envFiles = [".env", ".env.local", ".env.production"];
343
+
344
+ for (const file of envFiles) {
345
+ const filePath = path.join(projectRoot, file);
346
+ if (fs.existsSync(filePath)) {
347
+ try {
348
+ const content = fs.readFileSync(filePath, "utf-8");
349
+ for (const line of content.split("\n")) {
350
+ const match = line.match(/^([^#=]+)=(.*)$/);
351
+ if (match) {
352
+ const key = match[1].trim();
353
+ const value = match[2].trim().replace(/^["']|["']$/g, "");
354
+ if (!env[key]) env[key] = value; // Don't override existing
355
+ }
356
+ }
357
+ } catch {
358
+ // Skip unreadable files
359
+ }
360
+ }
361
+ }
362
+
363
+ return env;
364
+ }
365
+
366
+ /**
367
+ * Detect package manager
368
+ */
369
+ function detectPackageManager(projectRoot) {
370
+ if (fs.existsSync(path.join(projectRoot, "pnpm-lock.yaml"))) return "pnpm";
371
+ if (fs.existsSync(path.join(projectRoot, "yarn.lock"))) return "yarn";
372
+ if (fs.existsSync(path.join(projectRoot, "bun.lockb"))) return "bun";
373
+ return "npm";
374
+ }
375
+
376
+ /**
377
+ * Execute command with timeout
378
+ */
379
+ function execWithTimeout(command, options = {}, timeout = CHECK_TIMEOUT) {
380
+ return new Promise((resolve, reject) => {
381
+ const timer = setTimeout(() => {
382
+ reject(new Error(`Command timed out after ${timeout}ms`));
383
+ }, timeout);
384
+
385
+ try {
386
+ const result = execSync(command, { ...options, stdio: "pipe", timeout });
387
+ clearTimeout(timer);
388
+ resolve(result.toString());
389
+ } catch (err) {
390
+ clearTimeout(timer);
391
+ reject(err);
392
+ }
393
+ });
394
+ }
395
+
396
+ // ═══════════════════════════════════════════════════════════════════════════════
397
+ // ARGS PARSING
398
+ // ═══════════════════════════════════════════════════════════════════════════════
399
+
400
+ function parseArgs(args) {
401
+ const { parseGlobalFlags } = getGlobalFlags();
402
+ const { flags: globalFlags, cleanArgs } = parseGlobalFlags(args);
403
+
404
+ const opts = {
405
+ path: globalFlags.path || process.cwd(),
406
+ json: globalFlags.json || false,
407
+ ci: globalFlags.ci || false,
408
+ quiet: globalFlags.quiet || false,
409
+ verbose: globalFlags.verbose || false,
410
+ noBanner: globalFlags.noBanner || false,
411
+ help: globalFlags.help || false,
412
+ // Launch-specific options
413
+ strict: false,
414
+ skipSlow: false,
415
+ timeout: DEFAULT_TIMEOUT,
416
+ failFast: false,
417
+ skipPhases: [],
418
+ onlyPhases: [],
419
+ bundle: false,
420
+ output: null,
421
+ dryRun: false,
422
+ };
423
+
424
+ for (let i = 0; i < cleanArgs.length; i++) {
425
+ const arg = cleanArgs[i];
426
+ switch (arg) {
427
+ case "--strict":
428
+ opts.strict = true;
429
+ break;
430
+ case "--skip-slow":
431
+ opts.skipSlow = true;
432
+ break;
433
+ case "--timeout":
434
+ opts.timeout = parseInt(cleanArgs[++i], 10) || DEFAULT_TIMEOUT;
435
+ break;
436
+ case "--fail-fast":
437
+ opts.failFast = true;
438
+ break;
439
+ case "--skip":
440
+ opts.skipPhases = (cleanArgs[++i] || "").split(",").map(s => s.trim()).filter(Boolean);
441
+ break;
442
+ case "--only":
443
+ opts.onlyPhases = (cleanArgs[++i] || "").split(",").map(s => s.trim()).filter(Boolean);
444
+ break;
445
+ case "--bundle":
446
+ opts.bundle = true;
447
+ break;
448
+ case "--output":
449
+ case "-o":
450
+ opts.output = cleanArgs[++i];
451
+ break;
452
+ case "--path":
453
+ case "-p":
454
+ opts.path = cleanArgs[++i] || process.cwd();
455
+ break;
456
+ case "--dry-run":
457
+ opts.dryRun = true;
458
+ break;
459
+ }
460
+ }
461
+
462
+ opts.path = path.resolve(opts.path);
463
+ return opts;
464
+ }
465
+
466
+ // ═══════════════════════════════════════════════════════════════════════════════
467
+ // HELP
468
+ // ═══════════════════════════════════════════════════════════════════════════════
469
+
470
+ function printHelp() {
471
+ console.log(`
472
+ ${c.bold}${c.cyan}VIBECHECK LAUNCH${c.reset} - Pre-Release Validation Wizard (v${LAUNCH_VERSION})
473
+
474
+ ${c.bold}USAGE${c.reset}
475
+ ${c.cyan}vibecheck launch${c.reset} [options]
476
+
477
+ ${c.bold}DESCRIPTION${c.reset}
478
+ Run all pre-release validations in one command. This is your "last 10 minutes
479
+ before release" checklist that ensures everything is ready for production.
480
+
481
+ ${c.bold}OPTIONS${c.reset}
482
+ ${c.cyan}--ci${c.reset} CI mode (non-interactive, machine-readable output)
483
+ ${c.cyan}--json${c.reset} Output results as JSON
484
+ ${c.cyan}--strict${c.reset} Treat warnings as blockers
485
+ ${c.cyan}--skip-slow${c.reset} Skip checks that take >30s
486
+ ${c.cyan}--timeout <ms>${c.reset} Maximum execution time ${c.dim}(default: 600000)${c.reset}
487
+ ${c.cyan}--fail-fast${c.reset} Stop on first blocker
488
+ ${c.cyan}--skip <phases>${c.reset} Skip specific phases ${c.dim}(comma-separated)${c.reset}
489
+ ${c.cyan}--only <phases>${c.reset} Run only specific phases ${c.dim}(comma-separated)${c.reset}
490
+ ${c.cyan}--bundle${c.reset} Auto-generate launch bundle after completion
491
+ ${c.cyan}--output, -o <dir>${c.reset} Output directory for artifacts
492
+ ${c.cyan}--path, -p <dir>${c.reset} Project path ${c.dim}(default: current directory)${c.reset}
493
+ ${c.cyan}--dry-run${c.reset} Show what would be checked without running
494
+ ${c.cyan}--quiet, -q${c.reset} Suppress non-essential output
495
+ ${c.cyan}--verbose, -v${c.reset} Show detailed output
496
+ ${c.cyan}--help, -h${c.reset} Show this help
497
+
498
+ ${c.bold}PHASES${c.reset}
499
+ ${c.cyan}build${c.reset} Build validation (TypeScript, artifacts, bundle)
500
+ ${c.cyan}env${c.reset} Environment wiring (variables, secrets, URLs)
501
+ ${c.cyan}routes${c.reset} Route integrity (registration, handlers, drift)
502
+ ${c.cyan}auth${c.reset} Auth configuration (middleware, protection, CORS)
503
+ ${c.cyan}integrations${c.reset} Integration health (database, Redis, billing)
504
+ ${c.cyan}security${c.reset} Security posture (secrets scan, audit, mocks)
505
+
506
+ ${c.bold}EXAMPLES${c.reset}
507
+ ${c.dim}# Interactive wizard${c.reset}
508
+ vibecheck launch
509
+
510
+ ${c.dim}# CI mode with JSON output${c.reset}
511
+ vibecheck launch --ci --json
512
+
513
+ ${c.dim}# Strict mode (warnings = blockers)${c.reset}
514
+ vibecheck launch --strict
515
+
516
+ ${c.dim}# Only run specific phases${c.reset}
517
+ vibecheck launch --only build,env,security
518
+
519
+ ${c.dim}# Auto-bundle results${c.reset}
520
+ vibecheck launch --bundle
521
+
522
+ ${c.bold}EXIT CODES${c.reset}
523
+ ${c.green}0${c.reset} LAUNCH - Ready to ship, no blockers
524
+ ${c.yellow}1${c.reset} WARN - Ready to ship, but warnings exist
525
+ ${c.red}2${c.reset} BLOCK - Cannot ship, blockers found
526
+ ${c.red}3${c.reset} ERROR - Execution error
527
+
528
+ ${c.dim}Documentation: https://docs.vibecheckai.dev/commands/launch${c.reset}
529
+ `);
530
+ }
531
+
532
+ // ═══════════════════════════════════════════════════════════════════════════════
533
+ // CHECK RUNNER UTILITY
534
+ // ═══════════════════════════════════════════════════════════════════════════════
535
+
536
+ async function runCheck(name, description, fn, severity = "WARN", opts = {}) {
537
+ const start = Date.now();
538
+ const timeout = opts.timeout || CHECK_TIMEOUT;
539
+
540
+ try {
541
+ // Wrap with timeout
542
+ const result = await Promise.race([
543
+ fn(),
544
+ new Promise((_, reject) =>
545
+ setTimeout(() => reject(new Error("Check timed out")), timeout)
546
+ ),
547
+ ]);
548
+
549
+ return {
550
+ name,
551
+ description,
552
+ status: result.pass ? "pass" : (result.skip ? "skip" : "fail"),
553
+ severity: result.severity || severity,
554
+ duration: Date.now() - start,
555
+ details: result.details,
556
+ error: result.error,
557
+ fix: result.fix,
558
+ location: result.location,
559
+ findings: result.findings || [],
560
+ };
561
+ } catch (err) {
562
+ return {
563
+ name,
564
+ description,
565
+ status: "error",
566
+ severity,
567
+ duration: Date.now() - start,
568
+ error: err.message,
569
+ findings: [],
570
+ };
571
+ }
572
+ }
573
+
574
+ // ═══════════════════════════════════════════════════════════════════════════════
575
+ // PHASE 1: BUILD VALIDATION
576
+ // ═══════════════════════════════════════════════════════════════════════════════
577
+
578
+ async function runBuildPhase(ctx, opts) {
579
+ const checks = [];
580
+ const pm = detectPackageManager(ctx.projectRoot);
581
+
582
+ // TypeScript compilation
583
+ checks.push(await runCheck("typescript", "TypeScript compilation", async () => {
584
+ const tsconfigPath = path.join(ctx.projectRoot, "tsconfig.json");
585
+ if (!fs.existsSync(tsconfigPath)) {
586
+ return { skip: true, details: "No tsconfig.json found" };
587
+ }
588
+
589
+ try {
590
+ await execWithTimeout("npx tsc --noEmit", { cwd: ctx.projectRoot }, opts.skipSlow ? 30000 : 120000);
591
+ return { pass: true };
592
+ } catch (err) {
593
+ const stderr = err.stderr?.toString() || err.message;
594
+ const errorCount = (stderr.match(/error TS/g) || []).length;
595
+ return {
596
+ pass: false,
597
+ error: `TypeScript compilation failed (${errorCount} errors)`,
598
+ details: stderr.substring(0, 500),
599
+ fix: "Run 'npx tsc --noEmit' to see all errors",
600
+ };
601
+ }
602
+ }, "BLOCK", { timeout: opts.skipSlow ? 30000 : 120000 }));
603
+
604
+ // Build artifacts present
605
+ checks.push(await runCheck("build-artifacts", "Build artifacts present", async () => {
606
+ const distPaths = [
607
+ { path: "dist", framework: "generic" },
608
+ { path: "build", framework: "CRA/generic" },
609
+ { path: ".next", framework: "Next.js" },
610
+ { path: "out", framework: "Next.js static" },
611
+ { path: ".output", framework: "Nuxt" },
612
+ { path: ".svelte-kit", framework: "SvelteKit" },
613
+ ];
614
+
615
+ for (const { path: distPath, framework } of distPaths) {
616
+ const fullPath = path.join(ctx.projectRoot, distPath);
617
+ if (fs.existsSync(fullPath)) {
618
+ const stats = fs.statSync(fullPath);
619
+ const age = Date.now() - stats.mtimeMs;
620
+ const ageHours = Math.floor(age / (1000 * 60 * 60));
621
+ const isFresh = age < 24 * 60 * 60 * 1000;
622
+
623
+ return {
624
+ pass: true,
625
+ details: `Found ${distPath} (${framework}, ${isFresh ? "fresh" : `${ageHours}h old`})`,
626
+ };
627
+ }
628
+ }
629
+
630
+ return {
631
+ pass: false,
632
+ error: "No build artifacts found",
633
+ fix: `Run '${pm} build' to create production build`,
634
+ };
635
+ }, "BLOCK"));
636
+
637
+ // Package.json scripts
638
+ checks.push(await runCheck("package-scripts", "Required scripts defined", async () => {
639
+ const pkgPath = path.join(ctx.projectRoot, "package.json");
640
+ if (!fs.existsSync(pkgPath)) {
641
+ return { pass: false, error: "No package.json found" };
642
+ }
643
+
644
+ try {
645
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
646
+ const scripts = pkg.scripts || {};
647
+ const required = ["build", "start"];
648
+ const missing = required.filter(s => !scripts[s]);
649
+
650
+ if (missing.length > 0) {
651
+ return {
652
+ pass: false,
653
+ error: `Missing scripts: ${missing.join(", ")}`,
654
+ severity: "WARN",
655
+ };
656
+ }
657
+ return { pass: true };
658
+ } catch (err) {
659
+ return { pass: false, error: "Invalid package.json" };
660
+ }
661
+ }, "WARN"));
662
+
663
+ // Check for circular dependencies (if madge available)
664
+ if (!opts.skipSlow) {
665
+ checks.push(await runCheck("circular-deps", "No circular dependencies", async () => {
666
+ try {
667
+ // Try using madge if installed
668
+ const result = await execWithTimeout(
669
+ "npx madge --circular --extensions ts,tsx,js,jsx src/ 2>/dev/null || true",
670
+ { cwd: ctx.projectRoot },
671
+ 30000
672
+ );
673
+
674
+ if (result.includes("Found")) {
675
+ const count = (result.match(/Found (\d+)/)?.[1]) || "some";
676
+ return {
677
+ pass: false,
678
+ error: `${count} circular dependencies found`,
679
+ severity: "WARN",
680
+ details: result.substring(0, 300),
681
+ };
682
+ }
683
+ return { pass: true };
684
+ } catch {
685
+ return { skip: true, details: "madge not available" };
686
+ }
687
+ }, "WARN", { timeout: 30000 }));
688
+ }
689
+
690
+ return { phase: "build", checks };
691
+ }
692
+
693
+ // ═══════════════════════════════════════════════════════════════════════════════
694
+ // PHASE 2: ENVIRONMENT WIRING
695
+ // ═══════════════════════════════════════════════════════════════════════════════
696
+
697
+ async function runEnvPhase(ctx, opts) {
698
+ const checks = [];
699
+ const env = loadEnvFiles(ctx.projectRoot);
700
+ const nodeEnv = env.NODE_ENV || "development";
701
+ const isProduction = nodeEnv === "production";
702
+
703
+ // Core required variables
704
+ const coreVars = [
705
+ { key: "DATABASE_URL", required: true, minLength: 10, description: "Database connection" },
706
+ { key: "JWT_SECRET", required: true, minLength: 32, description: "JWT signing secret" },
707
+ ];
708
+
709
+ // Production-only variables
710
+ const prodVars = [
711
+ { key: "ENCRYPTION_KEY", pattern: /^[a-f0-9]{64}$/i, description: "64-char hex encryption key" },
712
+ { key: "FRONTEND_URL", pattern: /^https:\/\//, description: "HTTPS frontend URL" },
713
+ { key: "ALLOWED_ORIGINS", minLength: 1, description: "CORS allowed origins" },
714
+ { key: "COOKIE_SECRET", minLength: 32, description: "Cookie signing secret" },
715
+ ];
716
+
717
+ // Check core variables
718
+ for (const { key, minLength, pattern, description } of coreVars) {
719
+ checks.push(await runCheck(`env-${key.toLowerCase()}`, `${key} is configured`, async () => {
720
+ const value = env[key];
721
+ if (!value) {
722
+ return { pass: false, error: `${key} is not set`, fix: `Add ${key} to .env file` };
723
+ }
724
+ if (minLength && value.length < minLength) {
725
+ return { pass: false, error: `${key} is too short (need ${minLength}+ chars)` };
726
+ }
727
+ if (pattern && !pattern.test(value)) {
728
+ return { pass: false, error: `${key} has invalid format (${description})` };
729
+ }
730
+ return { pass: true };
731
+ }, "BLOCK"));
732
+ }
733
+
734
+ // Check production variables (warning in dev, block in prod)
735
+ if (isProduction) {
736
+ for (const { key, minLength, pattern, description } of prodVars) {
737
+ checks.push(await runCheck(`env-${key.toLowerCase()}`, `${key} is configured (production)`, async () => {
738
+ const value = env[key];
739
+ if (!value) {
740
+ return { pass: false, error: `${key} required in production` };
741
+ }
742
+ if (minLength && value.length < minLength) {
743
+ return { pass: false, error: `${key} is too short (need ${minLength}+ chars)` };
744
+ }
745
+ if (pattern && !pattern.test(value)) {
746
+ return { pass: false, error: `${key} invalid format (${description})` };
747
+ }
748
+ return { pass: true };
749
+ }, "BLOCK"));
750
+ }
751
+ }
752
+
753
+ // Check for localhost in URLs (critical in production)
754
+ checks.push(await runCheck("no-localhost", "No localhost in URLs", async () => {
755
+ const urlVars = ["DATABASE_URL", "FRONTEND_URL", "API_URL", "REDIS_URL", "BACKEND_URL"];
756
+ const violations = [];
757
+
758
+ for (const key of urlVars) {
759
+ const value = env[key];
760
+ if (value && (value.includes("localhost") || value.includes("127.0.0.1"))) {
761
+ violations.push(key);
762
+ }
763
+ }
764
+
765
+ if (violations.length > 0) {
766
+ return {
767
+ pass: false,
768
+ error: `Localhost found in: ${violations.join(", ")}`,
769
+ severity: isProduction ? "BLOCK" : "WARN",
770
+ fix: "Use production URLs for deployment",
771
+ };
772
+ }
773
+ return { pass: true };
774
+ }, isProduction ? "BLOCK" : "WARN"));
775
+
776
+ // Check for weak secrets
777
+ checks.push(await runCheck("secret-strength", "Secrets have sufficient entropy", async () => {
778
+ const secretVars = ["JWT_SECRET", "COOKIE_SECRET", "ENCRYPTION_KEY"];
779
+ const weak = [];
780
+
781
+ for (const key of secretVars) {
782
+ const value = env[key];
783
+ if (value) {
784
+ // Check for common weak patterns
785
+ if (value === "secret" || value === "password" || value === "changeme" ||
786
+ value.length < 16 || /^(.)\1+$/.test(value) || /^12345/.test(value)) {
787
+ weak.push(key);
788
+ }
789
+ }
790
+ }
791
+
792
+ if (weak.length > 0) {
793
+ return {
794
+ pass: false,
795
+ error: `Weak secrets detected: ${weak.join(", ")}`,
796
+ fix: "Use cryptographically random secrets (e.g., openssl rand -hex 32)",
797
+ };
798
+ }
799
+ return { pass: true };
800
+ }, "BLOCK"));
801
+
802
+ // Check .env.example exists
803
+ checks.push(await runCheck("env-template", "Environment template exists", async () => {
804
+ const templates = [".env.example", ".env.template", ".env.sample"];
805
+ const found = templates.find(t => fs.existsSync(path.join(ctx.projectRoot, t)));
806
+
807
+ if (!found) {
808
+ return {
809
+ pass: false,
810
+ error: "No .env.example file",
811
+ severity: "WARN",
812
+ fix: "Create .env.example with required variables (without values)",
813
+ };
814
+ }
815
+ return { pass: true, details: `Found ${found}` };
816
+ }, "WARN"));
817
+
818
+ return { phase: "env", checks };
819
+ }
820
+
821
+ // ═══════════════════════════════════════════════════════════════════════════════
822
+ // PHASE 3: ROUTE INTEGRITY
823
+ // ═══════════════════════════════════════════════════════════════════════════════
824
+
825
+ async function runRoutesPhase(ctx, opts) {
826
+ const checks = [];
827
+
828
+ // Try to load and use existing route analysis
829
+ checks.push(await runCheck("route-analysis", "Route analysis", async () => {
830
+ try {
831
+ const { buildTruthpackSmart } = require("./lib/truth");
832
+ const { findMissingRoutes, findMethodMismatch, findDeadUI } = require("./lib/analyzers");
833
+
834
+ const truth = await buildTruthpackSmart({ projectRoot: ctx.projectRoot });
835
+
836
+ // Missing routes
837
+ const missing = findMissingRoutes ? findMissingRoutes(truth) : [];
838
+ // Method mismatch
839
+ const mismatch = findMethodMismatch ? findMethodMismatch(truth) : [];
840
+ // Dead routes
841
+ const dead = findDeadUI ? findDeadUI(truth) : [];
842
+
843
+ const issues = [];
844
+ if (missing.length > 0) issues.push(`${missing.length} missing routes`);
845
+ if (mismatch.length > 0) issues.push(`${mismatch.length} method mismatches`);
846
+ if (dead.length > 0) issues.push(`${dead.length} dead routes`);
847
+
848
+ if (missing.length > 0 || mismatch.length > 0) {
849
+ return {
850
+ pass: false,
851
+ error: issues.join(", "),
852
+ findings: [...missing, ...mismatch].slice(0, 5),
853
+ };
854
+ }
855
+
856
+ if (dead.length > 0) {
857
+ return {
858
+ pass: true,
859
+ details: `${dead.length} dead routes detected`,
860
+ severity: "WARN",
861
+ };
862
+ }
863
+
864
+ return { pass: true, details: "All routes verified" };
865
+ } catch (err) {
866
+ return { skip: true, details: `Route analysis unavailable: ${err.message}` };
867
+ }
868
+ }, "BLOCK"));
869
+
870
+ // Check for API route files
871
+ checks.push(await runCheck("api-routes-exist", "API routes defined", async () => {
872
+ const apiPaths = [
873
+ "src/routes",
874
+ "src/api",
875
+ "src/pages/api",
876
+ "app/api",
877
+ "apps/api/src/routes",
878
+ ];
879
+
880
+ for (const apiPath of apiPaths) {
881
+ const fullPath = path.join(ctx.projectRoot, apiPath);
882
+ if (fs.existsSync(fullPath)) {
883
+ const files = findFiles(fullPath, [".ts", ".tsx", ".js", ".jsx"]);
884
+ return { pass: true, details: `Found ${files.length} route files in ${apiPath}` };
885
+ }
886
+ }
887
+
888
+ return { skip: true, details: "No API routes directory found" };
889
+ }, "INFO"));
890
+
891
+ return { phase: "routes", checks };
892
+ }
893
+
894
+ // ═══════════════════════════════════════════════════════════════════════════════
895
+ // PHASE 4: AUTH CONFIGURATION
896
+ // ═══════════════════════════════════════════════════════════════════════════════
897
+
898
+ async function runAuthPhase(ctx, opts) {
899
+ const checks = [];
900
+ const sourceFiles = findFiles(ctx.projectRoot, [".ts", ".tsx", ".js", ".jsx"]);
901
+
902
+ // Check for auth middleware
903
+ checks.push(await runCheck("auth-middleware", "Auth middleware is implemented", async () => {
904
+ const authFiles = sourceFiles.filter(f =>
905
+ f.includes("middleware") || f.includes("auth") || f.includes("guard")
906
+ );
907
+
908
+ let found = false;
909
+ for (const file of authFiles) {
910
+ if (fileContainsPatterns(file, AUTH_PATTERNS.middleware)) {
911
+ found = true;
912
+ break;
913
+ }
914
+ }
915
+
916
+ if (!found) {
917
+ return {
918
+ pass: false,
919
+ error: "No auth middleware detected",
920
+ fix: "Implement authentication middleware for protected routes",
921
+ };
922
+ }
923
+ return { pass: true, details: "Auth middleware patterns found" };
924
+ }, "BLOCK"));
925
+
926
+ // Check for hash-before-lookup pattern (API key security)
927
+ checks.push(await runCheck("hash-before-lookup", "API key hashing implemented", async () => {
928
+ const apiKeyFiles = sourceFiles.filter(f =>
929
+ f.includes("api-key") || f.includes("apikey") || f.includes("auth")
930
+ );
931
+
932
+ if (apiKeyFiles.length === 0) {
933
+ return { skip: true, details: "No API key handling detected" };
934
+ }
935
+
936
+ let found = false;
937
+ for (const file of apiKeyFiles) {
938
+ if (fileContainsPatterns(file, AUTH_PATTERNS.hashBeforeLookup)) {
939
+ found = true;
940
+ break;
941
+ }
942
+ }
943
+
944
+ if (!found) {
945
+ return {
946
+ pass: false,
947
+ error: "API keys should be hashed before database lookup",
948
+ severity: "WARN",
949
+ fix: "Implement hash-before-lookup pattern for API key validation",
950
+ };
951
+ }
952
+ return { pass: true };
953
+ }, "WARN"));
954
+
955
+ // Check for rate limiting
956
+ checks.push(await runCheck("rate-limiting", "Rate limiting configured", async () => {
957
+ let found = false;
958
+ for (const file of sourceFiles.slice(0, 100)) { // Limit scan
959
+ if (fileContainsPatterns(file, AUTH_PATTERNS.rateLimiting)) {
960
+ found = true;
961
+ break;
962
+ }
963
+ }
964
+
965
+ // Also check package.json for rate limit packages
966
+ const pkgPath = path.join(ctx.projectRoot, "package.json");
967
+ if (fs.existsSync(pkgPath)) {
968
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
969
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
970
+ if (deps["express-rate-limit"] || deps["@fastify/rate-limit"] || deps["rate-limiter-flexible"]) {
971
+ found = true;
972
+ }
973
+ }
974
+
975
+ if (!found) {
976
+ return {
977
+ pass: false,
978
+ error: "No rate limiting detected",
979
+ fix: "Add rate limiting to prevent abuse",
980
+ };
981
+ }
982
+ return { pass: true };
983
+ }, "WARN"));
984
+
985
+ // Check CORS configuration
986
+ checks.push(await runCheck("cors-config", "CORS properly configured", async () => {
987
+ const env = loadEnvFiles(ctx.projectRoot);
988
+
989
+ if (!env.ALLOWED_ORIGINS) {
990
+ return {
991
+ pass: false,
992
+ error: "ALLOWED_ORIGINS not configured",
993
+ fix: "Set ALLOWED_ORIGINS in environment",
994
+ };
995
+ }
996
+
997
+ // Check for wildcard CORS (dangerous in production)
998
+ if (env.ALLOWED_ORIGINS === "*") {
999
+ return {
1000
+ pass: false,
1001
+ error: "CORS allows all origins (*) - dangerous in production",
1002
+ fix: "Specify explicit allowed origins",
1003
+ };
1004
+ }
1005
+
1006
+ return { pass: true };
1007
+ }, "BLOCK"));
1008
+
1009
+ return { phase: "auth", checks };
1010
+ }
1011
+
1012
+ // ═══════════════════════════════════════════════════════════════════════════════
1013
+ // PHASE 5: INTEGRATION HEALTH
1014
+ // ═══════════════════════════════════════════════════════════════════════════════
1015
+
1016
+ async function runIntegrationsPhase(ctx, opts) {
1017
+ const checks = [];
1018
+ const env = loadEnvFiles(ctx.projectRoot);
1019
+
1020
+ // Database configuration
1021
+ checks.push(await runCheck("database-config", "Database is configured", async () => {
1022
+ const dbUrl = env.DATABASE_URL;
1023
+ if (!dbUrl) {
1024
+ return { pass: false, error: "DATABASE_URL not set" };
1025
+ }
1026
+
1027
+ // Parse and validate URL format
1028
+ try {
1029
+ const url = new URL(dbUrl);
1030
+ const protocol = url.protocol.replace(":", "");
1031
+ const validProtocols = ["postgresql", "postgres", "mysql", "mongodb", "mongodb+srv", "sqlite"];
1032
+
1033
+ if (!validProtocols.includes(protocol)) {
1034
+ return { pass: false, error: `Unknown database protocol: ${protocol}` };
1035
+ }
1036
+
1037
+ return { pass: true, details: `${protocol} database configured` };
1038
+ } catch {
1039
+ return { pass: false, error: "Invalid DATABASE_URL format" };
1040
+ }
1041
+ }, "BLOCK"));
1042
+
1043
+ // Redis configuration (optional)
1044
+ if (env.REDIS_URL) {
1045
+ checks.push(await runCheck("redis-config", "Redis is configured", async () => {
1046
+ try {
1047
+ const url = new URL(env.REDIS_URL);
1048
+ return { pass: true, details: `Redis at ${url.host}` };
1049
+ } catch {
1050
+ return { pass: false, error: "Invalid REDIS_URL format" };
1051
+ }
1052
+ }, "WARN"));
1053
+ }
1054
+
1055
+ // Stripe configuration (if used)
1056
+ if (env.STRIPE_SECRET_KEY || env.STRIPE_PUBLISHABLE_KEY) {
1057
+ checks.push(await runCheck("stripe-config", "Stripe properly configured", async () => {
1058
+ const secretKey = env.STRIPE_SECRET_KEY;
1059
+ const publishableKey = env.STRIPE_PUBLISHABLE_KEY;
1060
+ const isProduction = env.NODE_ENV === "production";
1061
+
1062
+ if (!secretKey) {
1063
+ return { pass: false, error: "STRIPE_SECRET_KEY not set" };
1064
+ }
1065
+
1066
+ if (isProduction && secretKey.startsWith("sk_test_")) {
1067
+ return {
1068
+ pass: false,
1069
+ error: "Using Stripe TEST key in production!",
1070
+ fix: "Use live Stripe keys for production deployment",
1071
+ };
1072
+ }
1073
+
1074
+ if (isProduction && publishableKey?.startsWith("pk_test_")) {
1075
+ return {
1076
+ pass: false,
1077
+ error: "Using Stripe TEST publishable key in production!",
1078
+ };
1079
+ }
1080
+
1081
+ // Check webhook secret
1082
+ if (!env.STRIPE_WEBHOOK_SECRET) {
1083
+ return {
1084
+ pass: false,
1085
+ error: "STRIPE_WEBHOOK_SECRET not set",
1086
+ severity: "WARN",
1087
+ fix: "Configure webhook secret for secure webhook handling",
1088
+ };
1089
+ }
1090
+
1091
+ return { pass: true };
1092
+ }, "BLOCK"));
1093
+ }
1094
+
1095
+ // Check for Prisma migrations
1096
+ const prismaPath = path.join(ctx.projectRoot, "prisma");
1097
+ if (fs.existsSync(prismaPath)) {
1098
+ checks.push(await runCheck("prisma-migrations", "Prisma migrations up to date", async () => {
1099
+ try {
1100
+ const result = await execWithTimeout(
1101
+ "npx prisma migrate status",
1102
+ { cwd: ctx.projectRoot },
1103
+ 30000
1104
+ );
1105
+
1106
+ if (result.includes("Database schema is up to date")) {
1107
+ return { pass: true };
1108
+ }
1109
+
1110
+ if (result.includes("pending migration")) {
1111
+ return {
1112
+ pass: false,
1113
+ error: "Pending database migrations",
1114
+ fix: "Run 'npx prisma migrate deploy' before deployment",
1115
+ };
1116
+ }
1117
+
1118
+ return { pass: true, details: "Migrations checked" };
1119
+ } catch (err) {
1120
+ // Migration status check failed - could be DB connection issue
1121
+ return {
1122
+ pass: false,
1123
+ error: "Could not check migration status",
1124
+ severity: "WARN",
1125
+ details: err.message,
1126
+ };
1127
+ }
1128
+ }, "WARN"));
1129
+ }
1130
+
1131
+ return { phase: "integrations", checks };
1132
+ }
1133
+
1134
+ // ═══════════════════════════════════════════════════════════════════════════════
1135
+ // PHASE 6: SECURITY POSTURE
1136
+ // ═══════════════════════════════════════════════════════════════════════════════
1137
+
1138
+ async function runSecurityPhase(ctx, opts) {
1139
+ const checks = [];
1140
+ const sourceFiles = findFiles(ctx.projectRoot, [".ts", ".tsx", ".js", ".jsx"]);
1141
+
1142
+ // Scan for hardcoded secrets
1143
+ checks.push(await runCheck("hardcoded-secrets", "No hardcoded secrets in code", async () => {
1144
+ const findings = [];
1145
+
1146
+ // Limit files to scan for performance
1147
+ const filesToScan = sourceFiles.slice(0, 500);
1148
+
1149
+ for (const file of filesToScan) {
1150
+ // Skip test files
1151
+ if (file.includes(".test.") || file.includes(".spec.") || file.includes("__test__")) {
1152
+ continue;
1153
+ }
1154
+
1155
+ const fileFindings = scanFileForPatterns(file, SECRET_PATTERNS);
1156
+ findings.push(...fileFindings);
1157
+
1158
+ // Stop early if we found critical issues
1159
+ if (findings.filter(f => f.severity === "BLOCK").length >= 3) {
1160
+ break;
1161
+ }
1162
+ }
1163
+
1164
+ const blockers = findings.filter(f => f.severity === "BLOCK");
1165
+ const warnings = findings.filter(f => f.severity === "WARN");
1166
+
1167
+ if (blockers.length > 0) {
1168
+ return {
1169
+ pass: false,
1170
+ error: `${blockers.length} hardcoded secrets found`,
1171
+ findings: blockers.slice(0, 5),
1172
+ fix: "Move secrets to environment variables",
1173
+ };
1174
+ }
1175
+
1176
+ if (warnings.length > 0) {
1177
+ return {
1178
+ pass: true,
1179
+ details: `${warnings.length} potential secrets (review recommended)`,
1180
+ severity: "WARN",
1181
+ findings: warnings.slice(0, 3),
1182
+ };
1183
+ }
1184
+
1185
+ return { pass: true };
1186
+ }, "BLOCK"));
1187
+
1188
+ // Scan for TODOs in security-critical code
1189
+ checks.push(await runCheck("security-todos", "No TODOs in security code", async () => {
1190
+ const todoPattern = { name: "TODO/FIXME", pattern: /TODO|FIXME|HACK|XXX/gi };
1191
+ const findings = [];
1192
+
1193
+ for (const secPath of SECURITY_PATHS) {
1194
+ const fullPath = path.join(ctx.projectRoot, secPath);
1195
+ if (!fs.existsSync(fullPath)) continue;
1196
+
1197
+ const files = findFiles(fullPath, [".ts", ".tsx", ".js", ".jsx"]);
1198
+ for (const file of files) {
1199
+ const fileFindings = scanFileForPatterns(file, [todoPattern]);
1200
+ findings.push(...fileFindings);
1201
+ }
1202
+ }
1203
+
1204
+ if (findings.length > 0) {
1205
+ return {
1206
+ pass: false,
1207
+ error: `${findings.length} TODOs in security-critical code`,
1208
+ findings: findings.slice(0, 5),
1209
+ fix: "Resolve TODOs before deployment",
1210
+ };
1211
+ }
1212
+
1213
+ return { pass: true };
1214
+ }, "BLOCK"));
1215
+
1216
+ // Scan for mock/demo data
1217
+ checks.push(await runCheck("mock-data", "No mock/demo data in production code", async () => {
1218
+ const findings = [];
1219
+
1220
+ // Only scan source directories, not tests
1221
+ const srcDirs = ["src", "app", "apps", "lib", "server"];
1222
+
1223
+ for (const dir of srcDirs) {
1224
+ const fullPath = path.join(ctx.projectRoot, dir);
1225
+ if (!fs.existsSync(fullPath)) continue;
1226
+
1227
+ const files = findFiles(fullPath, [".ts", ".tsx", ".js", ".jsx"]).slice(0, 200);
1228
+ for (const file of files) {
1229
+ // Skip test files
1230
+ if (file.includes(".test.") || file.includes(".spec.")) continue;
1231
+
1232
+ const fileFindings = scanFileForPatterns(file, MOCK_PATTERNS);
1233
+ findings.push(...fileFindings);
1234
+ }
1235
+ }
1236
+
1237
+ if (findings.length > 0) {
1238
+ return {
1239
+ pass: false,
1240
+ error: `${findings.length} mock/demo data patterns found`,
1241
+ findings: findings.slice(0, 5),
1242
+ fix: "Remove mock data before production deployment",
1243
+ };
1244
+ }
1245
+
1246
+ return { pass: true };
1247
+ }, "BLOCK"));
1248
+
1249
+ // Check for VIBECHECK_DEV_PRO bypass in production code
1250
+ checks.push(await runCheck("dev-bypass", "No development bypasses in production", async () => {
1251
+ const bypassPattern = {
1252
+ name: "DEV_PRO bypass",
1253
+ pattern: /VIBECHECK_DEV_PRO|DEV_BYPASS|SKIP_AUTH|DISABLE_AUTH/gi,
1254
+ severity: "BLOCK",
1255
+ };
1256
+
1257
+ const findings = [];
1258
+ const srcFiles = sourceFiles.filter(f =>
1259
+ !f.includes(".test.") && !f.includes(".spec.") && !f.includes("node_modules")
1260
+ ).slice(0, 300);
1261
+
1262
+ for (const file of srcFiles) {
1263
+ const fileFindings = scanFileForPatterns(file, [bypassPattern]);
1264
+ findings.push(...fileFindings);
1265
+ }
1266
+
1267
+ if (findings.length > 0) {
1268
+ return {
1269
+ pass: false,
1270
+ error: `${findings.length} development bypass patterns found`,
1271
+ findings: findings.slice(0, 5),
1272
+ fix: "Remove or properly gate development bypasses",
1273
+ };
1274
+ }
1275
+
1276
+ return { pass: true };
1277
+ }, "BLOCK"));
1278
+
1279
+ // Dependency audit
1280
+ if (!opts.skipSlow) {
1281
+ checks.push(await runCheck("dependency-audit", "No critical vulnerabilities", async () => {
1282
+ const pm = detectPackageManager(ctx.projectRoot);
1283
+
1284
+ try {
1285
+ let result;
1286
+ switch (pm) {
1287
+ case "pnpm":
1288
+ result = await execWithTimeout("pnpm audit --audit-level=critical 2>&1 || true", { cwd: ctx.projectRoot }, 60000);
1289
+ break;
1290
+ case "yarn":
1291
+ result = await execWithTimeout("yarn audit --level critical 2>&1 || true", { cwd: ctx.projectRoot }, 60000);
1292
+ break;
1293
+ default:
1294
+ result = await execWithTimeout("npm audit --audit-level=critical 2>&1 || true", { cwd: ctx.projectRoot }, 60000);
1295
+ }
1296
+
1297
+ // Check for critical vulnerabilities
1298
+ if (result.includes("critical") && !result.includes("0 critical")) {
1299
+ const criticalMatch = result.match(/(\d+)\s+critical/);
1300
+ const count = criticalMatch ? criticalMatch[1] : "some";
1301
+ return {
1302
+ pass: false,
1303
+ error: `${count} critical vulnerabilities found`,
1304
+ fix: `Run '${pm} audit fix' to attempt automatic fixes`,
1305
+ };
1306
+ }
1307
+
1308
+ // Check for high vulnerabilities (warning)
1309
+ if (result.includes("high") && !result.includes("0 high")) {
1310
+ const highMatch = result.match(/(\d+)\s+high/);
1311
+ const count = highMatch ? highMatch[1] : "some";
1312
+ return {
1313
+ pass: true,
1314
+ details: `${count} high-severity vulnerabilities (review recommended)`,
1315
+ severity: "WARN",
1316
+ };
1317
+ }
1318
+
1319
+ return { pass: true };
1320
+ } catch (err) {
1321
+ return {
1322
+ pass: true,
1323
+ details: "Audit check skipped",
1324
+ severity: "WARN",
1325
+ };
1326
+ }
1327
+ }, "BLOCK", { timeout: 60000 }));
1328
+ }
1329
+
1330
+ return { phase: "security", checks };
1331
+ }
1332
+
1333
+ // ═══════════════════════════════════════════════════════════════════════════════
1334
+ // ORCHESTRATOR
1335
+ // ═══════════════════════════════════════════════════════════════════════════════
1336
+
1337
+ async function runAllPhases(ctx, opts) {
1338
+ const results = {
1339
+ phases: [],
1340
+ blockers: [],
1341
+ warnings: [],
1342
+ passed: 0,
1343
+ skipped: 0,
1344
+ errors: 0,
1345
+ };
1346
+
1347
+ const phaseRunners = {
1348
+ build: runBuildPhase,
1349
+ env: runEnvPhase,
1350
+ routes: runRoutesPhase,
1351
+ auth: runAuthPhase,
1352
+ integrations: runIntegrationsPhase,
1353
+ security: runSecurityPhase,
1354
+ };
1355
+
1356
+ // Determine which phases to run
1357
+ let phasesToRun = [...PHASE_ORDER];
1358
+ if (opts.onlyPhases.length > 0) {
1359
+ phasesToRun = opts.onlyPhases.filter(p => PHASE_ORDER.includes(p));
1360
+ }
1361
+ phasesToRun = phasesToRun.filter(p => !opts.skipPhases.includes(p));
1362
+
1363
+ const totalPhases = phasesToRun.length;
1364
+
1365
+ for (let i = 0; i < phasesToRun.length; i++) {
1366
+ const phaseName = phasesToRun[i];
1367
+ const phaseConfig = PHASES[phaseName];
1368
+ const runner = phaseRunners[phaseName];
1369
+
1370
+ if (!opts.ci && !opts.quiet) {
1371
+ printPhaseStart(i + 1, totalPhases, phaseConfig);
1372
+ }
1373
+
1374
+ const start = Date.now();
1375
+
1376
+ try {
1377
+ const phaseResult = await runner(ctx, opts);
1378
+ phaseResult.duration = Date.now() - start;
1379
+
1380
+ // Aggregate results
1381
+ for (const check of phaseResult.checks) {
1382
+ if (check.status === "pass") {
1383
+ results.passed++;
1384
+ } else if (check.status === "skip") {
1385
+ results.skipped++;
1386
+ } else if (check.status === "error") {
1387
+ results.errors++;
1388
+ results.warnings.push({ phase: phaseName, ...check });
1389
+ } else if (check.status === "fail") {
1390
+ if (check.severity === "BLOCK") {
1391
+ results.blockers.push({ phase: phaseName, ...check });
1392
+ } else {
1393
+ results.warnings.push({ phase: phaseName, ...check });
1394
+ }
1395
+ }
1396
+
1397
+ if (!opts.ci && !opts.quiet) {
1398
+ printCheckResult(check, opts.verbose);
1399
+ }
1400
+ }
1401
+
1402
+ results.phases.push(phaseResult);
1403
+
1404
+ // Fail fast
1405
+ if (opts.failFast && results.blockers.length > 0) {
1406
+ if (!opts.ci && !opts.quiet) {
1407
+ console.log(`\n ${c.yellow}${sym.warning} Stopping early (--fail-fast)${c.reset}\n`);
1408
+ }
1409
+ break;
1410
+ }
1411
+ } catch (err) {
1412
+ results.errors++;
1413
+ results.phases.push({
1414
+ phase: phaseName,
1415
+ duration: Date.now() - start,
1416
+ checks: [{
1417
+ name: "phase-error",
1418
+ description: `Phase ${phaseName} failed`,
1419
+ status: "error",
1420
+ error: err.message,
1421
+ }],
1422
+ });
1423
+
1424
+ if (!opts.ci && !opts.quiet) {
1425
+ console.log(` ${c.red}${sym.cross} Phase failed: ${err.message}${c.reset}`);
1426
+ }
1427
+ }
1428
+ }
1429
+
1430
+ // Apply strict mode
1431
+ if (opts.strict) {
1432
+ results.blockers.push(...results.warnings);
1433
+ results.warnings = [];
1434
+ }
1435
+
1436
+ return results;
1437
+ }
1438
+
1439
+ // ═══════════════════════════════════════════════════════════════════════════════
1440
+ // OUTPUT RENDERING
1441
+ // ═══════════════════════════════════════════════════════════════════════════════
1442
+
1443
+ function printPhaseStart(num, total, phase) {
1444
+ const header = `PHASE ${num}: ${phase.name.toUpperCase()}`;
1445
+ const suffix = `[${num}/${total}] ${phase.estimatedTime}`;
1446
+ const padding = Math.max(0, 77 - header.length - suffix.length - 4);
1447
+
1448
+ console.log();
1449
+ console.log(`${c.cyan}${sym.box.topLeft}${"═".repeat(77)}${sym.box.topRight}${c.reset}`);
1450
+ console.log(`${c.cyan}${sym.box.vertical}${c.reset} ${c.bold}${header}${c.reset}${" ".repeat(padding)}${c.dim}${suffix}${c.reset} ${c.cyan}${sym.box.vertical}${c.reset}`);
1451
+ console.log(`${c.cyan}${sym.box.teeRight}${"═".repeat(77)}${sym.box.teeLeft}${c.reset}`);
1452
+ console.log(`${c.cyan}${sym.box.vertical}${c.reset}`);
1453
+ console.log(`${c.cyan}${sym.box.vertical}${c.reset} ${c.dim}${phase.description}${c.reset}`);
1454
+ console.log(`${c.cyan}${sym.box.vertical}${c.reset}`);
1455
+ }
1456
+
1457
+ function printCheckResult(check, verbose = false) {
1458
+ let icon, color;
1459
+ switch (check.status) {
1460
+ case "pass":
1461
+ icon = sym.check;
1462
+ color = c.green;
1463
+ break;
1464
+ case "fail":
1465
+ case "error":
1466
+ icon = check.severity === "BLOCK" ? sym.cross : sym.warning;
1467
+ color = check.severity === "BLOCK" ? c.red : c.yellow;
1468
+ break;
1469
+ case "skip":
1470
+ icon = sym.arrow;
1471
+ color = c.gray;
1472
+ break;
1473
+ default:
1474
+ icon = "?";
1475
+ color = c.gray;
1476
+ }
1477
+
1478
+ const desc = check.description.substring(0, 50);
1479
+ const status = check.status.toUpperCase().padEnd(5);
1480
+ const padding = Math.max(0, 55 - desc.length);
1481
+
1482
+ console.log(`${c.cyan}${sym.box.vertical}${c.reset} ${color}${icon}${c.reset} ${desc}${" ".repeat(padding)}${color}${status}${c.reset}`);
1483
+
1484
+ if (check.error && check.status !== "pass") {
1485
+ console.log(`${c.cyan}${sym.box.vertical}${c.reset} ${c.dim}${sym.arrow} ${check.error}${c.reset}`);
1486
+ }
1487
+
1488
+ if (verbose && check.details) {
1489
+ console.log(`${c.cyan}${sym.box.vertical}${c.reset} ${c.dim}${check.details}${c.reset}`);
1490
+ }
1491
+
1492
+ if (verbose && check.findings && check.findings.length > 0) {
1493
+ for (const finding of check.findings.slice(0, 3)) {
1494
+ const loc = finding.file ? path.relative(process.cwd(), finding.file) : "";
1495
+ console.log(`${c.cyan}${sym.box.vertical}${c.reset} ${c.dim}📍 ${loc}:${finding.line || "?"}${c.reset}`);
1496
+ }
1497
+ }
1498
+ }
1499
+
1500
+ function printBlockersSection(results) {
1501
+ const blockerCount = results.blockers.length;
1502
+ const warningCount = results.warnings.length;
1503
+
1504
+ console.log();
1505
+ console.log(`${c.cyan}${sym.box.topLeft}${"═".repeat(77)}${sym.box.topRight}${c.reset}`);
1506
+ console.log(`${c.cyan}${sym.box.vertical}${c.reset} ${c.cyan}${sym.box.vertical}${c.reset}`);
1507
+
1508
+ const blockerText = `${sym.block} LAUNCH BLOCKERS`;
1509
+ const blockerSuffix = `${blockerCount} found`;
1510
+ const blockerPadding = Math.max(0, 77 - blockerText.length - blockerSuffix.length - 10);
1511
+
1512
+ console.log(`${c.cyan}${sym.box.vertical}${c.reset} ${blockerText}${" ".repeat(blockerPadding)}${blockerCount > 0 ? c.red : c.green}${blockerSuffix}${c.reset} ${c.cyan}${sym.box.vertical}${c.reset}`);
1513
+ console.log(`${c.cyan}${sym.box.vertical}${c.reset} ${c.cyan}${sym.box.vertical}${c.reset}`);
1514
+ console.log(`${c.cyan}${sym.box.teeRight}${"═".repeat(77)}${sym.box.teeLeft}${c.reset}`);
1515
+
1516
+ if (blockerCount === 0) {
1517
+ console.log(`${c.cyan}${sym.box.vertical}${c.reset} ${c.cyan}${sym.box.vertical}${c.reset}`);
1518
+ console.log(`${c.cyan}${sym.box.vertical}${c.reset} ${c.green}${sym.check} No blockers! You're ready to launch.${c.reset} ${c.cyan}${sym.box.vertical}${c.reset}`);
1519
+ console.log(`${c.cyan}${sym.box.vertical}${c.reset} ${c.cyan}${sym.box.vertical}${c.reset}`);
1520
+ } else {
1521
+ console.log(`${c.cyan}${sym.box.vertical}${c.reset} ${c.cyan}${sym.box.vertical}${c.reset}`);
1522
+ results.blockers.forEach((blocker, i) => {
1523
+ const phaseLabel = `[${blocker.phase.toUpperCase()}]`;
1524
+ const desc = (blocker.description || blocker.name).substring(0, 50);
1525
+ console.log(`${c.cyan}${sym.box.vertical}${c.reset} ${c.bold}${i + 1}.${c.reset} ${c.red}${phaseLabel}${c.reset} ${desc}`);
1526
+ if (blocker.error) {
1527
+ console.log(`${c.cyan}${sym.box.vertical}${c.reset} ${c.dim}${sym.arrow} ${blocker.error.substring(0, 60)}${c.reset}`);
1528
+ }
1529
+ if (blocker.fix) {
1530
+ console.log(`${c.cyan}${sym.box.vertical}${c.reset} ${c.cyan}Fix: ${blocker.fix.substring(0, 55)}${c.reset}`);
1531
+ }
1532
+ console.log(`${c.cyan}${sym.box.vertical}${c.reset}`);
1533
+ });
1534
+ }
1535
+
1536
+ // Warnings section
1537
+ if (warningCount > 0) {
1538
+ console.log(`${c.cyan}${sym.box.teeRight}${"═".repeat(77)}${sym.box.teeLeft}${c.reset}`);
1539
+ console.log(`${c.cyan}${sym.box.vertical}${c.reset} ${c.cyan}${sym.box.vertical}${c.reset}`);
1540
+
1541
+ const warnText = `${sym.warning} WARNINGS (non-blocking)`;
1542
+ const warnSuffix = `${warningCount} found`;
1543
+ const warnPadding = Math.max(0, 77 - warnText.length - warnSuffix.length - 10);
1544
+
1545
+ console.log(`${c.cyan}${sym.box.vertical}${c.reset} ${c.yellow}${warnText}${c.reset}${" ".repeat(warnPadding)}${c.yellow}${warnSuffix}${c.reset} ${c.cyan}${sym.box.vertical}${c.reset}`);
1546
+ console.log(`${c.cyan}${sym.box.vertical}${c.reset} ${c.cyan}${sym.box.vertical}${c.reset}`);
1547
+ results.warnings.slice(0, 10).forEach(warning => {
1548
+ const desc = (warning.description || warning.name).substring(0, 65);
1549
+ console.log(`${c.cyan}${sym.box.vertical}${c.reset} ${c.yellow}•${c.reset} ${desc}`);
1550
+ });
1551
+ if (warningCount > 10) {
1552
+ console.log(`${c.cyan}${sym.box.vertical}${c.reset} ${c.dim}... and ${warningCount - 10} more${c.reset}`);
1553
+ }
1554
+ console.log(`${c.cyan}${sym.box.vertical}${c.reset} ${c.cyan}${sym.box.vertical}${c.reset}`);
1555
+ }
1556
+
1557
+ console.log(`${c.cyan}${sym.box.bottomLeft}${"═".repeat(77)}${sym.box.bottomRight}${c.reset}`);
1558
+ }
1559
+
1560
+ function printVerdict(results, duration) {
1561
+ const verdict = results.blockers.length === 0 ?
1562
+ (results.warnings.length === 0 ? "LAUNCH" : "WARN") : "BLOCK";
1563
+
1564
+ const total = results.passed + results.blockers.length + results.warnings.length + results.skipped;
1565
+ const score = total > 0 ? Math.max(0, Math.round(100 * (results.passed / total))) : 0;
1566
+
1567
+ let verdictIcon;
1568
+ switch (verdict) {
1569
+ case "LAUNCH":
1570
+ verdictIcon = sym.rocket;
1571
+ break;
1572
+ case "WARN":
1573
+ verdictIcon = sym.warning;
1574
+ break;
1575
+ case "BLOCK":
1576
+ verdictIcon = sym.block;
1577
+ break;
1578
+ }
1579
+
1580
+ const verdictText = verdict === "LAUNCH" ? "READY TO LAUNCH" : verdict;
1581
+
1582
+ console.log();
1583
+ console.log(`${c.cyan}${sym.box.topLeft}${"═".repeat(77)}${sym.box.topRight}${c.reset}`);
1584
+ console.log(`${c.cyan}${sym.box.vertical}${c.reset} ${c.cyan}${sym.box.vertical}${c.reset}`);
1585
+ console.log(`${c.cyan}${sym.box.vertical}${c.reset} ${verdictIcon} ${c.bold}VERDICT: ${verdictText}${c.reset} ${c.cyan}${sym.box.vertical}${c.reset}`);
1586
+ console.log(`${c.cyan}${sym.box.vertical}${c.reset} ${c.cyan}${sym.box.vertical}${c.reset}`);
1587
+ console.log(`${c.cyan}${sym.box.vertical}${c.reset} ${c.dim}┌─────────────────────────────────────────────────────────────────────┐${c.reset} ${c.cyan}${sym.box.vertical}${c.reset}`);
1588
+ console.log(`${c.cyan}${sym.box.vertical}${c.reset} ${c.dim}│${c.reset} Score: ${score}/100 ${c.dim}│${c.reset} ${c.cyan}${sym.box.vertical}${c.reset}`);
1589
+ console.log(`${c.cyan}${sym.box.vertical}${c.reset} ${c.dim}│${c.reset} Blockers: ${results.blockers.length} Warnings: ${results.warnings.length} Passed: ${results.passed} ${c.dim}│${c.reset} ${c.cyan}${sym.box.vertical}${c.reset}`);
1590
+ console.log(`${c.cyan}${sym.box.vertical}${c.reset} ${c.dim}│${c.reset} Duration: ${formatDuration(duration)} ${c.dim}│${c.reset} ${c.cyan}${sym.box.vertical}${c.reset}`);
1591
+ console.log(`${c.cyan}${sym.box.vertical}${c.reset} ${c.dim}└─────────────────────────────────────────────────────────────────────┘${c.reset} ${c.cyan}${sym.box.vertical}${c.reset}`);
1592
+ console.log(`${c.cyan}${sym.box.vertical}${c.reset} ${c.cyan}${sym.box.vertical}${c.reset}`);
1593
+ console.log(`${c.cyan}${sym.box.bottomLeft}${"═".repeat(77)}${sym.box.bottomRight}${c.reset}`);
1594
+
1595
+ return { verdict, score };
1596
+ }
1597
+
1598
+ function formatDuration(ms) {
1599
+ if (ms < 1000) return `${ms}ms`;
1600
+ if (ms < 60000) return `${(ms / 1000).toFixed(1)}s`;
1601
+ const minutes = Math.floor(ms / 60000);
1602
+ const seconds = Math.floor((ms % 60000) / 1000);
1603
+ return `${minutes}m ${seconds}s`;
1604
+ }
1605
+
1606
+ // ═══════════════════════════════════════════════════════════════════════════════
1607
+ // CI OUTPUT
1608
+ // ═══════════════════════════════════════════════════════════════════════════════
1609
+
1610
+ function printCIOutput(results, duration, packPath) {
1611
+ const verdict = results.blockers.length === 0 ?
1612
+ (results.warnings.length === 0 ? "LAUNCH" : "WARN") : "BLOCK";
1613
+ const total = results.passed + results.blockers.length + results.warnings.length + results.skipped;
1614
+ const score = total > 0 ? Math.max(0, Math.round(100 * (results.passed / total))) : 0;
1615
+
1616
+ console.log(`VERDICT=${verdict}`);
1617
+ console.log(`SCORE=${score}`);
1618
+ console.log(`BLOCKERS=${results.blockers.length}`);
1619
+ console.log(`WARNINGS=${results.warnings.length}`);
1620
+ console.log(`PASSED=${results.passed}`);
1621
+ console.log(`SKIPPED=${results.skipped}`);
1622
+ console.log(`DURATION_MS=${duration}`);
1623
+ if (packPath) {
1624
+ console.log(`PACK_PATH=${packPath}`);
1625
+ }
1626
+ }
1627
+
1628
+ function buildJsonOutput(results, duration, ctx) {
1629
+ const verdict = results.blockers.length === 0 ?
1630
+ (results.warnings.length === 0 ? "LAUNCH" : "WARN") : "BLOCK";
1631
+ const total = results.passed + results.blockers.length + results.warnings.length + results.skipped;
1632
+ const score = total > 0 ? Math.max(0, Math.round(100 * (results.passed / total))) : 0;
1633
+
1634
+ return {
1635
+ version: LAUNCH_VERSION,
1636
+ runId: `launch-${crypto.randomBytes(6).toString("hex")}`,
1637
+ timestamp: new Date().toISOString(),
1638
+ verdict,
1639
+ score,
1640
+ duration: {
1641
+ ms: duration,
1642
+ formatted: formatDuration(duration),
1643
+ },
1644
+ summary: {
1645
+ blockers: results.blockers.length,
1646
+ warnings: results.warnings.length,
1647
+ passed: results.passed,
1648
+ skipped: results.skipped,
1649
+ errors: results.errors,
1650
+ },
1651
+ phases: results.phases.map(p => ({
1652
+ phase: p.phase,
1653
+ duration: p.duration,
1654
+ checks: p.checks.map(ch => ({
1655
+ name: ch.name,
1656
+ description: ch.description,
1657
+ status: ch.status,
1658
+ severity: ch.severity,
1659
+ duration: ch.duration,
1660
+ error: ch.error,
1661
+ fix: ch.fix,
1662
+ })),
1663
+ })),
1664
+ blockers: results.blockers.map(b => ({
1665
+ phase: b.phase,
1666
+ name: b.name,
1667
+ description: b.description,
1668
+ error: b.error,
1669
+ fix: b.fix,
1670
+ findings: b.findings?.slice(0, 5),
1671
+ })),
1672
+ warnings: results.warnings.map(w => ({
1673
+ phase: w.phase,
1674
+ name: w.name,
1675
+ description: w.description,
1676
+ error: w.error,
1677
+ })),
1678
+ git: getGitInfo(ctx.projectRoot),
1679
+ environment: {
1680
+ nodeVersion: process.version,
1681
+ platform: process.platform,
1682
+ ci: process.env.CI === "true",
1683
+ nodeEnv: process.env.NODE_ENV || "development",
1684
+ },
1685
+ };
1686
+ }
1687
+
1688
+ // ═══════════════════════════════════════════════════════════════════════════════
1689
+ // ARTIFACT GENERATION
1690
+ // ═══════════════════════════════════════════════════════════════════════════════
1691
+
1692
+ async function generateLaunchPack(results, duration, ctx, opts) {
1693
+ const outputDir = opts.output || path.join(ctx.projectRoot, ".vibecheck", "launch");
1694
+
1695
+ fs.mkdirSync(outputDir, { recursive: true });
1696
+
1697
+ const timestamp = new Date().toISOString().replace(/[:.]/g, "-").slice(0, 19);
1698
+ const packId = `launch-${timestamp}`;
1699
+
1700
+ // Full results
1701
+ const fullResults = buildJsonOutput(results, duration, ctx);
1702
+ const fullPath = path.join(outputDir, `${packId}.json`);
1703
+ fs.writeFileSync(fullPath, JSON.stringify(fullResults, null, 2));
1704
+
1705
+ // Blockers only
1706
+ const blockersPath = path.join(outputDir, "blockers.json");
1707
+ fs.writeFileSync(blockersPath, JSON.stringify(results.blockers, null, 2));
1708
+
1709
+ // Human-readable checklist
1710
+ const checklistPath = path.join(outputDir, "checklist.md");
1711
+ fs.writeFileSync(checklistPath, generateChecklist(results, fullResults));
1712
+
1713
+ // Audit trail
1714
+ const auditPath = path.join(outputDir, "audit-trail.json");
1715
+ fs.writeFileSync(auditPath, JSON.stringify(generateAuditTrail(results, fullResults, ctx), null, 2));
1716
+
1717
+ // Manifest
1718
+ const manifestPath = path.join(outputDir, "manifest.json");
1719
+ fs.writeFileSync(manifestPath, JSON.stringify({
1720
+ id: packId,
1721
+ version: LAUNCH_VERSION,
1722
+ generated: new Date().toISOString(),
1723
+ verdict: fullResults.verdict,
1724
+ score: fullResults.score,
1725
+ files: [
1726
+ { name: `${packId}.json`, type: "results" },
1727
+ { name: "blockers.json", type: "blockers" },
1728
+ { name: "checklist.md", type: "checklist" },
1729
+ { name: "audit-trail.json", type: "audit" },
1730
+ ],
1731
+ }, null, 2));
1732
+
1733
+ return { packId, outputDir, fullPath };
1734
+ }
1735
+
1736
+ function generateChecklist(results, fullResults) {
1737
+ let md = `# Launch Checklist\n\n`;
1738
+ md += `**Generated:** ${new Date().toISOString()}\n`;
1739
+ md += `**Verdict:** ${fullResults.verdict}\n`;
1740
+ md += `**Score:** ${fullResults.score}/100\n`;
1741
+
1742
+ if (fullResults.git) {
1743
+ md += `**Git:** ${fullResults.git.branch}@${fullResults.git.commit}${fullResults.git.dirty ? " (dirty)" : ""}\n`;
1744
+ }
1745
+ md += `\n`;
1746
+
1747
+ md += `## Summary\n\n`;
1748
+ md += `| Metric | Count |\n`;
1749
+ md += `|--------|-------|\n`;
1750
+ md += `| Blockers | ${results.blockers.length} |\n`;
1751
+ md += `| Warnings | ${results.warnings.length} |\n`;
1752
+ md += `| Passed | ${results.passed} |\n`;
1753
+ md += `| Skipped | ${results.skipped} |\n`;
1754
+ md += `\n`;
1755
+
1756
+ if (results.blockers.length > 0) {
1757
+ md += `## 🚫 Blockers\n\n`;
1758
+ results.blockers.forEach((b, i) => {
1759
+ md += `### ${i + 1}. [${b.phase.toUpperCase()}] ${b.description || b.name}\n\n`;
1760
+ if (b.error) md += `**Error:** ${b.error}\n\n`;
1761
+ if (b.fix) md += `**Fix:** ${b.fix}\n\n`;
1762
+ if (b.findings && b.findings.length > 0) {
1763
+ md += `**Locations:**\n`;
1764
+ for (const f of b.findings.slice(0, 5)) {
1765
+ if (f.file) {
1766
+ md += `- \`${path.relative(process.cwd(), f.file)}:${f.line || "?"}\`\n`;
1767
+ }
1768
+ }
1769
+ md += `\n`;
1770
+ }
1771
+ });
1772
+ }
1773
+
1774
+ if (results.warnings.length > 0) {
1775
+ md += `## ⚠️ Warnings\n\n`;
1776
+ results.warnings.forEach((w, i) => {
1777
+ md += `${i + 1}. **[${w.phase.toUpperCase()}]** ${w.description || w.name}`;
1778
+ if (w.error) md += ` - ${w.error}`;
1779
+ md += `\n`;
1780
+ });
1781
+ md += `\n`;
1782
+ }
1783
+
1784
+ md += `## All Checks\n\n`;
1785
+ for (const phase of results.phases) {
1786
+ const phaseConfig = PHASES[phase.phase];
1787
+ md += `### ${phaseConfig?.icon || "📋"} ${phaseConfig?.name || phase.phase}\n\n`;
1788
+
1789
+ for (const check of phase.checks) {
1790
+ const icon = check.status === "pass" ? "✅" :
1791
+ check.status === "skip" ? "⏭️" :
1792
+ check.severity === "BLOCK" ? "❌" : "⚠️";
1793
+ md += `- ${icon} ${check.description || check.name}`;
1794
+ if (check.status !== "pass" && check.error) {
1795
+ md += ` *(${check.error})*`;
1796
+ }
1797
+ md += `\n`;
1798
+ }
1799
+ md += `\n`;
1800
+ }
1801
+
1802
+ return md;
1803
+ }
1804
+
1805
+ function generateAuditTrail(results, fullResults, ctx) {
1806
+ return {
1807
+ auditVersion: "1.0.0",
1808
+ runId: fullResults.runId,
1809
+ timestamp: fullResults.timestamp,
1810
+ operator: {
1811
+ userId: process.env.USER || process.env.USERNAME || "unknown",
1812
+ email: process.env.GIT_AUTHOR_EMAIL || null,
1813
+ method: process.env.CI ? "ci" : "cli",
1814
+ },
1815
+ repository: {
1816
+ path: ctx.projectRoot,
1817
+ ...fullResults.git,
1818
+ },
1819
+ environment: fullResults.environment,
1820
+ execution: {
1821
+ phases: results.phases.length,
1822
+ duration: fullResults.duration.ms,
1823
+ checks: results.phases.reduce((sum, p) => sum + p.checks.length, 0),
1824
+ },
1825
+ checks: results.phases.flatMap(phase =>
1826
+ phase.checks.map(check => ({
1827
+ phase: phase.phase,
1828
+ check: check.name,
1829
+ description: check.description,
1830
+ status: check.status,
1831
+ severity: check.severity,
1832
+ duration: check.duration,
1833
+ error: check.error || null,
1834
+ }))
1835
+ ),
1836
+ verdict: fullResults.verdict,
1837
+ score: fullResults.score,
1838
+ attestation: {
1839
+ hash: crypto.createHash("sha256")
1840
+ .update(JSON.stringify({ verdict: fullResults.verdict, score: fullResults.score, timestamp: fullResults.timestamp }))
1841
+ .digest("hex"),
1842
+ signature: null, // Could be signed with private key
1843
+ },
1844
+ };
1845
+ }
1846
+
1847
+ // ═══════════════════════════════════════════════════════════════════════════════
1848
+ // INTERACTIVE PROMPT
1849
+ // ═══════════════════════════════════════════════════════════════════════════════
1850
+
1851
+ async function promptToContinue() {
1852
+ return new Promise((resolve) => {
1853
+ const rl = readline.createInterface({
1854
+ input: process.stdin,
1855
+ output: process.stdout,
1856
+ });
1857
+
1858
+ rl.question(`\n ${c.dim}Press ENTER to start, or Ctrl+C to cancel...${c.reset}`, () => {
1859
+ rl.close();
1860
+ resolve(true);
1861
+ });
1862
+
1863
+ // Handle Ctrl+C gracefully
1864
+ rl.on("close", () => resolve(false));
1865
+ });
1866
+ }
1867
+
1868
+ // ═══════════════════════════════════════════════════════════════════════════════
1869
+ // MAIN ENTRY POINT
1870
+ // ═══════════════════════════════════════════════════════════════════════════════
1871
+
1872
+ async function runLaunch(args, context = {}) {
1873
+ const { EXIT } = getExitCodes();
1874
+ const opts = parseArgs(args);
1875
+
1876
+ // Help
1877
+ if (opts.help) {
1878
+ printHelp();
1879
+ return EXIT.SUCCESS;
1880
+ }
1881
+
1882
+ // Validate project path
1883
+ if (!fs.existsSync(opts.path)) {
1884
+ console.error(`${c.red}Error: Project path does not exist: ${opts.path}${c.reset}`);
1885
+ return EXIT.USER_ERROR;
1886
+ }
1887
+
1888
+ // Context
1889
+ const ctx = {
1890
+ projectRoot: opts.path,
1891
+ runId: context.runId || `launch-${Date.now()}`,
1892
+ startTime: Date.now(),
1893
+ isProduction: process.env.NODE_ENV === "production",
1894
+ };
1895
+
1896
+ // Dry run - show what would be checked
1897
+ if (opts.dryRun) {
1898
+ console.log(`\n${c.bold}Dry run - would check:${c.reset}\n`);
1899
+ let phasesToRun = [...PHASE_ORDER];
1900
+ if (opts.onlyPhases.length > 0) {
1901
+ phasesToRun = opts.onlyPhases.filter(p => PHASE_ORDER.includes(p));
1902
+ }
1903
+ phasesToRun = phasesToRun.filter(p => !opts.skipPhases.includes(p));
1904
+
1905
+ for (const phase of phasesToRun) {
1906
+ const config = PHASES[phase];
1907
+ console.log(` ${config.icon} ${config.name}`);
1908
+ console.log(` ${c.dim}${config.description}${c.reset}`);
1909
+ }
1910
+ console.log();
1911
+ return EXIT.SUCCESS;
1912
+ }
1913
+
1914
+ // Banner (interactive mode only)
1915
+ if (!opts.ci && !opts.quiet && !opts.noBanner) {
1916
+ printUnifiedBanner(projectRoot, opts);
1917
+ const proceed = await promptToContinue();
1918
+ if (!proceed) {
1919
+ console.log(`\n ${c.dim}Cancelled.${c.reset}\n`);
1920
+ return EXIT.SUCCESS;
1921
+ }
1922
+ }
1923
+
1924
+ // Run all phases
1925
+ const startTime = Date.now();
1926
+ let results;
1927
+
1928
+ try {
1929
+ results = await runAllPhases(ctx, opts);
1930
+ } catch (err) {
1931
+ if (!opts.ci && !opts.quiet) {
1932
+ console.error(`\n${c.red}Fatal error: ${err.message}${c.reset}\n`);
1933
+ }
1934
+ if (opts.json) {
1935
+ console.log(JSON.stringify({ error: err.message, verdict: "ERROR" }));
1936
+ }
1937
+ return EXIT.INTERNAL_ERROR;
1938
+ }
1939
+
1940
+ const duration = Date.now() - startTime;
1941
+
1942
+ // Generate pack
1943
+ let pack = null;
1944
+ try {
1945
+ pack = await generateLaunchPack(results, duration, ctx, opts);
1946
+ } catch (err) {
1947
+ if (!opts.quiet) {
1948
+ console.error(`${c.yellow}Warning: Could not generate launch pack: ${err.message}${c.reset}`);
1949
+ }
1950
+ }
1951
+
1952
+ // Output
1953
+ if (opts.json) {
1954
+ console.log(JSON.stringify(buildJsonOutput(results, duration, ctx), null, 2));
1955
+ } else if (opts.ci) {
1956
+ printCIOutput(results, duration, pack?.fullPath);
1957
+ } else if (!opts.quiet) {
1958
+ printBlockersSection(results);
1959
+ const { verdict } = printVerdict(results, duration);
1960
+
1961
+ if (pack) {
1962
+ console.log();
1963
+ console.log(` ${c.dim}📦 Launch pack saved: ${pack.fullPath}${c.reset}`);
1964
+ }
1965
+ console.log();
1966
+
1967
+ if (verdict === "LAUNCH" || verdict === "WARN") {
1968
+ console.log(` ${c.bold}Next:${c.reset} Deploy with confidence! Your launch audit is ready.`);
1969
+ console.log(` Share: ${c.cyan}vibecheck packs bundle --launch${c.reset}`);
1970
+ } else {
1971
+ console.log(` ${c.bold}Next:${c.reset} Fix the blockers above before deploying.`);
1972
+ console.log(` Re-run: ${c.cyan}vibecheck launch${c.reset}`);
1973
+ }
1974
+ console.log();
1975
+ }
1976
+
1977
+ // Exit code
1978
+ if (results.blockers.length > 0) {
1979
+ return EXIT.BLOCKING;
1980
+ } else if (results.warnings.length > 0) {
1981
+ return EXIT.WARNINGS;
1982
+ }
1983
+ return EXIT.SUCCESS;
1984
+ }
1985
+
1986
+ // ═══════════════════════════════════════════════════════════════════════════════
1987
+ // EXPORTS
1988
+ // ═══════════════════════════════════════════════════════════════════════════════
1989
+
1990
+ module.exports = { runLaunch };
1991
+
1992
+ // Direct execution
1993
+ if (require.main === module) {
1994
+ runLaunch(process.argv.slice(2))
1995
+ .then(exitCode => process.exit(exitCode))
1996
+ .catch(err => {
1997
+ console.error(err);
1998
+ process.exit(10);
1999
+ });
2000
+ }