@vibecheckai/cli 3.3.0 → 3.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/registry.js +389 -269
- package/bin/runners/cli-utils.js +2 -33
- package/bin/runners/context/generators/cursor.js +49 -2
- package/bin/runners/lib/agent-firewall/learning/learning-engine.js +849 -0
- package/bin/runners/lib/analyzers.js +599 -142
- package/bin/runners/lib/audit-logger.js +532 -0
- package/bin/runners/lib/authority/authorities/architecture.js +364 -0
- package/bin/runners/lib/authority/authorities/compliance.js +341 -0
- package/bin/runners/lib/authority/authorities/human.js +343 -0
- package/bin/runners/lib/authority/authorities/quality.js +420 -0
- package/bin/runners/lib/authority/authorities/security.js +228 -0
- package/bin/runners/lib/authority/index.js +293 -0
- package/bin/runners/lib/authority-badge.js +425 -425
- package/bin/runners/lib/bundle/bundle-intelligence.js +846 -0
- package/bin/runners/lib/cli-charts.js +368 -0
- package/bin/runners/lib/cli-config-display.js +405 -0
- package/bin/runners/lib/cli-demo.js +275 -0
- package/bin/runners/lib/cli-errors.js +438 -0
- package/bin/runners/lib/cli-help-formatter.js +439 -0
- package/bin/runners/lib/cli-interactive-menu.js +509 -0
- package/bin/runners/lib/cli-prompts.js +441 -0
- package/bin/runners/lib/cli-scan-cards.js +362 -0
- package/bin/runners/lib/compliance-reporter.js +710 -0
- package/bin/runners/lib/conductor/index.js +671 -0
- package/bin/runners/lib/easy/README.md +123 -0
- package/bin/runners/lib/easy/index.js +140 -0
- package/bin/runners/lib/easy/interactive-wizard.js +788 -0
- package/bin/runners/lib/easy/one-click-firewall.js +564 -0
- package/bin/runners/lib/easy/zero-config-reality.js +714 -0
- package/bin/runners/lib/engines/accessibility-engine.js +218 -18
- package/bin/runners/lib/engines/api-consistency-engine.js +335 -30
- package/bin/runners/lib/engines/async-patterns-engine.js +444 -0
- package/bin/runners/lib/engines/bundle-size-engine.js +433 -0
- package/bin/runners/lib/engines/confidence-scoring.js +276 -0
- package/bin/runners/lib/engines/context-detection.js +264 -0
- package/bin/runners/lib/engines/cross-file-analysis-engine.js +292 -27
- package/bin/runners/lib/engines/database-patterns-engine.js +429 -0
- package/bin/runners/lib/engines/duplicate-code-engine.js +354 -0
- package/bin/runners/lib/engines/empty-catch-engine.js +127 -17
- package/bin/runners/lib/engines/env-variables-engine.js +458 -0
- package/bin/runners/lib/engines/error-handling-engine.js +437 -0
- package/bin/runners/lib/engines/false-positive-prevention.js +630 -0
- package/bin/runners/lib/engines/framework-adapters/index.js +607 -0
- package/bin/runners/lib/engines/framework-detection.js +508 -0
- package/bin/runners/lib/engines/import-order-engine.js +429 -0
- package/bin/runners/lib/engines/mock-data-engine.js +53 -10
- package/bin/runners/lib/engines/naming-conventions-engine.js +544 -0
- package/bin/runners/lib/engines/noise-reduction-engine.js +452 -0
- package/bin/runners/lib/engines/orchestrator.js +334 -0
- package/bin/runners/lib/engines/performance-issues-engine.js +176 -36
- package/bin/runners/lib/engines/react-patterns-engine.js +457 -0
- package/bin/runners/lib/engines/security-vulnerabilities-engine.js +382 -54
- package/bin/runners/lib/engines/type-aware-engine.js +263 -39
- package/bin/runners/lib/engines/vibecheck-engines/index.js +122 -13
- package/bin/runners/lib/engines/vibecheck-engines/lib/ai-hallucination-engine.js +806 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/hardcoded-secrets-engine.js +373 -73
- package/bin/runners/lib/engines/vibecheck-engines/lib/smart-fix-engine.js +577 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/vibe-score-engine.js +543 -0
- package/bin/runners/lib/engines/vibecheck-engines.js +514 -0
- package/bin/runners/lib/enhanced-features/index.js +305 -0
- package/bin/runners/lib/enhanced-output.js +631 -0
- package/bin/runners/lib/enterprise.js +300 -0
- package/bin/runners/lib/entitlements-v2.js +161 -478
- package/bin/runners/lib/firewall/command-validator.js +351 -0
- package/bin/runners/lib/firewall/config.js +341 -0
- package/bin/runners/lib/firewall/content-validator.js +519 -0
- package/bin/runners/lib/firewall/index.js +101 -0
- package/bin/runners/lib/firewall/path-validator.js +256 -0
- package/bin/runners/lib/html-proof-report.js +350 -700
- package/bin/runners/lib/intelligence/cross-repo-intelligence.js +817 -0
- package/bin/runners/lib/mcp-utils.js +425 -0
- package/bin/runners/lib/missions/plan.js +46 -6
- package/bin/runners/lib/missions/templates.js +232 -0
- package/bin/runners/lib/output/index.js +1022 -0
- package/bin/runners/lib/policy-engine.js +652 -0
- package/bin/runners/lib/polish/autofix/accessibility-fixes.js +333 -0
- package/bin/runners/lib/polish/autofix/async-handlers.js +273 -0
- package/bin/runners/lib/polish/autofix/dead-code.js +280 -0
- package/bin/runners/lib/polish/autofix/imports-optimizer.js +344 -0
- package/bin/runners/lib/polish/autofix/index.js +200 -0
- package/bin/runners/lib/polish/autofix/remove-consoles.js +209 -0
- package/bin/runners/lib/polish/autofix/strengthen-types.js +245 -0
- package/bin/runners/lib/polish/backend-checks.js +148 -0
- package/bin/runners/lib/polish/documentation-checks.js +111 -0
- package/bin/runners/lib/polish/frontend-checks.js +168 -0
- package/bin/runners/lib/polish/index.js +71 -0
- package/bin/runners/lib/polish/infrastructure-checks.js +131 -0
- package/bin/runners/lib/polish/library-detection.js +175 -0
- package/bin/runners/lib/polish/performance-checks.js +100 -0
- package/bin/runners/lib/polish/security-checks.js +148 -0
- package/bin/runners/lib/polish/utils.js +203 -0
- package/bin/runners/lib/prompt-builder.js +540 -0
- package/bin/runners/lib/proof-certificate.js +634 -0
- package/bin/runners/lib/reality/accessibility-audit.js +946 -0
- package/bin/runners/lib/reality/api-contract-validator.js +1012 -0
- package/bin/runners/lib/reality/chaos-engineering.js +1084 -0
- package/bin/runners/lib/reality/performance-tracker.js +1077 -0
- package/bin/runners/lib/reality/scenario-generator.js +1404 -0
- package/bin/runners/lib/reality/visual-regression.js +852 -0
- package/bin/runners/lib/reality-profiler.js +717 -0
- package/bin/runners/lib/replay/flight-recorder-viewer.js +1160 -0
- package/bin/runners/lib/review/ai-code-review.js +832 -0
- package/bin/runners/lib/rules/custom-rule-engine.js +985 -0
- package/bin/runners/lib/sbom-generator.js +641 -0
- package/bin/runners/lib/scan-output-enhanced.js +512 -0
- package/bin/runners/lib/scan-output.js +65 -19
- package/bin/runners/lib/security/owasp-scanner.js +939 -0
- package/bin/runners/lib/ship-output.js +18 -25
- package/bin/runners/lib/terminal-ui.js +113 -1
- package/bin/runners/lib/unified-cli-output.js +603 -430
- package/bin/runners/lib/upsell.js +90 -338
- package/bin/runners/lib/validators/contract-validator.js +283 -0
- package/bin/runners/lib/validators/dead-export-detector.js +279 -0
- package/bin/runners/lib/validators/dep-audit.js +245 -0
- package/bin/runners/lib/validators/env-validator.js +319 -0
- package/bin/runners/lib/validators/index.js +120 -0
- package/bin/runners/lib/validators/license-checker.js +252 -0
- package/bin/runners/lib/validators/route-validator.js +290 -0
- package/bin/runners/runAIAgent.js +5 -10
- package/bin/runners/runAgent.js +3 -0
- package/bin/runners/runApprove.js +1233 -1200
- package/bin/runners/runAuth.js +22 -1
- package/bin/runners/runAuthority.js +528 -0
- package/bin/runners/runCheckpoint.js +4 -24
- package/bin/runners/runClassify.js +862 -859
- package/bin/runners/runConductor.js +772 -0
- package/bin/runners/runContainer.js +366 -0
- package/bin/runners/runContext.js +3 -0
- package/bin/runners/runDoctor.js +28 -41
- package/bin/runners/runEasy.js +410 -0
- package/bin/runners/runFirewall.js +3 -0
- package/bin/runners/runFirewallHook.js +3 -0
- package/bin/runners/runFix.js +76 -66
- package/bin/runners/runGuard.js +411 -18
- package/bin/runners/runIaC.js +372 -0
- package/bin/runners/runInit.js +10 -60
- package/bin/runners/runMcp.js +11 -12
- package/bin/runners/runPolish.js +240 -64
- package/bin/runners/runPromptFirewall.js +5 -12
- package/bin/runners/runProve.js +20 -55
- package/bin/runners/runReality.js +68 -59
- package/bin/runners/runReport.js +31 -5
- package/bin/runners/runRuntime.js +5 -8
- package/bin/runners/runScan.js +194 -1273
- package/bin/runners/runShip.js +695 -47
- package/bin/runners/runTruth.js +3 -0
- package/bin/runners/runValidate.js +7 -11
- package/bin/runners/runVibe.js +791 -0
- package/bin/runners/runWatch.js +14 -23
- package/bin/vibecheck.js +179 -65
- package/mcp-server/index.js +202 -636
- package/mcp-server/lib/api-client.cjs +7 -299
- package/mcp-server/package.json +1 -1
- package/mcp-server/tier-auth.js +175 -574
- package/mcp-server/tools-v3.js +800 -505
- package/mcp-server/tools.js +495 -0
- package/package.json +1 -1
- package/bin/runners/lib/engines/vibecheck-engines/lib/ast-cache.js +0 -164
- package/bin/runners/lib/engines/vibecheck-engines/lib/code-quality-engine.js +0 -291
- package/bin/runners/lib/engines/vibecheck-engines/lib/console-logs-engine.js +0 -83
- package/bin/runners/lib/engines/vibecheck-engines/lib/dead-code-engine.js +0 -198
- package/bin/runners/lib/engines/vibecheck-engines/lib/deprecated-api-engine.js +0 -275
- package/bin/runners/lib/engines/vibecheck-engines/lib/empty-catch-engine.js +0 -167
- package/bin/runners/lib/engines/vibecheck-engines/lib/file-filter.js +0 -217
- package/bin/runners/lib/engines/vibecheck-engines/lib/mock-data-engine.js +0 -140
- package/bin/runners/lib/engines/vibecheck-engines/lib/parallel-processor.js +0 -164
- package/bin/runners/lib/engines/vibecheck-engines/lib/performance-issues-engine.js +0 -234
- package/bin/runners/lib/engines/vibecheck-engines/lib/type-aware-engine.js +0 -217
- package/bin/runners/lib/engines/vibecheck-engines/lib/unsafe-regex-engine.js +0 -78
- package/mcp-server/index-v1.js +0 -698
|
@@ -0,0 +1,512 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Enhanced Scan Output - Beautiful scan results with charts and Pro upselling
|
|
3
|
+
*
|
|
4
|
+
* Integrates:
|
|
5
|
+
* - Verdict cards with health scores
|
|
6
|
+
* - Severity distribution charts
|
|
7
|
+
* - Category breakdown
|
|
8
|
+
* - Trend analysis (if baseline exists)
|
|
9
|
+
* - Pro feature upselling
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
"use strict";
|
|
13
|
+
|
|
14
|
+
const {
|
|
15
|
+
ansi,
|
|
16
|
+
sym,
|
|
17
|
+
box,
|
|
18
|
+
padRight,
|
|
19
|
+
padLeft,
|
|
20
|
+
center,
|
|
21
|
+
truncate,
|
|
22
|
+
visibleLength,
|
|
23
|
+
progressBar,
|
|
24
|
+
renderTable,
|
|
25
|
+
renderSectionHeader,
|
|
26
|
+
renderSuccess,
|
|
27
|
+
renderWarning,
|
|
28
|
+
renderInfo,
|
|
29
|
+
renderBullet,
|
|
30
|
+
formatDuration,
|
|
31
|
+
getTierBadge,
|
|
32
|
+
WIDTH,
|
|
33
|
+
} = require("./unified-cli-output");
|
|
34
|
+
|
|
35
|
+
const {
|
|
36
|
+
horizontalBarChart,
|
|
37
|
+
severityBreakdown,
|
|
38
|
+
sparkline,
|
|
39
|
+
trendIndicator,
|
|
40
|
+
statsSummary,
|
|
41
|
+
healthGauge,
|
|
42
|
+
} = require("./cli-charts");
|
|
43
|
+
|
|
44
|
+
const {
|
|
45
|
+
verdictCard,
|
|
46
|
+
compactVerdict,
|
|
47
|
+
comparisonCard,
|
|
48
|
+
quickSummary,
|
|
49
|
+
} = require("./cli-scan-cards");
|
|
50
|
+
|
|
51
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
52
|
+
// CONFIGURATION
|
|
53
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
54
|
+
|
|
55
|
+
const PRO_FEATURES = {
|
|
56
|
+
autofix: {
|
|
57
|
+
name: 'Auto-Fix',
|
|
58
|
+
description: 'AI-powered automatic code fixes',
|
|
59
|
+
command: 'vibecheck fix --apply',
|
|
60
|
+
},
|
|
61
|
+
reality: {
|
|
62
|
+
name: 'Reality Proof',
|
|
63
|
+
description: 'Runtime verification with Playwright',
|
|
64
|
+
command: 'vibecheck scan --reality',
|
|
65
|
+
},
|
|
66
|
+
badge: {
|
|
67
|
+
name: 'Status Badge',
|
|
68
|
+
description: 'Embeddable health badge for README',
|
|
69
|
+
command: 'vibecheck ship --badge',
|
|
70
|
+
},
|
|
71
|
+
ci: {
|
|
72
|
+
name: 'CI Integration',
|
|
73
|
+
description: 'GitHub Actions & CI/CD workflows',
|
|
74
|
+
command: 'vibecheck init --ci',
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
79
|
+
// MAIN ENHANCED OUTPUT
|
|
80
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Render enhanced scan output with charts and upselling
|
|
84
|
+
* @param {Object} result - Scan result
|
|
85
|
+
* @param {Object} options - Display options
|
|
86
|
+
*/
|
|
87
|
+
function renderEnhancedScanOutput(result, options = {}) {
|
|
88
|
+
const {
|
|
89
|
+
tier = 'free',
|
|
90
|
+
showCharts = true,
|
|
91
|
+
showUpsell = true,
|
|
92
|
+
baseline = null,
|
|
93
|
+
compact = false,
|
|
94
|
+
json = false,
|
|
95
|
+
} = options;
|
|
96
|
+
|
|
97
|
+
// Extract data
|
|
98
|
+
const {
|
|
99
|
+
verdict = 'WARN',
|
|
100
|
+
score = 0,
|
|
101
|
+
findings = [],
|
|
102
|
+
duration = 0,
|
|
103
|
+
filesScanned = 0,
|
|
104
|
+
projectName = '',
|
|
105
|
+
} = normalizeResult(result);
|
|
106
|
+
|
|
107
|
+
// JSON output
|
|
108
|
+
if (json) {
|
|
109
|
+
return JSON.stringify({
|
|
110
|
+
verdict,
|
|
111
|
+
score,
|
|
112
|
+
findings: findings.length,
|
|
113
|
+
duration,
|
|
114
|
+
filesScanned,
|
|
115
|
+
severityCounts: getSeverityCounts(findings),
|
|
116
|
+
}, null, 2);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Compact mode
|
|
120
|
+
if (compact) {
|
|
121
|
+
console.log(compactVerdict({ verdict, score, findings, duration }));
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const lines = [];
|
|
126
|
+
const isPro = tier === 'pro';
|
|
127
|
+
const hasIssues = findings.length > 0;
|
|
128
|
+
|
|
129
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
130
|
+
// 1. VERDICT CARD
|
|
131
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
132
|
+
|
|
133
|
+
lines.push('');
|
|
134
|
+
lines.push(verdictCard({ verdict, score, findings, duration, filesScanned, projectName }));
|
|
135
|
+
|
|
136
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
137
|
+
// 2. SEVERITY DISTRIBUTION CHART
|
|
138
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
139
|
+
|
|
140
|
+
if (showCharts && hasIssues) {
|
|
141
|
+
const sevCounts = getSeverityCounts(findings);
|
|
142
|
+
|
|
143
|
+
lines.push('');
|
|
144
|
+
lines.push(renderSectionHeader('Issue Distribution', sym.chart));
|
|
145
|
+
|
|
146
|
+
// Severity bar chart
|
|
147
|
+
lines.push(horizontalBarChart([
|
|
148
|
+
{ label: 'Critical', value: sevCounts.critical, color: ansi.red },
|
|
149
|
+
{ label: 'High', value: sevCounts.high, color: ansi.yellow },
|
|
150
|
+
{ label: 'Medium', value: sevCounts.medium, color: ansi.yellow },
|
|
151
|
+
{ label: 'Low', value: sevCounts.low, color: ansi.blue },
|
|
152
|
+
].filter(item => item.value > 0), { width: 25, showPercent: true }));
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
156
|
+
// 3. CATEGORY BREAKDOWN
|
|
157
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
158
|
+
|
|
159
|
+
if (showCharts && hasIssues) {
|
|
160
|
+
const categoryCounts = getCategoryCounts(findings);
|
|
161
|
+
const topCategories = Object.entries(categoryCounts)
|
|
162
|
+
.sort((a, b) => b[1] - a[1])
|
|
163
|
+
.slice(0, 5);
|
|
164
|
+
|
|
165
|
+
if (topCategories.length > 0) {
|
|
166
|
+
lines.push('');
|
|
167
|
+
lines.push(renderSectionHeader('By Category', sym.folder));
|
|
168
|
+
|
|
169
|
+
lines.push(horizontalBarChart(
|
|
170
|
+
topCategories.map(([cat, count]) => ({
|
|
171
|
+
label: formatCategory(cat),
|
|
172
|
+
value: count,
|
|
173
|
+
color: ansi.cyan,
|
|
174
|
+
})),
|
|
175
|
+
{ width: 25, showPercent: false }
|
|
176
|
+
));
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
181
|
+
// 4. BASELINE COMPARISON (if available)
|
|
182
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
183
|
+
|
|
184
|
+
if (baseline && showCharts) {
|
|
185
|
+
lines.push('');
|
|
186
|
+
lines.push(renderSectionHeader('vs Previous Scan', sym.chart));
|
|
187
|
+
lines.push(comparisonCard(
|
|
188
|
+
{ score: baseline.score, findings: baseline.findings || [] },
|
|
189
|
+
{ score, findings }
|
|
190
|
+
));
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
194
|
+
// 5. TOP FINDINGS TABLE
|
|
195
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
196
|
+
|
|
197
|
+
if (hasIssues) {
|
|
198
|
+
lines.push('');
|
|
199
|
+
lines.push(renderSectionHeader(`Top Issues (${Math.min(findings.length, 8)} of ${findings.length})`, sym.warning));
|
|
200
|
+
lines.push(renderFindingsTable(findings.slice(0, 8)));
|
|
201
|
+
|
|
202
|
+
if (findings.length > 8) {
|
|
203
|
+
lines.push(` ${ansi.dim}... and ${findings.length - 8} more issues${ansi.reset}`);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
208
|
+
// 6. PRO UPSELL SECTION
|
|
209
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
210
|
+
|
|
211
|
+
if (showUpsell && !isPro) {
|
|
212
|
+
lines.push('');
|
|
213
|
+
lines.push(renderProUpsell(findings, verdict, tier));
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
217
|
+
// 7. NEXT STEPS
|
|
218
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
219
|
+
|
|
220
|
+
lines.push('');
|
|
221
|
+
lines.push(renderNextSteps(verdict, hasIssues, isPro));
|
|
222
|
+
|
|
223
|
+
// Output
|
|
224
|
+
console.log(lines.join('\n'));
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
228
|
+
// PRO UPSELL
|
|
229
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
230
|
+
|
|
231
|
+
function renderProUpsell(findings, verdict, tier) {
|
|
232
|
+
const lines = [];
|
|
233
|
+
const issueCount = findings.length;
|
|
234
|
+
|
|
235
|
+
lines.push(` ${ansi.gray}${box.dTopLeft}${box.dHorizontal.repeat(WIDTH - 6)}${box.dTopRight}${ansi.reset}`);
|
|
236
|
+
lines.push(` ${ansi.gray}${box.dVertical}${ansi.reset}${' '.repeat(WIDTH - 6)}${ansi.gray}${box.dVertical}${ansi.reset}`);
|
|
237
|
+
|
|
238
|
+
// Header
|
|
239
|
+
const headerText = `${ansi.magenta}${sym.star}${ansi.reset} ${ansi.bold}UPGRADE TO PRO${ansi.reset}`;
|
|
240
|
+
lines.push(` ${ansi.gray}${box.dVertical}${ansi.reset}${center(headerText, WIDTH - 6 + 10)}${ansi.gray}${box.dVertical}${ansi.reset}`);
|
|
241
|
+
|
|
242
|
+
lines.push(` ${ansi.gray}${box.dVertical}${ansi.reset}${' '.repeat(WIDTH - 6)}${ansi.gray}${box.dVertical}${ansi.reset}`);
|
|
243
|
+
|
|
244
|
+
// Context-aware upsell messages
|
|
245
|
+
if (issueCount > 0) {
|
|
246
|
+
const fixMsg = ` Fix all ${ansi.bold}${issueCount} issues${ansi.reset} automatically with AI-powered auto-fix`;
|
|
247
|
+
lines.push(` ${ansi.gray}${box.dVertical}${ansi.reset}${fixMsg}${' '.repeat(Math.max(0, WIDTH - 6 - visibleLength(fixMsg)))}${ansi.gray}${box.dVertical}${ansi.reset}`);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
lines.push(` ${ansi.gray}${box.dVertical}${ansi.reset}${' '.repeat(WIDTH - 6)}${ansi.gray}${box.dVertical}${ansi.reset}`);
|
|
251
|
+
|
|
252
|
+
// Feature list
|
|
253
|
+
const features = [
|
|
254
|
+
`${ansi.cyan}${sym.wrench}${ansi.reset} Auto-Fix: AI applies fixes automatically`,
|
|
255
|
+
`${ansi.cyan}${sym.shield}${ansi.reset} Reality Proof: Runtime verification`,
|
|
256
|
+
`${ansi.cyan}${sym.chart}${ansi.reset} CI Integration: GitHub Actions ready`,
|
|
257
|
+
`${ansi.cyan}${sym.star}${ansi.reset} Status Badge: Show health in README`,
|
|
258
|
+
];
|
|
259
|
+
|
|
260
|
+
for (const feature of features) {
|
|
261
|
+
const featureLine = ` ${feature}`;
|
|
262
|
+
lines.push(` ${ansi.gray}${box.dVertical}${ansi.reset}${featureLine}${' '.repeat(Math.max(0, WIDTH - 6 - visibleLength(featureLine)))}${ansi.gray}${box.dVertical}${ansi.reset}`);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
lines.push(` ${ansi.gray}${box.dVertical}${ansi.reset}${' '.repeat(WIDTH - 6)}${ansi.gray}${box.dVertical}${ansi.reset}`);
|
|
266
|
+
|
|
267
|
+
// CTA
|
|
268
|
+
const ctaText = `${ansi.cyan}https://vibecheckai.dev/pricing${ansi.reset}`;
|
|
269
|
+
lines.push(` ${ansi.gray}${box.dVertical}${ansi.reset}${center(`Upgrade ${sym.arrow} ${ctaText}`, WIDTH - 6 + 10)}${ansi.gray}${box.dVertical}${ansi.reset}`);
|
|
270
|
+
|
|
271
|
+
lines.push(` ${ansi.gray}${box.dVertical}${ansi.reset}${' '.repeat(WIDTH - 6)}${ansi.gray}${box.dVertical}${ansi.reset}`);
|
|
272
|
+
lines.push(` ${ansi.gray}${box.dBottomLeft}${box.dHorizontal.repeat(WIDTH - 6)}${box.dBottomRight}${ansi.reset}`);
|
|
273
|
+
|
|
274
|
+
return lines.join('\n');
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
278
|
+
// CONTEXTUAL PRO PROMPTS (for inline use)
|
|
279
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Get contextual Pro upsell message based on current action
|
|
283
|
+
* @param {string} context - Context: 'scan', 'fix', 'ship', etc.
|
|
284
|
+
* @param {Object} data - Context data
|
|
285
|
+
*/
|
|
286
|
+
function getProPrompt(context, data = {}) {
|
|
287
|
+
const prompts = {
|
|
288
|
+
scan_issues: (count) => ({
|
|
289
|
+
message: `${ansi.magenta}${sym.star} PRO${ansi.reset} ${ansi.dim}Auto-fix ${count} issues instantly${ansi.reset}`,
|
|
290
|
+
command: 'vibecheck fix --apply',
|
|
291
|
+
cta: 'Try Pro free for 14 days',
|
|
292
|
+
}),
|
|
293
|
+
scan_clean: () => ({
|
|
294
|
+
message: `${ansi.magenta}${sym.star} PRO${ansi.reset} ${ansi.dim}Generate status badge for your README${ansi.reset}`,
|
|
295
|
+
command: 'vibecheck ship --badge',
|
|
296
|
+
cta: 'Show off your clean code',
|
|
297
|
+
}),
|
|
298
|
+
fix_plan: (count) => ({
|
|
299
|
+
message: `${ansi.magenta}${sym.star} PRO${ansi.reset} ${ansi.dim}Apply all ${count} fixes automatically${ansi.reset}`,
|
|
300
|
+
command: 'vibecheck fix --apply',
|
|
301
|
+
cta: 'Upgrade to apply fixes',
|
|
302
|
+
}),
|
|
303
|
+
ship_warn: () => ({
|
|
304
|
+
message: `${ansi.magenta}${sym.star} PRO${ansi.reset} ${ansi.dim}Get detailed fix suggestions${ansi.reset}`,
|
|
305
|
+
command: 'vibecheck fix --explain',
|
|
306
|
+
cta: 'Understand every issue',
|
|
307
|
+
}),
|
|
308
|
+
ci_setup: () => ({
|
|
309
|
+
message: `${ansi.magenta}${sym.star} PRO${ansi.reset} ${ansi.dim}Add to CI/CD pipeline${ansi.reset}`,
|
|
310
|
+
command: 'vibecheck init --ci',
|
|
311
|
+
cta: 'Automate quality checks',
|
|
312
|
+
}),
|
|
313
|
+
};
|
|
314
|
+
|
|
315
|
+
const key = context;
|
|
316
|
+
const promptFn = prompts[key];
|
|
317
|
+
|
|
318
|
+
if (!promptFn) return null;
|
|
319
|
+
|
|
320
|
+
const prompt = typeof promptFn === 'function'
|
|
321
|
+
? promptFn(data.count || data.issueCount || 0)
|
|
322
|
+
: promptFn;
|
|
323
|
+
|
|
324
|
+
return prompt;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Render inline Pro prompt
|
|
329
|
+
* @param {string} context - Context
|
|
330
|
+
* @param {Object} data - Data
|
|
331
|
+
*/
|
|
332
|
+
function renderInlineProPrompt(context, data = {}) {
|
|
333
|
+
const prompt = getProPrompt(context, data);
|
|
334
|
+
if (!prompt) return '';
|
|
335
|
+
|
|
336
|
+
return `
|
|
337
|
+
${prompt.message}
|
|
338
|
+
${ansi.dim}${sym.arrow} ${prompt.command}${ansi.reset}
|
|
339
|
+
${ansi.dim}${prompt.cta} at ${ansi.cyan}vibecheckai.dev${ansi.reset}
|
|
340
|
+
`;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
344
|
+
// NEXT STEPS
|
|
345
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
346
|
+
|
|
347
|
+
function renderNextSteps(verdict, hasIssues, isPro) {
|
|
348
|
+
const lines = [];
|
|
349
|
+
|
|
350
|
+
lines.push(` ${ansi.cyan}${sym.pointer}${ansi.reset} ${ansi.bold}Next Steps${ansi.reset}`);
|
|
351
|
+
lines.push(` ${ansi.gray}${box.horizontal.repeat(50)}${ansi.reset}`);
|
|
352
|
+
|
|
353
|
+
if (verdict === 'SHIP' || verdict === 'PASS') {
|
|
354
|
+
lines.push(` ${ansi.green}${sym.rocket}${ansi.reset} Your code is ready to ship!`);
|
|
355
|
+
if (!isPro) {
|
|
356
|
+
lines.push(` ${ansi.dim}${sym.arrow} Run ${ansi.cyan}vibecheck ship --badge${ansi.reset}${ansi.dim} to generate a status badge${ansi.reset} ${ansi.magenta}[PRO]${ansi.reset}`);
|
|
357
|
+
}
|
|
358
|
+
lines.push(` ${ansi.dim}${sym.arrow} Run ${ansi.cyan}vibecheck guard${ansi.reset}${ansi.dim} to validate AI-generated code${ansi.reset}`);
|
|
359
|
+
} else if (hasIssues) {
|
|
360
|
+
if (!isPro) {
|
|
361
|
+
lines.push(` ${ansi.dim}${sym.arrow} Run ${ansi.cyan}vibecheck fix --plan${ansi.reset}${ansi.dim} to see fix suggestions${ansi.reset}`);
|
|
362
|
+
lines.push(` ${ansi.dim}${sym.arrow} Run ${ansi.cyan}vibecheck fix --apply${ansi.reset}${ansi.dim} to auto-fix issues${ansi.reset} ${ansi.magenta}[PRO]${ansi.reset}`);
|
|
363
|
+
} else {
|
|
364
|
+
lines.push(` ${ansi.dim}${sym.arrow} Run ${ansi.cyan}vibecheck fix --apply${ansi.reset}${ansi.dim} to auto-fix issues${ansi.reset}`);
|
|
365
|
+
}
|
|
366
|
+
lines.push(` ${ansi.dim}${sym.arrow} Run ${ansi.cyan}vibecheck scan --baseline${ansi.reset}${ansi.dim} to track progress${ansi.reset}`);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
lines.push('');
|
|
370
|
+
return lines.join('\n');
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
374
|
+
// FINDINGS TABLE
|
|
375
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
376
|
+
|
|
377
|
+
function renderFindingsTable(findings) {
|
|
378
|
+
const sevIcons = {
|
|
379
|
+
critical: `${ansi.red}${sym.critical}${ansi.reset}`,
|
|
380
|
+
high: `${ansi.yellow}${sym.high}${ansi.reset}`,
|
|
381
|
+
medium: `${ansi.yellow}${sym.medium}${ansi.reset}`,
|
|
382
|
+
low: `${ansi.blue}${sym.low}${ansi.reset}`,
|
|
383
|
+
info: `${ansi.gray}${sym.info}${ansi.reset}`,
|
|
384
|
+
};
|
|
385
|
+
|
|
386
|
+
return renderTable({
|
|
387
|
+
columns: [
|
|
388
|
+
{
|
|
389
|
+
header: 'Sev',
|
|
390
|
+
key: 'severity',
|
|
391
|
+
width: 8,
|
|
392
|
+
format: (v) => sevIcons[v] || sevIcons.info,
|
|
393
|
+
},
|
|
394
|
+
{ header: 'Category', key: 'category', width: 14, format: formatCategory },
|
|
395
|
+
{ header: 'Issue', key: 'message', width: 45 },
|
|
396
|
+
],
|
|
397
|
+
data: findings.map(f => ({
|
|
398
|
+
severity: normalizeSeverity(f.severity),
|
|
399
|
+
category: f.category || f.type || 'Other',
|
|
400
|
+
message: f.message || f.title || f.description || '',
|
|
401
|
+
})),
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
406
|
+
// UTILITIES
|
|
407
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
408
|
+
|
|
409
|
+
function normalizeResult(result) {
|
|
410
|
+
// Handle different result structures
|
|
411
|
+
if (result.verdict && typeof result.verdict === 'object') {
|
|
412
|
+
return {
|
|
413
|
+
verdict: result.verdict.verdict || 'WARN',
|
|
414
|
+
score: result.verdict.score || calculateScore(result.findings || []),
|
|
415
|
+
findings: result.findings || [],
|
|
416
|
+
duration: result.timings?.total || result.duration || 0,
|
|
417
|
+
filesScanned: result.timings?.filesScanned || result.scannedFiles || 0,
|
|
418
|
+
projectName: result.projectName || '',
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
return {
|
|
423
|
+
verdict: result.verdict || 'WARN',
|
|
424
|
+
score: result.score || calculateScore(result.findings || []),
|
|
425
|
+
findings: result.findings || [],
|
|
426
|
+
duration: result.duration || result.timings?.total || 0,
|
|
427
|
+
filesScanned: result.filesScanned || result.scannedFiles || 0,
|
|
428
|
+
projectName: result.projectName || '',
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
function calculateScore(findings) {
|
|
433
|
+
if (!findings || findings.length === 0) return 100;
|
|
434
|
+
|
|
435
|
+
const deductions = findings.reduce((sum, f) => {
|
|
436
|
+
const sev = normalizeSeverity(f.severity);
|
|
437
|
+
if (sev === 'critical') return sum + 25;
|
|
438
|
+
if (sev === 'high') return sum + 15;
|
|
439
|
+
if (sev === 'medium') return sum + 5;
|
|
440
|
+
if (sev === 'low') return sum + 2;
|
|
441
|
+
return sum;
|
|
442
|
+
}, 0);
|
|
443
|
+
|
|
444
|
+
return Math.max(0, 100 - deductions);
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
function normalizeSeverity(severity) {
|
|
448
|
+
if (!severity) return 'medium';
|
|
449
|
+
const sev = String(severity).toLowerCase();
|
|
450
|
+
if (sev === 'block' || sev === 'critical') return 'critical';
|
|
451
|
+
if (sev === 'high') return 'high';
|
|
452
|
+
if (sev === 'warn' || sev === 'warning' || sev === 'medium') return 'medium';
|
|
453
|
+
if (sev === 'info' || sev === 'low') return 'low';
|
|
454
|
+
return 'medium';
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
function getSeverityCounts(findings) {
|
|
458
|
+
const counts = { critical: 0, high: 0, medium: 0, low: 0, info: 0 };
|
|
459
|
+
|
|
460
|
+
for (const f of findings) {
|
|
461
|
+
const sev = normalizeSeverity(f.severity);
|
|
462
|
+
counts[sev] = (counts[sev] || 0) + 1;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
return counts;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
function getCategoryCounts(findings) {
|
|
469
|
+
const counts = {};
|
|
470
|
+
|
|
471
|
+
for (const f of findings) {
|
|
472
|
+
const cat = f.category || f.type || f.ruleId || 'Other';
|
|
473
|
+
counts[cat] = (counts[cat] || 0) + 1;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
return counts;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
function formatCategory(cat) {
|
|
480
|
+
const categoryLabels = {
|
|
481
|
+
'EnvContract': 'Env Vars',
|
|
482
|
+
'MissingRoute': 'Routes',
|
|
483
|
+
'GhostAuth': 'Auth',
|
|
484
|
+
'FakeSuccess': 'Fake APIs',
|
|
485
|
+
'MockData': 'Mock Data',
|
|
486
|
+
'Secrets': 'Secrets',
|
|
487
|
+
'ConsoleLog': 'Console',
|
|
488
|
+
'TodoFixme': 'TODOs',
|
|
489
|
+
'CodeQuality': 'Quality',
|
|
490
|
+
'Security': 'Security',
|
|
491
|
+
'Performance': 'Performance',
|
|
492
|
+
};
|
|
493
|
+
|
|
494
|
+
return categoryLabels[cat] || cat;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
498
|
+
// EXPORTS
|
|
499
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
500
|
+
|
|
501
|
+
module.exports = {
|
|
502
|
+
renderEnhancedScanOutput,
|
|
503
|
+
renderProUpsell,
|
|
504
|
+
renderInlineProPrompt,
|
|
505
|
+
getProPrompt,
|
|
506
|
+
renderNextSteps,
|
|
507
|
+
renderFindingsTable,
|
|
508
|
+
normalizeResult,
|
|
509
|
+
calculateScore,
|
|
510
|
+
getSeverityCounts,
|
|
511
|
+
getCategoryCounts,
|
|
512
|
+
};
|
|
@@ -245,39 +245,38 @@ function formatScanOutput(result, options = {}) {
|
|
|
245
245
|
});
|
|
246
246
|
|
|
247
247
|
const categoryLabels = {
|
|
248
|
-
'EnvContract': '🔐 Env
|
|
248
|
+
'EnvContract': '🔐 Env',
|
|
249
249
|
'MissingRoute': '🔗 Routes',
|
|
250
|
-
'GhostAuth': '🛡️
|
|
251
|
-
'FakeSuccess': '⚠️
|
|
252
|
-
'MockData': '🎭
|
|
250
|
+
'GhostAuth': '🛡️ Auth',
|
|
251
|
+
'FakeSuccess': '⚠️ Fake',
|
|
252
|
+
'MockData': '🎭 Mocks',
|
|
253
253
|
'Secrets': '🔑 Secrets',
|
|
254
|
-
'ConsoleLog': '📝
|
|
254
|
+
'ConsoleLog': '📝 Logs',
|
|
255
255
|
'TodoFixme': '📋 TODOs',
|
|
256
|
+
'CodeQuality': '✨ Quality',
|
|
257
|
+
'Security': '⚡ Security',
|
|
258
|
+
'Performance': '⚡ Perf',
|
|
256
259
|
};
|
|
257
260
|
|
|
258
261
|
const summaryItems = Object.entries(categoryCounts)
|
|
259
262
|
.sort((a, b) => b[1] - a[1])
|
|
260
263
|
.slice(0, 6)
|
|
261
264
|
.map(([cat, count]) => {
|
|
262
|
-
const label = categoryLabels[cat] ||
|
|
265
|
+
const label = categoryLabels[cat] || cat;
|
|
263
266
|
return `${label}: ${chalk.bold}${count}${chalk.reset}`;
|
|
264
267
|
});
|
|
265
268
|
|
|
266
269
|
if (summaryItems.length > 0) {
|
|
267
|
-
//
|
|
268
|
-
const
|
|
269
|
-
const prefix = `${chalk.dim}Findings by Category:${chalk.reset} `;
|
|
270
|
-
const maxLineLength = WIDTH - 10; // Leave some margin
|
|
270
|
+
// Simple left-aligned format with consistent spacing
|
|
271
|
+
const indent = ' ';
|
|
271
272
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
lines.push(`${chalk.gray}${BOX.vertical}${chalk.reset}${
|
|
279
|
-
} else {
|
|
280
|
-
lines.push(`${chalk.gray}${BOX.vertical}${chalk.reset}${padCenter(`${prefix}${categoryText}`, WIDTH - 2)}${chalk.gray}${BOX.vertical}${chalk.reset}`);
|
|
273
|
+
// Always split into 2 rows of 3 for clean alignment
|
|
274
|
+
const row1 = summaryItems.slice(0, 3).join(' ');
|
|
275
|
+
const row2 = summaryItems.slice(3, 6).join(' ');
|
|
276
|
+
|
|
277
|
+
lines.push(`${chalk.gray}${BOX.vertical}${chalk.reset}${indent}${chalk.dim}FINDINGS:${chalk.reset} ${row1}${' '.repeat(Math.max(0, WIDTH - row1.length - 14))}${chalk.gray}${BOX.vertical}${chalk.reset}`);
|
|
278
|
+
if (row2) {
|
|
279
|
+
lines.push(`${chalk.gray}${BOX.vertical}${chalk.reset}${indent}${' '.repeat(10)}${row2}${' '.repeat(Math.max(0, WIDTH - row2.length - 14))}${chalk.gray}${BOX.vertical}${chalk.reset}`);
|
|
281
280
|
}
|
|
282
281
|
lines.push(`${chalk.gray}${BOX.vertical}${chalk.reset}${' '.repeat(WIDTH - 2)}${chalk.gray}${BOX.vertical}${chalk.reset}`);
|
|
283
282
|
}
|
|
@@ -511,8 +510,52 @@ function renderLayers(layers) {
|
|
|
511
510
|
return ''; // Implement if needed
|
|
512
511
|
}
|
|
513
512
|
|
|
513
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
514
|
+
// ENHANCED OUTPUT (with charts and Pro upselling)
|
|
515
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
516
|
+
|
|
517
|
+
let enhancedOutput;
|
|
518
|
+
try {
|
|
519
|
+
enhancedOutput = require('./scan-output-enhanced');
|
|
520
|
+
} catch (e) {
|
|
521
|
+
enhancedOutput = null;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
/**
|
|
525
|
+
* Format scan output with enhanced visuals (charts, upsell)
|
|
526
|
+
* Falls back to standard output if enhanced module not available
|
|
527
|
+
*/
|
|
528
|
+
function formatScanOutputEnhanced(result, options = {}) {
|
|
529
|
+
if (enhancedOutput && !options.legacy) {
|
|
530
|
+
enhancedOutput.renderEnhancedScanOutput(result, options);
|
|
531
|
+
return ''; // Output is printed directly
|
|
532
|
+
}
|
|
533
|
+
return formatScanOutput(result, options);
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
/**
|
|
537
|
+
* Get Pro upsell prompt for current context
|
|
538
|
+
*/
|
|
539
|
+
function getProUpsellPrompt(context, data = {}) {
|
|
540
|
+
if (enhancedOutput) {
|
|
541
|
+
return enhancedOutput.getProPrompt(context, data);
|
|
542
|
+
}
|
|
543
|
+
return null;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
/**
|
|
547
|
+
* Render inline Pro upsell
|
|
548
|
+
*/
|
|
549
|
+
function renderProUpsell(context, data = {}) {
|
|
550
|
+
if (enhancedOutput) {
|
|
551
|
+
return enhancedOutput.renderInlineProPrompt(context, data);
|
|
552
|
+
}
|
|
553
|
+
return '';
|
|
554
|
+
}
|
|
555
|
+
|
|
514
556
|
module.exports = {
|
|
515
557
|
formatScanOutput,
|
|
558
|
+
formatScanOutputEnhanced,
|
|
516
559
|
calculateScore,
|
|
517
560
|
getExitCode,
|
|
518
561
|
printError,
|
|
@@ -522,4 +565,7 @@ module.exports = {
|
|
|
522
565
|
renderBreakdown,
|
|
523
566
|
renderBlockers,
|
|
524
567
|
renderLayers,
|
|
568
|
+
// Pro upsell
|
|
569
|
+
getProUpsellPrompt,
|
|
570
|
+
renderProUpsell,
|
|
525
571
|
};
|