@vibecheckai/cli 3.5.0 → 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 (224) hide show
  1. package/bin/registry.js +214 -237
  2. package/bin/runners/cli-utils.js +33 -2
  3. package/bin/runners/context/analyzer.js +52 -1
  4. package/bin/runners/context/generators/cursor.js +2 -49
  5. package/bin/runners/context/git-context.js +3 -1
  6. package/bin/runners/context/team-conventions.js +33 -7
  7. package/bin/runners/lib/analysis-core.js +25 -5
  8. package/bin/runners/lib/analyzers.js +431 -481
  9. package/bin/runners/lib/default-config.js +127 -0
  10. package/bin/runners/lib/doctor/modules/security.js +3 -1
  11. package/bin/runners/lib/engine/ast-cache.js +210 -0
  12. package/bin/runners/lib/engine/auth-extractor.js +211 -0
  13. package/bin/runners/lib/engine/billing-extractor.js +112 -0
  14. package/bin/runners/lib/engine/enforcement-extractor.js +100 -0
  15. package/bin/runners/lib/engine/env-extractor.js +207 -0
  16. package/bin/runners/lib/engine/express-extractor.js +208 -0
  17. package/bin/runners/lib/engine/extractors.js +849 -0
  18. package/bin/runners/lib/engine/index.js +207 -0
  19. package/bin/runners/lib/engine/repo-index.js +514 -0
  20. package/bin/runners/lib/engine/types.js +124 -0
  21. package/bin/runners/lib/engines/accessibility-engine.js +18 -218
  22. package/bin/runners/lib/engines/api-consistency-engine.js +30 -335
  23. package/bin/runners/lib/engines/cross-file-analysis-engine.js +27 -292
  24. package/bin/runners/lib/engines/empty-catch-engine.js +17 -127
  25. package/bin/runners/lib/engines/mock-data-engine.js +10 -53
  26. package/bin/runners/lib/engines/performance-issues-engine.js +36 -176
  27. package/bin/runners/lib/engines/security-vulnerabilities-engine.js +54 -382
  28. package/bin/runners/lib/engines/type-aware-engine.js +39 -263
  29. package/bin/runners/lib/engines/vibecheck-engines/index.js +13 -122
  30. package/bin/runners/lib/engines/vibecheck-engines/lib/ast-cache.js +164 -0
  31. package/bin/runners/lib/engines/vibecheck-engines/lib/code-quality-engine.js +291 -0
  32. package/bin/runners/lib/engines/vibecheck-engines/lib/console-logs-engine.js +83 -0
  33. package/bin/runners/lib/engines/vibecheck-engines/lib/dead-code-engine.js +198 -0
  34. package/bin/runners/lib/engines/vibecheck-engines/lib/deprecated-api-engine.js +275 -0
  35. package/bin/runners/lib/engines/vibecheck-engines/lib/empty-catch-engine.js +167 -0
  36. package/bin/runners/lib/engines/vibecheck-engines/lib/file-filter.js +217 -0
  37. package/bin/runners/lib/engines/vibecheck-engines/lib/hardcoded-secrets-engine.js +73 -373
  38. package/bin/runners/lib/engines/vibecheck-engines/lib/mock-data-engine.js +140 -0
  39. package/bin/runners/lib/engines/vibecheck-engines/lib/parallel-processor.js +164 -0
  40. package/bin/runners/lib/engines/vibecheck-engines/lib/performance-issues-engine.js +234 -0
  41. package/bin/runners/lib/engines/vibecheck-engines/lib/type-aware-engine.js +217 -0
  42. package/bin/runners/lib/engines/vibecheck-engines/lib/unsafe-regex-engine.js +78 -0
  43. package/bin/runners/lib/entitlements-v2.js +73 -97
  44. package/bin/runners/lib/error-handler.js +44 -3
  45. package/bin/runners/lib/error-messages.js +289 -0
  46. package/bin/runners/lib/evidence-pack.js +7 -1
  47. package/bin/runners/lib/finding-id.js +69 -0
  48. package/bin/runners/lib/finding-sorter.js +89 -0
  49. package/bin/runners/lib/html-proof-report.js +700 -350
  50. package/bin/runners/lib/missions/plan.js +6 -46
  51. package/bin/runners/lib/missions/templates.js +0 -232
  52. package/bin/runners/lib/next-action.js +560 -0
  53. package/bin/runners/lib/prerequisites.js +149 -0
  54. package/bin/runners/lib/route-detection.js +137 -68
  55. package/bin/runners/lib/scan-output.js +91 -76
  56. package/bin/runners/lib/scan-runner.js +135 -0
  57. package/bin/runners/lib/schemas/ajv-validator.js +464 -0
  58. package/bin/runners/lib/schemas/error-envelope.schema.json +105 -0
  59. package/bin/runners/lib/schemas/finding-v3.schema.json +151 -0
  60. package/bin/runners/lib/schemas/report-artifact.schema.json +120 -0
  61. package/bin/runners/lib/schemas/run-request.schema.json +108 -0
  62. package/bin/runners/lib/schemas/validator.js +27 -0
  63. package/bin/runners/lib/schemas/verdict.schema.json +140 -0
  64. package/bin/runners/lib/ship-output-enterprise.js +23 -23
  65. package/bin/runners/lib/ship-output.js +75 -31
  66. package/bin/runners/lib/terminal-ui.js +6 -113
  67. package/bin/runners/lib/truth.js +351 -10
  68. package/bin/runners/lib/unified-cli-output.js +430 -603
  69. package/bin/runners/lib/unified-output.js +13 -9
  70. package/bin/runners/runAIAgent.js +10 -5
  71. package/bin/runners/runAgent.js +0 -3
  72. package/bin/runners/runAllowlist.js +389 -0
  73. package/bin/runners/runApprove.js +0 -33
  74. package/bin/runners/runAuth.js +73 -45
  75. package/bin/runners/runCheckpoint.js +51 -11
  76. package/bin/runners/runClassify.js +85 -21
  77. package/bin/runners/runContext.js +0 -3
  78. package/bin/runners/runDoctor.js +41 -28
  79. package/bin/runners/runEvidencePack.js +362 -0
  80. package/bin/runners/runFirewall.js +0 -3
  81. package/bin/runners/runFirewallHook.js +0 -3
  82. package/bin/runners/runFix.js +66 -76
  83. package/bin/runners/runGuard.js +18 -411
  84. package/bin/runners/runInit.js +113 -30
  85. package/bin/runners/runLabs.js +424 -0
  86. package/bin/runners/runMcp.js +19 -25
  87. package/bin/runners/runPolish.js +64 -240
  88. package/bin/runners/runPromptFirewall.js +12 -5
  89. package/bin/runners/runProve.js +57 -22
  90. package/bin/runners/runQuickstart.js +531 -0
  91. package/bin/runners/runReality.js +59 -68
  92. package/bin/runners/runReport.js +38 -33
  93. package/bin/runners/runRuntime.js +8 -5
  94. package/bin/runners/runScan.js +1413 -190
  95. package/bin/runners/runShip.js +113 -719
  96. package/bin/runners/runTruth.js +0 -3
  97. package/bin/runners/runValidate.js +13 -9
  98. package/bin/runners/runWatch.js +23 -14
  99. package/bin/scan.js +6 -1
  100. package/bin/vibecheck.js +204 -185
  101. package/mcp-server/deprecation-middleware.js +282 -0
  102. package/mcp-server/handlers/index.ts +15 -0
  103. package/mcp-server/handlers/tool-handler.ts +554 -0
  104. package/mcp-server/index-v1.js +698 -0
  105. package/mcp-server/index.js +210 -238
  106. package/mcp-server/lib/cache-wrapper.cjs +383 -0
  107. package/mcp-server/lib/error-envelope.js +138 -0
  108. package/mcp-server/lib/executor.ts +499 -0
  109. package/mcp-server/lib/index.ts +19 -0
  110. package/mcp-server/lib/rate-limiter.js +166 -0
  111. package/mcp-server/lib/sandbox.test.ts +519 -0
  112. package/mcp-server/lib/sandbox.ts +395 -0
  113. package/mcp-server/lib/types.ts +267 -0
  114. package/mcp-server/package.json +12 -3
  115. package/mcp-server/registry/tool-registry.js +794 -0
  116. package/mcp-server/registry/tools.json +605 -0
  117. package/mcp-server/registry.test.ts +334 -0
  118. package/mcp-server/tests/tier-gating.test.js +297 -0
  119. package/mcp-server/tier-auth.js +378 -45
  120. package/mcp-server/tools-v3.js +353 -442
  121. package/mcp-server/tsconfig.json +37 -0
  122. package/mcp-server/vibecheck-2.0-tools.js +14 -1
  123. package/package.json +1 -1
  124. package/bin/runners/lib/agent-firewall/learning/learning-engine.js +0 -849
  125. package/bin/runners/lib/audit-logger.js +0 -532
  126. package/bin/runners/lib/authority/authorities/architecture.js +0 -364
  127. package/bin/runners/lib/authority/authorities/compliance.js +0 -341
  128. package/bin/runners/lib/authority/authorities/human.js +0 -343
  129. package/bin/runners/lib/authority/authorities/quality.js +0 -420
  130. package/bin/runners/lib/authority/authorities/security.js +0 -228
  131. package/bin/runners/lib/authority/index.js +0 -293
  132. package/bin/runners/lib/bundle/bundle-intelligence.js +0 -846
  133. package/bin/runners/lib/cli-charts.js +0 -368
  134. package/bin/runners/lib/cli-config-display.js +0 -405
  135. package/bin/runners/lib/cli-demo.js +0 -275
  136. package/bin/runners/lib/cli-errors.js +0 -438
  137. package/bin/runners/lib/cli-help-formatter.js +0 -439
  138. package/bin/runners/lib/cli-interactive-menu.js +0 -509
  139. package/bin/runners/lib/cli-prompts.js +0 -441
  140. package/bin/runners/lib/cli-scan-cards.js +0 -362
  141. package/bin/runners/lib/compliance-reporter.js +0 -710
  142. package/bin/runners/lib/conductor/index.js +0 -671
  143. package/bin/runners/lib/easy/README.md +0 -123
  144. package/bin/runners/lib/easy/index.js +0 -140
  145. package/bin/runners/lib/easy/interactive-wizard.js +0 -788
  146. package/bin/runners/lib/easy/one-click-firewall.js +0 -564
  147. package/bin/runners/lib/easy/zero-config-reality.js +0 -714
  148. package/bin/runners/lib/engines/async-patterns-engine.js +0 -444
  149. package/bin/runners/lib/engines/bundle-size-engine.js +0 -433
  150. package/bin/runners/lib/engines/confidence-scoring.js +0 -276
  151. package/bin/runners/lib/engines/context-detection.js +0 -264
  152. package/bin/runners/lib/engines/database-patterns-engine.js +0 -429
  153. package/bin/runners/lib/engines/duplicate-code-engine.js +0 -354
  154. package/bin/runners/lib/engines/env-variables-engine.js +0 -458
  155. package/bin/runners/lib/engines/error-handling-engine.js +0 -437
  156. package/bin/runners/lib/engines/false-positive-prevention.js +0 -630
  157. package/bin/runners/lib/engines/framework-adapters/index.js +0 -607
  158. package/bin/runners/lib/engines/framework-detection.js +0 -508
  159. package/bin/runners/lib/engines/import-order-engine.js +0 -429
  160. package/bin/runners/lib/engines/naming-conventions-engine.js +0 -544
  161. package/bin/runners/lib/engines/noise-reduction-engine.js +0 -452
  162. package/bin/runners/lib/engines/orchestrator.js +0 -334
  163. package/bin/runners/lib/engines/react-patterns-engine.js +0 -457
  164. package/bin/runners/lib/engines/vibecheck-engines/lib/ai-hallucination-engine.js +0 -806
  165. package/bin/runners/lib/engines/vibecheck-engines/lib/smart-fix-engine.js +0 -577
  166. package/bin/runners/lib/engines/vibecheck-engines/lib/vibe-score-engine.js +0 -543
  167. package/bin/runners/lib/engines/vibecheck-engines.js +0 -514
  168. package/bin/runners/lib/enhanced-features/index.js +0 -305
  169. package/bin/runners/lib/enhanced-output.js +0 -631
  170. package/bin/runners/lib/enterprise.js +0 -300
  171. package/bin/runners/lib/firewall/command-validator.js +0 -351
  172. package/bin/runners/lib/firewall/config.js +0 -341
  173. package/bin/runners/lib/firewall/content-validator.js +0 -519
  174. package/bin/runners/lib/firewall/index.js +0 -101
  175. package/bin/runners/lib/firewall/path-validator.js +0 -256
  176. package/bin/runners/lib/intelligence/cross-repo-intelligence.js +0 -817
  177. package/bin/runners/lib/mcp-utils.js +0 -425
  178. package/bin/runners/lib/output/index.js +0 -1022
  179. package/bin/runners/lib/policy-engine.js +0 -652
  180. package/bin/runners/lib/polish/autofix/accessibility-fixes.js +0 -333
  181. package/bin/runners/lib/polish/autofix/async-handlers.js +0 -273
  182. package/bin/runners/lib/polish/autofix/dead-code.js +0 -280
  183. package/bin/runners/lib/polish/autofix/imports-optimizer.js +0 -344
  184. package/bin/runners/lib/polish/autofix/index.js +0 -200
  185. package/bin/runners/lib/polish/autofix/remove-consoles.js +0 -209
  186. package/bin/runners/lib/polish/autofix/strengthen-types.js +0 -245
  187. package/bin/runners/lib/polish/backend-checks.js +0 -148
  188. package/bin/runners/lib/polish/documentation-checks.js +0 -111
  189. package/bin/runners/lib/polish/frontend-checks.js +0 -168
  190. package/bin/runners/lib/polish/index.js +0 -71
  191. package/bin/runners/lib/polish/infrastructure-checks.js +0 -131
  192. package/bin/runners/lib/polish/library-detection.js +0 -175
  193. package/bin/runners/lib/polish/performance-checks.js +0 -100
  194. package/bin/runners/lib/polish/security-checks.js +0 -148
  195. package/bin/runners/lib/polish/utils.js +0 -203
  196. package/bin/runners/lib/prompt-builder.js +0 -540
  197. package/bin/runners/lib/proof-certificate.js +0 -634
  198. package/bin/runners/lib/reality/accessibility-audit.js +0 -946
  199. package/bin/runners/lib/reality/api-contract-validator.js +0 -1012
  200. package/bin/runners/lib/reality/chaos-engineering.js +0 -1084
  201. package/bin/runners/lib/reality/performance-tracker.js +0 -1077
  202. package/bin/runners/lib/reality/scenario-generator.js +0 -1404
  203. package/bin/runners/lib/reality/visual-regression.js +0 -852
  204. package/bin/runners/lib/reality-profiler.js +0 -717
  205. package/bin/runners/lib/replay/flight-recorder-viewer.js +0 -1160
  206. package/bin/runners/lib/review/ai-code-review.js +0 -832
  207. package/bin/runners/lib/rules/custom-rule-engine.js +0 -985
  208. package/bin/runners/lib/sbom-generator.js +0 -641
  209. package/bin/runners/lib/scan-output-enhanced.js +0 -512
  210. package/bin/runners/lib/security/owasp-scanner.js +0 -939
  211. package/bin/runners/lib/validators/contract-validator.js +0 -283
  212. package/bin/runners/lib/validators/dead-export-detector.js +0 -279
  213. package/bin/runners/lib/validators/dep-audit.js +0 -245
  214. package/bin/runners/lib/validators/env-validator.js +0 -319
  215. package/bin/runners/lib/validators/index.js +0 -120
  216. package/bin/runners/lib/validators/license-checker.js +0 -252
  217. package/bin/runners/lib/validators/route-validator.js +0 -290
  218. package/bin/runners/runAuthority.js +0 -528
  219. package/bin/runners/runConductor.js +0 -772
  220. package/bin/runners/runContainer.js +0 -366
  221. package/bin/runners/runEasy.js +0 -410
  222. package/bin/runners/runIaC.js +0 -372
  223. package/bin/runners/runVibe.js +0 -791
  224. package/mcp-server/tools.js +0 -495
