UNPKG

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
#!/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); }); }