@vibecheckai/cli 3.5.0 → 3.5.1

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 (326) hide show
  1. package/bin/registry.js +174 -449
  2. package/bin/runners/cli-utils.js +33 -2
  3. package/bin/runners/context/generators/cursor.js +2 -49
  4. package/bin/runners/context/generators/mcp.js +13 -15
  5. package/bin/runners/context/proof-context.js +1 -248
  6. package/bin/runners/lib/analysis-core.js +180 -198
  7. package/bin/runners/lib/analyzers.js +241 -2212
  8. package/bin/runners/lib/cli-output.js +210 -242
  9. package/bin/runners/lib/detectors-v2.js +785 -547
  10. package/bin/runners/lib/entitlements-v2.js +431 -161
  11. package/bin/runners/lib/error-handler.js +9 -16
  12. package/bin/runners/lib/global-flags.js +0 -37
  13. package/bin/runners/lib/html-proof-report.js +700 -350
  14. package/bin/runners/lib/missions/plan.js +6 -46
  15. package/bin/runners/lib/missions/templates.js +0 -232
  16. package/bin/runners/lib/route-truth.js +322 -1167
  17. package/bin/runners/lib/scan-output.js +467 -493
  18. package/bin/runners/lib/ship-output.js +27 -280
  19. package/bin/runners/lib/terminal-ui.js +700 -310
  20. package/bin/runners/lib/truth.js +321 -1004
  21. package/bin/runners/lib/unified-output.js +158 -162
  22. package/bin/runners/lib/upsell.js +204 -104
  23. package/bin/runners/runAIAgent.js +10 -5
  24. package/bin/runners/runAllowlist.js +324 -0
  25. package/bin/runners/runAuth.js +94 -344
  26. package/bin/runners/runCheckpoint.js +45 -43
  27. package/bin/runners/runContext.js +24 -139
  28. package/bin/runners/runDoctor.js +101 -136
  29. package/bin/runners/runEvidencePack.js +219 -0
  30. package/bin/runners/runFix.js +71 -82
  31. package/bin/runners/runGuard.js +119 -606
  32. package/bin/runners/runInit.js +60 -22
  33. package/bin/runners/runInstall.js +281 -0
  34. package/bin/runners/runLabs.js +341 -0
  35. package/bin/runners/runMcp.js +62 -139
  36. package/bin/runners/runPolish.js +83 -282
  37. package/bin/runners/runPromptFirewall.js +12 -5
  38. package/bin/runners/runProve.js +58 -33
  39. package/bin/runners/runReality.js +58 -81
  40. package/bin/runners/runReport.js +7 -34
  41. package/bin/runners/runRuntime.js +8 -5
  42. package/bin/runners/runScan.js +844 -219
  43. package/bin/runners/runShip.js +59 -721
  44. package/bin/runners/runValidate.js +11 -24
  45. package/bin/runners/runWatch.js +76 -131
  46. package/bin/vibecheck.js +69 -295
  47. package/mcp-server/ARCHITECTURE.md +339 -0
  48. package/mcp-server/__tests__/cache.test.ts +313 -0
  49. package/mcp-server/__tests__/executor.test.ts +239 -0
  50. package/mcp-server/__tests__/fixtures/exclusion-test/.cache/webpack/cache.pack +1 -0
  51. package/mcp-server/__tests__/fixtures/exclusion-test/.next/server/chunk.js +3 -0
  52. package/mcp-server/__tests__/fixtures/exclusion-test/.turbo/cache.json +3 -0
  53. package/mcp-server/__tests__/fixtures/exclusion-test/.venv/lib/env.py +3 -0
  54. package/mcp-server/__tests__/fixtures/exclusion-test/dist/bundle.js +3 -0
  55. package/mcp-server/__tests__/fixtures/exclusion-test/package.json +5 -0
  56. package/mcp-server/__tests__/fixtures/exclusion-test/src/app.ts +5 -0
  57. package/mcp-server/__tests__/fixtures/exclusion-test/venv/lib/config.py +4 -0
  58. package/mcp-server/__tests__/ids.test.ts +345 -0
  59. package/mcp-server/__tests__/integration/tools.test.ts +410 -0
  60. package/mcp-server/__tests__/registry.test.ts +365 -0
  61. package/mcp-server/__tests__/sandbox.test.ts +323 -0
  62. package/mcp-server/__tests__/schemas.test.ts +372 -0
  63. package/mcp-server/benchmarks/run-benchmarks.ts +304 -0
  64. package/mcp-server/examples/doctor.request.json +14 -0
  65. package/mcp-server/examples/doctor.response.json +53 -0
  66. package/mcp-server/examples/error.response.json +15 -0
  67. package/mcp-server/examples/scan.request.json +14 -0
  68. package/mcp-server/examples/scan.response.json +108 -0
  69. package/mcp-server/handlers/tool-handler.ts +671 -0
  70. package/mcp-server/index-v1.js +698 -0
  71. package/mcp-server/index-v3.ts +293 -0
  72. package/mcp-server/index.js +1080 -1757
  73. package/mcp-server/index.old.js +4137 -0
  74. package/mcp-server/lib/cache.ts +341 -0
  75. package/mcp-server/lib/errors.ts +346 -0
  76. package/mcp-server/lib/executor.ts +792 -0
  77. package/mcp-server/lib/ids.ts +238 -0
  78. package/mcp-server/lib/logger.ts +368 -0
  79. package/mcp-server/lib/metrics.ts +365 -0
  80. package/mcp-server/lib/sandbox.ts +337 -0
  81. package/mcp-server/lib/validator.ts +229 -0
  82. package/mcp-server/package-lock.json +165 -0
  83. package/mcp-server/package.json +32 -7
  84. package/mcp-server/premium-tools.js +2 -2
  85. package/mcp-server/registry/tools.json +476 -0
  86. package/mcp-server/schemas/error-envelope.schema.json +125 -0
  87. package/mcp-server/schemas/finding.schema.json +167 -0
  88. package/mcp-server/schemas/report-artifact.schema.json +88 -0
  89. package/mcp-server/schemas/run-request.schema.json +75 -0
  90. package/mcp-server/schemas/verdict.schema.json +168 -0
  91. package/mcp-server/tier-auth.d.ts +71 -0
  92. package/mcp-server/tier-auth.js +371 -183
  93. package/mcp-server/truth-context.js +90 -131
  94. package/mcp-server/truth-firewall-tools.js +1000 -1611
  95. package/mcp-server/tsconfig.json +34 -0
  96. package/mcp-server/vibecheck-tools.js +2 -2
  97. package/mcp-server/vitest.config.ts +16 -0
  98. package/package.json +3 -4
  99. package/bin/runners/lib/agent-firewall/ai/false-positive-analyzer.js +0 -474
  100. package/bin/runners/lib/agent-firewall/change-packet/builder.js +0 -488
  101. package/bin/runners/lib/agent-firewall/change-packet/schema.json +0 -228
  102. package/bin/runners/lib/agent-firewall/change-packet/store.js +0 -200
  103. package/bin/runners/lib/agent-firewall/claims/claim-types.js +0 -21
  104. package/bin/runners/lib/agent-firewall/claims/extractor.js +0 -303
  105. package/bin/runners/lib/agent-firewall/claims/patterns.js +0 -24
  106. package/bin/runners/lib/agent-firewall/critic/index.js +0 -151
  107. package/bin/runners/lib/agent-firewall/critic/judge.js +0 -432
  108. package/bin/runners/lib/agent-firewall/critic/prompts.js +0 -305
  109. package/bin/runners/lib/agent-firewall/evidence/auth-evidence.js +0 -88
  110. package/bin/runners/lib/agent-firewall/evidence/contract-evidence.js +0 -75
  111. package/bin/runners/lib/agent-firewall/evidence/env-evidence.js +0 -127
  112. package/bin/runners/lib/agent-firewall/evidence/resolver.js +0 -102
  113. package/bin/runners/lib/agent-firewall/evidence/route-evidence.js +0 -213
  114. package/bin/runners/lib/agent-firewall/evidence/side-effect-evidence.js +0 -145
  115. package/bin/runners/lib/agent-firewall/fs-hook/daemon.js +0 -19
  116. package/bin/runners/lib/agent-firewall/fs-hook/installer.js +0 -87
  117. package/bin/runners/lib/agent-firewall/fs-hook/watcher.js +0 -184
  118. package/bin/runners/lib/agent-firewall/git-hook/pre-commit.js +0 -163
  119. package/bin/runners/lib/agent-firewall/ide-extension/cursor.js +0 -107
  120. package/bin/runners/lib/agent-firewall/ide-extension/vscode.js +0 -68
  121. package/bin/runners/lib/agent-firewall/ide-extension/windsurf.js +0 -66
  122. package/bin/runners/lib/agent-firewall/interceptor/base.js +0 -304
  123. package/bin/runners/lib/agent-firewall/interceptor/cursor.js +0 -35
  124. package/bin/runners/lib/agent-firewall/interceptor/vscode.js +0 -35
  125. package/bin/runners/lib/agent-firewall/interceptor/windsurf.js +0 -34
  126. package/bin/runners/lib/agent-firewall/lawbook/distributor.js +0 -465
  127. package/bin/runners/lib/agent-firewall/lawbook/evaluator.js +0 -604
  128. package/bin/runners/lib/agent-firewall/lawbook/index.js +0 -304
  129. package/bin/runners/lib/agent-firewall/lawbook/registry.js +0 -514
  130. package/bin/runners/lib/agent-firewall/lawbook/schema.js +0 -420
  131. package/bin/runners/lib/agent-firewall/learning/learning-engine.js +0 -849
  132. package/bin/runners/lib/agent-firewall/logger.js +0 -141
  133. package/bin/runners/lib/agent-firewall/policy/default-policy.json +0 -90
  134. package/bin/runners/lib/agent-firewall/policy/engine.js +0 -103
  135. package/bin/runners/lib/agent-firewall/policy/loader.js +0 -451
  136. package/bin/runners/lib/agent-firewall/policy/rules/auth-drift.js +0 -50
  137. package/bin/runners/lib/agent-firewall/policy/rules/contract-drift.js +0 -50
  138. package/bin/runners/lib/agent-firewall/policy/rules/fake-success.js +0 -86
  139. package/bin/runners/lib/agent-firewall/policy/rules/ghost-env.js +0 -162
  140. package/bin/runners/lib/agent-firewall/policy/rules/ghost-route.js +0 -189
  141. package/bin/runners/lib/agent-firewall/policy/rules/scope.js +0 -93
  142. package/bin/runners/lib/agent-firewall/policy/rules/unsafe-side-effect.js +0 -57
  143. package/bin/runners/lib/agent-firewall/policy/schema.json +0 -183
  144. package/bin/runners/lib/agent-firewall/policy/verdict.js +0 -54
  145. package/bin/runners/lib/agent-firewall/proposal/extractor.js +0 -394
  146. package/bin/runners/lib/agent-firewall/proposal/index.js +0 -212
  147. package/bin/runners/lib/agent-firewall/proposal/schema.js +0 -251
  148. package/bin/runners/lib/agent-firewall/proposal/validator.js +0 -386
  149. package/bin/runners/lib/agent-firewall/reality/index.js +0 -332
  150. package/bin/runners/lib/agent-firewall/reality/state.js +0 -625
  151. package/bin/runners/lib/agent-firewall/reality/watcher.js +0 -322
  152. package/bin/runners/lib/agent-firewall/risk/index.js +0 -173
  153. package/bin/runners/lib/agent-firewall/risk/scorer.js +0 -328
  154. package/bin/runners/lib/agent-firewall/risk/thresholds.js +0 -321
  155. package/bin/runners/lib/agent-firewall/risk/vectors.js +0 -421
  156. package/bin/runners/lib/agent-firewall/simulator/diff-simulator.js +0 -472
  157. package/bin/runners/lib/agent-firewall/simulator/import-resolver.js +0 -346
  158. package/bin/runners/lib/agent-firewall/simulator/index.js +0 -181
  159. package/bin/runners/lib/agent-firewall/simulator/route-validator.js +0 -380
  160. package/bin/runners/lib/agent-firewall/time-machine/incident-correlator.js +0 -661
  161. package/bin/runners/lib/agent-firewall/time-machine/index.js +0 -267
  162. package/bin/runners/lib/agent-firewall/time-machine/replay-engine.js +0 -436
  163. package/bin/runners/lib/agent-firewall/time-machine/state-reconstructor.js +0 -490
  164. package/bin/runners/lib/agent-firewall/time-machine/timeline-builder.js +0 -530
  165. package/bin/runners/lib/agent-firewall/truthpack/index.js +0 -67
  166. package/bin/runners/lib/agent-firewall/truthpack/loader.js +0 -137
  167. package/bin/runners/lib/agent-firewall/unblock/planner.js +0 -337
  168. package/bin/runners/lib/agent-firewall/utils/ignore-checker.js +0 -118
  169. package/bin/runners/lib/api-client.js +0 -269
  170. package/bin/runners/lib/audit-logger.js +0 -532
  171. package/bin/runners/lib/authority/authorities/architecture.js +0 -364
  172. package/bin/runners/lib/authority/authorities/compliance.js +0 -341
  173. package/bin/runners/lib/authority/authorities/human.js +0 -343
  174. package/bin/runners/lib/authority/authorities/quality.js +0 -420
  175. package/bin/runners/lib/authority/authorities/security.js +0 -228
  176. package/bin/runners/lib/authority/index.js +0 -293
  177. package/bin/runners/lib/authority-badge.js +0 -425
  178. package/bin/runners/lib/bundle/bundle-intelligence.js +0 -846
  179. package/bin/runners/lib/cli-charts.js +0 -368
  180. package/bin/runners/lib/cli-config-display.js +0 -405
  181. package/bin/runners/lib/cli-demo.js +0 -275
  182. package/bin/runners/lib/cli-errors.js +0 -438
  183. package/bin/runners/lib/cli-help-formatter.js +0 -439
  184. package/bin/runners/lib/cli-interactive-menu.js +0 -509
  185. package/bin/runners/lib/cli-prompts.js +0 -441
  186. package/bin/runners/lib/cli-scan-cards.js +0 -362
  187. package/bin/runners/lib/compliance-reporter.js +0 -710
  188. package/bin/runners/lib/conductor/index.js +0 -671
  189. package/bin/runners/lib/easy/README.md +0 -123
  190. package/bin/runners/lib/easy/index.js +0 -140
  191. package/bin/runners/lib/easy/interactive-wizard.js +0 -788
  192. package/bin/runners/lib/easy/one-click-firewall.js +0 -564
  193. package/bin/runners/lib/easy/zero-config-reality.js +0 -714
  194. package/bin/runners/lib/engines/accessibility-engine.js +0 -390
  195. package/bin/runners/lib/engines/api-consistency-engine.js +0 -467
  196. package/bin/runners/lib/engines/ast-cache.js +0 -99
  197. package/bin/runners/lib/engines/async-patterns-engine.js +0 -444
  198. package/bin/runners/lib/engines/bundle-size-engine.js +0 -433
  199. package/bin/runners/lib/engines/code-quality-engine.js +0 -255
  200. package/bin/runners/lib/engines/confidence-scoring.js +0 -276
  201. package/bin/runners/lib/engines/console-logs-engine.js +0 -115
  202. package/bin/runners/lib/engines/context-detection.js +0 -264
  203. package/bin/runners/lib/engines/cross-file-analysis-engine.js +0 -533
  204. package/bin/runners/lib/engines/database-patterns-engine.js +0 -429
  205. package/bin/runners/lib/engines/dead-code-engine.js +0 -198
  206. package/bin/runners/lib/engines/deprecated-api-engine.js +0 -226
  207. package/bin/runners/lib/engines/duplicate-code-engine.js +0 -354
  208. package/bin/runners/lib/engines/empty-catch-engine.js +0 -260
  209. package/bin/runners/lib/engines/env-variables-engine.js +0 -458
  210. package/bin/runners/lib/engines/error-handling-engine.js +0 -437
  211. package/bin/runners/lib/engines/false-positive-prevention.js +0 -630
  212. package/bin/runners/lib/engines/file-filter.js +0 -131
  213. package/bin/runners/lib/engines/framework-adapters/index.js +0 -607
  214. package/bin/runners/lib/engines/framework-detection.js +0 -508
  215. package/bin/runners/lib/engines/hardcoded-secrets-engine.js +0 -251
  216. package/bin/runners/lib/engines/import-order-engine.js +0 -429
  217. package/bin/runners/lib/engines/mock-data-engine.js +0 -315
  218. package/bin/runners/lib/engines/naming-conventions-engine.js +0 -544
  219. package/bin/runners/lib/engines/noise-reduction-engine.js +0 -452
  220. package/bin/runners/lib/engines/orchestrator.js +0 -334
  221. package/bin/runners/lib/engines/parallel-processor.js +0 -71
  222. package/bin/runners/lib/engines/performance-issues-engine.js +0 -405
  223. package/bin/runners/lib/engines/react-patterns-engine.js +0 -457
  224. package/bin/runners/lib/engines/security-vulnerabilities-engine.js +0 -571
  225. package/bin/runners/lib/engines/todo-fixme-engine.js +0 -115
  226. package/bin/runners/lib/engines/type-aware-engine.js +0 -376
  227. package/bin/runners/lib/engines/unsafe-regex-engine.js +0 -225
  228. package/bin/runners/lib/engines/vibecheck-engines/README.md +0 -53
  229. package/bin/runners/lib/engines/vibecheck-engines/index.js +0 -124
  230. package/bin/runners/lib/engines/vibecheck-engines/lib/ai-hallucination-engine.js +0 -806
  231. package/bin/runners/lib/engines/vibecheck-engines/lib/hardcoded-secrets-engine.js +0 -439
  232. package/bin/runners/lib/engines/vibecheck-engines/lib/smart-fix-engine.js +0 -577
  233. package/bin/runners/lib/engines/vibecheck-engines/lib/vibe-score-engine.js +0 -543
  234. package/bin/runners/lib/engines/vibecheck-engines/package.json +0 -13
  235. package/bin/runners/lib/engines/vibecheck-engines.js +0 -514
  236. package/bin/runners/lib/enhanced-features/index.js +0 -305
  237. package/bin/runners/lib/enhanced-output.js +0 -631
  238. package/bin/runners/lib/enterprise.js +0 -300
  239. package/bin/runners/lib/exit-codes.js +0 -275
  240. package/bin/runners/lib/fingerprint.js +0 -377
  241. package/bin/runners/lib/firewall/command-validator.js +0 -351
  242. package/bin/runners/lib/firewall/config.js +0 -341
  243. package/bin/runners/lib/firewall/content-validator.js +0 -519
  244. package/bin/runners/lib/firewall/index.js +0 -101
  245. package/bin/runners/lib/firewall/path-validator.js +0 -256
  246. package/bin/runners/lib/help-formatter.js +0 -413
  247. package/bin/runners/lib/intelligence/cross-repo-intelligence.js +0 -817
  248. package/bin/runners/lib/logger.js +0 -38
  249. package/bin/runners/lib/mcp-utils.js +0 -425
  250. package/bin/runners/lib/output/index.js +0 -1022
  251. package/bin/runners/lib/policy-engine.js +0 -652
  252. package/bin/runners/lib/polish/autofix/accessibility-fixes.js +0 -333
  253. package/bin/runners/lib/polish/autofix/async-handlers.js +0 -273
  254. package/bin/runners/lib/polish/autofix/dead-code.js +0 -280
  255. package/bin/runners/lib/polish/autofix/imports-optimizer.js +0 -344
  256. package/bin/runners/lib/polish/autofix/index.js +0 -200
  257. package/bin/runners/lib/polish/autofix/remove-consoles.js +0 -209
  258. package/bin/runners/lib/polish/autofix/strengthen-types.js +0 -245
  259. package/bin/runners/lib/polish/backend-checks.js +0 -148
  260. package/bin/runners/lib/polish/documentation-checks.js +0 -111
  261. package/bin/runners/lib/polish/frontend-checks.js +0 -168
  262. package/bin/runners/lib/polish/index.js +0 -71
  263. package/bin/runners/lib/polish/infrastructure-checks.js +0 -131
  264. package/bin/runners/lib/polish/library-detection.js +0 -175
  265. package/bin/runners/lib/polish/performance-checks.js +0 -100
  266. package/bin/runners/lib/polish/security-checks.js +0 -148
  267. package/bin/runners/lib/polish/utils.js +0 -203
  268. package/bin/runners/lib/prompt-builder.js +0 -540
  269. package/bin/runners/lib/proof-certificate.js +0 -634
  270. package/bin/runners/lib/reality/accessibility-audit.js +0 -946
  271. package/bin/runners/lib/reality/api-contract-validator.js +0 -1012
  272. package/bin/runners/lib/reality/chaos-engineering.js +0 -1084
  273. package/bin/runners/lib/reality/performance-tracker.js +0 -1077
  274. package/bin/runners/lib/reality/scenario-generator.js +0 -1404
  275. package/bin/runners/lib/reality/visual-regression.js +0 -852
  276. package/bin/runners/lib/reality-profiler.js +0 -717
  277. package/bin/runners/lib/replay/flight-recorder-viewer.js +0 -1160
  278. package/bin/runners/lib/review/ai-code-review.js +0 -832
  279. package/bin/runners/lib/rules/custom-rule-engine.js +0 -985
  280. package/bin/runners/lib/sbom-generator.js +0 -641
  281. package/bin/runners/lib/scan-output-enhanced.js +0 -512
  282. package/bin/runners/lib/security/owasp-scanner.js +0 -939
  283. package/bin/runners/lib/ship-output-enterprise.js +0 -239
  284. package/bin/runners/lib/unified-cli-output.js +0 -777
  285. package/bin/runners/lib/validators/contract-validator.js +0 -283
  286. package/bin/runners/lib/validators/dead-export-detector.js +0 -279
  287. package/bin/runners/lib/validators/dep-audit.js +0 -245
  288. package/bin/runners/lib/validators/env-validator.js +0 -319
  289. package/bin/runners/lib/validators/index.js +0 -120
  290. package/bin/runners/lib/validators/license-checker.js +0 -252
  291. package/bin/runners/lib/validators/route-validator.js +0 -290
  292. package/bin/runners/runAgent.d.ts +0 -5
  293. package/bin/runners/runAgent.js +0 -164
  294. package/bin/runners/runApprove.js +0 -1233
  295. package/bin/runners/runAuthority.js +0 -528
  296. package/bin/runners/runClassify.js +0 -862
  297. package/bin/runners/runConductor.js +0 -772
  298. package/bin/runners/runContainer.js +0 -366
  299. package/bin/runners/runContext.d.ts +0 -4
  300. package/bin/runners/runEasy.js +0 -410
  301. package/bin/runners/runFirewall.d.ts +0 -5
  302. package/bin/runners/runFirewall.js +0 -137
  303. package/bin/runners/runFirewallHook.d.ts +0 -5
  304. package/bin/runners/runFirewallHook.js +0 -59
  305. package/bin/runners/runIaC.js +0 -372
  306. package/bin/runners/runPolish.d.ts +0 -4
  307. package/bin/runners/runProof.zip +0 -0
  308. package/bin/runners/runTruth.d.ts +0 -5
  309. package/bin/runners/runTruth.js +0 -104
  310. package/bin/runners/runVibe.js +0 -791
  311. package/mcp-server/HARDENING_SUMMARY.md +0 -299
  312. package/mcp-server/agent-firewall-interceptor.js +0 -500
  313. package/mcp-server/authority-tools.js +0 -569
  314. package/mcp-server/conductor/conflict-resolver.js +0 -588
  315. package/mcp-server/conductor/execution-planner.js +0 -544
  316. package/mcp-server/conductor/index.js +0 -377
  317. package/mcp-server/conductor/lock-manager.js +0 -615
  318. package/mcp-server/conductor/request-queue.js +0 -550
  319. package/mcp-server/conductor/session-manager.js +0 -500
  320. package/mcp-server/conductor/tools.js +0 -510
  321. package/mcp-server/lib/api-client.cjs +0 -13
  322. package/mcp-server/lib/logger.cjs +0 -30
  323. package/mcp-server/logger.js +0 -173
  324. package/mcp-server/tools-v3.js +0 -1039
  325. package/mcp-server/tools.js +0 -495
  326. package/mcp-server/vibecheck-mcp-server-3.2.0.tgz +0 -0
