vibe-code-build
Version:
Real-time code monitoring with teaching explanations, CLAUDE.md compliance checking, and interactive chat
402 lines (325 loc) âĸ 12.4 kB
JavaScript
import chalk from 'chalk';
import boxen from 'boxen';
export class CheckFormatter {
constructor() {
this.severityColors = {
critical: chalk.bgRed.white.bold,
high: chalk.red.bold,
medium: chalk.yellow,
low: chalk.gray,
info: chalk.blue,
passed: chalk.green,
failed: chalk.red,
warning: chalk.yellow,
skipped: chalk.gray
};
this.severityIcons = {
critical: 'đ¨',
high: 'â',
medium: 'â ī¸',
low: 'đĄ',
info: 'âšī¸',
passed: 'â
',
failed: 'â',
warning: 'â ī¸',
skipped: 'âī¸'
};
}
formatCheckResults(allResults) {
const sections = [];
sections.push(this.formatHeader());
sections.push(this.formatSummary(allResults));
for (const [category, results] of Object.entries(allResults)) {
if (results) {
sections.push(this.formatCategory(category, results));
}
}
sections.push(this.formatFooter(allResults));
return sections.filter(Boolean).join('\n\n');
}
formatHeader() {
const title = chalk.bold.cyan('đ Vibe Code Quality Report');
return boxen(title, {
padding: 1,
margin: 1,
borderStyle: 'round',
borderColor: 'cyan'
});
}
formatSummary(allResults) {
const stats = this.calculateStats(allResults);
const summaryLines = [
chalk.bold('đ Summary'),
'',
`Total Checks: ${stats.totalChecks}`,
`${this.severityIcons.passed} Passed: ${stats.passed}`,
`${this.severityIcons.failed} Failed: ${stats.failed}`,
`${this.severityIcons.warning} Warnings: ${stats.warnings}`,
`${this.severityIcons.skipped} Skipped: ${stats.skipped}`,
'',
this.formatHealthScore(stats)
];
return boxen(summaryLines.join('\n'), {
padding: 1,
borderStyle: 'single',
borderColor: this.getHealthColor(stats.healthScore)
});
}
formatCategory(category, results) {
const categoryTitle = this.formatCategoryTitle(category);
const sections = [chalk.bold(categoryTitle)];
if (results.formatResults && typeof results.formatResults === 'function') {
sections.push(results.formatResults());
} else {
for (const [check, result] of Object.entries(results)) {
if (result && typeof result === 'object') {
sections.push(this.formatCheckResult(check, result));
}
}
}
return sections.join('\n');
}
formatCheckResult(checkName, result) {
const status = result.status || 'unknown';
const icon = this.severityIcons[status] || 'â';
const color = this.severityColors[status] || chalk.white;
const lines = [
color(`${icon} ${this.formatCheckName(checkName)}: ${result.message || 'No message'}`)
];
// Special handling for SEO results with score and grade
if (checkName === 'seo' && result.score !== undefined) {
lines.push(this.formatSEOScore(result));
// Show category breakdown
if (result.categories) {
lines.push(chalk.bold(' đ Category Breakdown:'));
Object.entries(result.categories).forEach(([category, data]) => {
if (data && data.score !== undefined) {
const catColor = this.getScoreColor(data.score);
lines.push(catColor(` âĸ ${this.capitalize(category)}: ${data.score}/100 - ${data.message || ''}`));
}
});
}
}
if (result.findings && result.findings.length > 0) {
lines.push(this.formatFindings(result.findings.slice(0, 5)));
if (result.findings.length > 5) {
lines.push(chalk.gray(` ... and ${result.findings.length - 5} more`));
}
}
// Enhanced handling for SEO issues with explanations
if (result.issues && result.issues.length > 0) {
lines.push(this.formatSEOIssues(result.issues.slice(0, 5)));
if (result.issues.length > 5) {
lines.push(chalk.gray(` ... and ${result.issues.length - 5} more issues`));
}
}
if (result.recommendations && result.recommendations.length > 0) {
if (checkName === 'seo' && result.recommendations[0].priority) {
lines.push(this.formatSEORecommendations(result.recommendations.slice(0, 3)));
} else {
lines.push(chalk.gray(' đĄ Recommendations:'));
result.recommendations.slice(0, 3).forEach(rec => {
lines.push(chalk.gray(` âĸ ${rec}`));
});
}
}
if (result.summary) {
lines.push(this.formatResultSummary(result.summary));
}
return lines.join('\n');
}
formatFindings(findings) {
return findings.map(finding => {
const severity = finding.severity || 'info';
const color = this.severityColors[severity] || chalk.white;
const icon = this.severityIcons[severity] || 'âĸ';
if (finding.file && finding.line) {
return color(` ${icon} ${finding.file}:${finding.line} - ${finding.description || finding.message || finding.type}`);
} else if (finding.file) {
return color(` ${icon} ${finding.file} - ${finding.description || finding.message || finding.type}`);
} else {
return color(` ${icon} ${finding.description || finding.message || finding.type || 'Issue found'}`);
}
}).join('\n');
}
formatResultSummary(summary) {
const lines = [' Summary:'];
for (const [key, value] of Object.entries(summary)) {
if (typeof value === 'number' && value > 0) {
const severity = key.toLowerCase();
const color = this.severityColors[severity] || chalk.white;
const icon = this.severityIcons[severity] || 'âĸ';
lines.push(color(` ${icon} ${key}: ${value}`));
}
}
return lines.join('\n');
}
formatFooter(allResults) {
const stats = this.calculateStats(allResults);
const recommendations = this.getOverallRecommendations(allResults);
const footerLines = [];
if (recommendations.length > 0) {
footerLines.push(chalk.bold('đ¯ Top Recommendations:'));
recommendations.slice(0, 5).forEach((rec, i) => {
footerLines.push(`${i + 1}. ${rec}`);
});
}
footerLines.push('');
footerLines.push(chalk.gray(`Generated at ${new Date().toLocaleString()}`));
return boxen(footerLines.join('\n'), {
padding: 1,
borderStyle: 'single',
borderColor: 'gray'
});
}
calculateStats(allResults) {
let totalChecks = 0;
let passed = 0;
let failed = 0;
let warnings = 0;
let skipped = 0;
for (const category of Object.values(allResults)) {
if (!category) continue;
for (const result of Object.values(category)) {
if (!result || typeof result !== 'object' || !result.status) continue;
totalChecks++;
switch (result.status) {
case 'passed': passed++; break;
case 'failed': failed++; break;
case 'warning': warnings++; break;
case 'skipped': skipped++; break;
}
}
}
const healthScore = totalChecks > 0 ?
Math.round(((passed + skipped) / totalChecks) * 100) : 0;
return { totalChecks, passed, failed, warnings, skipped, healthScore };
}
formatHealthScore(stats) {
const score = stats.healthScore;
const color = this.getHealthColor(score);
const emoji = score >= 90 ? 'đ' : score >= 70 ? 'đ' : score >= 50 ? 'đ' : 'â ī¸';
return color(`${emoji} Health Score: ${score}%`);
}
getHealthColor(score) {
if (score >= 90) return chalk.green;
if (score >= 70) return chalk.yellow;
if (score >= 50) return chalk.red;
return chalk.red;
}
formatCategoryTitle(category) {
const titles = {
build: 'đ¨ Build & Compilation',
dependencies: 'đĻ Dependencies',
claude: 'đ¤ CLAUDE.md Compliance',
security: 'đ Security',
performance: '⥠Performance & SEO'
};
return titles[category] || category.charAt(0).toUpperCase() + category.slice(1);
}
formatCheckName(checkName) {
return checkName
.replace(/([A-Z])/g, ' $1')
.replace(/^./, str => str.toUpperCase())
.trim();
}
getOverallRecommendations(allResults) {
const recommendations = [];
if (allResults.security?.godMode?.status === 'failed') {
recommendations.push('đ¨ Critical: Remove god mode patterns from your code immediately');
}
if (allResults.security?.secrets?.status === 'failed') {
recommendations.push('đ Critical: Remove exposed secrets and add them to .gitignore');
}
if (allResults.dependencies?.vulnerabilities?.status === 'failed') {
recommendations.push('đĻ Run "npm audit fix" to resolve dependency vulnerabilities');
}
if (allResults.build?.build?.status === 'failed') {
recommendations.push('đ¨ Fix build errors before deploying');
}
if (allResults.performance?.bundleSize?.totalSize > 10 * 1024 * 1024) {
recommendations.push('đ Reduce bundle size with code splitting and tree shaking');
}
if (allResults.claude?.claudeMdExists?.status === 'failed') {
recommendations.push('đ Create a CLAUDE.md file to document AI guidelines');
}
if (allResults.performance?.seo?.totalIssues > 5) {
recommendations.push('đ Improve SEO by adding meta tags and alt text');
}
return recommendations;
}
formatCompactResults(allResults) {
const stats = this.calculateStats(allResults);
const status = stats.failed > 0 ? 'â' : stats.warnings > 0 ? 'â ī¸' : 'â
';
return `${status} Checks: ${stats.passed}/${stats.totalChecks} passed | ${stats.failed} failed | ${stats.warnings} warnings`;
}
formatJsonResults(allResults) {
const stats = this.calculateStats(allResults);
return JSON.stringify({
timestamp: new Date().toISOString(),
stats,
results: allResults,
recommendations: this.getOverallRecommendations(allResults)
}, null, 2);
}
formatSEOScore(result) {
const score = result.score || 0;
const grade = result.grade || 'N/A';
const color = this.getScoreColor(score);
return color.bold(` đ¯ SEO Score: ${score}/100 (${grade})`);
}
formatSEOIssues(issues) {
const lines = [' đ Issues Found:'];
issues.forEach(issue => {
const severity = issue.severity || 'info';
const color = this.severityColors[severity] || chalk.white;
const icon = this.severityIcons[severity] || 'âĸ';
lines.push(color(` ${icon} ${issue.message || issue.type}`));
if (issue.file) {
lines.push(chalk.gray(` đ ${issue.file}`));
}
if (issue.explanation) {
lines.push(chalk.gray(` âšī¸ ${issue.explanation}`));
}
if (issue.recommendation) {
lines.push(chalk.cyan(` đĄ ${issue.recommendation}`));
}
if (issue.example) {
lines.push(chalk.gray(` đ Example: ${issue.example.split('\n')[0]}...`));
}
});
return lines.join('\n');
}
formatSEORecommendations(recommendations) {
const lines = [' đ¯ Priority Actions:'];
recommendations.forEach((rec, index) => {
const priorityColors = {
critical: chalk.bgRed.white.bold,
high: chalk.red.bold,
medium: chalk.yellow,
low: chalk.gray
};
const color = priorityColors[rec.priority] || chalk.white;
lines.push(color(` ${index + 1}. ${rec.title}`));
lines.push(chalk.gray(` ${rec.description}`));
if (rec.timeframe) {
lines.push(chalk.green(` âąī¸ ${rec.timeframe}`));
}
if (rec.impact) {
lines.push(chalk.cyan(` đĄ Impact: ${rec.impact}`));
}
});
return lines.join('\n');
}
getScoreColor(score) {
if (score >= 90) return chalk.green.bold;
if (score >= 80) return chalk.green;
if (score >= 70) return chalk.yellow;
if (score >= 60) return chalk.yellow.dim;
if (score >= 50) return chalk.red;
return chalk.red.bold;
}
capitalize(str) {
return str.charAt(0).toUpperCase() + str.slice(1);
}
}