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,1128 @@
1
+ /**
2
+ * Enterprise Ship Output - V5 "Mission Control" Format
3
+ * Features:
4
+ * - Dynamic "SHIP" vs "NO SHIP" ASCII Art
5
+ * - Deployment Readiness Gauges
6
+ * - AI Hallucination Risk Assessment
7
+ * - Pixel-perfect 80-char alignment
8
+ */
9
+
10
+ const path = require('path');
11
+ const {
12
+ ansi,
13
+ colors,
14
+ box,
15
+ icons,
16
+ renderSection,
17
+ formatDuration,
18
+ truncate,
19
+ } = require('./terminal-ui');
20
+
21
+ // ANSI Color Helpers (direct for compatibility)
22
+ const ESC = '\x1b';
23
+ const chalk = {
24
+ reset: `${ESC}[0m`,
25
+ bold: `${ESC}[1m`,
26
+ dim: `${ESC}[2m`,
27
+ red: `${ESC}[31m`,
28
+ green: `${ESC}[32m`,
29
+ yellow: `${ESC}[33m`,
30
+ cyan: `${ESC}[36m`,
31
+ magenta: `${ESC}[35m`,
32
+ white: `${ESC}[37m`,
33
+ gray: `${ESC}[90m`,
34
+ bgRed: `${ESC}[41m`,
35
+ bgGreen: `${ESC}[42m`,
36
+ bgYellow: `${ESC}[43m`,
37
+ };
38
+
39
+ // ═══════════════════════════════════════════════════════════════════════════════
40
+ // CONFIGURATION
41
+ // ═══════════════════════════════════════════════════════════════════════════════
42
+
43
+ const WIDTH = 80;
44
+
45
+ const BOX_SHIP = {
46
+ topLeft: '╔', topRight: '╗', bottomLeft: '╚', bottomRight: '╝',
47
+ horizontal: '═', vertical: '║',
48
+ teeRight: '╠', teeLeft: '╣', teeTop: '╤', teeBottom: '╧',
49
+ cross: '╪',
50
+ lightH: '─', lightV: '│',
51
+ lightTeeLeft: '├', lightTeeRight: '┤', lightCross: '┼'
52
+ };
53
+
54
+ // EXTERNAL HEADER
55
+ const LOGO_VIBECHECK = `
56
+ ██╗ ██╗██╗██████╗ ███████╗ ██████╗██╗ ██╗███████╗ ██████╗██╗ ██╗
57
+ ██║ ██║██║██╔══██╗██╔════╝██╔════╝██║ ██║██╔════╝██╔════╝██║ ██╔╝
58
+ ██║ ██║██║██████╔╝█████╗ ██║ ███████║█████╗ ██║ █████╔╝
59
+ ╚██╗ ██╔╝██║██╔══██╗██╔══╝ ██║ ██╔══██║██╔══╝ ██║ ██╔═██╗
60
+ ╚████╔╝ ██║██████╔╝███████╗╚██████╗██║ ██║███████╗╚██████╗██║ ██╗
61
+ ╚═══╝ ╚═╝╚═════╝ ╚══════╝ ╚═════╝╚═╝ ╚═╝╚══════╝ ╚═════╝╚═╝ ╚═╝
62
+ `;
63
+
64
+ // HERO: SHIP (Green)
65
+ const LOGO_SHIP = `
66
+ ███████╗██╗ ██╗██╗██████╗
67
+ ██╔════╝██║ ██║██║██╔══██╗
68
+ ███████╗███████║██║██████╔╝
69
+ ╚════██║██╔══██║██║██╔═══╝
70
+ ███████║██║ ██║██║██║
71
+ ╚══════╝╚═╝ ╚═╝╚═╝╚═╝
72
+ `;
73
+
74
+ // HERO: NO SHIP (Red)
75
+ const LOGO_NOSHIP = `
76
+ ███╗ ██╗ ██████╗ ███████╗██╗ ██╗██╗██████╗
77
+ ████╗ ██║██╔═══██╗ ██╔════╝██║ ██║██║██╔══██╗
78
+ ██╔██╗ ██║██║ ██║ ███████╗███████║██║██████╔╝
79
+ ██║╚██╗██║██║ ██║ ╚════██║██╔══██║██║██╔═══╝
80
+ ██║ ╚████║╚██████╔╝ ███████║██║ ██║██║██║
81
+ ╚═╝ ╚═══╝ ╚═════╝ ╚══════╝╚═╝ ╚═╝╚═╝╚═╝
82
+ `;
83
+
84
+ // ═══════════════════════════════════════════════════════════════════════════════
85
+ // UTILITIES
86
+ // ═══════════════════════════════════════════════════════════════════════════════
87
+
88
+ function stripAnsi(str) {
89
+ return str.replace(/\x1b\[[0-9;]*m/g, '');
90
+ }
91
+
92
+ function padCenter(str, width) {
93
+ const visibleLen = stripAnsi(str).length;
94
+ const padding = Math.max(0, width - visibleLen);
95
+ const left = Math.floor(padding / 2);
96
+ const right = padding - left;
97
+ return ' '.repeat(left) + str + ' '.repeat(right);
98
+ }
99
+
100
+ function padRight(str, len) {
101
+ const visibleLen = stripAnsi(str).length;
102
+ const truncated = visibleLen > len ? str.substring(0, len - 3) + '...' : str;
103
+ return truncated + ' '.repeat(Math.max(0, len - visibleLen));
104
+ }
105
+
106
+ function renderHealthBar(percentage, width = 20, colorOverride = null) {
107
+ const fillChar = '█';
108
+ const fadeChars = ['▓', '▒', '░'];
109
+ const emptyChar = '░';
110
+
111
+ const fillAmount = Math.floor((percentage / 100) * width);
112
+
113
+ let barColor = colorOverride;
114
+ if (!barColor) {
115
+ barColor = percentage > 80 ? chalk.green : percentage > 50 ? chalk.yellow : chalk.red;
116
+ }
117
+
118
+ let bar = barColor + fillChar.repeat(fillAmount);
119
+
120
+ const remainder = ((percentage / 100) * width) - fillAmount;
121
+ if (fillAmount < width) {
122
+ if (remainder > 0.66) bar += fadeChars[0];
123
+ else if (remainder > 0.33) bar += fadeChars[1];
124
+ else bar += chalk.gray + emptyChar;
125
+ }
126
+
127
+ const currentVisible = fillAmount + (fillAmount < width ? 1 : 0);
128
+ const emptySpace = Math.max(0, width - currentVisible);
129
+ bar += chalk.gray + emptyChar.repeat(emptySpace) + chalk.reset;
130
+
131
+ return bar;
132
+ }
133
+
134
+ // ═══════════════════════════════════════════════════════════════════════════════
135
+ // SHIP-SPECIFIC ICONS
136
+ // ═══════════════════════════════════════════════════════════════════════════════
137
+
138
+ const shipIcons = {
139
+ ship: '🚀',
140
+ sparkle: '✨',
141
+ fire: '🔥',
142
+ lock: '🔐',
143
+ key: '🔑',
144
+ link: '🔗',
145
+ graph: '📊',
146
+ map: '🗺️',
147
+ doc: '📄',
148
+ folder: '📁',
149
+ clock: '⏱',
150
+ target: '🎯',
151
+ shield: '🛡️',
152
+ bug: '🐛',
153
+ wrench: '🔧',
154
+ lightning: '⚡',
155
+ package: '📦',
156
+ route: '🛤️',
157
+ env: '🌍',
158
+ auth: '🔒',
159
+ money: '💰',
160
+ ghost: '👻',
161
+ dead: '💀',
162
+ };
163
+
164
+ // ═══════════════════════════════════════════════════════════════════════════════
165
+ // VERDICT CONFIGURATION
166
+ // ═══════════════════════════════════════════════════════════════════════════════
167
+
168
+ function getVerdictConfig(verdict, score, blockers = 0, warnings = 0) {
169
+ if (verdict === 'SHIP' || (score >= 90 && blockers === 0)) {
170
+ return {
171
+ verdict: 'SHIP',
172
+ icon: '🚀',
173
+ headline: 'CLEAR TO SHIP',
174
+ tagline: 'Your app is production ready!',
175
+ color: colors.success,
176
+ borderColor: ansi.rgb(0, 200, 120),
177
+ bgColor: ansi.bgRgb(0, 60, 40),
178
+ };
179
+ }
180
+
181
+ if (verdict === 'WARN' || (score >= 50 && blockers <= 2)) {
182
+ return {
183
+ verdict: 'WARN',
184
+ icon: '⚠️',
185
+ headline: 'REVIEW BEFORE SHIP',
186
+ tagline: `${warnings} warning${warnings !== 1 ? 's' : ''} to address`,
187
+ color: colors.warning,
188
+ borderColor: ansi.rgb(200, 160, 0),
189
+ bgColor: ansi.bgRgb(60, 50, 0),
190
+ };
191
+ }
192
+
193
+ return {
194
+ verdict: 'BLOCK',
195
+ icon: '🛑',
196
+ headline: 'NOT SHIP READY',
197
+ tagline: `${blockers} blocker${blockers !== 1 ? 's' : ''} must be fixed`,
198
+ color: colors.error,
199
+ borderColor: ansi.rgb(200, 60, 60),
200
+ bgColor: ansi.bgRgb(60, 20, 20),
201
+ };
202
+ }
203
+
204
+ // ═══════════════════════════════════════════════════════════════════════════════
205
+ // VERDICT CARD - THE HERO MOMENT
206
+ // ═══════════════════════════════════════════════════════════════════════════════
207
+
208
+ function renderVerdictCard(options = {}) {
209
+ const {
210
+ verdict = 'WARN',
211
+ score = 0,
212
+ blockers = 0,
213
+ warnings = 0,
214
+ duration = 0,
215
+ cached = false,
216
+ } = options;
217
+
218
+ const config = getVerdictConfig(verdict, score, blockers, warnings);
219
+ const w = 68; // Inner width
220
+
221
+ const lines = [];
222
+ lines.push('');
223
+ lines.push('');
224
+
225
+ // Top border
226
+ lines.push(` ${config.borderColor}${box.doubleTopLeft}${box.doubleHorizontal.repeat(w)}${box.doubleTopRight}${ansi.reset}`);
227
+
228
+ // Empty line
229
+ lines.push(` ${config.borderColor}${box.doubleVertical}${ansi.reset}${' '.repeat(w)}${config.borderColor}${box.doubleVertical}${ansi.reset}`);
230
+
231
+ // Verdict icon and headline
232
+ const headlineText = `${config.icon} ${config.headline}`;
233
+ const headlinePadded = padCenter(headlineText, w);
234
+ lines.push(` ${config.borderColor}${box.doubleVertical}${ansi.reset}${config.color}${ansi.bold}${headlinePadded}${ansi.reset}${config.borderColor}${box.doubleVertical}${ansi.reset}`);
235
+
236
+ // Tagline
237
+ const taglinePadded = padCenter(config.tagline, w);
238
+ lines.push(` ${config.borderColor}${box.doubleVertical}${ansi.reset}${ansi.dim}${taglinePadded}${ansi.reset}${config.borderColor}${box.doubleVertical}${ansi.reset}`);
239
+
240
+ // Empty line
241
+ lines.push(` ${config.borderColor}${box.doubleVertical}${ansi.reset}${' '.repeat(w)}${config.borderColor}${box.doubleVertical}${ansi.reset}`);
242
+
243
+ // Score bar
244
+ const scoreBar = renderProgressBar(score, 35, config.color);
245
+ const scoreLabel = `VIBE SCORE`;
246
+ const scoreValue = `${score}/100`;
247
+ const scoreLine = ` ${scoreLabel} ${scoreBar} ${config.color}${ansi.bold}${scoreValue}${ansi.reset}`;
248
+ const scoreLinePadRight = ' '.repeat(Math.max(0, w - stripAnsi(scoreLine).length + 4));
249
+ lines.push(` ${config.borderColor}${box.doubleVertical}${ansi.reset}${scoreLine}${scoreLinePadRight}${config.borderColor}${box.doubleVertical}${ansi.reset}`);
250
+
251
+ // Empty line
252
+ lines.push(` ${config.borderColor}${box.doubleVertical}${ansi.reset}${' '.repeat(w)}${config.borderColor}${box.doubleVertical}${ansi.reset}`);
253
+
254
+ // Stats row
255
+ const blockerColor = blockers > 0 ? colors.error : colors.success;
256
+ const warningColor = warnings > 0 ? colors.warning : colors.success;
257
+ const statsLine = ` ${ansi.dim}Blockers:${ansi.reset} ${blockerColor}${ansi.bold}${blockers}${ansi.reset} ${ansi.dim}Warnings:${ansi.reset} ${warningColor}${ansi.bold}${warnings}${ansi.reset} ${ansi.dim}Duration:${ansi.reset} ${colors.accent}${ansi.bold}${formatDuration(duration)}${ansi.reset}${cached ? ` ${ansi.dim}(cached)${ansi.reset}` : ''}`;
258
+ const statsLinePadRight = ' '.repeat(Math.max(0, w - stripAnsi(statsLine).length + 4));
259
+ lines.push(` ${config.borderColor}${box.doubleVertical}${ansi.reset}${statsLine}${statsLinePadRight}${config.borderColor}${box.doubleVertical}${ansi.reset}`);
260
+
261
+ // Empty line
262
+ lines.push(` ${config.borderColor}${box.doubleVertical}${ansi.reset}${' '.repeat(w)}${config.borderColor}${box.doubleVertical}${ansi.reset}`);
263
+
264
+ // Bottom border
265
+ lines.push(` ${config.borderColor}${box.doubleBottomLeft}${box.doubleHorizontal.repeat(w)}${box.doubleBottomRight}${ansi.reset}`);
266
+
267
+ lines.push('');
268
+ return lines.join('\n');
269
+ }
270
+
271
+ // ═══════════════════════════════════════════════════════════════════════════════
272
+ // PROOF GRAPH VISUALIZATION
273
+ // ═══════════════════════════════════════════════════════════════════════════════
274
+
275
+ function renderProofGraph(proofGraph) {
276
+ if (!proofGraph || !proofGraph.summary) return '';
277
+
278
+ const lines = [];
279
+ lines.push(renderSection('PROOF GRAPH', shipIcons.graph));
280
+ lines.push('');
281
+
282
+ const { summary } = proofGraph;
283
+
284
+ // Confidence gauge
285
+ const confidence = Math.round((summary.confidence || 0) * 100);
286
+ const confColor = confidence >= 80 ? colors.success : confidence >= 50 ? colors.warning : colors.error;
287
+
288
+ lines.push(` ${ansi.bold}Analysis Confidence${ansi.reset}`);
289
+ lines.push(` ${renderProgressBar(confidence, 40, confColor)} ${confColor}${ansi.bold}${confidence}%${ansi.reset}`);
290
+ lines.push('');
291
+
292
+ // Claims summary
293
+ const verified = summary.verifiedClaims || 0;
294
+ const failed = summary.failedClaims || 0;
295
+ const total = summary.totalClaims || 0;
296
+
297
+ lines.push(` ${ansi.dim}Claims:${ansi.reset} ${colors.success}${verified}${ansi.reset} verified ${ansi.dim}/${ansi.reset} ${colors.error}${failed}${ansi.reset} failed ${ansi.dim}of${ansi.reset} ${total} total`);
298
+ lines.push(` ${ansi.dim}Gaps:${ansi.reset} ${summary.gaps || 0} identified`);
299
+ lines.push(` ${ansi.dim}Risk Score:${ansi.reset} ${summary.riskScore || 0}/100`);
300
+
301
+ // Top blockers
302
+ if (proofGraph.topBlockers && proofGraph.topBlockers.length > 0) {
303
+ lines.push('');
304
+ lines.push(` ${ansi.bold}Top Unverified Claims:${ansi.reset}`);
305
+ for (const blocker of proofGraph.topBlockers.slice(0, 3)) {
306
+ lines.push(` ${colors.error}${icons.error}${ansi.reset} ${truncate(blocker.assertion, 50)}`);
307
+ }
308
+ }
309
+
310
+ return lines.join('\n');
311
+ }
312
+
313
+ // ═══════════════════════════════════════════════════════════════════════════════
314
+ // ROUTE TRUTH MAP
315
+ // ═══════════════════════════════════════════════════════════════════════════════
316
+
317
+ function renderRouteTruthMap(truthpack) {
318
+ if (!truthpack) return '';
319
+
320
+ const lines = [];
321
+ lines.push(renderSection('ROUTE TRUTH MAP', shipIcons.map));
322
+ lines.push('');
323
+
324
+ const serverRoutes = truthpack.routes?.server || [];
325
+ const serverRouteCount = serverRoutes.length;
326
+ const clientRefs = truthpack.routes?.clientRefs?.length || 0;
327
+ const envVars = truthpack.env?.vars?.length || 0;
328
+ const envDeclared = truthpack.env?.declared?.length || 0;
329
+
330
+ // Framework detection output
331
+ const frameworkCounts = {};
332
+ for (const route of serverRoutes) {
333
+ const fw = route.framework || 'unknown';
334
+ frameworkCounts[fw] = (frameworkCounts[fw] || 0) + 1;
335
+ }
336
+
337
+ // Also include project-level detected frameworks
338
+ const detectedFrameworks = truthpack.project?.frameworks || [];
339
+ for (const fw of detectedFrameworks) {
340
+ if (!frameworkCounts[fw]) frameworkCounts[fw] = 0;
341
+ }
342
+
343
+ // Sort by count descending
344
+ const sortedFrameworks = Object.entries(frameworkCounts)
345
+ .filter(([fw]) => fw !== 'unknown')
346
+ .sort((a, b) => b[1] - a[1]);
347
+
348
+ if (sortedFrameworks.length > 0) {
349
+ const fwDisplay = sortedFrameworks
350
+ .slice(0, 5)
351
+ .map(([fw, count]) => `${fw} (${count})`)
352
+ .join(', ');
353
+ lines.push(` ${ansi.bold}Frameworks:${ansi.reset} ${colors.accent}${fwDisplay}${ansi.reset}`);
354
+ lines.push('');
355
+ }
356
+
357
+ // Routes coverage
358
+ const routeCoverage = serverRouteCount > 0 ? Math.round((clientRefs / serverRouteCount) * 100) : 100;
359
+ const routeColor = routeCoverage >= 80 ? colors.success : routeCoverage >= 50 ? colors.warning : colors.error;
360
+
361
+ lines.push(` ${shipIcons.route} ${ansi.bold}Routes${ansi.reset}`);
362
+ lines.push(` Server: ${colors.accent}${serverRouteCount}${ansi.reset} defined`);
363
+ lines.push(` Client: ${colors.accent}${clientRefs}${ansi.reset} references`);
364
+ lines.push(` Coverage: ${renderProgressBar(routeCoverage, 20, routeColor)} ${routeColor}${routeCoverage}%${ansi.reset}`);
365
+ lines.push('');
366
+
367
+ // Env coverage
368
+ const envCoverage = envVars > 0 ? Math.round((envDeclared / envVars) * 100) : 100;
369
+ const envColor = envCoverage >= 80 ? colors.success : envCoverage >= 50 ? colors.warning : colors.error;
370
+
371
+ lines.push(` ${shipIcons.env} ${ansi.bold}Environment${ansi.reset}`);
372
+ lines.push(` Used: ${colors.accent}${envVars}${ansi.reset} variables`);
373
+ lines.push(` Declared: ${colors.accent}${envDeclared}${ansi.reset} in .env`);
374
+ lines.push(` Coverage: ${renderProgressBar(envCoverage, 20, envColor)} ${envColor}${envCoverage}%${ansi.reset}`);
375
+
376
+ return lines.join('\n');
377
+ }
378
+
379
+ // ═══════════════════════════════════════════════════════════════════════════════
380
+ // FINDINGS BREAKDOWN
381
+ // ═══════════════════════════════════════════════════════════════════════════════
382
+
383
+ function getCategoryIcon(category) {
384
+ const iconMap = {
385
+ // AI Hallucination Categories (primary)
386
+ 'MissingRoute': shipIcons.route,
387
+ 'FakeSuccess': shipIcons.ghost,
388
+ 'GhostAuth': shipIcons.auth,
389
+ 'EnvContract': shipIcons.env,
390
+ 'EnvGap': shipIcons.env,
391
+ 'DeadUI': shipIcons.dead,
392
+ 'OwnerModeBypass': shipIcons.lock,
393
+ 'OptimisticNoRollback': '↩️',
394
+ 'SilentCatch': '🔇',
395
+ 'MethodMismatch': '🔀',
396
+ // Billing/Monetization
397
+ 'Billing': shipIcons.money,
398
+ 'StripeWebhook': shipIcons.money,
399
+ 'PaidSurface': shipIcons.money,
400
+ 'Entitlements': '💳',
401
+ // Security
402
+ 'Security': shipIcons.shield,
403
+ 'Auth': shipIcons.lock,
404
+ 'SECRET': shipIcons.key,
405
+ // Quality
406
+ 'ContractDrift': icons.warning,
407
+ 'ROUTE': shipIcons.route,
408
+ 'BILLING': shipIcons.money,
409
+ 'MOCK': shipIcons.ghost,
410
+ };
411
+ return iconMap[category] || shipIcons.bug;
412
+ }
413
+
414
+ function renderFindingsBreakdown(findings) {
415
+ if (!findings || findings.length === 0) {
416
+ const lines = [];
417
+ lines.push(renderSection('FINDINGS', icons.success));
418
+ lines.push('');
419
+ lines.push(` ${colors.success}${ansi.bold}${shipIcons.sparkle} No issues found! Your code is clean.${ansi.reset}`);
420
+ return lines.join('\n');
421
+ }
422
+
423
+ // Group by category
424
+ const byCategory = {};
425
+ for (const f of findings) {
426
+ const cat = f.category || 'Other';
427
+ if (!byCategory[cat]) byCategory[cat] = [];
428
+ byCategory[cat].push(f);
429
+ }
430
+
431
+ const blockers = findings.filter(f => f.severity === 'BLOCK' || f.severity === 'critical');
432
+ const warnings = findings.filter(f => f.severity === 'WARN' || f.severity === 'warning');
433
+
434
+ const lines = [];
435
+ lines.push(renderSection(`FINDINGS (${blockers.length} blockers, ${warnings.length} warnings)`, shipIcons.graph));
436
+ lines.push('');
437
+
438
+ // Sort categories by severity
439
+ const categories = Object.entries(byCategory).sort((a, b) => {
440
+ const aBlockers = a[1].filter(f => f.severity === 'BLOCK' || f.severity === 'critical').length;
441
+ const bBlockers = b[1].filter(f => f.severity === 'BLOCK' || f.severity === 'critical').length;
442
+ return bBlockers - aBlockers;
443
+ });
444
+
445
+ for (const [category, catFindings] of categories) {
446
+ const catBlockers = catFindings.filter(f => f.severity === 'BLOCK' || f.severity === 'critical').length;
447
+ const catWarnings = catFindings.filter(f => f.severity === 'WARN' || f.severity === 'warning').length;
448
+ const icon = getCategoryIcon(category);
449
+
450
+ const statusColor = catBlockers > 0 ? colors.error : catWarnings > 0 ? colors.warning : colors.success;
451
+ const statusIcon = catBlockers > 0 ? icons.error : catWarnings > 0 ? icons.warning : icons.success;
452
+
453
+ let stats = '';
454
+ if (catBlockers > 0) stats += `${colors.error}${catBlockers} blockers${ansi.reset}`;
455
+ if (catWarnings > 0) stats += `${stats ? ' ' : ''}${colors.warning}${catWarnings} warnings${ansi.reset}`;
456
+
457
+ lines.push(` ${statusColor}${statusIcon}${ansi.reset} ${icon} ${ansi.bold}${category.padEnd(20)}${ansi.reset} ${stats}`);
458
+ }
459
+
460
+ return lines.join('\n');
461
+ }
462
+
463
+ // ═══════════════════════════════════════════════════════════════════════════════
464
+ // BLOCKER DETAILS
465
+ // ═══════════════════════════════════════════════════════════════════════════════
466
+
467
+ function renderBlockerDetails(findings, maxShow = 8, options = {}) {
468
+ const { tier = 'free' } = options;
469
+ const blockers = findings.filter(f => f.severity === 'BLOCK' || f.severity === 'critical');
470
+
471
+ if (blockers.length === 0) return '';
472
+
473
+ const lines = [];
474
+ lines.push(renderSection(`BLOCKERS (${blockers.length})`, '🚨'));
475
+ lines.push('');
476
+
477
+ for (const blocker of blockers.slice(0, maxShow)) {
478
+ const icon = getCategoryIcon(blocker.category);
479
+ const severityBg = ansi.bgRgb(80, 20, 20);
480
+
481
+ // Check if this is a common AI hallucination pattern
482
+ const isAIPattern = AI_HALLUCINATION_CATEGORIES[blocker.category];
483
+ const aiTag = isAIPattern ? `${ansi.bgRgb(60, 40, 80)}${ansi.bold} 🤖 AI ${ansi.reset} ` : '';
484
+
485
+ // Severity badge + AI tag + title
486
+ lines.push(` ${severityBg}${ansi.bold} BLOCKER ${ansi.reset} ${aiTag}${icon} ${ansi.bold}${truncate(blocker.title || blocker.message, 45)}${ansi.reset}`);
487
+
488
+ // Why/description - emphasize AI-specific messaging
489
+ if (blocker.why || blocker.description) {
490
+ const desc = blocker.why || blocker.description;
491
+ lines.push(` ${ansi.dim}${truncate(desc, 55)}${ansi.reset}`);
492
+ }
493
+
494
+ // AI-specific warning for known patterns
495
+ if (isAIPattern) {
496
+ lines.push(` ${colors.warning}⚠ ${isAIPattern.desc}${ansi.reset}`);
497
+ }
498
+
499
+ // File location
500
+ if (blocker.evidence && blocker.evidence.length > 0) {
501
+ const ev = blocker.evidence[0];
502
+ const file = ev.file || blocker.file;
503
+ if (file) {
504
+ const fileDisplay = `${path.basename(file)}${ev.lines ? `:${ev.lines}` : blocker.line ? `:${blocker.line}` : ''}`;
505
+ lines.push(` ${colors.accent}${shipIcons.doc} ${fileDisplay}${ansi.reset}`);
506
+ }
507
+ } else if (blocker.file) {
508
+ const fileDisplay = `${path.basename(blocker.file)}${blocker.line ? `:${blocker.line}` : ''}`;
509
+ lines.push(` ${colors.accent}${shipIcons.doc} ${fileDisplay}${ansi.reset}`);
510
+ }
511
+
512
+ // Fix hint - shown to ALL tiers (FREE gets full details, PRO gets auto-fix capability)
513
+ if (blocker.fixHints && blocker.fixHints.length > 0) {
514
+ lines.push(` ${colors.success}→ ${truncate(blocker.fixHints[0], 50)}${ansi.reset}`);
515
+ } else if (blocker.fix) {
516
+ lines.push(` ${colors.success}→ ${truncate(blocker.fix, 50)}${ansi.reset}`);
517
+ }
518
+
519
+ lines.push('');
520
+ }
521
+
522
+ if (blockers.length > maxShow) {
523
+ lines.push(` ${ansi.dim}... and ${blockers.length - maxShow} more blockers (see full report)${ansi.reset}`);
524
+ lines.push('');
525
+ }
526
+
527
+ // Show PRO upsell for auto-fix capability (not viewing fix hints)
528
+ if (blockers.length > 0) {
529
+ lines.push(` ${ansi.dim}─────────────────────────────────────────${ansi.reset}`);
530
+ lines.push(` ${colors.accent}⚡${ansi.reset} ${ansi.bold}Auto-fix available${ansi.reset}`);
531
+ lines.push(` ${ansi.dim}Run${ansi.reset} ${colors.accent}vibecheck ship --fix${ansi.reset} ${ansi.dim}to apply fixes automatically${ansi.reset}`);
532
+ lines.push('');
533
+ }
534
+
535
+ return lines.join('\n');
536
+ }
537
+
538
+ // ═══════════════════════════════════════════════════════════════════════════════
539
+ // FIX MODE DISPLAY
540
+ // ═══════════════════════════════════════════════════════════════════════════════
541
+
542
+ function renderFixModeHeader() {
543
+ return `\n ${ansi.bgRgb(40, 80, 120)}${ansi.bold} ${shipIcons.wrench} AUTO-FIX MODE ${ansi.reset}\n`;
544
+ }
545
+
546
+ function renderFixResults(fixResults) {
547
+ if (!fixResults) return '';
548
+
549
+ const lines = [];
550
+ lines.push(renderSection('FIX RESULTS', shipIcons.wrench));
551
+ lines.push('');
552
+
553
+ // Errors
554
+ if (fixResults.errors && fixResults.errors.length > 0) {
555
+ for (const err of fixResults.errors) {
556
+ lines.push(` ${colors.error}${icons.error}${ansi.reset} ${err}`);
557
+ }
558
+ lines.push('');
559
+ }
560
+
561
+ // Actions taken
562
+ const actions = [
563
+ { done: fixResults.envExampleCreated, label: 'Created .env.example', icon: shipIcons.env },
564
+ { done: fixResults.gitignoreUpdated, label: 'Updated .gitignore', icon: shipIcons.shield },
565
+ { done: fixResults.fixesMdCreated, label: 'Generated fixes.md', icon: shipIcons.doc },
566
+ ];
567
+
568
+ for (const action of actions) {
569
+ const statusIcon = action.done ? `${colors.success}${icons.success}` : `${ansi.dim}•`;
570
+ const label = action.done ? `${ansi.reset}${action.label}` : `${ansi.dim}${action.label}${ansi.reset}`;
571
+ lines.push(` ${statusIcon}${ansi.reset} ${action.icon} ${label}`);
572
+ }
573
+
574
+ // Secrets found
575
+ if (fixResults.secretsFound && fixResults.secretsFound.length > 0) {
576
+ lines.push('');
577
+ lines.push(` ${ansi.bold}Secrets to migrate:${ansi.reset}`);
578
+ for (const secret of fixResults.secretsFound.slice(0, 5)) {
579
+ lines.push(` ${shipIcons.key} ${secret.varName} ${ansi.dim}(${secret.type})${ansi.reset}`);
580
+ }
581
+ }
582
+
583
+ lines.push('');
584
+ lines.push(` ${colors.success}${icons.success}${ansi.reset} ${ansi.bold}Safe fixes applied!${ansi.reset}`);
585
+ lines.push(` ${ansi.dim}Review changes and follow instructions in ${colors.accent}.vibecheck/fixes.md${ansi.reset}`);
586
+
587
+ return lines.join('\n');
588
+ }
589
+
590
+ // ═══════════════════════════════════════════════════════════════════════════════
591
+ // BADGE OUTPUT
592
+ // ═══════════════════════════════════════════════════════════════════════════════
593
+
594
+ function renderBadgeOutput(projectPath, verdict, score, options = {}) {
595
+ const { tier = 'free', isVerified = false } = options;
596
+ const projectName = path.basename(projectPath);
597
+ const projectId = projectName.toLowerCase().replace(/[^a-z0-9]/g, '-');
598
+
599
+ const config = getVerdictConfig(verdict, score);
600
+ const isPro = tier === 'pro' || tier === 'compliance';
601
+
602
+ const lines = [];
603
+
604
+ // Different header for verified vs basic badge
605
+ if (isPro && isVerified) {
606
+ lines.push(renderSection('VERIFIED BADGE', '✅'));
607
+ lines.push('');
608
+ lines.push(` ${ansi.bgRgb(40, 100, 60)}${ansi.bold} ✅ VERIFIED ${ansi.reset} ${ansi.dim}Reality-tested by AI${ansi.reset}`);
609
+ lines.push('');
610
+ } else {
611
+ lines.push(renderSection('SHIP BADGE', '📛'));
612
+ lines.push('');
613
+ }
614
+
615
+ // Badge preview
616
+ const badgeText = isPro && isVerified
617
+ ? `vibecheck | ✅ VERIFIED | ${score}`
618
+ : `vibecheck | ${verdict} | ${score}`;
619
+ lines.push(` ${config.bgColor}${ansi.bold} ${badgeText} ${ansi.reset}`);
620
+ lines.push('');
621
+
622
+ // Different URLs for verified vs basic badge
623
+ const badgeSuffix = (isPro && isVerified) ? '-verified' : '';
624
+ const badgeUrl = `https://vibecheck.dev/badge/${projectId}${badgeSuffix}.svg`;
625
+ const reportUrl = `https://vibecheck.dev/report/${projectId}`;
626
+ const markdown = `[![Vibecheck](${badgeUrl})](${reportUrl})`;
627
+
628
+ lines.push(` ${ansi.dim}Badge URL:${ansi.reset}`);
629
+ lines.push(` ${colors.accent}${badgeUrl}${ansi.reset}`);
630
+ lines.push('');
631
+ lines.push(` ${ansi.dim}Report URL:${ansi.reset}`);
632
+ lines.push(` ${colors.accent}${reportUrl}${ansi.reset}`);
633
+ lines.push('');
634
+ lines.push(` ${ansi.dim}Add to README.md:${ansi.reset}`);
635
+ lines.push(` ${colors.success}${markdown}${ansi.reset}`);
636
+
637
+ // Show verified badge upsell if not PRO
638
+ if (!isPro) {
639
+ lines.push('');
640
+ lines.push(` ${ansi.dim}─────────────────────────────────────────${ansi.reset}`);
641
+ lines.push(` ${colors.accent}✨${ansi.reset} ${ansi.bold}Want a verified badge?${ansi.reset}`);
642
+ lines.push(` ${ansi.dim}PRO users get a${ansi.reset} ${ansi.bgRgb(40, 100, 60)}${ansi.bold} ✅ VERIFIED ${ansi.reset} ${ansi.dim}badge${ansi.reset}`);
643
+ lines.push(` ${ansi.dim}proving your app was reality-tested by AI.${ansi.reset}`);
644
+ lines.push(` ${colors.accent}vibecheck login${ansi.reset} ${ansi.dim}to upgrade${ansi.reset}`);
645
+ }
646
+
647
+ return {
648
+ output: lines.join('\n'),
649
+ data: { projectId, badgeUrl, reportUrl, markdown, isVerified: isPro && isVerified },
650
+ };
651
+ }
652
+
653
+ // ═══════════════════════════════════════════════════════════════════════════════
654
+ // NEXT STEPS
655
+ // ═══════════════════════════════════════════════════════════════════════════════
656
+
657
+ function renderNextSteps(canShip, hasFix = false, options = {}) {
658
+ const { tier = 'free', showBadge = false } = options;
659
+ const isPro = tier === 'pro' || tier === 'compliance';
660
+
661
+ const lines = [];
662
+
663
+ // If codebase PASSED - show celebration and PRO upsell for verified badge
664
+ if (canShip) {
665
+ lines.push('');
666
+ lines.push(renderSection('WHAT\'S NEXT?', shipIcons.sparkle));
667
+ lines.push('');
668
+ lines.push(` ${colors.success}${icons.success}${ansi.reset} ${ansi.bold}Congratulations! Your code is ship-ready.${ansi.reset}`);
669
+ lines.push('');
670
+
671
+ if (!isPro) {
672
+ // Upsell PRO for verified badge
673
+ lines.push(` ${ansi.dim}─────────────────────────────────────────${ansi.reset}`);
674
+ lines.push(` ${colors.accent}🏆${ansi.reset} ${ansi.bold}Prove it to the world!${ansi.reset}`);
675
+ lines.push('');
676
+ lines.push(` ${ansi.dim}Upgrade to${ansi.reset} ${colors.accent}PRO${ansi.reset} ${ansi.dim}for a${ansi.reset} ${ansi.bgRgb(40, 100, 60)}${ansi.bold} ✅ VERIFIED ${ansi.reset} ${ansi.dim}badge${ansi.reset}`);
677
+ lines.push('');
678
+ lines.push(` ${ansi.dim}The verified badge proves:${ansi.reset}`);
679
+ lines.push(` ${colors.success}→${ansi.reset} AI reality-tested your app's actual behavior`);
680
+ lines.push(` ${colors.success}→${ansi.reset} Video proof of working features`);
681
+ lines.push(` ${colors.success}→${ansi.reset} Automated auth boundary verification`);
682
+ lines.push('');
683
+ lines.push(` ${colors.accent}vibecheck login${ansi.reset} ${ansi.dim}to start your PRO trial${ansi.reset}`);
684
+ lines.push('');
685
+ } else if (!showBadge) {
686
+ // PRO user who passed - prompt them to generate badge
687
+ lines.push(` ${colors.accent}vibecheck ship --badge${ansi.reset} ${ansi.dim}Generate your verified badge${ansi.reset}`);
688
+ lines.push(` ${colors.accent}vibecheck prove${ansi.reset} ${ansi.dim}Create video proof of working features${ansi.reset}`);
689
+ lines.push('');
690
+ }
691
+
692
+ return lines.join('\n');
693
+ }
694
+
695
+ // If codebase FAILED - show next steps to fix
696
+ lines.push(renderSection('NEXT STEPS', shipIcons.lightning));
697
+ lines.push('');
698
+
699
+ if (!hasFix) {
700
+ lines.push(` ${colors.accent}vibecheck ship --fix${ansi.reset} ${ansi.dim}Auto-fix what can be fixed${ansi.reset}`);
701
+ }
702
+ lines.push(` ${colors.accent}vibecheck ship --assist${ansi.reset} ${ansi.dim}Get AI help for complex issues${ansi.reset}`);
703
+ lines.push(` ${colors.accent}vibecheck report${ansi.reset} ${ansi.dim}Generate detailed HTML report${ansi.reset}`);
704
+ lines.push('');
705
+
706
+ return lines.join('\n');
707
+ }
708
+
709
+ // ═══════════════════════════════════════════════════════════════════════════════
710
+ // REPORT LINKS
711
+ // ═══════════════════════════════════════════════════════════════════════════════
712
+
713
+ function renderReportLinks(outputDir, hasFix = false) {
714
+ const lines = [];
715
+ lines.push(renderSection('REPORTS', shipIcons.doc));
716
+ lines.push('');
717
+ lines.push(` ${colors.accent}vibecheck report${ansi.reset} ${ansi.dim}Generate HTML report${ansi.reset}`);
718
+ lines.push(` ${ansi.dim}${outputDir}/last_ship.json${ansi.reset}`);
719
+ if (hasFix) {
720
+ lines.push(` ${colors.accent}${outputDir}/fixes.md${ansi.reset}`);
721
+ }
722
+ lines.push('');
723
+ return lines.join('\n');
724
+ }
725
+
726
+ // ═══════════════════════════════════════════════════════════════════════════════
727
+ // AI HALLUCINATION SCORE - Key metric for detecting AI-generated issues
728
+ // ═══════════════════════════════════════════════════════════════════════════════
729
+
730
+ // Categories that indicate common AI hallucination patterns
731
+ const AI_HALLUCINATION_CATEGORIES = {
732
+ // Route & API Issues (highest weight - these break apps)
733
+ 'MissingRoute': { weight: 25, label: 'Invented endpoints', desc: 'AI created API routes that don\'t exist' },
734
+ 'MethodMismatch': { weight: 20, label: 'Wrong HTTP method', desc: 'GET request to POST-only endpoint or vice versa' },
735
+
736
+ // Success/Error Handling Issues
737
+ 'FakeSuccess': { weight: 20, label: 'Fake success UI', desc: 'Shows success without verifying the operation' },
738
+ 'OptimisticNoRollback': { weight: 18, label: 'No rollback', desc: 'Optimistic update without failure rollback' },
739
+ 'SilentCatch': { weight: 15, label: 'Silent failure', desc: 'Catch block swallows errors without handling' },
740
+
741
+ // Auth & Security Issues
742
+ 'GhostAuth': { weight: 15, label: 'Missing auth', desc: 'Sensitive endpoints without server-side protection' },
743
+ 'OwnerModeBypass': { weight: 15, label: 'Backdoor access', desc: 'Production security bypasses left in code' },
744
+
745
+ // Environment & Config Issues
746
+ 'EnvGap': { weight: 10, label: 'Undefined env vars', desc: 'Uses environment variables that don\'t exist' },
747
+ 'EnvContract': { weight: 10, label: 'Env contract', desc: 'Environment variable contract violations' },
748
+
749
+ // UI & UX Issues
750
+ 'DeadUI': { weight: 10, label: 'Dead UI elements', desc: 'Buttons/forms/links that do nothing or go nowhere' },
751
+
752
+ // Billing & Monetization Issues
753
+ 'PaidSurface': { weight: 5, label: 'Billing bypass', desc: 'Paid features accessible without enforcement' },
754
+ 'Billing': { weight: 8, label: 'Billing issue', desc: 'Payment/webhook handling problems' },
755
+ 'Entitlements': { weight: 8, label: 'Entitlement gap', desc: 'Feature access without entitlement check' },
756
+ };
757
+
758
+ function calculateAIHallucinationScore(findings) {
759
+ if (!findings || findings.length === 0) return { score: 100, breakdown: [], totalIssues: 0 };
760
+
761
+ const breakdown = [];
762
+ let deductions = 0;
763
+
764
+ for (const [category, config] of Object.entries(AI_HALLUCINATION_CATEGORIES)) {
765
+ const categoryFindings = findings.filter(f => f.category === category);
766
+ const blockers = categoryFindings.filter(f => f.severity === 'BLOCK' || f.severity === 'critical');
767
+ const warnings = categoryFindings.filter(f => f.severity === 'WARN' || f.severity === 'warning');
768
+
769
+ if (categoryFindings.length > 0) {
770
+ // Blockers count more than warnings
771
+ const blockerDeduction = blockers.length * config.weight;
772
+ const warningDeduction = warnings.length * Math.floor(config.weight / 2);
773
+ const totalDeduction = blockerDeduction + warningDeduction;
774
+
775
+ deductions += totalDeduction;
776
+ breakdown.push({
777
+ category,
778
+ label: config.label,
779
+ desc: config.desc,
780
+ count: categoryFindings.length,
781
+ blockers: blockers.length,
782
+ warnings: warnings.length,
783
+ deduction: totalDeduction,
784
+ });
785
+ }
786
+ }
787
+
788
+ // Cap at 0
789
+ const score = Math.max(0, 100 - deductions);
790
+
791
+ return {
792
+ score,
793
+ breakdown: breakdown.sort((a, b) => b.deduction - a.deduction),
794
+ totalIssues: findings.length,
795
+ };
796
+ }
797
+
798
+ function renderAIHallucinationScore(findings) {
799
+ const { score, breakdown, totalIssues } = calculateAIHallucinationScore(findings);
800
+
801
+ const lines = [];
802
+ lines.push('');
803
+ lines.push(renderSection('AI REALITY CHECK', '🤖'));
804
+ lines.push('');
805
+
806
+ // Score display with color coding
807
+ const scoreColor = score >= 80 ? colors.success : score >= 50 ? colors.warning : colors.error;
808
+ const scoreLabel = score >= 80 ? 'Low Risk' : score >= 50 ? 'Medium Risk' : 'High Risk';
809
+
810
+ // Visual score bar
811
+ lines.push(` ${ansi.bold}AI Hallucination Risk${ansi.reset}`);
812
+ lines.push(` ${renderProgressBar(100 - score, 40, scoreColor)} ${scoreColor}${ansi.bold}${100 - score}%${ansi.reset} risk`);
813
+ lines.push('');
814
+
815
+ if (breakdown.length === 0) {
816
+ lines.push(` ${colors.success}${icons.success}${ansi.reset} ${ansi.bold}No AI hallucination patterns detected!${ansi.reset}`);
817
+ lines.push(` ${ansi.dim}Your code appears to be grounded in reality.${ansi.reset}`);
818
+ } else {
819
+ lines.push(` ${ansi.dim}Common AI mistakes found:${ansi.reset}`);
820
+ lines.push('');
821
+
822
+ for (const item of breakdown.slice(0, 5)) {
823
+ const itemColor = item.blockers > 0 ? colors.error : colors.warning;
824
+ const severityBadge = item.blockers > 0
825
+ ? `${colors.error}${ansi.bold}${item.blockers} BLOCK${ansi.reset}`
826
+ : `${colors.warning}${item.warnings} warn${ansi.reset}`;
827
+
828
+ lines.push(` ${itemColor}•${ansi.reset} ${ansi.bold}${item.label}${ansi.reset} ${ansi.dim}(${item.count})${ansi.reset} ${severityBadge}`);
829
+ lines.push(` ${ansi.dim}${item.desc}${ansi.reset}`);
830
+ }
831
+
832
+ if (breakdown.length > 5) {
833
+ lines.push(` ${ansi.dim}... and ${breakdown.length - 5} more categories${ansi.reset}`);
834
+ }
835
+ }
836
+
837
+ lines.push('');
838
+ return lines.join('\n');
839
+ }
840
+
841
+ // ═══════════════════════════════════════════════════════════════════════════════
842
+ // UPGRADE PROMPTS - Show FREE users what they're missing
843
+ // ═══════════════════════════════════════════════════════════════════════════════
844
+
845
+ function renderUpgradePrompts(findings, verdict) {
846
+ const lines = [];
847
+
848
+ // Only show if there are issues to fix
849
+ if (!findings || findings.length === 0) return '';
850
+
851
+ const blockers = findings.filter(f => f.severity === 'BLOCK' || f.severity === 'critical');
852
+ const fixableCount = findings.length;
853
+
854
+ lines.push('');
855
+ lines.push(` ${ansi.dim}${'─'.repeat(60)}${ansi.reset}`);
856
+ lines.push('');
857
+ lines.push(` ${colors.accent}⚡${ansi.reset} ${ansi.bold}Want to fix these automatically?${ansi.reset}`);
858
+ lines.push('');
859
+
860
+ // PRO tier benefits (2-tier: FREE / PRO)
861
+ lines.push(` ${colors.accent}PRO${ansi.reset} ${ansi.dim}($49/mo)${ansi.reset}`);
862
+ lines.push(` ${colors.success}→${ansi.reset} ${ansi.bold}vibecheck fix${ansi.reset} - AI auto-fix for ${fixableCount} issues`);
863
+ lines.push(` ${colors.success}→${ansi.reset} ${ansi.bold}vibecheck ship${ansi.reset} - GO/NO-GO verdicts with proof`);
864
+ lines.push(` ${colors.success}→${ansi.reset} ${ansi.bold}vibecheck prove${ansi.reset} - Video proof your app actually works`);
865
+ lines.push(` ${colors.success}→${ansi.reset} ${ansi.bold}vibecheck reality --agent${ansi.reset} - AI tests your app autonomously`);
866
+ lines.push('');
867
+
868
+ lines.push(` ${ansi.dim}Upgrade: ${colors.accent}vibecheckai.dev/pricing${ansi.reset}`);
869
+ lines.push('');
870
+
871
+ return lines.join('\n');
872
+ }
873
+
874
+ // ═══════════════════════════════════════════════════════════════════════════════
875
+ // FULL SHIP OUTPUT
876
+ // ═══════════════════════════════════════════════════════════════════════════════
877
+
878
+ function formatShipOutput(result, options = {}) {
879
+ // 1. Data Prep - Extract from result structure
880
+ const {
881
+ verdict,
882
+ score = 0,
883
+ findings = [],
884
+ blockers = [],
885
+ warnings = [],
886
+ duration = 0,
887
+ } = result;
888
+
889
+ // Calculate blockers and warnings if not provided
890
+ const actualBlockers = blockers.length > 0
891
+ ? blockers
892
+ : findings.filter(f => f.severity === 'BLOCK' || f.severity === 'critical');
893
+ const actualWarnings = warnings.length > 0
894
+ ? warnings
895
+ : findings.filter(f => f.severity === 'WARN' || f.severity === 'warning');
896
+
897
+ const canShip = verdict === 'SHIP';
898
+ const logo = canShip ? LOGO_SHIP : LOGO_NOSHIP;
899
+ const themeColor = canShip ? chalk.green : chalk.red;
900
+ const statusText = canShip ? 'CLEAR FOR LAUNCH' : 'DEPLOYMENT ABORTED';
901
+
902
+ // Calculate AI Hallucination Score
903
+ const aiHallucinationFindings = findings.filter(f =>
904
+ f.category === 'MissingRoute' ||
905
+ f.category === 'GhostAuth' ||
906
+ f.category === 'FakeSuccess' ||
907
+ f.type === 'MissingRoute' ||
908
+ f.type === 'GhostAuth'
909
+ );
910
+ const aiScore = Math.max(0, 100 - (aiHallucinationFindings.length * 15));
911
+
912
+ // Generate launch ID
913
+ const launchId = Math.floor(Math.random() * 9999);
914
+ const projectName = path.basename(process.cwd());
915
+
916
+ const lines = [];
917
+
918
+ // 2. Render External Header
919
+ lines.push(chalk.cyan + LOGO_VIBECHECK.trim() + chalk.reset);
920
+ lines.push('');
921
+
922
+ // 3. Render Box Top
923
+ lines.push(`${chalk.gray}${BOX_SHIP.topLeft}${BOX_SHIP.horizontal.repeat(WIDTH - 2)}${BOX_SHIP.topRight}${chalk.reset}`);
924
+ lines.push(`${chalk.gray}${BOX_SHIP.vertical}${' '.repeat(WIDTH - 2)}${BOX_SHIP.vertical}${chalk.reset}`);
925
+
926
+ // 4. Render Hero Logo (SHIP vs NO SHIP)
927
+ logo.trim().split('\n').filter(l => l.trim().length > 0).forEach(l => {
928
+ lines.push(`${chalk.gray}${BOX_SHIP.vertical}${chalk.reset}${themeColor}${padCenter(l.trim(), WIDTH - 2)}${chalk.reset}${chalk.gray}${BOX_SHIP.vertical}${chalk.reset}`);
929
+ });
930
+ lines.push(`${chalk.gray}${BOX_SHIP.vertical}${' '.repeat(WIDTH - 2)}${BOX_SHIP.vertical}${chalk.reset}`);
931
+
932
+ // 5. Info Row
933
+ const version = options.version || 'v3.5.5';
934
+ const integrity = canShip ? `${chalk.green}OPTIMAL${chalk.reset}` : `${chalk.red}COMPROMISED${chalk.reset}`;
935
+ const infoText = `${chalk.bold}${version} SHIP${chalk.reset} :: SYSTEM INTEGRITY: ${integrity}`;
936
+ const metaText = `Launch ID: #${launchId} | T-${duration}ms`;
937
+ const infoLine = ` ${infoText}${' '.repeat(Math.max(1, WIDTH - 6 - stripAnsi(infoText).length - stripAnsi(metaText).length))}${chalk.dim}${metaText}${chalk.reset}`;
938
+ lines.push(`${chalk.gray}${BOX_SHIP.vertical}${chalk.reset}${infoLine} ${chalk.gray}${BOX_SHIP.vertical}${chalk.reset}`);
939
+
940
+ const targetLine = ` Target: ${padRight(projectName, 50)}`;
941
+ lines.push(`${chalk.gray}${BOX_SHIP.vertical}${chalk.reset}${targetLine}${' '.repeat(WIDTH - 2 - stripAnsi(targetLine).length)}${chalk.gray}${BOX_SHIP.vertical}${chalk.reset}`);
942
+
943
+ // 6. Split Pane
944
+ lines.push(`${chalk.gray}${BOX_SHIP.teeRight}${BOX_SHIP.horizontal.repeat(44)}${BOX_SHIP.teeTop}${BOX_SHIP.horizontal.repeat(WIDTH - 47)}${BOX_SHIP.teeLeft}${chalk.reset}`);
945
+
946
+ function printSplitRow(left, right) {
947
+ const leftContent = padRight(left || '', 42);
948
+ const rightContent = padRight(right || '', WIDTH - 48);
949
+ lines.push(`${chalk.gray}${BOX_SHIP.vertical}${chalk.reset} ${leftContent} ${chalk.gray}${BOX_SHIP.lightV}${chalk.reset} ${rightContent} ${chalk.gray}${BOX_SHIP.vertical}${chalk.reset}`);
950
+ }
951
+
952
+ // Row 1: Headers
953
+ printSplitRow(`${chalk.bold}DEPLOYMENT VITALS${chalk.reset}`, `${chalk.bold}BLOCKER MANIFEST${chalk.reset}`);
954
+
955
+ // Row 2: Divider
956
+ lines.push(`${chalk.gray}${BOX_SHIP.vertical}${chalk.reset} ${BOX_SHIP.lightH.repeat(42)} ${chalk.gray}${BOX_SHIP.lightCross}${chalk.reset} ${BOX_SHIP.lightH.repeat(WIDTH - 48)} ${chalk.gray}${BOX_SHIP.vertical}${chalk.reset}`);
957
+
958
+ // Prepare Left Column
959
+ const confidence = canShip ? 99 : Math.max(10, score - 10);
960
+ const leftCol = [
961
+ '',
962
+ ` LAUNCH STATUS [${themeColor}${statusText}${chalk.reset}]`,
963
+ ` [${renderHealthBar(score, 25, themeColor)}] ${score}%`,
964
+ '',
965
+ ` AI REALITY CHECK [${aiScore > 80 ? 'PASSED' : 'RISK'}]`,
966
+ ` [${renderHealthBar(aiScore, 25)}] ${aiScore}%`,
967
+ '',
968
+ ` CONFIDENCE [${confidence > 90 ? 'HIGH' : 'LOW'}]`,
969
+ ` [${renderHealthBar(confidence, 25)}] ${confidence}%`,
970
+ '',
971
+ `${chalk.gray}${BOX_SHIP.lightH.repeat(42)}${chalk.reset}`,
972
+ `${chalk.bold} CERTIFICATION${chalk.reset}`,
973
+ '',
974
+ ];
975
+
976
+ // Certification checks
977
+ const hasRouteIssues = findings.some(f => f.category === 'MissingRoute' || f.type === 'MissingRoute');
978
+ const hasAuthIssues = findings.some(f => f.category === 'GhostAuth' || f.category === 'Auth' || f.type === 'Auth');
979
+ const hasEnvIssues = findings.some(f => f.category === 'EnvContract' || f.category === 'EnvGap');
980
+ const hasDbIssues = findings.some(f => f.category === 'Database' || f.type === 'Database');
981
+
982
+ leftCol.push(` ${!hasRouteIssues ? chalk.green + '✓' : chalk.red + '✖'}${chalk.reset} Routes...............${!hasRouteIssues ? 'READY' : 'FAIL'}`);
983
+ leftCol.push(` ${!hasAuthIssues ? chalk.green + '✓' : chalk.red + '✖'}${chalk.reset} Auth Boundaries......${!hasAuthIssues ? 'READY' : 'FAIL'}`);
984
+ leftCol.push(` ${!hasEnvIssues ? chalk.green + '✓' : chalk.red + '✖'}${chalk.reset} Environment..........${!hasEnvIssues ? 'READY' : 'FAIL'}`);
985
+ leftCol.push(` ${!hasDbIssues ? chalk.green + '✓' : chalk.red + '✖'}${chalk.reset} Database.............${!hasDbIssues ? 'READY' : 'FAIL'}`);
986
+ leftCol.push('');
987
+
988
+ // Prepare Right Column
989
+ const rightCol = [];
990
+ rightCol.push('');
991
+
992
+ if (canShip) {
993
+ rightCol.push(`${chalk.green} [✓] ALL SYSTEMS GO${chalk.reset}`);
994
+ rightCol.push(` No blockers detected.`);
995
+ rightCol.push('');
996
+ rightCol.push(` Ready for production.`);
997
+ rightCol.push(` 0 Blockers`);
998
+ rightCol.push(` ${actualWarnings.length} Warnings (Non-critical)`);
999
+ rightCol.push('');
1000
+ } else {
1001
+ // Show top 3 Blockers or Warnings
1002
+ const topItems = actualBlockers.length > 0 ? actualBlockers.slice(0, 3) : actualWarnings.slice(0, 3);
1003
+
1004
+ if (topItems.length === 0) {
1005
+ rightCol.push(`${chalk.yellow} [!] REVIEW REQUIRED${chalk.reset}`);
1006
+ rightCol.push(` Check findings.`);
1007
+ rightCol.push('');
1008
+ } else {
1009
+ topItems.forEach((f, i) => {
1010
+ const isBlock = f.severity === 'BLOCK' || f.severity === 'critical' || actualBlockers.includes(f);
1011
+ const sevColor = isBlock ? chalk.red : chalk.yellow;
1012
+ const icon = isBlock ? '🛑' : '⚠️';
1013
+
1014
+ const category = f.category || f.type || 'Issue';
1015
+ rightCol.push(`${sevColor}${icon} ${category}${chalk.reset}`);
1016
+ rightCol.push(`${chalk.gray}${BOX_SHIP.lightH.repeat(28)}${chalk.reset}`);
1017
+
1018
+ let file = f.file ? path.basename(f.file) : 'Project';
1019
+ rightCol.push(` ${i+1}. ${file}${f.line ? ':' + f.line : ''}`);
1020
+
1021
+ let msg = f.message || f.title || f.description || 'Unknown issue';
1022
+ if (msg.length > 25) msg = msg.substring(0, 25) + '...';
1023
+ rightCol.push(` > ${chalk.dim}${msg}${chalk.reset}`);
1024
+ rightCol.push('');
1025
+ });
1026
+
1027
+ const remaining = (actualBlockers.length + actualWarnings.length) - 3;
1028
+ if (remaining > 0) {
1029
+ rightCol.push(` ${chalk.dim}... and ${remaining} more items${chalk.reset}`);
1030
+ }
1031
+ }
1032
+ }
1033
+
1034
+ // Merge Columns
1035
+ const maxRows = Math.max(leftCol.length, rightCol.length);
1036
+ for (let i = 0; i < maxRows; i++) {
1037
+ printSplitRow(leftCol[i] || '', rightCol[i] || '');
1038
+ }
1039
+
1040
+ // 7. Action Footer
1041
+ lines.push(`${chalk.gray}${BOX_SHIP.teeRight}${BOX_SHIP.horizontal.repeat(WIDTH - 2)}${BOX_SHIP.teeLeft}${chalk.reset}`);
1042
+ lines.push(`${chalk.gray}${BOX_SHIP.vertical}${chalk.reset} ${chalk.bold}MISSION CONTROL PROTOCOLS${chalk.reset}${' '.repeat(WIDTH - 28)}${chalk.gray}${BOX_SHIP.vertical}${chalk.reset}`);
1043
+ lines.push(`${chalk.gray}${BOX_SHIP.vertical}${chalk.reset} ${BOX_SHIP.lightH.repeat(WIDTH - 4)} ${chalk.gray}${BOX_SHIP.vertical}${chalk.reset}`);
1044
+
1045
+ if (!canShip) {
1046
+ lines.push(`${chalk.gray}${BOX_SHIP.vertical}${chalk.reset} ${chalk.red}[!] LAUNCH SCRUBBED. ${actualBlockers.length} BLOCKERS FOUND.${chalk.reset}${' '.repeat(WIDTH - 42)}${chalk.gray}${BOX_SHIP.vertical}${chalk.reset}`);
1047
+ lines.push(`${chalk.gray}${BOX_SHIP.vertical}${' '.repeat(WIDTH - 2)}${BOX_SHIP.vertical}${chalk.reset}`);
1048
+ lines.push(`${chalk.gray}${BOX_SHIP.vertical}${chalk.reset} > ${chalk.cyan}vibecheck ship --fix${chalk.reset} [AUTO-PATCH] Resolve blockers automatically ${chalk.gray}${BOX_SHIP.vertical}${chalk.reset}`);
1049
+ lines.push(`${chalk.gray}${BOX_SHIP.vertical}${chalk.reset} > ${chalk.cyan}vibecheck report${chalk.reset} [DEBRIEF] View full HTML mission report ${chalk.gray}${BOX_SHIP.vertical}${chalk.reset}`);
1050
+ } else {
1051
+ lines.push(`${chalk.gray}${BOX_SHIP.vertical}${chalk.reset} ${chalk.green}[✓] ORBITAL INSERTION CALCULATED.${chalk.reset}${' '.repeat(WIDTH - 36)}${chalk.gray}${BOX_SHIP.vertical}${chalk.reset}`);
1052
+ lines.push(`${chalk.gray}${BOX_SHIP.vertical}${' '.repeat(WIDTH - 2)}${BOX_SHIP.vertical}${chalk.reset}`);
1053
+ lines.push(`${chalk.gray}${BOX_SHIP.vertical}${chalk.reset} > ${chalk.cyan}git push origin main${chalk.reset} [ENGAGE] Proceed with deployment ${chalk.gray}${BOX_SHIP.vertical}${chalk.reset}`);
1054
+ lines.push(`${chalk.gray}${BOX_SHIP.vertical}${chalk.reset} > ${chalk.cyan}vibecheck prove${chalk.reset} [RECORD] Generate verification proof ${chalk.gray}${BOX_SHIP.vertical}${chalk.reset}`);
1055
+ }
1056
+
1057
+ // 8. Box Bottom
1058
+ lines.push(`${chalk.gray}${BOX_SHIP.bottomLeft}${BOX_SHIP.horizontal.repeat(WIDTH - 2)}${BOX_SHIP.bottomRight}${chalk.reset}`);
1059
+
1060
+ return lines.join('\n');
1061
+ }
1062
+
1063
+ // ═══════════════════════════════════════════════════════════════════════════════
1064
+ // UTILITIES
1065
+ // ═══════════════════════════════════════════════════════════════════════════════
1066
+
1067
+ function renderProgressBar(percent, width, color) {
1068
+ const filled = Math.round((percent / 100) * width);
1069
+ const empty = width - filled;
1070
+ return `${color}${'█'.repeat(filled)}${ansi.dim}${'░'.repeat(empty)}${ansi.reset}`;
1071
+ }
1072
+
1073
+ // ═══════════════════════════════════════════════════════════════════════════════
1074
+ // EXIT CODES
1075
+ // ═══════════════════════════════════════════════════════════════════════════════
1076
+
1077
+ const EXIT_CODES = {
1078
+ SHIP: 0,
1079
+ WARN: 1,
1080
+ BLOCK: 2,
1081
+ ERROR: 3,
1082
+ };
1083
+
1084
+ function getExitCode(verdict) {
1085
+ return EXIT_CODES[verdict] || EXIT_CODES.ERROR;
1086
+ }
1087
+
1088
+ function verdictFromExitCode(code) {
1089
+ const map = { 0: 'SHIP', 1: 'WARN', 2: 'BLOCK', 3: 'ERROR' };
1090
+ return map[code] || 'ERROR';
1091
+ }
1092
+
1093
+ // ═══════════════════════════════════════════════════════════════════════════════
1094
+ // EXPORTS
1095
+ // ═══════════════════════════════════════════════════════════════════════════════
1096
+
1097
+ module.exports = {
1098
+ // Main formatters
1099
+ formatShipOutput,
1100
+
1101
+ // Component renderers
1102
+ renderVerdictCard,
1103
+ renderProofGraph,
1104
+ renderRouteTruthMap,
1105
+ renderFindingsBreakdown,
1106
+ renderBlockerDetails,
1107
+ renderFixModeHeader,
1108
+ renderFixResults,
1109
+ renderBadgeOutput,
1110
+ renderNextSteps,
1111
+ renderReportLinks,
1112
+ renderAIHallucinationScore,
1113
+ renderUpgradePrompts,
1114
+
1115
+ // AI Hallucination scoring
1116
+ calculateAIHallucinationScore,
1117
+ AI_HALLUCINATION_CATEGORIES,
1118
+
1119
+ // Utilities
1120
+ getVerdictConfig,
1121
+ getCategoryIcon,
1122
+ getExitCode,
1123
+ verdictFromExitCode,
1124
+ EXIT_CODES,
1125
+
1126
+ // Icons
1127
+ shipIcons,
1128
+ };