@vibecheckai/cli 2.8.2 → 3.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 (454) hide show
  1. package/README.md +8 -8
  2. package/bin/_deprecations.js +35 -0
  3. package/bin/_router.js +46 -0
  4. package/bin/cli-hygiene.js +241 -0
  5. package/bin/guardrail.js +834 -0
  6. package/bin/runners/cli-utils.js +1070 -0
  7. package/bin/runners/context/ai-task-decomposer.js +337 -0
  8. package/bin/runners/context/analyzer.js +462 -0
  9. package/bin/runners/context/api-contracts.js +427 -0
  10. package/bin/runners/context/context-diff.js +342 -0
  11. package/bin/runners/context/context-pruner.js +291 -0
  12. package/bin/runners/context/dependency-graph.js +414 -0
  13. package/bin/runners/context/generators/claude.js +107 -0
  14. package/bin/runners/context/generators/codex.js +108 -0
  15. package/bin/runners/context/generators/copilot.js +119 -0
  16. package/bin/runners/context/generators/cursor.js +514 -0
  17. package/bin/runners/context/generators/mcp.js +151 -0
  18. package/bin/runners/context/generators/windsurf.js +180 -0
  19. package/bin/runners/context/git-context.js +302 -0
  20. package/bin/runners/context/index.js +1042 -0
  21. package/bin/runners/context/insights.js +173 -0
  22. package/bin/runners/context/mcp-server/generate-rules.js +337 -0
  23. package/bin/runners/context/mcp-server/index.js +1176 -0
  24. package/bin/runners/context/mcp-server/package.json +24 -0
  25. package/bin/runners/context/memory.js +200 -0
  26. package/bin/runners/context/monorepo.js +215 -0
  27. package/bin/runners/context/multi-repo-federation.js +404 -0
  28. package/bin/runners/context/patterns.js +253 -0
  29. package/bin/runners/context/proof-context.js +972 -0
  30. package/bin/runners/context/security-scanner.js +303 -0
  31. package/bin/runners/context/semantic-search.js +350 -0
  32. package/bin/runners/context/shared.js +264 -0
  33. package/bin/runners/context/team-conventions.js +310 -0
  34. package/bin/runners/lib/ai-bridge.js +416 -0
  35. package/bin/runners/lib/analysis-core.js +271 -0
  36. package/bin/runners/lib/analyzers.js +541 -0
  37. package/bin/runners/lib/audit-bridge.js +391 -0
  38. package/bin/runners/lib/auth-truth.js +193 -0
  39. package/bin/runners/lib/auth.js +215 -0
  40. package/bin/runners/lib/backup.js +62 -0
  41. package/bin/runners/lib/billing.js +107 -0
  42. package/bin/runners/lib/claims.js +118 -0
  43. package/bin/runners/lib/cli-ui.js +540 -0
  44. package/bin/runners/lib/compliance-bridge-new.js +0 -0
  45. package/bin/runners/lib/compliance-bridge.js +165 -0
  46. package/bin/runners/lib/contracts/auth-contract.js +194 -0
  47. package/bin/runners/lib/contracts/env-contract.js +178 -0
  48. package/bin/runners/lib/contracts/external-contract.js +198 -0
  49. package/bin/runners/lib/contracts/guard.js +168 -0
  50. package/bin/runners/lib/contracts/index.js +89 -0
  51. package/bin/runners/lib/contracts/plan-validator.js +311 -0
  52. package/bin/runners/lib/contracts/route-contract.js +192 -0
  53. package/bin/runners/lib/detect.js +89 -0
  54. package/bin/runners/lib/doctor/autofix.js +254 -0
  55. package/bin/runners/lib/doctor/index.js +37 -0
  56. package/bin/runners/lib/doctor/modules/dependencies.js +325 -0
  57. package/bin/runners/lib/doctor/modules/index.js +46 -0
  58. package/bin/runners/lib/doctor/modules/network.js +250 -0
  59. package/bin/runners/lib/doctor/modules/project.js +312 -0
  60. package/bin/runners/lib/doctor/modules/runtime.js +224 -0
  61. package/bin/runners/lib/doctor/modules/security.js +348 -0
  62. package/bin/runners/lib/doctor/modules/system.js +213 -0
  63. package/bin/runners/lib/doctor/modules/vibecheck.js +394 -0
  64. package/bin/runners/lib/doctor/reporter.js +262 -0
  65. package/bin/runners/lib/doctor/service.js +262 -0
  66. package/bin/runners/lib/doctor/types.js +113 -0
  67. package/bin/runners/lib/doctor/ui.js +263 -0
  68. package/bin/runners/lib/doctor-enhanced.js +233 -0
  69. package/bin/runners/lib/doctor-v2.js +608 -0
  70. package/bin/runners/lib/enforcement.js +72 -0
  71. package/bin/runners/lib/enterprise-detect.js +603 -0
  72. package/bin/runners/lib/enterprise-init.js +942 -0
  73. package/bin/runners/lib/entitlements-v2.js +381 -0
  74. package/bin/runners/lib/entitlements.generated.js +0 -0
  75. package/bin/runners/lib/entitlements.js +332 -0
  76. package/bin/runners/lib/env-template.js +66 -0
  77. package/bin/runners/lib/env.js +189 -0
  78. package/bin/runners/lib/error-handler.js +320 -0
  79. package/bin/runners/lib/firewall-prompt.js +50 -0
  80. package/bin/runners/lib/graph/graph-builder.js +265 -0
  81. package/bin/runners/lib/graph/html-renderer.js +413 -0
  82. package/bin/runners/lib/graph/index.js +32 -0
  83. package/bin/runners/lib/graph/runtime-collector.js +215 -0
  84. package/bin/runners/lib/graph/static-extractor.js +518 -0
  85. package/bin/runners/lib/init-wizard.js +308 -0
  86. package/bin/runners/lib/json-output.js +76 -0
  87. package/bin/runners/lib/llm.js +75 -0
  88. package/bin/runners/lib/meter.js +61 -0
  89. package/bin/runners/lib/missions/evidence.js +126 -0
  90. package/bin/runners/lib/missions/plan.js +69 -0
  91. package/bin/runners/lib/missions/templates.js +147 -0
  92. package/bin/runners/lib/patch.js +40 -0
  93. package/bin/runners/lib/permissions/auth-model.js +213 -0
  94. package/bin/runners/lib/permissions/idor-prover.js +205 -0
  95. package/bin/runners/lib/permissions/index.js +45 -0
  96. package/bin/runners/lib/permissions/matrix-builder.js +198 -0
  97. package/bin/runners/lib/pkgjson.js +28 -0
  98. package/bin/runners/lib/preflight.js +142 -0
  99. package/bin/runners/lib/reality-findings.js +84 -0
  100. package/bin/runners/lib/redact.js +29 -0
  101. package/bin/runners/lib/replay/capsule-manager.js +154 -0
  102. package/bin/runners/lib/replay/index.js +263 -0
  103. package/bin/runners/lib/replay/player.js +348 -0
  104. package/bin/runners/lib/replay/recorder.js +331 -0
  105. package/bin/runners/lib/report-engine.js +447 -0
  106. package/bin/runners/lib/report-html.js +1117 -0
  107. package/bin/runners/lib/report-templates.js +964 -0
  108. package/bin/runners/lib/route-detection.js +1140 -0
  109. package/bin/runners/lib/route-truth.js +477 -0
  110. package/bin/runners/lib/sandbox/index.js +59 -0
  111. package/bin/runners/lib/sandbox/proof-chain.js +399 -0
  112. package/bin/runners/lib/sandbox/sandbox-runner.js +205 -0
  113. package/bin/runners/lib/sandbox/worktree.js +174 -0
  114. package/bin/runners/lib/scan-cache.js +330 -0
  115. package/bin/runners/lib/scan-output-schema.js +344 -0
  116. package/bin/runners/lib/score-history.js +282 -0
  117. package/bin/runners/lib/security-bridge.js +249 -0
  118. package/bin/runners/lib/server-usage.js +513 -0
  119. package/bin/runners/lib/share-pack.js +239 -0
  120. package/bin/runners/lib/snippets.js +67 -0
  121. package/bin/runners/lib/truth.js +667 -0
  122. package/bin/runners/lib/unified-output.js +189 -0
  123. package/bin/runners/lib/validate-patch.js +156 -0
  124. package/bin/runners/lib/verification.js +345 -0
  125. package/bin/runners/reality/engine.js +917 -0
  126. package/bin/runners/reality/flows.js +122 -0
  127. package/bin/runners/reality/report.js +378 -0
  128. package/bin/runners/reality/session.js +193 -0
  129. package/bin/runners/runAIAgent.js +2 -0
  130. package/bin/runners/runAudit.js +2 -0
  131. package/bin/runners/runAuth.js +106 -0
  132. package/bin/runners/runAutopilot.js +2 -0
  133. package/bin/runners/runBadge.js +2 -0
  134. package/bin/runners/runCertify.js +2 -0
  135. package/bin/runners/runClaimVerifier.js +483 -0
  136. package/bin/runners/runContext.js +56 -0
  137. package/bin/runners/runContextCompiler.js +385 -0
  138. package/bin/runners/runCtx.js +187 -0
  139. package/bin/runners/runCtxGuard.js +176 -0
  140. package/bin/runners/runCtxSync.js +116 -0
  141. package/bin/runners/runDashboard.js +10 -0
  142. package/bin/runners/runDoctor.js +245 -0
  143. package/bin/runners/runEnhancedShip.js +2 -0
  144. package/bin/runners/runFix.js +735 -0
  145. package/bin/runners/runFixPacks.js +2 -0
  146. package/bin/runners/runGate.js +17 -0
  147. package/bin/runners/runGraph.js +283 -0
  148. package/bin/runners/runInit.js +260 -0
  149. package/bin/runners/runInitGha.js +101 -0
  150. package/bin/runners/runInstall.js +76 -0
  151. package/bin/runners/runInteractive.js +388 -0
  152. package/bin/runners/runLaunch.js +2 -0
  153. package/bin/runners/runMcp.js +19 -0
  154. package/bin/runners/runMdc.js +2 -0
  155. package/bin/runners/runMissionGenerator.js +282 -0
  156. package/bin/runners/runNaturalLanguage.js +3 -0
  157. package/bin/runners/runPR.js +96 -0
  158. package/bin/runners/runPermissions.js +290 -0
  159. package/bin/runners/runPromptFirewall.js +211 -0
  160. package/bin/runners/runProof.js +2 -0
  161. package/bin/runners/runProve.js +392 -0
  162. package/bin/runners/runReality.js +489 -0
  163. package/bin/runners/runRealitySniff.js +2 -0
  164. package/bin/runners/runReplay.js +469 -0
  165. package/bin/runners/runReport.js +478 -0
  166. package/bin/runners/runScan.js +835 -0
  167. package/bin/runners/runShare.js +34 -0
  168. package/bin/runners/runShip.js +1062 -0
  169. package/bin/runners/runStatus.js +136 -0
  170. package/bin/runners/runTruthpack.js +634 -0
  171. package/bin/runners/runUpgrade.js +2 -0
  172. package/bin/runners/runValidate.js +2 -0
  173. package/bin/runners/runVerifyAgentOutput.js +2 -0
  174. package/bin/runners/runWatch.js +230 -0
  175. package/bin/runners/utils.js +360 -0
  176. package/bin/scan.js +612 -0
  177. package/bin/vibecheck.js +834 -0
  178. package/package.json +11 -11
  179. package/dist/autopatch/verified-autopatch.d.ts +0 -111
  180. package/dist/autopatch/verified-autopatch.d.ts.map +0 -1
  181. package/dist/autopatch/verified-autopatch.js +0 -503
  182. package/dist/autopatch/verified-autopatch.js.map +0 -1
  183. package/dist/bundles/index.js +0 -8
  184. package/dist/bundles/vibecheck-core.js +0 -25799
  185. package/dist/bundles/vibecheck-security.js +0 -208693
  186. package/dist/bundles/vibecheck-ship.js +0 -2318
  187. package/dist/commands/baseline.d.ts +0 -7
  188. package/dist/commands/baseline.d.ts.map +0 -1
  189. package/dist/commands/baseline.js +0 -79
  190. package/dist/commands/baseline.js.map +0 -1
  191. package/dist/commands/cache.d.ts +0 -13
  192. package/dist/commands/cache.d.ts.map +0 -1
  193. package/dist/commands/cache.js +0 -165
  194. package/dist/commands/cache.js.map +0 -1
  195. package/dist/commands/checkpoint.d.ts +0 -8
  196. package/dist/commands/checkpoint.d.ts.map +0 -1
  197. package/dist/commands/checkpoint.js +0 -35
  198. package/dist/commands/checkpoint.js.map +0 -1
  199. package/dist/commands/doctor.d.ts +0 -17
  200. package/dist/commands/doctor.d.ts.map +0 -1
  201. package/dist/commands/doctor.js +0 -226
  202. package/dist/commands/doctor.js.map +0 -1
  203. package/dist/commands/evidence.d.ts +0 -45
  204. package/dist/commands/evidence.d.ts.map +0 -1
  205. package/dist/commands/evidence.js +0 -197
  206. package/dist/commands/evidence.js.map +0 -1
  207. package/dist/commands/explain.d.ts +0 -8
  208. package/dist/commands/explain.d.ts.map +0 -1
  209. package/dist/commands/explain.js +0 -52
  210. package/dist/commands/explain.js.map +0 -1
  211. package/dist/commands/fix-consolidated.d.ts +0 -19
  212. package/dist/commands/fix-consolidated.d.ts.map +0 -1
  213. package/dist/commands/fix-consolidated.js +0 -165
  214. package/dist/commands/fix-consolidated.js.map +0 -1
  215. package/dist/commands/index.d.ts +0 -8
  216. package/dist/commands/index.d.ts.map +0 -1
  217. package/dist/commands/index.js +0 -15
  218. package/dist/commands/index.js.map +0 -1
  219. package/dist/commands/init.d.ts +0 -8
  220. package/dist/commands/init.d.ts.map +0 -1
  221. package/dist/commands/init.js +0 -125
  222. package/dist/commands/init.js.map +0 -1
  223. package/dist/commands/launcher.d.ts +0 -10
  224. package/dist/commands/launcher.d.ts.map +0 -1
  225. package/dist/commands/launcher.js +0 -174
  226. package/dist/commands/launcher.js.map +0 -1
  227. package/dist/commands/on.d.ts +0 -8
  228. package/dist/commands/on.d.ts.map +0 -1
  229. package/dist/commands/on.js +0 -123
  230. package/dist/commands/on.js.map +0 -1
  231. package/dist/commands/replay.d.ts +0 -8
  232. package/dist/commands/replay.d.ts.map +0 -1
  233. package/dist/commands/replay.js +0 -52
  234. package/dist/commands/replay.js.map +0 -1
  235. package/dist/commands/scan-consolidated.d.ts +0 -61
  236. package/dist/commands/scan-consolidated.d.ts.map +0 -1
  237. package/dist/commands/scan-consolidated.js +0 -243
  238. package/dist/commands/scan-consolidated.js.map +0 -1
  239. package/dist/commands/scan-secrets.d.ts +0 -47
  240. package/dist/commands/scan-secrets.d.ts.map +0 -1
  241. package/dist/commands/scan-secrets.js +0 -225
  242. package/dist/commands/scan-secrets.js.map +0 -1
  243. package/dist/commands/scan-vulnerabilities-enhanced.d.ts +0 -41
  244. package/dist/commands/scan-vulnerabilities-enhanced.d.ts.map +0 -1
  245. package/dist/commands/scan-vulnerabilities-enhanced.js +0 -368
  246. package/dist/commands/scan-vulnerabilities-enhanced.js.map +0 -1
  247. package/dist/commands/scan-vulnerabilities-osv.d.ts +0 -58
  248. package/dist/commands/scan-vulnerabilities-osv.d.ts.map +0 -1
  249. package/dist/commands/scan-vulnerabilities-osv.js +0 -722
  250. package/dist/commands/scan-vulnerabilities-osv.js.map +0 -1
  251. package/dist/commands/scan-vulnerabilities.d.ts +0 -32
  252. package/dist/commands/scan-vulnerabilities.d.ts.map +0 -1
  253. package/dist/commands/scan-vulnerabilities.js +0 -283
  254. package/dist/commands/scan-vulnerabilities.js.map +0 -1
  255. package/dist/commands/secrets-allowlist.d.ts +0 -7
  256. package/dist/commands/secrets-allowlist.d.ts.map +0 -1
  257. package/dist/commands/secrets-allowlist.js +0 -85
  258. package/dist/commands/secrets-allowlist.js.map +0 -1
  259. package/dist/commands/ship-consolidated.d.ts +0 -58
  260. package/dist/commands/ship-consolidated.d.ts.map +0 -1
  261. package/dist/commands/ship-consolidated.js +0 -515
  262. package/dist/commands/ship-consolidated.js.map +0 -1
  263. package/dist/commands/stats.d.ts +0 -8
  264. package/dist/commands/stats.d.ts.map +0 -1
  265. package/dist/commands/stats.js +0 -134
  266. package/dist/commands/stats.js.map +0 -1
  267. package/dist/commands/upgrade.d.ts +0 -8
  268. package/dist/commands/upgrade.d.ts.map +0 -1
  269. package/dist/commands/upgrade.js +0 -30
  270. package/dist/commands/upgrade.js.map +0 -1
  271. package/dist/fix/applicator.d.ts +0 -44
  272. package/dist/fix/applicator.d.ts.map +0 -1
  273. package/dist/fix/applicator.js +0 -144
  274. package/dist/fix/applicator.js.map +0 -1
  275. package/dist/fix/backup.d.ts +0 -38
  276. package/dist/fix/backup.d.ts.map +0 -1
  277. package/dist/fix/backup.js +0 -154
  278. package/dist/fix/backup.js.map +0 -1
  279. package/dist/fix/engine.d.ts +0 -55
  280. package/dist/fix/engine.d.ts.map +0 -1
  281. package/dist/fix/engine.js +0 -285
  282. package/dist/fix/engine.js.map +0 -1
  283. package/dist/fix/index.d.ts +0 -5
  284. package/dist/fix/index.d.ts.map +0 -1
  285. package/dist/fix/index.js +0 -12
  286. package/dist/fix/index.js.map +0 -1
  287. package/dist/fix/interactive.d.ts +0 -22
  288. package/dist/fix/interactive.d.ts.map +0 -1
  289. package/dist/fix/interactive.js +0 -172
  290. package/dist/fix/interactive.js.map +0 -1
  291. package/dist/formatters/index.d.ts +0 -6
  292. package/dist/formatters/index.d.ts.map +0 -1
  293. package/dist/formatters/index.js +0 -11
  294. package/dist/formatters/index.js.map +0 -1
  295. package/dist/formatters/sarif-enhanced.d.ts +0 -78
  296. package/dist/formatters/sarif-enhanced.d.ts.map +0 -1
  297. package/dist/formatters/sarif-enhanced.js +0 -144
  298. package/dist/formatters/sarif-enhanced.js.map +0 -1
  299. package/dist/formatters/sarif-v2.d.ts +0 -121
  300. package/dist/formatters/sarif-v2.d.ts.map +0 -1
  301. package/dist/formatters/sarif-v2.js +0 -356
  302. package/dist/formatters/sarif-v2.js.map +0 -1
  303. package/dist/formatters/sarif.d.ts +0 -72
  304. package/dist/formatters/sarif.d.ts.map +0 -1
  305. package/dist/formatters/sarif.js +0 -146
  306. package/dist/formatters/sarif.js.map +0 -1
  307. package/dist/index.d.ts +0 -61
  308. package/dist/index.d.ts.map +0 -1
  309. package/dist/index.js +0 -4388
  310. package/dist/index.js.map +0 -1
  311. package/dist/init/ci-generator.d.ts +0 -18
  312. package/dist/init/ci-generator.d.ts.map +0 -1
  313. package/dist/init/ci-generator.js +0 -317
  314. package/dist/init/ci-generator.js.map +0 -1
  315. package/dist/init/detect-framework.d.ts +0 -15
  316. package/dist/init/detect-framework.d.ts.map +0 -1
  317. package/dist/init/detect-framework.js +0 -301
  318. package/dist/init/detect-framework.js.map +0 -1
  319. package/dist/init/hooks-installer.d.ts +0 -22
  320. package/dist/init/hooks-installer.d.ts.map +0 -1
  321. package/dist/init/hooks-installer.js +0 -310
  322. package/dist/init/hooks-installer.js.map +0 -1
  323. package/dist/init/index.d.ts +0 -8
  324. package/dist/init/index.d.ts.map +0 -1
  325. package/dist/init/index.js +0 -22
  326. package/dist/init/index.js.map +0 -1
  327. package/dist/init/templates.d.ts +0 -402
  328. package/dist/init/templates.d.ts.map +0 -1
  329. package/dist/init/templates.js +0 -240
  330. package/dist/init/templates.js.map +0 -1
  331. package/dist/mcp/server.d.ts +0 -12
  332. package/dist/mcp/server.d.ts.map +0 -1
  333. package/dist/mcp/server.js +0 -42
  334. package/dist/mcp/server.js.map +0 -1
  335. package/dist/mcp/telemetry.d.ts +0 -40
  336. package/dist/mcp/telemetry.d.ts.map +0 -1
  337. package/dist/mcp/telemetry.js +0 -98
  338. package/dist/mcp/telemetry.js.map +0 -1
  339. package/dist/reality/no-dead-buttons/button-sweep-generator.d.ts +0 -32
  340. package/dist/reality/no-dead-buttons/button-sweep-generator.d.ts.map +0 -1
  341. package/dist/reality/no-dead-buttons/button-sweep-generator.js +0 -236
  342. package/dist/reality/no-dead-buttons/button-sweep-generator.js.map +0 -1
  343. package/dist/reality/no-dead-buttons/index.d.ts +0 -11
  344. package/dist/reality/no-dead-buttons/index.d.ts.map +0 -1
  345. package/dist/reality/no-dead-buttons/index.js +0 -18
  346. package/dist/reality/no-dead-buttons/index.js.map +0 -1
  347. package/dist/reality/no-dead-buttons/static-scanner.d.ts +0 -34
  348. package/dist/reality/no-dead-buttons/static-scanner.d.ts.map +0 -1
  349. package/dist/reality/no-dead-buttons/static-scanner.js +0 -230
  350. package/dist/reality/no-dead-buttons/static-scanner.js.map +0 -1
  351. package/dist/reality/reality-graph.d.ts +0 -192
  352. package/dist/reality/reality-graph.d.ts.map +0 -1
  353. package/dist/reality/reality-graph.js +0 -600
  354. package/dist/reality/reality-graph.js.map +0 -1
  355. package/dist/reality/reality-runner.d.ts +0 -89
  356. package/dist/reality/reality-runner.d.ts.map +0 -1
  357. package/dist/reality/reality-runner.js +0 -540
  358. package/dist/reality/reality-runner.js.map +0 -1
  359. package/dist/reality/receipt-generator.d.ts +0 -152
  360. package/dist/reality/receipt-generator.d.ts.map +0 -1
  361. package/dist/reality/receipt-generator.js +0 -495
  362. package/dist/reality/receipt-generator.js.map +0 -1
  363. package/dist/reality/runtime-tracer.d.ts +0 -75
  364. package/dist/reality/runtime-tracer.d.ts.map +0 -1
  365. package/dist/reality/runtime-tracer.js +0 -109
  366. package/dist/reality/runtime-tracer.js.map +0 -1
  367. package/dist/runtime/auth-utils.d.ts +0 -43
  368. package/dist/runtime/auth-utils.d.ts.map +0 -1
  369. package/dist/runtime/auth-utils.js +0 -130
  370. package/dist/runtime/auth-utils.js.map +0 -1
  371. package/dist/runtime/client.d.ts +0 -74
  372. package/dist/runtime/client.d.ts.map +0 -1
  373. package/dist/runtime/client.js +0 -222
  374. package/dist/runtime/client.js.map +0 -1
  375. package/dist/runtime/creds.d.ts +0 -48
  376. package/dist/runtime/creds.d.ts.map +0 -1
  377. package/dist/runtime/creds.js +0 -245
  378. package/dist/runtime/creds.js.map +0 -1
  379. package/dist/runtime/exit-codes.d.ts +0 -49
  380. package/dist/runtime/exit-codes.d.ts.map +0 -1
  381. package/dist/runtime/exit-codes.js +0 -93
  382. package/dist/runtime/exit-codes.js.map +0 -1
  383. package/dist/runtime/index.d.ts +0 -9
  384. package/dist/runtime/index.d.ts.map +0 -1
  385. package/dist/runtime/index.js +0 -25
  386. package/dist/runtime/index.js.map +0 -1
  387. package/dist/runtime/json-output.d.ts +0 -42
  388. package/dist/runtime/json-output.d.ts.map +0 -1
  389. package/dist/runtime/json-output.js +0 -59
  390. package/dist/runtime/json-output.js.map +0 -1
  391. package/dist/runtime/semver.d.ts +0 -37
  392. package/dist/runtime/semver.d.ts.map +0 -1
  393. package/dist/runtime/semver.js +0 -110
  394. package/dist/runtime/semver.js.map +0 -1
  395. package/dist/scan/dead-ui-detector.d.ts +0 -48
  396. package/dist/scan/dead-ui-detector.d.ts.map +0 -1
  397. package/dist/scan/dead-ui-detector.js +0 -170
  398. package/dist/scan/dead-ui-detector.js.map +0 -1
  399. package/dist/scan/playwright-sweep.d.ts +0 -40
  400. package/dist/scan/playwright-sweep.d.ts.map +0 -1
  401. package/dist/scan/playwright-sweep.js +0 -216
  402. package/dist/scan/playwright-sweep.js.map +0 -1
  403. package/dist/scan/proof-bundle.d.ts +0 -25
  404. package/dist/scan/proof-bundle.d.ts.map +0 -1
  405. package/dist/scan/proof-bundle.js +0 -203
  406. package/dist/scan/proof-bundle.js.map +0 -1
  407. package/dist/scan/proof-graph.d.ts +0 -59
  408. package/dist/scan/proof-graph.d.ts.map +0 -1
  409. package/dist/scan/proof-graph.js +0 -64
  410. package/dist/scan/proof-graph.js.map +0 -1
  411. package/dist/scan/reality-sniff.d.ts +0 -56
  412. package/dist/scan/reality-sniff.d.ts.map +0 -1
  413. package/dist/scan/reality-sniff.js +0 -200
  414. package/dist/scan/reality-sniff.js.map +0 -1
  415. package/dist/scan/structural-verifier.d.ts +0 -20
  416. package/dist/scan/structural-verifier.d.ts.map +0 -1
  417. package/dist/scan/structural-verifier.js +0 -112
  418. package/dist/scan/structural-verifier.js.map +0 -1
  419. package/dist/scan/verification-engine.d.ts +0 -47
  420. package/dist/scan/verification-engine.d.ts.map +0 -1
  421. package/dist/scan/verification-engine.js +0 -141
  422. package/dist/scan/verification-engine.js.map +0 -1
  423. package/dist/scanner/baseline.d.ts +0 -52
  424. package/dist/scanner/baseline.d.ts.map +0 -1
  425. package/dist/scanner/baseline.js +0 -85
  426. package/dist/scanner/baseline.js.map +0 -1
  427. package/dist/scanner/incremental.d.ts +0 -30
  428. package/dist/scanner/incremental.d.ts.map +0 -1
  429. package/dist/scanner/incremental.js +0 -82
  430. package/dist/scanner/incremental.js.map +0 -1
  431. package/dist/scanner/parallel.d.ts +0 -43
  432. package/dist/scanner/parallel.d.ts.map +0 -1
  433. package/dist/scanner/parallel.js +0 -99
  434. package/dist/scanner/parallel.js.map +0 -1
  435. package/dist/standalone.d.ts +0 -1
  436. package/dist/standalone.d.ts.map +0 -1
  437. package/dist/standalone.js +0 -1
  438. package/dist/standalone.js.map +0 -1
  439. package/dist/truth-pack/index.d.ts +0 -102
  440. package/dist/truth-pack/index.d.ts.map +0 -1
  441. package/dist/truth-pack/index.js +0 -694
  442. package/dist/truth-pack/index.js.map +0 -1
  443. package/dist/ui/frame.d.ts +0 -68
  444. package/dist/ui/frame.d.ts.map +0 -1
  445. package/dist/ui/frame.js +0 -165
  446. package/dist/ui/frame.js.map +0 -1
  447. package/dist/ui/index.d.ts +0 -5
  448. package/dist/ui/index.d.ts.map +0 -1
  449. package/dist/ui/index.js +0 -16
  450. package/dist/ui/index.js.map +0 -1
  451. package/dist/ui.d.ts +0 -36
  452. package/dist/ui.d.ts.map +0 -1
  453. package/dist/ui.js +0 -45
  454. package/dist/ui.js.map +0 -1