@@ -1,37 +1,33 @@
1
1
  /**
2
- * Terminal UI - The Vibecheck Design System
3
- * * Centralizes all visual primitives to ensure "World Class" consistency.
4
- * * features:
5
- * - Global Grid: 76 chars width
6
- * - Border Style: Double Outer / Single Inner
7
- * - Components: Spinners, Progress Bars, Tables, Headers
2
+ * Terminal UI - Premium CLI Components
3
+ *
4
+ * Reusable across all vibecheck commands:
5
+ * - Advanced spinners with phases
6
+ * - Multi-line progress displays
7
+ * - Score visualizations
8
+ * - Tables and cards
9
+ * - Color utilities
10
+ *
11
+ * Zero dependencies - pure ANSI escape codes
8
12
  */
9
13
 
10
- "use strict";
11
-
12
14
  // ═══════════════════════════════════════════════════════════════════════════════
13
- // 1. CORE CONSTANTS & ANSI
15
+ // ANSI ESCAPE CODES
14
16
  // ═══════════════════════════════════════════════════════════════════════════════
15
17
 
16
- const WIDTH = 76;
17
-
18
18
  const ESC = '\x1b';
19
- const SUPPORTS_TRUECOLOR = process.env.COLORTERM === 'truecolor' ||
20
- process.env.TERM_PROGRAM === 'iTerm.app' ||
21
- process.env.TERM_PROGRAM === 'Apple_Terminal' ||
22
- process.env.WT_SESSION;
23
19
 
