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,2089 @@
1
+ /**
2
+ * vibecheck packs - Unified Artifact Factory V2
3
+ *
4
+ * ═══════════════════════════════════════════════════════════════════════════════
5
+ * ENTERPRISE-GRADE ARTIFACT BUNDLING
6
+ * "One command to produce a ZIP bundle + manifest + HTML index"
7
+ * ═══════════════════════════════════════════════════════════════════════════════
8
+ *
9
+ * Consolidates all artifact generation into a single command:
10
+ *
11
+ * • EVIDENCE - Videos, traces, screenshots, HAR files
12
+ * • PERMISSIONS - AuthZ matrix, role-based permission mapping
13
+ * • PROOF-GRAPH - Verification coverage and proof chains
14
+ * • REPORTS - HTML, Markdown, SARIF, CSV, PDF
15
+ * • BUNDLE - All-in-one shareable evidence bundle (V2)
16
+ *
17
+ * V2 Features:
18
+ * - Stable naming scheme, reproducible builds
19
+ * - Cross-links to ship/reality/shield receipts
20
+ * - Unified manifest schema with build fingerprints
21
+ * - HTML index for all packs
22
+ *
23
+ * @module runPacks
24
+ * @version 5.0.0
25
+ * @license MIT
26
+ */
27
+
28
+ "use strict";
29
+
30
+ const path = require("path");
31
+ const fs = require("fs");
32
+ const crypto = require("crypto");
33
+
34
+ // ═══════════════════════════════════════════════════════════════════════════════
35
+ // LAZY IMPORTS
36
+ // ═══════════════════════════════════════════════════════════════════════════════
37
+
38
+ let _globalFlags = null;
39
+ let _exitCodes = null;
40
+ let _cliOutput = null;
41
+
42
+ function getGlobalFlags() {
43
+ if (!_globalFlags) {
44
+ _globalFlags = require("./lib/global-flags");
45
+ }
46
+ return _globalFlags;
47
+ }
48
+
49
+ function getExitCodes() {
50
+ if (!_exitCodes) {
51
+ _exitCodes = require("./lib/exit-codes");
52
+ }
53
+ return _exitCodes;
54
+ }
55
+
56
+ function getCliOutput() {
57
+ if (!_cliOutput) {
58
+ _cliOutput = require("./lib/unified-cli-output");
59
+ }
60
+ return _cliOutput;
61
+ }
62
+
63
+ // ═══════════════════════════════════════════════════════════════════════════════
64
+ // UNDERLYING IMPLEMENTATIONS
65
+ // ═══════════════════════════════════════════════════════════════════════════════
66
+
67
+ const implementations = {
68
+ evidencePack: null,
69
+ report: null,
70
+ };
71
+
72
+ function loadImplementation(name) {
73
+ if (implementations[name] !== null) return implementations[name];
74
+
75
+ try {
76
+ switch (name) {
77
+ case "evidencePack":
78
+ implementations[name] = require("./runEvidencePack").runEvidencePack;
79
+ break;
80
+ case "report":
81
+ implementations[name] = require("./runReport").runReport;
82
+ break;
83
+ default:
84
+ implementations[name] = false;
85
+ }
86
+ } catch (e) {
87
+ implementations[name] = false;
88
+ }
89
+
90
+ return implementations[name];
91
+ }
92
+
93
+ // ═══════════════════════════════════════════════════════════════════════════════
94
+ // V2 PACKS MODULES (Lazy Loaded)
95
+ // ═══════════════════════════════════════════════════════════════════════════════
96
+
97
+ let _packFactory = null;
98
+ let _permissionsPack = null;
99
+ let _proofGraphPack = null;
100
+ let _evidencePackV2 = null;
101
+ let _bundlePack = null;
102
+
103
+ function getPackFactory() {
104
+ if (!_packFactory) {
105
+ try {
106
+ _packFactory = require("./lib/packs/pack-factory");
107
+ } catch (e) {
108
+ _packFactory = false;
109
+ }
110
+ }
111
+ return _packFactory;
112
+ }
113
+
114
+ function getPermissionsPack() {
115
+ if (!_permissionsPack) {
116
+ try {
117
+ _permissionsPack = require("./lib/packs/permissions-pack");
118
+ } catch (e) {
119
+ _permissionsPack = false;
120
+ }
121
+ }
122
+ return _permissionsPack;
123
+ }
124
+
125
+ function getProofGraphPack() {
126
+ if (!_proofGraphPack) {
127
+ try {
128
+ _proofGraphPack = require("./lib/packs/proof-graph-pack");
129
+ } catch (e) {
130
+ _proofGraphPack = false;
131
+ }
132
+ }
133
+ return _proofGraphPack;
134
+ }
135
+
136
+ function getEvidencePackV2() {
137
+ if (!_evidencePackV2) {
138
+ try {
139
+ _evidencePackV2 = require("./lib/packs/evidence-pack");
140
+ } catch (e) {
141
+ _evidencePackV2 = false;
142
+ }
143
+ }
144
+ return _evidencePackV2;
145
+ }
146
+
147
+ function getBundlePack() {
148
+ if (!_bundlePack) {
149
+ try {
150
+ _bundlePack = require("./lib/packs/bundle");
151
+ } catch (e) {
152
+ _bundlePack = false;
153
+ }
154
+ }
155
+ return _bundlePack;
156
+ }
157
+
158
+ // ═══════════════════════════════════════════════════════════════════════════════
159
+ // CONSTANTS
160
+ // ═══════════════════════════════════════════════════════════════════════════════
161
+
162
+ const PACKS_VERSION = "5.0.0";
163
+
164
+ const SUBCOMMANDS = {
165
+ bundle: {
166
+ description: "All-in-one ZIP bundle + manifest + HTML index (V2)",
167
+ pro: false,
168
+ formats: ["zip", "dir"],
169
+ },
170
+ evidence: {
171
+ description: "Bundle videos, traces, screenshots, HAR files",
172
+ pro: false,
173
+ formats: ["pack", "zip"],
174
+ },
175
+ permissions: {
176
+ description: "AuthZ matrix, roles, protected routes",
177
+ pro: false,
178
+ formats: ["json", "html", "md"],
179
+ },
180
+ graph: {
181
+ description: "Proof graph with receipt cross-links",
182
+ pro: false,
183
+ formats: ["json", "dot", "mermaid", "html"],
184
+ },
185
+ report: {
186
+ description: "Generate HTML/MD/SARIF/CSV/PDF reports",
187
+ pro: false,
188
+ formats: ["html", "md", "json", "sarif", "csv", "pdf"],
189
+ },
190
+ manifest: {
191
+ description: "List all available artifacts",
192
+ pro: false,
193
+ formats: ["json", "table"],
194
+ },
195
+ launch: {
196
+ description: "Bundle launch validation results for team sharing",
197
+ pro: false,
198
+ formats: ["zip", "json", "md"],
199
+ },
200
+ list: {
201
+ description: "List existing packs and bundles",
202
+ pro: false,
203
+ formats: ["json", "table"],
204
+ },
205
+ cleanup: {
206
+ description: "Remove old packs (keeps 5 most recent)",
207
+ pro: false,
208
+ formats: ["json"],
209
+ },
210
+ };
211
+
212
+ const GRAPH_QUERIES = {
213
+ all: "All nodes and edges",
214
+ unexecuted: "Nodes never executed at runtime",
215
+ "unhit-routes": "Routes never called",
216
+ "dead-ui": "UI elements with no handlers",
217
+ gaps: "Missing connections (proof gaps)",
218
+ };
219
+
220
+ // ═══════════════════════════════════════════════════════════════════════════════
221
+ // HELP SYSTEM
222
+ // ═══════════════════════════════════════════════════════════════════════════════
223
+
224
+ function printHelp(detailed = false) {
225
+ const cli = getCliOutput();
226
+ const { ansi, renderMinimalHeader } = cli;
227
+
228
+ // Print unified header
229
+ renderMinimalHeader("packs", "free");
230
+
231
+ console.log(` ${ansi.bold}ARTIFACT PACKS V2${ansi.reset}
232
+ ${ansi.dim}One command to produce ZIP bundle + manifest + HTML index${ansi.reset}
233
+
234
+ ${ansi.bold}USAGE${ansi.reset}
235
+ ${ansi.cyan}vibecheck packs${ansi.reset} [subcommand] [options]
236
+
237
+ ${ansi.dim}Generate shareable evidence bundles with stable naming and reproducible builds.
238
+ Cross-links to ship/reality/shield receipts for complete audit trails.${ansi.reset}
239
+
240
+ ${ansi.bold}SUBCOMMANDS${ansi.reset}`);
241
+
242
+ const maxLen = Math.max(...Object.keys(SUBCOMMANDS).map(k => k.length));
243
+ for (const [name, info] of Object.entries(SUBCOMMANDS)) {
244
+ const proTag = info.pro ? `${ansi.magenta}PRO${ansi.reset} ` : " ";
245
+ console.log(` ${ansi.cyan}${name.padEnd(maxLen + 2)}${ansi.reset}${proTag}${info.description}`);
246
+ }
247
+
248
+ console.log(`
249
+ ${ansi.bold}BUNDLE OPTIONS (V2)${ansi.reset}
250
+ ${ansi.cyan}--output, -o <path>${ansi.reset} Output directory
251
+ ${ansi.cyan}--no-zip${ansi.reset} Create directory instead of ZIP
252
+ ${ansi.cyan}--no-evidence${ansi.reset} Exclude evidence pack
253
+ ${ansi.cyan}--no-permissions${ansi.reset} Exclude permissions pack
254
+ ${ansi.cyan}--no-graph${ansi.reset} Exclude proof graph pack
255
+ ${ansi.cyan}--no-report${ansi.reset} Exclude report pack
256
+
257
+ ${ansi.bold}EVIDENCE OPTIONS${ansi.reset}
258
+ ${ansi.cyan}--output, -o <path>${ansi.reset} Output directory
259
+ ${ansi.cyan}--run-id <id>${ansi.reset} Bundle specific run
260
+ ${ansi.cyan}--zip${ansi.reset} Create ZIP bundle
261
+
262
+ ${ansi.bold}PERMISSIONS OPTIONS${ansi.reset}
263
+ ${ansi.cyan}--output, -o <path>${ansi.reset} Output directory
264
+ ${ansi.cyan}--zip${ansi.reset} Create ZIP bundle
265
+
266
+ ${ansi.bold}GRAPH OPTIONS${ansi.reset}
267
+ ${ansi.cyan}--output, -o <path>${ansi.reset} Output directory
268
+ ${ansi.cyan}--zip${ansi.reset} Create ZIP bundle
269
+
270
+ ${ansi.bold}REPORT OPTIONS${ansi.reset}
271
+ ${ansi.cyan}--format <fmt>${ansi.reset} Output format: html, md, json, sarif, csv, pdf
272
+ ${ansi.cyan}--type <type>${ansi.reset} Report type: executive, technical, compliance
273
+ ${ansi.cyan}--output, -o <path>${ansi.reset} Output file path
274
+ ${ansi.cyan}--open${ansi.reset} Open report in browser
275
+
276
+ ${ansi.bold}CLEANUP OPTIONS${ansi.reset}
277
+ ${ansi.cyan}--keep <n>${ansi.reset} Number of bundles to keep (default: 5)
278
+ ${ansi.cyan}--max-age <days>${ansi.reset} Max age in days (default: 30)
279
+
280
+ ${ansi.bold}GENERAL OPTIONS${ansi.reset}
281
+ ${ansi.cyan}--json${ansi.reset} Output as JSON (CI integration)
282
+ ${ansi.cyan}--quiet, -q${ansi.reset} Suppress non-essential output
283
+ ${ansi.cyan}--verbose, -v${ansi.reset} Show detailed output
284
+ ${ansi.cyan}--help, -h${ansi.reset} Show this help
285
+
286
+ ${ansi.bold}EXAMPLES${ansi.reset}
287
+ ${ansi.dim}# Create all-in-one evidence bundle (default)${ansi.reset}
288
+ vibecheck packs
289
+
290
+ ${ansi.dim}# Create bundle with specific components${ansi.reset}
291
+ vibecheck packs bundle --no-report
292
+
293
+ ${ansi.dim}# Generate permissions/AuthZ matrix${ansi.reset}
294
+ vibecheck packs permissions
295
+
296
+ ${ansi.dim}# Generate proof graph with visualizations${ansi.reset}
297
+ vibecheck packs graph
298
+
299
+ ${ansi.dim}# Bundle evidence with ZIP${ansi.reset}
300
+ vibecheck packs evidence --zip
301
+
302
+ ${ansi.dim}# Generate HTML report${ansi.reset}
303
+ vibecheck packs report --format html --open
304
+
305
+ ${ansi.dim}# List existing packs${ansi.reset}
306
+ vibecheck packs list
307
+
308
+ ${ansi.dim}# Clean up old packs${ansi.reset}
309
+ vibecheck packs cleanup`);
310
+
311
+ if (detailed) {
312
+ console.log(`
313
+ ${ansi.bold}GRAPH QUERIES${ansi.reset}`);
314
+ for (const [query, desc] of Object.entries(GRAPH_QUERIES)) {
315
+ console.log(` ${ansi.cyan}${query.padEnd(16)}${ansi.reset} ${desc}`);
316
+ }
317
+
318
+ console.log(`
319
+ ${ansi.bold}OUTPUT LOCATIONS${ansi.reset}
320
+ ${ansi.dim}Evidence Packs:${ansi.reset} .vibecheck/evidence-packs/<pack-id>/
321
+ ${ansi.dim}Reports:${ansi.reset} .vibecheck/reports/
322
+ ${ansi.dim}Graphs:${ansi.reset} .vibecheck/graphs/
323
+ ${ansi.dim}Manifests:${ansi.reset} .vibecheck/manifests/`);
324
+ }
325
+
326
+ console.log(`
327
+ ${ansi.bold}EXIT CODES${ansi.reset}
328
+ ${ansi.green}0${ansi.reset} Success
329
+ ${ansi.yellow}1${ansi.reset} No data found
330
+ ${ansi.red}2${ansi.reset} Generation failed
331
+ ${ansi.red}4${ansi.reset} Invalid arguments
332
+
333
+ ${ansi.dim}────────────────────────────────────────────────────────────────────${ansi.reset}
334
+ ${ansi.dim}Documentation: https://docs.vibecheckai.dev/cli/packs${ansi.reset}
335
+ ${ansi.dim}Version: ${PACKS_VERSION}${ansi.reset}
336
+ `);
337
+ }
338
+
339
+ // ═══════════════════════════════════════════════════════════════════════════════
340
+ // MAIN ENTRY POINT
341
+ // ═══════════════════════════════════════════════════════════════════════════════
342
+
343
+ /**
344
+ * Main packs command handler
345
+ *
346
+ * @param {string[]} args - Command arguments
347
+ * @param {Object} context - Execution context
348
+ * @returns {Promise<number>} Exit code
349
+ */
350
+ async function runPacks(args = [], context = {}) {
351
+ const { parseGlobalFlags, shouldSuppressOutput, isJsonMode } = getGlobalFlags();
352
+ const { EXIT } = getExitCodes();
353
+
354
+ const { flags: globalFlags, cleanArgs } = parseGlobalFlags(args);
355
+ const quiet = shouldSuppressOutput(globalFlags);
356
+ const json = isJsonMode(globalFlags) || args.includes("--json");
357
+ const verbose = args.includes("--verbose") || args.includes("-v");
358
+ const projectRoot = context.repoRoot || globalFlags.path || process.cwd();
359
+
360
+ // Parse subcommand
361
+ const subcommand = cleanArgs[0];
362
+ const subArgs = cleanArgs.slice(1);
363
+
364
+ // Handle help
365
+ if (globalFlags.help || args.includes("--help") || args.includes("-h")) {
366
+ if (!subcommand || ["--help", "-h"].includes(subcommand)) {
367
+ printHelp(verbose);
368
+ return EXIT.SUCCESS;
369
+ }
370
+ }
371
+
372
+ // Create execution context
373
+ const execContext = {
374
+ projectRoot,
375
+ json,
376
+ quiet,
377
+ verbose,
378
+ args: subArgs,
379
+ originalArgs: args,
380
+ startTime: Date.now(),
381
+ };
382
+
383
+ try {
384
+ // Route to appropriate handler
385
+ switch (subcommand) {
386
+ case "bundle":
387
+ return await handleBundle(execContext);
388
+
389
+ case "evidence":
390
+ return await handleEvidenceV2(execContext);
391
+
392
+ case "permissions":
393
+ return await handlePermissionsV2(execContext);
394
+
395
+ case "graph":
396
+ return await handleGraphV2(execContext);
397
+
398
+ case "report":
399
+ return await handleReport(execContext);
400
+
401
+ case "manifest":
402
+ return await handleManifest(execContext);
403
+
404
+ case "launch":
405
+ return await handleLaunch(execContext);
406
+
407
+ case "list":
408
+ return await handleList(execContext);
409
+
410
+ case "cleanup":
411
+ return await handleCleanup(execContext);
412
+
413
+ default:
414
+ // Default to bundle if no subcommand
415
+ if (!subcommand) {
416
+ return await handleBundle({ ...execContext, args: cleanArgs });
417
+ }
418
+
419
+ // If it looks like a flag, pass to bundle
420
+ if (subcommand.startsWith("--")) {
421
+ return await handleBundle({ ...execContext, args: cleanArgs });
422
+ }
423
+
424
+ return handleUnknownSubcommand(subcommand, execContext);
425
+ }
426
+ } catch (error) {
427
+ return handleError(error, execContext);
428
+ }
429
+ }
430
+
431
+ // ═══════════════════════════════════════════════════════════════════════════════
432
+ // SUBCOMMAND HANDLERS
433
+ // ═══════════════════════════════════════════════════════════════════════════════
434
+
435
+ /**
436
+ * Handle evidence subcommand
437
+ */
438
+ async function handleEvidence(ctx) {
439
+ const { EXIT } = getExitCodes();
440
+ const { ansi, renderMinimalHeader, renderSectionHeader, renderSuccess, renderWarning, sym, Spinner } = getCliOutput();
441
+
442
+ const runEvidencePack = loadImplementation("evidencePack");
443
+
444
+ if (!ctx.json && !ctx.quiet) {
445
+ renderMinimalHeader("packs", "free");
446
+ renderSectionHeader("Evidence Pack Generation", sym.pack);
447
+ }
448
+
449
+ if (runEvidencePack) {
450
+ // Delegate to existing implementation
451
+ return await runEvidencePack(ctx.originalArgs);
452
+ }
453
+
454
+ // Fallback implementation
455
+ const spinner = !ctx.quiet && !ctx.json ? new Spinner("Collecting evidence artifacts").start() : null;
456
+
457
+ try {
458
+ const packId = `pack-${Date.now()}-${crypto.randomBytes(4).toString("hex")}`;
459
+ const outputDir = getArgValue(ctx.args, "--output") || getArgValue(ctx.args, "-o") ||
460
+ path.join(ctx.projectRoot, ".vibecheck", "evidence-packs", packId);
461
+
462
+ // Collect artifacts
463
+ const artifacts = await collectArtifacts(ctx.projectRoot, ctx.args);
464
+
465
+ if (artifacts.count === 0) {
466
+ spinner?.warn("No artifacts found");
467
+
468
+ if (ctx.json) {
469
+ console.log(JSON.stringify({ success: false, packId, artifacts: [], message: "No artifacts found" }));
470
+ } else if (!ctx.quiet) {
471
+ renderWarning("No evidence artifacts found");
472
+ console.log(`
473
+ ${ansi.dim}Run verification commands first to generate artifacts:${ansi.reset}
474
+ ${ansi.cyan}vibecheck prove --url http://localhost:3000${ansi.reset}
475
+ ${ansi.cyan}vibecheck reality --url http://localhost:3000${ansi.reset}
476
+ `);
477
+ }
478
+ return EXIT.NOT_FOUND;
479
+ }
480
+
481
+ // Create pack directory
482
+ fs.mkdirSync(outputDir, { recursive: true });
483
+
484
+ // Copy artifacts
485
+ for (const artifact of artifacts.items) {
486
+ const destPath = path.join(outputDir, artifact.type, artifact.name);
487
+ fs.mkdirSync(path.dirname(destPath), { recursive: true });
488
+ fs.copyFileSync(artifact.source, destPath);
489
+ }
490
+
491
+ // Generate manifest
492
+ const manifest = {
493
+ id: packId,
494
+ version: PACKS_VERSION,
495
+ generated: new Date().toISOString(),
496
+ projectRoot: ctx.projectRoot,
497
+ artifacts: artifacts.items.map(a => ({
498
+ type: a.type,
499
+ name: a.name,
500
+ size: a.size,
501
+ hash: a.hash,
502
+ })),
503
+ summary: artifacts.summary,
504
+ };
505
+
506
+ // Add signature if requested
507
+ if (ctx.args.includes("--sign")) {
508
+ manifest.signature = {
509
+ algorithm: "sha256",
510
+ hash: crypto.createHash("sha256").update(JSON.stringify(manifest)).digest("hex"),
511
+ };
512
+ }
513
+
514
+ fs.writeFileSync(path.join(outputDir, "manifest.json"), JSON.stringify(manifest, null, 2));
515
+
516
+ // Generate HTML viewer
517
+ const htmlViewer = generateHtmlViewer(manifest);
518
+ fs.writeFileSync(path.join(outputDir, "index.html"), htmlViewer);
519
+
520
+ spinner?.succeed(`Evidence pack created: ${packId}`);
521
+
522
+ if (ctx.json) {
523
+ console.log(JSON.stringify({
524
+ success: true,
525
+ packId,
526
+ outputDir,
527
+ manifest,
528
+ }, null, 2));
529
+ } else if (!ctx.quiet) {
530
+ console.log(`
531
+ ${ansi.bold}Pack Contents${ansi.reset}
532
+ ${ansi.dim}Videos:${ansi.reset} ${artifacts.summary.videos || 0}
533
+ ${ansi.dim}Traces:${ansi.reset} ${artifacts.summary.traces || 0}
534
+ ${ansi.dim}Screenshots:${ansi.reset} ${artifacts.summary.screenshots || 0}
535
+ ${ansi.dim}HAR Files:${ansi.reset} ${artifacts.summary.har || 0}
536
+ ${ansi.dim}Reports:${ansi.reset} ${artifacts.summary.reports || 0}
537
+
538
+ ${ansi.bold}Output${ansi.reset}
539
+ ${ansi.dim}Directory:${ansi.reset} ${outputDir}
540
+ ${ansi.dim}Viewer:${ansi.reset} ${path.join(outputDir, "index.html")}
541
+ `);
542
+ }
543
+
544
+ return EXIT.SUCCESS;
545
+ } catch (error) {
546
+ spinner?.fail("Failed to create evidence pack");
547
+ throw error;
548
+ }
549
+ }
550
+
551
+ /**
552
+ * Handle report subcommand
553
+ */
554
+ async function handleReport(ctx) {
555
+ const { EXIT } = getExitCodes();
556
+ const { ansi, renderMinimalHeader, renderSectionHeader, sym } = getCliOutput();
557
+
558
+ const runReport = loadImplementation("report");
559
+
560
+ if (!ctx.json && !ctx.quiet) {
561
+ renderMinimalHeader("packs", "free");
562
+ renderSectionHeader("Report Generation", sym.file);
563
+ }
564
+
565
+ if (runReport) {
566
+ // Delegate to existing implementation
567
+ return await runReport(ctx.originalArgs);
568
+ }
569
+
570
+ // Fallback: basic report generation
571
+ const format = getArgValue(ctx.args, "--format") || "html";
572
+ const output = getArgValue(ctx.args, "--output") || getArgValue(ctx.args, "-o");
573
+ const reportType = getArgValue(ctx.args, "--type") || "technical";
574
+
575
+ // Load scan data
576
+ const scanPath = path.join(ctx.projectRoot, ".vibecheck", "scan", "last_scan.json");
577
+ let scanData = { findings: [], summary: {} };
578
+
579
+ if (fs.existsSync(scanPath)) {
580
+ try {
581
+ scanData = JSON.parse(fs.readFileSync(scanPath, "utf-8"));
582
+ } catch {
583
+ // Use defaults
584
+ }
585
+ }
586
+
587
+ const report = {
588
+ type: reportType,
589
+ format,
590
+ generated: new Date().toISOString(),
591
+ version: PACKS_VERSION,
592
+ summary: {
593
+ total: scanData.findings?.length || 0,
594
+ critical: scanData.findings?.filter(f => f.severity === "critical").length || 0,
595
+ high: scanData.findings?.filter(f => f.severity === "high").length || 0,
596
+ medium: scanData.findings?.filter(f => f.severity === "medium").length || 0,
597
+ low: scanData.findings?.filter(f => f.severity === "low").length || 0,
598
+ },
599
+ findings: scanData.findings || [],
600
+ };
601
+
602
+ let outputContent;
603
+ switch (format) {
604
+ case "json":
605
+ outputContent = JSON.stringify(report, null, 2);
606
+ break;
607
+ case "sarif":
608
+ outputContent = generateSarif(report);
609
+ break;
610
+ case "md":
611
+ outputContent = generateMarkdownReport(report);
612
+ break;
613
+ case "html":
614
+ default:
615
+ outputContent = generateHtmlReport(report);
616
+ break;
617
+ }
618
+
619
+ if (output) {
620
+ const outputPath = path.isAbsolute(output) ? output : path.join(ctx.projectRoot, output);
621
+ fs.mkdirSync(path.dirname(outputPath), { recursive: true });
622
+ fs.writeFileSync(outputPath, outputContent);
623
+
624
+ if (ctx.json) {
625
+ console.log(JSON.stringify({ success: true, path: outputPath, format, type: reportType }));
626
+ } else if (!ctx.quiet) {
627
+ console.log(`
628
+ ${ansi.green}✓${ansi.reset} Report generated: ${outputPath}
629
+
630
+ ${ansi.dim}Format: ${format} | Type: ${reportType}${ansi.reset}
631
+ ${ansi.dim}Findings: ${report.summary.total} (${report.summary.critical} critical, ${report.summary.high} high)${ansi.reset}
632
+ `);
633
+
634
+ if (ctx.args.includes("--open")) {
635
+ const open = require("open");
636
+ open(outputPath);
637
+ }
638
+ }
639
+ } else {
640
+ console.log(outputContent);
641
+ }
642
+
643
+ return EXIT.SUCCESS;
644
+ }
645
+
646
+ /**
647
+ * Handle graph subcommand
648
+ */
649
+ async function handleGraph(ctx) {
650
+ const { EXIT } = getExitCodes();
651
+ const { ansi, renderMinimalHeader, renderSectionHeader, renderWarning, sym, Spinner } = getCliOutput();
652
+
653
+ const format = getArgValue(ctx.args, "--format") || "json";
654
+ const output = getArgValue(ctx.args, "--output") || getArgValue(ctx.args, "-o");
655
+ const query = getArgValue(ctx.args, "--query");
656
+ const depth = parseInt(getArgValue(ctx.args, "--depth") || "0", 10);
657
+
658
+ if (!ctx.json && !ctx.quiet) {
659
+ renderMinimalHeader("packs", "pro");
660
+ renderSectionHeader("Proof Graph Generation", sym.chart);
661
+ }
662
+
663
+ const spinner = !ctx.quiet && !ctx.json ? new Spinner("Building proof graph").start() : null;
664
+
665
+ try {
666
+ // Load graph data from various sources
667
+ let graphData = await loadGraphData(ctx.projectRoot);
668
+
669
+ if (!graphData || (graphData.nodes?.length === 0 && graphData.edges?.length === 0)) {
670
+ spinner?.warn("No graph data found");
671
+
672
+ if (ctx.json) {
673
+ console.log(JSON.stringify({
674
+ success: false,
675
+ error: "No proof graph data found",
676
+ hint: "Run vibecheck prove or vibecheck reality first",
677
+ }));
678
+ } else if (!ctx.quiet) {
679
+ renderWarning("No proof graph data found");
680
+ console.log(`
681
+ ${ansi.dim}Run verification commands to generate graph data:${ansi.reset}
682
+ ${ansi.cyan}vibecheck prove --url http://localhost:3000${ansi.reset}
683
+ ${ansi.cyan}vibecheck reality --url http://localhost:3000${ansi.reset}
684
+ `);
685
+ }
686
+ return EXIT.NOT_FOUND;
687
+ }
688
+
689
+ // Apply query filter
690
+ if (query && GRAPH_QUERIES[query]) {
691
+ graphData = filterGraphByQuery(graphData, query);
692
+ if (spinner) spinner.text = `Filtering: ${GRAPH_QUERIES[query]}`;
693
+ }
694
+
695
+ // Apply depth limit
696
+ if (depth > 0) {
697
+ graphData = limitGraphDepth(graphData, depth);
698
+ }
699
+
700
+ // Add metadata
701
+ graphData.metadata = {
702
+ version: PACKS_VERSION,
703
+ generated: new Date().toISOString(),
704
+ query: query || "all",
705
+ depth: depth || "unlimited",
706
+ stats: {
707
+ nodes: graphData.nodes?.length || 0,
708
+ edges: graphData.edges?.length || 0,
709
+ gaps: graphData.gaps?.length || 0,
710
+ },
711
+ };
712
+
713
+ // Format output
714
+ let outputContent;
715
+ switch (format) {
716
+ case "dot":
717
+ outputContent = graphToDot(graphData);
718
+ break;
719
+ case "mermaid":
720
+ outputContent = graphToMermaid(graphData);
721
+ break;
722
+ case "html":
723
+ outputContent = graphToHtml(graphData);
724
+ break;
725
+ case "json":
726
+ default:
727
+ outputContent = JSON.stringify(graphData, null, 2);
728
+ break;
729
+ }
730
+
731
+ spinner?.succeed(`Graph generated (${graphData.metadata.stats.nodes} nodes, ${graphData.metadata.stats.edges} edges)`);
732
+
733
+ // Write or print output
734
+ if (output) {
735
+ const outputPath = path.isAbsolute(output) ? output : path.join(ctx.projectRoot, output);
736
+ fs.mkdirSync(path.dirname(outputPath), { recursive: true });
737
+ fs.writeFileSync(outputPath, outputContent);
738
+
739
+ if (ctx.json) {
740
+ console.log(JSON.stringify({
741
+ success: true,
742
+ path: outputPath,
743
+ format,
744
+ stats: graphData.metadata.stats,
745
+ }));
746
+ } else if (!ctx.quiet) {
747
+ console.log(`
748
+ ${ansi.bold}Graph Statistics${ansi.reset}
749
+ ${ansi.dim}Nodes:${ansi.reset} ${graphData.metadata.stats.nodes}
750
+ ${ansi.dim}Edges:${ansi.reset} ${graphData.metadata.stats.edges}
751
+ ${ansi.dim}Gaps:${ansi.reset} ${graphData.metadata.stats.gaps}
752
+
753
+ ${ansi.bold}Output${ansi.reset}
754
+ ${ansi.dim}Path:${ansi.reset} ${outputPath}
755
+ ${ansi.dim}Format:${ansi.reset} ${format}
756
+ `);
757
+ }
758
+ } else {
759
+ console.log(outputContent);
760
+ }
761
+
762
+ return EXIT.SUCCESS;
763
+ } catch (error) {
764
+ spinner?.fail("Graph generation failed");
765
+ throw error;
766
+ }
767
+ }
768
+
769
+ /**
770
+ * Handle permissions subcommand (placeholder - legacy)
771
+ */
772
+ async function handlePermissions(ctx) {
773
+ // Redirect to V2
774
+ return handlePermissionsV2(ctx);
775
+ }
776
+
777
+ // ═══════════════════════════════════════════════════════════════════════════════
778
+ // V2 HANDLERS
779
+ // ═══════════════════════════════════════════════════════════════════════════════
780
+
781
+ /**
782
+ * Handle bundle subcommand - All-in-one ZIP bundle + manifest + HTML index
783
+ */
784
+ async function handleBundle(ctx) {
785
+ const { EXIT } = getExitCodes();
786
+ const { ansi, renderMinimalHeader, renderSectionHeader, sym } = getCliOutput();
787
+
788
+ const bundlePack = getBundlePack();
789
+
790
+ if (!ctx.json && !ctx.quiet) {
791
+ renderMinimalHeader("packs", "free");
792
+ renderSectionHeader("Unified Bundle Generation (V2)", "📦");
793
+ console.log();
794
+ }
795
+
796
+ if (!bundlePack) {
797
+ if (ctx.json) {
798
+ console.log(JSON.stringify({ success: false, error: "Bundle pack module not available" }));
799
+ } else if (!ctx.quiet) {
800
+ console.log(`${ansi.red}✗${ansi.reset} Bundle pack module not available\n`);
801
+ }
802
+ return EXIT.INTERNAL_ERROR;
803
+ }
804
+
805
+ try {
806
+ const outputDir = getArgValue(ctx.args, "--output") || getArgValue(ctx.args, "-o");
807
+ const noZip = ctx.args.includes("--no-zip");
808
+ const noEvidence = ctx.args.includes("--no-evidence");
809
+ const noPermissions = ctx.args.includes("--no-permissions");
810
+ const noGraph = ctx.args.includes("--no-graph");
811
+ const noReport = ctx.args.includes("--no-report");
812
+
813
+ const result = await bundlePack.buildBundle(ctx.projectRoot, {
814
+ outputDir,
815
+ createZip: !noZip,
816
+ includeEvidence: !noEvidence,
817
+ includePermissions: !noPermissions,
818
+ includeProofGraph: !noGraph,
819
+ includeReport: !noReport,
820
+ });
821
+
822
+ if (ctx.json) {
823
+ console.log(JSON.stringify({
824
+ success: true,
825
+ bundleId: result.bundleId,
826
+ bundleDir: result.bundleDir,
827
+ zipPath: result.zipPath,
828
+ components: Object.keys(result.components).filter(k => result.components[k]),
829
+ manifest: result.manifest,
830
+ }, null, 2));
831
+ } else if (!ctx.quiet) {
832
+ console.log(`\n${ansi.green}✓${ansi.reset} ${ansi.bold}Bundle created successfully!${ansi.reset}\n`);
833
+ console.log(` ${ansi.bold}Bundle ID:${ansi.reset} ${result.bundleId}`);
834
+ console.log(` ${ansi.bold}Directory:${ansi.reset} ${result.bundleDir}`);
835
+ if (result.zipPath) {
836
+ console.log(` ${ansi.bold}ZIP File:${ansi.reset} ${result.zipPath}`);
837
+ }
838
+ console.log(` ${ansi.bold}Index:${ansi.reset} ${path.join(result.bundleDir, "index.html")}`);
839
+
840
+ console.log(`\n ${ansi.bold}Components${ansi.reset}`);
841
+ for (const [name, comp] of Object.entries(result.components)) {
842
+ if (comp) {
843
+ const icon = { evidence: "📸", permissions: "🔐", "proof-graph": "🕸️", report: "📊" }[name] || "📦";
844
+ console.log(` ${icon} ${name}`);
845
+ }
846
+ }
847
+
848
+ if (result.manifest.receipts?.ship) {
849
+ console.log(`\n ${ansi.bold}Cross-References${ansi.reset}`);
850
+ console.log(` Ship: ${result.manifest.receipts.ship.verdict || "N/A"}`);
851
+ }
852
+ if (result.manifest.receipts?.reality) {
853
+ console.log(` Reality: ${result.manifest.receipts.reality.verdict || "N/A"}`);
854
+ }
855
+
856
+ console.log(`\n ${ansi.dim}Open index.html in a browser to view the bundle${ansi.reset}\n`);
857
+ }
858
+
859
+ return EXIT.SUCCESS;
860
+ } catch (error) {
861
+ if (ctx.json) {
862
+ console.log(JSON.stringify({ success: false, error: error.message }));
863
+ } else if (!ctx.quiet) {
864
+ console.log(`\n${ansi.red}✗${ansi.reset} Bundle creation failed: ${error.message}\n`);
865
+ }
866
+ return EXIT.INTERNAL_ERROR;
867
+ }
868
+ }
869
+
870
+ /**
871
+ * Handle evidence subcommand V2 - Enhanced with pack factory
872
+ */
873
+ async function handleEvidenceV2(ctx) {
874
+ const { EXIT } = getExitCodes();
875
+ const { ansi, renderMinimalHeader, renderSectionHeader, sym } = getCliOutput();
876
+
877
+ const evidencePack = getEvidencePackV2();
878
+
879
+ if (!ctx.json && !ctx.quiet) {
880
+ renderMinimalHeader("packs", "free");
881
+ renderSectionHeader("Evidence Pack Generation", sym.pack);
882
+ console.log();
883
+ }
884
+
885
+ if (!evidencePack) {
886
+ // Fall back to legacy handler
887
+ return await handleEvidence(ctx);
888
+ }
889
+
890
+ try {
891
+ const outputDir = getArgValue(ctx.args, "--output") || getArgValue(ctx.args, "-o");
892
+ const createZip = ctx.args.includes("--zip");
893
+ const runId = getArgValue(ctx.args, "--run-id");
894
+
895
+ const result = await evidencePack.buildEvidencePack(ctx.projectRoot, {
896
+ outputDir,
897
+ createZip,
898
+ runId,
899
+ });
900
+
901
+ if (ctx.json) {
902
+ console.log(JSON.stringify({
903
+ success: true,
904
+ packId: result.packId,
905
+ outputDir: result.outputDir,
906
+ artifactCount: result.artifactCount,
907
+ manifest: result.manifest,
908
+ }, null, 2));
909
+ } else if (!ctx.quiet) {
910
+ console.log(`${ansi.green}✓${ansi.reset} Evidence pack created: ${result.packId}\n`);
911
+ console.log(` ${ansi.bold}Artifacts${ansi.reset}`);
912
+ console.log(` Screenshots: ${result.manifest.metadata.screenshotCount}`);
913
+ console.log(` Videos: ${result.manifest.metadata.videoCount}`);
914
+ console.log(` Traces: ${result.manifest.metadata.traceCount}`);
915
+ console.log(` Findings: ${result.manifest.metadata.findingCount}`);
916
+ console.log(`\n ${ansi.bold}Output${ansi.reset}`);
917
+ console.log(` Directory: ${result.outputDir}`);
918
+ console.log(` Gallery: ${path.join(result.outputDir, "gallery.html")}\n`);
919
+ }
920
+
921
+ return EXIT.SUCCESS;
922
+ } catch (error) {
923
+ if (ctx.json) {
924
+ console.log(JSON.stringify({ success: false, error: error.message }));
925
+ } else if (!ctx.quiet) {
926
+ console.log(`\n${ansi.red}✗${ansi.reset} Evidence pack failed: ${error.message}\n`);
927
+ }
928
+ return EXIT.INTERNAL_ERROR;
929
+ }
930
+ }
931
+
932
+ /**
933
+ * Handle permissions subcommand V2 - AuthZ matrix extraction
934
+ */
935
+ async function handlePermissionsV2(ctx) {
936
+ const { EXIT } = getExitCodes();
937
+ const { ansi, renderMinimalHeader, renderSectionHeader, sym, Spinner } = getCliOutput();
938
+
939
+ const permissionsPack = getPermissionsPack();
940
+
941
+ if (!ctx.json && !ctx.quiet) {
942
+ renderMinimalHeader("packs", "free");
943
+ renderSectionHeader("Permissions Pack Generation", "🔐");
944
+ console.log();
945
+ }
946
+
947
+ if (!permissionsPack) {
948
+ if (ctx.json) {
949
+ console.log(JSON.stringify({ success: false, error: "Permissions pack module not available" }));
950
+ } else if (!ctx.quiet) {
951
+ console.log(`${ansi.red}✗${ansi.reset} Permissions pack module not available\n`);
952
+ }
953
+ return EXIT.INTERNAL_ERROR;
954
+ }
955
+
956
+ const spinner = !ctx.quiet && !ctx.json ? new Spinner("Scanning codebase for permissions").start() : null;
957
+
958
+ try {
959
+ const outputDir = getArgValue(ctx.args, "--output") || getArgValue(ctx.args, "-o");
960
+ const createZip = ctx.args.includes("--zip");
961
+
962
+ const result = await permissionsPack.buildPermissionsPack(ctx.projectRoot, {
963
+ outputDir,
964
+ createZip,
965
+ });
966
+
967
+ spinner?.succeed("Permissions pack created");
968
+
969
+ if (ctx.json) {
970
+ console.log(JSON.stringify({
971
+ success: true,
972
+ packId: result.packId,
973
+ outputDir: result.outputDir,
974
+ metadata: result.manifest.metadata,
975
+ }, null, 2));
976
+ } else if (!ctx.quiet) {
977
+ console.log(`\n ${ansi.bold}AuthZ Matrix${ansi.reset}`);
978
+ console.log(` Roles: ${result.manifest.metadata.rolesCount}`);
979
+ console.log(` Permissions: ${result.manifest.metadata.permissionsCount}`);
980
+ console.log(` Protected Routes: ${result.manifest.metadata.protectedRoutesCount}`);
981
+ console.log(` Entitlements: ${result.manifest.metadata.entitlementsCount}`);
982
+ console.log(` Files Scanned: ${result.manifest.metadata.filesScanned}`);
983
+ console.log(`\n ${ansi.bold}Output${ansi.reset}`);
984
+ console.log(` Directory: ${result.outputDir}`);
985
+ console.log(` Matrix: ${path.join(result.outputDir, "matrix.html")}`);
986
+ console.log(` Report: ${path.join(result.outputDir, "permissions-report.md")}\n`);
987
+ }
988
+
989
+ return EXIT.SUCCESS;
990
+ } catch (error) {
991
+ spinner?.fail("Permissions pack failed");
992
+ if (ctx.json) {
993
+ console.log(JSON.stringify({ success: false, error: error.message }));
994
+ } else if (!ctx.quiet) {
995
+ console.log(`\n${ansi.red}✗${ansi.reset} Permissions pack failed: ${error.message}\n`);
996
+ }
997
+ return EXIT.INTERNAL_ERROR;
998
+ }
999
+ }
1000
+
1001
+ /**
1002
+ * Handle graph subcommand V2 - Proof graph with receipt cross-links
1003
+ */
1004
+ async function handleGraphV2(ctx) {
1005
+ const { EXIT } = getExitCodes();
1006
+ const { ansi, renderMinimalHeader, renderSectionHeader, sym, Spinner } = getCliOutput();
1007
+
1008
+ const proofGraphPack = getProofGraphPack();
1009
+
1010
+ if (!ctx.json && !ctx.quiet) {
1011
+ renderMinimalHeader("packs", "free");
1012
+ renderSectionHeader("Proof Graph Pack Generation", "🕸️");
1013
+ console.log();
1014
+ }
1015
+
1016
+ if (!proofGraphPack) {
1017
+ // Fall back to legacy handler
1018
+ return await handleGraph(ctx);
1019
+ }
1020
+
1021
+ const spinner = !ctx.quiet && !ctx.json ? new Spinner("Building proof graph").start() : null;
1022
+
1023
+ try {
1024
+ const outputDir = getArgValue(ctx.args, "--output") || getArgValue(ctx.args, "-o");
1025
+ const createZip = ctx.args.includes("--zip");
1026
+
1027
+ const result = await proofGraphPack.buildProofGraphPack(ctx.projectRoot, {
1028
+ outputDir,
1029
+ createZip,
1030
+ });
1031
+
1032
+ spinner?.succeed("Proof graph pack created");
1033
+
1034
+ if (ctx.json) {
1035
+ console.log(JSON.stringify({
1036
+ success: true,
1037
+ packId: result.packId,
1038
+ outputDir: result.outputDir,
1039
+ metadata: result.manifest.metadata,
1040
+ }, null, 2));
1041
+ } else if (!ctx.quiet) {
1042
+ console.log(`\n ${ansi.bold}Graph Statistics${ansi.reset}`);
1043
+ console.log(` Nodes: ${result.manifest.metadata.nodeCount}`);
1044
+ console.log(` Edges: ${result.manifest.metadata.edgeCount}`);
1045
+ console.log(`\n ${ansi.bold}Receipt Cross-Links${ansi.reset}`);
1046
+ console.log(` Ship Receipt: ${result.manifest.metadata.hasShipReceipt ? "✓" : "✗"}`);
1047
+ console.log(` Reality Receipt: ${result.manifest.metadata.hasRealityReceipt ? "✓" : "✗"}`);
1048
+ console.log(` Scan Receipt: ${result.manifest.metadata.hasScanReceipt ? "✓" : "✗"}`);
1049
+ console.log(` Proof Runs: ${result.manifest.metadata.proofRunCount}`);
1050
+ console.log(`\n ${ansi.bold}Output${ansi.reset}`);
1051
+ console.log(` Directory: ${result.outputDir}`);
1052
+ console.log(` Visualization: ${path.join(result.outputDir, "visualization.html")}`);
1053
+ console.log(` DOT Format: ${path.join(result.outputDir, "proof-graph.dot")}`);
1054
+ console.log(` Mermaid: ${path.join(result.outputDir, "proof-graph.mmd")}\n`);
1055
+ }
1056
+
1057
+ return EXIT.SUCCESS;
1058
+ } catch (error) {
1059
+ spinner?.fail("Proof graph pack failed");
1060
+ if (ctx.json) {
1061
+ console.log(JSON.stringify({ success: false, error: error.message }));
1062
+ } else if (!ctx.quiet) {
1063
+ console.log(`\n${ansi.red}✗${ansi.reset} Proof graph pack failed: ${error.message}\n`);
1064
+ }
1065
+ return EXIT.INTERNAL_ERROR;
1066
+ }
1067
+ }
1068
+
1069
+ /**
1070
+ * Handle list subcommand - List existing packs and bundles
1071
+ */
1072
+ async function handleList(ctx) {
1073
+ const { EXIT } = getExitCodes();
1074
+ const { ansi, renderMinimalHeader, renderSectionHeader, sym } = getCliOutput();
1075
+
1076
+ const bundlePack = getBundlePack();
1077
+ const packFactory = getPackFactory();
1078
+
1079
+ if (!ctx.json && !ctx.quiet) {
1080
+ renderMinimalHeader("packs", "free");
1081
+ renderSectionHeader("Available Packs", sym.list);
1082
+ console.log();
1083
+ }
1084
+
1085
+ const result = {
1086
+ bundles: [],
1087
+ packs: [],
1088
+ };
1089
+
1090
+ // List bundles
1091
+ if (bundlePack && bundlePack.listBundles) {
1092
+ result.bundles = bundlePack.listBundles(ctx.projectRoot);
1093
+ }
1094
+
1095
+ // List individual packs
1096
+ const packsDir = path.join(ctx.projectRoot, ".vibecheck/packs");
1097
+ if (fs.existsSync(packsDir)) {
1098
+ const dirs = fs.readdirSync(packsDir, { withFileTypes: true })
1099
+ .filter(d => d.isDirectory())
1100
+ .map(d => d.name);
1101
+
1102
+ for (const dir of dirs) {
1103
+ const manifestPath = path.join(packsDir, dir, "manifest.json");
1104
+ if (fs.existsSync(manifestPath)) {
1105
+ try {
1106
+ const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf-8"));
1107
+ result.packs.push({
1108
+ id: manifest.id,
1109
+ type: manifest.type,
1110
+ generatedAt: manifest.generatedAt,
1111
+ artifactCount: manifest.summary?.artifactCount || 0,
1112
+ });
1113
+ } catch (e) {
1114
+ // Skip invalid
1115
+ }
1116
+ }
1117
+ }
1118
+ }
1119
+
1120
+ if (ctx.json) {
1121
+ console.log(JSON.stringify(result, null, 2));
1122
+ } else if (!ctx.quiet) {
1123
+ console.log(` ${ansi.bold}Bundles${ansi.reset} (${result.bundles.length})`);
1124
+ if (result.bundles.length === 0) {
1125
+ console.log(` ${ansi.dim}No bundles found${ansi.reset}`);
1126
+ } else {
1127
+ for (const bundle of result.bundles.slice(0, 5)) {
1128
+ const time = bundle.generatedAt ? new Date(bundle.generatedAt).toLocaleString() : "?";
1129
+ const verdict = bundle.verdict ? ` [${bundle.verdict}]` : "";
1130
+ console.log(` ${ansi.cyan}${bundle.id}${ansi.reset}${verdict}`);
1131
+ console.log(` ${ansi.dim}${time} • ${bundle.componentCount} components${ansi.reset}`);
1132
+ }
1133
+ }
1134
+
1135
+ console.log(`\n ${ansi.bold}Packs${ansi.reset} (${result.packs.length})`);
1136
+ if (result.packs.length === 0) {
1137
+ console.log(` ${ansi.dim}No packs found${ansi.reset}`);
1138
+ } else {
1139
+ for (const pack of result.packs.slice(0, 10)) {
1140
+ const time = pack.generatedAt ? new Date(pack.generatedAt).toLocaleString() : "?";
1141
+ console.log(` ${ansi.cyan}${pack.id}${ansi.reset} [${pack.type}]`);
1142
+ console.log(` ${ansi.dim}${time} • ${pack.artifactCount} artifacts${ansi.reset}`);
1143
+ }
1144
+ }
1145
+ console.log();
1146
+ }
1147
+
1148
+ return EXIT.SUCCESS;
1149
+ }
1150
+
1151
+ /**
1152
+ * Handle cleanup subcommand - Remove old packs
1153
+ */
1154
+ async function handleCleanup(ctx) {
1155
+ const { EXIT } = getExitCodes();
1156
+ const { ansi, renderMinimalHeader, renderSectionHeader, sym } = getCliOutput();
1157
+
1158
+ const bundlePack = getBundlePack();
1159
+
1160
+ if (!ctx.json && !ctx.quiet) {
1161
+ renderMinimalHeader("packs", "free");
1162
+ renderSectionHeader("Cleanup Old Packs", "🧹");
1163
+ console.log();
1164
+ }
1165
+
1166
+ let removed = 0;
1167
+
1168
+ // Cleanup bundles
1169
+ if (bundlePack && bundlePack.cleanupBundles) {
1170
+ const maxCount = parseInt(getArgValue(ctx.args, "--keep") || "5", 10);
1171
+ const maxAgeDays = parseInt(getArgValue(ctx.args, "--max-age") || "30", 10);
1172
+
1173
+ removed = bundlePack.cleanupBundles(ctx.projectRoot, { maxCount, maxAgeDays });
1174
+ }
1175
+
1176
+ // Cleanup individual packs
1177
+ const packsDir = path.join(ctx.projectRoot, ".vibecheck/packs");
1178
+ if (fs.existsSync(packsDir)) {
1179
+ const dirs = fs.readdirSync(packsDir, { withFileTypes: true })
1180
+ .filter(d => d.isDirectory())
1181
+ .map(d => d.name)
1182
+ .sort()
1183
+ .reverse();
1184
+
1185
+ // Keep 10 most recent
1186
+ for (const dir of dirs.slice(10)) {
1187
+ try {
1188
+ fs.rmSync(path.join(packsDir, dir), { recursive: true, force: true });
1189
+ removed++;
1190
+ } catch (e) {
1191
+ // Skip
1192
+ }
1193
+ }
1194
+ }
1195
+
1196
+ if (ctx.json) {
1197
+ console.log(JSON.stringify({ success: true, removed }));
1198
+ } else if (!ctx.quiet) {
1199
+ if (removed > 0) {
1200
+ console.log(` ${ansi.green}✓${ansi.reset} Removed ${removed} old packs\n`);
1201
+ } else {
1202
+ console.log(` ${ansi.dim}No packs to clean up${ansi.reset}\n`);
1203
+ }
1204
+ }
1205
+
1206
+ return EXIT.SUCCESS;
1207
+ }
1208
+
1209
+ /**
1210
+ * Handle manifest subcommand - list all artifacts
1211
+ */
1212
+ async function handleManifest(ctx) {
1213
+ const { EXIT } = getExitCodes();
1214
+ const { ansi, renderMinimalHeader, renderSectionHeader, sym } = getCliOutput();
1215
+
1216
+ if (!ctx.json && !ctx.quiet) {
1217
+ renderMinimalHeader("packs", "free");
1218
+ renderSectionHeader("Artifact Manifest", sym.list);
1219
+ }
1220
+
1221
+ const artifacts = {
1222
+ scans: [],
1223
+ evidence: [],
1224
+ reports: [],
1225
+ graphs: [],
1226
+ };
1227
+
1228
+ // Collect scans
1229
+ const scansDir = path.join(ctx.projectRoot, ".vibecheck", "scan");
1230
+ if (fs.existsSync(scansDir)) {
1231
+ const files = fs.readdirSync(scansDir).filter(f => f.endsWith(".json"));
1232
+ artifacts.scans = files.map(f => ({
1233
+ name: f,
1234
+ path: path.join(scansDir, f),
1235
+ modified: fs.statSync(path.join(scansDir, f)).mtime,
1236
+ }));
1237
+ }
1238
+
1239
+ // Collect evidence packs
1240
+ const packsDir = path.join(ctx.projectRoot, ".vibecheck", "evidence-packs");
1241
+ if (fs.existsSync(packsDir)) {
1242
+ const dirs = fs.readdirSync(packsDir).filter(d =>
1243
+ fs.statSync(path.join(packsDir, d)).isDirectory()
1244
+ );
1245
+ artifacts.evidence = dirs.map(d => {
1246
+ const manifestPath = path.join(packsDir, d, "manifest.json");
1247
+ let manifest = null;
1248
+ if (fs.existsSync(manifestPath)) {
1249
+ try {
1250
+ manifest = JSON.parse(fs.readFileSync(manifestPath, "utf-8"));
1251
+ } catch {
1252
+ // Skip invalid manifests
1253
+ }
1254
+ }
1255
+ return {
1256
+ id: d,
1257
+ path: path.join(packsDir, d),
1258
+ manifest,
1259
+ };
1260
+ });
1261
+ }
1262
+
1263
+ // Collect reports
1264
+ const reportsDir = path.join(ctx.projectRoot, ".vibecheck", "reports");
1265
+ if (fs.existsSync(reportsDir)) {
1266
+ const files = fs.readdirSync(reportsDir);
1267
+ artifacts.reports = files.map(f => ({
1268
+ name: f,
1269
+ path: path.join(reportsDir, f),
1270
+ modified: fs.statSync(path.join(reportsDir, f)).mtime,
1271
+ }));
1272
+ }
1273
+
1274
+ const manifest = {
1275
+ version: PACKS_VERSION,
1276
+ generated: new Date().toISOString(),
1277
+ projectRoot: ctx.projectRoot,
1278
+ artifacts,
1279
+ summary: {
1280
+ scans: artifacts.scans.length,
1281
+ evidence: artifacts.evidence.length,
1282
+ reports: artifacts.reports.length,
1283
+ graphs: artifacts.graphs.length,
1284
+ total: artifacts.scans.length + artifacts.evidence.length + artifacts.reports.length + artifacts.graphs.length,
1285
+ },
1286
+ };
1287
+
1288
+ if (ctx.json) {
1289
+ console.log(JSON.stringify(manifest, null, 2));
1290
+ } else if (!ctx.quiet) {
1291
+ console.log(`
1292
+ ${ansi.bold}Artifact Summary${ansi.reset}
1293
+ ${ansi.dim}Scans:${ansi.reset} ${manifest.summary.scans}
1294
+ ${ansi.dim}Evidence Packs:${ansi.reset} ${manifest.summary.evidence}
1295
+ ${ansi.dim}Reports:${ansi.reset} ${manifest.summary.reports}
1296
+ ${ansi.dim}Graphs:${ansi.reset} ${manifest.summary.graphs}
1297
+ ${ansi.dim}Total:${ansi.reset} ${manifest.summary.total}
1298
+ `);
1299
+
1300
+ if (artifacts.evidence.length > 0) {
1301
+ console.log(` ${ansi.bold}Recent Evidence Packs${ansi.reset}`);
1302
+ for (const pack of artifacts.evidence.slice(-5)) {
1303
+ const time = pack.manifest?.generated ? new Date(pack.manifest.generated).toLocaleString() : "?";
1304
+ console.log(` ${ansi.dim}${pack.id}${ansi.reset} - ${time}`);
1305
+ }
1306
+ console.log();
1307
+ }
1308
+
1309
+ if (artifacts.scans.length > 0) {
1310
+ console.log(` ${ansi.bold}Recent Scans${ansi.reset}`);
1311
+ for (const scan of artifacts.scans.slice(-5)) {
1312
+ const time = scan.modified ? new Date(scan.modified).toLocaleString() : "?";
1313
+ console.log(` ${ansi.dim}${scan.name}${ansi.reset} - ${time}`);
1314
+ }
1315
+ console.log();
1316
+ }
1317
+ }
1318
+
1319
+ return EXIT.SUCCESS;
1320
+ }
1321
+
1322
+ /**
1323
+ * Handle launch subcommand - bundle launch validation results
1324
+ */
1325
+ async function handleLaunch(ctx) {
1326
+ const { EXIT } = getExitCodes();
1327
+ const { ansi, renderMinimalHeader, renderSectionHeader, sym } = getCliOutput();
1328
+
1329
+ if (!ctx.json && !ctx.quiet) {
1330
+ renderMinimalHeader("packs", "free");
1331
+ renderSectionHeader("Launch Pack Bundle", "🚀");
1332
+ }
1333
+
1334
+ const launchDir = path.join(ctx.projectRoot, ".vibecheck", "launch");
1335
+
1336
+ if (!fs.existsSync(launchDir)) {
1337
+ if (ctx.json) {
1338
+ console.log(JSON.stringify({
1339
+ success: false,
1340
+ error: "No launch results found. Run 'vibecheck launch' first."
1341
+ }));
1342
+ } else if (!ctx.quiet) {
1343
+ console.log(`\n ${ansi.yellow}No launch results found.${ansi.reset}`);
1344
+ console.log(` Run ${ansi.cyan}vibecheck launch${ansi.reset} first to generate results.\n`);
1345
+ }
1346
+ return EXIT.NOT_FOUND;
1347
+ }
1348
+
1349
+ // Find the most recent launch results
1350
+ const files = fs.readdirSync(launchDir)
1351
+ .filter(f => f.startsWith("launch-") && f.endsWith(".json"))
1352
+ .sort()
1353
+ .reverse();
1354
+
1355
+ if (files.length === 0) {
1356
+ if (ctx.json) {
1357
+ console.log(JSON.stringify({
1358
+ success: false,
1359
+ error: "No launch results found."
1360
+ }));
1361
+ } else if (!ctx.quiet) {
1362
+ console.log(`\n ${ansi.yellow}No launch results found.${ansi.reset}\n`);
1363
+ }
1364
+ return EXIT.NOT_FOUND;
1365
+ }
1366
+
1367
+ const latestFile = files[0];
1368
+ const format = getArgValue(ctx.args, "--format") || "zip";
1369
+ const outputPath = getArgValue(ctx.args, "--output") || getArgValue(ctx.args, "-o");
1370
+
1371
+ // Collect all launch artifacts
1372
+ const artifacts = [
1373
+ { name: latestFile, path: path.join(launchDir, latestFile), type: "results" },
1374
+ ];
1375
+
1376
+ // Add other standard launch files if they exist
1377
+ const standardFiles = ["blockers.json", "checklist.md", "audit-trail.json", "manifest.json"];
1378
+ for (const file of standardFiles) {
1379
+ const filePath = path.join(launchDir, file);
1380
+ if (fs.existsSync(filePath)) {
1381
+ artifacts.push({ name: file, path: filePath, type: file.replace(/\.[^.]+$/, "") });
1382
+ }
1383
+ }
1384
+
1385
+ if (format === "json") {
1386
+ // Output as JSON listing
1387
+ const result = {
1388
+ success: true,
1389
+ packId: latestFile.replace(/\.json$/, ""),
1390
+ artifacts: artifacts.map(a => ({ name: a.name, type: a.type })),
1391
+ launchDir,
1392
+ };
1393
+ console.log(JSON.stringify(result, null, 2));
1394
+ return EXIT.SUCCESS;
1395
+ }
1396
+
1397
+ if (format === "md") {
1398
+ // Output the checklist markdown
1399
+ const checklistPath = path.join(launchDir, "checklist.md");
1400
+ if (fs.existsSync(checklistPath)) {
1401
+ console.log(fs.readFileSync(checklistPath, "utf-8"));
1402
+ } else {
1403
+ console.log("# Launch Checklist\n\nNo checklist generated yet.");
1404
+ }
1405
+ return EXIT.SUCCESS;
1406
+ }
1407
+
1408
+ // Default: ZIP bundle
1409
+ const timestamp = new Date().toISOString().replace(/[:.]/g, "-").slice(0, 19);
1410
+ const zipName = outputPath || `launch-pack-${timestamp}.zip`;
1411
+ const zipPath = path.resolve(ctx.projectRoot, zipName);
1412
+
1413
+ try {
1414
+ // Create a simple zip using Node.js (no external deps)
1415
+ const archiver = require("archiver");
1416
+ const output = fs.createWriteStream(zipPath);
1417
+ const archive = archiver("zip", { zlib: { level: 9 } });
1418
+
1419
+ archive.pipe(output);
1420
+
1421
+ for (const artifact of artifacts) {
1422
+ archive.file(artifact.path, { name: artifact.name });
1423
+ }
1424
+
1425
+ await archive.finalize();
1426
+
1427
+ if (!ctx.quiet) {
1428
+ console.log(`\n ${ansi.green}✓${ansi.reset} Created: ${ansi.cyan}${zipName}${ansi.reset}`);
1429
+ console.log(` • ${artifacts.length} files bundled`);
1430
+ console.log(` • ${artifacts.map(a => a.name).join(", ")}`);
1431
+ console.log();
1432
+ }
1433
+
1434
+ if (ctx.json) {
1435
+ console.log(JSON.stringify({
1436
+ success: true,
1437
+ path: zipPath,
1438
+ artifacts: artifacts.length,
1439
+ files: artifacts.map(a => a.name),
1440
+ }));
1441
+ }
1442
+
1443
+ return EXIT.SUCCESS;
1444
+ } catch (err) {
1445
+ // Fallback: just copy files to a directory if archiver not available
1446
+ const bundleDir = outputPath || path.join(ctx.projectRoot, `launch-pack-${timestamp}`);
1447
+ fs.mkdirSync(bundleDir, { recursive: true });
1448
+
1449
+ for (const artifact of artifacts) {
1450
+ fs.copyFileSync(artifact.path, path.join(bundleDir, artifact.name));
1451
+ }
1452
+
1453
+ if (!ctx.quiet) {
1454
+ console.log(`\n ${ansi.green}✓${ansi.reset} Created bundle directory: ${ansi.cyan}${bundleDir}${ansi.reset}`);
1455
+ console.log(` • ${artifacts.length} files copied`);
1456
+ console.log();
1457
+ }
1458
+
1459
+ return EXIT.SUCCESS;
1460
+ }
1461
+ }
1462
+
1463
+ // ═══════════════════════════════════════════════════════════════════════════════
1464
+ // ERROR HANDLING
1465
+ // ═══════════════════════════════════════════════════════════════════════════════
1466
+
1467
+ function handleUnknownSubcommand(subcommand, ctx) {
1468
+ const { EXIT } = getExitCodes();
1469
+ const { ansi } = getCliOutput();
1470
+
1471
+ if (ctx.json) {
1472
+ console.log(JSON.stringify({
1473
+ success: false,
1474
+ error: `Unknown subcommand: ${subcommand}`,
1475
+ available: Object.keys(SUBCOMMANDS),
1476
+ }));
1477
+ } else {
1478
+ console.log(`
1479
+ ${ansi.red}Error: Unknown subcommand '${subcommand}'${ansi.reset}
1480
+
1481
+ ${ansi.dim}Available subcommands:${ansi.reset}
1482
+ ${Object.keys(SUBCOMMANDS).map(s => ` ${ansi.cyan}${s}${ansi.reset}`).join("\n")}
1483
+
1484
+ Run ${ansi.cyan}vibecheck packs --help${ansi.reset} for usage.
1485
+ `);
1486
+ }
1487
+
1488
+ return EXIT.USER_ERROR;
1489
+ }
1490
+
1491
+ function handleError(error, ctx) {
1492
+ const { EXIT } = getExitCodes();
1493
+ const { ansi, renderError } = getCliOutput();
1494
+
1495
+ if (ctx.json) {
1496
+ console.log(JSON.stringify({
1497
+ success: false,
1498
+ error: error.message,
1499
+ stack: ctx.verbose ? error.stack : undefined,
1500
+ }));
1501
+ } else {
1502
+ renderError(`Packs error: ${error.message}`);
1503
+ if (ctx.verbose) {
1504
+ console.log(`\n${ansi.dim}${error.stack}${ansi.reset}\n`);
1505
+ }
1506
+ }
1507
+
1508
+ return EXIT.INTERNAL_ERROR;
1509
+ }
1510
+
1511
+ // ═══════════════════════════════════════════════════════════════════════════════
1512
+ // UTILITY FUNCTIONS
1513
+ // ═══════════════════════════════════════════════════════════════════════════════
1514
+
1515
+ function getArgValue(args, flag) {
1516
+ const index = args.indexOf(flag);
1517
+ if (index !== -1 && args[index + 1] && !args[index + 1].startsWith("--")) {
1518
+ return args[index + 1];
1519
+ }
1520
+ return null;
1521
+ }
1522
+
1523
+ async function collectArtifacts(projectRoot, args) {
1524
+ const artifacts = { items: [], summary: {}, count: 0 };
1525
+ const includeVideos = !args.includes("--no-videos");
1526
+ const includeTraces = !args.includes("--no-traces");
1527
+ const includeHar = !args.includes("--no-har");
1528
+
1529
+ // Common artifact locations
1530
+ const locations = [
1531
+ { type: "videos", dir: ".vibecheck/prove/videos", enabled: includeVideos },
1532
+ { type: "traces", dir: ".vibecheck/prove/traces", enabled: includeTraces },
1533
+ { type: "screenshots", dir: ".vibecheck/prove/screenshots", enabled: true },
1534
+ { type: "har", dir: ".vibecheck/prove/har", enabled: includeHar },
1535
+ { type: "reports", dir: ".vibecheck/scan", enabled: true },
1536
+ ];
1537
+
1538
+ for (const loc of locations) {
1539
+ if (!loc.enabled) continue;
1540
+
1541
+ const dirPath = path.join(projectRoot, loc.dir);
1542
+ if (!fs.existsSync(dirPath)) continue;
1543
+
1544
+ const files = fs.readdirSync(dirPath);
1545
+ artifacts.summary[loc.type] = files.length;
1546
+
1547
+ for (const file of files) {
1548
+ const filePath = path.join(dirPath, file);
1549
+ const stats = fs.statSync(filePath);
1550
+ if (stats.isFile()) {
1551
+ const hash = crypto.createHash("md5")
1552
+ .update(fs.readFileSync(filePath))
1553
+ .digest("hex")
1554
+ .slice(0, 8);
1555
+
1556
+ artifacts.items.push({
1557
+ type: loc.type,
1558
+ name: file,
1559
+ source: filePath,
1560
+ size: stats.size,
1561
+ hash,
1562
+ });
1563
+ artifacts.count++;
1564
+ }
1565
+ }
1566
+ }
1567
+
1568
+ return artifacts;
1569
+ }
1570
+
1571
+ async function loadGraphData(projectRoot) {
1572
+ // Try various sources in order of preference
1573
+ const sources = [
1574
+ ".vibecheck/proof-graph.json",
1575
+ ".vibecheck/reality/last_reality.json",
1576
+ ".vibecheck/prove/last_prove.json",
1577
+ ".vibecheck/scan/last_scan.json",
1578
+ ];
1579
+
1580
+ for (const source of sources) {
1581
+ const sourcePath = path.join(projectRoot, source);
1582
+ if (fs.existsSync(sourcePath)) {
1583
+ try {
1584
+ const data = JSON.parse(fs.readFileSync(sourcePath, "utf-8"));
1585
+ if (data.proofGraph) return data.proofGraph;
1586
+ if (data.nodes && data.edges) return data;
1587
+
1588
+ // Build from scan data
1589
+ if (data.findings) {
1590
+ return buildGraphFromScan(data);
1591
+ }
1592
+ } catch {
1593
+ continue;
1594
+ }
1595
+ }
1596
+ }
1597
+
1598
+ return null;
1599
+ }
1600
+
1601
+ function buildGraphFromScan(scanData) {
1602
+ const nodes = [];
1603
+ const edges = [];
1604
+ const gaps = [];
1605
+ const nodeIds = new Set();
1606
+
1607
+ for (const finding of (scanData.findings || [])) {
1608
+ if (finding.file && !nodeIds.has(`file:${finding.file}`)) {
1609
+ nodeIds.add(`file:${finding.file}`);
1610
+ nodes.push({
1611
+ id: `file:${finding.file}`,
1612
+ type: "file",
1613
+ label: path.basename(finding.file),
1614
+ path: finding.file,
1615
+ });
1616
+ }
1617
+
1618
+ if (finding.ruleId?.includes("ROUTE") || finding.ruleId?.includes("GHOST")) {
1619
+ gaps.push({
1620
+ type: "finding",
1621
+ ruleId: finding.ruleId,
1622
+ evidence: finding,
1623
+ });
1624
+ }
1625
+ }
1626
+
1627
+ return { nodes, edges, gaps, generatedFrom: "scan" };
1628
+ }
1629
+
1630
+ function filterGraphByQuery(graph, query) {
1631
+ switch (query) {
1632
+ case "unexecuted":
1633
+ return {
1634
+ ...graph,
1635
+ nodes: graph.nodes?.filter(n => !n.executed) || [],
1636
+ edges: graph.edges?.filter(e => {
1637
+ const nodes = graph.nodes?.filter(n => !n.executed).map(n => n.id) || [];
1638
+ return nodes.includes(e.source) || nodes.includes(e.target);
1639
+ }) || [],
1640
+ };
1641
+ case "unhit-routes":
1642
+ return {
1643
+ ...graph,
1644
+ nodes: graph.nodes?.filter(n => n.type === "route" && !n.hit) || [],
1645
+ };
1646
+ case "dead-ui":
1647
+ return {
1648
+ ...graph,
1649
+ nodes: graph.nodes?.filter(n => n.type === "ui" && n.dead) || [],
1650
+ };
1651
+ case "gaps":
1652
+ return {
1653
+ ...graph,
1654
+ nodes: graph.nodes?.filter(n => n.gap) || [],
1655
+ gaps: graph.gaps || [],
1656
+ };
1657
+ default:
1658
+ return graph;
1659
+ }
1660
+ }
1661
+
1662
+ function limitGraphDepth(graph, depth) {
1663
+ // Simple depth limiting - could be more sophisticated
1664
+ return {
1665
+ ...graph,
1666
+ nodes: graph.nodes?.slice(0, depth * 10) || [],
1667
+ edges: graph.edges?.slice(0, depth * 15) || [],
1668
+ };
1669
+ }
1670
+
1671
+ // ═══════════════════════════════════════════════════════════════════════════════
1672
+ // OUTPUT FORMATTERS
1673
+ // ═══════════════════════════════════════════════════════════════════════════════
1674
+
1675
+ function graphToDot(graph) {
1676
+ let dot = `digraph ProofGraph {
1677
+ rankdir=LR;
1678
+ node [shape=box, style="rounded,filled", fillcolor="#f8f9fa", fontname="Arial"];
1679
+ edge [color="#6c757d"];
1680
+
1681
+ // Graph metadata
1682
+ label="Proof Graph - ${graph.metadata?.generated || new Date().toISOString()}";
1683
+ labelloc=t;
1684
+ fontname="Arial Bold";
1685
+
1686
+ `;
1687
+
1688
+ // Group nodes by type
1689
+ const nodesByType = {};
1690
+ for (const node of (graph.nodes || [])) {
1691
+ const type = node.type || "unknown";
1692
+ if (!nodesByType[type]) nodesByType[type] = [];
1693
+ nodesByType[type].push(node);
1694
+ }
1695
+
1696
+ // Add subgraphs for each type
1697
+ for (const [type, nodes] of Object.entries(nodesByType)) {
1698
+ dot += ` subgraph cluster_${type} {\n`;
1699
+ dot += ` label="${type.toUpperCase()}";\n`;
1700
+ dot += ` style=dashed;\n`;
1701
+
1702
+ for (const node of nodes) {
1703
+ const label = (node.label || node.id).replace(/"/g, '\\"');
1704
+ const color = node.gap ? "#dc3545" : node.executed ? "#28a745" : "#ffc107";
1705
+ dot += ` "${node.id}" [label="${label}", fillcolor="${color}"];\n`;
1706
+ }
1707
+
1708
+ dot += ` }\n\n`;
1709
+ }
1710
+
1711
+ // Add edges
1712
+ dot += " // Edges\n";
1713
+ for (const edge of (graph.edges || [])) {
1714
+ const label = edge.label ? ` [label="${edge.label}"]` : "";
1715
+ dot += ` "${edge.source}" -> "${edge.target}"${label};\n`;
1716
+ }
1717
+
1718
+ dot += "}\n";
1719
+ return dot;
1720
+ }
1721
+
1722
+ function graphToMermaid(graph) {
1723
+ let mermaid = `%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#00d4ff'}}}%%
1724
+ graph LR
1725
+ `;
1726
+
1727
+ // Add nodes with styling
1728
+ for (const node of (graph.nodes || [])) {
1729
+ const safeId = node.id.replace(/[^a-zA-Z0-9]/g, "_");
1730
+ const label = (node.label || node.id).replace(/"/g, "'");
1731
+
1732
+ if (node.type === "route") {
1733
+ mermaid += ` ${safeId}[/"${label}"/]\n`;
1734
+ } else if (node.type === "ui") {
1735
+ mermaid += ` ${safeId}(("${label}"))\n`;
1736
+ } else {
1737
+ mermaid += ` ${safeId}["${label}"]\n`;
1738
+ }
1739
+ }
1740
+
1741
+ mermaid += "\n";
1742
+
1743
+ // Add edges
1744
+ for (const edge of (graph.edges || [])) {
1745
+ const sourceId = edge.source.replace(/[^a-zA-Z0-9]/g, "_");
1746
+ const targetId = edge.target.replace(/[^a-zA-Z0-9]/g, "_");
1747
+ const label = edge.label ? `|"${edge.label}"|` : "";
1748
+ mermaid += ` ${sourceId} -->${label} ${targetId}\n`;
1749
+ }
1750
+
1751
+ // Add styles for gaps
1752
+ const gapNodes = (graph.nodes || []).filter(n => n.gap);
1753
+ if (gapNodes.length > 0) {
1754
+ mermaid += "\n %% Highlight gaps\n";
1755
+ for (const node of gapNodes) {
1756
+ const safeId = node.id.replace(/[^a-zA-Z0-9]/g, "_");
1757
+ mermaid += ` style ${safeId} fill:#dc3545,color:#fff\n`;
1758
+ }
1759
+ }
1760
+
1761
+ return mermaid;
1762
+ }
1763
+
1764
+ function graphToHtml(graph) {
1765
+ const mermaid = graphToMermaid(graph);
1766
+ const stats = graph.metadata?.stats || { nodes: 0, edges: 0, gaps: 0 };
1767
+
1768
+ return `<!DOCTYPE html>
1769
+ <html lang="en">
1770
+ <head>
1771
+ <meta charset="UTF-8">
1772
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
1773
+ <title>Proof Graph - VibeCheck</title>
1774
+ <script src="https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js"></script>
1775
+ <style>
1776
+ :root {
1777
+ --bg-primary: #0a0a1a;
1778
+ --bg-secondary: #1a1a2e;
1779
+ --accent: #00d4ff;
1780
+ --text: #e0e0e0;
1781
+ --text-dim: #888;
1782
+ }
1783
+ * { margin: 0; padding: 0; box-sizing: border-box; }
1784
+ body {
1785
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
1786
+ background: var(--bg-primary);
1787
+ color: var(--text);
1788
+ min-height: 100vh;
1789
+ padding: 20px;
1790
+ }
1791
+ .container { max-width: 1400px; margin: 0 auto; }
1792
+ header {
1793
+ display: flex;
1794
+ justify-content: space-between;
1795
+ align-items: center;
1796
+ padding: 20px;
1797
+ background: var(--bg-secondary);
1798
+ border-radius: 12px;
1799
+ margin-bottom: 20px;
1800
+ }
1801
+ h1 { font-size: 1.5rem; color: var(--accent); }
1802
+ .meta { color: var(--text-dim); font-size: 0.9rem; }
1803
+ .stats {
1804
+ display: flex;
1805
+ gap: 30px;
1806
+ padding: 20px;
1807
+ background: var(--bg-secondary);
1808
+ border-radius: 12px;
1809
+ margin-bottom: 20px;
1810
+ }
1811
+ .stat { text-align: center; }
1812
+ .stat-value { font-size: 2rem; font-weight: bold; color: var(--accent); }
1813
+ .stat-label { color: var(--text-dim); font-size: 0.85rem; text-transform: uppercase; }
1814
+ .graph-container {
1815
+ background: #fff;
1816
+ border-radius: 12px;
1817
+ padding: 30px;
1818
+ overflow: auto;
1819
+ }
1820
+ .mermaid { min-height: 400px; }
1821
+ footer {
1822
+ margin-top: 20px;
1823
+ padding: 15px;
1824
+ text-align: center;
1825
+ color: var(--text-dim);
1826
+ font-size: 0.85rem;
1827
+ }
1828
+ footer a { color: var(--accent); text-decoration: none; }
1829
+ </style>
1830
+ </head>
1831
+ <body>
1832
+ <div class="container">
1833
+ <header>
1834
+ <h1>Proof Graph</h1>
1835
+ <div class="meta">Generated: ${graph.metadata?.generated || new Date().toISOString()}</div>
1836
+ </header>
1837
+
1838
+ <div class="stats">
1839
+ <div class="stat">
1840
+ <div class="stat-value">${stats.nodes}</div>
1841
+ <div class="stat-label">Nodes</div>
1842
+ </div>
1843
+ <div class="stat">
1844
+ <div class="stat-value">${stats.edges}</div>
1845
+ <div class="stat-label">Edges</div>
1846
+ </div>
1847
+ <div class="stat">
1848
+ <div class="stat-value">${stats.gaps}</div>
1849
+ <div class="stat-label">Gaps</div>
1850
+ </div>
1851
+ </div>
1852
+
1853
+ <div class="graph-container">
1854
+ <pre class="mermaid">
1855
+ ${mermaid}
1856
+ </pre>
1857
+ </div>
1858
+
1859
+ <footer>
1860
+ Powered by <a href="https://vibecheckai.dev">VibeCheck</a> v${PACKS_VERSION}
1861
+ </footer>
1862
+ </div>
1863
+
1864
+ <script>
1865
+ mermaid.initialize({
1866
+ startOnLoad: true,
1867
+ theme: 'default',
1868
+ securityLevel: 'loose',
1869
+ });
1870
+ </script>
1871
+ </body>
1872
+ </html>`;
1873
+ }
1874
+
1875
+ function generateHtmlViewer(manifest) {
1876
+ return `<!DOCTYPE html>
1877
+ <html lang="en">
1878
+ <head>
1879
+ <meta charset="UTF-8">
1880
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
1881
+ <title>Evidence Pack - ${manifest.id}</title>
1882
+ <style>
1883
+ :root {
1884
+ --bg: #0a0a1a;
1885
+ --card: #1a1a2e;
1886
+ --accent: #00d4ff;
1887
+ --text: #e0e0e0;
1888
+ --dim: #666;
1889
+ }
1890
+ * { margin: 0; padding: 0; box-sizing: border-box; }
1891
+ body { font-family: system-ui, sans-serif; background: var(--bg); color: var(--text); padding: 20px; }
1892
+ .container { max-width: 1200px; margin: 0 auto; }
1893
+ header { text-align: center; padding: 40px 0; }
1894
+ h1 { color: var(--accent); margin-bottom: 10px; }
1895
+ .id { color: var(--dim); font-family: monospace; }
1896
+ .grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 20px; margin-top: 30px; }
1897
+ .card { background: var(--card); border-radius: 12px; padding: 20px; }
1898
+ .card h3 { color: var(--accent); margin-bottom: 15px; }
1899
+ .artifact { display: flex; justify-content: space-between; padding: 8px 0; border-bottom: 1px solid #333; }
1900
+ .artifact:last-child { border-bottom: none; }
1901
+ .artifact-name { font-family: monospace; }
1902
+ .artifact-size { color: var(--dim); }
1903
+ </style>
1904
+ </head>
1905
+ <body>
1906
+ <div class="container">
1907
+ <header>
1908
+ <h1>Evidence Pack</h1>
1909
+ <div class="id">${manifest.id}</div>
1910
+ <p style="color: var(--dim); margin-top: 10px;">Generated: ${manifest.generated}</p>
1911
+ </header>
1912
+
1913
+ <div class="grid">
1914
+ ${Object.entries(manifest.summary || {}).map(([type, count]) => `
1915
+ <div class="card">
1916
+ <h3>${type.charAt(0).toUpperCase() + type.slice(1)}</h3>
1917
+ <p>${count} files</p>
1918
+ ${(manifest.artifacts || []).filter(a => a.type === type).map(a => `
1919
+ <div class="artifact">
1920
+ <span class="artifact-name">${a.name}</span>
1921
+ <span class="artifact-size">${formatBytes(a.size)}</span>
1922
+ </div>
1923
+ `).join('')}
1924
+ </div>
1925
+ `).join('')}
1926
+ </div>
1927
+ </div>
1928
+
1929
+ <script>
1930
+ function formatBytes(bytes) {
1931
+ if (bytes === 0) return '0 B';
1932
+ const k = 1024;
1933
+ const sizes = ['B', 'KB', 'MB', 'GB'];
1934
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
1935
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i];
1936
+ }
1937
+ </script>
1938
+ </body>
1939
+ </html>`;
1940
+ }
1941
+
1942
+ function formatBytes(bytes) {
1943
+ if (bytes === 0) return '0 B';
1944
+ const k = 1024;
1945
+ const sizes = ['B', 'KB', 'MB', 'GB'];
1946
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
1947
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i];
1948
+ }
1949
+
1950
+ function generateSarif(report) {
1951
+ return JSON.stringify({
1952
+ $schema: "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json",
1953
+ version: "2.1.0",
1954
+ runs: [{
1955
+ tool: {
1956
+ driver: {
1957
+ name: "VibeCheck",
1958
+ version: PACKS_VERSION,
1959
+ informationUri: "https://vibecheckai.dev",
1960
+ rules: [],
1961
+ },
1962
+ },
1963
+ results: (report.findings || []).map(f => ({
1964
+ ruleId: f.ruleId || f.id || "unknown",
1965
+ message: { text: f.message || f.description },
1966
+ level: f.severity === "critical" ? "error" : f.severity === "high" ? "error" : "warning",
1967
+ locations: f.file ? [{
1968
+ physicalLocation: {
1969
+ artifactLocation: { uri: f.file },
1970
+ region: { startLine: f.line || 1 },
1971
+ },
1972
+ }] : [],
1973
+ })),
1974
+ }],
1975
+ }, null, 2);
1976
+ }
1977
+
1978
+ function generateMarkdownReport(report) {
1979
+ return `# VibeCheck Report
1980
+
1981
+ Generated: ${report.generated}
1982
+ Type: ${report.type}
1983
+
1984
+ ## Summary
1985
+
1986
+ | Severity | Count |
1987
+ |----------|-------|
1988
+ | Critical | ${report.summary.critical} |
1989
+ | High | ${report.summary.high} |
1990
+ | Medium | ${report.summary.medium} |
1991
+ | Low | ${report.summary.low} |
1992
+ | **Total** | **${report.summary.total}** |
1993
+
1994
+ ## Findings
1995
+
1996
+ ${(report.findings || []).map(f => `
1997
+ ### ${f.ruleId || f.id}
1998
+
1999
+ - **Severity**: ${f.severity}
2000
+ - **File**: ${f.file || 'N/A'}
2001
+ - **Line**: ${f.line || 'N/A'}
2002
+
2003
+ ${f.message || f.description}
2004
+ `).join('\n---\n')}
2005
+
2006
+ ---
2007
+ *Generated by VibeCheck v${PACKS_VERSION}*
2008
+ `;
2009
+ }
2010
+
2011
+ function generateHtmlReport(report) {
2012
+ return `<!DOCTYPE html>
2013
+ <html lang="en">
2014
+ <head>
2015
+ <meta charset="UTF-8">
2016
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
2017
+ <title>VibeCheck Report</title>
2018
+ <style>
2019
+ :root { --accent: #00d4ff; --bg: #0a0a1a; --card: #1a1a2e; --text: #e0e0e0; }
2020
+ * { margin: 0; padding: 0; box-sizing: border-box; }
2021
+ body { font-family: system-ui, sans-serif; background: var(--bg); color: var(--text); padding: 40px; }
2022
+ .container { max-width: 1000px; margin: 0 auto; }
2023
+ h1 { color: var(--accent); margin-bottom: 30px; }
2024
+ .summary { display: flex; gap: 20px; margin-bottom: 40px; }
2025
+ .stat { background: var(--card); padding: 20px; border-radius: 12px; text-align: center; flex: 1; }
2026
+ .stat-value { font-size: 2rem; font-weight: bold; }
2027
+ .stat-label { color: #888; font-size: 0.85rem; }
2028
+ .critical { color: #dc3545; }
2029
+ .high { color: #fd7e14; }
2030
+ .medium { color: #ffc107; }
2031
+ .low { color: #28a745; }
2032
+ .finding { background: var(--card); padding: 20px; border-radius: 12px; margin-bottom: 15px; }
2033
+ .finding-header { display: flex; justify-content: space-between; margin-bottom: 10px; }
2034
+ .finding-id { font-family: monospace; color: var(--accent); }
2035
+ .finding-file { color: #888; font-size: 0.9rem; }
2036
+ </style>
2037
+ </head>
2038
+ <body>
2039
+ <div class="container">
2040
+ <h1>VibeCheck Report</h1>
2041
+
2042
+ <div class="summary">
2043
+ <div class="stat"><div class="stat-value critical">${report.summary.critical}</div><div class="stat-label">Critical</div></div>
2044
+ <div class="stat"><div class="stat-value high">${report.summary.high}</div><div class="stat-label">High</div></div>
2045
+ <div class="stat"><div class="stat-value medium">${report.summary.medium}</div><div class="stat-label">Medium</div></div>
2046
+ <div class="stat"><div class="stat-value low">${report.summary.low}</div><div class="stat-label">Low</div></div>
2047
+ </div>
2048
+
2049
+ <h2 style="margin-bottom: 20px;">Findings</h2>
2050
+
2051
+ ${(report.findings || []).map(f => `
2052
+ <div class="finding">
2053
+ <div class="finding-header">
2054
+ <span class="finding-id">${f.ruleId || f.id}</span>
2055
+ <span class="finding-severity ${f.severity}">${f.severity?.toUpperCase()}</span>
2056
+ </div>
2057
+ <p>${f.message || f.description}</p>
2058
+ ${f.file ? `<div class="finding-file">${f.file}${f.line ? `:${f.line}` : ''}</div>` : ''}
2059
+ </div>
2060
+ `).join('')}
2061
+ </div>
2062
+ </body>
2063
+ </html>`;
2064
+ }
2065
+
2066
+ // ═══════════════════════════════════════════════════════════════════════════════
2067
+ // EXPORTS
2068
+ // ═══════════════════════════════════════════════════════════════════════════════
2069
+
2070
+ module.exports = {
2071
+ runPacks,
2072
+ // Export constants for testing
2073
+ PACKS_VERSION,
2074
+ SUBCOMMANDS,
2075
+ GRAPH_QUERIES,
2076
+ // Export individual handlers (V2)
2077
+ handleBundle,
2078
+ handleEvidenceV2,
2079
+ handlePermissionsV2,
2080
+ handleGraphV2,
2081
+ handleList,
2082
+ handleCleanup,
2083
+ // Legacy handlers
2084
+ handleEvidence,
2085
+ handleReport,
2086
+ handleGraph,
2087
+ handlePermissions,
2088
+ handleManifest,
2089
+ };