@vibecheckai/cli 3.5.1 → 3.5.2

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 (272) hide show
  1. package/bin/registry.js +406 -154
  2. package/bin/runners/context/analyzer.js +52 -1
  3. package/bin/runners/context/generators/mcp.js +15 -13
  4. package/bin/runners/context/git-context.js +3 -1
  5. package/bin/runners/context/proof-context.js +248 -1
  6. package/bin/runners/context/team-conventions.js +33 -7
  7. package/bin/runners/lib/agent-firewall/ai/false-positive-analyzer.js +474 -0
  8. package/bin/runners/lib/agent-firewall/change-packet/builder.js +488 -0
  9. package/bin/runners/lib/agent-firewall/change-packet/schema.json +228 -0
  10. package/bin/runners/lib/agent-firewall/change-packet/store.js +200 -0
  11. package/bin/runners/lib/agent-firewall/claims/claim-types.js +21 -0
  12. package/bin/runners/lib/agent-firewall/claims/extractor.js +303 -0
  13. package/bin/runners/lib/agent-firewall/claims/patterns.js +24 -0
  14. package/bin/runners/lib/agent-firewall/critic/index.js +151 -0
  15. package/bin/runners/lib/agent-firewall/critic/judge.js +432 -0
  16. package/bin/runners/lib/agent-firewall/critic/prompts.js +305 -0
  17. package/bin/runners/lib/agent-firewall/evidence/auth-evidence.js +88 -0
  18. package/bin/runners/lib/agent-firewall/evidence/contract-evidence.js +75 -0
  19. package/bin/runners/lib/agent-firewall/evidence/env-evidence.js +127 -0
  20. package/bin/runners/lib/agent-firewall/evidence/resolver.js +102 -0
  21. package/bin/runners/lib/agent-firewall/evidence/route-evidence.js +213 -0
  22. package/bin/runners/lib/agent-firewall/evidence/side-effect-evidence.js +145 -0
  23. package/bin/runners/lib/agent-firewall/fs-hook/daemon.js +19 -0
  24. package/bin/runners/lib/agent-firewall/fs-hook/installer.js +87 -0
  25. package/bin/runners/lib/agent-firewall/fs-hook/watcher.js +184 -0
  26. package/bin/runners/lib/agent-firewall/git-hook/pre-commit.js +163 -0
  27. package/bin/runners/lib/agent-firewall/ide-extension/cursor.js +107 -0
  28. package/bin/runners/lib/agent-firewall/ide-extension/vscode.js +68 -0
  29. package/bin/runners/lib/agent-firewall/ide-extension/windsurf.js +66 -0
  30. package/bin/runners/lib/agent-firewall/interceptor/base.js +304 -0
  31. package/bin/runners/lib/agent-firewall/interceptor/cursor.js +35 -0
  32. package/bin/runners/lib/agent-firewall/interceptor/vscode.js +35 -0
  33. package/bin/runners/lib/agent-firewall/interceptor/windsurf.js +34 -0
  34. package/bin/runners/lib/agent-firewall/lawbook/distributor.js +465 -0
  35. package/bin/runners/lib/agent-firewall/lawbook/evaluator.js +604 -0
  36. package/bin/runners/lib/agent-firewall/lawbook/index.js +304 -0
  37. package/bin/runners/lib/agent-firewall/lawbook/registry.js +514 -0
  38. package/bin/runners/lib/agent-firewall/lawbook/schema.js +420 -0
  39. package/bin/runners/lib/agent-firewall/logger.js +141 -0
  40. package/bin/runners/lib/agent-firewall/policy/default-policy.json +90 -0
  41. package/bin/runners/lib/agent-firewall/policy/engine.js +103 -0
  42. package/bin/runners/lib/agent-firewall/policy/loader.js +451 -0
  43. package/bin/runners/lib/agent-firewall/policy/rules/auth-drift.js +50 -0
  44. package/bin/runners/lib/agent-firewall/policy/rules/contract-drift.js +50 -0
  45. package/bin/runners/lib/agent-firewall/policy/rules/fake-success.js +86 -0
  46. package/bin/runners/lib/agent-firewall/policy/rules/ghost-env.js +162 -0
  47. package/bin/runners/lib/agent-firewall/policy/rules/ghost-route.js +189 -0
  48. package/bin/runners/lib/agent-firewall/policy/rules/scope.js +93 -0
  49. package/bin/runners/lib/agent-firewall/policy/rules/unsafe-side-effect.js +57 -0
  50. package/bin/runners/lib/agent-firewall/policy/schema.json +183 -0
  51. package/bin/runners/lib/agent-firewall/policy/verdict.js +54 -0
  52. package/bin/runners/lib/agent-firewall/proposal/extractor.js +394 -0
  53. package/bin/runners/lib/agent-firewall/proposal/index.js +212 -0
  54. package/bin/runners/lib/agent-firewall/proposal/schema.js +251 -0
  55. package/bin/runners/lib/agent-firewall/proposal/validator.js +386 -0
  56. package/bin/runners/lib/agent-firewall/reality/index.js +332 -0
  57. package/bin/runners/lib/agent-firewall/reality/state.js +625 -0
  58. package/bin/runners/lib/agent-firewall/reality/watcher.js +322 -0
  59. package/bin/runners/lib/agent-firewall/risk/index.js +173 -0
  60. package/bin/runners/lib/agent-firewall/risk/scorer.js +328 -0
  61. package/bin/runners/lib/agent-firewall/risk/thresholds.js +321 -0
  62. package/bin/runners/lib/agent-firewall/risk/vectors.js +421 -0
  63. package/bin/runners/lib/agent-firewall/simulator/diff-simulator.js +472 -0
  64. package/bin/runners/lib/agent-firewall/simulator/import-resolver.js +346 -0
  65. package/bin/runners/lib/agent-firewall/simulator/index.js +181 -0
  66. package/bin/runners/lib/agent-firewall/simulator/route-validator.js +380 -0
  67. package/bin/runners/lib/agent-firewall/time-machine/incident-correlator.js +661 -0
  68. package/bin/runners/lib/agent-firewall/time-machine/index.js +267 -0
  69. package/bin/runners/lib/agent-firewall/time-machine/replay-engine.js +436 -0
  70. package/bin/runners/lib/agent-firewall/time-machine/state-reconstructor.js +490 -0
  71. package/bin/runners/lib/agent-firewall/time-machine/timeline-builder.js +530 -0
  72. package/bin/runners/lib/agent-firewall/truthpack/index.js +67 -0
  73. package/bin/runners/lib/agent-firewall/truthpack/loader.js +137 -0
  74. package/bin/runners/lib/agent-firewall/unblock/planner.js +337 -0
  75. package/bin/runners/lib/agent-firewall/utils/ignore-checker.js +118 -0
  76. package/bin/runners/lib/analysis-core.js +220 -182
  77. package/bin/runners/lib/analyzers.js +2145 -224
  78. package/bin/runners/lib/api-client.js +269 -0
  79. package/bin/runners/lib/authority-badge.js +425 -0
  80. package/bin/runners/lib/cli-output.js +242 -210
  81. package/bin/runners/lib/default-config.js +127 -0
  82. package/bin/runners/lib/detectors-v2.js +547 -785
  83. package/bin/runners/lib/doctor/modules/security.js +3 -1
  84. package/bin/runners/lib/engine/ast-cache.js +210 -0
  85. package/bin/runners/lib/engine/auth-extractor.js +211 -0
  86. package/bin/runners/lib/engine/billing-extractor.js +112 -0
  87. package/bin/runners/lib/engine/enforcement-extractor.js +100 -0
  88. package/bin/runners/lib/engine/env-extractor.js +207 -0
  89. package/bin/runners/lib/engine/express-extractor.js +208 -0
  90. package/bin/runners/lib/engine/extractors.js +849 -0
  91. package/bin/runners/lib/engine/index.js +207 -0
  92. package/bin/runners/lib/engine/repo-index.js +514 -0
  93. package/bin/runners/lib/engine/types.js +124 -0
  94. package/bin/runners/lib/engines/accessibility-engine.js +190 -0
  95. package/bin/runners/lib/engines/api-consistency-engine.js +162 -0
  96. package/bin/runners/lib/engines/ast-cache.js +99 -0
  97. package/bin/runners/lib/engines/code-quality-engine.js +255 -0
  98. package/bin/runners/lib/engines/console-logs-engine.js +115 -0
  99. package/bin/runners/lib/engines/cross-file-analysis-engine.js +268 -0
  100. package/bin/runners/lib/engines/dead-code-engine.js +198 -0
  101. package/bin/runners/lib/engines/deprecated-api-engine.js +226 -0
  102. package/bin/runners/lib/engines/empty-catch-engine.js +150 -0
  103. package/bin/runners/lib/engines/file-filter.js +131 -0
  104. package/bin/runners/lib/engines/hardcoded-secrets-engine.js +251 -0
  105. package/bin/runners/lib/engines/mock-data-engine.js +272 -0
  106. package/bin/runners/lib/engines/parallel-processor.js +71 -0
  107. package/bin/runners/lib/engines/performance-issues-engine.js +265 -0
  108. package/bin/runners/lib/engines/security-vulnerabilities-engine.js +243 -0
  109. package/bin/runners/lib/engines/todo-fixme-engine.js +115 -0
  110. package/bin/runners/lib/engines/type-aware-engine.js +152 -0
  111. package/bin/runners/lib/engines/unsafe-regex-engine.js +225 -0
  112. package/bin/runners/lib/engines/vibecheck-engines/README.md +53 -0
  113. package/bin/runners/lib/engines/vibecheck-engines/index.js +15 -0
  114. package/bin/runners/lib/engines/vibecheck-engines/lib/ast-cache.js +164 -0
  115. package/bin/runners/lib/engines/vibecheck-engines/lib/code-quality-engine.js +291 -0
  116. package/bin/runners/lib/engines/vibecheck-engines/lib/console-logs-engine.js +83 -0
  117. package/bin/runners/lib/engines/vibecheck-engines/lib/dead-code-engine.js +198 -0
  118. package/bin/runners/lib/engines/vibecheck-engines/lib/deprecated-api-engine.js +275 -0
  119. package/bin/runners/lib/engines/vibecheck-engines/lib/empty-catch-engine.js +167 -0
  120. package/bin/runners/lib/engines/vibecheck-engines/lib/file-filter.js +217 -0
  121. package/bin/runners/lib/engines/vibecheck-engines/lib/hardcoded-secrets-engine.js +139 -0
  122. package/bin/runners/lib/engines/vibecheck-engines/lib/mock-data-engine.js +140 -0
  123. package/bin/runners/lib/engines/vibecheck-engines/lib/parallel-processor.js +164 -0
  124. package/bin/runners/lib/engines/vibecheck-engines/lib/performance-issues-engine.js +234 -0
  125. package/bin/runners/lib/engines/vibecheck-engines/lib/type-aware-engine.js +217 -0
  126. package/bin/runners/lib/engines/vibecheck-engines/lib/unsafe-regex-engine.js +78 -0
  127. package/bin/runners/lib/engines/vibecheck-engines/package.json +13 -0
  128. package/bin/runners/lib/entitlements-v2.js +152 -446
  129. package/bin/runners/lib/error-handler.js +60 -12
  130. package/bin/runners/lib/error-messages.js +289 -0
  131. package/bin/runners/lib/evidence-pack.js +7 -1
  132. package/bin/runners/lib/exit-codes.js +275 -0
  133. package/bin/runners/lib/finding-id.js +69 -0
  134. package/bin/runners/lib/finding-sorter.js +89 -0
  135. package/bin/runners/lib/fingerprint.js +377 -0
  136. package/bin/runners/lib/global-flags.js +37 -0
  137. package/bin/runners/lib/help-formatter.js +413 -0
  138. package/bin/runners/lib/logger.js +38 -0
  139. package/bin/runners/lib/next-action.js +560 -0
  140. package/bin/runners/lib/prerequisites.js +149 -0
  141. package/bin/runners/lib/route-detection.js +137 -68
  142. package/bin/runners/lib/route-truth.js +1167 -322
  143. package/bin/runners/lib/scan-output.js +504 -463
  144. package/bin/runners/lib/scan-runner.js +135 -0
  145. package/bin/runners/lib/schemas/ajv-validator.js +464 -0
  146. package/bin/runners/lib/schemas/error-envelope.schema.json +105 -0
  147. package/bin/runners/lib/schemas/finding-v3.schema.json +151 -0
  148. package/bin/runners/lib/schemas/report-artifact.schema.json +120 -0
  149. package/bin/runners/lib/schemas/run-request.schema.json +108 -0
  150. package/bin/runners/lib/schemas/validator.js +27 -0
  151. package/bin/runners/lib/schemas/verdict.schema.json +140 -0
  152. package/bin/runners/lib/ship-output-enterprise.js +239 -0
  153. package/bin/runners/lib/ship-output.js +328 -31
  154. package/bin/runners/lib/terminal-ui.js +234 -731
  155. package/bin/runners/lib/truth.js +1332 -308
  156. package/bin/runners/lib/unified-cli-output.js +604 -0
  157. package/bin/runners/lib/unified-output.js +163 -155
  158. package/bin/runners/lib/upsell.js +104 -204
  159. package/bin/runners/runAgent.d.ts +5 -0
  160. package/bin/runners/runAgent.js +161 -0
  161. package/bin/runners/runAllowlist.js +166 -101
  162. package/bin/runners/runApprove.js +1200 -0
  163. package/bin/runners/runAuth.js +373 -95
  164. package/bin/runners/runCheckpoint.js +59 -21
  165. package/bin/runners/runClassify.js +926 -0
  166. package/bin/runners/runContext.d.ts +4 -0
  167. package/bin/runners/runContext.js +136 -24
  168. package/bin/runners/runDoctor.js +115 -67
  169. package/bin/runners/runEvidencePack.js +239 -96
  170. package/bin/runners/runFirewall.d.ts +5 -0
  171. package/bin/runners/runFirewall.js +134 -0
  172. package/bin/runners/runFirewallHook.d.ts +5 -0
  173. package/bin/runners/runFirewallHook.js +56 -0
  174. package/bin/runners/runFix.js +6 -5
  175. package/bin/runners/runGuard.js +212 -118
  176. package/bin/runners/runInit.js +66 -21
  177. package/bin/runners/runLabs.js +204 -121
  178. package/bin/runners/runMcp.js +131 -60
  179. package/bin/runners/runPolish.d.ts +4 -0
  180. package/bin/runners/runPolish.js +43 -20
  181. package/bin/runners/runProof.zip +0 -0
  182. package/bin/runners/runProve.js +15 -5
  183. package/bin/runners/runQuickstart.js +531 -0
  184. package/bin/runners/runReality.js +14 -0
  185. package/bin/runners/runReport.js +36 -4
  186. package/bin/runners/runScan.js +689 -91
  187. package/bin/runners/runShip.js +96 -40
  188. package/bin/runners/runTruth.d.ts +5 -0
  189. package/bin/runners/runTruth.js +101 -0
  190. package/bin/runners/runValidate.js +21 -4
  191. package/bin/runners/runWatch.js +118 -54
  192. package/bin/scan.js +6 -1
  193. package/bin/vibecheck.js +297 -52
  194. package/mcp-server/HARDENING_SUMMARY.md +299 -0
  195. package/mcp-server/agent-firewall-interceptor.js +500 -0
  196. package/mcp-server/authority-tools.js +569 -0
  197. package/mcp-server/conductor/conflict-resolver.js +588 -0
  198. package/mcp-server/conductor/execution-planner.js +544 -0
  199. package/mcp-server/conductor/index.js +377 -0
  200. package/mcp-server/conductor/lock-manager.js +615 -0
  201. package/mcp-server/conductor/request-queue.js +550 -0
  202. package/mcp-server/conductor/session-manager.js +500 -0
  203. package/mcp-server/conductor/tools.js +510 -0
  204. package/mcp-server/deprecation-middleware.js +282 -0
  205. package/mcp-server/handlers/index.ts +15 -0
  206. package/mcp-server/handlers/tool-handler.ts +474 -591
  207. package/mcp-server/index.js +1748 -1099
  208. package/mcp-server/lib/api-client.cjs +13 -0
  209. package/mcp-server/lib/cache-wrapper.cjs +383 -0
  210. package/mcp-server/lib/error-envelope.js +138 -0
  211. package/mcp-server/lib/executor.ts +428 -721
  212. package/mcp-server/lib/index.ts +19 -0
  213. package/mcp-server/lib/logger.cjs +30 -0
  214. package/mcp-server/lib/rate-limiter.js +166 -0
  215. package/mcp-server/lib/sandbox.test.ts +519 -0
  216. package/mcp-server/lib/sandbox.ts +342 -284
  217. package/mcp-server/lib/types.ts +267 -0
  218. package/mcp-server/logger.js +173 -0
  219. package/mcp-server/package.json +11 -27
  220. package/mcp-server/premium-tools.js +2 -2
  221. package/mcp-server/registry/tool-registry.js +794 -0
  222. package/mcp-server/registry/tools.json +507 -378
  223. package/mcp-server/registry.test.ts +334 -0
  224. package/mcp-server/tests/tier-gating.test.js +297 -0
  225. package/mcp-server/tier-auth.js +492 -347
  226. package/mcp-server/tools-v3.js +950 -0
  227. package/mcp-server/truth-context.js +131 -90
  228. package/mcp-server/truth-firewall-tools.js +1612 -1001
  229. package/mcp-server/tsconfig.json +8 -5
  230. package/mcp-server/vibecheck-2.0-tools.js +14 -1
  231. package/mcp-server/vibecheck-mcp-server-3.2.0.tgz +0 -0
  232. package/mcp-server/vibecheck-tools.js +2 -2
  233. package/package.json +4 -3
  234. package/bin/runners/runInstall.js +0 -281
  235. package/mcp-server/ARCHITECTURE.md +0 -339
  236. package/mcp-server/__tests__/cache.test.ts +0 -313
  237. package/mcp-server/__tests__/executor.test.ts +0 -239
  238. package/mcp-server/__tests__/fixtures/exclusion-test/.cache/webpack/cache.pack +0 -1
  239. package/mcp-server/__tests__/fixtures/exclusion-test/.next/server/chunk.js +0 -3
  240. package/mcp-server/__tests__/fixtures/exclusion-test/.turbo/cache.json +0 -3
  241. package/mcp-server/__tests__/fixtures/exclusion-test/.venv/lib/env.py +0 -3
  242. package/mcp-server/__tests__/fixtures/exclusion-test/dist/bundle.js +0 -3
  243. package/mcp-server/__tests__/fixtures/exclusion-test/package.json +0 -5
  244. package/mcp-server/__tests__/fixtures/exclusion-test/src/app.ts +0 -5
  245. package/mcp-server/__tests__/fixtures/exclusion-test/venv/lib/config.py +0 -4
  246. package/mcp-server/__tests__/ids.test.ts +0 -345
  247. package/mcp-server/__tests__/integration/tools.test.ts +0 -410
  248. package/mcp-server/__tests__/registry.test.ts +0 -365
  249. package/mcp-server/__tests__/sandbox.test.ts +0 -323
  250. package/mcp-server/__tests__/schemas.test.ts +0 -372
  251. package/mcp-server/benchmarks/run-benchmarks.ts +0 -304
  252. package/mcp-server/examples/doctor.request.json +0 -14
  253. package/mcp-server/examples/doctor.response.json +0 -53
  254. package/mcp-server/examples/error.response.json +0 -15
  255. package/mcp-server/examples/scan.request.json +0 -14
  256. package/mcp-server/examples/scan.response.json +0 -108
  257. package/mcp-server/index-v3.ts +0 -293
  258. package/mcp-server/index.old.js +0 -4137
  259. package/mcp-server/lib/cache.ts +0 -341
  260. package/mcp-server/lib/errors.ts +0 -346
  261. package/mcp-server/lib/ids.ts +0 -238
  262. package/mcp-server/lib/logger.ts +0 -368
  263. package/mcp-server/lib/metrics.ts +0 -365
  264. package/mcp-server/lib/validator.ts +0 -229
  265. package/mcp-server/package-lock.json +0 -165
  266. package/mcp-server/schemas/error-envelope.schema.json +0 -125
  267. package/mcp-server/schemas/finding.schema.json +0 -167
  268. package/mcp-server/schemas/report-artifact.schema.json +0 -88
  269. package/mcp-server/schemas/run-request.schema.json +0 -75
  270. package/mcp-server/schemas/verdict.schema.json +0 -168
  271. package/mcp-server/tier-auth.d.ts +0 -71
  272. package/mcp-server/vitest.config.ts +0 -16
