UNPKG

design-agent

Version:

Universal AI Design Review Agent - CLI tool for scanning code for design drift

188 lines (153 loc) 5.21 kB
/** * Performance monitoring utilities for design agent * Tracks performance metrics and suggests optimizations */ export function trackPerformance() { const startTime = Date.now(); const metrics = { startTime, endTime: null, duration: 0, filesScanned: 0, findingsGenerated: 0, adaptersUsed: [], memoryUsage: process.memoryUsage() }; return { start: () => { metrics.startTime = Date.now(); metrics.memoryUsage = process.memoryUsage(); }, end: () => { metrics.endTime = Date.now(); metrics.duration = metrics.endTime - metrics.startTime; return metrics; }, addFile: () => { metrics.filesScanned++; }, addFinding: () => { metrics.findingsGenerated++; }, addAdapter: (adapter) => { if (!metrics.adaptersUsed.includes(adapter)) { metrics.adaptersUsed.push(adapter); } }, getMetrics: () => metrics }; } export function analyzePerformance(metrics) { const analysis = { duration: metrics.duration, filesPerSecond: metrics.filesScanned / (metrics.duration / 1000), findingsPerFile: metrics.findingsGenerated / metrics.filesScanned, memoryUsage: metrics.memoryUsage, recommendations: [] }; // Performance recommendations if (metrics.duration > 30000) { // 30 seconds analysis.recommendations.push('Scan took longer than 30 seconds - consider using delta scan for faster results'); } if (metrics.filesPerSecond < 10) { analysis.recommendations.push('Low file processing speed - consider excluding unnecessary file types'); } if (metrics.findingsPerFile > 20) { analysis.recommendations.push('High findings per file - consider reviewing design system consistency'); } if (metrics.memoryUsage.heapUsed > 100 * 1024 * 1024) { // 100MB analysis.recommendations.push('High memory usage - consider processing files in smaller batches'); } return analysis; } export function generatePerformanceReport(metrics) { const analysis = analyzePerformance(metrics); let report = '## Performance Metrics\n\n'; report += `- **Duration:** ${metrics.duration}ms\n`; report += `- **Files Scanned:** ${metrics.filesScanned}\n`; report += `- **Findings Generated:** ${metrics.findingsGenerated}\n`; report += `- **Files/Second:** ${analysis.filesPerSecond.toFixed(2)}\n`; report += `- **Findings/File:** ${analysis.findingsPerFile.toFixed(2)}\n`; report += `- **Memory Usage:** ${(metrics.memoryUsage.heapUsed / 1024 / 1024).toFixed(2)}MB\n`; report += `- **Adapters Used:** ${metrics.adaptersUsed.join(', ')}\n\n`; if (analysis.recommendations.length > 0) { report += '## Performance Recommendations\n\n'; analysis.recommendations.forEach(rec => { report += `- ${rec}\n`; }); } return report; } export function optimizeScanPaths(paths, config) { const optimized = []; for (const path of paths) { // Skip node_modules and build directories if (path.includes('node_modules') || path.includes('dist') || path.includes('build')) { continue; } // Skip test files if not explicitly included if (path.includes('.test.') || path.includes('.spec.') && !config.includeTests) { continue; } // Skip generated files if (path.includes('generated') || path.includes('.min.')) { continue; } optimized.push(path); } return optimized; } export function suggestOptimizations(findings, config) { const suggestions = []; // Check for too many findings in single file const findingsByFile = {}; findings.forEach(f => { findingsByFile[f.file] = (findingsByFile[f.file] || 0) + 1; }); for (const [file, count] of Object.entries(findingsByFile)) { if (count > 50) { suggestions.push({ type: 'performance', file, message: `File has ${count} findings - consider breaking into smaller components`, severity: 'minor' }); } } // Check for repeated patterns const patterns = {}; findings.forEach(f => { const key = `${f.kind}:${f.msg}`; patterns[key] = (patterns[key] || 0) + 1; }); for (const [pattern, count] of Object.entries(patterns)) { if (count > 10) { suggestions.push({ type: 'consistency', pattern, message: `Pattern "${pattern}" appears ${count} times - consider creating a utility or component`, severity: 'minor' }); } } return suggestions; } export function generateOptimizationReport(suggestions) { if (suggestions.length === 0) { return '## Optimization Report\n\nNo optimization suggestions found.\n'; } let report = '## Optimization Report\n\n'; const byType = {}; suggestions.forEach(s => { if (!byType[s.type]) byType[s.type] = []; byType[s.type].push(s); }); for (const [type, typeSuggestions] of Object.entries(byType)) { report += `### ${type.charAt(0).toUpperCase() + type.slice(1)} Optimizations\n\n`; typeSuggestions.forEach(s => { report += `- **${s.file || 'General'}:** ${s.message}\n`; }); report += '\n'; } return report; }