@@ -1,791 +0,0 @@
1
- /**
2
- * vibecheck vibe - The Vibe Coder's Reality Check
3
- *
4
- * ═══════════════════════════════════════════════════════════════════════════════
5
- * ENTERPRISE EDITION - Comprehensive Vibe Score Analysis
6
- * ═══════════════════════════════════════════════════════════════════════════════
7
- *
8
- * The ultimate command for vibe coders. Analyzes your entire codebase and answers:
9
- * "How much of this code actually works vs. just looks like it works?"
10
- *
11
- * What it checks:
12
- * - AI Hallucinations (fake success returns, stub implementations)
13
- * - Error Handling Quality (swallowed errors, optimistic catch blocks)
14
- * - API/Data Integrity (placeholder URLs, fake data)
15
- * - Code Quality (complexity, dead code, console.logs)
16
- * - Security Posture (hardcoded secrets, auth bypasses)
17
- *
18
- * Output: A comprehensive Vibe Score from 0-100 with actionable insights.
19
- */
20
-
21
- "use strict";
22
-
23
- const fs = require("fs");
24
- const path = require("path");
25
- const { parseGlobalFlags, shouldShowBanner } = require("./lib/global-flags");
26
- const { withErrorHandling } = require("./lib/error-handler");
27
- const { getApiKey } = require("./lib/auth");
28
-
29
- // Import new vibecheck engines
30
- let vibeEngines;
31
- try {
32
- vibeEngines = require("./lib/engines/vibecheck-engines");
33
- } catch (e) {
34
- // Engines not available
35
- vibeEngines = null;
36
- }
37
-
38
- // ═══════════════════════════════════════════════════════════════════════════════
39
- // TERMINAL UI - Import from shared module
40
- // ═══════════════════════════════════════════════════════════════════════════════
41
-
42
- const { c, rgb, bgRgb, icons, Spinner } = require("./lib/terminal-ui");
43
-
44
- // Unified Output System
45
- const { output } = require("./lib/output/index.js");
46
-
47
- // Premium color palette (vibrant purple/pink for "vibe")
48
- const colors = {
49
- primary: rgb(200, 100, 255),
50
- secondary: rgb(255, 100, 200),
51
- success: rgb(0, 255, 150),
52
- warning: rgb(255, 200, 0),
53
- error: rgb(255, 80, 80),
54
- info: rgb(100, 200, 255),
55
- accent: rgb(180, 130, 255),
56
- muted: rgb(120, 100, 140),
57
- };
58
-
59
- // ═══════════════════════════════════════════════════════════════════════════════
60
- // BANNER
61
- // ═══════════════════════════════════════════════════════════════════════════════
62
-
63
- const VIBE_BANNER = `
64
- ${rgb(255, 100, 200)} ██╗ ██╗██╗██████╗ ███████╗${c.reset}
65
- ${rgb(230, 100, 220)} ██║ ██║██║██╔══██╗██╔════╝${c.reset}
66
- ${rgb(200, 100, 240)} ██║ ██║██║██████╔╝█████╗ ${c.reset}
67
- ${rgb(180, 100, 255)} ╚██╗ ██╔╝██║██╔══██╗██╔══╝ ${c.reset}
68
- ${rgb(160, 100, 255)} ╚████╔╝ ██║██████╔╝███████╗${c.reset}
69
- ${rgb(140, 100, 255)} ╚═══╝ ╚═╝╚═════╝ ╚══════╝${c.reset}
70
-
71
- ${c.dim} ┌─────────────────────────────────────────────────────────────────┐${c.reset}
72
- ${c.dim} │${c.reset} ${rgb(255, 100, 200)}✨${c.reset} ${c.bold}VIBE SCORE${c.reset} ${c.dim}•${c.reset} ${rgb(200, 200, 200)}AI Detection${c.reset} ${c.dim}•${c.reset} ${rgb(150, 150, 150)}Reality Check${c.reset} ${c.dim}•${c.reset} ${rgb(100, 100, 100)}Ship Ready?${c.reset} ${c.dim}│${c.reset}
73
- ${c.dim} └─────────────────────────────────────────────────────────────────┘${c.reset}
74
- `;
75
-
76
- // ═══════════════════════════════════════════════════════════════════════════════
77
- // ICONS & BOX DRAWING
78
- // ═══════════════════════════════════════════════════════════════════════════════
79
-
80
- const ICONS = {
81
- vibe: '✨',
82
- check: '✓',
83
- cross: '✗',
84
- warning: '⚠',
85
- info: 'ℹ',
86
- arrow: '→',
87
- bullet: '•',
88
- sparkle: '✨',
89
- fire: '🔥',
90
- rocket: '🚀',
91
- ghost: '👻',
92
- robot: '🤖',
93
- brain: '🧠',
94
- shield: '🛡️',
95
- bug: '🐛',
96
- lightbulb: '💡',
97
- chart: '📊',
98
- target: '🎯',
99
- };
100
-
101
- const BOX = {
102
- topLeft: '╭', topRight: '╮', bottomLeft: '╰', bottomRight: '╯',
103
- horizontal: '─', vertical: '│',
104
- dTopLeft: '╔', dTopRight: '╗', dBottomLeft: '╚', dBottomRight: '╝',
105
- dHorizontal: '═', dVertical: '║',
106
- };
107
-
108
- // ═══════════════════════════════════════════════════════════════════════════════
109
- // UTILITIES
110
- // ═══════════════════════════════════════════════════════════════════════════════
111
-
112
- function formatDuration(ms) {
113
- if (ms < 1000) return `${ms}ms`;
114
- if (ms < 60000) return `${(ms / 1000).toFixed(1)}s`;
115
- const mins = Math.floor(ms / 60000);
116
- const secs = Math.floor((ms % 60000) / 1000);
117
- return `${mins}m ${secs}s`;
118
- }
119
-
120
- function progressBar(percent, width = 30, opts = {}) {
121
- const filled = Math.round((percent / 100) * width);
122
- const empty = width - filled;
123
-
124
- let filledColor;
125
- if (opts.color) {
126
- filledColor = opts.color;
127
- } else if (percent >= 80) {
128
- filledColor = colors.success;
129
- } else if (percent >= 50) {
130
- filledColor = colors.warning;
131
- } else {
132
- filledColor = colors.error;
133
- }
134
-
135
- return `${filledColor}${'█'.repeat(filled)}${c.dim}${'░'.repeat(empty)}${c.reset}`;
136
- }
137
-
138
- function padCenter(str, width) {
139
- const padding = Math.max(0, width - str.length);
140
- const left = Math.floor(padding / 2);
141
- const right = padding - left;
142
- return ' '.repeat(left) + str + ' '.repeat(right);
143
- }
144
-
145
- function truncate(str, len) {
146
- if (!str) return '';
147
- if (str.length <= len) return str;
148
- return str.slice(0, len - 3) + '...';
149
- }
150
-
151
- // Spinner
152
- const SPINNER_FRAMES = ['⣾', '⣽', '⣻', '⢿', '⡿', '⣟', '⣯', '⣷'];
153
- let spinnerIndex = 0;
154
- let spinnerInterval = null;
155
- let spinnerStartTime = null;
156
-
157
- function startSpinner(message) {
158
- spinnerStartTime = Date.now();
159
- process.stdout.write(c.hideCursor);
160
-
161
- spinnerInterval = setInterval(() => {
162
- const elapsed = formatDuration(Date.now() - spinnerStartTime);
163
- process.stdout.write(`\r${c.clearLine} ${colors.primary}${SPINNER_FRAMES[spinnerIndex]}${c.reset} ${message} ${c.dim}${elapsed}${c.reset}`);
164
- spinnerIndex = (spinnerIndex + 1) % SPINNER_FRAMES.length;
165
- }, 80);
166
- }
167
-
168
- function stopSpinner(message, success = true) {
169
- if (spinnerInterval) {
170
- clearInterval(spinnerInterval);
171
- spinnerInterval = null;
172
- }
173
- const elapsed = spinnerStartTime ? formatDuration(Date.now() - spinnerStartTime) : '';
174
- const icon = success ? `${colors.success}${ICONS.check}${c.reset}` : `${colors.error}${ICONS.cross}${c.reset}`;
175
- process.stdout.write(`\r${c.clearLine} ${icon} ${message} ${c.dim}${elapsed}${c.reset}\n`);
176
- process.stdout.write(c.showCursor);
177
- spinnerStartTime = null;
178
- }
179
-
180
- // ═══════════════════════════════════════════════════════════════════════════════
181
- // FILE SCANNER
182
- // ═══════════════════════════════════════════════════════════════════════════════
183
-
184
- const IGNORE_PATTERNS = [
185
- 'node_modules',
186
- '.git',
187
- 'dist',
188
- 'build',
189
- '.next',
190
- '.nuxt',
191
- 'coverage',
192
- '__pycache__',
193
- '.vibecheck',
194
- '.cache',
195
- ];
196
-
197
- const CODE_EXTENSIONS = ['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs'];
198
-
199
- function* walkFiles(dir, depth = 0, maxDepth = 10) {
200
- if (depth > maxDepth) return;
201
-
202
- let entries;
203
- try {
204
- entries = fs.readdirSync(dir, { withFileTypes: true });
205
- } catch {
206
- return;
207
- }
208
-
209
- for (const entry of entries) {
210
- const fullPath = path.join(dir, entry.name);
211
-
212
- if (entry.isDirectory()) {
213
- if (IGNORE_PATTERNS.some(p => entry.name.includes(p))) continue;
214
- yield* walkFiles(fullPath, depth + 1, maxDepth);
215
- } else if (entry.isFile()) {
216
- const ext = path.extname(entry.name);
217
- if (CODE_EXTENSIONS.includes(ext)) {
218
- yield fullPath;
219
- }
220
- }
221
- }
222
- }
223
-
224
- // ═══════════════════════════════════════════════════════════════════════════════
225
- // MAIN VIBE ANALYSIS
226
- // ═══════════════════════════════════════════════════════════════════════════════
227
-
228
- async function analyzeProject(projectPath, options = {}) {
229
- const allFindings = [];
230
- let filesAnalyzed = 0;
231
- let linesOfCode = 0;
232
-
233
- // Collect files
234
- const files = Array.from(walkFiles(projectPath));
235
-
236
- for (const filePath of files) {
237
- try {
238
- const content = fs.readFileSync(filePath, 'utf-8');
239
- const lines = content.split('\n').length;
240
- linesOfCode += lines;
241
- filesAnalyzed++;
242
-
243
- // Run AI hallucination detection
244
- if (vibeEngines?.analyzeAIHallucinations) {
245
- const findings = vibeEngines.analyzeAIHallucinations(content, filePath, {
246
- includeTests: options.includeTests || false,
247
- });
248
- allFindings.push(...findings);
249
- }
250
-
251
- // Run empty catch detection
252
- if (vibeEngines?.analyzeEmptyCatch) {
253
- try {
254
- const findings = vibeEngines.analyzeEmptyCatch(content, filePath);
255
- allFindings.push(...findings.map(f => ({
256
- ...f,
257
- category: 'EmptyCatch',
258
- })));
259
- } catch {}
260
- }
261
-
262
- // Run mock data detection
263
- if (vibeEngines?.analyzeMockData) {
264
- try {
265
- const findings = vibeEngines.analyzeMockData(content, filePath);
266
- allFindings.push(...findings.map(f => ({
267
- ...f,
268
- category: 'MockData',
269
- })));
270
- } catch {}
271
- }
272
-
273
- // Run hardcoded secrets detection
274
- if (vibeEngines?.analyzeHardcodedSecrets) {
275
- try {
276
- const findings = vibeEngines.analyzeHardcodedSecrets(content, filePath);
277
- allFindings.push(...findings.map(f => ({
278
- ...f,
279
- category: 'HardcodedSecret',
280
- })));
281
- } catch {}
282
- }
283
-
284
- // Run console.log detection
285
- if (vibeEngines?.analyzeConsoleLogs) {
286
- try {
287
- const findings = vibeEngines.analyzeConsoleLogs(content, filePath);
288
- allFindings.push(...findings.map(f => ({
289
- ...f,
290
- category: 'ConsoleLog',
291
- })));
292
- } catch {}
293
- }
294
-
295
- // Run environment variable analysis
296
- if (vibeEngines?.analyzeEnvVariables) {
297
- try {
298
- const findings = vibeEngines.analyzeEnvVariables(content, filePath);
299
- allFindings.push(...findings.map(f => ({
300
- ...f,
301
- category: f.category || 'EnvVariable',
302
- })));
303
- } catch {}
304
- }
305
-
306
- // Run async patterns analysis
307
- if (vibeEngines?.analyzeAsyncPatterns) {
308
- try {
309
- const findings = vibeEngines.analyzeAsyncPatterns(content, filePath);
310
- allFindings.push(...findings.map(f => ({
311
- ...f,
312
- category: f.category || 'AsyncPatterns',
313
- })));
314
- } catch {}
315
- }
316
-
317
- // Run naming conventions analysis
318
- if (vibeEngines?.analyzeNamingConventions) {
319
- try {
320
- const findings = vibeEngines.analyzeNamingConventions(content, filePath);
321
- allFindings.push(...findings.map(f => ({
322
- ...f,
323
- category: f.category || 'NamingConventions',
324
- })));
325
- } catch {}
326
- }
327
-
328
- // Run import order analysis
329
- if (vibeEngines?.analyzeImportOrder) {
330
- try {
331
- const findings = vibeEngines.analyzeImportOrder(content, filePath);
332
- allFindings.push(...findings.map(f => ({
333
- ...f,
334
- category: f.category || 'ImportOrder',
335
- })));
336
- } catch {}
337
- }
338
-
339
- // Run React patterns analysis (for .tsx/.jsx files)
340
- if (vibeEngines?.analyzeReactPatterns) {
341
- try {
342
- const findings = vibeEngines.analyzeReactPatterns(content, filePath);
343
- allFindings.push(...findings.map(f => ({
344
- ...f,
345
- category: f.category || 'ReactPatterns',
346
- })));
347
- } catch {}
348
- }
349
-
350
- // Run error handling analysis
351
- if (vibeEngines?.analyzeErrorHandling) {
352
- try {
353
- const findings = vibeEngines.analyzeErrorHandling(content, filePath);
354
- allFindings.push(...findings.map(f => ({
355
- ...f,
356
- category: f.category || 'ErrorHandling',
357
- })));
358
- } catch {}
359
- }
360
-
361
- // Run database patterns analysis
362
- if (vibeEngines?.analyzeDatabasePatterns) {
363
- try {
364
- const findings = vibeEngines.analyzeDatabasePatterns(content, filePath);
365
- allFindings.push(...findings.map(f => ({
366
- ...f,
367
- category: f.category || 'DatabasePatterns',
368
- })));
369
- } catch {}
370
- }
371
-
372
- } catch (e) {
373
- // Skip files that can't be read
374
- }
375
- }
376
-
377
- // Run env setup analysis at project level
378
- if (vibeEngines?.analyzeEnvSetup) {
379
- try {
380
- const findings = vibeEngines.analyzeEnvSetup(projectPath);
381
- allFindings.push(...findings.map(f => ({
382
- ...f,
383
- category: f.category || 'EnvSetup',
384
- })));
385
- } catch {}
386
- }
387
-
388
- return {
389
- findings: allFindings,
390
- meta: {
391
- filesAnalyzed,
392
- linesOfCode,
393
- projectPath,
394
- },
395
- };
396
- }
397
-
398
- // ═══════════════════════════════════════════════════════════════════════════════
399
- // OUTPUT FORMATTERS
400
- // ═══════════════════════════════════════════════════════════════════════════════
401
-
402
- function printVibeScoreCard(vibeScore) {
403
- const { score, grade, label, emoji, riskLevel } = vibeScore;
404
- const w = 68;
405
-
406
- // Determine colors based on grade
407
- let borderColor, bgColor;
408
- if (grade === 'A' || grade === 'B') {
409
- borderColor = rgb(0, 200, 120);
410
- bgColor = bgRgb(0, 80, 50);
411
- } else if (grade === 'C') {
412
- borderColor = rgb(200, 160, 0);
413
- bgColor = bgRgb(80, 60, 0);
414
- } else {
415
- borderColor = rgb(200, 60, 60);
416
- bgColor = bgRgb(80, 20, 20);
417
- }
418
-
419
- console.log();
420
- console.log();
421
-
422
- // Top border
423
- console.log(` ${borderColor}${BOX.dTopLeft}${BOX.dHorizontal.repeat(w)}${BOX.dTopRight}${c.reset}`);
424
-
425
- // Empty line
426
- console.log(` ${borderColor}${BOX.dVertical}${c.reset}${' '.repeat(w)}${borderColor}${BOX.dVertical}${c.reset}`);
427
-
428
- // Score display
429
- const scoreText = `${emoji} VIBE SCORE: ${score}/100 ${emoji}`;
430
- const scorePadded = padCenter(scoreText, w);
431
- console.log(` ${borderColor}${BOX.dVertical}${c.reset}${c.bold}${scorePadded}${c.reset}${borderColor}${BOX.dVertical}${c.reset}`);
432
-
433
- // Grade
434
- const gradeText = `Grade: ${grade} - ${label}`;
435
- const gradePadded = padCenter(gradeText, w);
436
- console.log(` ${borderColor}${BOX.dVertical}${c.reset}${c.dim}${gradePadded}${c.reset}${borderColor}${BOX.dVertical}${c.reset}`);
437
-
438
- // Empty line
439
- console.log(` ${borderColor}${BOX.dVertical}${c.reset}${' '.repeat(w)}${borderColor}${BOX.dVertical}${c.reset}`);
440
-
441
- // Progress bar
442
- const bar = progressBar(score, 40);
443
- const barLine = ` ${bar}`;
444
- const barPadded = barLine + ' '.repeat(Math.max(0, w - barLine.length + 15));
445
- console.log(` ${borderColor}${BOX.dVertical}${c.reset}${barPadded}${borderColor}${BOX.dVertical}${c.reset}`);
446
-
447
- // Empty line
448
- console.log(` ${borderColor}${BOX.dVertical}${c.reset}${' '.repeat(w)}${borderColor}${BOX.dVertical}${c.reset}`);
449
-
450
- // Risk level
451
- const riskColor = riskLevel === 'minimal' ? colors.success :
452
- riskLevel === 'low' ? colors.success :
453
- riskLevel === 'medium' ? colors.warning :
454
- colors.error;
455
- const riskText = `Risk Level: ${riskLevel.toUpperCase()}`;
456
- const riskPadded = padCenter(riskText, w);
457
- console.log(` ${borderColor}${BOX.dVertical}${c.reset}${riskColor}${riskPadded}${c.reset}${borderColor}${BOX.dVertical}${c.reset}`);
458
-
459
- // Empty line
460
- console.log(` ${borderColor}${BOX.dVertical}${c.reset}${' '.repeat(w)}${borderColor}${BOX.dVertical}${c.reset}`);
461
-
462
- // Bottom border
463
- console.log(` ${borderColor}${BOX.dBottomLeft}${BOX.dHorizontal.repeat(w)}${BOX.dBottomRight}${c.reset}`);
464
-
465
- console.log();
466
- }
467
-
468
- function printComponentBreakdown(components) {
469
- console.log();
470
- console.log(` ${colors.primary}${ICONS.chart}${c.reset} ${c.bold}COMPONENT BREAKDOWN${c.reset}`);
471
- console.log(` ${c.dim}${'─'.repeat(65)}${c.reset}`);
472
- console.log();
473
-
474
- const componentNames = {
475
- implementationCompleteness: { name: 'Implementation', icon: ICONS.brain },
476
- errorHandling: { name: 'Error Handling', icon: ICONS.shield },
477
- apiDataIntegrity: { name: 'API/Data Integrity', icon: ICONS.target },
478
- codeQuality: { name: 'Code Quality', icon: ICONS.sparkle },
479
- securityPosture: { name: 'Security', icon: ICONS.shield },
480
- asyncPatterns: { name: 'Async Patterns', icon: '⚡' },
481
- envConfig: { name: 'Env Config', icon: '🌍' },
482
- namingConventions: { name: 'Naming', icon: '📝' },
483
- reactPatterns: { name: 'React Patterns', icon: '⚛️' },
484
- databasePatterns: { name: 'Database', icon: '🗃️' },
485
- importOrder: { name: 'Imports', icon: '📦' },
486
- };
487
-
488
- for (const [key, data] of Object.entries(components)) {
489
- const info = componentNames[key] || { name: key, icon: ICONS.bullet };
490
- const statusIcon = data.status === 'excellent' ? colors.success + ICONS.check :
491
- data.status === 'good' ? colors.success + ICONS.check :
492
- data.status === 'needs-work' ? colors.warning + ICONS.warning :
493
- colors.error + ICONS.cross;
494
-
495
- const bar = progressBar(data.score, 20);
496
- const issueText = data.issueCount > 0 ? ` (${data.issueCount} issues)` : '';
497
-
498
- console.log(` ${statusIcon}${c.reset} ${info.icon} ${info.name.padEnd(22)} ${bar} ${data.score}/100${c.dim}${issueText}${c.reset}`);
499
- }
500
-
501
- console.log();
502
- }
503
-
504
- function printInsights(insights) {
505
- if (!insights || insights.length === 0) return;
506
-
507
- console.log();
508
- console.log(` ${colors.primary}${ICONS.lightbulb}${c.reset} ${c.bold}INSIGHTS${c.reset}`);
509
- console.log(` ${c.dim}${'─'.repeat(65)}${c.reset}`);
510
- console.log();
511
-
512
- for (const insight of insights.slice(0, 5)) {
513
- const icon = insight.type === 'critical' ? `${colors.error}${ICONS.warning}` :
514
- insight.type === 'pattern' ? `${colors.warning}${ICONS.robot}` :
515
- insight.type === 'positive' ? `${colors.success}${ICONS.sparkle}` :
516
- `${colors.info}${ICONS.info}`;
517
-
518
- console.log(` ${icon}${c.reset} ${c.bold}${insight.title}${c.reset}`);
519
- console.log(` ${c.dim}${insight.message}${c.reset}`);
520
- console.log(` ${colors.success}${ICONS.arrow}${c.reset} ${insight.action}`);
521
- console.log();
522
- }
523
- }
524
-
525
- function printRiskMetrics(risk) {
526
- console.log();
527
- console.log(` ${colors.primary}${ICONS.shield}${c.reset} ${c.bold}RISK ASSESSMENT${c.reset}`);
528
- console.log(` ${c.dim}${'─'.repeat(65)}${c.reset}`);
529
- console.log();
530
-
531
- const debtColor = risk.technicalDebtLabel === 'low' ? colors.success :
532
- risk.technicalDebtLabel === 'medium' ? colors.warning :
533
- colors.error;
534
-
535
- console.log(` ${c.dim}Technical Debt:${c.reset} ~${debtColor}${risk.technicalDebtHours} hours${c.reset} (${risk.technicalDebtLabel})`);
536
-
537
- const secColor = risk.securityRiskLevel === 'normal' ? colors.success :
538
- risk.securityRiskLevel === 'elevated' ? colors.warning :
539
- colors.error;
540
- console.log(` ${c.dim}Security Risk:${c.reset} ${secColor}${risk.securityRiskLevel}${c.reset}`);
541
-
542
- const relColor = risk.reliabilityRiskLevel === 'low' ? colors.success :
543
- risk.reliabilityRiskLevel === 'medium' ? colors.warning :
544
- colors.error;
545
- console.log(` ${c.dim}Reliability:${c.reset} ${relColor}${risk.reliabilityRiskLevel}${c.reset}`);
546
-
547
- const deployColor = risk.deploymentRisk === 'acceptable' ? colors.success :
548
- risk.deploymentRisk === 'medium-risk' ? colors.warning :
549
- colors.error;
550
- console.log(` ${c.dim}Deployment Risk:${c.reset} ${deployColor}${risk.deploymentRisk}${c.reset}`);
551
-
552
- console.log();
553
- }
554
-
555
- function printSummary(summary, meta) {
556
- console.log();
557
- console.log(` ${colors.primary}${ICONS.chart}${c.reset} ${c.bold}SUMMARY${c.reset}`);
558
- console.log(` ${c.dim}${'─'.repeat(65)}${c.reset}`);
559
- console.log();
560
-
561
- const blockerColor = summary.blockers > 0 ? colors.error : colors.success;
562
- const warningColor = summary.warnings > 0 ? colors.warning : colors.success;
563
-
564
- console.log(` ${blockerColor}${summary.blockers}${c.reset} blockers ${c.dim}|${c.reset} ${warningColor}${summary.warnings}${c.reset} warnings ${c.dim}|${c.reset} ${summary.info} info`);
565
- console.log(` ${c.dim}Files analyzed:${c.reset} ${meta.filesAnalyzed}`);
566
- console.log(` ${c.dim}Lines of code:${c.reset} ${meta.linesOfCode.toLocaleString()}`);
567
- console.log();
568
- }
569
-
570
- function printNextSteps(vibeScore) {
571
- console.log();
572
- console.log(` ${colors.primary}${ICONS.rocket}${c.reset} ${c.bold}NEXT STEPS${c.reset}`);
573
- console.log(` ${c.dim}${'─'.repeat(65)}${c.reset}`);
574
- console.log();
575
-
576
- if (vibeScore.score >= 90) {
577
- console.log(` ${colors.success}${ICONS.check}${c.reset} Your code is production ready!`);
578
- console.log(` ${c.dim}Run${c.reset} ${colors.primary}vibecheck ship${c.reset} ${c.dim}to get your ship badge${c.reset}`);
579
- } else if (vibeScore.score >= 70) {
580
- console.log(` ${colors.warning}${ICONS.warning}${c.reset} Minor issues to address:`);
581
- console.log(` ${c.dim}Run${c.reset} ${colors.primary}vibecheck fix${c.reset} ${c.dim}to auto-fix what can be fixed${c.reset}`);
582
- console.log(` ${c.dim}Run${c.reset} ${colors.primary}vibecheck ship --assist${c.reset} ${c.dim}for AI fix prompts${c.reset}`);
583
- } else {
584
- console.log(` ${colors.error}${ICONS.cross}${c.reset} Significant issues found:`);
585
- console.log(` ${c.dim}Run${c.reset} ${colors.primary}vibecheck fix --apply${c.reset} ${c.dim}to fix blockers${c.reset}`);
586
- console.log(` ${c.dim}Run${c.reset} ${colors.primary}vibecheck reality --url <url>${c.reset} ${c.dim}to test at runtime${c.reset}`);
587
- }
588
-
589
- console.log();
590
- }
591
-
592
- // ═══════════════════════════════════════════════════════════════════════════════
593
- // HELP
594
- // ═══════════════════════════════════════════════════════════════════════════════
595
-
596
- function printHelp(showBanner = true) {
597
- if (showBanner) {
598
- console.log(VIBE_BANNER);
599
- }
600
- console.log(`
601
- ${c.bold}Usage:${c.reset} vibecheck vibe [options]
602
-
603
- ${c.bold}The Vibe Coder's Reality Check${c.reset} — Find out how much of your code actually works.
604
-
605
- ${c.bold}What It Detects:${c.reset}
606
- ${colors.primary}${ICONS.robot} AI Hallucinations${c.reset} Fake success returns, stub implementations
607
- ${colors.primary}${ICONS.shield} Error Handling${c.reset} Swallowed errors, generic messages, missing catches
608
- ${colors.primary}${ICONS.target} API/Data Integrity${c.reset} Placeholder URLs, fake data
609
- ${colors.primary}${ICONS.sparkle} Code Quality${c.reset} Console.logs, complexity, dead code
610
- ${colors.primary}${ICONS.shield} Security${c.reset} Hardcoded secrets, auth bypasses
611
- ${colors.primary}⚡ Async Patterns${c.reset} Floating promises, unhandled rejections
612
- ${colors.primary}🌍 Env Variables${c.reset} Missing validation, insecure defaults
613
- ${colors.primary}📝 Naming${c.reset} React components, hooks, conventions
614
- ${colors.primary}⚛️ React Patterns${c.reset} Missing keys, conditional hooks, state mutations
615
- ${colors.primary}🗃️ Database${c.reset} N+1 queries, missing transactions, SQL injection
616
- ${colors.primary}📦 Import Order${c.reset} Mixed styles, unused imports, circular deps
617
-
618
- ${c.bold}Options:${c.reset}
619
- ${colors.primary}--path, -p <path>${c.reset} Project path ${c.dim}(default: current directory)${c.reset}
620
- ${colors.primary}--json${c.reset} Output as JSON
621
- ${colors.primary}--ci${c.reset} CI-friendly output
622
- ${colors.primary}--include-tests${c.reset} Include test files in analysis
623
- ${colors.primary}--verbose, -v${c.reset} Show detailed output
624
- ${colors.primary}--help, -h${c.reset} Show this help
625
-
626
- ${c.bold}Output:${c.reset}
627
- A comprehensive Vibe Score from 0-100 with:
628
- • Component breakdown (implementation, errors, API, quality, security)
629
- • Risk assessment (technical debt, deployment risk)
630
- • Actionable insights with fix suggestions
631
- • Next steps to improve your score
632
-
633
- ${c.bold}Examples:${c.reset}
634
- ${c.dim}# Analyze current directory${c.reset}
635
- vibecheck vibe
636
-
637
- ${c.dim}# Analyze specific project${c.reset}
638
- vibecheck vibe --path ./my-app
639
-
640
- ${c.dim}# CI-friendly output${c.reset}
641
- vibecheck vibe --ci
642
-
643
- ${c.dim}# JSON output for scripts${c.reset}
644
- vibecheck vibe --json
645
- `);
646
- }
647
-
648
- // ═══════════════════════════════════════════════════════════════════════════════
649
- // MAIN FUNCTION
650
- // ═══════════════════════════════════════════════════════════════════════════════
651
-
652
- async function runVibe(args = []) {
653
- const startTime = Date.now();
654
-
655
- // Parse arguments
656
- const { flags: globalFlags, cleanArgs } = parseGlobalFlags(args);
657
-
658
- if (globalFlags.help) {
659
- printHelp(shouldShowBanner(globalFlags));
660
- return 0;
661
- }
662
-
663
- // Parse command-specific args
664
- const getArg = (flags) => {
665
- for (const f of flags) {
666
- const idx = cleanArgs.indexOf(f);
667
- if (idx !== -1 && idx < cleanArgs.length - 1) return cleanArgs[idx + 1];
668
- }
669
- return undefined;
670
- };
671
-
672
- const opts = {
673
- path: getArg(['--path', '-p']) || globalFlags.path || '.',
674
- json: globalFlags.json || false,
675
- ci: globalFlags.ci || false,
676
- verbose: globalFlags.verbose || false,
677
- includeTests: cleanArgs.includes('--include-tests'),
678
- noBanner: globalFlags.noBanner || false,
679
- quiet: globalFlags.quiet || false,
680
- };
681
-
682
- const projectPath = path.resolve(opts.path);
683
- const projectName = path.basename(projectPath);
684
-
685
- // Check if engines are available
686
- if (!vibeEngines) {
687
- console.error(`\n ${colors.error}${ICONS.cross}${c.reset} Vibecheck engines not available\n`);
688
- return 1;
689
- }
690
-
691
- // Print banner (unless JSON/CI/quiet)
692
- if (!opts.json && !opts.ci && !opts.quiet && shouldShowBanner(opts)) {
693
- console.log(VIBE_BANNER);
694
- console.log(` ${c.dim}Project:${c.reset} ${c.bold}${projectName}${c.reset}`);
695
- console.log(` ${c.dim}Path:${c.reset} ${projectPath}`);
696
- console.log();
697
- }
698
-
699
- // Run analysis
700
- if (!opts.json && !opts.ci) {
701
- startSpinner('Analyzing codebase for vibe issues...');
702
- }
703
-
704
- const { findings, meta } = await analyzeProject(projectPath, {
705
- includeTests: opts.includeTests,
706
- });
707
-
708
- if (!opts.json && !opts.ci) {
709
- stopSpinner(`Analyzed ${meta.filesAnalyzed} files (${meta.linesOfCode.toLocaleString()} lines)`, true);
710
- }
711
-
712
- // Calculate vibe score
713
- const vibeScore = vibeEngines.calculateVibeScore(findings, {
714
- filesAnalyzed: meta.filesAnalyzed,
715
- linesOfCode: meta.linesOfCode,
716
- });
717
-
718
- const duration = Date.now() - startTime;
719
-
720
- // Output
721
- if (opts.json) {
722
- const output = vibeEngines.generateVibeReportJSON(vibeScore);
723
- output.meta = {
724
- ...output.meta,
725
- projectPath,
726
- projectName,
727
- duration,
728
- };
729
- console.log(JSON.stringify(output, null, 2));
730
- return vibeScore.score >= 70 ? 0 : vibeScore.score >= 50 ? 1 : 2;
731
- }
732
-
733
- if (opts.ci) {
734
- console.log(`VIBE_SCORE=${vibeScore.score}`);
735
- console.log(`VIBE_GRADE=${vibeScore.grade}`);
736
- console.log(`VIBE_RISK=${vibeScore.riskLevel}`);
737
- console.log(`BLOCKERS=${vibeScore.summary.blockers}`);
738
- console.log(`WARNINGS=${vibeScore.summary.warnings}`);
739
- console.log(`FILES=${meta.filesAnalyzed}`);
740
- console.log(`LINES=${meta.linesOfCode}`);
741
- return vibeScore.score >= 70 ? 0 : vibeScore.score >= 50 ? 1 : 2;
742
- }
743
-
744
- // Human-readable output
745
- printVibeScoreCard(vibeScore);
746
- printComponentBreakdown(vibeScore.components);
747
- printInsights(vibeScore.insights);
748
- printRiskMetrics(vibeScore.risk);
749
- printSummary(vibeScore.summary, meta);
750
- printNextSteps(vibeScore);
751
-
752
- // Pro upsell for free users
753
- const { key } = getApiKey();
754
- const currentTier = key ? "pro" : "free";
755
-
756
- if (currentTier === 'free' && !opts.quiet) {
757
- console.log();
758
- console.log(` ${c.gray}╭${'─'.repeat(64)}╮${c.reset}`);
759
- console.log(` ${c.gray}│${c.reset}${' '.repeat(64)}${c.gray}│${c.reset}`);
760
-
761
- if (vibeScore.summary.blockers > 0) {
762
- console.log(` ${c.gray}│${c.reset} ${c.magenta}★ PRO${c.reset} Auto-fix all ${c.bold}${vibeScore.summary.blockers} blockers${c.reset} with AI-powered fixes ${c.gray}│${c.reset}`);
763
- console.log(` ${c.gray}│${c.reset} Run: ${c.cyan}vibecheck fix --apply${c.reset} ${c.gray}│${c.reset}`);
764
- } else if (vibeScore.score >= 70) {
765
- console.log(` ${c.gray}│${c.reset} ${c.magenta}★ PRO${c.reset} Your code is ${c.green}ship-ready${c.reset}! Generate a status badge ${c.gray}│${c.reset}`);
766
- console.log(` ${c.gray}│${c.reset} Run: ${c.cyan}vibecheck ship --badge${c.reset} ${c.gray}│${c.reset}`);
767
- } else {
768
- console.log(` ${c.gray}│${c.reset} ${c.magenta}★ PRO${c.reset} Get AI-powered fixes and CI/CD enforcement ${c.gray}│${c.reset}`);
769
- console.log(` ${c.gray}│${c.reset} Upgrade at: ${c.cyan}https://vibecheckai.dev/pricing${c.reset} ${c.gray}│${c.reset}`);
770
- }
771
-
772
- console.log(` ${c.gray}│${c.reset}${' '.repeat(64)}${c.gray}│${c.reset}`);
773
- console.log(` ${c.gray}╰${'─'.repeat(64)}╯${c.reset}`);
774
- console.log();
775
- }
776
-
777
- // Duration
778
- console.log(` ${c.dim}Completed in ${formatDuration(duration)}${c.reset}`);
779
- console.log();
780
-
781
- // Exit code: 0 = good (70+), 1 = warn (50-69), 2 = bad (<50)
782
- return vibeScore.score >= 70 ? 0 : vibeScore.score >= 50 ? 1 : 2;
783
- }
784
-
785
- // ═══════════════════════════════════════════════════════════════════════════════
786
- // EXPORTS
787
- // ═══════════════════════════════════════════════════════════════════════════════
788
-
789
- module.exports = {
790
- runVibe: withErrorHandling(runVibe, "Vibe analysis failed"),
791
- };