vibecheck-ai 2.0.2 → 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,688 @@
1
+ /**
2
+ * runScan.js — Ultimate Scanner v5.0 Runner
3
+ *
4
+ * ═══════════════════════════════════════════════════════════════════════════════
5
+ * Inline scanner with 3 core engines (credentials, fake-features, security).
6
+ * When dist/scanner/ is compiled, upgrades to full 8-engine mode automatically.
7
+ *
8
+ * Architecture:
9
+ * Files → Classify → [Engines in parallel] → Deduplicate → Score → Report
10
+ * ═══════════════════════════════════════════════════════════════════════════════
11
+ */
12
+
13
+ "use strict";
14
+
15
+ const path = require("path");
16
+ const fs = require("fs");
17
+ const crypto = require("crypto");
18
+ const { withErrorHandling } = require("./lib/error-handler");
19
+ const { parseGlobalFlags } = require("./lib/global-flags");
20
+ const { EXIT } = require("./lib/exit-codes");
21
+
22
+ // ═══════════════════════════════════════════════════════════════════════════════
23
+ // LAZY-LOAD: Try compiled 8-engine scanner first
24
+ // ═══════════════════════════════════════════════════════════════════════════════
25
+
26
+ let _scanner = null;
27
+ function getScanner() {
28
+ if (_scanner) return _scanner;
29
+ try {
30
+ _scanner = require("../../dist/scanner/index.js");
31
+ return _scanner;
32
+ } catch {
33
+ return null;
34
+ }
35
+ }
36
+
37
+ // ═══════════════════════════════════════════════════════════════════════════════
38
+ // TERMINAL STYLING (zero deps)
39
+ // ═══════════════════════════════════════════════════════════════════════════════
40
+
41
+ const SUPPORTS_COLOR = process.stdout.isTTY && !process.env.NO_COLOR;
42
+ const c = SUPPORTS_COLOR
43
+ ? {
44
+ reset: "\x1b[0m", bold: "\x1b[1m", dim: "\x1b[2m",
45
+ red: "\x1b[31m", green: "\x1b[32m", yellow: "\x1b[33m",
46
+ blue: "\x1b[34m", magenta: "\x1b[35m", cyan: "\x1b[36m",
47
+ white: "\x1b[37m", gray: "\x1b[90m",
48
+ }
49
+ : Object.fromEntries(
50
+ ["reset","bold","dim","red","green","yellow","blue","magenta","cyan","white","gray"].map(k => [k, ""])
51
+ );
52
+
53
+ const SEV_ICON = { critical: `${c.red}●${c.reset}`, high: `${c.yellow}●${c.reset}`, medium: `${c.cyan}●${c.reset}`, low: `${c.dim}●${c.reset}` };
54
+ const SEV_LABEL = { critical: `${c.red}CRITICAL${c.reset}`, high: `${c.yellow}HIGH${c.reset}`, medium: `${c.cyan}MEDIUM${c.reset}`, low: `${c.dim}LOW${c.reset}` };
55
+ const SEV_ORDER = { critical: 4, high: 3, medium: 2, low: 1 };
56
+
57
+ // ═══════════════════════════════════════════════════════════════════════════════
58
+ // ARGS PARSER
59
+ // ═══════════════════════════════════════════════════════════════════════════════
60
+
61
+ function parseArgs(args) {
62
+ const { flags: globalFlags, cleanArgs } = parseGlobalFlags(args);
63
+ const opts = {
64
+ path: globalFlags.path || process.cwd(),
65
+ format: "pretty", severity: "low", engines: null, includeTests: false,
66
+ parallel: true, fix: false, dryRun: false, category: null,
67
+ help: globalFlags.help || false, json: globalFlags.json || false,
68
+ quiet: globalFlags.quiet || false, ci: globalFlags.ci || false,
69
+ verbose: globalFlags.verbose || false, sarif: false, failOn: null,
70
+ output: globalFlags.output || null,
71
+ };
72
+ for (let i = 0; i < cleanArgs.length; i++) {
73
+ const a = cleanArgs[i], n = cleanArgs[i + 1];
74
+ switch (a) {
75
+ case "--format": opts.format = n || "pretty"; i++; break;
76
+ case "--severity": opts.severity = n || "low"; i++; break;
77
+ case "--engines": opts.engines = n ? n.split(",") : null; i++; break;
78
+ case "--include-tests": opts.includeTests = true; break;
79
+ case "--parallel": opts.parallel = true; break;
80
+ case "--no-parallel": opts.parallel = false; break;
81
+ case "--fix": case "--auto-fix": opts.fix = true; break;
82
+ case "--dry-run": opts.dryRun = true; break;
83
+ case "--category": opts.category = n; i++; break;
84
+ case "--sarif": opts.sarif = true; opts.format = "sarif"; break;
85
+ case "--fail-on": opts.failOn = n; i++; break;
86
+ case "--output": opts.output = n; i++; break;
87
+ case "--json": opts.json = true; opts.format = "json"; break;
88
+ case "--quiet": case "-q": opts.quiet = true; break;
89
+ case "--verbose": opts.verbose = true; break;
90
+ case "--help": case "-h": opts.help = true; break;
91
+ default: if (!a.startsWith("-") && !opts.category) opts.category = a; break;
92
+ }
93
+ }
94
+ return opts;
95
+ }
96
+
97
+ // ═══════════════════════════════════════════════════════════════════════════════
98
+ // HELP
99
+ // ═══════════════════════════════════════════════════════════════════════════════
100
+
101
+ function printHelp() {
102
+ console.log(`
103
+ ${c.bold}${c.cyan}vibecheck scan${c.reset} — The most accurate AI code scanner
104
+
105
+ ${c.bold}Usage:${c.reset}
106
+ vibecheck scan [subcommand] [options]
107
+
108
+ ${c.bold}Subcommands:${c.reset}
109
+ ${c.cyan}secrets${c.reset} Scan for leaked credentials & API keys
110
+ ${c.cyan}vulns${c.reset} Scan for dependency vulnerabilities
111
+ ${c.cyan}routes${c.reset} Scan for dead/orphan routes
112
+ ${c.cyan}env${c.reset} Scan for ghost environment variables
113
+ ${c.cyan}auth${c.reset} Scan for auth drift & unprotected endpoints
114
+
115
+ ${c.bold}Options:${c.reset}
116
+ --path <dir> Project path (default: .)
117
+ --format <fmt> Output: pretty, json, sarif (default: pretty)
118
+ --severity <level> Min severity: low, medium, high, critical
119
+ --engines <list> Comma-separated engine names
120
+ --include-tests Include test files
121
+ --fix Auto-fix fixable findings
122
+ --dry-run Preview fixes without applying
123
+ --sarif SARIF output for GitHub
124
+ --fail-on <level> Exit non-zero if findings >= level (CI mode)
125
+ --output <dir> Report output directory
126
+ --quiet Minimal output
127
+ --help Show this help
128
+
129
+ ${c.bold}Engines:${c.reset}
130
+ credentials, security, fake-features, hallucinations,
131
+ dead-ui, code-quality, import-graph, runtime-verify
132
+
133
+ ${c.bold}Examples:${c.reset}
134
+ vibecheck scan
135
+ vibecheck scan secrets
136
+ vibecheck scan --sarif --fail-on high
137
+ vibecheck scan --fix --dry-run
138
+ `);
139
+ }
140
+
141
+ // ═══════════════════════════════════════════════════════════════════════════════
142
+ // PATH CLASSIFIER
143
+ // ═══════════════════════════════════════════════════════════════════════════════
144
+
145
+ const CODE_EXTS = new Set([
146
+ ".ts",".tsx",".js",".jsx",".mjs",".cjs",".vue",".svelte",
147
+ ".py",".rb",".go",".rs",".java",".kt",".cs",".php",".swift",
148
+ ]);
149
+
150
+ function isCodeFile(fp) { return CODE_EXTS.has(path.extname(fp).toLowerCase()); }
151
+
152
+ function classifyPath(rel) {
153
+ const r = rel.replace(/\\/g, "/");
154
+ if (/node_modules\/|vendor\/|\.yarn\/|\.pnpm\//.test(r)) return { category: "third_party", excludeByDefault: true, isCriticalPath: false };
155
+ if (/\/dist\/|\/build\/|\/.next\/|\/.nuxt\/|\.min\.(js|css)$/.test(r)) return { category: "build_output", excludeByDefault: true, isCriticalPath: false };
156
+ if (/\.d\.ts$|\/generated\/|\.gen\.(ts|js)$|\.lock$/.test(r)) return { category: "generated", excludeByDefault: true, isCriticalPath: false };
157
+ if (/\.(test|spec)\.(ts|tsx|js|jsx)$|__tests__|__mocks__|\.stories\./.test(r)) return { category: "test", excludeByDefault: false, isCriticalPath: false };
158
+ if (/\.(md|mdx|rst|txt)$|\/docs\//.test(r)) return { category: "documentation", excludeByDefault: true, isCriticalPath: false };
159
+ const isCrit = /\/api\/|\/auth\/|\/payment\/|\/billing\/|\/admin\/|middleware/i.test(r);
160
+ return { category: "user_code", excludeByDefault: false, isCriticalPath: isCrit };
161
+ }
162
+
163
+ function escalateSev(sev, isCrit) {
164
+ if (!isCrit) return sev;
165
+ if (sev === "low") return "medium";
166
+ if (sev === "medium") return "high";
167
+ return sev;
168
+ }
169
+
170
+ // ═══════════════════════════════════════════════════════════════════════════════
171
+ // FILE LOADER
172
+ // ═══════════════════════════════════════════════════════════════════════════════
173
+
174
+ const DEFAULT_EXCLUDE = ["node_modules",".git","dist","build",".next",".nuxt","coverage","__pycache__",".venv",".output"];
175
+
176
+ function loadFiles(root, opts) {
177
+ const files = new Map();
178
+ const exclude = DEFAULT_EXCLUDE;
179
+ const maxSize = 512 * 1024;
180
+
181
+ function walk(dir) {
182
+ let entries;
183
+ try { entries = fs.readdirSync(dir); } catch { return; }
184
+ for (const entry of entries) {
185
+ const full = path.join(dir, entry);
186
+ if (exclude.includes(entry) || entry.startsWith(".")) {
187
+ try { if (fs.statSync(full).isDirectory()) continue; } catch { continue; }
188
+ }
189
+ let stat;
190
+ try { stat = fs.statSync(full); } catch { continue; }
191
+ if (stat.isDirectory()) { walk(full); continue; }
192
+ if (!isCodeFile(full)) continue;
193
+ if (stat.size > maxSize) continue;
194
+ const rel = path.relative(root, full).replace(/\\/g, "/");
195
+ const cl = classifyPath(rel);
196
+ if (cl.excludeByDefault && cl.category !== "test") continue;
197
+ if (cl.category === "test" && !opts.includeTests) continue;
198
+ try {
199
+ const content = fs.readFileSync(full, "utf-8");
200
+ files.set(rel, {
201
+ path: rel, absolutePath: full, content, lines: content.split("\n"),
202
+ ext: path.extname(full).toLowerCase(),
203
+ hash: crypto.createHash("md5").update(content).digest("hex"),
204
+ classification: cl,
205
+ });
206
+ } catch { /* skip unreadable */ }
207
+ }
208
+ }
209
+ walk(root);
210
+ return files;
211
+ }
212
+
213
+ // ═══════════════════════════════════════════════════════════════════════════════
214
+ // ENGINE: CREDENTIALS (20 patterns)
215
+ // ═══════════════════════════════════════════════════════════════════════════════
216
+
217
+ const CRED_PATTERNS = [
218
+ { n: "stripe-live-secret", id: "CRED001", re: /['"`](sk_live_[a-zA-Z0-9]{20,})['"`]/, sev: "critical", msg: "Stripe live secret key hardcoded", fix: "Use process.env.STRIPE_SECRET_KEY", conf: 99, test: true, cwe: "CWE-798" },
219
+ { n: "stripe-live-pub", id: "CRED001", re: /['"`](pk_live_[a-zA-Z0-9]{20,})['"`]/, sev: "high", msg: "Stripe live publishable key hardcoded", fix: "Use process.env.NEXT_PUBLIC_STRIPE_KEY", conf: 95, test: false },
220
+ { n: "aws-access", id: "CRED001", re: /['"`](AKIA[0-9A-Z]{16})['"`]/, sev: "critical", msg: "AWS Access Key ID hardcoded", fix: "Use AWS SDK credential chain", conf: 99, test: true, cwe: "CWE-798" },
221
+ { n: "aws-secret", id: "CRED001", re: /aws_secret_access_key\s*[:=]\s*['"`]([^'"`]{20,})['"`]/i, sev: "critical", msg: "AWS Secret Access Key hardcoded", fix: "Use AWS_SECRET_ACCESS_KEY env var", conf: 98, test: true, cwe: "CWE-798" },
222
+ { n: "openai", id: "CRED001", re: /['"`](sk-[a-zA-Z0-9]{32,})['"`]/, sev: "critical", msg: "OpenAI API key hardcoded", fix: "Use process.env.OPENAI_API_KEY", conf: 92, test: true },
223
+ { n: "anthropic", id: "CRED001", re: /['"`](sk-ant-[a-zA-Z0-9-]{20,})['"`]/, sev: "critical", msg: "Anthropic API key hardcoded", fix: "Use process.env.ANTHROPIC_API_KEY", conf: 98, test: true },
224
+ { n: "github-pat", id: "CRED001", re: /['"`](ghp_[a-zA-Z0-9]{36,})['"`]/, sev: "critical", msg: "GitHub personal access token hardcoded", fix: "Use process.env.GITHUB_TOKEN", conf: 99, test: true },
225
+ { n: "github-oauth", id: "CRED001", re: /['"`](gho_[a-zA-Z0-9]{36,})['"`]/, sev: "critical", msg: "GitHub OAuth token hardcoded", fix: "Use process.env.GITHUB_OAUTH_TOKEN", conf: 99, test: true },
226
+ { n: "google-api", id: "CRED001", re: /['"`](AIza[0-9A-Za-z_-]{35})['"`]/, sev: "high", msg: "Google API key hardcoded", fix: "Use process.env.GOOGLE_API_KEY", conf: 95, test: false },
227
+ { n: "jwt-secret", id: "CRED003", re: /(?:jwt[_-]?secret|JWT_SECRET)\s*[:=]\s*['"`]([^'"`]{8,})['"`]/i, sev: "critical", msg: "JWT signing secret hardcoded", fix: "Use process.env.JWT_SECRET", conf: 90, test: false, cwe: "CWE-798" },
228
+ { n: "password", id: "CRED002", re: /(?:password|passwd|pwd)\s*[:=]\s*['"`]([^'"`]{4,})['"`]/i, sev: "high", msg: "Hardcoded password detected", fix: "Use env variable or secrets manager", conf: 75, test: false, cwe: "CWE-798" },
229
+ { n: "generic-secret", id: "CRED002", re: /(?:secret|api[_-]?key|auth[_-]?token)\s*[:=]\s*['"`]([^'"`]{8,})['"`]/i, sev: "high", msg: "Hardcoded secret/token detected", fix: "Use environment variable", conf: 70, test: false },
230
+ { n: "private-key", id: "CRED004", re: /-----BEGIN (?:RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----/, sev: "critical", msg: "Private key embedded in source code", fix: "Store key in file, reference via env var", conf: 99, test: true, cwe: "CWE-321" },
231
+ { n: "conn-string", id: "CRED005", re: /['"`](?:mongodb(?:\+srv)?|postgres(?:ql)?|mysql|redis|amqp):\/\/[^'"`\s]{10,}['"`]/i, sev: "critical", msg: "Database connection string with credentials", fix: "Use DATABASE_URL env var", conf: 92, test: false, cwe: "CWE-798" },
232
+ { n: "slack", id: "CRED001", re: /['"`](xox[bpoas]-[0-9a-zA-Z-]{10,})['"`]/, sev: "critical", msg: "Slack token hardcoded", fix: "Use process.env.SLACK_TOKEN", conf: 97, test: true },
233
+ { n: "sendgrid", id: "CRED001", re: /['"`](SG\.[a-zA-Z0-9_-]{22}\.[a-zA-Z0-9_-]{43})['"`]/, sev: "critical", msg: "SendGrid API key hardcoded", fix: "Use process.env.SENDGRID_API_KEY", conf: 99, test: true },
234
+ { n: "npm-token", id: "CRED001", re: /['"`](npm_[a-zA-Z0-9]{36,})['"`]/, sev: "critical", msg: "npm auth token hardcoded", fix: "Use .npmrc with env var interpolation", conf: 98, test: true },
235
+ ];
236
+
237
+ function scanCredentials(files) {
238
+ const findings = [];
239
+ for (const [, file] of files) {
240
+ const isTest = file.classification.category === "test";
241
+ const isExample = /\.(example|sample|template)\b/.test(file.path);
242
+ const isCrit = file.classification.isCriticalPath;
243
+ for (let i = 0; i < file.lines.length; i++) {
244
+ const line = file.lines[i];
245
+ const trimmed = line.trim();
246
+ if (trimmed.startsWith("//") && !trimmed.includes("=")) continue;
247
+ if (trimmed.startsWith("*")) continue;
248
+ for (const p of CRED_PATTERNS) {
249
+ if (isTest && !p.test) continue;
250
+ if (isExample) continue;
251
+ const m = line.match(p.re);
252
+ if (!m) continue;
253
+ if (p.n === "password") {
254
+ if (/type\s|interface\s|placeholder|example/i.test(line)) continue;
255
+ if (!m[1] || m[1].length < 6) continue;
256
+ }
257
+ if (p.n === "generic-secret") {
258
+ if (!m[1] || /^[a-z_]+$/i.test(m[1])) continue;
259
+ if (/placeholder|example|your[_-]/i.test(m[1])) continue;
260
+ }
261
+ findings.push({
262
+ id: `${p.id}-${file.path}-${i+1}`, ruleId: p.id, engine: "credentials",
263
+ category: "credentials", severity: escalateSev(p.sev, isCrit),
264
+ confidence: p.conf >= 90 ? "certain" : p.conf >= 70 ? "likely" : "possible",
265
+ confidenceScore: p.conf, file: file.path, line: i+1, column: m.index,
266
+ code: trimmed, message: p.msg, why: "Hardcoded credentials can be extracted from source, builds, or version history.", fix: p.fix,
267
+ autoFixable: false, tags: ["secrets","credentials"], cwe: p.cwe, verified: false,
268
+ });
269
+ break;
270
+ }
271
+ }
272
+ }
273
+ return findings;
274
+ }
275
+
276
+ // ═══════════════════════════════════════════════════════════════════════════════
277
+ // ENGINE: FAKE FEATURES (6 pattern groups)
278
+ // ═══════════════════════════════════════════════════════════════════════════════
279
+
280
+ const PLACEHOLDER_RE = [
281
+ /[=:]\s*['"]?placeholder['"]?(?:\s*[,;}\]]|$)/i, /\bstub(?:bed|bing)?\s*(?:function|method|implementation)/i,
282
+ /\bdummy[-_]?(?:data|value|user|id|token|response)\b/i, /\bfake[-_]?(?:data|response|user|api|endpoint)\b/i,
283
+ /\bTODO\b.*(?:implement|fix|complete|replace|remove)/i, /\bWIP\b(?:\s*[-:]\s|\s+)/i,
284
+ /\bcoming\s+soon\b/i, /\bCHANGE[-_]?ME\b/i, /\bREPLACE[-_]?ME\b/i,
285
+ ];
286
+ const FAKE_SUCCESS_RE = [
287
+ /^\s*return\s+true\s*;?\s*$/i, /return\s*{\s*success:\s*true\s*}/i,
288
+ /return\s*{\s*status:\s*["']ok["']\s*}/i, /\/\/\s*(?:fake|mock|stub)\s*success/i,
289
+ ];
290
+ const SILENT_FAIL_RE = [
291
+ /catch\s*\(\s*(?:_|e|err|error)?\s*\)\s*{\s*}/, /catch\s*\([^)]*\)\s*{\s*return(?:\s+(?:undefined|null))?\s*;?\s*}/,
292
+ /catch\s*\([^)]*\)\s*{\s*\/\/[^\n]*\s*}/, /catch\s*\([^)]*\)\s*{\s*console\.log\([^)]+\)\s*;?\s*}/,
293
+ ];
294
+ const AUTH_BYPASS_RE = [
295
+ /(?:if|&&|\|\|)\s*\(\s*(?:true|1)\s*\)\s*.*(?:admin|auth)/i, /\b(?:skip|disable|bypass)Auth(?:entication)?\s*[=:]\s*true\b/i,
296
+ /\b(?:const|let|var)\s+isAdmin\s*=\s*true\b/i, /\/\/\s*(?:bypass|skip|disable)\s*auth/i,
297
+ /permissions?\s*[=:]\s*\[\s*['"]?\*['"]?\s*\]/i, /(?:isAuth|isAdmin|hasPermission|canAccess)\s*[=:]\s*\(\)\s*=>\s*true/i,
298
+ ];
299
+ const DANGER_DEFAULT_RE = [
300
+ /process\.env\.(?:\w*(?:SECRET|KEY|TOKEN|PASSWORD|CREDENTIAL)\w*)\s*\|\|\s*["'][^'"]{0,50}["']/i,
301
+ /(?:api[_-]?key|secret|token|password)\s*[=:]\s*["'](?:CHANGEME|REPLACE_ME|YOUR_[A-Z_]+|xxx+|TODO)["']/i,
302
+ ];
303
+ const EMPTY_FN_RE = [
304
+ /async\s+(?:function\s+\w+|(?:const|let)\s+\w+\s*=\s*async)\s*\([^)]*\)\s*(?::\s*\w+[<>\[\]]*\s*)?\s*{\s*}/,
305
+ /function\s+\w+\s*\([^)]*\)\s*{\s*return\s*;?\s*}/, /=>\s*{\s*}\s*[;,)]/,
306
+ ];
307
+
308
+ const FAKE_RULES = {
309
+ FAKE001: { desc: "Empty/stub function — no implementation", why: "Functions with no body silently do nothing. Users think the feature works.", fix: "Implement the function body or throw NotImplementedError.", tags: ["stub","empty"], auto: false },
310
+ FAKE002: { desc: "Fake success return — always returns true/ok", why: "Functions that always return success bypass real logic. Login that always succeeds = no auth.", fix: "Implement actual validation before returning success.", tags: ["fake-success"], auto: false },
311
+ FAKE003: { desc: "Silent error swallowing — empty catch block", why: "Empty catch blocks hide failures. Users see no error but nothing actually works.", fix: "Log the error, rethrow, or handle it meaningfully.", tags: ["silent-failure"], auto: true },
312
+ FAKE004: { desc: "Placeholder/TODO marker in production code", why: "Placeholder values indicate unfinished work that may be deployed.", fix: "Complete the implementation or remove the placeholder.", tags: ["placeholder","todo"], auto: false },
313
+ FAKE005: { desc: "Auth bypass — hardcoded admin or skipped auth", why: "Authentication bypasses let anyone access protected resources.", fix: "Remove hardcoded bypasses. Implement proper auth checks.", tags: ["auth-bypass","security"], auto: false },
314
+ FAKE006: { desc: "Dangerous default — secret env var with insecure fallback", why: "If the env var is missing, the app runs with an insecure default value.", fix: "Throw on missing required secrets. Never provide insecure defaults.", tags: ["dangerous-default"], auto: false },
315
+ };
316
+
317
+ function scanFakeFeatures(files, opts) {
318
+ const findings = [];
319
+ for (const [, file] of files) {
320
+ if (file.classification.category === "test" && !opts.includeTests) continue;
321
+ if (file.classification.category === "documentation") continue;
322
+ const isCrit = file.classification.isCriticalPath;
323
+ for (let i = 0; i < file.lines.length; i++) {
324
+ const line = file.lines[i], trimmed = line.trim();
325
+ const isComment = /^\s*(?:\/\/|\/\*|\*|#)/.test(trimmed);
326
+
327
+ // Placeholders (including TODO comments)
328
+ if (!isComment || /\b(?:TODO|FIXME|WIP)\b/i.test(trimmed)) {
329
+ if (!/\.(md|mdx|rst|txt)$/.test(file.path) && !file.path.includes("/docs/")) {
330
+ for (const re of PLACEHOLDER_RE) {
331
+ if (re.test(line)) { findings.push(makeFake("FAKE004", file, i, trimmed, "medium", 65, isCrit)); break; }
332
+ }
333
+ }
334
+ }
335
+ if (isComment) continue;
336
+
337
+ // Fake success
338
+ for (const re of FAKE_SUCCESS_RE) {
339
+ if (re.test(line) && !file.path.includes(".test.") && !file.path.includes(".spec.")) {
340
+ findings.push(makeFake("FAKE002", file, i, trimmed, "medium", 65, isCrit)); break;
341
+ }
342
+ }
343
+ // Silent failures
344
+ for (const re of SILENT_FAIL_RE) {
345
+ if (re.test(line) && !line.toLowerCase().includes("finally")) {
346
+ findings.push(makeFake("FAKE003", file, i, trimmed, "high", 85, isCrit)); break;
347
+ }
348
+ }
349
+ // Auth bypass
350
+ for (const re of AUTH_BYPASS_RE) {
351
+ if (re.test(line) && !file.path.includes(".config.") && !file.path.includes(".env")) {
352
+ findings.push(makeFake("FAKE005", file, i, trimmed, "critical", 88, isCrit)); break;
353
+ }
354
+ }
355
+ // Dangerous defaults
356
+ for (const re of DANGER_DEFAULT_RE) {
357
+ if (re.test(line) && !file.path.includes(".example") && !file.path.includes(".template")) {
358
+ findings.push(makeFake("FAKE006", file, i, trimmed, "high", 82, isCrit)); break;
359
+ }
360
+ }
361
+ // Empty functions
362
+ for (const re of EMPTY_FN_RE) {
363
+ if (re.test(line) && !/noop|passthrough|abstract|interface|override/i.test(line)) {
364
+ findings.push(makeFake("FAKE001", file, i, trimmed, "high", 75, isCrit)); break;
365
+ }
366
+ }
367
+ }
368
+ }
369
+ return findings;
370
+ }
371
+
372
+ function makeFake(ruleId, file, idx, code, sev, conf, isCrit) {
373
+ const r = FAKE_RULES[ruleId];
374
+ return {
375
+ id: `${ruleId}-${file.path}-${idx+1}`, ruleId, engine: "fake-features",
376
+ category: "fake-features", severity: escalateSev(sev, isCrit),
377
+ confidence: conf >= 85 ? "certain" : conf >= 65 ? "likely" : "possible",
378
+ confidenceScore: conf, file: file.path, line: idx+1, code,
379
+ message: r.desc, why: r.why, fix: r.fix, autoFixable: r.auto,
380
+ tags: [...r.tags], verified: false,
381
+ };
382
+ }
383
+
384
+ // ═══════════════════════════════════════════════════════════════════════════════
385
+ // ENGINE: SECURITY (15 patterns)
386
+ // ═══════════════════════════════════════════════════════════════════════════════
387
+
388
+ const SEC_PATTERNS = [
389
+ { n: "sql-inject-tpl", id: "SEC001", re: /(?:query|execute|raw)\s*\(\s*[`'"](?:SELECT|INSERT|UPDATE|DELETE|DROP|ALTER)\s[^`'"]*\$\{/i, sev: "critical", msg: "SQL injection: template literal in query", fix: "Use parameterized queries: db.query('SELECT * FROM users WHERE id = $1', [userId])", conf: 92, cwe: "CWE-89" },
390
+ { n: "sql-inject-concat", id: "SEC001", re: /(?:query|execute|raw)\s*\(\s*['"](?:SELECT|INSERT|UPDATE|DELETE)\s[^'"]*['"]\s*\+/i, sev: "critical", msg: "SQL injection: string concatenation in query", fix: "Use parameterized queries. Never concatenate user input into SQL.", conf: 90, cwe: "CWE-89" },
391
+ { n: "unfiltered-delete", id: "SEC011", re: /DELETE\s+FROM\s+\w+\s*;?\s*$/im, sev: "critical", msg: "Unfiltered DELETE (no WHERE clause)", fix: "Add WHERE clause. Add confirmation for destructive ops.", conf: 85, cwe: "CWE-89" },
392
+ { n: "exec-tpl", id: "SEC002", re: /(?:exec|execSync|spawn|spawnSync)\s*\(\s*`[^`]*\$\{/, sev: "critical", msg: "Command injection: template literal in shell command", fix: "Use execFile() with args array", conf: 95, cwe: "CWE-78" },
393
+ { n: "exec-concat", id: "SEC002", re: /(?:exec|execSync)\s*\(\s*['"][^'"]*['"]\s*\+/, sev: "critical", msg: "Command injection: concatenation in shell command", fix: "Use execFile() with args array", conf: 93, cwe: "CWE-78" },
394
+ { n: "innerHTML", id: "SEC003", re: /\.innerHTML\s*=/, sev: "high", msg: "innerHTML assignment — potential XSS", fix: "Use textContent for text. Use DOMPurify.sanitize() for HTML.", conf: 75, cwe: "CWE-79" },
395
+ { n: "dangerouslySet", id: "SEC003", re: /dangerouslySetInnerHTML/, sev: "high", msg: "dangerouslySetInnerHTML — ensure content is sanitized", fix: "Sanitize with DOMPurify before passing.", conf: 70, cwe: "CWE-79" },
396
+ { n: "document-write", id: "SEC003", re: /document\.write\s*\(/, sev: "high", msg: "document.write() can overwrite page with unescaped content", fix: "Use DOM manipulation methods instead.", conf: 80, cwe: "CWE-79" },
397
+ { n: "path-traversal", id: "SEC004", re: /(?:path\.join|path\.resolve)\s*\([^)]*(?:req\.|params\.|query\.|body\.)/, sev: "high", msg: "User input in file path — potential path traversal", fix: "Validate/sanitize file paths. Use path.normalize() and check for '..'.", conf: 82, cwe: "CWE-22" },
398
+ { n: "eval", id: "SEC005", re: /\beval\s*\(/, sev: "critical", msg: "eval() executes arbitrary code", fix: "Replace with JSON.parse(), Function constructor, or safer alternatives.", conf: 88, cwe: "CWE-95" },
399
+ { n: "cors-star", id: "SEC006", re: /(?:Access-Control-Allow-Origin|cors)\s*[=:]\s*['"`]\s*\*\s*['"`]/, sev: "high", msg: "CORS wildcard allows any origin", fix: "Restrict to specific trusted origins.", conf: 85, cwe: "CWE-942" },
400
+ { n: "no-https", id: "SEC007", re: /http:\/\/(?!localhost|127\.0\.0\.1|0\.0\.0\.0)[\w.-]+\.\w+/, sev: "medium", msg: "Non-HTTPS URL in production code", fix: "Use HTTPS for all external endpoints.", conf: 60 },
401
+ { n: "disable-ssl", id: "SEC007", re: /rejectUnauthorized\s*:\s*false/, sev: "critical", msg: "TLS verification disabled — MITM attack risk", fix: "Enable TLS verification. Fix certificates instead of disabling.", conf: 95, cwe: "CWE-295" },
402
+ { n: "debug-true", id: "SEC008", re: /(?:debug|DEBUG)\s*[:=]\s*true\b/, sev: "medium", msg: "Debug mode enabled — may leak sensitive info", fix: "Use env-based debug flag. Disable in production.", conf: 55 },
403
+ { n: "no-rate-limit", id: "SEC010", re: /\/\/\s*(?:TODO|FIXME).*rate.?limit/i, sev: "high", msg: "Missing rate limiting (noted in TODO)", fix: "Implement rate limiting for public endpoints.", conf: 80, cwe: "CWE-770" },
404
+ ];
405
+
406
+ function scanSecurity(files, opts) {
407
+ const findings = [];
408
+ for (const [, file] of files) {
409
+ if (file.classification.category === "test" && !opts.includeTests) continue;
410
+ if (file.classification.category === "documentation") continue;
411
+ const isCrit = file.classification.isCriticalPath;
412
+ for (let i = 0; i < file.lines.length; i++) {
413
+ const line = file.lines[i], trimmed = line.trim();
414
+ if (/^\s*(?:\/\/|\/\*|\*)/.test(trimmed) && !/TODO|FIXME/i.test(trimmed)) continue;
415
+ for (const p of SEC_PATTERNS) {
416
+ if (!p.re.test(line)) continue;
417
+ findings.push({
418
+ id: `${p.id}-${file.path}-${i+1}`, ruleId: p.id, engine: "security",
419
+ category: "security", severity: escalateSev(p.sev, isCrit),
420
+ confidence: p.conf >= 85 ? "certain" : p.conf >= 65 ? "likely" : "possible",
421
+ confidenceScore: p.conf, file: file.path, line: i+1, code: trimmed,
422
+ message: p.msg, why: "Security vulnerabilities can be exploited to compromise your application.", fix: p.fix,
423
+ autoFixable: false, tags: ["security"], cwe: p.cwe, verified: false,
424
+ });
425
+ break;
426
+ }
427
+ }
428
+ }
429
+ return findings;
430
+ }
431
+
432
+ // ═══════════════════════════════════════════════════════════════════════════════
433
+ // DEDUPLICATION
434
+ // ═══════════════════════════════════════════════════════════════════════════════
435
+
436
+ function dedup(findings) {
437
+ const groups = new Map();
438
+ for (const f of findings) {
439
+ const key = `${f.file}:${f.line}`;
440
+ if (!groups.has(key)) groups.set(key, []);
441
+ groups.get(key).push(f);
442
+ }
443
+ const out = [];
444
+ let suppressed = 0;
445
+ for (const [, group] of groups) {
446
+ if (group.length === 1) { out.push(group[0]); continue; }
447
+ const subs = new Map();
448
+ for (const f of group) {
449
+ if (!subs.has(f.category)) subs.set(f.category, []);
450
+ subs.get(f.category).push(f);
451
+ }
452
+ for (const [, sub] of subs) {
453
+ if (sub.length === 1) { out.push(sub[0]); continue; }
454
+ sub.sort((a, b) => (b.confidenceScore - a.confidenceScore) || ((SEV_ORDER[b.severity]||0) - (SEV_ORDER[a.severity]||0)));
455
+ const best = { ...sub[0] };
456
+ const engines = new Set(sub.map(f => f.engine));
457
+ if (engines.size >= 2) {
458
+ best.verified = true;
459
+ best.confidenceScore = Math.min(99, best.confidenceScore + 5);
460
+ }
461
+ out.push(best);
462
+ suppressed += sub.length - 1;
463
+ }
464
+ }
465
+ out.sort((a, b) => ((SEV_ORDER[b.severity]||0) - (SEV_ORDER[a.severity]||0)) || (b.confidenceScore - a.confidenceScore));
466
+ return { findings: out, suppressed };
467
+ }
468
+
469
+ // ═══════════════════════════════════════════════════════════════════════════════
470
+ // HEALTH SCORE
471
+ // ═══════════════════════════════════════════════════════════════════════════════
472
+
473
+ function healthScore(findings) {
474
+ let score = 100;
475
+ for (const f of findings) {
476
+ switch (f.severity) { case "critical": score -= 15; break; case "high": score -= 8; break; case "medium": score -= 3; break; case "low": score -= 1; break; }
477
+ if (f.category === "hallucinations") score -= 3;
478
+ if (f.ruleId === "FAKE005") score -= 5;
479
+ }
480
+ return Math.max(0, Math.min(100, score));
481
+ }
482
+
483
+ // ═══════════════════════════════════════════════════════════════════════════════
484
+ // INLINE SCAN (lite mode — 3 engines, no build required)
485
+ // ═══════════════════════════════════════════════════════════════════════════════
486
+
487
+ async function inlineScan(projectPath, opts) {
488
+ const startTime = Date.now();
489
+ const scanId = `scan-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`;
490
+
491
+ const files = loadFiles(projectPath, opts);
492
+ const engineResults = [];
493
+ let allFindings = [];
494
+
495
+ // Map category to engine filter
496
+ const catMap = {
497
+ secrets: ["credentials"], credentials: ["credentials"],
498
+ security: ["security"], auth: ["fake-features", "security"],
499
+ "fake-features": ["fake-features"], env: ["fake-features"],
500
+ routes: ["fake-features"], vulns: [],
501
+ };
502
+ const allowedEngines = opts.category && catMap[opts.category] ? catMap[opts.category] : null;
503
+
504
+ const engines = [
505
+ { name: "credentials", fn: () => scanCredentials(files) },
506
+ { name: "fake-features", fn: () => scanFakeFeatures(files, opts) },
507
+ { name: "security", fn: () => scanSecurity(files, opts) },
508
+ ];
509
+
510
+ for (const eng of engines) {
511
+ if (allowedEngines && !allowedEngines.includes(eng.name)) continue;
512
+ if (opts.engines && !opts.engines.includes(eng.name)) continue;
513
+ const t = Date.now();
514
+ try {
515
+ const f = await eng.fn();
516
+ engineResults.push({ engine: eng.name, findings: f.length, durationMs: Date.now() - t, success: true });
517
+ allFindings.push(...f);
518
+ } catch (e) {
519
+ engineResults.push({ engine: eng.name, findings: 0, durationMs: Date.now() - t, success: false, error: e.message });
520
+ }
521
+ }
522
+
523
+ const { findings: deduplicated, suppressed } = dedup(allFindings);
524
+
525
+ // Severity filter
526
+ const minSev = SEV_ORDER[opts.severity] || 1;
527
+ const filtered = deduplicated.filter(f => (SEV_ORDER[f.severity] || 0) >= minSev);
528
+
529
+ const bySeverity = { critical: 0, high: 0, medium: 0, low: 0 };
530
+ const byCategory = {}, byEngine = {};
531
+ for (const f of filtered) {
532
+ bySeverity[f.severity]++;
533
+ byCategory[f.category] = (byCategory[f.category] || 0) + 1;
534
+ byEngine[f.engine] = (byEngine[f.engine] || 0) + 1;
535
+ }
536
+ const dur = Date.now() - startTime;
537
+ const timings = {};
538
+ for (const r of engineResults) timings[r.engine] = r.durationMs;
539
+
540
+ return {
541
+ scanId, timestamp: new Date().toISOString(), findings: filtered,
542
+ summary: { totalFiles: files.size, filesScanned: files.size, totalFindings: filtered.length, bySeverity, byCategory, byEngine, autoFixable: filtered.filter(f => f.autoFixable).length, suppressedDuplicates: suppressed },
543
+ healthScore: healthScore(filtered), engineResults,
544
+ metrics: { durationMs: dur, filesPerSecond: files.size > 0 ? Math.round((files.size / dur) * 1000) : 0, engineTimings: timings },
545
+ };
546
+ }
547
+
548
+ // ═══════════════════════════════════════════════════════════════════════════════
549
+ // PRETTY REPORT
550
+ // ═══════════════════════════════════════════════════════════════════════════════
551
+
552
+ function getHealthBar(score) {
553
+ const filled = Math.round(score / 5), empty = 20 - filled;
554
+ const bar = "\u2588".repeat(filled) + "\u2591".repeat(empty);
555
+ return score >= 80 ? `${c.green}${bar}${c.reset}` : score >= 50 ? `${c.yellow}${bar}${c.reset}` : `${c.red}${bar}${c.reset}`;
556
+ }
557
+
558
+ function printReport(report, mode) {
559
+ const { summary, findings, healthScore: hs, metrics, engineResults } = report;
560
+ console.log("");
561
+ console.log(`${c.magenta}${c.bold} VIBECHECK SCAN${c.reset} ${c.dim}${mode}${c.reset}`);
562
+ console.log(`${c.dim} ${engineResults.length} engines \u00b7 ${summary.totalFiles} files \u00b7 ${metrics.durationMs}ms${c.reset}`);
563
+ console.log("");
564
+ const sc = hs >= 80 ? c.green : hs >= 50 ? c.yellow : c.red;
565
+ console.log(` ${c.bold}Health Score:${c.reset} ${getHealthBar(hs)} ${sc}${hs}%${c.reset}`);
566
+ console.log("");
567
+ console.log(` ${c.bold}Files scanned:${c.reset} ${summary.filesScanned}`);
568
+ console.log(` ${c.bold}Total findings:${c.reset} ${summary.totalFindings}${summary.suppressedDuplicates > 0 ? `${c.dim} (${summary.suppressedDuplicates} dupes suppressed)${c.reset}` : ""}`);
569
+ console.log(` ${c.bold}Auto-fixable:${c.reset} ${summary.autoFixable}`);
570
+ console.log("");
571
+ if (summary.bySeverity.critical > 0) console.log(` ${SEV_ICON.critical} ${c.red}${summary.bySeverity.critical} critical${c.reset}`);
572
+ if (summary.bySeverity.high > 0) console.log(` ${SEV_ICON.high} ${c.yellow}${summary.bySeverity.high} high${c.reset}`);
573
+ if (summary.bySeverity.medium > 0) console.log(` ${SEV_ICON.medium} ${c.cyan}${summary.bySeverity.medium} medium${c.reset}`);
574
+ if (summary.bySeverity.low > 0) console.log(` ${SEV_ICON.low} ${c.dim}${summary.bySeverity.low} low${c.reset}`);
575
+ console.log("");
576
+ console.log(`${c.dim} \u2500\u2500\u2500 Engine Performance \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500${c.reset}`);
577
+ for (const er of engineResults) {
578
+ const st = er.success ? `${c.green}\u2713${c.reset}` : `${c.red}\u2717${c.reset}`;
579
+ const fc = er.findings > 0 ? `${c.yellow} ${er.findings} findings${c.reset}` : `${c.dim} 0 findings${c.reset}`;
580
+ console.log(` ${st} ${er.engine.padEnd(16)}${String(er.durationMs).padStart(5)}ms${fc}`);
581
+ }
582
+ console.log(` ${c.dim} Total: ${metrics.durationMs}ms (${metrics.filesPerSecond} files/sec)${c.reset}`);
583
+ console.log("");
584
+ if (findings.length > 0) {
585
+ console.log(`${c.bold} \u2500\u2500\u2500 Findings \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500${c.reset}`);
586
+ console.log("");
587
+ const byFile = new Map();
588
+ for (const f of findings) { if (!byFile.has(f.file)) byFile.set(f.file, []); byFile.get(f.file).push(f); }
589
+ for (const [file, ff] of byFile) {
590
+ console.log(` ${c.bold}${c.white}${file}${c.reset}`);
591
+ for (const f of ff) {
592
+ console.log(` ${SEV_ICON[f.severity]||"\u25cf"} ${SEV_LABEL[f.severity]||f.severity} ${c.dim}[${f.ruleId}]${c.reset} ${f.message} ${c.dim}:${f.line}${c.reset}`);
593
+ console.log(` ${c.dim}\u2192${c.reset} ${f.fix}`);
594
+ if (f.verified) console.log(` ${c.green}\u2713 Verified by multiple engines${c.reset}`);
595
+ }
596
+ console.log("");
597
+ }
598
+ } else {
599
+ console.log(`${c.green} \u2713 No issues found. Ship it!${c.reset}`);
600
+ console.log("");
601
+ }
602
+ const verdict = hs >= 80 ? `${c.green}${c.bold}SHIP \u2713${c.reset}` : hs >= 50 ? `${c.yellow}${c.bold}WARN \u26a0${c.reset}` : `${c.red}${c.bold}BLOCK \u2717${c.reset}`;
603
+ console.log(` ${c.bold}Verdict:${c.reset} ${verdict}`);
604
+ if (mode && mode.includes("lite")) {
605
+ console.log(`\n ${c.dim}Lite mode (3 engines). Run 'pnpm build' to unlock all 8 engines (160+ patterns).${c.reset}`);
606
+ }
607
+ console.log("");
608
+ }
609
+
610
+ function toSarif(report) {
611
+ return {
612
+ $schema: "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json",
613
+ version: "2.1.0",
614
+ runs: [{ tool: { driver: { name: "VibeCheck Ultimate Scanner", version: "5.0.0", informationUri: "https://vibecheckai.dev",
615
+ rules: report.findings.map(f => ({ id: f.ruleId, shortDescription: { text: f.message }, help: { text: f.fix } })) } },
616
+ results: report.findings.map(f => ({ ruleId: f.ruleId, level: (f.severity === "critical" || f.severity === "high") ? "error" : "warning",
617
+ message: { text: f.message }, locations: [{ physicalLocation: { artifactLocation: { uri: f.file }, region: { startLine: f.line } } }] })) }],
618
+ };
619
+ }
620
+
621
+ // ═══════════════════════════════════════════════════════════════════════════════
622
+ // MAIN RUNNER
623
+ // ═══════════════════════════════════════════════════════════════════════════════
624
+
625
+ async function runScan(args, context = {}) {
626
+ const opts = parseArgs(args);
627
+ if (opts.help) { printHelp(); return 0; }
628
+ const projectPath = path.resolve(opts.path);
629
+
630
+ // Try compiled 8-engine scanner first
631
+ const scanner = getScanner();
632
+ let report;
633
+ let mode;
634
+
635
+ if (scanner) {
636
+ mode = "8 engines \u00b7 160+ patterns";
637
+ if (!opts.quiet && !opts.json && !opts.sarif) console.log(`${c.dim} Scanning ${projectPath}...${c.reset}`);
638
+ const scanOpts = {
639
+ projectRoot: projectPath, severityThreshold: opts.severity,
640
+ engines: opts.engines || undefined, includeTests: opts.includeTests,
641
+ parallel: opts.parallel,
642
+ onProgress: (opts.quiet || opts.json || opts.sarif) ? undefined : (p) => {
643
+ if (p.phase === "loading") process.stdout.write(`${c.dim}\r Loading files...${c.reset}`);
644
+ if (p.phase === "scanning") process.stdout.write(`${c.dim}\r Running engines...${c.reset}`);
645
+ if (p.phase === "complete") process.stdout.write("\r" + " ".repeat(40) + "\r");
646
+ },
647
+ };
648
+ report = await scanner.scan(scanOpts);
649
+ } else {
650
+ mode = "3 engines \u00b7 52 patterns (lite)";
651
+ if (!opts.quiet && !opts.json && !opts.sarif) console.log(`${c.dim} Scanning ${projectPath}...${c.reset}`);
652
+ report = await inlineScan(projectPath, opts);
653
+ }
654
+
655
+ // Output
656
+ if (opts.format === "json" || opts.json) {
657
+ console.log(JSON.stringify(report, null, 2));
658
+ } else if (opts.format === "sarif" || opts.sarif) {
659
+ console.log(JSON.stringify(toSarif(report), null, 2));
660
+ } else {
661
+ printReport(report, mode);
662
+ }
663
+
664
+ // Save to output dir
665
+ if (opts.output) {
666
+ const outDir = path.resolve(opts.output);
667
+ if (!fs.existsSync(outDir)) fs.mkdirSync(outDir, { recursive: true });
668
+ fs.writeFileSync(path.join(outDir, "scan-report.json"), JSON.stringify(report, null, 2));
669
+ if (!opts.quiet) console.log(`${c.dim} Report saved to ${outDir}/scan-report.json${c.reset}`);
670
+ }
671
+
672
+ // Exit code
673
+ if (opts.failOn) {
674
+ const threshold = SEV_ORDER[opts.failOn] || 0;
675
+ for (const f of report.findings) { if ((SEV_ORDER[f.severity] || 0) >= threshold) return EXIT.FINDINGS || 1; }
676
+ }
677
+ if (report.summary.bySeverity.critical > 0) return 2;
678
+ if (report.summary.bySeverity.high > 0) return 1;
679
+ return 0;
680
+ }
681
+
682
+ // ═══════════════════════════════════════════════════════════════════════════════
683
+ // EXPORTS
684
+ // ═══════════════════════════════════════════════════════════════════════════════
685
+
686
+ module.exports = {
687
+ runScan: withErrorHandling(runScan, "Scan failed"),
688
+ };