UNPKG

@paulohenriquevn/m2js

Version:

Transform TypeScript/JavaScript code into LLM-friendly Markdown summaries + Smart Dead Code Detection + Graph-Deep Diff Analysis. Extract exported functions, classes, and JSDoc comments for better AI context with 60%+ token reduction. Intelligent dead cod

399 lines (383 loc) • 16.5 kB
"use strict"; /** * CLI interface for Graph-Deep Diff Analysis * LLM-friendly reporting with rich context and impact scoring */ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.getGraphDiffHelpText = getGraphDiffHelpText; exports.executeGraphDiffAnalysis = executeGraphDiffAnalysis; const chalk_1 = __importDefault(require("chalk")); const graph_diff_analyzer_1 = require("./graph-diff-analyzer"); /** * Get detailed help text for graph diff analysis */ function getGraphDiffHelpText() { return ` ${chalk_1.default.bold.blue('šŸ” M2JS Graph-Deep Diff Analysis')} ${chalk_1.default.bold('PURPOSE:')} Compare architectural states between git references to detect problematic changes and track technical debt evolution over time. ${chalk_1.default.bold('BASIC USAGE:')} m2js <path> --graph-diff --baseline <ref> m2js <path> --graph-diff --baseline main --current feature-branch m2js <path> --graph-diff --baseline HEAD~1 --format json ${chalk_1.default.bold('OPTIONS:')} --baseline <ref> Baseline git reference (required) Examples: main, HEAD~1, v1.0.0, commit-hash --current <ref> Current reference (default: working directory) Examples: feature-branch, HEAD, working files --format <type> Output format: table (default), json --min-severity <level> Filter by severity: low, medium, high, critical --include-details Include detailed change analysis (default: true) --include-impact Include impact scoring (default: true) --include-suggestions Include improvement suggestions (default: true) ${chalk_1.default.bold('DETECTION CAPABILITIES:')} šŸ”„ Circular Dependencies - New/resolved circular imports šŸ”— Coupling Changes - Average dependencies per module šŸ“¦ External Dependencies - NPM package additions/removals šŸ—ļø Layer Violations - Architecture boundary violations 🧩 Complexity Hotspots - High-coupling modules šŸ“ Architecture Layers - New/removed structural layers ${chalk_1.default.bold('OUTPUT INFORMATION:')} • Severity Distribution - Critical, High, Medium, Low changes • Category Breakdown - Dependencies, Architecture, Coupling, etc. • Health Score Tracking - 0-100 architectural health metric • Impact Analysis - Maintainability, Performance, Testability • Actionable Recommendations - Prioritized improvement steps ${chalk_1.default.bold('EXAMPLES:')} # Compare current branch to main m2js src/ --graph-diff --baseline main # Compare two specific commits m2js . --graph-diff --baseline v1.0.0 --current v1.1.0 # Focus on critical issues only m2js src/ --graph-diff --baseline HEAD~5 --min-severity high # JSON output for CI/CD integration m2js . --graph-diff --baseline main --format json > architecture-report.json ${chalk_1.default.bold('CI/CD INTEGRATION:')} # Use in PR checks to prevent architectural regression m2js src/ --graph-diff --baseline origin/main --format json | jq '.impact.healthChange.delta' ${chalk_1.default.bold('COMMON WORKFLOWS:')} 1. Pre-merge validation - Compare feature branch to main 2. Release preparation - Compare current to last stable version 3. Refactoring tracking - Monitor architectural improvements 4. Technical debt audit - Analyze historical architectural changes ${chalk_1.default.green('šŸ’” Pro Tip:')} Use --baseline HEAD~1 for commit-by-commit analysis ${chalk_1.default.green('šŸ’” Pro Tip:')} Set up git hooks to track architectural health automatically `; } /** * Execute graph diff analysis with LLM-friendly output */ async function executeGraphDiffAnalysis(projectPath, options) { const startTime = Date.now(); try { console.log(chalk_1.default.blue('šŸ” Starting Graph-Deep Diff Analysis...')); console.log(chalk_1.default.gray(`Comparing ${options.baseline} → ${options.current || 'current'}`)); // Perform analysis const report = await (0, graph_diff_analyzer_1.analyzeGraphDiff)(projectPath, options); // Output results based on format if (options.format === 'json') { console.log(JSON.stringify(report, null, 2)); } else { await displayTableReport(report, options); } const duration = Date.now() - startTime; console.log(chalk_1.default.gray(`\nā±ļø Analysis completed in ${duration}ms`)); } catch (error) { console.error(chalk_1.default.red(`āŒ Graph diff analysis failed: ${error.message}`)); process.exit(1); } } /** * Display analysis results in table format (LLM-friendly) */ async function displayTableReport(report, options) { console.log('\n' + chalk_1.default.bold.blue('šŸ“Š GRAPH-DEEP DIFF ANALYSIS REPORT')); console.log(chalk_1.default.blue('='.repeat(60))); // Comparison overview displayComparisonOverview(report); // Impact summary displayImpactSummary(report.impact); // Key metrics changes displayMetricsChanges(report); // Architectural changes if (options.includeDetails !== false) { displayArchitecturalChanges(report.changes, options); } // Recommendations if (options.includeSuggestions !== false) { displayRecommendations(report.recommendations); } // Health score analysis displayHealthScoreAnalysis(report.impact); } /** * Display comparison overview */ function displayComparisonOverview(report) { console.log('\n' + chalk_1.default.bold.yellow('šŸ“‹ COMPARISON OVERVIEW')); console.log(chalk_1.default.yellow('-'.repeat(40))); console.log(`${chalk_1.default.cyan('Project:')} ${report.projectPath}`); console.log(`${chalk_1.default.cyan('Baseline:')} ${report.comparison.baseline}`); console.log(`${chalk_1.default.cyan('Current:')} ${report.comparison.current}`); console.log(`${chalk_1.default.cyan('Analyzed:')} ${report.comparison.timestamp.toISOString()}`); console.log(`${chalk_1.default.cyan('Total Changes:')} ${report.changes.length}`); } /** * Display impact summary with severity distribution */ function displayImpactSummary(impact) { console.log('\n' + chalk_1.default.bold.red('šŸŽÆ IMPACT SUMMARY')); console.log(chalk_1.default.red('-'.repeat(40))); // Severity breakdown console.log(chalk_1.default.bold('Severity Distribution:')); const severities = ['critical', 'high', 'medium', 'low']; severities.forEach(severity => { const count = impact.bySeverity[severity] || 0; const color = getSeverityColor(severity); const icon = getSeverityIcon(severity); console.log(` ${icon} ${color(severity.toUpperCase())}: ${count} changes`); }); // Category breakdown console.log(chalk_1.default.bold('\nCategory Distribution:')); Object.entries(impact.byCategory).forEach(([category, count]) => { const icon = getCategoryIcon(category); console.log(` ${icon} ${chalk_1.default.cyan(category)}: ${count} changes`); }); // Health score change const healthDelta = impact.healthChange.delta; const healthColor = healthDelta >= 0 ? chalk_1.default.green : chalk_1.default.red; const healthIcon = healthDelta >= 0 ? 'šŸ“ˆ' : 'šŸ“‰'; console.log(`\n${healthIcon} ${chalk_1.default.bold('Health Score:')} ${impact.healthChange.before} → ${impact.healthChange.after} (${healthColor(healthDelta > 0 ? '+' : '')}${healthDelta.toFixed(1)})`); } /** * Display key metrics changes */ function displayMetricsChanges(report) { console.log('\n' + chalk_1.default.bold.green('šŸ“ˆ KEY METRICS CHANGES')); console.log(chalk_1.default.green('-'.repeat(40))); const metrics = report.impact.keyMetrics; displayMetricChange('Circular Dependencies', metrics.circularDependencies.before, metrics.circularDependencies.after, metrics.circularDependencies.delta, 'šŸ”„'); displayMetricChange('Average Coupling', metrics.averageCoupling.before, metrics.averageCoupling.after, metrics.averageCoupling.delta, 'šŸ”—', 1); displayMetricChange('External Dependencies', metrics.externalDependencies.before, metrics.externalDependencies.after, metrics.externalDependencies.delta, 'šŸ“¦'); displayMetricChange('Module Count', metrics.moduleCount.before, metrics.moduleCount.after, metrics.moduleCount.delta, 'šŸ“'); } /** * Display individual metric change */ function displayMetricChange(name, before, after, delta, icon, decimals = 0) { const deltaColor = delta > 0 ? chalk_1.default.red : delta < 0 ? chalk_1.default.green : chalk_1.default.gray; const deltaSign = delta > 0 ? '+' : ''; const beforeStr = decimals > 0 ? before.toFixed(decimals) : before.toString(); const afterStr = decimals > 0 ? after.toFixed(decimals) : after.toString(); const deltaStr = decimals > 0 ? delta.toFixed(decimals) : delta.toString(); console.log(`${icon} ${chalk_1.default.cyan(name)}: ${beforeStr} → ${afterStr} (${deltaColor(deltaSign)}${deltaColor(deltaStr)})`); } /** * Display architectural changes with filtering */ function displayArchitecturalChanges(changes, options) { if (changes.length === 0) { console.log('\n' + chalk_1.default.bold.green('āœ… NO ARCHITECTURAL CHANGES DETECTED')); return; } console.log('\n' + chalk_1.default.bold.magenta('šŸ—ļø ARCHITECTURAL CHANGES')); console.log(chalk_1.default.magenta('-'.repeat(40))); // Filter by minimum severity if specified let filteredChanges = changes; if (options.minSeverity) { const severityOrder = ['low', 'medium', 'high', 'critical']; const minIndex = severityOrder.indexOf(options.minSeverity); filteredChanges = changes.filter(change => severityOrder.indexOf(change.severity) >= minIndex); } // Sort by severity (critical first) const severityOrder = ['critical', 'high', 'medium', 'low']; filteredChanges.sort((a, b) => severityOrder.indexOf(a.severity) - severityOrder.indexOf(b.severity)); filteredChanges.forEach((change, index) => { displayArchitecturalChange(change, index + 1, options); }); if (filteredChanges.length < changes.length) { const hiddenCount = changes.length - filteredChanges.length; console.log(chalk_1.default.gray(`\n... and ${hiddenCount} more lower-severity changes`)); } } /** * Display individual architectural change */ function displayArchitecturalChange(change, index, options) { const severityColor = getSeverityColor(change.severity); const severityIcon = getSeverityIcon(change.severity); const categoryIcon = getCategoryIcon(change.category); console.log(`\n${index}. ${severityIcon} ${severityColor(change.severity.toUpperCase())} - ${categoryIcon} ${change.category}`); console.log(` ${chalk_1.default.bold(change.description)}`); if (options?.includeImpact !== false) { const impact = change.impact; console.log(` ${chalk_1.default.cyan('Risk:')} ${impact.riskLevel} | ${chalk_1.default.cyan('Overall Score:')} ${impact.overallScore > 0 ? '+' : ''}${impact.overallScore}`); console.log(` ${chalk_1.default.cyan('Reasoning:')} ${impact.reasoning}`); if (impact.affectedAreas.length > 0) { console.log(` ${chalk_1.default.cyan('Affected Areas:')} ${impact.affectedAreas.join(', ')}`); } } if (change.affected.length > 0) { console.log(` ${chalk_1.default.cyan('Affected Modules:')} ${change.affected.slice(0, 3).join(', ')}${change.affected.length > 3 ? '...' : ''}`); } } /** * Display recommendations */ function displayRecommendations(recommendations) { if (recommendations.length === 0) { return; } console.log('\n' + chalk_1.default.bold.blue('šŸ’” RECOMMENDATIONS')); console.log(chalk_1.default.blue('-'.repeat(40))); // Sort by priority const priorityOrder = ['critical', 'high', 'medium', 'low']; const sortedRecs = recommendations.sort((a, b) => priorityOrder.indexOf(a.priority) - priorityOrder.indexOf(b.priority)); sortedRecs.forEach((rec, index) => { displayRecommendation(rec, index + 1); }); } /** * Display individual recommendation */ function displayRecommendation(rec, index) { const priorityColor = getSeverityColor(rec.priority); const typeIcon = getRecommendationTypeIcon(rec.type); const effortIcon = getEffortIcon(rec.effort); console.log(`\n${index}. ${typeIcon} ${priorityColor(rec.priority.toUpperCase())} - ${rec.title}`); console.log(` ${chalk_1.default.gray(rec.description)}`); console.log(` ${effortIcon} ${chalk_1.default.cyan('Effort:')} ${rec.effort} | ${chalk_1.default.cyan('Impact:')} ${rec.expectedImpact}`); if (rec.actions.length > 0) { console.log(` ${chalk_1.default.cyan('Actions:')}`); rec.actions.forEach(action => { console.log(` • ${action}`); }); } } /** * Display health score analysis */ function displayHealthScoreAnalysis(impact) { console.log('\n' + chalk_1.default.bold.cyan('šŸ„ ARCHITECTURAL HEALTH ANALYSIS')); console.log(chalk_1.default.cyan('-'.repeat(40))); const healthDelta = impact.healthChange.delta; const currentHealth = impact.healthChange.after; let healthStatus = ''; let recommendations = ''; if (currentHealth >= 80) { healthStatus = chalk_1.default.green('EXCELLENT'); recommendations = 'Continue following good practices'; } else if (currentHealth >= 60) { healthStatus = chalk_1.default.yellow('GOOD'); recommendations = 'Minor improvements recommended'; } else if (currentHealth >= 40) { healthStatus = chalk_1.default.yellow('FAIR'); recommendations = 'Significant refactoring needed'; } else { healthStatus = chalk_1.default.red('POOR'); recommendations = 'Critical architectural issues require immediate attention'; } console.log(`${chalk_1.default.cyan('Current Health:')} ${currentHealth.toFixed(1)}/100 (${healthStatus})`); console.log(`${chalk_1.default.cyan('Health Trend:')} ${healthDelta >= 0 ? 'šŸ“ˆ Improving' : 'šŸ“‰ Declining'} (${healthDelta > 0 ? '+' : ''}${healthDelta.toFixed(1)})`); console.log(`${chalk_1.default.cyan('Recommendation:')} ${recommendations}`); } /** * Get color for severity level */ function getSeverityColor(severity) { switch (severity) { case 'critical': return chalk_1.default.red.bold; case 'high': return chalk_1.default.red; case 'medium': return chalk_1.default.yellow; case 'low': return chalk_1.default.green; default: return chalk_1.default.gray; } } /** * Get icon for severity level */ function getSeverityIcon(severity) { switch (severity) { case 'critical': return '🚨'; case 'high': return 'āš ļø'; case 'medium': return '⚔'; case 'low': return 'šŸ’”'; default: return 'šŸ“'; } } /** * Get icon for category */ function getCategoryIcon(category) { switch (category) { case 'dependencies': return 'šŸ”—'; case 'architecture': return 'šŸ—ļø'; case 'coupling': return 'šŸ”€'; case 'complexity': return '🧩'; case 'external': return 'šŸ“¦'; case 'performance': return '⚔'; case 'maintainability': return 'šŸ”§'; default: return 'šŸ“‹'; } } /** * Get icon for recommendation type */ function getRecommendationTypeIcon(type) { switch (type) { case 'fix-issue': return 'šŸ”§'; case 'improve-architecture': return 'šŸ—ļø'; case 'refactor': return 'ā™»ļø'; case 'monitor': return 'šŸ‘€'; default: return 'šŸ’”'; } } /** * Get icon for effort level */ function getEffortIcon(effort) { switch (effort) { case 'low': return '🟢'; case 'medium': return '🟔'; case 'high': return 'šŸ”“'; default: return '⚪'; } } //# sourceMappingURL=graph-diff-cli.js.map