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,928 @@
1
+ /**
2
+ * vibecheck classify - Inventory Authority Command
3
+ *
4
+ * Produces duplication maps, legacy code maps, and risk classifications.
5
+ * Available on FREE tier - read-only analysis with no enforcement.
6
+ *
7
+ * Output:
8
+ * - Duplication map (exact, near, semantic duplicates)
9
+ * - Legacy map (deprecated, backup, obsolete, dead code)
10
+ * - Risk classifications (LOW, MEDIUM, HIGH, CRITICAL)
11
+ *
12
+ * Part of the Authority System - "The AI That Says No"
13
+ */
14
+
15
+ const path = require("path");
16
+ const fs = require("fs");
17
+ const crypto = require("crypto");
18
+ const { withErrorHandling, createUserError } = require("./lib/error-handler");
19
+ const { parseGlobalFlags, shouldShowBanner } = require("./lib/global-flags");
20
+ const { EXIT } = require("./lib/exit-codes");
21
+
22
+ // Engine V2 support (when VIBECHECK_ENGINE_V2=1)
23
+ let createIndex = null;
24
+ try {
25
+ createIndex = require("./lib/engine").createIndex;
26
+ } catch {
27
+ // Engine V2 not available, use legacy file walking
28
+ }
29
+
30
+ // ═══════════════════════════════════════════════════════════════════════════════
31
+ // TERMINAL UI
32
+ // ═══════════════════════════════════════════════════════════════════════════════
33
+
34
+ const {
35
+ ansi,
36
+ colors,
37
+ Spinner,
38
+ } = require("./lib/terminal-ui");
39
+
40
+ const BANNER = `
41
+ ${ansi.rgb(0, 200, 255)} ██╗ ██╗██╗██████╗ ███████╗ ██████╗██╗ ██╗███████╗ ██████╗██╗ ██╗${ansi.reset}
42
+ ${ansi.rgb(30, 180, 255)} ██║ ██║██║██╔══██╗██╔════╝██╔════╝██║ ██║██╔════╝██╔════╝██║ ██╔╝${ansi.reset}
43
+ ${ansi.rgb(60, 160, 255)} ██║ ██║██║██████╔╝█████╗ ██║ ███████║█████╗ ██║ █████╔╝ ${ansi.reset}
44
+ ${ansi.rgb(90, 140, 255)} ╚██╗ ██╔╝██║██╔══██╗██╔══╝ ██║ ██╔══██║██╔══╝ ██║ ██╔═██╗ ${ansi.reset}
45
+ ${ansi.rgb(120, 120, 255)} ╚████╔╝ ██║██████╔╝███████╗╚██████╗██║ ██║███████╗╚██████╗██║ ██╗${ansi.reset}
46
+ ${ansi.rgb(150, 100, 255)} ╚═══╝ ╚═╝╚═════╝ ╚══════╝ ╚═════╝╚═╝ ╚═╝╚══════╝ ╚═════╝╚═╝ ╚═╝${ansi.reset}
47
+
48
+ ${ansi.dim} ┌─────────────────────────────────────────────────────────────────────┐${ansi.reset}
49
+ ${ansi.dim} │${ansi.reset} ${ansi.rgb(255, 255, 255)}${ansi.bold}Authority System${ansi.reset} ${ansi.dim}•${ansi.reset} ${ansi.rgb(200, 200, 200)}Inventory${ansi.reset} ${ansi.dim}•${ansi.reset} ${ansi.rgb(150, 150, 150)}Read-Only Classification${ansi.reset} ${ansi.dim}│${ansi.reset}
50
+ ${ansi.dim} └─────────────────────────────────────────────────────────────────────┘${ansi.reset}
51
+ `;
52
+
53
+ function printBanner() {
54
+ console.log(BANNER);
55
+ }
56
+
57
+ // ═══════════════════════════════════════════════════════════════════════════════
58
+ // ARGS PARSER
59
+ // ═══════════════════════════════════════════════════════════════════════════════
60
+
61
+ function parseArgs(args) {
62
+ const { flags: globalFlags, cleanArgs } = parseGlobalFlags(args);
63
+
64
+ const opts = {
65
+ path: globalFlags.path || process.cwd(),
66
+ json: globalFlags.json || false,
67
+ verbose: globalFlags.verbose || false,
68
+ help: globalFlags.help || false,
69
+ noBanner: globalFlags.noBanner || false,
70
+ ci: globalFlags.ci || false,
71
+ quiet: globalFlags.quiet || false,
72
+ // Classification options
73
+ includeNear: true, // Include near-duplicates
74
+ includeSemantic: false, // Include semantic duplicates (slower)
75
+ threshold: 0.8, // Similarity threshold (0-1)
76
+ maxFiles: 5000, // Max files to analyze
77
+ // Output options
78
+ output: null, // Output file path
79
+ format: 'table', // table, json, markdown
80
+ };
81
+
82
+ for (let i = 0; i < cleanArgs.length; i++) {
83
+ const arg = cleanArgs[i];
84
+
85
+ if (arg === '--include-semantic' || arg === '-s') opts.includeSemantic = true;
86
+ else if (arg === '--no-near') opts.includeNear = false;
87
+ else if (arg === '--threshold' || arg === '-t') opts.threshold = parseFloat(cleanArgs[++i]) || 0.8;
88
+ else if (arg === '--max-files') opts.maxFiles = parseInt(cleanArgs[++i], 10) || 5000;
89
+ else if (arg === '--output' || arg === '-o') opts.output = cleanArgs[++i];
90
+ else if (arg === '--format' || arg === '-f') opts.format = cleanArgs[++i] || 'table';
91
+ else if (arg === '--path' || arg === '-p') opts.path = cleanArgs[++i] || process.cwd();
92
+ else if (arg.startsWith('--path=')) opts.path = arg.split('=')[1];
93
+ else if (!arg.startsWith('-')) opts.path = path.resolve(arg);
94
+ }
95
+
96
+ return opts;
97
+ }
98
+
99
+ function printHelp(showBanner = true) {
100
+ if (showBanner && shouldShowBanner({})) {
101
+ printBanner();
102
+ }
103
+ console.log(`
104
+ ${ansi.bold}USAGE${ansi.reset}
105
+ ${colors.accent}vibecheck classify${ansi.reset} [path] [options]
106
+
107
+ ${ansi.dim}Authority: inventory (FREE tier)${ansi.reset}
108
+
109
+ Produces a read-only inventory of your codebase:
110
+ - Duplication map (exact, near, semantic duplicates)
111
+ - Legacy code map (deprecated, backup, obsolete, dead)
112
+ - Risk classifications (LOW, MEDIUM, HIGH, CRITICAL)
113
+
114
+ ${ansi.bold}OPTIONS${ansi.reset}
115
+ ${colors.accent}--include-semantic, -s${ansi.reset} Include semantic duplicates ${ansi.dim}(slower)${ansi.reset}
116
+ ${colors.accent}--no-near${ansi.reset} Skip near-duplicate detection
117
+ ${colors.accent}--threshold, -t <n>${ansi.reset} Similarity threshold 0-1 ${ansi.dim}(default: 0.8)${ansi.reset}
118
+ ${colors.accent}--max-files <n>${ansi.reset} Max files to analyze ${ansi.dim}(default: 5000)${ansi.reset}
119
+
120
+ ${ansi.bold}OUTPUT OPTIONS${ansi.reset}
121
+ ${colors.accent}--json${ansi.reset} Output as JSON
122
+ ${colors.accent}--output, -o <file>${ansi.reset} Save output to file
123
+ ${colors.accent}--format, -f <fmt>${ansi.reset} Format: table, json, markdown
124
+
125
+ ${ansi.bold}GLOBAL OPTIONS${ansi.reset}
126
+ ${colors.accent}--path, -p <dir>${ansi.reset} Run in specified directory
127
+ ${colors.accent}--verbose, -v${ansi.reset} Show detailed progress
128
+ ${colors.accent}--quiet, -q${ansi.reset} Suppress non-essential output
129
+ ${colors.accent}--ci${ansi.reset} CI mode
130
+ ${colors.accent}--help, -h${ansi.reset} Show this help
131
+
132
+ ${ansi.bold}💡 EXAMPLES${ansi.reset}
133
+
134
+ ${ansi.dim}# Quick inventory of current directory${ansi.reset}
135
+ vibecheck classify
136
+
137
+ ${ansi.dim}# JSON output for processing${ansi.reset}
138
+ vibecheck classify --json > inventory.json
139
+
140
+ ${ansi.dim}# Include semantic duplicates (deeper analysis)${ansi.reset}
141
+ vibecheck classify --include-semantic
142
+
143
+ ${ansi.dim}# Classify specific directory${ansi.reset}
144
+ vibecheck classify ./packages/core
145
+
146
+ ${ansi.bold}📊 OUTPUT${ansi.reset}
147
+ Results include:
148
+ - duplicationMap: Files with duplicated code
149
+ - legacyMap: Deprecated/obsolete code locations
150
+ - riskClassifications: Per-file risk levels
151
+ - summary: Statistics overview
152
+
153
+ ${ansi.bold}🔗 RELATED COMMANDS${ansi.reset}
154
+ ${colors.accent}vibecheck approve${ansi.reset} Get authority verdicts ${ansi.cyan}[STARTER]${ansi.reset}
155
+ ${colors.accent}vibecheck scan${ansi.reset} Full code analysis
156
+
157
+ ${ansi.dim}─────────────────────────────────────────────────────────────${ansi.reset}
158
+ ${ansi.dim}Documentation: https://docs.vibecheckai.dev/cli/classify${ansi.reset}
159
+ `);
160
+ }
161
+
162
+ // ═══════════════════════════════════════════════════════════════════════════════
163
+ // INVENTORY ANALYSIS ENGINE
164
+ // ═══════════════════════════════════════════════════════════════════════════════
165
+
166
+ /**
167
+ * Legacy code indicators
168
+ */
169
+ const LEGACY_INDICATORS = [
170
+ { pattern: /\.old\.(js|ts|tsx|jsx)$/i, type: 'backup', confidence: 0.9 },
171
+ { pattern: /\.bak\.(js|ts|tsx|jsx)$/i, type: 'backup', confidence: 0.95 },
172
+ { pattern: /\.backup\.(js|ts|tsx|jsx)$/i, type: 'backup', confidence: 0.95 },
173
+ { pattern: /\.deprecated\.(js|ts|tsx|jsx)$/i, type: 'deprecated', confidence: 0.9 },
174
+ { pattern: /\.legacy\.(js|ts|tsx|jsx)$/i, type: 'obsolete', confidence: 0.85 },
175
+ { pattern: /_old\.(js|ts|tsx|jsx)$/i, type: 'backup', confidence: 0.8 },
176
+ { pattern: /-old\.(js|ts|tsx|jsx)$/i, type: 'backup', confidence: 0.8 },
177
+ { pattern: /\/deprecated\//i, type: 'deprecated', confidence: 0.85 },
178
+ { pattern: /\/legacy\//i, type: 'obsolete', confidence: 0.8 },
179
+ { pattern: /\/old\//i, type: 'backup', confidence: 0.7 },
180
+ ];
181
+
182
+ /**
183
+ * Content patterns indicating deprecated code
184
+ */
185
+ const DEPRECATED_CONTENT_PATTERNS = [
186
+ { regex: /@deprecated/gi, type: 'deprecated', confidence: 0.95 },
187
+ { regex: /TODO:\s*remove/gi, type: 'obsolete', confidence: 0.7 },
188
+ { regex: /FIXME:\s*delete/gi, type: 'obsolete', confidence: 0.7 },
189
+ { regex: /\/\/\s*LEGACY/gi, type: 'obsolete', confidence: 0.75 },
190
+ { regex: /\/\/\s*DEPRECATED/gi, type: 'deprecated', confidence: 0.9 },
191
+ { regex: /\/\*\*?\s*@deprecated/gi, type: 'deprecated', confidence: 0.95 },
192
+ ];
193
+
194
+ /**
195
+ * High-risk path patterns
196
+ */
197
+ const HIGH_RISK_PATHS = [
198
+ { pattern: /\/auth\//i, reason: 'Authentication code', level: 'HIGH' },
199
+ { pattern: /\/security\//i, reason: 'Security-related code', level: 'HIGH' },
200
+ { pattern: /\/migrations?\//i, reason: 'Database migrations', level: 'CRITICAL' },
201
+ { pattern: /\/billing\//i, reason: 'Billing/payment code', level: 'CRITICAL' },
202
+ { pattern: /\/stripe\//i, reason: 'Payment processing', level: 'CRITICAL' },
203
+ { pattern: /\.env/i, reason: 'Environment configuration', level: 'CRITICAL' },
204
+ { pattern: /secrets?\.(js|ts|json)/i, reason: 'Secrets configuration', level: 'CRITICAL' },
205
+ { pattern: /\/prisma\//i, reason: 'Database schema', level: 'HIGH' },
206
+ { pattern: /\/middleware\//i, reason: 'Request middleware', level: 'MEDIUM' },
207
+ ];
208
+
209
+ /**
210
+ * Excluded directories
211
+ */
212
+ const EXCLUDED_DIRS = new Set([
213
+ 'node_modules',
214
+ '.git',
215
+ 'dist',
216
+ 'build',
217
+ '.next',
218
+ 'coverage',
219
+ '.vibecheck',
220
+ '__pycache__',
221
+ '.cache',
222
+ 'vendor',
223
+ ]);
224
+
225
+ /**
226
+ * Find all source files using RepoIndex (V2) or legacy walk
227
+ */
228
+ async function findSourceFiles(rootPath, maxFiles) {
229
+ const extensions = new Set(['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs']);
230
+
231
+ // Use RepoIndex if V2 is enabled and available
232
+ if (process.env.VIBECHECK_ENGINE_V2 === "1" && createIndex) {
233
+ const index = await createIndex(rootPath);
234
+ const jsFiles = index.getJsFiles();
235
+
236
+ const files = jsFiles
237
+ .slice(0, maxFiles)
238
+ .map(f => ({
239
+ path: f.abs,
240
+ relativePath: f.rel,
241
+ name: path.basename(f.abs),
242
+ ext: f.ext,
243
+ _indexContent: index.getContent(f.abs), // Cache content from index
244
+ }));
245
+
246
+ // Store index reference for cleanup
247
+ files._index = index;
248
+ return files;
249
+ }
250
+
251
+ // Legacy file walking
252
+ const files = [];
253
+
254
+ async function walk(dir, depth = 0) {
255
+ if (depth > 20 || files.length >= maxFiles) return;
256
+
257
+ try {
258
+ const entries = await fs.promises.readdir(dir, { withFileTypes: true });
259
+
260
+ for (const entry of entries) {
261
+ if (files.length >= maxFiles) break;
262
+
263
+ const fullPath = path.join(dir, entry.name);
264
+ const relativePath = path.relative(rootPath, fullPath);
265
+
266
+ if (entry.isDirectory()) {
267
+ if (!EXCLUDED_DIRS.has(entry.name) && !entry.name.startsWith('.')) {
268
+ await walk(fullPath, depth + 1);
269
+ }
270
+ } else if (entry.isFile()) {
271
+ const ext = path.extname(entry.name).toLowerCase();
272
+ if (extensions.has(ext)) {
273
+ files.push({
274
+ path: fullPath,
275
+ relativePath,
276
+ name: entry.name,
277
+ ext,
278
+ });
279
+ }
280
+ }
281
+ }
282
+ } catch (err) {
283
+ // Skip inaccessible directories
284
+ }
285
+ }
286
+
287
+ await walk(rootPath);
288
+ return files;
289
+ }
290
+
291
+ /**
292
+ * Calculate file hash for duplicate detection
293
+ */
294
+ function hashContent(content) {
295
+ return crypto.createHash('sha256').update(content).digest('hex').slice(0, 16);
296
+ }
297
+
298
+ /**
299
+ * Normalize content for near-duplicate detection
300
+ */
301
+ function normalizeContent(content) {
302
+ return content
303
+ .replace(/\/\/.*$/gm, '') // Remove single-line comments
304
+ .replace(/\/\*[\s\S]*?\*\//g, '') // Remove multi-line comments
305
+ .replace(/\s+/g, ' ') // Normalize whitespace
306
+ .replace(/['"`]/g, '"') // Normalize quotes
307
+ .trim();
308
+ }
309
+
310
+ /**
311
+ * Calculate Jaccard similarity between two sets
312
+ */
313
+ function jaccardSimilarity(set1, set2) {
314
+ const intersection = new Set([...set1].filter(x => set2.has(x)));
315
+ const union = new Set([...set1, ...set2]);
316
+ return union.size === 0 ? 0 : intersection.size / union.size;
317
+ }
318
+
319
+ /**
320
+ * Tokenize content for similarity comparison
321
+ */
322
+ function tokenize(content) {
323
+ return new Set(
324
+ content
325
+ .split(/[\s\(\)\{\}\[\];,.:=<>!&|?]+/)
326
+ .filter(t => t.length > 2)
327
+ .map(t => t.toLowerCase())
328
+ );
329
+ }
330
+
331
+ /**
332
+ * Detect legacy code indicators
333
+ */
334
+ function detectLegacyIndicators(filePath, content) {
335
+ const indicators = [];
336
+
337
+ // Check file path patterns
338
+ for (const { pattern, type, confidence } of LEGACY_INDICATORS) {
339
+ if (pattern.test(filePath)) {
340
+ indicators.push({ type, confidence, evidence: `File path matches: ${pattern}` });
341
+ }
342
+ }
343
+
344
+ // Check content patterns
345
+ for (const { regex, type, confidence } of DEPRECATED_CONTENT_PATTERNS) {
346
+ const matches = content.match(regex);
347
+ if (matches) {
348
+ indicators.push({
349
+ type,
350
+ confidence,
351
+ evidence: `Found ${matches.length}x: ${regex.source}`,
352
+ count: matches.length,
353
+ });
354
+ }
355
+ }
356
+
357
+ return indicators;
358
+ }
359
+
360
+ /**
361
+ * Classify file risk level
362
+ */
363
+ function classifyRisk(filePath, content) {
364
+ // Check path patterns
365
+ for (const { pattern, reason, level } of HIGH_RISK_PATHS) {
366
+ if (pattern.test(filePath)) {
367
+ return { level, reason, tags: [reason.toLowerCase().replace(/\s+/g, '-')] };
368
+ }
369
+ }
370
+
371
+ // Check content indicators
372
+ const indicators = [];
373
+
374
+ if (/process\.env\./i.test(content)) {
375
+ indicators.push('uses-env-vars');
376
+ }
377
+ if (/import.*['"](crypto|bcrypt|argon2|jose)['"]/i.test(content)) {
378
+ indicators.push('cryptography');
379
+ }
380
+ if (/sql|query|execute/i.test(content) && /where|select|insert|update|delete/i.test(content)) {
381
+ indicators.push('database-operations');
382
+ }
383
+ if (/(Bearer|Authorization|api[_-]?key|secret|token)/i.test(content)) {
384
+ indicators.push('auth-related');
385
+ }
386
+
387
+ if (indicators.includes('cryptography') || indicators.includes('auth-related')) {
388
+ return { level: 'HIGH', reason: 'Security-sensitive operations', tags: indicators };
389
+ }
390
+ if (indicators.includes('database-operations')) {
391
+ return { level: 'MEDIUM', reason: 'Database operations', tags: indicators };
392
+ }
393
+ if (indicators.includes('uses-env-vars')) {
394
+ return { level: 'MEDIUM', reason: 'Environment-dependent', tags: indicators };
395
+ }
396
+
397
+ return { level: 'LOW', reason: 'Standard code', tags: [] };
398
+ }
399
+
400
+ /**
401
+ * Run inventory analysis
402
+ */
403
+ async function runInventoryAnalysis(projectPath, opts, spinner) {
404
+ const startTime = Date.now();
405
+
406
+ // Find source files
407
+ if (spinner) spinner.update('Discovering source files...');
408
+ const files = await findSourceFiles(projectPath, opts.maxFiles);
409
+
410
+ if (files.length === 0) {
411
+ return {
412
+ duplicationMap: [],
413
+ legacyMap: [],
414
+ riskClassifications: [],
415
+ summary: {
416
+ totalFiles: 0,
417
+ duplicatedFiles: 0,
418
+ legacyFiles: 0,
419
+ highRiskFiles: 0,
420
+ totalDuplicateLines: 0,
421
+ },
422
+ };
423
+ }
424
+
425
+ if (spinner) spinner.update(`Analyzing ${files.length} files...`);
426
+
427
+ // Read all file contents (use cached content from RepoIndex when available)
428
+ const fileContents = new Map();
429
+ const fileHashes = new Map();
430
+ const normalizedHashes = new Map();
431
+
432
+ for (const file of files) {
433
+ try {
434
+ // Use cached content from RepoIndex if available (V2 optimization)
435
+ const content = file._indexContent || await fs.promises.readFile(file.path, 'utf-8');
436
+ const lines = content.split('\n').length;
437
+ fileContents.set(file.relativePath, { content, lines });
438
+
439
+ // Calculate hashes
440
+ const hash = hashContent(content);
441
+ const normalizedHash = hashContent(normalizeContent(content));
442
+
443
+ fileHashes.set(file.relativePath, hash);
444
+ normalizedHashes.set(file.relativePath, normalizedHash);
445
+ } catch (err) {
446
+ // Skip unreadable files
447
+ }
448
+ }
449
+
450
+ // Cleanup RepoIndex cache if used
451
+ if (files._index) {
452
+ files._index.clearContentCache();
453
+ }
454
+
455
+ // ═══════════════════════════════════════════════════════════════════════════
456
+ // DUPLICATE DETECTION
457
+ // ═══════════════════════════════════════════════════════════════════════════
458
+
459
+ if (spinner) spinner.update('Detecting duplicates...');
460
+
461
+ const duplicationMap = [];
462
+ const exactDuplicates = new Map(); // hash -> [files]
463
+ const nearDuplicates = new Map(); // normalizedHash -> [files]
464
+
465
+ // Group by exact hash
466
+ for (const [filePath, hash] of fileHashes.entries()) {
467
+ if (!exactDuplicates.has(hash)) {
468
+ exactDuplicates.set(hash, []);
469
+ }
470
+ exactDuplicates.get(hash).push(filePath);
471
+ }
472
+
473
+ // Find exact duplicates
474
+ for (const [hash, files] of exactDuplicates.entries()) {
475
+ if (files.length > 1) {
476
+ const primary = files[0];
477
+ const duplicates = files.slice(1);
478
+ const { lines } = fileContents.get(primary) || { lines: 0 };
479
+
480
+ duplicationMap.push({
481
+ primary,
482
+ duplicates,
483
+ similarity: 1.0,
484
+ type: 'exact',
485
+ lineCount: lines,
486
+ });
487
+ }
488
+ }
489
+
490
+ // Group by normalized hash for near-duplicates
491
+ if (opts.includeNear) {
492
+ for (const [filePath, hash] of normalizedHashes.entries()) {
493
+ if (!nearDuplicates.has(hash)) {
494
+ nearDuplicates.set(hash, []);
495
+ }
496
+ nearDuplicates.get(hash).push(filePath);
497
+ }
498
+
499
+ // Find near-duplicates (not already exact)
500
+ const exactPairs = new Set();
501
+ for (const entry of duplicationMap) {
502
+ for (const dup of entry.duplicates) {
503
+ exactPairs.add(`${entry.primary}:${dup}`);
504
+ exactPairs.add(`${dup}:${entry.primary}`);
505
+ }
506
+ }
507
+
508
+ for (const [hash, files] of nearDuplicates.entries()) {
509
+ if (files.length > 1) {
510
+ const primary = files[0];
511
+ const potentialDups = files.slice(1).filter(f => !exactPairs.has(`${primary}:${f}`));
512
+
513
+ if (potentialDups.length > 0) {
514
+ const { lines } = fileContents.get(primary) || { lines: 0 };
515
+ duplicationMap.push({
516
+ primary,
517
+ duplicates: potentialDups,
518
+ similarity: 0.95, // High similarity due to normalized match
519
+ type: 'near',
520
+ lineCount: lines,
521
+ });
522
+ }
523
+ }
524
+ }
525
+ }
526
+
527
+ // Semantic duplicates (token-based similarity)
528
+ if (opts.includeSemantic) {
529
+ if (spinner) spinner.update('Computing semantic similarity...');
530
+
531
+ const tokenSets = new Map();
532
+ for (const [filePath, { content }] of fileContents.entries()) {
533
+ tokenSets.set(filePath, tokenize(content));
534
+ }
535
+
536
+ const processedPairs = new Set();
537
+ const filePaths = Array.from(fileContents.keys());
538
+
539
+ for (let i = 0; i < filePaths.length && i < 500; i++) {
540
+ for (let j = i + 1; j < filePaths.length && j < 500; j++) {
541
+ const file1 = filePaths[i];
542
+ const file2 = filePaths[j];
543
+ const pairKey = `${file1}:${file2}`;
544
+
545
+ if (processedPairs.has(pairKey)) continue;
546
+ processedPairs.add(pairKey);
547
+
548
+ const tokens1 = tokenSets.get(file1);
549
+ const tokens2 = tokenSets.get(file2);
550
+
551
+ const similarity = jaccardSimilarity(tokens1, tokens2);
552
+
553
+ if (similarity >= opts.threshold) {
554
+ // Check not already in duplicationMap
555
+ const existing = duplicationMap.find(d =>
556
+ (d.primary === file1 && d.duplicates.includes(file2)) ||
557
+ (d.primary === file2 && d.duplicates.includes(file1))
558
+ );
559
+
560
+ if (!existing) {
561
+ const { lines } = fileContents.get(file1) || { lines: 0 };
562
+ duplicationMap.push({
563
+ primary: file1,
564
+ duplicates: [file2],
565
+ similarity: Math.round(similarity * 100) / 100,
566
+ type: 'semantic',
567
+ lineCount: lines,
568
+ });
569
+ }
570
+ }
571
+ }
572
+ }
573
+ }
574
+
575
+ // ═══════════════════════════════════════════════════════════════════════════
576
+ // LEGACY CODE DETECTION
577
+ // ═══════════════════════════════════════════════════════════════════════════
578
+
579
+ if (spinner) spinner.update('Detecting legacy code...');
580
+
581
+ const legacyMap = [];
582
+
583
+ for (const [filePath, { content }] of fileContents.entries()) {
584
+ const indicators = detectLegacyIndicators(filePath, content);
585
+
586
+ if (indicators.length > 0) {
587
+ // Use highest confidence indicator
588
+ const bestIndicator = indicators.reduce((a, b) => a.confidence > b.confidence ? a : b);
589
+
590
+ legacyMap.push({
591
+ file: filePath,
592
+ type: bestIndicator.type,
593
+ evidence: indicators.map(i => i.evidence),
594
+ confidence: bestIndicator.confidence,
595
+ });
596
+ }
597
+ }
598
+
599
+ // ═══════════════════════════════════════════════════════════════════════════
600
+ // RISK CLASSIFICATION
601
+ // ═══════════════════════════════════════════════════════════════════════════
602
+
603
+ if (spinner) spinner.update('Classifying risk levels...');
604
+
605
+ const riskClassifications = [];
606
+
607
+ for (const [filePath, { content }] of fileContents.entries()) {
608
+ const risk = classifyRisk(filePath, content);
609
+
610
+ if (risk.level !== 'LOW' || opts.verbose) {
611
+ riskClassifications.push({
612
+ file: filePath,
613
+ level: risk.level,
614
+ reason: risk.reason,
615
+ tags: risk.tags,
616
+ });
617
+ }
618
+ }
619
+
620
+ // ═══════════════════════════════════════════════════════════════════════════
621
+ // SUMMARY
622
+ // ═══════════════════════════════════════════════════════════════════════════
623
+
624
+ const duplicatedFiles = new Set();
625
+ let totalDuplicateLines = 0;
626
+
627
+ for (const entry of duplicationMap) {
628
+ duplicatedFiles.add(entry.primary);
629
+ for (const dup of entry.duplicates) {
630
+ duplicatedFiles.add(dup);
631
+ }
632
+ totalDuplicateLines += entry.lineCount * entry.duplicates.length;
633
+ }
634
+
635
+ const summary = {
636
+ totalFiles: files.length,
637
+ duplicatedFiles: duplicatedFiles.size,
638
+ legacyFiles: legacyMap.length,
639
+ highRiskFiles: riskClassifications.filter(r => r.level === 'HIGH' || r.level === 'CRITICAL').length,
640
+ totalDuplicateLines,
641
+ analysisTimeMs: Date.now() - startTime,
642
+ };
643
+
644
+ return {
645
+ duplicationMap,
646
+ legacyMap,
647
+ riskClassifications,
648
+ summary,
649
+ };
650
+ }
651
+
652
+ // ═══════════════════════════════════════════════════════════════════════════════
653
+ // OUTPUT FORMATTERS
654
+ // ═══════════════════════════════════════════════════════════════════════════════
655
+
656
+ function formatTableOutput(result, projectPath) {
657
+ const lines = [];
658
+ const { duplicationMap, legacyMap, riskClassifications, summary } = result;
659
+
660
+ lines.push('');
661
+ lines.push('┌────────────────────────────────────────────────────────────────────┐');
662
+ lines.push('│ Authority Verdict: INVENTORY (Read-Only) │');
663
+ lines.push('├────────────────────────────────────────────────────────────────────┤');
664
+ lines.push(`│ Project: ${path.basename(projectPath).padEnd(55)}│`);
665
+ lines.push(`│ Files analyzed: ${String(summary.totalFiles).padEnd(48)}│`);
666
+ lines.push(`│ Analysis time: ${String(summary.analysisTimeMs + 'ms').padEnd(49)}│`);
667
+ lines.push('└────────────────────────────────────────────────────────────────────┘');
668
+ lines.push('');
669
+
670
+ // Summary stats
671
+ lines.push('┌─────────────────────────────────────────┐');
672
+ lines.push('│ Summary │');
673
+ lines.push('├─────────────────────────────────────────┤');
674
+ lines.push(`│ Duplicated files: ${String(summary.duplicatedFiles).padStart(5)} │`);
675
+ lines.push(`│ Legacy files: ${String(summary.legacyFiles).padStart(5)} │`);
676
+ lines.push(`│ High-risk files: ${String(summary.highRiskFiles).padStart(5)} │`);
677
+ lines.push(`│ Duplicate lines: ${String(summary.totalDuplicateLines).padStart(5)} │`);
678
+ lines.push('└─────────────────────────────────────────┘');
679
+ lines.push('');
680
+
681
+ // Duplications
682
+ if (duplicationMap.length > 0) {
683
+ lines.push(`${ansi.bold}Duplications (${duplicationMap.length})${ansi.reset}`);
684
+ lines.push('');
685
+
686
+ for (const entry of duplicationMap.slice(0, 15)) {
687
+ const typeIcon = entry.type === 'exact' ? '=' : entry.type === 'near' ? '≈' : '~';
688
+ const similarity = Math.round(entry.similarity * 100);
689
+ lines.push(` ${typeIcon} ${ansi.dim}[${similarity}%]${ansi.reset} ${colors.accent}${entry.primary}${ansi.reset}`);
690
+ for (const dup of entry.duplicates.slice(0, 3)) {
691
+ lines.push(` └─ ${dup}`);
692
+ }
693
+ if (entry.duplicates.length > 3) {
694
+ lines.push(` ${ansi.dim}... and ${entry.duplicates.length - 3} more${ansi.reset}`);
695
+ }
696
+ }
697
+
698
+ if (duplicationMap.length > 15) {
699
+ lines.push(` ${ansi.dim}... and ${duplicationMap.length - 15} more duplicate groups${ansi.reset}`);
700
+ }
701
+ lines.push('');
702
+ }
703
+
704
+ // Legacy code
705
+ if (legacyMap.length > 0) {
706
+ lines.push(`${ansi.bold}Legacy Code (${legacyMap.length})${ansi.reset}`);
707
+ lines.push('');
708
+
709
+ for (const entry of legacyMap.slice(0, 15)) {
710
+ const typeIcon = entry.type === 'deprecated' ? '⚠' : entry.type === 'backup' ? '📦' : '👻';
711
+ const conf = Math.round(entry.confidence * 100);
712
+ lines.push(` ${typeIcon} ${ansi.dim}[${conf}%]${ansi.reset} ${colors.warning}${entry.file}${ansi.reset}`);
713
+ lines.push(` ${ansi.dim}Type: ${entry.type}${ansi.reset}`);
714
+ }
715
+
716
+ if (legacyMap.length > 15) {
717
+ lines.push(` ${ansi.dim}... and ${legacyMap.length - 15} more legacy files${ansi.reset}`);
718
+ }
719
+ lines.push('');
720
+ }
721
+
722
+ // High-risk files
723
+ const highRisk = riskClassifications.filter(r => r.level === 'HIGH' || r.level === 'CRITICAL');
724
+ if (highRisk.length > 0) {
725
+ lines.push(`${ansi.bold}High-Risk Files (${highRisk.length})${ansi.reset}`);
726
+ lines.push('');
727
+
728
+ for (const entry of highRisk.slice(0, 15)) {
729
+ const levelIcon = entry.level === 'CRITICAL' ? '🔴' : '🟠';
730
+ lines.push(` ${levelIcon} ${colors.error}${entry.file}${ansi.reset}`);
731
+ lines.push(` ${ansi.dim}${entry.reason}${ansi.reset}`);
732
+ }
733
+
734
+ if (highRisk.length > 15) {
735
+ lines.push(` ${ansi.dim}... and ${highRisk.length - 15} more high-risk files${ansi.reset}`);
736
+ }
737
+ lines.push('');
738
+ }
739
+
740
+ // Footer
741
+ lines.push('─────────────────────────────────────────────────────────────────────');
742
+ lines.push(`${ansi.dim}This is a read-only inventory. Use 'vibecheck approve' to get verdicts.${ansi.reset}`);
743
+ lines.push('');
744
+
745
+ return lines.join('\n');
746
+ }
747
+
748
+ function formatMarkdownOutput(result, projectPath) {
749
+ const { duplicationMap, legacyMap, riskClassifications, summary } = result;
750
+ const lines = [];
751
+
752
+ lines.push(`# Inventory Report`);
753
+ lines.push('');
754
+ lines.push(`**Project:** ${path.basename(projectPath)}`);
755
+ lines.push(`**Authority:** inventory (read-only)`);
756
+ lines.push(`**Generated:** ${new Date().toISOString()}`);
757
+ lines.push('');
758
+
759
+ lines.push('## Summary');
760
+ lines.push('');
761
+ lines.push('| Metric | Count |');
762
+ lines.push('|--------|-------|');
763
+ lines.push(`| Total files analyzed | ${summary.totalFiles} |`);
764
+ lines.push(`| Duplicated files | ${summary.duplicatedFiles} |`);
765
+ lines.push(`| Legacy files | ${summary.legacyFiles} |`);
766
+ lines.push(`| High-risk files | ${summary.highRiskFiles} |`);
767
+ lines.push(`| Duplicate lines | ${summary.totalDuplicateLines} |`);
768
+ lines.push('');
769
+
770
+ if (duplicationMap.length > 0) {
771
+ lines.push('## Duplications');
772
+ lines.push('');
773
+ for (const entry of duplicationMap) {
774
+ lines.push(`### ${entry.primary}`);
775
+ lines.push(`- Type: ${entry.type}`);
776
+ lines.push(`- Similarity: ${Math.round(entry.similarity * 100)}%`);
777
+ lines.push(`- Lines: ${entry.lineCount}`);
778
+ lines.push(`- Duplicates:`);
779
+ for (const dup of entry.duplicates) {
780
+ lines.push(` - \`${dup}\``);
781
+ }
782
+ lines.push('');
783
+ }
784
+ }
785
+
786
+ if (legacyMap.length > 0) {
787
+ lines.push('## Legacy Code');
788
+ lines.push('');
789
+ lines.push('| File | Type | Confidence |');
790
+ lines.push('|------|------|------------|');
791
+ for (const entry of legacyMap) {
792
+ lines.push(`| \`${entry.file}\` | ${entry.type} | ${Math.round(entry.confidence * 100)}% |`);
793
+ }
794
+ lines.push('');
795
+ }
796
+
797
+ const highRisk = riskClassifications.filter(r => r.level === 'HIGH' || r.level === 'CRITICAL');
798
+ if (highRisk.length > 0) {
799
+ lines.push('## High-Risk Files');
800
+ lines.push('');
801
+ lines.push('| File | Level | Reason |');
802
+ lines.push('|------|-------|--------|');
803
+ for (const entry of highRisk) {
804
+ lines.push(`| \`${entry.file}\` | ${entry.level} | ${entry.reason} |`);
805
+ }
806
+ lines.push('');
807
+ }
808
+
809
+ return lines.join('\n');
810
+ }
811
+
812
+ // ═══════════════════════════════════════════════════════════════════════════════
813
+ // MAIN COMMAND
814
+ // ═══════════════════════════════════════════════════════════════════════════════
815
+
816
+ async function runClassify(args) {
817
+ const opts = parseArgs(args);
818
+
819
+ // Show help
820
+ if (opts.help) {
821
+ printHelp(shouldShowBanner(opts));
822
+ return 0;
823
+ }
824
+
825
+ // CRITICAL: Check for JSON output FIRST - skip all banners/output if this flag is set
826
+ if (opts.json) {
827
+ process.env.NO_COLOR = '1';
828
+ // Skip all banner/output until we output JSON
829
+ }
830
+
831
+ // Print banner (skip if JSON mode)
832
+ if (shouldShowBanner(opts) && !opts.json) {
833
+ printBanner();
834
+ }
835
+
836
+ const projectPath = path.resolve(opts.path);
837
+
838
+ // Validate project path
839
+ if (!fs.existsSync(projectPath)) {
840
+ throw createUserError(`Project path does not exist: ${projectPath}`, "ValidationError");
841
+ }
842
+
843
+ if (!opts.quiet && !opts.json) {
844
+ console.log(` ${ansi.dim}Project:${ansi.reset} ${ansi.bold}${path.basename(projectPath)}${ansi.reset}`);
845
+ console.log(` ${ansi.dim}Authority:${ansi.reset} ${colors.accent}inventory${ansi.reset} (FREE tier)`);
846
+ console.log();
847
+ }
848
+
849
+ // Run analysis (skip spinner if JSON mode)
850
+ const spinner = !opts.json ? new Spinner({ color: colors.primary }) : null;
851
+ if (spinner) spinner.start('Starting inventory analysis...');
852
+
853
+ try {
854
+ const result = await runInventoryAnalysis(projectPath, opts, spinner);
855
+
856
+ if (spinner) {
857
+ spinner.succeed(`Analysis complete (${result.summary.analysisTimeMs}ms)`);
858
+ }
859
+
860
+ // Construct full output
861
+ const output = {
862
+ authority: 'inventory',
863
+ version: '1.0.0',
864
+ timestamp: new Date().toISOString(),
865
+ action: 'PROCEED', // Inventory is read-only, always PROCEED
866
+ projectPath: projectPath,
867
+ ...result,
868
+ };
869
+
870
+ // Output based on format
871
+ if (opts.json) {
872
+ // CRITICAL: Output ONLY JSON, no banners/colors
873
+ process.env.NO_COLOR = '1';
874
+ console.log(JSON.stringify(output, null, 2));
875
+ // Exit immediately after JSON output
876
+ return EXIT.SUCCESS;
877
+ } else if (opts.format === 'markdown') {
878
+ console.log(formatMarkdownOutput(result, projectPath));
879
+ } else {
880
+ // Use Mission Control format
881
+ const { formatClassifyOutput } = require('./lib/classify-output');
882
+ console.log(formatClassifyOutput(result, { projectPath, version: 'v3.5.5' }));
883
+ }
884
+
885
+ // Save to file if requested
886
+ if (opts.output) {
887
+ const outputPath = path.resolve(opts.output);
888
+ const content = opts.format === 'markdown'
889
+ ? formatMarkdownOutput(result, projectPath)
890
+ : JSON.stringify(output, null, 2);
891
+
892
+ await fs.promises.writeFile(outputPath, content);
893
+
894
+ if (!opts.quiet && !opts.json) {
895
+ console.log(` ${colors.success}✓${ansi.reset} Output saved to: ${outputPath}`);
896
+ }
897
+ }
898
+
899
+ return EXIT.SUCCESS;
900
+
901
+ } catch (error) {
902
+ if (spinner && !opts.json) {
903
+ spinner.fail(`Analysis failed: ${error.message}`);
904
+ }
905
+
906
+ // JSON error output
907
+ if (opts.json) {
908
+ process.env.NO_COLOR = '1';
909
+ const errorOutput = {
910
+ success: false,
911
+ error: {
912
+ code: error.code || 'CLASSIFY_ERROR',
913
+ message: error.message,
914
+ },
915
+ exitCode: error.code === 'VALIDATION_ERROR' ? EXIT.USER_ERROR : EXIT.INTERNAL_ERROR
916
+ };
917
+ console.log(JSON.stringify(errorOutput, null, 2));
918
+ process.exit(errorOutput.exitCode);
919
+ return errorOutput.exitCode;
920
+ }
921
+
922
+ throw error;
923
+ }
924
+ }
925
+
926
+ module.exports = {
927
+ runClassify: withErrorHandling(runClassify, "Classify failed"),
928
+ };