@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.
- package/bin/registry.js +214 -237
- package/bin/runners/cli-utils.js +33 -2
- package/bin/runners/context/analyzer.js +52 -1
- package/bin/runners/context/generators/cursor.js +2 -49
- package/bin/runners/context/git-context.js +3 -1
- package/bin/runners/context/team-conventions.js +33 -7
- package/bin/runners/lib/analysis-core.js +25 -5
- package/bin/runners/lib/analyzers.js +431 -481
- package/bin/runners/lib/default-config.js +127 -0
- package/bin/runners/lib/doctor/modules/security.js +3 -1
- package/bin/runners/lib/engine/ast-cache.js +210 -0
- package/bin/runners/lib/engine/auth-extractor.js +211 -0
- package/bin/runners/lib/engine/billing-extractor.js +112 -0
- package/bin/runners/lib/engine/enforcement-extractor.js +100 -0
- package/bin/runners/lib/engine/env-extractor.js +207 -0
- package/bin/runners/lib/engine/express-extractor.js +208 -0
- package/bin/runners/lib/engine/extractors.js +849 -0
- package/bin/runners/lib/engine/index.js +207 -0
- package/bin/runners/lib/engine/repo-index.js +514 -0
- package/bin/runners/lib/engine/types.js +124 -0
- package/bin/runners/lib/engines/accessibility-engine.js +18 -218
- package/bin/runners/lib/engines/api-consistency-engine.js +30 -335
- package/bin/runners/lib/engines/cross-file-analysis-engine.js +27 -292
- package/bin/runners/lib/engines/empty-catch-engine.js +17 -127
- package/bin/runners/lib/engines/mock-data-engine.js +10 -53
- package/bin/runners/lib/engines/performance-issues-engine.js +36 -176
- package/bin/runners/lib/engines/security-vulnerabilities-engine.js +54 -382
- package/bin/runners/lib/engines/type-aware-engine.js +39 -263
- package/bin/runners/lib/engines/vibecheck-engines/index.js +13 -122
- package/bin/runners/lib/engines/vibecheck-engines/lib/ast-cache.js +164 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/code-quality-engine.js +291 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/console-logs-engine.js +83 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/dead-code-engine.js +198 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/deprecated-api-engine.js +275 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/empty-catch-engine.js +167 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/file-filter.js +217 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/hardcoded-secrets-engine.js +73 -373
- package/bin/runners/lib/engines/vibecheck-engines/lib/mock-data-engine.js +140 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/parallel-processor.js +164 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/performance-issues-engine.js +234 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/type-aware-engine.js +217 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/unsafe-regex-engine.js +78 -0
- package/bin/runners/lib/entitlements-v2.js +73 -97
- package/bin/runners/lib/error-handler.js +44 -3
- package/bin/runners/lib/error-messages.js +289 -0
- package/bin/runners/lib/evidence-pack.js +7 -1
- package/bin/runners/lib/finding-id.js +69 -0
- package/bin/runners/lib/finding-sorter.js +89 -0
- package/bin/runners/lib/html-proof-report.js +700 -350
- package/bin/runners/lib/missions/plan.js +6 -46
- package/bin/runners/lib/missions/templates.js +0 -232
- package/bin/runners/lib/next-action.js +560 -0
- package/bin/runners/lib/prerequisites.js +149 -0
- package/bin/runners/lib/route-detection.js +137 -68
- package/bin/runners/lib/scan-output.js +91 -76
- package/bin/runners/lib/scan-runner.js +135 -0
- package/bin/runners/lib/schemas/ajv-validator.js +464 -0
- package/bin/runners/lib/schemas/error-envelope.schema.json +105 -0
- package/bin/runners/lib/schemas/finding-v3.schema.json +151 -0
- package/bin/runners/lib/schemas/report-artifact.schema.json +120 -0
- package/bin/runners/lib/schemas/run-request.schema.json +108 -0
- package/bin/runners/lib/schemas/validator.js +27 -0
- package/bin/runners/lib/schemas/verdict.schema.json +140 -0
- package/bin/runners/lib/ship-output-enterprise.js +23 -23
- package/bin/runners/lib/ship-output.js +75 -31
- package/bin/runners/lib/terminal-ui.js +6 -113
- package/bin/runners/lib/truth.js +351 -10
- package/bin/runners/lib/unified-cli-output.js +430 -603
- package/bin/runners/lib/unified-output.js +13 -9
- package/bin/runners/runAIAgent.js +10 -5
- package/bin/runners/runAgent.js +0 -3
- package/bin/runners/runAllowlist.js +389 -0
- package/bin/runners/runApprove.js +0 -33
- package/bin/runners/runAuth.js +73 -45
- package/bin/runners/runCheckpoint.js +51 -11
- package/bin/runners/runClassify.js +85 -21
- package/bin/runners/runContext.js +0 -3
- package/bin/runners/runDoctor.js +41 -28
- package/bin/runners/runEvidencePack.js +362 -0
- package/bin/runners/runFirewall.js +0 -3
- package/bin/runners/runFirewallHook.js +0 -3
- package/bin/runners/runFix.js +66 -76
- package/bin/runners/runGuard.js +18 -411
- package/bin/runners/runInit.js +113 -30
- package/bin/runners/runLabs.js +424 -0
- package/bin/runners/runMcp.js +19 -25
- package/bin/runners/runPolish.js +64 -240
- package/bin/runners/runPromptFirewall.js +12 -5
- package/bin/runners/runProve.js +57 -22
- package/bin/runners/runQuickstart.js +531 -0
- package/bin/runners/runReality.js +59 -68
- package/bin/runners/runReport.js +38 -33
- package/bin/runners/runRuntime.js +8 -5
- package/bin/runners/runScan.js +1413 -190
- package/bin/runners/runShip.js +113 -719
- package/bin/runners/runTruth.js +0 -3
- package/bin/runners/runValidate.js +13 -9
- package/bin/runners/runWatch.js +23 -14
- package/bin/scan.js +6 -1
- package/bin/vibecheck.js +204 -185
- package/mcp-server/deprecation-middleware.js +282 -0
- package/mcp-server/handlers/index.ts +15 -0
- package/mcp-server/handlers/tool-handler.ts +554 -0
- package/mcp-server/index-v1.js +698 -0
- package/mcp-server/index.js +210 -238
- package/mcp-server/lib/cache-wrapper.cjs +383 -0
- package/mcp-server/lib/error-envelope.js +138 -0
- package/mcp-server/lib/executor.ts +499 -0
- package/mcp-server/lib/index.ts +19 -0
- package/mcp-server/lib/rate-limiter.js +166 -0
- package/mcp-server/lib/sandbox.test.ts +519 -0
- package/mcp-server/lib/sandbox.ts +395 -0
- package/mcp-server/lib/types.ts +267 -0
- package/mcp-server/package.json +12 -3
- package/mcp-server/registry/tool-registry.js +794 -0
- package/mcp-server/registry/tools.json +605 -0
- package/mcp-server/registry.test.ts +334 -0
- package/mcp-server/tests/tier-gating.test.js +297 -0
- package/mcp-server/tier-auth.js +378 -45
- package/mcp-server/tools-v3.js +353 -442
- package/mcp-server/tsconfig.json +37 -0
- package/mcp-server/vibecheck-2.0-tools.js +14 -1
- package/package.json +1 -1
- package/bin/runners/lib/agent-firewall/learning/learning-engine.js +0 -849
- package/bin/runners/lib/audit-logger.js +0 -532
- package/bin/runners/lib/authority/authorities/architecture.js +0 -364
- package/bin/runners/lib/authority/authorities/compliance.js +0 -341
- package/bin/runners/lib/authority/authorities/human.js +0 -343
- package/bin/runners/lib/authority/authorities/quality.js +0 -420
- package/bin/runners/lib/authority/authorities/security.js +0 -228
- package/bin/runners/lib/authority/index.js +0 -293
- package/bin/runners/lib/bundle/bundle-intelligence.js +0 -846
- package/bin/runners/lib/cli-charts.js +0 -368
- package/bin/runners/lib/cli-config-display.js +0 -405
- package/bin/runners/lib/cli-demo.js +0 -275
- package/bin/runners/lib/cli-errors.js +0 -438
- package/bin/runners/lib/cli-help-formatter.js +0 -439
- package/bin/runners/lib/cli-interactive-menu.js +0 -509
- package/bin/runners/lib/cli-prompts.js +0 -441
- package/bin/runners/lib/cli-scan-cards.js +0 -362
- package/bin/runners/lib/compliance-reporter.js +0 -710
- package/bin/runners/lib/conductor/index.js +0 -671
- package/bin/runners/lib/easy/README.md +0 -123
- package/bin/runners/lib/easy/index.js +0 -140
- package/bin/runners/lib/easy/interactive-wizard.js +0 -788
- package/bin/runners/lib/easy/one-click-firewall.js +0 -564
- package/bin/runners/lib/easy/zero-config-reality.js +0 -714
- package/bin/runners/lib/engines/async-patterns-engine.js +0 -444
- package/bin/runners/lib/engines/bundle-size-engine.js +0 -433
- package/bin/runners/lib/engines/confidence-scoring.js +0 -276
- package/bin/runners/lib/engines/context-detection.js +0 -264
- package/bin/runners/lib/engines/database-patterns-engine.js +0 -429
- package/bin/runners/lib/engines/duplicate-code-engine.js +0 -354
- package/bin/runners/lib/engines/env-variables-engine.js +0 -458
- package/bin/runners/lib/engines/error-handling-engine.js +0 -437
- package/bin/runners/lib/engines/false-positive-prevention.js +0 -630
- package/bin/runners/lib/engines/framework-adapters/index.js +0 -607
- package/bin/runners/lib/engines/framework-detection.js +0 -508
- package/bin/runners/lib/engines/import-order-engine.js +0 -429
- package/bin/runners/lib/engines/naming-conventions-engine.js +0 -544
- package/bin/runners/lib/engines/noise-reduction-engine.js +0 -452
- package/bin/runners/lib/engines/orchestrator.js +0 -334
- package/bin/runners/lib/engines/react-patterns-engine.js +0 -457
- package/bin/runners/lib/engines/vibecheck-engines/lib/ai-hallucination-engine.js +0 -806
- package/bin/runners/lib/engines/vibecheck-engines/lib/smart-fix-engine.js +0 -577
- package/bin/runners/lib/engines/vibecheck-engines/lib/vibe-score-engine.js +0 -543
- package/bin/runners/lib/engines/vibecheck-engines.js +0 -514
- package/bin/runners/lib/enhanced-features/index.js +0 -305
- package/bin/runners/lib/enhanced-output.js +0 -631
- package/bin/runners/lib/enterprise.js +0 -300
- package/bin/runners/lib/firewall/command-validator.js +0 -351
- package/bin/runners/lib/firewall/config.js +0 -341
- package/bin/runners/lib/firewall/content-validator.js +0 -519
- package/bin/runners/lib/firewall/index.js +0 -101
- package/bin/runners/lib/firewall/path-validator.js +0 -256
- package/bin/runners/lib/intelligence/cross-repo-intelligence.js +0 -817
- package/bin/runners/lib/mcp-utils.js +0 -425
- package/bin/runners/lib/output/index.js +0 -1022
- package/bin/runners/lib/policy-engine.js +0 -652
- package/bin/runners/lib/polish/autofix/accessibility-fixes.js +0 -333
- package/bin/runners/lib/polish/autofix/async-handlers.js +0 -273
- package/bin/runners/lib/polish/autofix/dead-code.js +0 -280
- package/bin/runners/lib/polish/autofix/imports-optimizer.js +0 -344
- package/bin/runners/lib/polish/autofix/index.js +0 -200
- package/bin/runners/lib/polish/autofix/remove-consoles.js +0 -209
- package/bin/runners/lib/polish/autofix/strengthen-types.js +0 -245
- package/bin/runners/lib/polish/backend-checks.js +0 -148
- package/bin/runners/lib/polish/documentation-checks.js +0 -111
- package/bin/runners/lib/polish/frontend-checks.js +0 -168
- package/bin/runners/lib/polish/index.js +0 -71
- package/bin/runners/lib/polish/infrastructure-checks.js +0 -131
- package/bin/runners/lib/polish/library-detection.js +0 -175
- package/bin/runners/lib/polish/performance-checks.js +0 -100
- package/bin/runners/lib/polish/security-checks.js +0 -148
- package/bin/runners/lib/polish/utils.js +0 -203
- package/bin/runners/lib/prompt-builder.js +0 -540
- package/bin/runners/lib/proof-certificate.js +0 -634
- package/bin/runners/lib/reality/accessibility-audit.js +0 -946
- package/bin/runners/lib/reality/api-contract-validator.js +0 -1012
- package/bin/runners/lib/reality/chaos-engineering.js +0 -1084
- package/bin/runners/lib/reality/performance-tracker.js +0 -1077
- package/bin/runners/lib/reality/scenario-generator.js +0 -1404
- package/bin/runners/lib/reality/visual-regression.js +0 -852
- package/bin/runners/lib/reality-profiler.js +0 -717
- package/bin/runners/lib/replay/flight-recorder-viewer.js +0 -1160
- package/bin/runners/lib/review/ai-code-review.js +0 -832
- package/bin/runners/lib/rules/custom-rule-engine.js +0 -985
- package/bin/runners/lib/sbom-generator.js +0 -641
- package/bin/runners/lib/scan-output-enhanced.js +0 -512
- package/bin/runners/lib/security/owasp-scanner.js +0 -939
- package/bin/runners/lib/validators/contract-validator.js +0 -283
- package/bin/runners/lib/validators/dead-export-detector.js +0 -279
- package/bin/runners/lib/validators/dep-audit.js +0 -245
- package/bin/runners/lib/validators/env-validator.js +0 -319
- package/bin/runners/lib/validators/index.js +0 -120
- package/bin/runners/lib/validators/license-checker.js +0 -252
- package/bin/runners/lib/validators/route-validator.js +0 -290
- package/bin/runners/runAuthority.js +0 -528
- package/bin/runners/runConductor.js +0 -772
- package/bin/runners/runContainer.js +0 -366
- package/bin/runners/runEasy.js +0 -410
- package/bin/runners/runIaC.js +0 -372
- package/bin/runners/runVibe.js +0 -791
- package/mcp-server/tools.js +0 -495
package/bin/runners/runVibe.js
DELETED
|
@@ -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
|
-
};
|