UNPKG

aios-core

Version:

Synkra AIOS: AI-Orchestrated System for Full Stack Development - Core Framework

1,139 lines (990 loc) 34.7 kB
const fs = require('fs').promises; const path = require('path'); const chalk = require('chalk'); const { parse } = require('@babel/parser'); const traverse = require('@babel/traverse').default; const generate = require('@babel/generator').default; const t = require('@babel/types'); /** * Automated refactoring suggestion system * Analyzes code and suggests refactoring opportunities */ class RefactoringSuggester { constructor(options = {}) { this.rootPath = options.rootPath || process.cwd(); this.suggestions = []; this.refactoringPatterns = new Map(); this.codeMetrics = new Map(); this.initializePatterns(); } /** * Initialize refactoring patterns */ initializePatterns() { // Method extraction pattern this.refactoringPatterns.set('extract_method', { name: 'Extract Method', description: 'Extract long methods into smaller, focused methods', detector: this.detectLongMethods.bind(this), suggester: this.suggestMethodExtraction.bind(this), priority: 'high', }); // Variable extraction pattern this.refactoringPatterns.set('extract_variable', { name: 'Extract Variable', description: 'Extract complex expressions into named variables', detector: this.detectComplexExpressions.bind(this), suggester: this.suggestVariableExtraction.bind(this), priority: 'medium', }); // Parameter object pattern this.refactoringPatterns.set('introduce_parameter_object', { name: 'Introduce Parameter Object', description: 'Group related parameters into an object', detector: this.detectLongParameterLists.bind(this), suggester: this.suggestParameterObject.bind(this), priority: 'medium', }); // Replace conditional with polymorphism this.refactoringPatterns.set('replace_conditional', { name: 'Replace Conditional with Polymorphism', description: 'Replace complex conditionals with polymorphic behavior', detector: this.detectComplexConditionals.bind(this), suggester: this.suggestPolymorphism.bind(this), priority: 'high', }); // Inline temp pattern this.refactoringPatterns.set('inline_temp', { name: 'Inline Temporary Variable', description: 'Replace temporary variables used only once', detector: this.detectSingleUseTempVariables.bind(this), suggester: this.suggestInlineTemp.bind(this), priority: 'low', }); // Remove dead code this.refactoringPatterns.set('remove_dead_code', { name: 'Remove Dead Code', description: 'Remove unreachable or unused code', detector: this.detectDeadCode.bind(this), suggester: this.suggestDeadCodeRemoval.bind(this), priority: 'high', }); // Consolidate duplicate code this.refactoringPatterns.set('consolidate_duplicates', { name: 'Consolidate Duplicate Code', description: 'Extract duplicate code into shared functions', detector: this.detectDuplicateCode.bind(this), suggester: this.suggestCodeConsolidation.bind(this), priority: 'high', }); // Simplify nested conditionals this.refactoringPatterns.set('simplify_conditionals', { name: 'Simplify Nested Conditionals', description: 'Flatten deeply nested if-else chains', detector: this.detectNestedConditionals.bind(this), suggester: this.suggestConditionalSimplification.bind(this), priority: 'medium', }); // Replace magic numbers this.refactoringPatterns.set('replace_magic_numbers', { name: 'Replace Magic Numbers', description: 'Replace hard-coded numbers with named constants', detector: this.detectMagicNumbers.bind(this), suggester: this.suggestConstantExtraction.bind(this), priority: 'low', }); // Decompose complex class this.refactoringPatterns.set('decompose_class', { name: 'Decompose Complex Class', description: 'Split large classes into smaller, focused classes', detector: this.detectLargeClasses.bind(this), suggester: this.suggestClassDecomposition.bind(this), priority: 'high', }); } /** * Analyze code and suggest refactorings */ async analyzeCode(filePath, options = {}) { console.log(chalk.blue(`🔍 Analyzing: ${filePath}`)); try { const content = await fs.readFile(filePath, 'utf-8'); const fileType = path.extname(filePath); if (!['.js', '.jsx', '.ts', '.tsx'].includes(fileType)) { return { filePath, suggestions: [], error: 'Unsupported file type', }; } // Parse code const ast = this.parseCode(content, filePath); // Calculate code metrics const metrics = this.calculateCodeMetrics(ast, content); this.codeMetrics.set(filePath, metrics); // Clear previous suggestions this.suggestions = []; // Run all refactoring detectors for (const [patternId, pattern] of this.refactoringPatterns) { if (options.patterns && !options.patterns.includes(patternId)) { continue; // Skip if not in requested patterns } try { const detected = await pattern.detector(ast, content, metrics); if (detected && detected.length > 0) { for (const detection of detected) { const suggestion = await pattern.suggester(detection, ast, content); if (suggestion) { this.suggestions.push({ ...suggestion, patternId, pattern: pattern.name, priority: pattern.priority, filePath, }); } } } } catch (error) { console.warn(chalk.yellow(`Failed to run ${pattern.name}: ${error.message}`)); } } // Sort suggestions by priority and impact this.suggestions.sort((a, b) => { const priorityOrder = { high: 3, medium: 2, low: 1 }; const priorityDiff = priorityOrder[b.priority] - priorityOrder[a.priority]; if (priorityDiff !== 0) return priorityDiff; return (b.impact || 0) - (a.impact || 0); }); return { filePath, metrics, suggestions: this.suggestions, }; } catch (error) { return { filePath, suggestions: [], error: error.message, }; } } /** * Parse code into AST */ parseCode(content, filePath) { const parserOptions = { sourceType: 'module', plugins: [ 'jsx', 'typescript', 'decorators-legacy', 'classProperties', 'asyncGenerators', 'dynamicImport', 'optionalChaining', 'nullishCoalescingOperator', ], errorRecovery: true, }; try { return parse(content, parserOptions); } catch (error) { console.warn(chalk.yellow(`Parse error in ${filePath}: ${error.message}`)); // Try with more lenient options return parse(content, { ...parserOptions, errorRecovery: true }); } } /** * Calculate code metrics */ calculateCodeMetrics(ast, content) { const metrics = { lines: content.split('\n').length, functions: 0, classes: 0, complexity: 0, maxNesting: 0, duplicateBlocks: 0, comments: 0, imports: 0, }; let currentNesting = 0; traverse(ast, { FunctionDeclaration: () => metrics.functions++, FunctionExpression: () => metrics.functions++, ArrowFunctionExpression: () => metrics.functions++, ClassDeclaration: () => metrics.classes++, ImportDeclaration: () => metrics.imports++, IfStatement: { enter: () => { metrics.complexity++; currentNesting++; metrics.maxNesting = Math.max(metrics.maxNesting, currentNesting); }, exit: () => currentNesting--, }, SwitchStatement: () => metrics.complexity += 2, ForStatement: () => metrics.complexity++, WhileStatement: () => metrics.complexity++, DoWhileStatement: () => metrics.complexity++, ConditionalExpression: () => metrics.complexity++, LogicalExpression: (path) => { if (path.node.operator === '&&' || path.node.operator === '||') { metrics.complexity++; } }, Comment: () => metrics.comments++, }); return metrics; } // Refactoring detectors async detectLongMethods(ast, content, metrics) { const longMethods = []; const methodSizeThreshold = 30; // lines traverse(ast, { 'FunctionDeclaration|FunctionExpression|ArrowFunctionExpression': (path) => { const start = path.node.loc.start.line; const end = path.node.loc.end.line; const methodLines = end - start + 1; if (methodLines > methodSizeThreshold) { const methodName = this.getMethodName(path); longMethods.push({ type: 'long_method', node: path.node, path: path, name: methodName, lines: methodLines, startLine: start, endLine: end, complexity: this.calculateMethodComplexity(path), }); } }, }); return longMethods; } async detectComplexExpressions(ast, content, metrics) { const complexExpressions = []; const complexityThreshold = 3; // nesting/chaining depth traverse(ast, { Expression: (path) => { const complexity = this.calculateExpressionComplexity(path.node); if (complexity > complexityThreshold) { complexExpressions.push({ type: 'complex_expression', node: path.node, path: path, complexity: complexity, startLine: path.node.loc?.start.line, endLine: path.node.loc?.end.line, }); } }, }); return complexExpressions; } async detectLongParameterLists(ast, content, metrics) { const longParameterLists = []; const parameterThreshold = 4; traverse(ast, { 'FunctionDeclaration|FunctionExpression|ArrowFunctionExpression': (path) => { const params = path.node.params; if (params.length > parameterThreshold) { const methodName = this.getMethodName(path); longParameterLists.push({ type: 'long_parameter_list', node: path.node, path: path, name: methodName, parameterCount: params.length, parameters: params.map(p => p.name || 'unknown'), startLine: path.node.loc?.start.line, }); } }, }); return longParameterLists; } async detectComplexConditionals(ast, content, metrics) { const complexConditionals = []; const branchThreshold = 4; traverse(ast, { IfStatement: (path) => { const branches = this.countConditionalBranches(path); if (branches > branchThreshold) { complexConditionals.push({ type: 'complex_conditional', node: path.node, path: path, branches: branches, startLine: path.node.loc?.start.line, endLine: path.node.loc?.end.line, }); } }, SwitchStatement: (path) => { const cases = path.node.cases.length; if (cases > branchThreshold) { complexConditionals.push({ type: 'complex_switch', node: path.node, path: path, cases: cases, startLine: path.node.loc?.start.line, endLine: path.node.loc?.end.line, }); } }, }); return complexConditionals; } async detectSingleUseTempVariables(ast, content, metrics) { const singleUseVars = []; const varUsage = new Map(); // First pass: collect all variable declarations and usages traverse(ast, { VariableDeclarator: (path) => { if (path.node.id.type === 'Identifier') { const varName = path.node.id.name; if (!varUsage.has(varName)) { varUsage.set(varName, { declaration: path, uses: [], }); } } }, Identifier: (path) => { if (path.isReferencedIdentifier()) { const varName = path.node.name; if (varUsage.has(varName)) { varUsage.get(varName).uses.push(path); } } }, }); // Second pass: find single-use variables for (const [varName, usage] of varUsage) { if (usage.uses.length === 1 && usage.declaration.node.init) { singleUseVars.push({ type: 'single_use_temp', name: varName, declaration: usage.declaration, use: usage.uses[0], startLine: usage.declaration.node.loc?.start.line, }); } } return singleUseVars; } async detectDeadCode(ast, content, metrics) { const deadCode = []; traverse(ast, { // Unreachable code after return/throw 'ReturnStatement|ThrowStatement': (path) => { const parent = path.parent; if (parent.type === 'BlockStatement') { const siblings = parent.body; const currentIndex = siblings.indexOf(path.node); for (let i = currentIndex + 1; i < siblings.length; i++) { deadCode.push({ type: 'unreachable_code', node: siblings[i], reason: 'after_return_throw', startLine: siblings[i].loc?.start.line, }); } } }, // Unused functions FunctionDeclaration: (path) => { const functionName = path.node.id?.name; if (functionName && !this.isFunctionUsed(functionName, ast)) { deadCode.push({ type: 'unused_function', node: path.node, name: functionName, startLine: path.node.loc?.start.line, }); } }, // Always false conditions IfStatement: (path) => { if (path.node.test.type === 'BooleanLiteral' && !path.node.test.value) { deadCode.push({ type: 'dead_branch', node: path.node.consequent, reason: 'always_false', startLine: path.node.loc?.start.line, }); } }, }); return deadCode; } async detectDuplicateCode(ast, content, metrics) { const duplicates = []; const codeBlocks = new Map(); const minBlockSize = 5; // minimum lines for duplicate detection traverse(ast, { BlockStatement: (path) => { if (path.node.body.length >= minBlockSize) { const blockHash = this.hashCodeBlock(path.node); if (codeBlocks.has(blockHash)) { const original = codeBlocks.get(blockHash); duplicates.push({ type: 'duplicate_code', original: original, duplicate: path, startLine: path.node.loc?.start.line, endLine: path.node.loc?.end.line, lines: path.node.loc?.end.line - path.node.loc?.start.line + 1, }); } else { codeBlocks.set(blockHash, path); } } }, }); return duplicates; } async detectNestedConditionals(ast, content, metrics) { const nestedConditionals = []; const nestingThreshold = 3; const checkNesting = (path, depth = 0) => { if (depth > nestingThreshold) { nestedConditionals.push({ type: 'nested_conditional', node: path.node, path: path, depth: depth, startLine: path.node.loc?.start.line, endLine: path.node.loc?.end.line, }); } // Check nested ifs traverse(path.node, { IfStatement: (innerPath) => { if (innerPath.node !== path.node) { checkNesting(innerPath, depth + 1); innerPath.skip(); } }, }, path.scope, path); }; traverse(ast, { IfStatement: (path) => checkNesting(path, 1), }); return nestedConditionals; } async detectMagicNumbers(ast, content, metrics) { const magicNumbers = []; const ignoredNumbers = new Set([0, 1, -1, 2, 10, 100, 1000]); traverse(ast, { NumericLiteral: (path) => { const value = path.node.value; // Skip common/obvious numbers if (ignoredNumbers.has(value)) return; // Skip array indices if (path.parent.type === 'MemberExpression' && path.parent.computed) return; // Skip in constant declarations if (path.findParent(p => p.isVariableDeclarator() && p.parent.kind === 'const')) return; magicNumbers.push({ type: 'magic_number', node: path.node, path: path, value: value, context: path.parent.type, startLine: path.node.loc?.start.line, }); }, }); return magicNumbers; } async detectLargeClasses(ast, content, metrics) { const largeClasses = []; const methodThreshold = 10; const propertyThreshold = 15; traverse(ast, { ClassDeclaration: (path) => { const methods = path.node.body.body.filter(m => m.type === 'ClassMethod' || m.type === 'ClassProperty', ); const methodCount = methods.filter(m => m.type === 'ClassMethod').length; const propertyCount = methods.filter(m => m.type === 'ClassProperty').length; if (methodCount > methodThreshold || propertyCount > propertyThreshold) { largeClasses.push({ type: 'large_class', node: path.node, path: path, name: path.node.id?.name, methodCount: methodCount, propertyCount: propertyCount, totalMembers: methods.length, startLine: path.node.loc?.start.line, endLine: path.node.loc?.end.line, }); } }, }); return largeClasses; } // Refactoring suggesters async suggestMethodExtraction(detection, ast, content) { const suggestion = { type: 'extract_method', description: `Extract method '${detection.name}' (${detection.lines} lines)`, location: { start: detection.startLine, end: detection.endLine, }, impact: Math.min(10, Math.floor(detection.lines / 10) + Math.floor(detection.complexity / 5)), details: `Method has ${detection.lines} lines and complexity of ${detection.complexity}. Consider extracting logical sections into separate methods.`, suggestedRefactoring: this.generateMethodExtractionSuggestion(detection), }; return suggestion; } async suggestVariableExtraction(detection, ast, content) { const suggestion = { type: 'extract_variable', description: 'Extract complex expression into variable', location: { start: detection.startLine, end: detection.endLine, }, impact: Math.min(5, detection.complexity - 2), details: `Expression has complexity of ${detection.complexity}. Extract into a named variable for better readability.`, suggestedRefactoring: this.generateVariableExtractionSuggestion(detection), }; return suggestion; } async suggestParameterObject(detection, ast, content) { const suggestion = { type: 'introduce_parameter_object', description: `Group ${detection.parameterCount} parameters in '${detection.name}'`, location: { start: detection.startLine, end: detection.startLine, }, impact: Math.min(7, detection.parameterCount - 3), details: `Method has ${detection.parameterCount} parameters: ${detection.parameters.join(', ')}. Consider grouping related parameters into an object.`, suggestedRefactoring: this.generateParameterObjectSuggestion(detection), }; return suggestion; } async suggestPolymorphism(detection, ast, content) { const suggestion = { type: 'replace_conditional', description: `Replace ${detection.type === 'complex_switch' ? 'switch' : 'conditional'} with polymorphism`, location: { start: detection.startLine, end: detection.endLine, }, impact: Math.min(8, detection.branches || detection.cases), details: `Complex ${detection.type === 'complex_switch' ? 'switch' : 'conditional'} with ${detection.branches || detection.cases} branches. Consider using polymorphism or strategy pattern.`, suggestedRefactoring: this.generatePolymorphismSuggestion(detection), }; return suggestion; } async suggestInlineTemp(detection, ast, content) { const suggestion = { type: 'inline_temp', description: `Inline temporary variable '${detection.name}'`, location: { start: detection.startLine, end: detection.startLine, }, impact: 2, details: `Variable '${detection.name}' is used only once. Consider inlining it.`, suggestedRefactoring: this.generateInlineTempSuggestion(detection), }; return suggestion; } async suggestDeadCodeRemoval(detection, ast, content) { const suggestion = { type: 'remove_dead_code', description: `Remove ${detection.type.replace('_', ' ')}${detection.name ? `: ${detection.name}` : ''}`, location: { start: detection.startLine, end: detection.node.loc?.end.line || detection.startLine, }, impact: 5, details: `${detection.type === 'unreachable_code' ? 'Code is unreachable' : detection.type === 'unused_function' ? 'Function is never called' : 'Code is dead'}`, suggestedRefactoring: { action: 'delete', lines: [detection.startLine, detection.node.loc?.end.line || detection.startLine], }, }; return suggestion; } async suggestCodeConsolidation(detection, ast, content) { const suggestion = { type: 'consolidate_duplicates', description: `Extract duplicate code block (${detection.lines} lines)`, location: { start: detection.startLine, end: detection.endLine, }, impact: Math.min(9, detection.lines), details: 'Found duplicate code block. Extract into a shared function.', suggestedRefactoring: this.generateConsolidationSuggestion(detection), }; return suggestion; } async suggestConditionalSimplification(detection, ast, content) { const suggestion = { type: 'simplify_conditionals', description: `Simplify nested conditionals (depth: ${detection.depth})`, location: { start: detection.startLine, end: detection.endLine, }, impact: Math.min(7, detection.depth * 2), details: `Deeply nested conditionals (${detection.depth} levels). Consider early returns or guard clauses.`, suggestedRefactoring: this.generateConditionalSimplificationSuggestion(detection), }; return suggestion; } async suggestConstantExtraction(detection, ast, content) { const suggestion = { type: 'replace_magic_numbers', description: `Replace magic number ${detection.value}`, location: { start: detection.startLine, end: detection.startLine, }, impact: 3, details: `Magic number ${detection.value} found in ${detection.context}. Extract to named constant.`, suggestedRefactoring: this.generateConstantExtractionSuggestion(detection), }; return suggestion; } async suggestClassDecomposition(detection, ast, content) { const suggestion = { type: 'decompose_class', description: `Decompose large class '${detection.name}' (${detection.totalMembers} members)`, location: { start: detection.startLine, end: detection.endLine, }, impact: Math.min(10, Math.floor(detection.totalMembers / 5)), details: `Class has ${detection.methodCount} methods and ${detection.propertyCount} properties. Consider splitting into smaller, focused classes.`, suggestedRefactoring: this.generateClassDecompositionSuggestion(detection), }; return suggestion; } // Helper methods getMethodName(path) { if (path.node.id) { return path.node.id.name; } // Check if it's a method in a class if (path.parent.type === 'ClassMethod') { return path.parent.key.name; } // Check if it's assigned to a variable if (path.parent.type === 'VariableDeclarator') { return path.parent.id.name; } // Check if it's a property if (path.parent.type === 'ObjectProperty') { return path.parent.key.name || path.parent.key.value; } return 'anonymous'; } calculateMethodComplexity(path) { let complexity = 1; traverse(path.node, { IfStatement: () => complexity++, ConditionalExpression: () => complexity++, SwitchCase: () => complexity++, WhileStatement: () => complexity++, ForStatement: () => complexity++, DoWhileStatement: () => complexity++, LogicalExpression: (innerPath) => { if (innerPath.node.operator === '&&' || innerPath.node.operator === '||') { complexity++; } }, }, path.scope, path); return complexity; } calculateExpressionComplexity(node, depth = 0) { if (!node) return depth; let maxDepth = depth; // Check different expression types if (node.type === 'CallExpression') { maxDepth = Math.max(maxDepth, this.calculateExpressionComplexity(node.callee, depth + 1)); for (const arg of node.arguments) { maxDepth = Math.max(maxDepth, this.calculateExpressionComplexity(arg, depth + 1)); } } else if (node.type === 'MemberExpression') { maxDepth = Math.max(maxDepth, this.calculateExpressionComplexity(node.object, depth + 1)); } else if (node.type === 'ConditionalExpression') { maxDepth = Math.max(maxDepth, this.calculateExpressionComplexity(node.test, depth + 1), this.calculateExpressionComplexity(node.consequent, depth + 1), this.calculateExpressionComplexity(node.alternate, depth + 1), ); } else if (node.type === 'BinaryExpression' || node.type === 'LogicalExpression') { maxDepth = Math.max(maxDepth, this.calculateExpressionComplexity(node.left, depth + 1), this.calculateExpressionComplexity(node.right, depth + 1), ); } return maxDepth; } countConditionalBranches(path) { let branches = 1; // Initial if branch let current = path.node; while (current.alternate) { branches++; if (current.alternate.type === 'IfStatement') { current = current.alternate; } else { break; } } return branches; } isFunctionUsed(functionName, ast) { let used = false; traverse(ast, { CallExpression: (path) => { if (path.node.callee.type === 'Identifier' && path.node.callee.name === functionName) { used = true; path.stop(); } }, Identifier: (path) => { if (path.node.name === functionName && path.isReferencedIdentifier() && !path.isFunction()) { used = true; path.stop(); } }, }); return used; } hashCodeBlock(node) { // Simple hash based on code structure const code = generate(node, { compact: true }).code; return code.replace(/\s+/g, ' ').trim(); } // Suggestion generators generateMethodExtractionSuggestion(detection) { return { action: 'extract_method', extractedMethods: [ { name: `extracted${detection.name.charAt(0).toUpperCase() + detection.name.slice(1)}Part1`, description: 'Extract first logical section', suggestedLines: [detection.startLine + 5, detection.startLine + 15], }, { name: `extracted${detection.name.charAt(0).toUpperCase() + detection.name.slice(1)}Part2`, description: 'Extract second logical section', suggestedLines: [detection.startLine + 16, detection.endLine - 5], }, ], }; } generateVariableExtractionSuggestion(detection) { return { action: 'extract_variable', variableName: 'extractedExpression', insertBefore: detection.startLine, }; } generateParameterObjectSuggestion(detection) { return { action: 'introduce_parameter_object', objectName: `${detection.name}Options`, groupedParameters: detection.parameters.slice(2), // Keep first 2 params separate keepParameters: detection.parameters.slice(0, 2), }; } generatePolymorphismSuggestion(detection) { return { action: 'replace_with_polymorphism', strategyPattern: true, suggestedClasses: ['BaseHandler', 'TypeAHandler', 'TypeBHandler'], interfaceMethod: 'handle', }; } generateInlineTempSuggestion(detection) { return { action: 'inline_variable', variableName: detection.name, declarationLine: detection.declaration.node.loc?.start.line, usageLine: detection.use.node.loc?.start.line, }; } generateConsolidationSuggestion(detection) { return { action: 'extract_shared_function', functionName: 'extractedSharedFunction', originalLocations: [ { start: detection.original.node.loc?.start.line, end: detection.original.node.loc?.end.line, }, { start: detection.duplicate.node.loc?.start.line, end: detection.duplicate.node.loc?.end.line, }, ], }; } generateConditionalSimplificationSuggestion(detection) { return { action: 'simplify_nested_conditionals', techniques: ['early_return', 'guard_clauses', 'extract_condition'], suggestedStructure: 'Use guard clauses for edge cases and early returns', }; } generateConstantExtractionSuggestion(detection) { const constantName = this.suggestConstantName(detection.value, detection.context); return { action: 'extract_constant', constantName: constantName, value: detection.value, scope: 'module', // or 'class' depending on context }; } generateClassDecompositionSuggestion(detection) { return { action: 'decompose_class', suggestedClasses: [ { name: `${detection.name}Core`, description: 'Core functionality', methods: 'Core business logic methods', }, { name: `${detection.name}Utils`, description: 'Utility methods', methods: 'Helper and utility methods', }, { name: `${detection.name}Config`, description: 'Configuration and setup', methods: 'Configuration-related methods', }, ], }; } suggestConstantName(value, context) { // Generate meaningful constant names based on value and context const contextMap = { 'BinaryExpression': 'THRESHOLD', 'IfStatement': 'CONDITION', 'ForStatement': 'LIMIT', 'CallExpression': 'PARAMETER', }; const baseContext = contextMap[context] || 'VALUE'; return `${baseContext}_${Math.abs(value).toString().replace('.', '_')}`; } /** * Apply refactoring suggestion */ async applySuggestion(suggestion, options = {}) { console.log(chalk.blue(`🔧 Applying ${suggestion.type} refactoring...`)); try { // This would integrate with the actual refactoring implementation // For now, it's a placeholder showing the structure const result = { success: false, changes: [], error: null, }; switch (suggestion.type) { case 'extract_method': result.changes = await this.applyMethodExtraction(suggestion); break; case 'extract_variable': result.changes = await this.applyVariableExtraction(suggestion); break; case 'inline_temp': result.changes = await this.applyInlineTemp(suggestion); break; case 'remove_dead_code': result.changes = await this.applyDeadCodeRemoval(suggestion); break; default: throw new Error(`Refactoring type ${suggestion.type} not implemented`); } result.success = true; return result; } catch (error) { console.error(chalk.red(`Failed to apply refactoring: ${error.message}`)); return { success: false, changes: [], error: error.message, }; } } // Placeholder methods for applying refactorings async applyMethodExtraction(suggestion) { // Implementation would use AST transformation return [{ type: 'extract_method', file: suggestion.filePath, description: `Extracted method from lines ${suggestion.location.start}-${suggestion.location.end}`, }]; } async applyVariableExtraction(suggestion) { return [{ type: 'extract_variable', file: suggestion.filePath, description: `Extracted variable at line ${suggestion.location.start}`, }]; } async applyInlineTemp(suggestion) { return [{ type: 'inline_temp', file: suggestion.filePath, description: `Inlined variable at line ${suggestion.location.start}`, }]; } async applyDeadCodeRemoval(suggestion) { return [{ type: 'remove_dead_code', file: suggestion.filePath, description: `Removed dead code at lines ${suggestion.location.start}-${suggestion.location.end}`, }]; } /** * Get refactoring statistics */ getStatistics() { const stats = { totalSuggestions: this.suggestions.length, byType: {}, byPriority: { high: 0, medium: 0, low: 0, }, averageImpact: 0, }; let totalImpact = 0; for (const suggestion of this.suggestions) { // By type stats.byType[suggestion.type] = (stats.byType[suggestion.type] || 0) + 1; // By priority stats.byPriority[suggestion.priority]++; // Impact totalImpact += suggestion.impact || 0; } stats.averageImpact = stats.totalSuggestions > 0 ? (totalImpact / stats.totalSuggestions).toFixed(2) : 0; return stats; } } module.exports = RefactoringSuggester;