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,696 @@
1
+ /**
2
+ * vibecheck safelist - Matching Engine
3
+ *
4
+ * ═══════════════════════════════════════════════════════════════════════════════
5
+ * Determines if findings should be suppressed based on safelist entries
6
+ *
7
+ * Features:
8
+ * - Multiple matching strategies (ID, pattern, rule, file)
9
+ * - Command-scoped suppressions
10
+ * - Branch-aware matching
11
+ * - Match tracking and statistics
12
+ * - Dry-run preview support
13
+ * ═══════════════════════════════════════════════════════════════════════════════
14
+ */
15
+
16
+ "use strict";
17
+
18
+ const path = require("path");
19
+ const { SAFELIST_COMMANDS, LIMITS } = require("./schema");
20
+
21
+ // ═══════════════════════════════════════════════════════════════════════════════
22
+ // REGEX CACHE - Avoid recompiling patterns
23
+ // ═══════════════════════════════════════════════════════════════════════════════
24
+
25
+ const regexCache = new Map();
26
+ const MAX_CACHE_SIZE = 100;
27
+
28
+ /**
29
+ * Get or create cached regex
30
+ */
31
+ function getCachedRegex(pattern, flags = "i") {
32
+ const key = `${pattern}:${flags}`;
33
+
34
+ if (regexCache.has(key)) {
35
+ return regexCache.get(key);
36
+ }
37
+
38
+ try {
39
+ const regex = new RegExp(pattern, flags);
40
+
41
+ // Evict oldest if cache is full
42
+ if (regexCache.size >= MAX_CACHE_SIZE) {
43
+ const firstKey = regexCache.keys().next().value;
44
+ regexCache.delete(firstKey);
45
+ }
46
+
47
+ regexCache.set(key, regex);
48
+ return regex;
49
+ } catch (err) {
50
+ return null;
51
+ }
52
+ }
53
+
54
+ // ═══════════════════════════════════════════════════════════════════════════════
55
+ // MATCHING
56
+ // ═══════════════════════════════════════════════════════════════════════════════
57
+
58
+ /**
59
+ * Check if a finding matches a safelist entry
60
+ * @param {Object} finding - Finding to check
61
+ * @param {Object} entry - Safelist entry
62
+ * @param {Object} context - Context (filePath, command, branch, etc.)
63
+ * @returns {{ matches: boolean, reason: string|null, matchType: string|null, confidence: number }}
64
+ */
65
+ function matchEntry(finding, entry, context = {}) {
66
+ const { filePath, command, projectRoot, branch, dryRun } = context;
67
+
68
+ const noMatch = { matches: false, reason: null, matchType: null, confidence: 0 };
69
+
70
+ // ═══════════════════════════════════════════════════════════════════════════
71
+ // EXPIRY CHECK
72
+ // ═══════════════════════════════════════════════════════════════════════════
73
+
74
+ if (entry.lifecycle?.expiresAt) {
75
+ const expiresAt = new Date(entry.lifecycle.expiresAt).getTime();
76
+ if (expiresAt < Date.now()) {
77
+ return { ...noMatch, reason: "Entry expired" };
78
+ }
79
+ }
80
+
81
+ // ═══════════════════════════════════════════════════════════════════════════
82
+ // COMMAND SCOPE CHECK
83
+ // ═══════════════════════════════════════════════════════════════════════════
84
+
85
+ if (entry.scope?.commands && entry.scope.commands.length > 0) {
86
+ if (command && !entry.scope.commands.includes(command)) {
87
+ return { ...noMatch, reason: "Command not in scope" };
88
+ }
89
+ }
90
+
91
+ // ═══════════════════════════════════════════════════════════════════════════
92
+ // BRANCH SCOPE CHECK
93
+ // ═══════════════════════════════════════════════════════════════════════════
94
+
95
+ if (entry.scope?.type === "branch" && entry.scope?.branches) {
96
+ if (branch && !entry.scope.branches.some(b => matchBranch(b, branch))) {
97
+ return { ...noMatch, reason: "Branch not in scope" };
98
+ }
99
+ }
100
+
101
+ // ═══════════════════════════════════════════════════════════════════════════
102
+ // MATCH BY ENTRY TYPE
103
+ // ═══════════════════════════════════════════════════════════════════════════
104
+
105
+ let result;
106
+ switch (entry.type) {
107
+ case "finding":
108
+ result = matchFindingEntry(finding, entry);
109
+ break;
110
+ case "pattern":
111
+ result = matchPatternEntry(finding, entry, { filePath, projectRoot });
112
+ break;
113
+ case "rule":
114
+ result = matchRuleEntry(finding, entry);
115
+ break;
116
+ case "file":
117
+ result = matchFileEntry(finding, entry, { filePath, projectRoot });
118
+ break;
119
+ default:
120
+ return { ...noMatch, reason: "Unknown entry type" };
121
+ }
122
+
123
+ // Add match type and confidence
124
+ if (result.matches) {
125
+ result.matchType = entry.type;
126
+ result.confidence = calculateConfidence(finding, entry, result);
127
+ }
128
+
129
+ return result;
130
+ }
131
+
132
+ /**
133
+ * Calculate match confidence (0-100)
134
+ * Higher = more specific match, less likely to be wrong
135
+ */
136
+ function calculateConfidence(finding, entry, matchResult) {
137
+ let confidence = 50; // Base
138
+
139
+ // Exact ID match = highest confidence
140
+ if (entry.type === "finding" && entry.target?.findingId === finding.id) {
141
+ confidence = 100;
142
+ }
143
+
144
+ // Rule match = high confidence
145
+ if (entry.type === "rule") {
146
+ confidence = 90;
147
+ }
148
+
149
+ // File + line match = high confidence
150
+ if (entry.type === "file" && entry.target?.lines) {
151
+ confidence = 85;
152
+ }
153
+
154
+ // Pattern match = variable confidence
155
+ if (entry.type === "pattern") {
156
+ const pattern = entry.target?.pattern || "";
157
+
158
+ // Longer patterns = more specific = higher confidence
159
+ if (pattern.length > 20) confidence += 15;
160
+ else if (pattern.length > 10) confidence += 5;
161
+
162
+ // File-scoped patterns = higher confidence
163
+ if (entry.target?.file) confidence += 10;
164
+
165
+ // Line-scoped patterns = highest confidence for patterns
166
+ if (entry.target?.lines) confidence += 15;
167
+ }
168
+
169
+ return Math.min(100, Math.max(0, confidence));
170
+ }
171
+
172
+ /**
173
+ * Match branch name against pattern
174
+ */
175
+ function matchBranch(pattern, branch) {
176
+ if (pattern === branch) return true;
177
+ if (pattern === "*") return true;
178
+
179
+ // Support glob patterns
180
+ if (pattern.includes("*")) {
181
+ const regex = pattern
182
+ .replace(/\*/g, ".*")
183
+ .replace(/\?/g, ".");
184
+ return new RegExp(`^${regex}$`).test(branch);
185
+ }
186
+
187
+ return false;
188
+ }
189
+
190
+ /**
191
+ * Match by specific finding ID
192
+ */
193
+ function matchFindingEntry(finding, entry) {
194
+ if (!entry.target?.findingId) {
195
+ return { matches: false, reason: "No finding ID in entry" };
196
+ }
197
+
198
+ if (finding.id === entry.target.findingId) {
199
+ return { matches: true, reason: entry.justification?.reason };
200
+ }
201
+
202
+ return { matches: false, reason: null };
203
+ }
204
+
205
+ /**
206
+ * Match by regex pattern
207
+ */
208
+ function matchPatternEntry(finding, entry, context) {
209
+ if (!entry.target?.pattern) {
210
+ return { matches: false, reason: "No pattern in entry" };
211
+ }
212
+
213
+ // Use cached regex
214
+ const regex = getCachedRegex(entry.target.pattern, "i");
215
+ if (!regex) {
216
+ return { matches: false, reason: "Invalid regex pattern" };
217
+ }
218
+
219
+ // Build list of targets to match against (ordered by specificity)
220
+ const targets = [];
221
+
222
+ // Most specific - exact fields
223
+ if (finding.id) targets.push({ value: finding.id, field: "id" });
224
+ if (finding.ruleId) targets.push({ value: finding.ruleId, field: "ruleId" });
225
+
226
+ // Medium specific - content fields
227
+ if (finding.message) targets.push({ value: finding.message, field: "message" });
228
+ if (finding.title) targets.push({ value: finding.title, field: "title" });
229
+ if (finding.description) targets.push({ value: finding.description, field: "description" });
230
+
231
+ // Less specific - category/type
232
+ if (finding.category) targets.push({ value: finding.category, field: "category" });
233
+ if (finding.type) targets.push({ value: finding.type, field: "type" });
234
+
235
+ // Context - file path
236
+ if (finding.file) targets.push({ value: finding.file, field: "file" });
237
+
238
+ // Try to match
239
+ let matchedField = null;
240
+ const matched = targets.some(t => {
241
+ if (regex.test(t.value)) {
242
+ matchedField = t.field;
243
+ return true;
244
+ }
245
+ return false;
246
+ });
247
+
248
+ if (!matched) {
249
+ return { matches: false, reason: null };
250
+ }
251
+
252
+ // Check file scope if specified
253
+ if (entry.target.file) {
254
+ const { filePath, projectRoot } = context;
255
+ const findingFile = finding.file || filePath;
256
+
257
+ if (!findingFile) {
258
+ return { matches: false, reason: "File scope but no file context" };
259
+ }
260
+
261
+ const normalizedEntry = normalizePath(entry.target.file, projectRoot);
262
+ const normalizedFile = normalizePath(findingFile, projectRoot);
263
+
264
+ // Support glob patterns
265
+ if (entry.target.file.includes("*")) {
266
+ if (!matchGlob(entry.target.file, normalizedFile)) {
267
+ return { matches: false, reason: "File does not match glob" };
268
+ }
269
+ } else {
270
+ // Check for exact match, suffix match, or contains
271
+ if (normalizedFile !== normalizedEntry &&
272
+ !normalizedFile.endsWith("/" + normalizedEntry) &&
273
+ !normalizedFile.includes(normalizedEntry)) {
274
+ return { matches: false, reason: "File does not match" };
275
+ }
276
+ }
277
+ }
278
+
279
+ // Check line range if specified
280
+ if (entry.target.lines) {
281
+ const findingLine = finding.line || finding.startLine;
282
+ if (findingLine) {
283
+ const [start, end] = entry.target.lines;
284
+ if (findingLine < start || findingLine > end) {
285
+ return { matches: false, reason: "Line not in range" };
286
+ }
287
+ } else if (entry.target.lines[0] !== 1 || entry.target.lines[1] !== Infinity) {
288
+ // If we have line constraints but no line info in finding, don't match
289
+ // unless the range is effectively "all lines"
290
+ return { matches: false, reason: "Line info required but not available" };
291
+ }
292
+ }
293
+
294
+ return {
295
+ matches: true,
296
+ reason: entry.justification?.reason,
297
+ matchedField,
298
+ };
299
+ }
300
+
301
+ /**
302
+ * Match by rule ID
303
+ */
304
+ function matchRuleEntry(finding, entry) {
305
+ if (!entry.target?.ruleId) {
306
+ return { matches: false, reason: "No rule ID in entry" };
307
+ }
308
+
309
+ const findingRuleId = finding.ruleId || finding.id?.split("_")[0] || finding.type;
310
+
311
+ if (findingRuleId === entry.target.ruleId) {
312
+ return { matches: true, reason: entry.justification?.reason };
313
+ }
314
+
315
+ return { matches: false, reason: null };
316
+ }
317
+
318
+ /**
319
+ * Match by file path
320
+ */
321
+ function matchFileEntry(finding, entry, context) {
322
+ if (!entry.target?.file) {
323
+ return { matches: false, reason: "No file in entry" };
324
+ }
325
+
326
+ const { filePath, projectRoot } = context;
327
+ const findingFile = finding.file || filePath;
328
+
329
+ if (!findingFile) {
330
+ return { matches: false, reason: "No file context" };
331
+ }
332
+
333
+ const normalizedEntry = normalizePath(entry.target.file, projectRoot);
334
+ const normalizedFile = normalizePath(findingFile, projectRoot);
335
+
336
+ // Support glob patterns
337
+ if (entry.target.file.includes("*")) {
338
+ if (!matchGlob(entry.target.file, normalizedFile)) {
339
+ return { matches: false, reason: "File does not match glob" };
340
+ }
341
+ } else if (!normalizedFile.endsWith(normalizedEntry) && !normalizedFile.includes(normalizedEntry)) {
342
+ return { matches: false, reason: "File does not match" };
343
+ }
344
+
345
+ // Check line range if specified
346
+ if (entry.target.lines && finding.line) {
347
+ const [start, end] = entry.target.lines;
348
+ if (finding.line < start || finding.line > end) {
349
+ return { matches: false, reason: "Line not in range" };
350
+ }
351
+ }
352
+
353
+ return { matches: true, reason: entry.justification?.reason };
354
+ }
355
+
356
+ // ═══════════════════════════════════════════════════════════════════════════════
357
+ // BULK MATCHING
358
+ // ═══════════════════════════════════════════════════════════════════════════════
359
+
360
+ /**
361
+ * Check if a finding is suppressed by any safelist entry
362
+ * @param {Object} finding - Finding to check
363
+ * @param {Object} safelist - Loaded safelist
364
+ * @param {Object} context - Context (filePath, command, etc.)
365
+ * @returns {{ suppressed: boolean, entry: Object|null, reason: string|null, matchType: string|null, matchedField: string|null, confidence: number }}
366
+ */
367
+ function isSuppressed(finding, safelist, context = {}) {
368
+ const noSuppress = {
369
+ suppressed: false,
370
+ entry: null,
371
+ reason: null,
372
+ matchType: null,
373
+ matchedField: null,
374
+ confidence: 0,
375
+ };
376
+
377
+ if (!safelist?.entries || safelist.entries.length === 0) {
378
+ return noSuppress;
379
+ }
380
+
381
+ // Try each entry, track best match (highest confidence)
382
+ let bestMatch = null;
383
+ let bestConfidence = 0;
384
+
385
+ for (const entry of safelist.entries) {
386
+ const result = matchEntry(finding, entry, context);
387
+
388
+ if (result.matches) {
389
+ // For exact ID matches, return immediately (100% confidence)
390
+ if (result.confidence === 100) {
391
+ return {
392
+ suppressed: true,
393
+ entry,
394
+ reason: result.reason,
395
+ matchType: result.matchType,
396
+ matchedField: result.matchedField,
397
+ confidence: result.confidence,
398
+ };
399
+ }
400
+
401
+ // Track best match
402
+ if (result.confidence > bestConfidence) {
403
+ bestMatch = { entry, result };
404
+ bestConfidence = result.confidence;
405
+ }
406
+ }
407
+ }
408
+
409
+ // Return best match if found
410
+ if (bestMatch) {
411
+ return {
412
+ suppressed: true,
413
+ entry: bestMatch.entry,
414
+ reason: bestMatch.result.reason,
415
+ matchType: bestMatch.result.matchType,
416
+ matchedField: bestMatch.result.matchedField,
417
+ confidence: bestMatch.result.confidence,
418
+ };
419
+ }
420
+
421
+ return noSuppress;
422
+ }
423
+
424
+ /**
425
+ * Filter findings, separating active from suppressed
426
+ * @param {Array} findings - Findings to filter
427
+ * @param {Object} safelist - Loaded safelist
428
+ * @param {Object} context - Context
429
+ * @returns {{ active: Array, suppressed: Array, stats: Object }}
430
+ */
431
+ function filterFindings(findings, safelist, context = {}) {
432
+ const active = [];
433
+ const suppressed = [];
434
+ const stats = {
435
+ total: findings.length,
436
+ active: 0,
437
+ suppressed: 0,
438
+ byEntry: {},
439
+ byCategory: {},
440
+ byMatchType: {},
441
+ byConfidence: { high: 0, medium: 0, low: 0 },
442
+ matchedEntries: new Set(),
443
+ };
444
+
445
+ for (const finding of findings) {
446
+ const result = isSuppressed(finding, safelist, {
447
+ ...context,
448
+ filePath: finding.file,
449
+ });
450
+
451
+ if (result.suppressed) {
452
+ suppressed.push({
453
+ ...finding,
454
+ _suppression: {
455
+ entryId: result.entry?.id,
456
+ reason: result.reason,
457
+ category: result.entry?.justification?.category,
458
+ owner: result.entry?.owner?.name,
459
+ ownerTeam: result.entry?.owner?.team,
460
+ expiresAt: result.entry?.lifecycle?.expiresAt,
461
+ matchType: result.matchType,
462
+ matchedField: result.matchedField,
463
+ confidence: result.confidence,
464
+ },
465
+ });
466
+
467
+ stats.suppressed++;
468
+
469
+ // Track by entry
470
+ const entryId = result.entry?.id || "unknown";
471
+ stats.byEntry[entryId] = (stats.byEntry[entryId] || 0) + 1;
472
+ stats.matchedEntries.add(entryId);
473
+
474
+ // Track by category
475
+ const category = result.entry?.justification?.category || "unknown";
476
+ stats.byCategory[category] = (stats.byCategory[category] || 0) + 1;
477
+
478
+ // Track by match type
479
+ const matchType = result.matchType || "unknown";
480
+ stats.byMatchType[matchType] = (stats.byMatchType[matchType] || 0) + 1;
481
+
482
+ // Track by confidence
483
+ const confidence = result.confidence || 0;
484
+ if (confidence >= 80) stats.byConfidence.high++;
485
+ else if (confidence >= 50) stats.byConfidence.medium++;
486
+ else stats.byConfidence.low++;
487
+ } else {
488
+ active.push(finding);
489
+ stats.active++;
490
+ }
491
+ }
492
+
493
+ // Convert Set to Array for serialization
494
+ stats.matchedEntries = Array.from(stats.matchedEntries);
495
+
496
+ return { active, suppressed, stats };
497
+ }
498
+
499
+ /**
500
+ * Preview what would be suppressed (dry-run mode)
501
+ * @param {Array} findings - Findings to check
502
+ * @param {Object} safelist - Loaded safelist
503
+ * @param {Object} context - Context
504
+ * @returns {{ wouldSuppress: Array, stats: Object }}
505
+ */
506
+ function previewSuppression(findings, safelist, context = {}) {
507
+ const result = filterFindings(findings, safelist, { ...context, dryRun: true });
508
+
509
+ return {
510
+ wouldSuppress: result.suppressed.map(f => ({
511
+ finding: {
512
+ id: f.id,
513
+ message: f.message,
514
+ file: f.file,
515
+ line: f.line,
516
+ category: f.category,
517
+ },
518
+ suppressedBy: {
519
+ entryId: f._suppression.entryId,
520
+ reason: f._suppression.reason,
521
+ category: f._suppression.category,
522
+ owner: f._suppression.owner,
523
+ matchType: f._suppression.matchType,
524
+ confidence: f._suppression.confidence,
525
+ },
526
+ })),
527
+ stats: result.stats,
528
+ };
529
+ }
530
+
531
+ /**
532
+ * Find which safelist entries are unused (match nothing)
533
+ * @param {Array} findings - All findings
534
+ * @param {Object} safelist - Loaded safelist
535
+ * @param {Object} context - Context
536
+ * @returns {Array} Entries that matched nothing
537
+ */
538
+ function findUnusedEntries(findings, safelist, context = {}) {
539
+ const { stats } = filterFindings(findings, safelist, context);
540
+ const matchedIds = new Set(stats.matchedEntries);
541
+
542
+ return safelist.entries.filter(entry => !matchedIds.has(entry.id));
543
+ }
544
+
545
+ /**
546
+ * Update match counts for entries that matched
547
+ * @param {Object} safelist - Safelist with _source annotations
548
+ * @param {Array} matchedEntryIds - Entry IDs that matched
549
+ * @param {Object} store - Store module for saving
550
+ * @param {string} projectRoot - Project root
551
+ */
552
+ function updateMatchCounts(safelist, matchedEntryIds, store, projectRoot) {
553
+ const uniqueIds = [...new Set(matchedEntryIds)];
554
+ const now = new Date().toISOString();
555
+
556
+ for (const entryId of uniqueIds) {
557
+ store.updateEntry(projectRoot, entryId, {
558
+ lifecycle: {
559
+ lastMatchedAt: now,
560
+ matchCount: (safelist.entries.find(e => e.id === entryId)?.lifecycle?.matchCount || 0) + 1,
561
+ },
562
+ });
563
+ }
564
+ }
565
+
566
+ // ═══════════════════════════════════════════════════════════════════════════════
567
+ // UTILITIES
568
+ // ═══════════════════════════════════════════════════════════════════════════════
569
+
570
+ /**
571
+ * Normalize file path for comparison
572
+ */
573
+ function normalizePath(filePath, projectRoot) {
574
+ if (!filePath) return "";
575
+
576
+ let normalized = filePath.replace(/\\/g, "/");
577
+
578
+ // Remove project root prefix if present
579
+ if (projectRoot) {
580
+ const normalizedRoot = projectRoot.replace(/\\/g, "/");
581
+ if (normalized.startsWith(normalizedRoot)) {
582
+ normalized = normalized.substring(normalizedRoot.length);
583
+ }
584
+ }
585
+
586
+ // Remove leading slash
587
+ if (normalized.startsWith("/")) {
588
+ normalized = normalized.substring(1);
589
+ }
590
+
591
+ return normalized;
592
+ }
593
+
594
+ /**
595
+ * Simple glob matching
596
+ */
597
+ function matchGlob(pattern, filePath) {
598
+ const regexPattern = pattern
599
+ .replace(/\./g, "\\.")
600
+ .replace(/\*\*/g, "{{GLOBSTAR}}")
601
+ .replace(/\*/g, "[^/]*")
602
+ .replace(/\?/g, "[^/]")
603
+ .replace(/{{GLOBSTAR}}/g, ".*");
604
+
605
+ return new RegExp(`^${regexPattern}$`, "i").test(filePath) ||
606
+ new RegExp(`${regexPattern}$`, "i").test(filePath);
607
+ }
608
+
609
+ /**
610
+ * Get entries that are expiring soon
611
+ * @param {Object} safelist - Loaded safelist
612
+ * @param {number} days - Days threshold
613
+ * @returns {Array} Entries expiring within threshold
614
+ */
615
+ function getExpiringSoon(safelist, days = 7) {
616
+ const threshold = Date.now() + (days * 24 * 60 * 60 * 1000);
617
+
618
+ return safelist.entries.filter(entry => {
619
+ if (!entry.lifecycle?.expiresAt) return false;
620
+ const expiresAt = new Date(entry.lifecycle.expiresAt).getTime();
621
+ return expiresAt > Date.now() && expiresAt <= threshold;
622
+ });
623
+ }
624
+
625
+ /**
626
+ * Get entries due for review
627
+ * @param {Object} safelist - Loaded safelist
628
+ * @returns {Array} Entries due for review
629
+ */
630
+ function getDueForReview(safelist) {
631
+ const now = Date.now();
632
+
633
+ return safelist.entries.filter(entry => {
634
+ if (!entry.lifecycle?.reviewAt) return false;
635
+ return new Date(entry.lifecycle.reviewAt).getTime() <= now;
636
+ });
637
+ }
638
+
639
+ /**
640
+ * Get expired entries
641
+ * @param {Object} safelist - Loaded safelist
642
+ * @returns {Array} Expired entries
643
+ */
644
+ function getExpired(safelist) {
645
+ const now = Date.now();
646
+
647
+ return safelist.entries.filter(entry => {
648
+ if (!entry.lifecycle?.expiresAt) return false;
649
+ return new Date(entry.lifecycle.expiresAt).getTime() < now;
650
+ });
651
+ }
652
+
653
+ /**
654
+ * Get unused entries (never matched)
655
+ * @param {Object} safelist - Loaded safelist
656
+ * @param {number} days - Days since creation
657
+ * @returns {Array} Entries that have never matched
658
+ */
659
+ function getUnused(safelist, days = 30) {
660
+ const threshold = Date.now() - (days * 24 * 60 * 60 * 1000);
661
+
662
+ return safelist.entries.filter(entry => {
663
+ if (entry.lifecycle?.matchCount > 0) return false;
664
+ const createdAt = new Date(entry.lifecycle?.createdAt).getTime();
665
+ return createdAt < threshold;
666
+ });
667
+ }
668
+
669
+ // ═══════════════════════════════════════════════════════════════════════════════
670
+ // EXPORTS
671
+ // ═══════════════════════════════════════════════════════════════════════════════
672
+
673
+ module.exports = {
674
+ // Core matching
675
+ matchEntry,
676
+ isSuppressed,
677
+ filterFindings,
678
+
679
+ // Preview & analysis
680
+ previewSuppression,
681
+ findUnusedEntries,
682
+ calculateConfidence,
683
+
684
+ // Lifecycle management
685
+ updateMatchCounts,
686
+ getExpiringSoon,
687
+ getDueForReview,
688
+ getExpired,
689
+ getUnused,
690
+
691
+ // Utilities
692
+ normalizePath,
693
+ matchGlob,
694
+ matchBranch,
695
+ getCachedRegex,
696
+ };