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