@@ -0,0 +1,344 @@
1
+ /**
2
+ * Scan Output Schema & Validation
3
+ *
4
+ * Defines the stable contract for scan output in JSON mode.
5
+ * Used by CI/CD systems and programmatic consumers.
6
+ */
7
+
8
+ const SCHEMA_VERSION = "1.0.0";
9
+
10
+ /**
11
+ * @typedef {Object} Finding
12
+ * @property {string} id - Unique finding identifier
13
+ * @property {string} type - Finding type (secret, stub, vulnerability, etc.)
14
+ * @property {'critical'|'high'|'medium'|'low'|'info'} severity - Severity level
15
+ * @property {string} message - Human-readable description
16
+ * @property {string} [file] - File path where found
17
+ * @property {number} [line] - Line number
18
+ * @property {number} confidence - Confidence score 0-100
19
+ * @property {string} [suggestedFix] - Remediation guidance
20
+ * @property {boolean} blocksShip - Whether this blocks shipping
21
+ */
22
+
23
+ /**
24
+ * @typedef {Object} ScanResult
25
+ * @property {string} schemaVersion - Schema version for compatibility
26
+ * @property {boolean} success - Whether scan completed without errors
27
+ * @property {'pass'|'fail'|'warn'} verdict - Overall verdict
28
+ * @property {number} score - Health score 0-100
29
+ * @property {Object} summary - Summary counts
30
+ * @property {number} summary.total - Total findings
31
+ * @property {number} summary.critical - Critical findings
32
+ * @property {number} summary.high - High findings
33
+ * @property {number} summary.medium - Medium findings
34
+ * @property {number} summary.low - Low findings
35
+ * @property {number} summary.blockers - Findings that block shipping
36
+ * @property {Finding[]} findings - All findings
37
+ * @property {Object} metadata - Scan metadata
38
+ * @property {string} metadata.scanId - Unique scan ID
39
+ * @property {string} metadata.timestamp - ISO timestamp
40
+ * @property {number} metadata.duration - Duration in ms
41
+ * @property {string} metadata.projectPath - Scanned path
42
+ * @property {Object} [error] - Error details if failed
43
+ * @property {string} error.message - Error message
44
+ * @property {string} error.code - Error code
45
+ */
46
+
47
+ /**
48
+ * Confidence levels for different finding types
49
+ */
50
+ const CONFIDENCE_LEVELS = {
51
+ // High confidence - these are almost certainly issues
52
+ AWS_KEY: 95,
53
+ PRIVATE_KEY: 95,
54
+ STRIPE_LIVE_KEY: 95,
55
+ GITHUB_TOKEN: 90,
56
+ DATABASE_URL: 90,
57
+
58
+ // Medium-high confidence - very likely issues
59
+ GENERIC_API_KEY: 75,
60
+ JWT_TOKEN: 70,
61
+ BEARER_TOKEN: 70,
62
+ HARDCODED_PASSWORD: 70,
63
+
64
+ // Medium confidence - review recommended
65
+ TODO_COMMENT: 50,
66
+ MOCK_DATA: 60,
67
+ PLACEHOLDER: 65,
68
+ LOREM_IPSUM: 80,
69
+
70
+ // Lower confidence - context-dependent
71
+ LOCALHOST_URL: 40,
72
+ TEST_EMAIL: 35,
73
+ CONSOLE_LOG: 30,
74
+ };
75
+
76
+ /**
77
+ * Determine if a finding should block shipping
78
+ * Based on severity and confidence
79
+ */
80
+ function isBlocker(finding) {
81
+ // Critical findings block only if confidence > 80% (hardened threshold)
82
+ if (finding.severity === 'critical' && finding.confidence > 80) {
83
+ return true;
84
+ }
85
+
86
+ // High findings block only if confidence > 90% (hardened threshold)
87
+ if (finding.severity === 'high' && finding.confidence > 90) {
88
+ return true;
89
+ }
90
+
91
+ // Secrets always block in ship mode (high confidence by nature)
92
+ if (finding.type?.includes('secret') || finding.type?.includes('key')) {
93
+ return true;
94
+ }
95
+
96
+ return false;
97
+ }
98
+
99
+ /**
100
+ * Get confidence score for a finding type
101
+ */
102
+ function getConfidenceScore(type, context = {}) {
103
+ const baseConfidence = CONFIDENCE_LEVELS[type] || 50;
104
+
105
+ // Adjust based on context
106
+ let adjusted = baseConfidence;
107
+
108
+ // Lower confidence if in test/fixture file
109
+ if (context.isTestFile) {
110
+ adjusted = Math.max(10, adjusted - 30);
111
+ }
112
+
113
+ // Higher confidence if in source file
114
+ if (context.isSourceFile) {
115
+ adjusted = Math.min(100, adjusted + 10);
116
+ }
117
+
118
+ // Lower confidence for commented code
119
+ if (context.inComment) {
120
+ adjusted = Math.max(10, adjusted - 20);
121
+ }
122
+
123
+ return Math.round(adjusted);
124
+ }
125
+
126
+ /**
127
+ * Calculate overall verdict from findings
128
+ * Uses hardened blocker logic - only high-confidence findings block
129
+ * SECURITY: High-confidence critical/high findings always block, even if blocksShip is explicitly false
130
+ */
131
+ function calculateVerdict(findings) {
132
+ if (!findings || findings.length === 0) {
133
+ return 'pass';
134
+ }
135
+
136
+ // Apply hardened blocker logic - high-confidence critical/high findings always block
137
+ const blockers = findings.filter(f => {
138
+ // If explicitly marked as blocker, respect it
139
+ if (f.blocksShip === true) return true;
140
+
141
+ // Apply hardened thresholds: critical > 80%, high > 90%
142
+ if (f.severity === 'critical' && f.confidence > 80) return true;
143
+ if (f.severity === 'high' && f.confidence > 90) return true;
144
+
145
+ // Secrets always block
146
+ if (f.type?.includes('secret') || f.type?.includes('key')) return true;
147
+
148
+ return false;
149
+ });
150
+
151
+ // Any blockers = fail
152
+ if (blockers.length > 0) {
153
+ return 'fail';
154
+ }
155
+
156
+ // Any critical, high, or medium findings = warn (but don't block if confidence too low)
157
+ const criticals = findings.filter(f => f.severity === 'critical');
158
+ const highs = findings.filter(f => f.severity === 'high');
159
+ const mediums = findings.filter(f => f.severity === 'medium');
160
+ if (criticals.length > 0 || highs.length > 0 || mediums.length > 0) {
161
+ return 'warn';
162
+ }
163
+
164
+ // Only low/info findings = pass
165
+ return 'pass';
166
+ }
167
+
168
+ /**
169
+ * Calculate health score from findings
170
+ */
171
+ function calculateScore(findings) {
172
+ let score = 100;
173
+
174
+ for (const finding of findings) {
175
+ const weight = {
176
+ critical: 25,
177
+ high: 15,
178
+ medium: 5,
179
+ low: 2,
180
+ info: 0,
181
+ }[finding.severity] || 0;
182
+
183
+ // Weight by confidence
184
+ const adjustedWeight = weight * (finding.confidence / 100);
185
+ score -= adjustedWeight;
186
+ }
187
+
188
+ return Math.max(0, Math.round(score));
189
+ }
190
+
191
+ /**
192
+ * Deduplicate findings by unique key
193
+ */
194
+ function dedupeFindings(findings) {
195
+ const seen = new Map();
196
+
197
+ for (const finding of findings) {
198
+ // Create unique key based on type, file, line, and message
199
+ const key = `${finding.type}:${finding.file || ''}:${finding.line || ''}:${finding.message?.slice(0, 50) || ''}`;
200
+
201
+ if (!seen.has(key)) {
202
+ seen.set(key, finding);
203
+ } else {
204
+ // Keep the higher severity/confidence one
205
+ const existing = seen.get(key);
206
+ const severityOrder = { critical: 4, high: 3, medium: 2, low: 1, info: 0 };
207
+
208
+ if (severityOrder[finding.severity] > severityOrder[existing.severity] ||
209
+ (severityOrder[finding.severity] === severityOrder[existing.severity] &&
210
+ finding.confidence > existing.confidence)) {
211
+ seen.set(key, finding);
212
+ }
213
+ }
214
+ }
215
+
216
+ return Array.from(seen.values());
217
+ }
218
+
219
+ /**
220
+ * Sort findings by shipping impact (blockers first, then by severity/confidence)
221
+ * This ensures users see the most critical issues that block shipping first.
222
+ */
223
+ function sortFindings(findings) {
224
+ const severityOrder = { critical: 4, high: 3, medium: 2, low: 1, info: 0 };
225
+
226
+ return [...findings].sort((a, b) => {
227
+ // 1. Blockers first (most important - these block shipping)
228
+ const aIsBlocker = a.blocksShip === true;
229
+ const bIsBlocker = b.blocksShip === true;
230
+ if (aIsBlocker !== bIsBlocker) {
231
+ return aIsBlocker ? -1 : 1;
232
+ }
233
+
234
+ // 2. Then by severity (critical > high > medium > low > info)
235
+ if (severityOrder[a.severity] !== severityOrder[b.severity]) {
236
+ return severityOrder[b.severity] - severityOrder[a.severity];
237
+ }
238
+
239
+ // 3. Then by confidence (higher confidence = more important)
240
+ return b.confidence - a.confidence;
241
+ });
242
+ }
243
+
244
+ /**
245
+ * Create a valid scan result object
246
+ */
247
+ function createScanResult(options) {
248
+ const {
249
+ findings = [],
250
+ projectPath = process.cwd(),
251
+ scanId = `scan_${Date.now()}`,
252
+ startTime = Date.now(),
253
+ error = null,
254
+ } = options;
255
+
256
+ // Process findings
257
+ const processedFindings = findings.map(f => ({
258
+ ...f,
259
+ confidence: f.confidence || getConfidenceScore(f.type),
260
+ blocksShip: f.blocksShip ?? isBlocker(f),
261
+ }));
262
+
263
+ const dedupedFindings = dedupeFindings(processedFindings);
264
+ const sortedFindings = sortFindings(dedupedFindings);
265
+
266
+ const summary = {
267
+ total: sortedFindings.length,
268
+ critical: sortedFindings.filter(f => f.severity === 'critical').length,
269
+ high: sortedFindings.filter(f => f.severity === 'high').length,
270
+ medium: sortedFindings.filter(f => f.severity === 'medium').length,
271
+ low: sortedFindings.filter(f => f.severity === 'low').length,
272
+ blockers: sortedFindings.filter(f => f.blocksShip).length,
273
+ };
274
+
275
+ return {
276
+ schemaVersion: SCHEMA_VERSION,
277
+ success: !error,
278
+ verdict: error ? 'fail' : calculateVerdict(sortedFindings),
279
+ score: calculateScore(sortedFindings),
280
+ summary,
281
+ findings: sortedFindings,
282
+ metadata: {
283
+ scanId,
284
+ timestamp: new Date().toISOString(),
285
+ duration: Date.now() - startTime,
286
+ projectPath,
287
+ version: require('../../../package.json').version,
288
+ },
289
+ ...(error ? { error: { message: error.message, code: error.code || 'SCAN_ERROR' } } : {}),
290
+ };
291
+ }
292
+
293
+ /**
294
+ * Validate a scan result against the schema
295
+ */
296
+ function validateScanResult(result) {
297
+ const errors = [];
298
+
299
+ if (!result.schemaVersion) {
300
+ errors.push("Missing schemaVersion");
301
+ }
302
+
303
+ if (typeof result.success !== 'boolean') {
304
+ errors.push("Invalid or missing success field");
305
+ }
306
+
307
+ if (!['pass', 'fail', 'warn'].includes(result.verdict)) {
308
+ errors.push(`Invalid verdict: ${result.verdict}`);
309
+ }
310
+
311
+ if (typeof result.score !== 'number' || result.score < 0 || result.score > 100) {
312
+ errors.push(`Invalid score: ${result.score}`);
313
+ }
314
+
315
+ if (!result.summary || typeof result.summary.total !== 'number') {
316
+ errors.push("Invalid or missing summary");
317
+ }
318
+
319
+ if (!Array.isArray(result.findings)) {
320
+ errors.push("Findings must be an array");
321
+ }
322
+
323
+ if (!result.metadata?.scanId || !result.metadata?.timestamp) {
324
+ errors.push("Missing required metadata fields");
325
+ }
326
+
327
+ return {
328
+ valid: errors.length === 0,
329
+ errors,
330
+ };
331
+ }
332
+
333
+ module.exports = {
334
+ SCHEMA_VERSION,
335
+ CONFIDENCE_LEVELS,
336
+ isBlocker,
337
+ getConfidenceScore,
338
+ calculateVerdict,
339
+ calculateScore,
340
+ dedupeFindings,
341
+ sortFindings,
342
+ createScanResult,
343
+ validateScanResult,
344
+ };
@@ -0,0 +1,282 @@
1
+ /**
2
+ * Score History Tracking
3
+ *
4
+ * Tracks Vibe Score history over time for trend analysis,
5
+ * streaks, and gamification.
6
+ */
7
+
8
+ const fs = require("fs");
9
+ const path = require("path");
10
+
11
+ /**
12
+ * Get history file path for a project
13
+ */
14
+ function getHistoryPath(projectPath) {
15
+ return path.join(projectPath, ".vibecheck", "history.json");
16
+ }
17
+
18
+ /**
19
+ * Load score history
20
+ */
21
+ function loadHistory(projectPath) {
22
+ const historyPath = getHistoryPath(projectPath);
23
+
24
+ try {
25
+ if (fs.existsSync(historyPath)) {
26
+ return JSON.parse(fs.readFileSync(historyPath, "utf8"));
27
+ }
28
+ } catch {}
29
+
30
+ return {
31
+ projectId: path.basename(projectPath),
32
+ checks: [],
33
+ };
34
+ }
35
+
36
+ /**
37
+ * Save score history
38
+ */
39
+ function saveHistory(projectPath, history) {
40
+ const historyPath = getHistoryPath(projectPath);
41
+ const dir = path.dirname(historyPath);
42
+
43
+ try {
44
+ if (!fs.existsSync(dir)) {
45
+ fs.mkdirSync(dir, { recursive: true });
46
+ }
47
+ fs.writeFileSync(historyPath, JSON.stringify(history, null, 2));
48
+ } catch (err) {
49
+ console.warn(`Warning: Could not save history: ${err.message}`);
50
+ }
51
+ }
52
+
53
+ /**
54
+ * Record a new check
55
+ */
56
+ function recordCheck(projectPath, checkData) {
57
+ const history = loadHistory(projectPath);
58
+
59
+ const check = {
60
+ timestamp: new Date().toISOString(),
61
+ score: checkData.score || 0,
62
+ verdict: checkData.verdict || "BLOCK",
63
+ commitSha: checkData.commitSha || null,
64
+ commitMessage: checkData.commitMessage || null,
65
+ branch: checkData.branch || null,
66
+ categoryScores: checkData.categoryScores || {},
67
+ findingCounts: checkData.findingCounts || {
68
+ critical: 0,
69
+ high: 0,
70
+ medium: 0,
71
+ low: 0,
72
+ },
73
+ };
74
+
75
+ history.checks.unshift(check);
76
+
77
+ // Keep only last 100 checks
78
+ if (history.checks.length > 100) {
79
+ history.checks = history.checks.slice(0, 100);
80
+ }
81
+
82
+ saveHistory(projectPath, history);
83
+
84
+ return check;
85
+ }
86
+
87
+ /**
88
+ * Calculate trend from recent checks
89
+ */
90
+ function calculateTrend(projectPath, limit = 10) {
91
+ const history = loadHistory(projectPath);
92
+ const recentChecks = history.checks.slice(0, limit);
93
+
94
+ if (recentChecks.length < 2) {
95
+ return {
96
+ direction: "stable",
97
+ delta: 0,
98
+ previousScore: null,
99
+ streak: null,
100
+ sparkline: [],
101
+ };
102
+ }
103
+
104
+ const latest = recentChecks[0].score;
105
+ const previous = recentChecks[1].score;
106
+ const delta = latest - previous;
107
+
108
+ // Calculate streak
109
+ let streak = { count: 1, type: recentChecks[0].verdict };
110
+ for (let i = 1; i < recentChecks.length; i++) {
111
+ if (recentChecks[i].verdict === streak.type) {
112
+ streak.count++;
113
+ } else {
114
+ break;
115
+ }
116
+ }
117
+
118
+ // Generate sparkline data (last 10 scores)
119
+ const sparkline = recentChecks.slice(0, 10).map(c => c.score).reverse();
120
+
121
+ return {
122
+ direction: delta > 0 ? "up" : delta < 0 ? "down" : "stable",
123
+ delta: Math.abs(delta),
124
+ previousScore: previous,
125
+ streak: streak.count >= 3 ? streak : null,
126
+ sparkline,
127
+ };
128
+ }
129
+
130
+ /**
131
+ * Get milestones achieved
132
+ */
133
+ function getMilestones(projectPath) {
134
+ const history = loadHistory(projectPath);
135
+ const milestones = [];
136
+
137
+ if (history.checks.length === 0) return milestones;
138
+
139
+ const latest = history.checks[0];
140
+
141
+ // First check
142
+ if (history.checks.length === 1) {
143
+ milestones.push("First check completed!");
144
+ }
145
+
146
+ // First 90+ score
147
+ const first90 = history.checks.find(c => c.score >= 90);
148
+ if (first90 && first90 === latest) {
149
+ milestones.push("First 90+ score!");
150
+ }
151
+
152
+ // Perfect 100
153
+ if (latest.score === 100) {
154
+ milestones.push("Perfect 100! 🏆");
155
+ }
156
+
157
+ // Streak milestones
158
+ const trend = calculateTrend(projectPath);
159
+ if (trend.streak) {
160
+ if (trend.streak.count >= 10 && trend.streak.type === "SHIP") {
161
+ milestones.push("10 SHIP streak! 🔥🔥🔥");
162
+ } else if (trend.streak.count >= 5 && trend.streak.type === "SHIP") {
163
+ milestones.push("5 SHIP streak! 🔥");
164
+ }
165
+ }
166
+
167
+ // Improvement milestones
168
+ if (trend.direction === "up" && trend.delta >= 10) {
169
+ milestones.push(`+${trend.delta} points improvement!`);
170
+ }
171
+
172
+ return milestones;
173
+ }
174
+
175
+ /**
176
+ * Format history for CLI display
177
+ */
178
+ function formatHistoryDisplay(projectPath, limit = 10) {
179
+ const c = {
180
+ reset: "\x1b[0m",
181
+ bold: "\x1b[1m",
182
+ dim: "\x1b[2m",
183
+ red: "\x1b[31m",
184
+ green: "\x1b[32m",
185
+ yellow: "\x1b[33m",
186
+ cyan: "\x1b[36m",
187
+ };
188
+
189
+ const history = loadHistory(projectPath);
190
+ const trend = calculateTrend(projectPath);
191
+ const milestones = getMilestones(projectPath);
192
+
193
+ if (history.checks.length === 0) {
194
+ return `${c.dim}No history yet. Run \`vibecheck ship\` to start tracking.${c.reset}`;
195
+ }
196
+
197
+ let output = `\n${c.bold}📊 Score History${c.reset}\n\n`;
198
+
199
+ // Trend line
200
+ if (trend.direction !== "stable") {
201
+ const trendIcon = trend.direction === "up" ? "↑" : "↓";
202
+ const trendColor = trend.direction === "up" ? c.green : c.red;
203
+ output += ` ${trendColor}${trendIcon} ${trend.delta} since last check${c.reset}\n`;
204
+ }
205
+
206
+ // Streak
207
+ if (trend.streak) {
208
+ output += ` ${c.yellow}🔥 ${trend.streak.count} consecutive ${trend.streak.type} verdicts${c.reset}\n`;
209
+ }
210
+
211
+ // Milestones
212
+ for (const milestone of milestones) {
213
+ output += ` ${c.cyan}🏆 ${milestone}${c.reset}\n`;
214
+ }
215
+
216
+ output += `\n ${c.dim}Recent checks:${c.reset}\n`;
217
+
218
+ // Recent checks
219
+ const recentChecks = history.checks.slice(0, limit);
220
+ for (const check of recentChecks) {
221
+ const date = new Date(check.timestamp);
222
+ const timeAgo = formatTimeAgo(date);
223
+ const verdictColor = check.verdict === "SHIP" ? c.green :
224
+ check.verdict === "WARN" ? c.yellow : c.red;
225
+ const verdictIcon = check.verdict === "SHIP" ? "✅" :
226
+ check.verdict === "WARN" ? "⚠️" : "🚫";
227
+
228
+ output += ` ${c.dim}${timeAgo.padEnd(12)}${c.reset} ${verdictColor}${check.score.toString().padStart(3)}${c.reset} ${verdictIcon} ${verdictColor}${check.verdict}${c.reset}`;
229
+ if (check.commitSha) {
230
+ output += ` ${c.dim}${check.commitSha.substring(0, 7)}${c.reset}`;
231
+ }
232
+ output += "\n";
233
+ }
234
+
235
+ // Sparkline
236
+ if (trend.sparkline.length > 2) {
237
+ output += `\n ${c.dim}Trend:${c.reset} ${formatSparkline(trend.sparkline)}\n`;
238
+ }
239
+
240
+ return output;
241
+ }
242
+
243
+ /**
244
+ * Format relative time
245
+ */
246
+ function formatTimeAgo(date) {
247
+ const now = new Date();
248
+ const diffMs = now - date;
249
+ const diffMins = Math.floor(diffMs / 60000);
250
+ const diffHours = Math.floor(diffMs / 3600000);
251
+ const diffDays = Math.floor(diffMs / 86400000);
252
+
253
+ if (diffMins < 1) return "just now";
254
+ if (diffMins < 60) return `${diffMins}m ago`;
255
+ if (diffHours < 24) return `${diffHours}h ago`;
256
+ if (diffDays < 7) return `${diffDays}d ago`;
257
+ return date.toLocaleDateString();
258
+ }
259
+
260
+ /**
261
+ * Format sparkline from scores
262
+ */
263
+ function formatSparkline(scores) {
264
+ const chars = ["▁", "▂", "▃", "▄", "▅", "▆", "▇", "█"];
265
+ const min = Math.min(...scores);
266
+ const max = Math.max(...scores);
267
+ const range = max - min || 1;
268
+
269
+ return scores.map(s => {
270
+ const idx = Math.floor(((s - min) / range) * (chars.length - 1));
271
+ return chars[idx];
272
+ }).join("");
273
+ }
274
+
275
+ module.exports = {
276
+ loadHistory,
277
+ saveHistory,
278
+ recordCheck,
279
+ calculateTrend,
280
+ getMilestones,
281
+ formatHistoryDisplay,
282
+ };