design-agent
Version:
Universal AI Design Review Agent - CLI tool for scanning code for design drift
188 lines (153 loc) • 5.21 kB
JavaScript
/**
* 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;
}