code-simplifier 1.0.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,853 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * AI-Powered Refactor Advisor
5
+ * Provides intelligent refactoring suggestions with impact assessment
6
+ * Generates step-by-step refactoring plans and before/after comparisons
7
+ */
8
+
9
+ const fs = require('fs-extra')
10
+ const path = require('path')
11
+ const chalk = require('chalk')
12
+ const AIQualityAnalyzer = require('./ai-quality-analyzer')
13
+ const { OpenAI } = require('openai')
14
+ const Anthropic = require('@anthropic-ai/sdk')
15
+
16
+ /**
17
+ * AI Refactor Advisor
18
+ */
19
+ class AIRefactorAdvisor {
20
+ constructor(config = {}) {
21
+ this.config = {
22
+ refactorTypes: config.refactorTypes || [
23
+ 'extract-method',
24
+ 'inline-method',
25
+ 'rename-variable',
26
+ 'rename-class',
27
+ 'extract-class',
28
+ 'move-method',
29
+ 'replace-loop-with-pipeline',
30
+ 'simplify-conditional',
31
+ 'remove-duplicate-code',
32
+ 'decompose-conditional',
33
+ 'replace-temp-with-query',
34
+ 'introduce-parameter-object',
35
+ 'replace-magic-number',
36
+ 'consolidate-duplicate-conditional',
37
+ 'extract-interface',
38
+ 'move-class',
39
+ 'change-function-signature',
40
+ 'extract-superclass',
41
+ 'push-down-method',
42
+ 'pull-up-method'
43
+ ],
44
+ confidenceThreshold: config.confidenceThreshold || 0.8,
45
+ maxRefactorSuggestions: config.maxRefactorSuggestions || 50,
46
+ impactThreshold: config.impactThreshold || 0.8,
47
+ openaiApiKey: config.openaiApiKey || process.env.OPENAI_API_KEY,
48
+ anthropicApiKey: config.anthropicApiKey || process.env.ANTHROPIC_API_KEY,
49
+ ...config
50
+ }
51
+
52
+ this.refactorSuggestions = []
53
+ this.analysisResults = {}
54
+ this.stats = {
55
+ opportunitiesIdentified: 0,
56
+ suggestionsGenerated: 0,
57
+ highImpactRefactors: 0,
58
+ avgConfidence: 0
59
+ }
60
+
61
+ this.qualityAnalyzer = new AIQualityAnalyzer({
62
+ openaiApiKey: this.config.openaiApiKey,
63
+ anthropicApiKey: this.config.anthropicApiKey
64
+ })
65
+
66
+ this.openai = this.config.openaiApiKey ? new OpenAI({ apiKey: this.config.openaiApiKey }) : null
67
+ this.anthropic = this.config.anthropicApiKey ? new Anthropic({ apiKey: this.config.anthropicApiKey }) : null
68
+
69
+ // Refactoring patterns database
70
+ this.refactorPatterns = {
71
+ 'extract-method': {
72
+ description: 'Extract a method from a large function',
73
+ indicators: ['long function', 'duplicate code', 'multiple responsibilities'],
74
+ impact: 'high',
75
+ complexity: 'medium',
76
+ timeEstimate: '1-2 hours'
77
+ },
78
+ 'rename-variable': {
79
+ description: 'Rename variables to be more descriptive',
80
+ indicators: ['bad variable names', 'single letter variables', 'unclear names'],
81
+ impact: 'medium',
82
+ complexity: 'low',
83
+ timeEstimate: '15-30 minutes'
84
+ },
85
+ 'extract-class': {
86
+ description: 'Extract a class from a large class or module',
87
+ indicators: ['god class', 'too many methods', '分散责任'],
88
+ impact: 'high',
89
+ complexity: 'high',
90
+ timeEstimate: '4-8 hours'
91
+ },
92
+ 'move-method': {
93
+ description: 'Move a method to a more appropriate class',
94
+ indicators: ['feature envy', 'inappropriate intimacy'],
95
+ impact: 'medium',
96
+ complexity: 'medium',
97
+ timeEstimate: '1-3 hours'
98
+ },
99
+ 'simplify-conditional': {
100
+ description: 'Simplify complex conditional logic',
101
+ indicators: ['complex conditionals', 'deeply nested ifs'],
102
+ impact: 'high',
103
+ complexity: 'medium',
104
+ timeEstimate: '2-4 hours'
105
+ },
106
+ 'replace-loop-with-pipeline': {
107
+ description: 'Replace loops with functional programming patterns',
108
+ indicators: ['imperative loops', 'manual iteration'],
109
+ impact: 'medium',
110
+ complexity: 'medium',
111
+ timeEstimate: '1-2 hours'
112
+ },
113
+ 'remove-duplicate-code': {
114
+ description: 'Remove duplicate code by creating shared methods',
115
+ indicators: ['duplicate code', 'code clones'],
116
+ impact: 'high',
117
+ complexity: 'low',
118
+ timeEstimate: '30 minutes - 2 hours'
119
+ },
120
+ 'extract-interface': {
121
+ description: 'Extract interface from class for better abstraction',
122
+ indicators: ['concrete implementation only', 'multiple implementations'],
123
+ impact: 'medium',
124
+ complexity: 'medium',
125
+ timeEstimate: '1-3 hours'
126
+ },
127
+ 'introduce-parameter-object': {
128
+ description: 'Replace long parameter lists with parameter object',
129
+ indicators: ['long parameter list', 'parameter groups'],
130
+ impact: 'medium',
131
+ complexity: 'medium',
132
+ timeEstimate: '1-2 hours'
133
+ },
134
+ 'replace-magic-number': {
135
+ description: 'Replace magic numbers with named constants',
136
+ indicators: ['magic numbers', 'hardcoded values'],
137
+ impact: 'low',
138
+ complexity: 'low',
139
+ timeEstimate: '15-30 minutes'
140
+ },
141
+ 'decompose-conditional': {
142
+ description: 'Break down complex conditionals into smaller functions',
143
+ indicators: ['complex conditionals', 'long boolean expressions'],
144
+ impact: 'medium',
145
+ complexity: 'medium',
146
+ timeEstimate: '1-2 hours'
147
+ },
148
+ 'push-down-method': {
149
+ description: 'Move method from parent class to child class',
150
+ indicators: ['method not used by all children'],
151
+ impact: 'medium',
152
+ complexity: 'medium',
153
+ timeEstimate: '2-4 hours'
154
+ },
155
+ 'pull-up-method': {
156
+ description: 'Move method from child class to parent class',
157
+ indicators: ['duplicate methods in children'],
158
+ impact: 'medium',
159
+ complexity: 'medium',
160
+ timeEstimate: '2-4 hours'
161
+ },
162
+ 'change-function-signature': {
163
+ description: 'Change function parameters or return type',
164
+ indicators: ['function needs different params', 'api change needed'],
165
+ impact: 'high',
166
+ complexity: 'high',
167
+ timeEstimate: '2-6 hours'
168
+ },
169
+ 'extract-superclass': {
170
+ description: 'Extract common functionality into superclass',
171
+ indicators: ['duplicate code in siblings', 'shared behavior'],
172
+ impact: 'medium',
173
+ complexity: 'high',
174
+ timeEstimate: '4-8 hours'
175
+ }
176
+ }
177
+ }
178
+
179
+ /**
180
+ * Initialize Refactor Advisor
181
+ */
182
+ async initialize() {
183
+ console.log(chalk.blue('🔧 Initializing AI Refactor Advisor...'))
184
+ console.log(chalk.green(`✓ Loaded ${Object.keys(this.refactorPatterns).length} refactoring patterns`))
185
+ console.log(chalk.green('✓ AI Refactor Advisor initialized'))
186
+ }
187
+
188
+ /**
189
+ * Analyze code and identify refactoring opportunities
190
+ */
191
+ async analyzeCode(codePath, options = {}) {
192
+ console.log(chalk.blue('\n🔧 Analyzing code for refactoring opportunities...'))
193
+
194
+ try {
195
+ // Analyze code quality
196
+ const analysis = await this.qualityAnalyzer.analyzeCode(codePath, {
197
+ languages: options.languages || ['javascript', 'typescript', 'python', 'java'],
198
+ includeTests: options.includeTests || false
199
+ })
200
+
201
+ // Identify refactoring opportunities
202
+ const opportunities = this.identifyOpportunities(analysis)
203
+
204
+ // Generate suggestions
205
+ this.refactorSuggestions = await this.generateSuggestions(opportunities, analysis)
206
+
207
+ // Calculate statistics
208
+ this.stats.opportunitiesIdentified = opportunities.length
209
+ this.stats.suggestionsGenerated = this.refactorSuggestions.length
210
+ this.stats.highImpactRefactors = this.refactorSuggestions.filter(s => s.impact === 'high').length
211
+ this.stats.avgConfidence = this.calculateAverageConfidence()
212
+
213
+ console.log(chalk.green(`✓ Found ${opportunities.length} refactoring opportunities`))
214
+ console.log(chalk.green(`✓ Generated ${this.refactorSuggestions.length} suggestions`))
215
+
216
+ return this.generateReport()
217
+
218
+ } catch (error) {
219
+ console.error(chalk.red('❌ Refactoring analysis failed:'), error.message)
220
+ throw error
221
+ }
222
+ }
223
+
224
+ /**
225
+ * Identify refactoring opportunities from code analysis
226
+ */
227
+ identifyOpportunities(analysis) {
228
+ const opportunities = []
229
+
230
+ for (const fileResult of analysis.files || []) {
231
+ const issues = fileResult.issues || []
232
+
233
+ // Map issues to refactoring opportunities
234
+ issues.forEach(issue => {
235
+ const opportunity = this.mapIssueToRefactor(issue, fileResult)
236
+ if (opportunity) {
237
+ opportunities.push(opportunity)
238
+ }
239
+ })
240
+
241
+ // Additional opportunity detection
242
+ this.detectCodeSmells(fileResult).forEach(opp => {
243
+ opportunities.push(opp)
244
+ })
245
+ }
246
+
247
+ return opportunities
248
+ }
249
+
250
+ /**
251
+ * Map code issues to refactoring opportunities
252
+ */
253
+ mapIssueToRefactor(issue, fileResult) {
254
+ const type = issue.type
255
+ const severity = issue.severity
256
+
257
+ // Map based on issue type
258
+ const refactorMap = {
259
+ 'longFunction': {
260
+ refactorType: 'extract-method',
261
+ confidence: 0.9,
262
+ reason: 'Function is too long and should be split'
263
+ },
264
+ 'badVariableNames': {
265
+ refactorType: 'rename-variable',
266
+ confidence: 0.85,
267
+ reason: 'Variable names are unclear'
268
+ },
269
+ 'duplicateCode': {
270
+ refactorType: 'remove-duplicate-code',
271
+ confidence: 0.95,
272
+ reason: 'Duplicate code found'
273
+ },
274
+ 'highComplexity': {
275
+ refactorType: 'simplify-conditional',
276
+ confidence: 0.8,
277
+ reason: 'High cyclomatic complexity'
278
+ },
279
+ 'magicNumbers': {
280
+ refactorType: 'replace-magic-number',
281
+ confidence: 0.9,
282
+ reason: 'Magic numbers should be replaced with constants'
283
+ },
284
+ 'nestedConditionals': {
285
+ refactorType: 'decompose-conditional',
286
+ confidence: 0.85,
287
+ reason: 'Nested conditionals are hard to understand'
288
+ },
289
+ 'longParameterList': {
290
+ refactorType: 'introduce-parameter-object',
291
+ confidence: 0.8,
292
+ reason: 'Parameter list is too long'
293
+ }
294
+ }
295
+
296
+ const refactorInfo = refactorMap[type]
297
+ if (!refactorInfo) return null
298
+
299
+ return {
300
+ filePath: fileResult.filePath,
301
+ language: fileResult.language,
302
+ issue: issue,
303
+ refactorType: refactorInfo.refactorType,
304
+ confidence: refactorInfo.confidence,
305
+ reason: refactorInfo.reason,
306
+ line: issue.line,
307
+ severity: severity
308
+ }
309
+ }
310
+
311
+ /**
312
+ * Detect code smells not covered by quality analyzer
313
+ */
314
+ detectCodeSmells(fileResult) {
315
+ const opportunities = []
316
+
317
+ // Read file content to analyze
318
+ // This would be implemented with actual file parsing
319
+
320
+ return opportunities
321
+ }
322
+
323
+ /**
324
+ * Generate refactoring suggestions
325
+ */
326
+ async generateSuggestions(opportunities, analysis) {
327
+ const suggestions = []
328
+
329
+ for (const opportunity of opportunities) {
330
+ // Get AI-powered suggestion
331
+ const aiSuggestion = await this.generateAISuggestion(opportunity, analysis)
332
+
333
+ // Enhance with pattern information
334
+ const pattern = this.refactorPatterns[opportunity.refactorType]
335
+ const suggestion = {
336
+ id: this.generateSuggestionId(),
337
+ ...opportunity,
338
+ title: this.generateTitle(opportunity, pattern),
339
+ description: pattern.description,
340
+ impact: pattern.impact,
341
+ complexity: pattern.complexity,
342
+ timeEstimate: pattern.timeEstimate,
343
+ steps: aiSuggestion.steps || this.generateDefaultSteps(opportunity.refactorType),
344
+ beforeCode: aiSuggestion.beforeCode,
345
+ afterCode: aiSuggestion.afterCode,
346
+ benefits: aiSuggestion.benefits || this.generateDefaultBenefits(opportunity.refactorType),
347
+ risks: aiSuggestion.risks || this.generateDefaultRisks(opportunity.refactorType),
348
+ testStrategy: aiSuggestion.testStrategy || this.generateDefaultTestStrategy(opportunity.refactorType),
349
+ dependencies: aiSuggestion.dependencies || [],
350
+ estimatedRisk: this.calculateRisk(opportunity, pattern),
351
+ aiConfidence: aiSuggestion.confidence || opportunity.confidence,
352
+ finalConfidence: (opportunity.confidence + (aiSuggestion.confidence || 0)) / 2
353
+ }
354
+
355
+ suggestions.push(suggestion)
356
+ }
357
+
358
+ // Sort by confidence and impact
359
+ suggestions.sort((a, b) => {
360
+ if (a.impact !== b.impact) {
361
+ const impactOrder = { high: 3, medium: 2, low: 1 }
362
+ return impactOrder[b.impact] - impactOrder[a.impact]
363
+ }
364
+ return b.finalConfidence - a.finalConfidence
365
+ })
366
+
367
+ return suggestions.slice(0, this.config.maxRefactorSuggestions)
368
+ }
369
+
370
+ /**
371
+ * Generate AI-powered suggestion
372
+ */
373
+ async generateAISuggestion(opportunity, analysis) {
374
+ const prompt = `
375
+ As an expert refactoring advisor, provide a detailed refactoring plan:
376
+
377
+ File: ${opportunity.filePath}
378
+ Language: ${opportunity.language}
379
+ Refactoring Type: ${opportunity.refactorType}
380
+ Issue: ${opportunity.reason}
381
+ Line: ${opportunity.line}
382
+
383
+ Provide a detailed refactoring suggestion in JSON format:
384
+ {
385
+ "steps": ["Step 1", "Step 2", "..."],
386
+ "beforeCode": "Code before refactoring",
387
+ "afterCode": "Code after refactoring",
388
+ "benefits": ["Benefit 1", "Benefit 2", "..."],
389
+ "risks": ["Risk 1", "Risk 2", "..."],
390
+ "testStrategy": "How to test this refactoring",
391
+ "confidence": 0.0-1.0
392
+ }
393
+
394
+ Make the steps actionable and specific.
395
+ `
396
+
397
+ try {
398
+ const response = await this.generateWithAI(prompt)
399
+ return JSON.parse(response)
400
+ } catch (error) {
401
+ console.warn(chalk.yellow(`⚠ AI suggestion failed, using template`))
402
+ return this.generateTemplateSuggestion(opportunity)
403
+ }
404
+ }
405
+
406
+ /**
407
+ * Generate template suggestion
408
+ */
409
+ generateTemplateSuggestion(opportunity) {
410
+ const pattern = this.refactorPatterns[opportunity.refactorType]
411
+
412
+ return {
413
+ steps: this.generateDefaultSteps(opportunity.refactorType),
414
+ benefits: this.generateDefaultBenefits(opportunity.refactorType),
415
+ risks: this.generateDefaultRisks(opportunity.refactorType),
416
+ testStrategy: this.generateDefaultTestStrategy(opportunity.refactorType),
417
+ confidence: 0.7
418
+ }
419
+ }
420
+
421
+ /**
422
+ * Generate default steps for refactoring type
423
+ */
424
+ generateDefaultSteps(refactorType) {
425
+ const stepMap = {
426
+ 'extract-method': [
427
+ 'Identify the section of code to extract',
428
+ 'Create a new method with a descriptive name',
429
+ 'Move the code to the new method',
430
+ 'Replace the original code with a call to the new method',
431
+ 'Pass parameters as needed',
432
+ 'Update tests to cover the new method'
433
+ ],
434
+ 'rename-variable': [
435
+ 'Choose a more descriptive name',
436
+ 'Find all occurrences of the variable',
437
+ 'Rename all occurrences consistently',
438
+ 'Update comments that reference the old name',
439
+ 'Run tests to ensure no breakage'
440
+ ],
441
+ 'remove-duplicate-code': [
442
+ 'Identify the duplicate code section',
443
+ 'Create a shared method or constant',
444
+ 'Replace all instances with calls to the shared code',
445
+ 'Ensure the shared code handles all use cases',
446
+ 'Update tests accordingly'
447
+ ],
448
+ 'simplify-conditional': [
449
+ 'Analyze the complex conditional logic',
450
+ 'Extract complex conditions into well-named methods',
451
+ 'Use early returns to reduce nesting',
452
+ 'Simplify boolean expressions',
453
+ 'Add comments to explain the logic',
454
+ 'Test all code paths'
455
+ ]
456
+ }
457
+
458
+ return stepMap[refactorType] || [
459
+ 'Analyze the code to be refactored',
460
+ 'Plan the refactoring approach',
461
+ 'Implement the refactoring',
462
+ 'Test thoroughly',
463
+ 'Review the changes'
464
+ ]
465
+ }
466
+
467
+ /**
468
+ * Generate default benefits
469
+ */
470
+ generateDefaultBenefits(refactorType) {
471
+ const benefitMap = {
472
+ 'extract-method': [
473
+ 'Improved readability',
474
+ 'Reduced code duplication',
475
+ 'Easier to test individual methods',
476
+ 'Better adherence to Single Responsibility Principle'
477
+ ],
478
+ 'rename-variable': [
479
+ 'Improved code readability',
480
+ 'Reduced cognitive load',
481
+ 'Easier maintenance',
482
+ 'Self-documenting code'
483
+ ],
484
+ 'remove-duplicate-code': [
485
+ 'Reduced maintenance burden',
486
+ 'Single source of truth',
487
+ 'Easier bug fixes',
488
+ 'Reduced code size'
489
+ ]
490
+ }
491
+
492
+ return benefitMap[refactorType] || [
493
+ 'Improved code quality',
494
+ 'Better maintainability',
495
+ 'Enhanced readability'
496
+ ]
497
+ }
498
+
499
+ /**
500
+ * Generate default risks
501
+ */
502
+ generateDefaultRisks(refactorType) {
503
+ return [
504
+ 'Potential to introduce bugs if not tested thoroughly',
505
+ 'May affect existing code that depends on the current structure',
506
+ 'Requires comprehensive testing',
507
+ 'May need to update documentation'
508
+ ]
509
+ }
510
+
511
+ /**
512
+ * Generate default test strategy
513
+ */
514
+ generateDefaultTestStrategy(refactorType) {
515
+ return [
516
+ 'Run existing test suite before refactoring',
517
+ 'Write tests for the refactored code if not present',
518
+ 'Run tests after each step of refactoring',
519
+ 'Perform integration testing',
520
+ 'Review test coverage'
521
+ ]
522
+ }
523
+
524
+ /**
525
+ * Generate suggestion title
526
+ */
527
+ generateTitle(opportunity, pattern) {
528
+ return `${this.titleCase(opportunity.refactorType)} in ${path.basename(opportunity.filePath)}`
529
+ }
530
+
531
+ /**
532
+ * Calculate risk for refactoring
533
+ */
534
+ calculateRisk(opportunity, pattern) {
535
+ let riskScore = 0
536
+
537
+ // Higher risk for high impact
538
+ if (pattern.impact === 'high') riskScore += 3
539
+ else if (pattern.impact === 'medium') riskScore += 2
540
+ else riskScore += 1
541
+
542
+ // Higher risk for high complexity
543
+ if (pattern.complexity === 'high') riskScore += 3
544
+ else if (pattern.complexity === 'medium') riskScore += 2
545
+ else riskScore += 1
546
+
547
+ // Adjust based on confidence (inverse relationship)
548
+ riskScore = riskScore * (1 - opportunity.confidence)
549
+
550
+ if (riskScore >= 5) return 'high'
551
+ if (riskScore >= 3) return 'medium'
552
+ return 'low'
553
+ }
554
+
555
+ /**
556
+ * Calculate average confidence
557
+ */
558
+ calculateAverageConfidence() {
559
+ if (this.refactorSuggestions.length === 0) return 0
560
+
561
+ const total = this.refactorSuggestions.reduce((sum, s) => sum + s.finalConfidence, 0)
562
+ return (total / this.refactorSuggestions.length).toFixed(2)
563
+ }
564
+
565
+ /**
566
+ * Generate comprehensive refactoring report
567
+ */
568
+ generateReport() {
569
+ const report = {
570
+ timestamp: new Date().toISOString(),
571
+ summary: {
572
+ totalOpportunities: this.stats.opportunitiesIdentified,
573
+ totalSuggestions: this.stats.suggestionsGenerated,
574
+ highImpactRefactors: this.stats.highImpactRefactors,
575
+ averageConfidence: this.stats.avgConfidence,
576
+ refactoringTypes: [...new Set(this.refactorSuggestions.map(s => s.refactorType))].length
577
+ },
578
+ suggestions: this.refactorSuggestions,
579
+ groupedByImpact: this.groupByImpact(),
580
+ phasedPlan: this.generatePhasedPlan(),
581
+ statistics: this.stats
582
+ }
583
+
584
+ return report
585
+ }
586
+
587
+ /**
588
+ * Group suggestions by impact
589
+ */
590
+ groupByImpact() {
591
+ const grouped = {
592
+ high: [],
593
+ medium: [],
594
+ low: []
595
+ }
596
+
597
+ this.refactorSuggestions.forEach(suggestion => {
598
+ grouped[suggestion.impact].push(suggestion)
599
+ })
600
+
601
+ return grouped
602
+ }
603
+
604
+ /**
605
+ * Generate phased refactoring plan
606
+ */
607
+ generatePhasedPlan() {
608
+ // Phase 1: Low risk, high confidence
609
+ const phase1 = this.refactorSuggestions.filter(s =>
610
+ s.estimatedRisk === 'low' && s.finalConfidence >= this.config.confidenceThreshold
611
+ )
612
+
613
+ // Phase 2: Medium risk or medium confidence
614
+ const phase2 = this.refactorSuggestions.filter(s =>
615
+ s.estimatedRisk === 'medium' && s.finalConfidence >= 0.7
616
+ )
617
+
618
+ // Phase 3: High risk or lower confidence
619
+ const phase3 = this.refactorSuggestions.filter(s =>
620
+ s.estimatedRisk === 'high' || s.finalConfidence < 0.7
621
+ )
622
+
623
+ return {
624
+ phase1: {
625
+ name: 'Quick Wins',
626
+ description: 'Low risk, high confidence refactoring',
627
+ suggestions: phase1,
628
+ estimatedTime: this.calculatePhaseTime(phase1)
629
+ },
630
+ phase2: {
631
+ name: 'Moderate Changes',
632
+ description: 'Medium risk, moderate impact refactoring',
633
+ suggestions: phase2,
634
+ estimatedTime: this.calculatePhaseTime(phase2)
635
+ },
636
+ phase3: {
637
+ name: 'Major Refactoring',
638
+ description: 'High risk, complex refactoring',
639
+ suggestions: phase3,
640
+ estimatedTime: this.calculatePhaseTime(phase3)
641
+ }
642
+ }
643
+ }
644
+
645
+ /**
646
+ * Calculate time for a phase
647
+ */
648
+ calculatePhaseTime(suggestions) {
649
+ const totalHours = suggestions.reduce((sum, s) => {
650
+ const hours = this.parseTimeEstimate(s.timeEstimate)
651
+ return sum + hours
652
+ }, 0)
653
+
654
+ if (totalHours < 1) return 'Less than 1 hour'
655
+ if (totalHours < 8) return `${Math.ceil(totalHours)} hours`
656
+ return `${Math.ceil(totalHours / 8)} days`
657
+ }
658
+
659
+ /**
660
+ * Parse time estimate string to hours
661
+ */
662
+ parseTimeEstimate(estimate) {
663
+ if (estimate.includes('minutes')) {
664
+ const match = estimate.match(/(\d+)/)
665
+ return match ? parseInt(match[1]) / 60 : 0.5
666
+ }
667
+ if (estimate.includes('hour')) {
668
+ const match = estimate.match(/(\d+)/)
669
+ return match ? parseInt(match[1]) : 1
670
+ }
671
+ if (estimate.includes('day')) {
672
+ const match = estimate.match(/(\d+)/)
673
+ return match ? parseInt(match[1]) * 8 : 8
674
+ }
675
+ return 1 // Default
676
+ }
677
+
678
+ /**
679
+ * Display refactoring suggestions
680
+ */
681
+ displaySuggestions(report) {
682
+ console.log(chalk.blue('\n' + '='.repeat(70)))
683
+ console.log(chalk.blue('🔧 REFACTORING ADVISOR REPORT'))
684
+ console.log(chalk.blue('='.repeat(70)))
685
+
686
+ // Summary
687
+ console.log(chalk.cyan('\n📊 Summary:'))
688
+ console.log(` Opportunities Found: ${report.summary.totalOpportunities}`)
689
+ console.log(` Suggestions Generated: ${report.summary.totalSuggestions}`)
690
+ console.log(` High Impact Refactors: ${report.summary.highImpactRefactors}`)
691
+ console.log(` Average Confidence: ${report.summary.averageConfidence}`)
692
+ console.log(` Refactoring Types: ${report.summary.refactoringTypes}`)
693
+
694
+ // Phased plan
695
+ console.log(chalk.cyan('\n📋 Phased Refactoring Plan:'))
696
+ Object.entries(report.phasedPlan).forEach(([phaseKey, phase]) => {
697
+ if (phase.suggestions.length === 0) return
698
+
699
+ console.log(chalk.green(`\n ${phase.name}:`))
700
+ console.log(` ${phase.description}`)
701
+ console.log(` Estimated Time: ${phase.estimatedTime}`)
702
+ console.log(` Suggestions: ${phase.suggestions.length}`)
703
+
704
+ // Show top 3 suggestions
705
+ phase.suggestions.slice(0, 3).forEach(s => {
706
+ console.log(chalk.yellow(` • ${s.title}`))
707
+ })
708
+
709
+ if (phase.suggestions.length > 3) {
710
+ console.log(` ... and ${phase.suggestions.length - 3} more`)
711
+ }
712
+ })
713
+
714
+ // Top suggestions
715
+ console.log(chalk.cyan('\n⭐ Top Suggestions:'))
716
+ report.suggestions.slice(0, 5).forEach((s, i) => {
717
+ console.log(chalk.green(`\n ${i + 1}. ${s.title}`))
718
+ console.log(` Type: ${s.refactorType}`)
719
+ console.log(` Impact: ${s.impact}, Risk: ${s.estimatedRisk}`)
720
+ console.log(` Confidence: ${(s.finalConfidence * 100).toFixed(0)}%`)
721
+ console.log(` Time: ${s.timeEstimate}`)
722
+ console.log(` Steps: ${s.steps.length} steps`)
723
+ })
724
+
725
+ console.log(chalk.blue('\n' + '='.repeat(70)))
726
+ }
727
+
728
+ /**
729
+ * Export report
730
+ */
731
+ async exportReport(report, format = 'json', outputPath) {
732
+ if (!outputPath) {
733
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-')
734
+ outputPath = path.join(process.cwd(), `refactor-report-${timestamp}.${format}`)
735
+ }
736
+
737
+ if (format === 'json') {
738
+ await fs.writeJson(outputPath, report, { spaces: 2 })
739
+ } else if (format === 'markdown') {
740
+ const markdown = this.generateMarkdownReport(report)
741
+ await fs.writeFile(outputPath, markdown)
742
+ }
743
+
744
+ console.log(chalk.green(`✓ Report exported to: ${outputPath}`))
745
+ }
746
+
747
+ /**
748
+ * Generate Markdown report
749
+ */
750
+ generateMarkdownReport(report) {
751
+ let markdown = `# Refactoring Advisor Report
752
+
753
+ Generated: ${report.timestamp}
754
+
755
+ ## Summary
756
+
757
+ - **Total Opportunities:** ${report.summary.totalOpportunities}
758
+ - **Suggestions Generated:** ${report.summary.totalSuggestions}
759
+ - **High Impact Refactors:** ${report.summary.highImpactRefactors}
760
+ - **Average Confidence:** ${report.summary.averageConfidence}
761
+ - **Refactoring Types:** ${report.summary.refactoringTypes}
762
+
763
+ ## Phased Plan
764
+
765
+ `
766
+
767
+ Object.entries(report.phasedPlan).forEach(([phaseKey, phase]) => {
768
+ if (phase.suggestions.length === 0) return
769
+
770
+ markdown += `### ${phase.name}\n\n`
771
+ markdown += `${phase.description}\n\n`
772
+ markdown += `**Estimated Time:** ${phase.estimatedTime}\n\n`
773
+ markdown += `**Suggestions:** ${phase.suggestions.length}\n\n`
774
+
775
+ phase.suggestions.forEach(s => {
776
+ markdown += `#### ${s.title}\n\n`
777
+ markdown += `- **Type:** ${s.refactorType}\n`
778
+ markdown += `- **Impact:** ${s.impact}\n`
779
+ markdown += `- **Risk:** ${s.estimatedRisk}\n`
780
+ markdown += `- **Confidence:** ${(s.finalConfidence * 100).toFixed(0)}%\n`
781
+ markdown += `- **Time Estimate:** ${s.timeEstimate}\n\n`
782
+ markdown += `**Description:** ${s.description}\n\n`
783
+ markdown += `**Steps:**\n`
784
+ s.steps.forEach(step => markdown += `1. ${step}\n`)
785
+ markdown += `\n`
786
+ })
787
+ })
788
+
789
+ return markdown
790
+ }
791
+
792
+ /**
793
+ * Generate suggestion ID
794
+ */
795
+ generateSuggestionId() {
796
+ return `REF-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`
797
+ }
798
+
799
+ /**
800
+ * Title case helper
801
+ */
802
+ titleCase(str) {
803
+ return str.replace(/-/g, ' ')
804
+ .split(' ')
805
+ .map(word => word.charAt(0).toUpperCase() + word.slice(1))
806
+ .join(' ')
807
+ }
808
+
809
+ /**
810
+ * Generate with AI
811
+ */
812
+ async generateWithAI(prompt) {
813
+ // Try OpenAI first
814
+ if (this.openai) {
815
+ try {
816
+ const response = await this.openai.chat.completions.create({
817
+ model: 'gpt-4',
818
+ messages: [{ role: 'user', content: prompt }],
819
+ temperature: 0.7,
820
+ max_tokens: 2000
821
+ })
822
+ return response.choices[0].message.content
823
+ } catch (error) {
824
+ console.warn(chalk.yellow('OpenAI failed, trying Anthropic...'))
825
+ }
826
+ }
827
+
828
+ // Try Anthropic
829
+ if (this.anthropic) {
830
+ try {
831
+ const response = await this.anthropic.messages.create({
832
+ model: 'claude-3-5-sonnet-20241022',
833
+ max_tokens: 2000,
834
+ messages: [{ role: 'user', content: prompt }]
835
+ })
836
+ return response.content[0].text
837
+ } catch (error) {
838
+ console.warn(chalk.yellow('Anthropic failed, using mock mode'))
839
+ }
840
+ }
841
+
842
+ // Mock mode
843
+ return JSON.stringify({
844
+ steps: ['Step 1', 'Step 2', 'Step 3'],
845
+ benefits: ['Improved code quality'],
846
+ risks: ['May need comprehensive testing'],
847
+ testStrategy: 'Run all tests before and after',
848
+ confidence: 0.8
849
+ })
850
+ }
851
+ }
852
+
853
+ module.exports = AIRefactorAdvisor