UNPKG

agentsqripts

Version:

Comprehensive static code analysis toolkit for identifying technical debt, security vulnerabilities, performance issues, and code quality problems

267 lines (240 loc) 10.6 kB
#!/usr/bin/env node /** * @file Command-line interface for export opportunity analysis and code reusability optimization * @description Single responsibility: Provide interactive CLI for identifying function export promotion opportunities * * This CLI tool serves as the export analysis interface for the AgentSqripts platform, * detecting unexported utility functions and classes that could benefit from being exported * to improve code reusability and API design. It implements utility detection, cross-file * usage analysis, and prioritized recommendations to support refactoring workflows. * * Design rationale: * - Export-focused CLI design enables systematic API improvement workflows * - Utility function detection identifies high-value promotion candidates * - Priority-based recommendations help focus refactoring efforts on maximum impact changes * - Multiple output formats support both manual review and automated refactoring tools * - Cross-file analysis reveals actual usage patterns for informed export decisions */ const fs = require('fs'); const { promises: fsPromises } = require('fs'); const path = require('path'); const { getProcessArgs } = require('../lib/utils/processHelpers'); const { analyzeProjectRefactoring } = require('../lib/export-promotion/projectRefactoringAnalyzer'); const { analyzeFileRefactoring } = require('../lib/export-promotion/fileRefactoringAnalyzer'); const { createHelpFunction } = require('./lib/helpFormatter'); const { createDetailedFormatter, formatIssueWithIcons } = require('./lib/detailedFormatter'); const { parseArgs: sharedParseArgs } = require('./lib/argumentParser'); const { handleAnalysisError, handleFileAccessError } = require('./lib/errorHandler'); /** * Display help information */ const showHelp = createHelpFunction({ command: 'analyze-export-opportunities.js', description: 'Analyzes code for refactoring opportunities including utility function promotion,\nexport optimization, and module organization improvements.', 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: '--include-non-utility', description: 'Include non-utility files in analysis (default: false)' }, { flag: '--include-barrel-files', description: 'Include barrel file analysis (default: true)' }, { flag: '--exclude-barrel-files', description: 'Exclude barrel file analysis' }, { flag: '--target-dirs <dirs>', description: 'Comma-separated list of target utility directories' }, { flag: '--help', description: 'Show this help message' } ], modes: ' file Analyze a single file\n project Analyze entire project (default)', examples: [ 'node analyze-export-opportunities.js .', 'node analyze-export-opportunities.js --include-non-utility src/', 'node analyze-export-opportunities.js --output-format json --target-dirs src/utils,lib .', 'node analyze-export-opportunities.js --exclude-barrel-files --extensions .js,.ts src/' ], output: ' The tool identifies refactoring opportunities by checking:\n - Unexported utility functions that could be promoted to exports\n - Missing barrel files (index.ts) for better import organization\n - Export optimization opportunities in utility directories\n - Module organization improvements', sections: { 'OPPORTUNITY TYPES': ' 📈 PROMOTION: Utility functions that could be exported\n 📁 BARREL: Missing index files for better imports\n 🔧 EXPORT: Export optimization opportunities\n 📦 MODULE: Module organization improvements' } }); const { createSummaryFormatter, createRecommendationsSection, createTopIssuesSection } = require('./lib/summaryFormatter'); const { formatResults } = require('./lib/commonFormatters'); // Create custom summary formatter using shared utility const formatSummary = createSummaryFormatter({ title: '📦 Export Opportunities Analysis', scoreField: 'organizationScore', gradeField: 'organizationGrade', filesField: 'totalFiles', issuesField: 'totalOpportunities', customMetrics: [ { field: 'refactoringRate', icon: '📈', label: 'Organization Rate', suffix: '%' }, { field: 'utilityFunctions', icon: '🔧', label: 'Utility Functions' }, { field: 'utilityFiles', icon: '📁', label: 'Utility Files' }, { field: 'missingBarrelFiles', icon: '📦', label: 'Missing Barrel Files' } ], breakdowns: [ { field: 'opportunityBreakdown', type: 'category', icon: '📊', title: 'Opportunity Types', icons: { 'PROMOTION': '📈', 'BARREL': '📁', 'EXPORT': '🔧', 'MODULE': '📦' } } ], customSections: [ createRecommendationsSection('Top Refactoring Opportunities', '💡'), createTopIssuesSection({ field: 'topOpportunities', title: 'High-Impact Export Opportunities', limit: 10, formatItem: (opp, i) => { const typeIcon = opp.type === 'PROMOTION' ? '📈' : opp.type === 'BARREL' ? '📁' : opp.type === 'EXPORT' ? '🔧' : '📦'; return ` ${i + 1}. ${typeIcon} ${opp.description}\n 📍 ${opp.location}\n 🎯 Impact: ${opp.impact} | ⏱️ Effort: ${opp.effort}/3`; } }) ], footer: { showTimestamp: true, showAnalysisTime: true } }); // Old formatSummary implementation removed - using shared formatter /** * Format detailed output */ // Old formatDetailed removed - using shared formatter const formatDetailed = createDetailedFormatter({ title: '📋 Detailed Opportunity Breakdown', formatSummary: formatSummary, filesField: 'files', issuesField: 'promotionOpportunities', formatFile: (fileData) => { const lines = [ `📄 ${fileData.file}`, ` Utility File: ${fileData.isUtility ? 'Yes' : 'No'}`, ` Exported Items: ${fileData.metrics.exportedCount}`, ` Unexported Items: ${fileData.metrics.unexportedCount}`, ` Opportunities: ${fileData.metrics.promotionOpportunityCount}` ]; return lines.join('\n') + '\n'; }, formatIssue: (opp) => { const emoji = opp.priority === 'HIGH' ? '🚨' : opp.priority === 'MEDIUM' ? '⚠️' : '🔍'; const typeEmoji = opp.type === 'export_promotion' ? '📈' : '📁'; const lines = [ ` ${emoji} ${typeEmoji} [${opp.type.toUpperCase()}] ${opp.reason}`, ` 💡 ${opp.suggestion}` ]; if (opp.item) { lines.push(` 📝 ${opp.item.content}`); } return lines; } }); /** * Parse command line arguments */ function parseArgs() { const toolOptions = { includeNonUtility: { flags: ['--include-non-utility'], boolean: true, default: false, description: 'Include non-utility files in analysis' }, includeBarrelFiles: { flags: ['--include-barrel-files'], boolean: true, default: true, description: 'Include barrel file analysis' }, excludeBarrelFiles: { flags: ['--exclude-barrel-files'], boolean: true, description: 'Exclude barrel file analysis' }, targetDirs: { flags: ['--target-dirs'], parser: (value) => value.split(',').map(dir => dir.trim()), description: 'Comma-separated list of target utility directories' } }; const { targetPath, mode, options } = sharedParseArgs(getProcessArgs(), toolOptions); // Handle exclusion flag if (options.excludeBarrelFiles) options.includeBarrelFiles = false; if (options.help) { showHelp(); process.exit(0); } return { options, targetPath, mode }; } /** * Main execution function */ async function main() { try { const { options, targetPath, mode } = parseArgs(); try { await fsPromises.access(targetPath); } catch (error) { handleFileAccessError(targetPath); } console.log(`🔧 Refactoring analysis for: ${targetPath}`); console.log(`📊 Mode: ${mode}`); console.log(`📁 Extensions: ${options.extensions.join(', ')}`); const includeOptions = []; if (options.includeNonUtility) includeOptions.push('non-utility'); if (options.includeBarrelFiles) includeOptions.push('barrel-files'); console.log(`🔧 Including: ${includeOptions.join(', ')}`); console.log(''); let results; if (mode === 'file') { const analysis = await analyzeFileRefactoring(targetPath, options); if (analysis.error) { handleAnalysisError(analysis.error, 'generic'); } // Convert single file result to project-like format for consistent output results = { timestamp: new Date().toISOString(), summary: { totalFiles: 1, filesWithOpportunities: analysis.metrics.hasRefactoringOpportunities ? 1 : 0, totalOpportunities: analysis.metrics.promotionOpportunityCount, refactoringRate: analysis.metrics.hasRefactoringOpportunities ? 0 : 100, opportunityBreakdown: { export_promotion: analysis.promotionOpportunities.filter(o => o.type === 'export_promotion').length, barrel_file_creation: analysis.promotionOpportunities.filter(o => o.type === 'barrel_file_creation').length }, priorityBreakdown: { HIGH: 0, MEDIUM: 0, LOW: 0 }, utilityStats: { totalUtilityFiles: analysis.isUtility ? 1 : 0, totalExportedItems: analysis.metrics.exportedCount, totalUnexportedItems: analysis.metrics.unexportedCount }, indexFilesFound: analysis.indexFiles.length }, files: analysis.metrics.hasRefactoringOpportunities ? [analysis] : [], indexFiles: [], recommendations: [], analysisTime: 0 }; // Calculate priority breakdown analysis.promotionOpportunities.forEach(opp => { results.summary.priorityBreakdown[opp.priority || 'LOW']++; }); } else { results = await analyzeProjectRefactoring(targetPath, options); } console.log(formatResults(results, options.outputFormat, {}, formatSummary, formatDetailed)); } catch (error) { handleAnalysisError(error, 'generic'); } } // Run the CLI if (require.main === module) { main().catch(error => { console.error("Fatal error:", error); process.exit(1); }); } module.exports = { main, analyzeProjectRefactoring, analyzeFileRefactoring };