UNPKG

smartui-migration-tool

Version:

Enterprise-grade CLI tool for migrating visual testing platforms to LambdaTest SmartUI

1,101 lines (930 loc) 35.3 kB
const { Command, Flags } = require('@oclif/core'); const chalk = require('chalk'); const fs = require('fs-extra'); const path = require('path'); const glob = require('glob'); class SuggestionEngine extends Command { static description = 'Generate intelligent suggestions for code improvement and migration'; static flags = { path: Flags.string({ char: 'p', description: 'Path to analyze (default: current directory)', default: process.cwd() }), include: Flags.string({ char: 'i', description: 'File patterns to include (comma-separated)', default: '**/*.{js,ts,jsx,tsx,py,java,cs}' }), exclude: Flags.string({ char: 'e', description: 'File patterns to exclude (comma-separated)', default: 'node_modules/**,dist/**,build/**,*.min.js' }), output: Flags.string({ char: 'o', description: 'Output file for suggestions', default: 'suggestions.json' }), type: Flags.string({ char: 't', description: 'Type of suggestions to generate', default: 'all', options: ['all', 'quality', 'performance', 'security', 'migration', 'architecture'] }), priority: Flags.string({ char: 'r', description: 'Minimum priority level', default: 'medium', options: ['low', 'medium', 'high', 'critical'] }), verbose: Flags.boolean({ char: 'v', description: 'Enable verbose output', default: false }) }; async run() { const { flags } = await this.parse(SuggestionEngine); console.log(chalk.blue.bold('\n💡 Intelligent Suggestion Engine')); console.log(chalk.gray('Generating smart suggestions for code improvement...\n')); try { // Create suggestion engine const engine = this.createSuggestionEngine(); // Generate suggestions const results = await this.generateSuggestions(flags, engine); // Display results this.displayResults(results, flags.verbose); // Save results if (flags.output) { await fs.writeJson(flags.output, results, { spaces: 2 }); console.log(chalk.green(`\n✅ Suggestions saved to: ${flags.output}`)); } } catch (error) { console.error(chalk.red(`\n❌ Error generating suggestions: ${error.message}`)); this.exit(1); } } createSuggestionEngine() { return { // Code Quality Suggestions generateQualitySuggestions: (files) => { const suggestions = []; files.forEach(file => { if (file.content) { // Complexity suggestions const complexitySuggestions = this.analyzeComplexity(file); suggestions.push(...complexitySuggestions); // Maintainability suggestions const maintainabilitySuggestions = this.analyzeMaintainability(file); suggestions.push(...maintainabilitySuggestions); // Readability suggestions const readabilitySuggestions = this.analyzeReadability(file); suggestions.push(...readabilitySuggestions); // Code style suggestions const styleSuggestions = this.analyzeCodeStyle(file); suggestions.push(...styleSuggestions); } }); return suggestions; }, // Performance Suggestions generatePerformanceSuggestions: (files) => { const suggestions = []; files.forEach(file => { if (file.content) { // Performance anti-patterns const performanceSuggestions = this.analyzePerformance(file); suggestions.push(...performanceSuggestions); // Memory usage suggestions const memorySuggestions = this.analyzeMemoryUsage(file); suggestions.push(...memorySuggestions); // Algorithm efficiency suggestions const algorithmSuggestions = this.analyzeAlgorithms(file); suggestions.push(...algorithmSuggestions); } }); return suggestions; }, // Security Suggestions generateSecuritySuggestions: (files) => { const suggestions = []; files.forEach(file => { if (file.content) { // Security vulnerabilities const securitySuggestions = this.analyzeSecurity(file); suggestions.push(...securitySuggestions); // Input validation suggestions const validationSuggestions = this.analyzeInputValidation(file); suggestions.push(...validationSuggestions); // Authentication suggestions const authSuggestions = this.analyzeAuthentication(file); suggestions.push(...authSuggestions); } }); return suggestions; }, // Migration Suggestions generateMigrationSuggestions: (files) => { const suggestions = []; files.forEach(file => { if (file.content) { // Visual testing migration const visualTestingSuggestions = this.analyzeVisualTesting(file); suggestions.push(...visualTestingSuggestions); // Framework migration const frameworkSuggestions = this.analyzeFrameworkMigration(file); suggestions.push(...frameworkSuggestions); // API migration const apiSuggestions = this.analyzeAPIMigration(file); suggestions.push(...apiSuggestions); } }); return suggestions; }, // Architecture Suggestions generateArchitectureSuggestions: (files) => { const suggestions = []; // Design pattern suggestions const patternSuggestions = this.analyzeDesignPatterns(files); suggestions.push(...patternSuggestions); // Modularity suggestions const modularitySuggestions = this.analyzeModularity(files); suggestions.push(...modularitySuggestions); // Scalability suggestions const scalabilitySuggestions = this.analyzeScalability(files); suggestions.push(...scalabilitySuggestions); return suggestions; }, // Confidence Scoring calculateConfidence: (suggestion) => { let confidence = 0.5; // Base confidence // Increase confidence based on evidence if (suggestion.evidence && suggestion.evidence.length > 0) { confidence += 0.2; } if (suggestion.severity === 'high' || suggestion.severity === 'critical') { confidence += 0.1; } if (suggestion.impact === 'high') { confidence += 0.1; } return Math.min(confidence, 1.0); }, // Priority Ranking calculatePriority: (suggestion) => { let priority = 0; // Base priority from severity const severityMap = { low: 1, medium: 2, high: 3, critical: 4 }; priority += severityMap[suggestion.severity] || 1; // Increase priority based on impact const impactMap = { low: 1, medium: 2, high: 3 }; priority += impactMap[suggestion.impact] || 1; // Increase priority based on confidence priority += suggestion.confidence * 2; return priority; }, // Transformation Suggestions generateTransformations: (suggestion) => { const transformations = []; if (suggestion.type === 'complexity') { transformations.push({ type: 'refactor', description: 'Break down complex function into smaller functions', code: this.generateRefactoringCode(suggestion) }); } else if (suggestion.type === 'performance') { transformations.push({ type: 'optimize', description: 'Optimize performance bottleneck', code: this.generateOptimizationCode(suggestion) }); } else if (suggestion.type === 'security') { transformations.push({ type: 'secure', description: 'Implement security best practices', code: this.generateSecurityCode(suggestion) }); } else if (suggestion.type === 'migration') { transformations.push({ type: 'migrate', description: 'Migrate to recommended solution', code: this.generateMigrationCode(suggestion) }); } return transformations; } }; } async generateSuggestions(flags, engine) { const results = { timestamp: new Date().toISOString(), path: flags.path, type: flags.type, priority: flags.priority, files: [], suggestions: [], summary: {} }; // Find files to analyze const files = await this.findFiles(flags); results.files = files; // Generate suggestions based on type let allSuggestions = []; if (flags.type === 'all' || flags.type === 'quality') { const qualitySuggestions = engine.generateQualitySuggestions(files); allSuggestions.push(...qualitySuggestions); } if (flags.type === 'all' || flags.type === 'performance') { const performanceSuggestions = engine.generatePerformanceSuggestions(files); allSuggestions.push(...performanceSuggestions); } if (flags.type === 'all' || flags.type === 'security') { const securitySuggestions = engine.generateSecuritySuggestions(files); allSuggestions.push(...securitySuggestions); } if (flags.type === 'all' || flags.type === 'migration') { const migrationSuggestions = engine.generateMigrationSuggestions(files); allSuggestions.push(...migrationSuggestions); } if (flags.type === 'all' || flags.type === 'architecture') { const architectureSuggestions = engine.generateArchitectureSuggestions(files); allSuggestions.push(...architectureSuggestions); } // Calculate confidence and priority for each suggestion allSuggestions.forEach(suggestion => { suggestion.confidence = engine.calculateConfidence(suggestion); suggestion.priority = engine.calculatePriority(suggestion); suggestion.transformations = engine.generateTransformations(suggestion); }); // Filter by priority const priorityMap = { low: 1, medium: 2, high: 3, critical: 4 }; const minPriority = priorityMap[flags.priority] || 2; results.suggestions = allSuggestions.filter(s => s.priority >= minPriority); // Sort by priority (descending) results.suggestions.sort((a, b) => b.priority - a.priority); // Generate summary results.summary = this.generateSummary(results.suggestions); return results; } async findFiles(flags) { const includePatterns = flags.include.split(','); const excludePatterns = flags.exclude.split(','); const files = []; for (const pattern of includePatterns) { const matches = glob.sync(pattern, { cwd: flags.path, absolute: true, ignore: excludePatterns }); files.push(...matches.map(file => ({ path: file }))); } // Read file contents for (const file of files) { try { const content = await fs.readFile(file.path, 'utf8'); const language = this.detectLanguage(file.path); file.content = content; file.language = language; file.size = content.length; file.lines = content.split('\n').length; } catch (error) { if (flags.verbose) { console.warn(chalk.yellow(`⚠️ Could not read file: ${file.path}`)); } } } return files; } detectLanguage(filePath) { const ext = path.extname(filePath).toLowerCase(); const languageMap = { '.js': 'javascript', '.jsx': 'javascript', '.ts': 'typescript', '.tsx': 'typescript', '.py': 'python', '.java': 'java', '.cs': 'csharp' }; return languageMap[ext] || 'unknown'; } analyzeComplexity(file) { const suggestions = []; const content = file.content; const lines = content.split('\n'); // Check for long functions const functionRegex = /function\s+(\w+)\s*\([^)]*\)\s*\{/g; let match; while ((match = functionRegex.exec(content)) !== null) { const functionName = match[1]; const functionStart = match.index; const functionEnd = this.findFunctionEnd(content, functionStart); const functionLines = content.substring(functionStart, functionEnd).split('\n').length; if (functionLines > 50) { suggestions.push({ type: 'complexity', severity: 'medium', impact: 'medium', title: 'Long Function Detected', description: `Function '${functionName}' is ${functionLines} lines long`, file: file.path, line: this.getLineNumber(content, functionStart), evidence: [`Function length: ${functionLines} lines`], recommendation: 'Consider breaking this function into smaller, more focused functions' }); } } // Check for deep nesting const nestingLevel = this.calculateNestingLevel(content); if (nestingLevel > 4) { suggestions.push({ type: 'complexity', severity: 'high', impact: 'high', title: 'Deep Nesting Detected', description: `Maximum nesting level: ${nestingLevel}`, file: file.path, evidence: [`Nesting level: ${nestingLevel}`], recommendation: 'Refactor to reduce nesting depth and improve readability' }); } return suggestions; } analyzeMaintainability(file) { const suggestions = []; const content = file.content; // Check for magic numbers const magicNumberRegex = /\b\d{3,}\b/g; const magicNumbers = content.match(magicNumberRegex); if (magicNumbers && magicNumbers.length > 5) { suggestions.push({ type: 'maintainability', severity: 'medium', impact: 'medium', title: 'Magic Numbers Detected', description: `${magicNumbers.length} magic numbers found`, file: file.path, evidence: magicNumbers.slice(0, 5), recommendation: 'Replace magic numbers with named constants' }); } // Check for commented code const commentedCodeRegex = /\/\/.*\w+.*\(/g; const commentedCode = content.match(commentedCodeRegex); if (commentedCode && commentedCode.length > 3) { suggestions.push({ type: 'maintainability', severity: 'low', impact: 'low', title: 'Commented Code Detected', description: `${commentedCode.length} lines of commented code found`, file: file.path, evidence: commentedCode.slice(0, 3), recommendation: 'Remove commented code or convert to documentation' }); } return suggestions; } analyzeReadability(file) { const suggestions = []; const content = file.content; const lines = content.split('\n'); // Check for long lines lines.forEach((line, index) => { if (line.length > 120) { suggestions.push({ type: 'readability', severity: 'low', impact: 'low', title: 'Long Line Detected', description: `Line ${index + 1} is ${line.length} characters long`, file: file.path, line: index + 1, evidence: [line.substring(0, 50) + '...'], recommendation: 'Break long lines to improve readability' }); } }); // Check for inconsistent naming const variableRegex = /(?:let|const|var)\s+([a-zA-Z_][a-zA-Z0-9_]*)/g; const variables = []; let match; while ((match = variableRegex.exec(content)) !== null) { variables.push(match[1]); } const namingIssues = this.checkNamingConsistency(variables); if (namingIssues.length > 0) { suggestions.push({ type: 'readability', severity: 'low', impact: 'low', title: 'Inconsistent Naming Detected', description: `${namingIssues.length} naming inconsistencies found`, file: file.path, evidence: namingIssues.slice(0, 3), recommendation: 'Use consistent naming conventions throughout the codebase' }); } return suggestions; } analyzeCodeStyle(file) { const suggestions = []; const content = file.content; // Check for missing semicolons (JavaScript/TypeScript) if (file.language === 'javascript' || file.language === 'typescript') { const lines = content.split('\n'); const missingSemicolons = []; lines.forEach((line, index) => { const trimmedLine = line.trim(); if (trimmedLine && !trimmedLine.endsWith(';') && !trimmedLine.endsWith('{') && !trimmedLine.endsWith('}') && !trimmedLine.startsWith('//') && !trimmedLine.startsWith('*') && !trimmedLine.startsWith('if') && !trimmedLine.startsWith('for') && !trimmedLine.startsWith('while') && !trimmedLine.startsWith('switch') && !trimmedLine.startsWith('function') && !trimmedLine.startsWith('class')) { missingSemicolons.push(index + 1); } }); if (missingSemicolons.length > 5) { suggestions.push({ type: 'style', severity: 'low', impact: 'low', title: 'Missing Semicolons', description: `${missingSemicolons.length} lines missing semicolons`, file: file.path, evidence: missingSemicolons.slice(0, 5), recommendation: 'Add semicolons for consistency and to avoid potential issues' }); } } return suggestions; } analyzePerformance(file) { const suggestions = []; const content = file.content; // Check for inefficient loops const forLoopRegex = /for\s*\(\s*var\s+(\w+)\s*=\s*0\s*;\s*\1\s*<\s*(\w+)\.length\s*;\s*\1\+\+\)/g; let match; while ((match = forLoopRegex.exec(content)) !== null) { suggestions.push({ type: 'performance', severity: 'medium', impact: 'medium', title: 'Inefficient Loop Detected', description: 'Consider caching array length or using forEach', file: file.path, line: this.getLineNumber(content, match.index), evidence: [match[0]], recommendation: 'Cache array length or use forEach for better performance' }); } // Check for string concatenation in loops const stringConcatRegex = /(\w+)\s*\+=\s*['"][^'"]*['"]/g; const stringConcats = content.match(stringConcatRegex); if (stringConcats && stringConcats.length > 3) { suggestions.push({ type: 'performance', severity: 'medium', impact: 'medium', title: 'String Concatenation in Loop', description: 'Consider using array.join() or template literals', file: file.path, evidence: stringConcats.slice(0, 3), recommendation: 'Use array.join() or template literals for better performance' }); } return suggestions; } analyzeMemoryUsage(file) { const suggestions = []; const content = file.content; // Check for potential memory leaks const eventListenerRegex = /addEventListener\s*\([^,]+,\s*[^)]+\)/g; const eventListeners = content.match(eventListenerRegex); if (eventListeners && eventListeners.length > 5) { suggestions.push({ type: 'performance', severity: 'medium', impact: 'medium', title: 'Multiple Event Listeners', description: `${eventListeners.length} event listeners found`, file: file.path, evidence: eventListeners.slice(0, 3), recommendation: 'Ensure event listeners are properly removed to prevent memory leaks' }); } return suggestions; } analyzeAlgorithms(file) { const suggestions = []; const content = file.content; // Check for O(n²) algorithms const nestedLoopRegex = /for\s*\([^)]+\)\s*\{[^}]*for\s*\([^)]+\)\s*\{/g; if (nestedLoopRegex.test(content)) { suggestions.push({ type: 'performance', severity: 'medium', impact: 'medium', title: 'Nested Loops Detected', description: 'Consider optimizing nested loops for better performance', file: file.path, evidence: ['Nested loop structure detected'], recommendation: 'Consider using more efficient algorithms or data structures' }); } return suggestions; } analyzeSecurity(file) { const suggestions = []; const content = file.content; // Check for eval usage if (content.includes('eval(')) { suggestions.push({ type: 'security', severity: 'critical', impact: 'high', title: 'eval() Usage Detected', description: 'eval() can be dangerous and should be avoided', file: file.path, evidence: ['eval() function found'], recommendation: 'Replace eval() with safer alternatives like JSON.parse() or Function constructor' }); } // Check for innerHTML usage if (content.includes('innerHTML')) { suggestions.push({ type: 'security', severity: 'high', impact: 'high', title: 'innerHTML Usage Detected', description: 'innerHTML can lead to XSS vulnerabilities', file: file.path, evidence: ['innerHTML property found'], recommendation: 'Use textContent or createElement for safer DOM manipulation' }); } return suggestions; } analyzeInputValidation(file) { const suggestions = []; const content = file.content; // Check for missing input validation const inputRegex = /input\s*\[[^\]]+\]/g; const inputs = content.match(inputRegex); if (inputs && inputs.length > 0) { const hasValidation = content.includes('validate') || content.includes('check') || content.includes('test'); if (!hasValidation) { suggestions.push({ type: 'security', severity: 'high', impact: 'high', title: 'Missing Input Validation', description: 'Input validation not detected', file: file.path, evidence: ['Input access without validation'], recommendation: 'Add proper input validation and sanitization' }); } } return suggestions; } analyzeAuthentication(file) { const suggestions = []; const content = file.content; // Check for hardcoded credentials const credentialRegex = /(?:password|secret|key|token)\s*[:=]\s*['"][^'"]+['"]/gi; const credentials = content.match(credentialRegex); if (credentials && credentials.length > 0) { suggestions.push({ type: 'security', severity: 'critical', impact: 'high', title: 'Hardcoded Credentials Detected', description: 'Hardcoded credentials found in code', file: file.path, evidence: credentials.slice(0, 3), recommendation: 'Move credentials to environment variables or secure configuration' }); } return suggestions; } analyzeVisualTesting(file) { const suggestions = []; const content = file.content; // Check for Percy usage if (content.includes('percy') || content.includes('Percy')) { suggestions.push({ type: 'migration', severity: 'high', impact: 'high', title: 'Percy Visual Testing Detected', description: 'Percy visual testing code found', file: file.path, evidence: ['Percy API usage detected'], recommendation: 'Consider migrating to SmartUI for better performance and features' }); } // Check for Applitools usage if (content.includes('applitools') || content.includes('eyes')) { suggestions.push({ type: 'migration', severity: 'high', impact: 'high', title: 'Applitools Visual Testing Detected', description: 'Applitools visual testing code found', file: file.path, evidence: ['Applitools API usage detected'], recommendation: 'Consider migrating to SmartUI for better performance and features' }); } return suggestions; } analyzeFrameworkMigration(file) { const suggestions = []; const content = file.content; // Check for outdated framework usage if (content.includes('jQuery') && !content.includes('$')) { suggestions.push({ type: 'migration', severity: 'medium', impact: 'medium', title: 'jQuery Usage Detected', description: 'Consider migrating to modern frameworks', file: file.path, evidence: ['jQuery library usage'], recommendation: 'Consider migrating to React, Vue, or Angular for better maintainability' }); } return suggestions; } analyzeAPIMigration(file) { const suggestions = []; const content = file.content; // Check for deprecated API usage if (content.includes('XMLHttpRequest') && !content.includes('fetch')) { suggestions.push({ type: 'migration', severity: 'medium', impact: 'medium', title: 'XMLHttpRequest Usage Detected', description: 'Consider using modern fetch API', file: file.path, evidence: ['XMLHttpRequest usage'], recommendation: 'Migrate to fetch API for better promise support and cleaner code' }); } return suggestions; } analyzeDesignPatterns(files) { const suggestions = []; // Check for singleton pattern usage const hasSingleton = files.some(f => f.content && f.content.includes('getInstance') && f.content.includes('static') ); if (hasSingleton) { suggestions.push({ type: 'architecture', severity: 'low', impact: 'low', title: 'Singleton Pattern Detected', description: 'Consider if singleton is the right pattern for your use case', evidence: ['Singleton pattern implementation found'], recommendation: 'Evaluate if singleton is necessary or if dependency injection would be better' }); } return suggestions; } analyzeModularity(files) { const suggestions = []; // Check for large files const largeFiles = files.filter(f => f.content && f.content.length > 10000); if (largeFiles.length > 0) { suggestions.push({ type: 'architecture', severity: 'medium', impact: 'medium', title: 'Large Files Detected', description: `${largeFiles.length} files are larger than 10KB`, evidence: largeFiles.map(f => `${path.basename(f.path)}: ${f.content.length} characters`), recommendation: 'Consider breaking large files into smaller, more focused modules' }); } return suggestions; } analyzeScalability(files) { const suggestions = []; // Check for synchronous operations const syncOps = files.filter(f => f.content && (f.content.includes('sync') || f.content.includes('Sync')) ); if (syncOps.length > 0) { suggestions.push({ type: 'architecture', severity: 'medium', impact: 'medium', title: 'Synchronous Operations Detected', description: 'Consider using asynchronous operations for better scalability', evidence: ['Synchronous operations found'], recommendation: 'Replace synchronous operations with async/await or promises where possible' }); } return suggestions; } checkNamingConsistency(variables) { const issues = []; const camelCase = /^[a-z][a-zA-Z0-9]*$/; const snakeCase = /^[a-z][a-z0-9_]*$/; const pascalCase = /^[A-Z][a-zA-Z0-9]*$/; const namingStyles = { camelCase: variables.filter(v => camelCase.test(v)).length, snakeCase: variables.filter(v => snakeCase.test(v)).length, pascalCase: variables.filter(v => pascalCase.test(v)).length }; const total = variables.length; const dominantStyle = Object.entries(namingStyles).reduce((a, b) => namingStyles[a[0]] > namingStyles[b[0]] ? a : b )[0]; variables.forEach(variable => { if (dominantStyle === 'camelCase' && !camelCase.test(variable)) { issues.push(variable); } else if (dominantStyle === 'snakeCase' && !snakeCase.test(variable)) { issues.push(variable); } else if (dominantStyle === 'pascalCase' && !pascalCase.test(variable)) { issues.push(variable); } }); return issues; } calculateNestingLevel(content) { let maxNesting = 0; let currentNesting = 0; for (let i = 0; i < content.length; i++) { if (content[i] === '{') { currentNesting++; maxNesting = Math.max(maxNesting, currentNesting); } else if (content[i] === '}') { currentNesting--; } } return maxNesting; } findFunctionEnd(content, start) { let braceCount = 0; let i = start; while (i < content.length) { if (content[i] === '{') { braceCount++; } else if (content[i] === '}') { braceCount--; if (braceCount === 0) { return i + 1; } } i++; } return content.length; } getLineNumber(content, position) { return content.substring(0, position).split('\n').length; } generateRefactoringCode(suggestion) { return `// Refactored version of ${suggestion.title} // Break down complex function into smaller, focused functions function processData(data) { const validatedData = validateData(data); const processedData = transformData(validatedData); return processedData; } function validateData(data) { // Validation logic here return data; } function transformData(data) { // Transformation logic here return data; }`; } generateOptimizationCode(suggestion) { return `// Optimized version for better performance // Cache array length and use efficient algorithms // Before: for (var i = 0; i < array.length; i++) // After: const length = array.length; for (let i = 0; i < length; i++) { // Process array[i] } // Or use forEach for better readability array.forEach((item, index) => { // Process item });`; } generateSecurityCode(suggestion) { return `// Secure implementation // Replace dangerous functions with safe alternatives // Instead of eval(): const result = JSON.parse(jsonString); // Instead of innerHTML: element.textContent = userInput; // Or use createElement for safe DOM manipulation const div = document.createElement('div'); div.textContent = userInput;`; } generateMigrationCode(suggestion) { return `// Migration to SmartUI // Replace Percy/Applitools with SmartUI // Before (Percy): // percy.snapshot('Homepage'); // After (SmartUI): await smartui.snapshot('Homepage', { name: 'Homepage', fullPage: true });`; } generateSummary(suggestions) { const summary = { total: suggestions.length, byType: {}, bySeverity: {}, byImpact: {}, topSuggestions: suggestions.slice(0, 5) }; suggestions.forEach(suggestion => { // Count by type summary.byType[suggestion.type] = (summary.byType[suggestion.type] || 0) + 1; // Count by severity summary.bySeverity[suggestion.severity] = (summary.bySeverity[suggestion.severity] || 0) + 1; // Count by impact summary.byImpact[suggestion.impact] = (summary.byImpact[suggestion.impact] || 0) + 1; }); return summary; } displayResults(results, verbose) { console.log(chalk.green.bold('\n💡 Suggestion Engine Results')); console.log(chalk.gray('=' * 50)); // Summary console.log(chalk.blue.bold('\n📊 Summary:')); console.log(` Total suggestions: ${results.summary.total}`); console.log(` Files analyzed: ${results.files.length}`); // By Type console.log(chalk.blue.bold('\n📋 By Type:')); Object.entries(results.summary.byType).forEach(([type, count]) => { console.log(` ${type}: ${count}`); }); // By Severity console.log(chalk.blue.bold('\n⚠️ By Severity:')); Object.entries(results.summary.bySeverity).forEach(([severity, count]) => { const color = severity === 'critical' ? chalk.red : severity === 'high' ? chalk.yellow : severity === 'medium' ? chalk.blue : chalk.green; console.log(` ${color(severity)}: ${count}`); }); // Top Suggestions console.log(chalk.blue.bold('\n🎯 Top Suggestions:')); results.summary.topSuggestions.forEach((suggestion, index) => { const severityColor = suggestion.severity === 'critical' ? chalk.red : suggestion.severity === 'high' ? chalk.yellow : suggestion.severity === 'medium' ? chalk.blue : chalk.green; console.log(` ${index + 1}. ${severityColor(suggestion.title)} (${suggestion.severity})`); console.log(` ${suggestion.description}`); console.log(` File: ${path.basename(suggestion.file)}`); console.log(` Confidence: ${(suggestion.confidence * 100).toFixed(1)}%`); console.log(` Priority: ${suggestion.priority.toFixed(1)}`); console.log(''); }); if (verbose) { console.log(chalk.blue.bold('\n🔍 Detailed Suggestions:')); results.suggestions.forEach((suggestion, index) => { console.log(`\n${index + 1}. ${suggestion.title}`); console.log(` Type: ${suggestion.type}`); console.log(` Severity: ${suggestion.severity}`); console.log(` Impact: ${suggestion.impact}`); console.log(` Confidence: ${(suggestion.confidence * 100).toFixed(1)}%`); console.log(` Priority: ${suggestion.priority.toFixed(1)}`); console.log(` File: ${suggestion.file}`); if (suggestion.line) { console.log(` Line: ${suggestion.line}`); } console.log(` Description: ${suggestion.description}`); console.log(` Recommendation: ${suggestion.recommendation}`); if (suggestion.evidence && suggestion.evidence.length > 0) { console.log(` Evidence: ${suggestion.evidence.join(', ')}`); } if (suggestion.transformations && suggestion.transformations.length > 0) { console.log(` Transformations:`); suggestion.transformations.forEach((transformation, tIndex) => { console.log(` ${tIndex + 1}. ${transformation.description}`); }); } }); } } } module.exports.default = SuggestionEngine;