@@ -1,189 +1,197 @@
1
1
  /**
2
- * Unified Output System
3
- *
4
- * Provides consistent, deterministic output across all vibecheck commands.
5
- * This is what makes vibecheck feel "enterprise-grade".
2
+ * Unified Output System - World Class
3
+ * * The central nervous system for Vibecheck's CLI output.
4
+ * Delivers consistent, high-fidelity visualization and strict exit code compliance.
6
5
  */
7
6
 
8
- // Graceful fallback for missing compiled modules
9
- let EXIT_CODES, formatVerdictOutput;
7
+ "use strict";
10
8
 
11
- // Exit codes per product spec:
12
- // 0 = SHIP / no blockers
13
- // 1 = WARN (allowed if you choose)
14
- // 2 = BLOCK / blockers found
15
- EXIT_CODES = {
9
+ const chalk = require('chalk');
10
+ const os = require('os');
11
+
12
+ // ═══════════════════════════════════════════════════════════════════════════════
13
+ // CONFIGURATION & CONSTANTS
14
+ // ═══════════════════════════════════════════════════════════════════════════════
15
+
16
+ const EXIT_CODES = {
16
17
  SHIP: 0, // Ready to ship, no blockers
17
- PASS: 0, // Alias for SHIP
18
- WARN: 1, // Warnings found (allowed)
19
- BLOCK: 2, // Blockers found, do not ship
20
- FAIL: 2, // Alias for BLOCK
21
- MISCONFIG: 2, // Configuration error = block
22
- INTERNAL: 2, // Internal error = block (safe default)
18
+ WARN: 1, // Warnings found (allowed via flag)
19
+ BLOCK: 2, // Blockers found (hard stop)
20
+ INTERNAL: 2, // Internal error (hard stop)
21
+ MISCONFIG: 2, // Configuration error (hard stop)
23
22
  };
24
23
 
25
- try {
26
- const verdictFormatter = require('../../../dist/lib/cli/verdict-formatter');
27
- formatVerdictOutput = verdictFormatter.formatVerdictOutput;
28
- } catch (err) {
29
- // Fallback when dist not built
30
- formatVerdictOutput = (verdict, options) => {
31
- const c = {
32
- reset: '\x1b[0m',
33
- green: '\x1b[32m',
34
- red: '\x1b[31m',
35
- yellow: '\x1b[33m',
36
- cyan: '\x1b[36m',
37
- dim: '\x1b[2m',
38
- bold: '\x1b[1m',
39
- };
40
-
41
- if (!verdict) return 'No verdict available';
42
-
43
- const icon = verdict.verdict === 'PASS' ? `${c.green}✓${c.reset}` :
44
- verdict.verdict === 'FAIL' ? `${c.red}✗${c.reset}` :
45
- `${c.yellow}⚠${c.reset}`;
46
-
47
- let output = `\n${icon} ${c.bold}${verdict.verdict}${c.reset}\n`;
48
-
49
- if (verdict.summary) {
50
- output += `\n${c.dim}Summary:${c.reset}\n`;
51
- output += ` Blockers: ${verdict.summary.blockers || 0}\n`;
52
- output += ` Warnings: ${verdict.summary.warnings || 0}\n`;
53
- }
54
-
55
- if (verdict.findings && verdict.findings.length > 0) {
56
- output += `\n${c.dim}Findings:${c.reset}\n`;
57
- for (const finding of verdict.findings.slice(0, 10)) {
58
- const sevColor = finding.severity === 'critical' ? c.red :
59
- finding.severity === 'high' ? c.red :
60
- finding.severity === 'medium' ? c.yellow : c.dim;
61
- output += ` ${sevColor}[${finding.severity?.toUpperCase() || 'INFO'}]${c.reset} ${finding.title || finding.message || 'Unknown issue'}\n`;
62
- if (finding.file) {
63
- output += ` ${c.cyan}${finding.file}${finding.line ? `:${finding.line}` : ''}${c.reset}\n`;
64
- }
65
- }
66
- if (verdict.findings.length > 10) {
67
- output += ` ${c.dim}... and ${verdict.findings.length - 10} more${c.reset}\n`;
68
- }
69
- }
70
-
71
- return output;
72
- };
24
+ const WIDTH = 76;
25
+
26
+ const BOX = {
27
+ tl: '╔', tr: '╗', bl: '╚', br: '╝', h: '═', v: '║',
28
+ trT: '╠', tlT: '╣',
29
+ // Light (Inner)
30
+ ltl: '┌', ltr: '┐', lbl: '└', lbr: '┘', lh: '─', lv: '│',
31
+ lt: '', lb: '┴', lx: '┼', ltrT: '├', ltlT: '┤'
32
+ };
33
+
34
+ // ═══════════════════════════════════════════════════════════════════════════════
35
+ // VISUAL RENDERING ENGINE
36
+ // ═══════════════════════════════════════════════════════════════════════════════
37
+
38
+ function padCenter(str, width) {
39
+ const visibleLen = str.replace(/\u001b\[\d+m/g, '').length;
40
+ const padding = Math.max(0, width - visibleLen);
41
+ const left = Math.floor(padding / 2);
42
+ return ' '.repeat(left) + str + ' '.repeat(padding - left);
73
43
  }
74
44
 
75
- /**
76
- * Format scan output with unified contract
77
- */
78
- function formatScanOutput(result, options = {}) {
79
- const { verbose = false, json = false } = options;
80
-
81
- if (json) {
82
- return JSON.stringify(result, null, 2);
83
- }
84
-
85
- return formatVerdictOutput(result.verdict, { verbose, json });
45
+ function padRight(str, len) {
46
+ const visibleLen = str.length;
47
+ const truncated = visibleLen > len ? str.substring(0, len - 3) + '...' : str;
48
+ return truncated + ' '.repeat(Math.max(0, len - truncated.length));
86
49
  }
87
50
 
88
- /**
89
- * Get exit code from verdict
90
- * Per spec: 0=SHIP, 1=WARN, 2=BLOCK
91
- */
92
- function getExitCode(verdict) {
93
- if (!verdict) return EXIT_CODES.BLOCK; // Safe default
94
-
95
- switch (verdict.verdict) {
96
- case 'SHIP':
97
- case 'PASS':
98
- return EXIT_CODES.SHIP; // 0
99
- case 'WARN':
100
- return EXIT_CODES.WARN; // 1
101
- case 'BLOCK':
102
- case 'FAIL':
103
- return EXIT_CODES.BLOCK; // 2
104
- case 'ERROR':
105
- case 'MISCONFIG':
106
- return EXIT_CODES.BLOCK; // 2 (safe default)
107
- default:
108
- return EXIT_CODES.BLOCK; // 2 (safe default)
109
- }
51
+ function renderProgressBar(score, width = 20) {
52
+ const filled = Math.round((score / 100) * width);
53
+ return chalk.green('█'.repeat(filled)) + chalk.gray('░'.repeat(width - filled));
110
54
  }
111
55
 
112
- /**
113
- * Handle errors with proper exit codes
114
- */
115
- function handleError(error, context = '') {
116
- const errorType = classifyError(error);
56
+ // --- The Core Formatter ---
57
+
58
+ function renderVerdict(verdictData, options = {}) {
59
+ const {
60
+ score = 0,
61
+ findings = [],
62
+ duration = 0,
63
+ scannedFiles = 0
64
+ } = verdictData;
65
+
66
+ const lines = [];
67
+ const hasIssues = findings.length > 0;
117
68
 
118
- let exitCode = EXIT_CODES.INTERNAL;
119
- let message = error.message || 'Unknown error';
120
- let nextStep = 'Run: vibecheck doctor';
69
+ // 1. FRAME HEADER
70
+ lines.push(chalk.gray(BOX.tl + BOX.h.repeat(WIDTH - 2) + BOX.tr));
71
+ lines.push(chalk.gray(BOX.v) + ' '.repeat(WIDTH - 2) + chalk.gray(BOX.v));
72
+
73
+ // 2. CLEAN HEADER (No ASCII Logo)
74
+ const titleColor = hasIssues ? chalk.red.bold : chalk.cyan.bold;
75
+ const subTitle = hasIssues ? 'INTEGRITY SCAN • ISSUES DETECTED' : 'INTEGRITY SCAN • CLEAN';
121
76
 
122
- switch (errorType) {
123
- case 'MISCONFIG':
124
- exitCode = EXIT_CODES.MISCONFIG;
125
- message = `Configuration error: ${message}`;
126
- nextStep = 'Run: vibecheck doctor --fix';
127
- break;
128
- case 'MISSING_DEPS':
129
- exitCode = EXIT_CODES.MISCONFIG;
130
- message = `Missing dependencies: ${message}`;
131
- nextStep = 'Run: vibecheck doctor';
132
- break;
133
- case 'PERMISSION':
134
- exitCode = EXIT_CODES.MISCONFIG;
135
- message = `Permission error: ${message}`;
136
- nextStep = 'Check file permissions and run: vibecheck doctor';
137
- break;
138
- default:
139
- exitCode = EXIT_CODES.INTERNAL;
140
- message = `Internal error: ${message}`;
141
- nextStep = 'This looks like a bug. Run: vibecheck doctor';
77
+ // Add some vertical padding for elegance
78
+ lines.push(chalk.gray(BOX.v) + ' '.repeat(WIDTH - 2) + chalk.gray(BOX.v));
79
+ lines.push(chalk.gray(BOX.v) + padCenter(titleColor(subTitle), WIDTH - 2) + chalk.gray(BOX.v));
80
+ lines.push(chalk.gray(BOX.v) + ' '.repeat(WIDTH - 2) + chalk.gray(BOX.v));
81
+
82
+ // 3. TELEMETRY
83
+ lines.push(chalk.gray(BOX.trT + BOX.h.repeat(WIDTH - 2) + BOX.tlT));
84
+ const heapMB = Math.round(process.memoryUsage().heapUsed / 1024 / 1024);
85
+ const stats = `📡 TELEMETRY │ ${duration}ms │ 📂 ${scannedFiles || '?'} Files │ 📦 ${heapMB}MB`;
86
+ lines.push(chalk.gray(BOX.v) + padCenter(stats, WIDTH - 2) + chalk.gray(BOX.v));
87
+ lines.push(chalk.gray(BOX.trT + BOX.h.repeat(WIDTH - 2) + BOX.tlT));
88
+
89
+ if (!hasIssues) {
90
+ // CLEAN STATE
91
+ lines.push(chalk.gray(BOX.v) + ' '.repeat(WIDTH - 2) + chalk.gray(BOX.v));
92
+ lines.push(chalk.gray(BOX.v) + padCenter(chalk.green('✅ NO STATIC ISSUES FOUND'), WIDTH - 2) + chalk.gray(BOX.v));
93
+ lines.push(chalk.gray(BOX.v) + ' '.repeat(WIDTH - 2) + chalk.gray(BOX.v));
94
+ } else {
95
+ // ISSUES STATE
96
+ lines.push(chalk.gray(BOX.v) + ' '.repeat(WIDTH - 2) + chalk.gray(BOX.v));
97
+ const scoreBar = renderProgressBar(score, 20);
98
+ lines.push(chalk.gray(BOX.v) + padCenter(`HEALTH SCORE [${scoreBar}] ${score} / 100`, WIDTH + 18) + chalk.gray(BOX.v));
99
+ lines.push(chalk.gray(BOX.v) + ' '.repeat(WIDTH - 2) + chalk.gray(BOX.v));
100
+
101
+ // TABLE HEADER
102
+ lines.push(chalk.gray(BOX.v) + padCenter('DETECTED VULNERABILITIES', WIDTH - 2) + chalk.gray(BOX.v));
103
+
104
+ // Hardcoded column widths for perfect alignment
105
+ const C1=10, C2=13, C3=41;
106
+
107
+ const tTop = ` ${BOX.ltl}${BOX.lh.repeat(C1)}${BOX.lt}${BOX.lh.repeat(C2)}${BOX.lt}${BOX.lh.repeat(C3)}${BOX.ltr} `;
108
+ const tMid = ` ${BOX.ltrT}${BOX.lh.repeat(C1)}${BOX.lx}${BOX.lh.repeat(C2)}${BOX.lx}${BOX.lh.repeat(C3)}${BOX.ltlT} `;
109
+ const tBot = ` ${BOX.lbl}${BOX.lh.repeat(C1)}${BOX.lb}${BOX.lh.repeat(C2)}${BOX.lb}${BOX.lh.repeat(C3)}${BOX.lbr} `;
110
+
111
+ lines.push(chalk.gray(BOX.v) + chalk.gray(tTop) + chalk.gray(BOX.v));
112
+ const header = ` ${BOX.lv}${padRight(' SEVERITY', C1)}${BOX.lv}${padRight(' TYPE', C2)}${BOX.lv}${padRight(' FINDING', C3)}${BOX.lv} `;
113
+ lines.push(chalk.gray(BOX.v) + chalk.bold(header) + chalk.gray(BOX.v));
114
+ lines.push(chalk.gray(BOX.v) + chalk.gray(tMid) + chalk.gray(BOX.v));
115
+
116
+ // ROWS
117
+ findings.slice(0, 5).forEach(f => {
118
+ const sev = (f.severity === 'critical' || f.severity === 'BLOCK') ? chalk.red('🛑 CRIT ') : chalk.yellow('🟡 WARN ');
119
+ const row = ` ${chalk.gray(BOX.lv)}${sev}${chalk.gray(BOX.lv)}${padRight(' '+(f.category||'General'), C2)}${chalk.gray(BOX.lv)}${padRight(' '+(f.message||f.title), C3)}${chalk.gray(BOX.lv)} `;
120
+ lines.push(chalk.gray(BOX.v) + row + chalk.gray(BOX.v));
121
+ });
122
+
123
+ lines.push(chalk.gray(BOX.v) + chalk.gray(tBot) + chalk.gray(BOX.v));
124
+ lines.push(chalk.gray(BOX.v) + ' '.repeat(WIDTH - 2) + chalk.gray(BOX.v));
125
+
126
+ // AUTO-FIX PROMPT
127
+ lines.push(chalk.gray(BOX.trT + BOX.h.repeat(WIDTH - 2) + BOX.tlT));
128
+ const fixTitle = chalk.white.bold('⚡ AUTO-FIX AVAILABLE');
129
+ lines.push(chalk.gray(BOX.v) + padCenter(fixTitle, WIDTH + 9) + chalk.gray(BOX.v));
130
+ lines.push(chalk.gray(BOX.v) + padCenter(chalk.gray('─'.repeat(60)), WIDTH + 9) + chalk.gray(BOX.v));
131
+
132
+ // Fix options
133
+ lines.push(chalk.gray(BOX.v) + padCenter(chalk.dim('Run ') + chalk.cyan('vibecheck scan --autofix') + chalk.dim(' to apply fixes'), WIDTH + 9) + chalk.gray(BOX.v));
134
+ lines.push(chalk.gray(BOX.v) + padCenter(chalk.dim('Run ') + chalk.cyan('vibecheck fix') + chalk.dim(' to generate AI fix missions'), WIDTH + 9) + chalk.gray(BOX.v));
135
+
136
+ lines.push(chalk.gray(BOX.v) + ' '.repeat(WIDTH - 2) + chalk.gray(BOX.v));
142
137
  }
143
-
144
- return {
145
- exitCode,
146
- message: `${context ? `[${context}] ` : ''}${message}`,
147
- nextStep,
148
- errorType,
149
- };
138
+
139
+ // BOTTOM FRAME
140
+ lines.push(chalk.gray(BOX.v) + ' '.repeat(WIDTH - 2) + chalk.gray(BOX.v));
141
+ lines.push(chalk.gray(BOX.bl + BOX.h.repeat(WIDTH - 2) + BOX.br));
142
+
143
+ return lines.join('\n');
150
144
  }
151
145
 
152
- function classifyError(error) {
153
- const message = (error.message || '').toLowerCase();
154
- const code = error.code || '';
155
-
156
- if (code === 'ENOENT' || message.includes('not found') || message.includes('missing')) {
157
- return 'MISSING_DEPS';
158
- }
159
-
160
- if (code === 'EACCES' || code === 'EPERM' || message.includes('permission')) {
161
- return 'PERMISSION';
162
- }
146
+ // ═══════════════════════════════════════════════════════════════════════════════
147
+ // PUBLIC API
148
+ // ═══════════════════════════════════════════════════════════════════════════════
149
+
150
+ function formatScanOutput(result, options = {}) {
151
+ const { json = false } = options;
163
152
 
164
- if (message.includes('config') || message.includes('environment') || message.includes('env')) {
165
- return 'MISCONFIG';
153
+ // Sort findings deterministically for stable output
154
+ if (result.findings && Array.isArray(result.findings)) {
155
+ const { sortFindings } = require('./finding-sorter');
156
+ result.findings = sortFindings(result.findings);
166
157
  }
167
158
 
168
- return 'INTERNAL';
159
+ if (json) return JSON.stringify(result, null, 2);
160
+ return renderVerdict(result, options);
161
+ }
162
+
163
+ function getExitCode(verdict) {
164
+ if (!verdict) return EXIT_CODES.BLOCK;
165
+ const v = (typeof verdict === 'string' ? verdict : verdict.verdict).toUpperCase();
166
+ if (v === 'SHIP' || v === 'PASS') return EXIT_CODES.SHIP;
167
+ if (v === 'WARN') return EXIT_CODES.WARN;
168
+ return EXIT_CODES.BLOCK;
169
169
  }
170
170
 
171
- /**
172
- * Print error with proper formatting
173
- */
174
171
  function printError(error, context = '') {
175
- const handled = handleError(error, context);
172
+ const isConfig = error.message.toLowerCase().includes('config') || error.code === 'ENOENT';
173
+ const exitCode = isConfig ? EXIT_CODES.MISCONFIG : EXIT_CODES.INTERNAL;
176
174
 
177
- console.error(`\n${handled.message}`);
178
- console.error(`\n${handled.nextStep}\n`);
175
+ const lines = [];
176
+ lines.push('');
177
+ lines.push(chalk.bgRed.white.bold(` 🛑 SYSTEM ALERT: ${context || 'INTERNAL_ERROR'} `));
178
+ lines.push(chalk.red('─'.repeat(WIDTH)));
179
+ lines.push(`${chalk.bold('Error:')} ${error.message}`);
180
+ if (error.code) lines.push(`${chalk.bold('Code:')} ${error.code}`);
181
+ lines.push('');
179
182
 
180
- return handled.exitCode;
183
+ const fixCmd = isConfig ? 'vibecheck doctor --fix' : 'vibecheck doctor';
184
+ lines.push(chalk.gray('Recovery Step:'));
185
+ lines.push(`$ ${chalk.cyan(fixCmd)}`);
186
+ lines.push('');
187
+
188
+ console.error(lines.join('\n'));
189
+ return exitCode;
181
190
  }
182
191
 
183
192
  module.exports = {
184
193
  formatScanOutput,
185
194
  getExitCode,
186
- handleError,
187
195
  printError,
188
196
  EXIT_CODES,
189
197
  };