agentsqripts
Version:
Comprehensive static code analysis toolkit for identifying technical debt, security vulnerabilities, performance issues, and code quality problems
273 lines (250 loc) โข 10.7 kB
JavaScript
#!/usr/bin/env node
/**
* @file Command-line interface for static bug analysis and error detection
* @description Single responsibility: Provide interactive CLI for identifying real bugs and logic errors
*
* This CLI tool serves as the static bug analysis interface for the AgentSqripts platform,
* detecting real programming errors, logic bugs, security vulnerabilities, and common
* programming mistakes using AST parsing and pattern matching. It implements severity
* filtering, category-based analysis, and comprehensive reporting to support bug fixing
* workflows and code quality improvement initiatives.
*
* Design rationale:
* - AST-based analysis enables accurate detection without false positives
* - Category filtering enables focused bug fixing efforts by error type
* - Severity-based prioritization helps teams focus on critical issues first
* - Quality metrics provide quantitative assessment of codebase health
* - Comprehensive help system reduces learning curve for bug detection workflows
*/
const fs = require('fs');
const path = require('path');
const { getProcessArgs } = require('../lib/utils/processHelpers');
const { shouldShowHelp } = require('../lib/utils/cliHelpers');
const { analyzeStaticBugs, analyzeProjectStaticBugs } = require('../lib/static-bugs/analyzeStaticBugs');
const { formatResults, getGradeEmoji } = require('./lib/commonFormatters');
const { createDetailedFormatter, formatIssueWithIcons } = require('./lib/detailedFormatter');
const { createHelpFunction } = require('./lib/helpFormatter');
const { parseArgs: sharedParseArgs } = require('./lib/argumentParser');
const { handleAnalysisError, exitOnSeverityThreshold } = require('./lib/errorHandler');
const { createSummaryFormatter, createRecommendationsSection, createTopIssuesSection } = require('./lib/summaryFormatter');
/**
* Display help information
*/
const showHelp = createHelpFunction({
command: 'analyze-static-bugs.js',
description: 'Analyzes JavaScript/TypeScript code for real bugs and logic errors using AST parsing.\nDetects patterns that commonly lead to runtime errors and unexpected behavior.',
options: [
{ flag: '--extensions <exts>', description: 'File extensions to analyze (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: "Type Safety", "Logic Error", "Security", "Error Handling", etc.' },
{ flag: '--help', description: 'Show this help message' }
],
examples: [
'node analyze-static-bugs.js .',
'node analyze-static-bugs.js --extensions .js,.jsx src/',
'node analyze-static-bugs.js --output-format json --severity HIGH .',
'node analyze-static-bugs.js --category "Security" projects/'
],
sections: getBugAnalysisHelpSections()
});
// Extract help sections to avoid large closure
function getBugAnalysisHelpSections() {
return {
'BUG CATEGORIES': getBugCategoriesList(),
'SEVERITY LEVELS': ' ๐ฅ HIGH: Bugs that will cause runtime errors or security issues\n โก MEDIUM: Issues that affect maintainability and can cause problems\n ๐ก LOW: Minor issues and code cleanup opportunities',
'BUG PATTERNS DETECTED': getBugPatternsList(),
'QUALITY METRICS': ' - Quality Score: Overall code quality (0-100, higher is better)\n - Bug Density: Number of issues per file\n - Fix Effort: Estimated effort to resolve all issues\n - Severity Distribution: HIGH/MEDIUM/LOW issue counts',
'FIX RECOMMENDATIONS': ' Each issue includes specific fix suggestions and code examples'
};
}
// Extract bug patterns to avoid large inline string
function getBugPatternsList() {
// Return smaller, split sections to reduce memory footprint
const patterns = [
' - Type coercion bugs (== instead of ===)',
' - eval/Function usage (security risk)',
' - Missing await in async functions',
' - JSON.parse without try/catch',
' - Switch statements without default',
' - Unreachable code after return'
];
return patterns.join('\n');
}
// Extract categories to reduce closure size
function getBugCategoriesList() {
const categories = [
' ๐ Type Safety: == vs ===, NaN comparisons',
' ๐ง Logic Error: Unreachable code, conditions',
' ๐ Security: eval/Function usage',
' โ ๏ธ Error Handling: Missing try/catch',
' ๐ API Usage: parseInt without radix',
' โก Async/Await: Missing await'
];
return categories.join('\n');
}
// Removed duplicate formatResults - now using shared version from commonFormatters
// Create custom summary formatter using shared utility
const formatSummary = createSummaryFormatter({
title: '๐ Static Bug Analysis',
scoreField: 'qualityScore',
gradeField: 'qualityGrade',
filesField: 'filesAnalyzed',
issuesField: 'totalIssues',
showEffort: true,
effortField: 'totalEffort',
breakdowns: [
{
field: 'severityBreakdown',
type: 'severity',
icon: '๐',
title: 'Severity Breakdown'
},
{
field: 'categoryBreakdown',
type: 'category',
icon: '๐ท๏ธ',
title: 'Bug Categories',
icons: {
'Type Safety': '๐',
'Logic Error': '๐ง ',
'Security': '๐',
'Error Handling': 'โ ๏ธ',
'API Usage': '๐',
'Async/Await': 'โก',
'Promise Usage': '๐',
'Control Flow': '๐',
'Array Usage': '๐๏ธ',
'Module Usage': '๐ฆ',
'Dead Code': '๐งน'
}
}
],
customSections: [
createRecommendationsSection(),
createTopIssuesSection({
field: 'issues',
title: 'Top Bugs',
formatItem: (issue, i) => {
const severityIcon = issue.severity === 'HIGH' ? '๐ฅ' :
issue.severity === 'MEDIUM' ? 'โก' : '๐ก';
// Include file path and line number for usability
const filePath = issue.file ? path.relative(process.cwd(), issue.file) : 'Unknown file';
const lineInfo = issue.line ? `:${issue.line}` : '';
const columnInfo = issue.column ? `:${issue.column}` : '';
const location = `${filePath}${lineInfo}${columnInfo}`;
return ` ${i + 1}. ${severityIcon} ${issue.summary || issue.type} (${issue.category}) - ${location}`;
}
})
]
});
// Removed all the duplicate code - formatSummary is already created above using createSummaryFormatter
// Create detailed formatter using shared utility
const formatDetailed = createDetailedFormatter({
formatSummary: formatSummary,
fileField: 'fileResults',
customSections: [
{
title: 'File Analysis Summary',
icon: '๐',
dataField: 'fileResults',
formatter: (fileResults) => {
return fileResults.map(fileResult => {
const fileName = path.relative('.', fileResult.file);
const lines = [` ๐ ${fileName}: ${fileResult.summary.totalIssues} bugs`];
if (fileResult.summary.highSeverity > 0) {
lines.push(` ๐ฅ ${fileResult.summary.highSeverity} HIGH severity`);
}
if (fileResult.summary.mediumSeverity > 0) {
lines.push(` โก ${fileResult.summary.mediumSeverity} MEDIUM severity`);
}
if (fileResult.summary.lowSeverity > 0) {
lines.push(` ๐ก ${fileResult.summary.lowSeverity} LOW severity`);
}
return lines.join('\n');
}).join('\n');
}
},
{
title: 'Bug Details',
icon: '๐',
dataField: 'issues',
limit: 10,
formatter: (issues, limit) => {
return issues.slice(0, limit).map((issue, i) => {
const lines = [
`--- Bug #${i + 1} ---`,
`Type: ${issue.type}`,
`Severity: ${issue.severity}`,
`Category: ${issue.category}`,
`Location: ${issue.file ? path.relative(process.cwd(), issue.file) : 'Unknown file'}${issue.line ? ':' + issue.line : ''}${issue.column ? ':' + issue.column : ''}`,
`Summary: ${issue.summary}`,
`Recommendation: ${issue.recommendation}`,
`Effort: ${issue.effort}/3`,
`Impact: ${issue.impact}`
];
if (issue.operator) lines.push(`Operator: ${issue.operator}`);
if (issue.function) lines.push(`Function: ${issue.function}`);
if (issue.variable) lines.push(`Variable: ${issue.variable}`);
if (issue.method) lines.push(`Method: ${issue.method}`);
if (issue.object) lines.push(`Object: ${issue.object}`);
return lines.join('\n');
}).join('\n\n');
}
}
]
});
/**
* Main function
*/
async function main() {
const args = getProcessArgs();
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
},
flags: {
extensions: { type: 'list' },
outputFormat: { type: 'string' },
severity: { type: 'string', transform: (v) => v.toUpperCase() },
category: { type: 'string' }
}
});
if (error) {
const { logAnalysisError } = require('../lib/utils/errorMessages');
logAnalysisError('static bug', error);
process.exit(1);
}
const { extensions, outputFormat, severity, category } = parsedOptions;
const targetPath = parsedPath;
try {
// Build options
const options = {
extensions,
excludePatterns: ['node_modules', '.git', 'dist', 'build', 'coverage', 'test']
};
// Analyze static bugs
const results = await analyzeStaticBugs(targetPath, options);
// Output results
const formatted = formatResults(results, outputFormat, { severity, category }, formatSummary, formatDetailed);
console.log(formatted);
// Exit with error code if high-severity bugs found
const hasHighSeverityBugs = results.summary && results.summary.severityBreakdown && results.summary.severityBreakdown.HIGH > 0;
if (hasHighSeverityBugs) {
process.exit(1);
}
} catch (error) {
handleAnalysisError(error, 'static bugs');
}
}
if (require.main === module) {
main().catch(error => { console.error("Fatal error:", error); process.exit(1); });
}