agentsqripts
Version:
Comprehensive static code analysis toolkit for identifying technical debt, security vulnerabilities, performance issues, and code quality problems
214 lines (192 loc) ⢠9.03 kB
JavaScript
#!/usr/bin/env node
/**
* @file Command-line interface for performance analysis and optimization opportunity detection
* @description Single responsibility: Provide interactive CLI for identifying performance bottlenecks
*
* This CLI tool serves as the performance analysis interface for the AgentSqripts platform,
* detecting algorithmic inefficiencies, I/O bottlenecks, memory issues, and concurrency
* optimization opportunities. It implements severity filtering, category-based analysis,
* and effort-based prioritization to support performance optimization workflows.
*
* Design rationale:
* - Performance-focused CLI design enables systematic optimization workflows
* - Category filtering allows targeted optimization efforts (Algorithm, I/O, Memory, etc.)
* - Effort-level filtering helps prioritize quick wins vs complex refactoring
* - Quantified impact metrics enable ROI-based optimization decisions
* - Multiple output formats support both human analysis and automated tooling
*/
const fs = require('fs');
const path = require('path');
const { analyzeProjectPerformance, analyzeFilePerformance } = require('../lib/performance/analyzePerformance');
const { formatResults, getGradeEmoji, formatSeverity } = require('./lib/commonFormatters');
const { createHelpFunction } = require('./lib/helpFormatter');
const { parseArgs: sharedParseArgs } = require('./lib/argumentParser');
const { handleAnalysisError, exitOnSeverityThreshold } = require('./lib/errorHandler');
const { createSummaryFormatter, createRecommendationsSection, createTopIssuesSection } = require('./lib/summaryFormatter');
const { createDetailedFormatter, formatIssueWithIcons } = require('./lib/detailedFormatter');
/**
* Display help information
*/
const showHelp = createHelpFunction({
command: 'analyze-performance.js',
description: 'Analyzes code for performance bottlenecks including O(n²) algorithmic patterns, blocking sync I/O,\nJSON operations in loops, serial awaits, large inline objects, and other optimization opportunities.',
options: [
{ flag: '--extensions <exts>', description: 'Comma-separated list of file extensions (default: .js,.ts,.jsx,.tsx)' },
{ flag: '--output-format <fmt>', description: 'Output format: json, summary, detailed (default: summary)' },
{ flag: '--severity <level>', description: 'Minimum severity to report: LOW, MEDIUM, HIGH (default: LOW)' },
{ flag: '--category <cat>', description: 'Filter by category: Algorithm, I/O, CPU, Memory, Concurrency (default: all)' },
{ flag: '--effort <level>', description: 'Filter by effort level: 1, 2, 3 (default: all)' },
{ flag: '--help', description: 'Show this help message' }
],
modes: ' file Analyze a single file\n project Analyze entire project (default)',
examples: [
'node analyze-performance.js .',
'node analyze-performance.js --severity HIGH src/',
'node analyze-performance.js --output-format json --extensions .js,.ts .',
'node analyze-performance.js --category Algorithm --effort 1 src/'
],
output: ' The tool identifies performance bottlenecks by checking:\n - O(n²) algorithmic patterns (nested loops, inefficient array methods)\n - Blocking synchronous I/O operations (fs.readFileSync, execSync)\n - JSON operations in hot paths (stringify/parse in loops)\n - Serial await chains that could be parallelized\n - Large inline objects and strings (memory optimization)\n - Unbounded array growth patterns\n - Inefficient regex compilation in loops',
sections: {
'ISSUE TYPES': ' š„ HIGH: Major bottlenecks with significant performance impact\n ā” MEDIUM: Moderate optimization opportunities\n š” LOW: Minor performance improvements',
'CATEGORIES': ' š§® Algorithm: Computational complexity issues\n š¾ I/O: Input/output bottlenecks\n š§ CPU: CPU-intensive operations\n š§ Memory: Memory usage optimization\n ā” Concurrency: Parallelization opportunities',
'EFFORT LEVELS': ' 1: Quick wins (easy fixes)\n 2: Moderate effort required\n 3: Complex refactoring needed'
}
});
// Removed duplicate formatResults - now using shared version from commonFormatters
// Create custom summary formatter using shared utility
const formatSummary = createSummaryFormatter({
title: 'š Performance Analysis Results',
scoreField: 'performanceScore',
gradeField: 'performanceGrade',
filesField: 'filesAnalyzed',
issuesField: 'totalIssues',
showEffort: true,
effortField: 'totalEffort',
customMetrics: [
{ field: 'filesWithIssues', icon: 'ā ļø', label: 'Files with Issues' }
],
breakdowns: [
{
field: 'severityBreakdown',
type: 'severity',
icon: 'š',
title: 'Issue Breakdown'
},
{
field: 'categoryBreakdown',
type: 'category',
icon: 'š·ļø',
title: 'Category Breakdown',
icons: {
'Algorithm': 'š§®',
'I/O': 'š¾',
'CPU': 'š§',
'Memory': 'š§ ',
'Concurrency': 'ā”'
}
}
],
customSections: [
createRecommendationsSection(),
createTopIssuesSection({
field: 'topIssues',
title: 'Top Performance Issues',
limit: 20,
formatItem: (issue, i) => {
const severityIcon = issue.severity === 'HIGH' ? 'š„' :
issue.severity === 'MEDIUM' ? 'ā”' : 'š”';
const categoryIcon = issue.category === 'Algorithm' ? 'š§®' :
issue.category === 'I/O' ? 'š¾' :
issue.category === 'CPU' ? 'š§' :
issue.category === 'Memory' ? 'š§ ' : 'ā”';
return ` ${i + 1}. ${severityIcon} ${categoryIcon} ${issue.type}\n š ${issue.summary}\n š” ${issue.recommendation}\n ā±ļø Effort: ${issue.effort}/3 | šÆ Impact: ${issue.impact}`;
}
})
]
});
// Old formatSummary implementation fully removed - using shared formatter
// Old formatDetailed removed - using shared detailed formatter
// Replace with shared detailed formatter
const formatDetailedNew = createDetailedFormatter({
title: 'š Detailed Performance Analysis',
formatSummary: formatSummary,
filesField: 'files',
issuesField: 'issues',
formatFile: (fileData) => {
return `š ${path.relative('.', fileData.file)} (Score: ${fileData.performanceScore}/100, Effort: ${fileData.metrics.totalEffort})\n`;
},
formatIssue: (issue) => {
const icons = {
severity: { 'HIGH': 'š„', 'MEDIUM': 'ā”', 'LOW': 'š”' },
category: { 'Algorithm': 'š§®', 'I/O': 'š¾', 'CPU': 'š§', 'Memory': 'š§ ', 'Concurrency': 'ā”' }
};
const lines = formatIssueWithIcons(issue, icons);
// Add code snippet if available
if (issue.code) {
lines.splice(1, 0, ` š Code: ${issue.code}`);
}
return lines;
}
});
/**
* Main function
*/
async function main() {
const { getProcessArgs } = require('../lib/utils/processHelpers');
const args = getProcessArgs();
const { shouldShowHelp } = require('../lib/utils/cliHelpers');
if (shouldShowHelp(args)) {
showHelp();
return;
}
// Parse arguments using shared parser
const { options: parsedOptions, targetPath: parsedPath, error } = sharedParseArgs(args, {
defaults: {
extensions: ['.js', '.ts', '.jsx', '.tsx'],
outputFormat: 'summary',
severity: 'LOW',
category: null,
effort: null
},
flags: {
extensions: { type: 'list' },
outputFormat: { type: 'string' },
severity: { type: 'string', transform: (v) => v.toUpperCase() },
category: { type: 'string' },
effort: { type: 'string' }
}
});
if (error) {
const { logAnalysisError } = require('../lib/utils/errorMessages');
logAnalysisError('performance', error);
process.exit(1);
}
const { extensions, outputFormat, severity, category, effort } = parsedOptions;
const targetPath = parsedPath;
try {
// Check if target is a file or directory
const stat = await fs.promises.stat(targetPath);
let results;
if (stat.isFile()) {
// Analyze single file
results = await analyzeFilePerformance(targetPath);
} else {
// Analyze project
results = await analyzeProjectPerformance(targetPath, {
extensions,
excludePatterns: ['node_modules', '.git', 'dist', 'build', 'coverage'],
maxFiles: Infinity // Enable unlimited file processing
});
}
// Output results
const formatted = formatResults(results, outputFormat, { severity, category, effort }, formatSummary, formatDetailedNew);
console.log(formatted);
// Exit with error code if high-impact issues found
exitOnSeverityThreshold(results);
} catch (error) {
handleAnalysisError(error, 'performance');
}
}
if (require.main === module) {
main().catch(error => { console.error("Fatal error:", error); process.exit(1); });
}