24
20
  const ansi = {
21
+ // Text styles
25
22
  reset: `${ESC}[0m`,
26
23
  bold: `${ESC}[1m`,
27
24
  dim: `${ESC}[2m`,
28
25
  italic: `${ESC}[3m`,
29
26
  underline: `${ESC}[4m`,
30
- blink: `${ESC}[5m`,
31
27
  inverse: `${ESC}[7m`,
32
- hidden: `${ESC}[8m`,
33
- strike: `${ESC}[9m`,
34
- // Colors
28
+ strikethrough: `${ESC}[9m`,
29
+
30
+ // Standard colors
35
31
  black: `${ESC}[30m`,
36
32
  red: `${ESC}[31m`,
37
33
  green: `${ESC}[32m`,
@@ -40,6 +36,7 @@ const ansi = {
40
36
  magenta: `${ESC}[35m`,
41
37
  cyan: `${ESC}[36m`,
42
38
  white: `${ESC}[37m`,
39
+
43
40
  // Bright colors
44
41
  gray: `${ESC}[90m`,
45
42
  brightRed: `${ESC}[91m`,
@@ -49,10 +46,8 @@ const ansi = {
49
46
  brightMagenta: `${ESC}[95m`,
50
47
  brightCyan: `${ESC}[96m`,
51
48
  brightWhite: `${ESC}[97m`,
52
- // RGB colors (truecolor support)
53
- rgb: (r, g, b) => SUPPORTS_TRUECOLOR ? `${ESC}[38;2;${r};${g};${b}m` : '',
54
- bgRgb: (r, g, b) => SUPPORTS_TRUECOLOR ? `${ESC}[48;2;${r};${g};${b}m` : '',
55
- // Backgrounds
49
+
50
+ // Background colors
56
51
  bgBlack: `${ESC}[40m`,
57
52
  bgRed: `${ESC}[41m`,
58
53
  bgGreen: `${ESC}[42m`,
@@ -61,403 +56,798 @@ const ansi = {
61
56
  bgMagenta: `${ESC}[45m`,
62
57
  bgCyan: `${ESC}[46m`,
63
58
  bgWhite: `${ESC}[47m`,
64
- bgBrightBlack: `${ESC}[100m`,
59
+
65
60
  // Cursor control
66
61
  hideCursor: `${ESC}[?25l`,
67
62
  showCursor: `${ESC}[?25h`,
68
- clearLine: `${ESC}[2K`,
69
- clearScreen: `${ESC}[2J`,
70
- clearToEnd: `${ESC}[0J`,
71
63
  saveCursor: `${ESC}[s`,
72
64
  restoreCursor: `${ESC}[u`,
65
+ clearLine: `${ESC}[2K`,
66
+ clearScreen: `${ESC}[2J`,
73
67
  cursorUp: (n = 1) => `${ESC}[${n}A`,
74
68
  cursorDown: (n = 1) => `${ESC}[${n}B`,
75
69
  cursorRight: (n = 1) => `${ESC}[${n}C`,
76
70
  cursorLeft: (n = 1) => `${ESC}[${n}D`,
77
- cursorTo: (row, col) => `${ESC}[${row};${col}H`,
78
- cursorHome: `${ESC}[H`,
71
+ cursorTo: (x, y) => `${ESC}[${y};${x}H`,
72
+
73
+ // 24-bit color (truecolor)
74
+ rgb: (r, g, b) => `${ESC}[38;2;${r};${g};${b}m`,
75
+ bgRgb: (r, g, b) => `${ESC}[48;2;${r};${g};${b}m`,
76
+ };
77
+
78
+ // ═══════════════════════════════════════════════════════════════════════════════
79
+ // COLOR PALETTE - Electric Blue Theme
80
+ // ═══════════════════════════════════════════════════════════════════════════════
81
+
82
+ const colors = {
83
+ // Primary brand colors
84
+ primary: ansi.rgb(99, 102, 241), // Indigo
85
+ secondary: ansi.rgb(139, 92, 246), // Purple
86
+ accent: ansi.rgb(6, 182, 212), // Cyan
87
+
88
+ // Semantic colors
89
+ success: ansi.rgb(16, 185, 129), // Emerald
90
+ warning: ansi.rgb(245, 158, 11), // Amber
91
+ error: ansi.rgb(239, 68, 68), // Red
92
+ info: ansi.rgb(59, 130, 246), // Blue
93
+
94
+ // Severity colors
95
+ critical: ansi.rgb(220, 38, 38), // Red-600
96
+ high: ansi.rgb(234, 88, 12), // Orange-600
97
+ medium: ansi.rgb(202, 138, 4), // Yellow-600
98
+ low: ansi.rgb(37, 99, 235), // Blue-600
99
+
100
+ // Gradient stops
101
+ gradient: {
102
+ cyan: ansi.rgb(0, 255, 255),
103
+ blue: ansi.rgb(100, 149, 237),
104
+ purple: ansi.rgb(138, 43, 226),
105
+ pink: ansi.rgb(236, 72, 153),
106
+ orange: ansi.rgb(251, 146, 60),
107
+ },
108
+
109
+ // Background variants
110
+ bg: {
111
+ success: ansi.bgRgb(16, 185, 129),
112
+ warning: ansi.bgRgb(245, 158, 11),
113
+ error: ansi.bgRgb(220, 38, 38),
114
+ info: ansi.bgRgb(59, 130, 246),
115
+ muted: ansi.bgRgb(39, 39, 42),
116
+ },
79
117
  };
80
118
 
81
- // Semantic Palette
82
- const style = {
83
- success: (t) => `${ansi.green}${t}${ansi.reset}`,
84
- error: (t) => `${ansi.red}${t}${ansi.reset}`,
85
- warning: (t) => `${ansi.yellow}${t}${ansi.reset}`,
86
- info: (t) => `${ansi.cyan}${t}${ansi.reset}`,
87
- subtle: (t) => `${ansi.gray}${t}${ansi.reset}`,
88
- highlight: (t) => `${ansi.bold}${ansi.white}${t}${ansi.reset}`,
89
- label: (t) => `${ansi.blue}${t}${ansi.reset}`,
90
- // Backgrounds for badges
91
- bgSuccess: (t) => `${ansi.bgGreen}${ansi.bold}${ansi.white} ${t} ${ansi.reset}`,
92
- bgError: (t) => `${ansi.bgRed}${ansi.bold}${ansi.white} ${t} ${ansi.reset}`,
93
- bgWarn: (t) => `${ansi.bgYellow}${ansi.bold}${ansi.white} ${t} ${ansi.reset}`,
119
+ // ═══════════════════════════════════════════════════════════════════════════════
120
+ // BOX DRAWING CHARACTERS
121
+ // ═══════════════════════════════════════════════════════════════════════════════
122
+
123
+ const box = {
124
+ // Rounded corners
125
+ topLeft: '╭',
126
+ topRight: '╮',
127
+ bottomLeft: '╰',
128
+ bottomRight: '╯',
129
+ horizontal: '─',
130
+ vertical: '│',
131
+
132
+ // Sharp corners
133
+ sharpTopLeft: '┌',
134
+ sharpTopRight: '┐',
135
+ sharpBottomLeft: '└',
136
+ sharpBottomRight: '┘',
137
+
138
+ // Double lines
139
+ doubleHorizontal: '═',
140
+ doubleVertical: '║',
141
+ doubleTopLeft: '╔',
142
+ doubleTopRight: '╗',
143
+ doubleBottomLeft: '╚',
144
+ doubleBottomRight: '╝',
145
+
146
+ // Connectors
147
+ teeRight: '├',
148
+ teeLeft: '┤',
149
+ teeDown: '┬',
150
+ teeUp: '┴',
151
+ cross: '┼',
152
+
153
+ // Block elements
154
+ fullBlock: '█',
155
+ lightShade: '░',
156
+ mediumShade: '▒',
157
+ darkShade: '▓',
158
+ upperHalf: '▀',
159
+ lowerHalf: '▄',
160
+ leftHalf: '▌',
161
+ rightHalf: '▐',
94
162
  };
95
163
 
164
+ // ═══════════════════════════════════════════════════════════════════════════════
165
+ // ICONS & SYMBOLS
166
+ // ═══════════════════════════════════════════════════════════════════════════════
167
+
96
168
  const icons = {
97
169
  // Status
98
170
  success: '✓',
99
- check: '✓',
100
171
  error: '✗',
101
- cross: '✗',
102
172
  warning: '⚠',
103
173
  info: 'ℹ',
104
- // Navigation
105
- bullet: '•',
106
- pointer: '❯',
174
+ question: '?',
175
+
176
+ // Arrows
107
177
  arrowRight: '→',
108
- arrow: '→',
109
178
  arrowLeft: '←',
110
179
  arrowUp: '↑',
111
180
  arrowDown: '↓',
112
- // Shapes
113
- line: '─',
114
- radioOn: '◉',
115
- radioOff: '',
181
+ arrowBoth: '↔',
182
+
183
+ // Pointers
184
+ pointer: '',
185
+ pointerSmall: '›',
186
+ bullet: '•',
187
+ dot: '·',
188
+
189
+ // Misc
116
190
  star: '★',
117
- sparkle: '',
118
- // Emoji icons
119
- lock: '🔒',
120
- ship: '🚀',
121
- rocket: '🚀',
122
- eye: '👁️',
123
- brain: '🧠',
124
- target: '🎯',
125
- chart: '📊',
126
- wrench: '🔧',
127
- gear: '⚙️',
128
- shield: '🛡️',
191
+ heart: '',
129
192
  lightning: '⚡',
130
193
  fire: '🔥',
131
- folder: '📁',
132
- file: '📄',
133
- search: '🔍',
134
- book: '📖',
135
- server: '🖥️',
136
- // Severity
137
- critical: '🔴',
138
- high: '🟠',
194
+ rocket: '🚀',
195
+ check: '',
196
+ radioOn: '',
197
+ radioOff: '',
198
+
199
+ // Severity badges
200
+ critical: '🚨',
201
+ high: '🔴',
139
202
  medium: '🟡',
140
203
  low: '🔵',
141
204
  };
142
205
 
143
206
  // ═══════════════════════════════════════════════════════════════════════════════
144
- // 2. LAYOUT PRIMITIVES (The "Grid")
207
+ // SPINNER - Advanced Multi-Phase Spinner
145
208
  // ═══════════════════════════════════════════════════════════════════════════════
146
209
 
147
- const BOX = {
148
- // Outer Frame (Double)
149
- tl: '', tr: '', bl: '', br: '', h: '═', v: '║',
150
- trT: '', tlT: '',
151
- // Inner Dividers (Single)
152
- ltl: '', ltr: '', lbl: '', lbr: '', lh: '', lv: '',
153
- lt: '', lb: '', lx: '', ltrT: '', ltlT: ''
210
+ const SPINNER_FRAMES = {
211
+ dots: ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'],
212
+ line: ['|', '/', '-', '\\'],
213
+ arc: ['', '◠', '', '◞', '◡', '◟'],
214
+ circle: ['◐', '◓', '◑', '◒'],
215
+ bounce: ['', '⠂', '', '⡀', '', '', '', ''],
216
+ pulse: ['', '▓', '', '', '', ''],
154
217
  };
155
218
 
156
- /**
157
- * Centers text within the standard width, accounting for ANSI codes
158
- */
159
- function padCenter(str, width = WIDTH - 2) {
160
- const visibleLen = str.replace(/\u001b\[\d+m/g, '').length;
161
- const padding = Math.max(0, width - visibleLen);
162
- const left = Math.floor(padding / 2);
163
- return ' '.repeat(left) + str + ' '.repeat(padding - left);
164
- }
165
-
166
- /**
167
- * Pads text to the right, accounting for ANSI codes
168
- */
169
- function padRight(str, len) {
170
- const visibleLen = str.replace(/\u001b\[\d+m/g, '').length;
171
- const truncated = visibleLen > len ? str.substring(0, len - 3) + '...' : str;
172
- const finalLen = truncated.replace(/\u001b\[\d+m/g, '').length;
173
- return truncated + ' '.repeat(Math.max(0, len - finalLen));
174
- }
175
-
176
- /**
177
- * Truncates text with ellipsis
178
- */
179
- function truncate(str, len) {
180
- if (!str) return '';
181
- const clean = str.replace(/\u001b\[\d+m/g, '');
182
- if (clean.length <= len) return str;
183
- return clean.substring(0, len - 3) + '...';
184
- }
185
-
186
- // ═══════════════════════════════════════════════════════════════════════════════
187
- // 3. COMPONENT: SPINNER
188
- // ═══════════════════════════════════════════════════════════════════════════════
189
-
190
- const SPINNER_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
191
-
192
219
  class Spinner {
193
- constructor(text = '', colorStr = ansi.cyan) {
194
- this.text = text;
195
- this.colorStr = colorStr;
196
- this.timer = null;
220
+ constructor(options = {}) {
221
+ this.frames = SPINNER_FRAMES[options.type || 'dots'];
222
+ this.interval = options.interval || 80;
223
+ this.stream = options.stream || process.stdout;
224
+ this.color = options.color || colors.primary;
197
225
  this.frameIndex = 0;
226
+ this.timer = null;
227
+ this.text = '';
228
+ this.phases = [];
229
+ this.currentPhase = 0;
230
+ this.lines = 1;
198
231
  }
199
-
232
+
200
233
  start(text) {
201
- if (text) this.text = text;
202
- process.stdout.write(ansi.hideCursor);
203
- this.timer = setInterval(() => {
204
- const frame = SPINNER_FRAMES[this.frameIndex];
205
- this.frameIndex = (this.frameIndex + 1) % SPINNER_FRAMES.length;
206
- process.stdout.write(`\r ${this.colorStr}${frame}${ansi.reset} ${this.text}`);
207
- }, 80);
234
+ this.text = text;
235
+ this.stream.write(ansi.hideCursor);
236
+ this._render();
237
+ this.timer = setInterval(() => this._render(), this.interval);
208
238
  return this;
209
239
  }
210
-
211
- stop(symbol = icons.success, color = ansi.green, finalMsg) {
212
- if (this.timer) clearInterval(this.timer);
213
- process.stdout.write(`\r${ansi.clearLine}`);
214
- if (finalMsg !== null) { // Pass null to clear completely
215
- const msg = finalMsg || this.text;
216
- console.log(` ${color}${symbol}${ansi.reset} ${msg}`);
240
+
241
+ update(text) {
242
+ this.text = text;
243
+ return this;
244
+ }
245
+
246
+ setPhases(phases) {
247
+ this.phases = phases;
248
+ this.currentPhase = 0;
249
+ return this;
250
+ }
251
+
252
+ nextPhase() {
253
+ if (this.currentPhase < this.phases.length - 1) {
254
+ this.currentPhase++;
255
+ this.text = this.phases[this.currentPhase];
217
256
  }
218
- process.stdout.write(ansi.showCursor);
219
257
  return this;
220
258
  }
221
-
222
- succeed(text) { return this.stop(icons.success, ansi.green, text); }
223
- fail(text) { return this.stop(icons.error, ansi.red, text); }
224
- warn(text) { return this.stop(icons.warning, ansi.yellow, text); }
259
+
260
+ succeed(text) {
261
+ this._stop();
262
+ this._clear();
263
+ const msg = text || this.text;
264
+ this.stream.write(` ${colors.success}${icons.success}${ansi.reset} ${msg}\n`);
265
+ this.stream.write(ansi.showCursor);
266
+ return this;
267
+ }
268
+
269
+ fail(text) {
270
+ this._stop();
271
+ this._clear();
272
+ const msg = text || this.text;
273
+ this.stream.write(` ${colors.error}${icons.error}${ansi.reset} ${msg}\n`);
274
+ this.stream.write(ansi.showCursor);
275
+ return this;
276
+ }
277
+
278
+ warn(text) {
279
+ this._stop();
280
+ this._clear();
281
+ const msg = text || this.text;
282
+ this.stream.write(` ${colors.warning}${icons.warning}${ansi.reset} ${msg}\n`);
283
+ this.stream.write(ansi.showCursor);
284
+ return this;
285
+ }
286
+
287
+ info(text) {
288
+ this._stop();
289
+ this._clear();
290
+ const msg = text || this.text;
291
+ this.stream.write(` ${colors.info}${icons.info}${ansi.reset} ${msg}\n`);
292
+ this.stream.write(ansi.showCursor);
293
+ return this;
294
+ }
295
+
296
+ stop() {
297
+ this._stop();
298
+ this._clear();
299
+ this.stream.write(ansi.showCursor);
300
+ return this;
301
+ }
302
+
303
+ _render() {
304
+ const frame = this.frames[this.frameIndex];
305
+ this.frameIndex = (this.frameIndex + 1) % this.frames.length;
306
+
307
+ this._clear();
308
+
309
+ let output = ` ${this.color}${frame}${ansi.reset} ${this.text}`;
310
+
311
+ // Add phase indicator if phases are set
312
+ if (this.phases.length > 0) {
313
+ const phaseIndicator = this.phases.map((_, i) =>
314
+ i < this.currentPhase ? `${colors.success}●${ansi.reset}` :
315
+ i === this.currentPhase ? `${this.color}●${ansi.reset}` :
316
+ `${ansi.dim}○${ansi.reset}`
317
+ ).join(' ');
318
+ output += ` ${ansi.dim}[${ansi.reset}${phaseIndicator}${ansi.dim}]${ansi.reset}`;
319
+ }
320
+
321
+ this.stream.write(`\r${output}`);
322
+ }
323
+
324
+ _stop() {
325
+ if (this.timer) {
326
+ clearInterval(this.timer);
327
+ this.timer = null;
328
+ }
329
+ }
330
+
331
+ _clear() {
332
+ this.stream.write(`\r${ansi.clearLine}`);
333
+ }
225
334
  }
226
335
 
227
336
  // ═══════════════════════════════════════════════════════════════════════════════
228
- // 4. COMPONENT: PROGRESS BAR
337
+ // PROGRESS BAR
229
338
  // ═══════════════════════════════════════════════════════════════════════════════
230
339
 
231
- function renderProgressBar(percentage, width = 20, color = ansi.green) {
232
- const filled = Math.round((percentage / 100) * width);
233
- return `${color}${'█'.repeat(filled)}${ansi.gray}${'░'.repeat(width - filled)}${ansi.reset}`;
340
+ class ProgressBar {
341
+ constructor(options = {}) {
342
+ this.total = options.total || 100;
343
+ this.width = options.width || 40;
344
+ this.complete = options.complete || '█';
345
+ this.incomplete = options.incomplete || '░';
346
+ this.stream = options.stream || process.stdout;
347
+ this.current = 0;
348
+ this.startTime = Date.now();
349
+ }
350
+
351
+ update(current, label = '') {
352
+ this.current = current;
353
+ const percent = Math.min(100, Math.round((current / this.total) * 100));
354
+ const filled = Math.round((percent / 100) * this.width);
355
+ const empty = this.width - filled;
356
+
357
+ const color = percent >= 80 ? colors.success : percent >= 50 ? colors.warning : colors.error;
358
+ const bar = `${color}${this.complete.repeat(filled)}${ansi.dim}${this.incomplete.repeat(empty)}${ansi.reset}`;
359
+
360
+ const elapsed = ((Date.now() - this.startTime) / 1000).toFixed(1);
361
+ const eta = current > 0 ? (((this.total - current) / current) * (Date.now() - this.startTime) / 1000).toFixed(1) : '?';
362
+
363
+ this.stream.write(`\r${ansi.clearLine} ${bar} ${ansi.bold}${percent}%${ansi.reset} ${ansi.dim}${label} (${elapsed}s / ~${eta}s)${ansi.reset}`);
364
+ }
365
+
366
+ complete(label = 'Complete') {
367
+ this.update(this.total, label);
368
+ this.stream.write('\n');
369
+ }
234
370
  }
235
371
 
236
372
  // ═══════════════════════════════════════════════════════════════════════════════
237
- // 5. STANDARD RENDERERS (The "Look")
373
+ // MULTI-LINE PROGRESS - Phase-Based Progress Display
238
374
  // ═══════════════════════════════════════════════════════════════════════════════
239
375
 
240
- /**
241
- * Renders the Standard Double-Border Header with Logo
242
- */
243
- function renderScreenHeader(logoAscii, title, logoColor = (t) => t, context) {
244
- const lines = [];
376
+ class PhaseProgress {
377
+ constructor(phases, options = {}) {
378
+ this.phases = phases.map(p => ({
379
+ name: p.name || p,
380
+ status: 'pending', // pending, running, success, error, skipped
381
+ message: '',
382
+ duration: null,
383
+ }));
384
+ this.currentPhase = -1;
385
+ this.stream = options.stream || process.stdout;
386
+ this.startTime = null;
387
+ this.phaseStartTime = null;
388
+ this.spinner = new Spinner({ color: colors.primary });
389
+ }
245
390
 
246
- // Top Frame
247
- lines.push(ansi.gray + BOX.tl + BOX.h.repeat(WIDTH - 2) + BOX.tr + ansi.reset);
248
- lines.push(ansi.gray + BOX.v + ansi.reset + ' '.repeat(WIDTH - 2) + ansi.gray + BOX.v + ansi.reset);
249
-
250
- // Logo Processing
251
- logoAscii.trim().split('\n').forEach(line => {
252
- const cleanLine = line.replace(/\r/g, '');
253
- const maxLen = Math.max(...logoAscii.split('\n').map(l => l.length));
254
- const padding = ' '.repeat(Math.floor((WIDTH - 2 - maxLen) / 2));
255
- const rightPad = ' '.repeat(WIDTH - 2 - padding.length - cleanLine.length);
391
+ start() {
392
+ this.startTime = Date.now();
393
+ this.stream.write(ansi.hideCursor);
394
+ this._render();
395
+ return this;
396
+ }
397
+
398
+ startPhase(index, message = '') {
399
+ if (this.currentPhase >= 0 && this.phases[this.currentPhase].status === 'running') {
400
+ this.phases[this.currentPhase].status = 'success';
401
+ this.phases[this.currentPhase].duration = Date.now() - this.phaseStartTime;
402
+ }
256
403
 
257
- lines.push(ansi.gray + BOX.v + ansi.reset + padding + logoColor(cleanLine) + rightPad + ansi.gray + BOX.v + ansi.reset);
258
- });
259
-
260
- // Title
261
- lines.push(ansi.gray + BOX.v + ansi.reset + ' '.repeat(WIDTH - 2) + ansi.gray + BOX.v + ansi.reset);
262
- lines.push(ansi.gray + BOX.v + ansi.reset + padCenter(ansi.bold + ansi.white + title + ansi.reset, WIDTH - 2) + ansi.gray + BOX.v + ansi.reset);
263
- lines.push(ansi.gray + BOX.v + ansi.reset + ' '.repeat(WIDTH - 2) + ansi.gray + BOX.v + ansi.reset);
264
-
265
- // Context Bar (Optional)
266
- if (context) {
267
- lines.push(ansi.gray + BOX.trT + BOX.h.repeat(WIDTH - 2) + BOX.tlT + ansi.reset);
268
- const ctxStr = `${ansi.gray}${context.label}: ${ansi.reset}${ansi.cyan}${context.value}${ansi.reset}`;
269
- lines.push(ansi.gray + BOX.v + ansi.reset + padCenter(ctxStr, WIDTH - 2) + ansi.gray + BOX.v + ansi.reset);
270
- lines.push(ansi.gray + BOX.trT + BOX.h.repeat(WIDTH - 2) + BOX.tlT + ansi.reset);
271
- } else {
272
- lines.push(ansi.gray + BOX.trT + BOX.h.repeat(WIDTH - 2) + BOX.tlT + ansi.reset);
404
+ this.currentPhase = index;
405
+ this.phaseStartTime = Date.now();
406
+ this.phases[index].status = 'running';
407
+ this.phases[index].message = message;
408
+ this._render();
409
+ return this;
410
+ }
411
+
412
+ updatePhase(message) {
413
+ if (this.currentPhase >= 0) {
414
+ this.phases[this.currentPhase].message = message;
415
+ this._render();
416
+ }
417
+ return this;
273
418
  }
419
+
420
+ succeedPhase(message = '') {
421
+ if (this.currentPhase >= 0) {
422
+ this.phases[this.currentPhase].status = 'success';
423
+ this.phases[this.currentPhase].duration = Date.now() - this.phaseStartTime;
424
+ if (message) this.phases[this.currentPhase].message = message;
425
+ this._render();
426
+ }
427
+ return this;
428
+ }
429
+
430
+ failPhase(message = '') {
431
+ if (this.currentPhase >= 0) {
432
+ this.phases[this.currentPhase].status = 'error';
433
+ this.phases[this.currentPhase].duration = Date.now() - this.phaseStartTime;
434
+ if (message) this.phases[this.currentPhase].message = message;
435
+ this._render();
436
+ }
437
+ return this;
438
+ }
439
+
440
+ skipPhase(message = 'Skipped') {
441
+ if (this.currentPhase >= 0) {
442
+ this.phases[this.currentPhase].status = 'skipped';
443
+ this.phases[this.currentPhase].message = message;
444
+ this._render();
445
+ }
446
+ return this;
447
+ }
448
+
449
+ finish() {
450
+ if (this.currentPhase >= 0 && this.phases[this.currentPhase].status === 'running') {
451
+ this.phases[this.currentPhase].status = 'success';
452
+ this.phases[this.currentPhase].duration = Date.now() - this.phaseStartTime;
453
+ }
454
+ this._render();
455
+ this.stream.write(ansi.showCursor);
456
+ return this;
457
+ }
458
+
459
+ _render() {
460
+ // Move cursor up to clear previous render
461
+ if (this._rendered) {
462
+ this.stream.write(ansi.cursorUp(this.phases.length + 1));
463
+ }
464
+ this._rendered = true;
465
+
466
+ const totalDuration = Date.now() - this.startTime;
467
+
468
+ for (const phase of this.phases) {
469
+ this.stream.write(ansi.clearLine);
470
+
471
+ let statusIcon, statusColor;
472
+ switch (phase.status) {
473
+ case 'success':
474
+ statusIcon = icons.success;
475
+ statusColor = colors.success;
476
+ break;
477
+ case 'error':
478
+ statusIcon = icons.error;
479
+ statusColor = colors.error;
480
+ break;
481
+ case 'running':
482
+ statusIcon = this.spinner.frames[this.spinner.frameIndex];
483
+ statusColor = colors.primary;
484
+ this.spinner.frameIndex = (this.spinner.frameIndex + 1) % this.spinner.frames.length;
485
+ break;
486
+ case 'skipped':
487
+ statusIcon = '○';
488
+ statusColor = ansi.dim;
489
+ break;
490
+ default:
491
+ statusIcon = '○';
492
+ statusColor = ansi.dim;
493
+ }
494
+
495
+ const duration = phase.duration ? `${ansi.dim}${phase.duration}ms${ansi.reset}` : '';
496
+ const message = phase.message ? `${ansi.dim}${phase.message}${ansi.reset}` : '';
497
+
498
+ this.stream.write(` ${statusColor}${statusIcon}${ansi.reset} ${phase.name.padEnd(25)} ${duration.padEnd(15)} ${message}\n`);
499
+ }
500
+
501
+ // Total line
502
+ this.stream.write(ansi.clearLine);
503
+ this.stream.write(` ${ansi.dim}${'─'.repeat(60)}${ansi.reset}\n`);
504
+
505
+ // Keep spinner running for active phase
506
+ if (this.phases.some(p => p.status === 'running')) {
507
+ setTimeout(() => this._render(), 80);
508
+ }
509
+ }
510
+ }
274
511
 
275
- console.log(lines.join('\n'));
512
+ // ═══════════════════════════════════════════════════════════════════════════════
513
+ // SCORE DISPLAY - Animated Score Card
514
+ // ═══════════════════════════════════════════════════════════════════════════════
515
+
516
+ function getScoreColor(score) {
517
+ if (score >= 90) return colors.success;
518
+ if (score >= 70) return colors.warning;
519
+ if (score >= 50) return ansi.rgb(251, 146, 60);
520
+ return colors.error;
276
521
  }
277
522
 
278
- /**
279
- * Renders the Standard Footer
280
- */
281
- function renderScreenFooter() {
282
- console.log(ansi.gray + BOX.bl + BOX.h.repeat(WIDTH - 2) + BOX.br + ansi.reset);
523
+ function getGrade(score) {
524
+ if (score >= 90) return 'A';
525
+ if (score >= 80) return 'B';
526
+ if (score >= 70) return 'C';
527
+ if (score >= 60) return 'D';
528
+ return 'F';
283
529
  }
284
530
 
285
- /**
286
- * Renders a Standard Verdict/Telemetry Table
287
- */
288
- function renderVerdictTable(stats, score, findings) {
531
+ function renderScoreCard(score, options = {}) {
532
+ const {
533
+ verdict = score >= 80 ? 'SHIP' : score >= 60 ? 'WARN' : 'BLOCK',
534
+ findings = { critical: 0, high: 0, medium: 0, low: 0 },
535
+ duration = null,
536
+ cached = false,
537
+ } = options;
538
+
539
+ const scoreColor = getScoreColor(score);
540
+ const grade = getGrade(score);
541
+ const gradeColor = scoreColor;
542
+
543
+ const verdictConfig = {
544
+ SHIP: { bg: colors.bg.success, text: ' ✓ SHIP ', desc: 'Ready to ship' },
545
+ WARN: { bg: colors.bg.warning, text: ' ⚠ WARN ', desc: 'Review before shipping' },
546
+ BLOCK: { bg: colors.bg.error, text: ' ✗ BLOCK ', desc: 'Fix issues before shipping' },
547
+ PASS: { bg: colors.bg.success, text: ' ✓ PASS ', desc: 'All checks passed' },
548
+ FAIL: { bg: colors.bg.error, text: ' ✗ FAIL ', desc: 'Checks failed' },
549
+ };
550
+ const v = verdictConfig[verdict] || verdictConfig.WARN;
551
+
552
+ // Build progress bar
553
+ const barWidth = 40;
554
+ const filled = Math.round((score / 100) * barWidth);
555
+ const bar = `${scoreColor}${'█'.repeat(filled)}${ansi.dim}${'░'.repeat(barWidth - filled)}${ansi.reset}`;
556
+
289
557
  const lines = [];
558
+ lines.push('');
559
+ lines.push(` ${ansi.dim}${box.topLeft}${'─'.repeat(66)}${box.topRight}${ansi.reset}`);
560
+ lines.push(` ${ansi.dim}${box.vertical}${ansi.reset}${' '.repeat(66)}${ansi.dim}${box.vertical}${ansi.reset}`);
561
+
562
+ // Score + Grade row
563
+ const scoreStr = String(score).padStart(3);
564
+ lines.push(` ${ansi.dim}${box.vertical}${ansi.reset} ${ansi.dim}SCORE${ansi.reset} ${scoreColor}${ansi.bold}${scoreStr}${ansi.reset}${ansi.dim}/100${ansi.reset} ${ansi.dim}GRADE${ansi.reset} ${gradeColor}${ansi.bold}${grade}${ansi.reset} ${cached ? `${ansi.dim}(cached)${ansi.reset}` : ''} ${ansi.dim}${box.vertical}${ansi.reset}`);
565
+ lines.push(` ${ansi.dim}${box.vertical}${ansi.reset}${' '.repeat(66)}${ansi.dim}${box.vertical}${ansi.reset}`);
566
+
567
+ // Progress bar
568
+ lines.push(` ${ansi.dim}${box.vertical}${ansi.reset} ${bar} ${ansi.dim}${box.vertical}${ansi.reset}`);
569
+ lines.push(` ${ansi.dim}${box.vertical}${ansi.reset}${' '.repeat(66)}${ansi.dim}${box.vertical}${ansi.reset}`);
570
+
571
+ // Verdict badge
572
+ const verdictPad = ' '.repeat(Math.max(0, 23 - v.text.length));
573
+ lines.push(` ${ansi.dim}${box.vertical}${ansi.reset}${' '.repeat(20)}${v.bg}${ansi.bold}${v.text}${ansi.reset}${verdictPad}${ansi.dim}${v.desc}${ansi.reset} ${ansi.dim}${box.vertical}${ansi.reset}`);
574
+ lines.push(` ${ansi.dim}${box.vertical}${ansi.reset}${' '.repeat(66)}${ansi.dim}${box.vertical}${ansi.reset}`);
575
+
576
+ // Findings summary
577
+ const criticalStr = `${colors.critical}${findings.critical || 0}${ansi.reset} critical`;
578
+ const highStr = `${colors.high}${findings.high || 0}${ansi.reset} high`;
579
+ const mediumStr = `${colors.medium}${findings.medium || 0}${ansi.reset} medium`;
580
+ const lowStr = `${colors.low}${findings.low || 0}${ansi.reset} low`;
581
+ lines.push(` ${ansi.dim}${box.vertical}${ansi.reset} ${criticalStr} ${ansi.dim}│${ansi.reset} ${highStr} ${ansi.dim}│${ansi.reset} ${mediumStr} ${ansi.dim}│${ansi.reset} ${lowStr} ${ansi.dim}${box.vertical}${ansi.reset}`);
582
+ lines.push(` ${ansi.dim}${box.vertical}${ansi.reset}${' '.repeat(66)}${ansi.dim}${box.vertical}${ansi.reset}`);
583
+
584
+ // Duration if provided
585
+ if (duration) {
586
+ const durationStr = typeof duration === 'number' ? `${duration}ms` : duration;
587
+ lines.push(` ${ansi.dim}${box.vertical}${ansi.reset} ${ansi.dim}Completed in ${durationStr}${ansi.reset}${' '.repeat(Math.max(0, 46 - durationStr.length))}${ansi.dim}${box.vertical}${ansi.reset}`);
588
+ lines.push(` ${ansi.dim}${box.vertical}${ansi.reset}${' '.repeat(66)}${ansi.dim}${box.vertical}${ansi.reset}`);
589
+ }
590
+
591
+ lines.push(` ${ansi.dim}${box.bottomLeft}${'─'.repeat(66)}${box.bottomRight}${ansi.reset}`);
592
+ lines.push('');
593
+
594
+ return lines.join('\n');
595
+ }
596
+
597
+ // ═══════════════════════════════════════════════════════════════════════════════
598
+ // FINDINGS LIST - Premium Findings Display
599
+ // ═══════════════════════════════════════════════════════════════════════════════
290
600
 
291
- // Telemetry Row
292
- const heapMB = Math.round(stats.heap / 1024 / 1024);
293
- const statsStr = `📡 TELEMETRY │ ⏱ ${stats.duration}ms │ 📂 ${stats.files || '?'} Files │ 📦 ${heapMB}MB`;
294
- lines.push(ansi.gray + BOX.v + ansi.reset + padCenter(statsStr, WIDTH - 2) + ansi.gray + BOX.v + ansi.reset);
295
- lines.push(ansi.gray + BOX.trT + BOX.h.repeat(WIDTH - 2) + BOX.tlT + ansi.reset);
296
-
297
- // Score Row
298
- lines.push(ansi.gray + BOX.v + ansi.reset + ' '.repeat(WIDTH - 2) + ansi.gray + BOX.v + ansi.reset);
299
- const bar = renderProgressBar(score, 20);
300
- lines.push(ansi.gray + BOX.v + ansi.reset + padCenter(`HEALTH SCORE [${bar}] ${score} / 100`, WIDTH + 18) + ansi.gray + BOX.v + ansi.reset);
301
- lines.push(ansi.gray + BOX.v + ansi.reset + ' '.repeat(WIDTH - 2) + ansi.gray + BOX.v + ansi.reset);
302
-
303
- if (findings.length > 0) {
304
- // Table Header
305
- const C1=10, C2=13, C3=41;
306
- const tTop = ` ${BOX.ltl}${BOX.lh.repeat(C1)}${BOX.lt}${BOX.lh.repeat(C2)}${BOX.lt}${BOX.lh.repeat(C3)}${BOX.ltr} `;
307
- const header = ` ${BOX.lv}${padRight(' SEVERITY', C1)}${BOX.lv}${padRight(' TYPE', C2)}${BOX.lv}${padRight(' FINDING', C3)}${BOX.lv} `;
308
- const tBot = ` ${BOX.lbl}${BOX.lh.repeat(C1)}${BOX.lb}${BOX.lh.repeat(C2)}${BOX.lb}${BOX.lh.repeat(C3)}${BOX.lbr} `;
309
-
310
- lines.push(ansi.gray + BOX.v + ansi.reset + ansi.gray + tTop + ansi.reset + ansi.gray + BOX.v + ansi.reset);
311
- lines.push(ansi.gray + BOX.v + ansi.reset + ansi.bold + header + ansi.reset + ansi.gray + BOX.v + ansi.reset);
601
+ function renderFindingsList(findings, options = {}) {
602
+ const { maxItems = 10, showCode = false, groupBySeverity = true } = options;
603
+
604
+ if (!findings || findings.length === 0) {
605
+ return `\n ${colors.success}${icons.success}${ansi.reset} ${ansi.bold}No issues found${ansi.reset}\n`;
606
+ }
607
+
608
+ const lines = [];
609
+
610
+ if (groupBySeverity) {
611
+ const groups = {
612
+ critical: findings.filter(f => f.severity === 'critical' || f.severity === 'BLOCK'),
613
+ high: findings.filter(f => f.severity === 'high'),
614
+ medium: findings.filter(f => f.severity === 'medium' || f.severity === 'WARN' || f.severity === 'warning'),
615
+ low: findings.filter(f => f.severity === 'low' || f.severity === 'INFO' || f.severity === 'info'),
616
+ };
312
617
 
313
- // Rows
314
- findings.slice(0, 5).forEach(f => {
315
- let sev = style.subtle(' INFO ');
316
- if (f.severity === 'critical' || f.severity === 'BLOCK') sev = style.error('🛑 CRIT ');
317
- else if (f.severity === 'high' || f.severity === 'WARN') sev = style.warning('🟡 WARN ');
618
+ for (const [severity, items] of Object.entries(groups)) {
619
+ if (items.length === 0) continue;
318
620
 
319
- const row = ` ${ansi.gray}${BOX.lv}${ansi.reset}${sev}${ansi.gray}${BOX.lv}${ansi.reset}${padRight(' '+(f.category||'General'), C2)}${ansi.gray}${BOX.lv}${ansi.reset}${padRight(' '+(f.message||f.title), C3)}${ansi.gray}${BOX.lv}${ansi.reset} `;
320
- lines.push(ansi.gray + BOX.v + ansi.reset + row + ansi.gray + BOX.v + ansi.reset);
321
- });
322
-
323
- lines.push(ansi.gray + BOX.v + ansi.reset + ansi.gray + tBot + ansi.reset + ansi.gray + BOX.v + ansi.reset);
621
+ const color = colors[severity] || ansi.dim;
622
+ const icon = icons[severity] || icons.bullet;
623
+
624
+ lines.push('');
625
+ lines.push(` ${color}${ansi.bold}${severity.toUpperCase()} (${items.length})${ansi.reset}`);
626
+ lines.push(` ${ansi.dim}${'─'.repeat(40)}${ansi.reset}`);
627
+
628
+ for (const finding of items.slice(0, Math.ceil(maxItems / 4))) {
629
+ lines.push(...renderFinding(finding, { showCode, color }));
630
+ }
631
+
632
+ if (items.length > Math.ceil(maxItems / 4)) {
633
+ lines.push(` ${ansi.dim} ... and ${items.length - Math.ceil(maxItems / 4)} more ${severity} findings${ansi.reset}`);
634
+ }
635
+ }
324
636
  } else {
325
- lines.push(ansi.gray + BOX.v + ansi.reset + padCenter(style.success('✅ NO ISSUES FOUND'), WIDTH - 2) + ansi.gray + BOX.v + ansi.reset);
637
+ for (const finding of findings.slice(0, maxItems)) {
638
+ lines.push(...renderFinding(finding, { showCode }));
639
+ }
640
+
641
+ if (findings.length > maxItems) {
642
+ lines.push('');
643
+ lines.push(` ${ansi.dim}... and ${findings.length - maxItems} more findings${ansi.reset}`);
644
+ }
326
645
  }
646
+
647
+ return lines.join('\n');
648
+ }
327
649
 
328
- lines.push(ansi.gray + BOX.v + ansi.reset + ' '.repeat(WIDTH - 2) + ansi.gray + BOX.v + ansi.reset);
329
- console.log(lines.join('\n'));
650
+ function renderFinding(finding, options = {}) {
651
+ const { showCode = false, color = ansi.dim } = options;
652
+ const lines = [];
653
+
654
+ const severityColor = {
655
+ critical: colors.critical,
656
+ BLOCK: colors.critical,
657
+ high: colors.high,
658
+ medium: colors.medium,
659
+ WARN: colors.medium,
660
+ warning: colors.medium,
661
+ low: colors.low,
662
+ INFO: colors.low,
663
+ info: colors.low,
664
+ }[finding.severity] || ansi.dim;
665
+
666
+ const title = finding.title || finding.message || 'Unknown issue';
667
+ lines.push(` ${severityColor}${icons.pointer}${ansi.reset} ${ansi.bold}${truncate(title, 60)}${ansi.reset}`);
668
+
669
+ if (finding.file) {
670
+ const fileStr = finding.file + (finding.line ? `:${finding.line}` : '');
671
+ lines.push(` ${ansi.dim}${truncate(fileStr, 55)}${ansi.reset}`);
672
+ }
673
+
674
+ if (finding.fix || finding.fixSuggestion) {
675
+ lines.push(` ${colors.success}${icons.arrowRight}${ansi.reset} ${ansi.dim}${truncate(finding.fix || finding.fixSuggestion, 50)}${ansi.reset}`);
676
+ }
677
+
678
+ if (showCode && finding.codeSnippet) {
679
+ lines.push(` ${ansi.dim}┌──────────────────────────────────────${ansi.reset}`);
680
+ for (const line of finding.codeSnippet.split('\n').slice(0, 3)) {
681
+ lines.push(` ${ansi.dim}│${ansi.reset} ${truncate(line, 50)}`);
682
+ }
683
+ lines.push(` ${ansi.dim}└──────────────────────────────────────${ansi.reset}`);
684
+ }
685
+
686
+ lines.push('');
687
+ return lines;
330
688
  }
331
689
 
332
690
  // ═══════════════════════════════════════════════════════════════════════════════
333
- // COLORS OBJECT (for compatibility)
691
+ // TABLE RENDERING
334
692
  // ═══════════════════════════════════════════════════════════════════════════════
335
693
 
336
- const colors = {
337
- success: ansi.green,
338
- error: ansi.red,
339
- warning: ansi.yellow,
340
- info: ansi.cyan,
341
- accent: ansi.cyan,
342
- muted: ansi.gray,
343
- highlight: ansi.white,
344
- // Extended palette for specific use cases
345
- shipGreen: ansi.rgb(0, 255, 150),
346
- warnAmber: ansi.rgb(255, 200, 0),
347
- blockRed: ansi.rgb(255, 80, 80),
348
- watching: ansi.rgb(100, 200, 255),
349
- gradient1: ansi.rgb(150, 100, 255),
350
- gradient2: ansi.rgb(130, 80, 255),
351
- gradient3: ansi.rgb(110, 60, 255),
352
- };
694
+ function renderTable(headers, rows, options = {}) {
695
+ const { padding = 2, headerColor = colors.primary } = options;
696
+
697
+ // Calculate column widths
698
+ const colWidths = headers.map((h, i) => {
699
+ const maxData = Math.max(...rows.map(r => stripAnsi(String(r[i] || '')).length));
700
+ return Math.max(stripAnsi(h).length, maxData) + padding;
701
+ });
702
+
703
+ const lines = [];
704
+ const totalWidth = colWidths.reduce((a, b) => a + b, 0) + colWidths.length + 1;
705
+
706
+ // Header
707
+ lines.push(` ${ansi.dim}${box.topLeft}${'─'.repeat(totalWidth - 2)}${box.topRight}${ansi.reset}`);
708
+ lines.push(` ${ansi.dim}${box.vertical}${ansi.reset}${headers.map((h, i) => `${headerColor}${ansi.bold}${h.padEnd(colWidths[i])}${ansi.reset}`).join(`${ansi.dim}│${ansi.reset}`)}${ansi.dim}${box.vertical}${ansi.reset}`);
709
+ lines.push(` ${ansi.dim}${box.teeRight}${'─'.repeat(totalWidth - 2)}${box.teeLeft}${ansi.reset}`);
710
+
711
+ // Rows
712
+ for (const row of rows) {
713
+ const cells = row.map((cell, i) => {
714
+ const str = String(cell || '');
715
+ const visibleLen = stripAnsi(str).length;
716
+ return str + ' '.repeat(Math.max(0, colWidths[i] - visibleLen));
717
+ });
718
+ lines.push(` ${ansi.dim}${box.vertical}${ansi.reset}${cells.join(`${ansi.dim}│${ansi.reset}`)}${ansi.dim}${box.vertical}${ansi.reset}`);
719
+ }
720
+
721
+ lines.push(` ${ansi.dim}${box.bottomLeft}${'─'.repeat(totalWidth - 2)}${box.bottomRight}${ansi.reset}`);
722
+
723
+ return lines.join('\n');
724
+ }
353
725
 
354
726
  // ═══════════════════════════════════════════════════════════════════════════════
355
- // SHORTHAND 'c' ALIAS (for legacy runner compatibility)
727
+ // SECTION HEADERS
356
728
  // ═══════════════════════════════════════════════════════════════════════════════
357
729
 
358
- /**
359
- * Legacy shorthand - prefer using `ansi` directly in new code
360
- */
361
- const c = {
362
- ...ansi,
363
- // Clear shorthand
364
- clear: `${ESC}[2J${ESC}[H`,
365
- };
730
+ function renderSection(title, icon = '◆') {
731
+ return `\n ${colors.accent}${icon}${ansi.reset} ${ansi.bold}${title}${ansi.reset}\n ${ansi.dim}${'─'.repeat(60)}${ansi.reset}`;
732
+ }
733
+
734
+ function renderDivider(char = '─', width = 60) {
735
+ return ` ${ansi.dim}${char.repeat(width)}${ansi.reset}`;
736
+ }
366
737
 
367
738
  // ═══════════════════════════════════════════════════════════════════════════════
368
739
  // UTILITY FUNCTIONS
369
740
  // ═══════════════════════════════════════════════════════════════════════════════
370
741
 
371
- /**
372
- * Format duration in milliseconds to human-readable string
373
- */
374
- function formatDuration(ms) {
375
- if (ms < 1000) return `${ms}ms`;
376
- if (ms < 60000) return `${(ms / 1000).toFixed(1)}s`;
377
- const minutes = Math.floor(ms / 60000);
378
- const seconds = Math.floor((ms % 60000) / 1000);
379
- return `${minutes}m ${seconds}s`;
742
+ function truncate(str, len) {
743
+ if (!str) return '';
744
+ str = String(str);
745
+ const visible = stripAnsi(str);
746
+ if (visible.length <= len) return str;
747
+ return str.slice(0, len - 3) + '...';
380
748
  }
381
749
 
382
- /**
383
- * Render a section header
384
- */
385
- function renderSection(title, icon = '•') {
386
- return `\n ${ansi.cyan}${icon}${ansi.reset} ${ansi.bold}${title}${ansi.reset}\n ${ansi.gray}${BOX.lh.repeat(WIDTH - 4)}${ansi.reset}`;
750
+ function stripAnsi(str) {
751
+ return str.replace(/\x1b\[[0-9;]*m/g, '');
387
752
  }
388
753
 
389
- /**
390
- * Render banner (placeholder - actual banner is in runScan.js)
391
- */
392
- function renderBanner() {
393
- // Banner is handled in individual command files
394
- return '';
754
+ function formatNumber(num) {
755
+ return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
395
756
  }
396
757
 
397
- /**
398
- * PhaseProgress class (placeholder)
399
- */
400
- class PhaseProgress {
401
- constructor() {}
402
- start() { return this; }
403
- update() { return this; }
404
- stop() { return this; }
758
+ function formatDuration(ms) {
759
+ if (ms < 1000) return `${ms}ms`;
760
+ if (ms < 60000) return `${(ms / 1000).toFixed(1)}s`;
761
+ return `${Math.floor(ms / 60000)}m ${Math.round((ms % 60000) / 1000)}s`;
762
+ }
763
+
764
+ function formatBytes(bytes) {
765
+ if (bytes === 0) return '0 B';
766
+ const k = 1024;
767
+ const sizes = ['B', 'KB', 'MB', 'GB'];
768
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
769
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i];
405
770
  }
406
771
 
407
772
  // ═══════════════════════════════════════════════════════════════════════════════
408
- // TOP-LEVEL RGB FUNCTIONS (for migration ease)
773
+ // BANNER GENERATOR
409
774
  // ═══════════════════════════════════════════════════════════════════════════════
410
775
 
411
- const rgb = ansi.rgb;
412
- const bgRgb = ansi.bgRgb;
776
+ function renderBanner(name = 'VIBECHECK', subtitle = '') {
777
+ const gradient = [
778
+ ansi.rgb(0, 200, 255),
779
+ ansi.rgb(30, 180, 255),
780
+ ansi.rgb(60, 160, 255),
781
+ ansi.rgb(90, 140, 255),
782
+ ansi.rgb(120, 120, 255),
783
+ ansi.rgb(150, 100, 255),
784
+ ];
785
+
786
+ const asciiArt = {
787
+ V: ['██╗ ██╗', '██║ ██║', '██║ ██║', '╚██╗ ██╔╝', ' ╚████╔╝ ', ' ╚═══╝ '],
788
+ I: ['██╗', '██║', '██║', '██║', '██║', '╚═╝'],
789
+ B: ['██████╗ ', '██╔══██╗', '██████╔╝', '██╔══██╗', '██████╔╝', '╚═════╝ '],
790
+ E: ['███████╗', '██╔════╝', '█████╗ ', '██╔══╝ ', '███████╗', '╚══════╝'],
791
+ C: [' ██████╗', '██╔════╝', '██║ ', '██║ ', '╚██████╗', ' ╚═════╝'],
792
+ H: ['██╗ ██╗', '██║ ██║', '███████║', '██╔══██║', '██║ ██║', '╚═╝ ╚═╝'],
793
+ K: ['██╗ ██╗', '██║ ██╔╝', '█████╔╝ ', '██╔═██╗ ', '██║ ██╗', '╚═╝ ╚═╝'],
794
+ };
795
+
796
+ const lines = [];
797
+ lines.push('');
798
+
799
+ for (let row = 0; row < 6; row++) {
800
+ let line = ' ';
801
+ for (const char of name) {
802
+ if (asciiArt[char]) {
803
+ line += asciiArt[char][row];
804
+ }
805
+ }
806
+ lines.push(`${gradient[row]}${line}${ansi.reset}`);
807
+ }
808
+
809
+ if (subtitle) {
810
+ lines.push('');
811
+ lines.push(` ${ansi.dim}${box.topLeft}${'─'.repeat(subtitle.length + 4)}${box.topRight}${ansi.reset}`);
812
+ lines.push(` ${ansi.dim}${box.vertical}${ansi.reset} ${subtitle} ${ansi.dim}${box.vertical}${ansi.reset}`);
813
+ lines.push(` ${ansi.dim}${box.bottomLeft}${'─'.repeat(subtitle.length + 4)}${box.bottomRight}${ansi.reset}`);
814
+ }
815
+
816
+ lines.push('');
817
+ return lines.join('\n');
818
+ }
413
819
 
414
820
  // ═══════════════════════════════════════════════════════════════════════════════
415
821
  // EXPORTS
416
822
  // ═══════════════════════════════════════════════════════════════════════════════
417
823
 
418
824
  module.exports = {
419
- // Core constants
420
- WIDTH,
421
- ESC,
422
- SUPPORTS_TRUECOLOR,
423
-
424
- // ANSI codes (main export)
825
+ // ANSI codes
425
826
  ansi,
426
-
427
- // Legacy shorthand alias
428
- c,
429
-
430
- // Semantic colors
431
827
  colors,
432
- style,
433
-
434
- // Icons
828
+ box,
435
829
  icons,
436
830
 
437
- // Box drawing
438
- BOX,
439
-
440
- // RGB functions (top-level)
441
- rgb,
442
- bgRgb,
443
-
444
- // Text utilities
445
- padCenter,
446
- padRight,
447
- truncate,
448
-
449
831
  // Components
450
832
  Spinner,
833
+ ProgressBar,
451
834
  PhaseProgress,
452
835
 
453
- // Render functions
454
- renderProgressBar,
455
- renderScreenHeader,
456
- renderScreenFooter,
457
- renderVerdictTable,
836
+ // Renderers
837
+ renderScoreCard,
838
+ renderFindingsList,
839
+ renderFinding,
840
+ renderTable,
458
841
  renderSection,
842
+ renderDivider,
459
843
  renderBanner,
460
844
 
461
845
  // Utilities
846
+ truncate,
847
+ stripAnsi,
848
+ formatNumber,
462
849
  formatDuration,
463
- };
850
+ formatBytes,
851
+ getScoreColor,
852
+ getGrade,
853
+ };