aios-core 4.2.13 → 4.2.15

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.
Files changed (95) hide show
  1. package/.aios-core/core/code-intel/helpers/dev-helper.js +206 -0
  2. package/.aios-core/core/registry/registry-schema.json +166 -166
  3. package/.aios-core/core/synapse/diagnostics/collectors/hook-collector.js +3 -3
  4. package/.aios-core/data/entity-registry.yaml +27 -0
  5. package/.aios-core/development/scripts/approval-workflow.js +642 -642
  6. package/.aios-core/development/scripts/backup-manager.js +606 -606
  7. package/.aios-core/development/scripts/branch-manager.js +389 -389
  8. package/.aios-core/development/scripts/code-quality-improver.js +1311 -1311
  9. package/.aios-core/development/scripts/commit-message-generator.js +849 -849
  10. package/.aios-core/development/scripts/conflict-resolver.js +674 -674
  11. package/.aios-core/development/scripts/dependency-analyzer.js +637 -637
  12. package/.aios-core/development/scripts/diff-generator.js +351 -351
  13. package/.aios-core/development/scripts/elicitation-engine.js +384 -384
  14. package/.aios-core/development/scripts/elicitation-session-manager.js +299 -299
  15. package/.aios-core/development/scripts/git-wrapper.js +461 -461
  16. package/.aios-core/development/scripts/manifest-preview.js +244 -244
  17. package/.aios-core/development/scripts/metrics-tracker.js +775 -775
  18. package/.aios-core/development/scripts/modification-validator.js +554 -554
  19. package/.aios-core/development/scripts/pattern-learner.js +1224 -1224
  20. package/.aios-core/development/scripts/performance-analyzer.js +757 -757
  21. package/.aios-core/development/scripts/refactoring-suggester.js +1138 -1138
  22. package/.aios-core/development/scripts/rollback-handler.js +530 -530
  23. package/.aios-core/development/scripts/security-checker.js +358 -358
  24. package/.aios-core/development/scripts/template-engine.js +239 -239
  25. package/.aios-core/development/scripts/template-validator.js +278 -278
  26. package/.aios-core/development/scripts/test-generator.js +843 -843
  27. package/.aios-core/development/scripts/transaction-manager.js +589 -589
  28. package/.aios-core/development/scripts/usage-tracker.js +673 -673
  29. package/.aios-core/development/scripts/validate-filenames.js +226 -226
  30. package/.aios-core/development/scripts/version-tracker.js +526 -526
  31. package/.aios-core/development/scripts/yaml-validator.js +396 -396
  32. package/.aios-core/development/tasks/build-autonomous.md +10 -4
  33. package/.aios-core/development/tasks/create-service.md +23 -0
  34. package/.aios-core/development/tasks/dev-develop-story.md +12 -6
  35. package/.aios-core/development/tasks/dev-suggest-refactoring.md +7 -1
  36. package/.aios-core/development/tasks/publish-npm.md +3 -3
  37. package/.aios-core/hooks/unified/README.md +1 -1
  38. package/.aios-core/install-manifest.yaml +65 -61
  39. package/.aios-core/manifests/schema/manifest-schema.json +190 -190
  40. package/.aios-core/product/templates/component-react-tmpl.tsx +98 -98
  41. package/.aios-core/product/templates/engine/schemas/adr.schema.json +102 -102
  42. package/.aios-core/product/templates/engine/schemas/dbdr.schema.json +205 -205
  43. package/.aios-core/product/templates/engine/schemas/epic.schema.json +175 -175
  44. package/.aios-core/product/templates/engine/schemas/pmdr.schema.json +175 -175
  45. package/.aios-core/product/templates/engine/schemas/prd-v2.schema.json +300 -300
  46. package/.aios-core/product/templates/engine/schemas/prd.schema.json +152 -152
  47. package/.aios-core/product/templates/engine/schemas/story.schema.json +222 -222
  48. package/.aios-core/product/templates/engine/schemas/task.schema.json +154 -154
  49. package/.aios-core/product/templates/eslintrc-security.json +32 -32
  50. package/.aios-core/product/templates/github-actions-cd.yml +212 -212
  51. package/.aios-core/product/templates/github-actions-ci.yml +172 -172
  52. package/.aios-core/product/templates/shock-report-tmpl.html +502 -502
  53. package/.aios-core/product/templates/token-exports-css-tmpl.css +240 -240
  54. package/.aios-core/quality/schemas/quality-metrics.schema.json +233 -233
  55. package/.aios-core/scripts/migrate-framework-docs.sh +300 -300
  56. package/README.en.md +747 -0
  57. package/README.md +4 -2
  58. package/bin/aios.js +7 -4
  59. package/package.json +1 -1
  60. package/packages/aios-pro-cli/src/recover.js +1 -1
  61. package/packages/installer/src/wizard/ide-config-generator.js +6 -6
  62. package/packages/installer/src/wizard/pro-setup.js +3 -3
  63. package/pro/license/degradation.js +220 -220
  64. package/pro/license/errors.js +450 -450
  65. package/pro/license/feature-gate.js +354 -354
  66. package/pro/license/index.js +181 -181
  67. package/pro/license/license-cache.js +523 -523
  68. package/pro/license/license-crypto.js +303 -303
  69. package/scripts/package-synapse.js +5 -5
  70. package/scripts/validate-package-completeness.js +3 -3
  71. package/.aios-core/.session/current-session.json +0 -14
  72. package/.aios-core/data/registry-update-log.jsonl +0 -191
  73. package/.aios-core/docs/SHARD-TRANSLATION-GUIDE.md +0 -335
  74. package/.aios-core/docs/component-creation-guide.md +0 -458
  75. package/.aios-core/docs/session-update-pattern.md +0 -307
  76. package/.aios-core/docs/standards/AIOS-FRAMEWORK-MASTER.md +0 -1963
  77. package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO-V2.1-SUMMARY.md +0 -1190
  78. package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO-V2.1.md +0 -439
  79. package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO.md +0 -5398
  80. package/.aios-core/docs/standards/V3-ARCHITECTURAL-DECISIONS.md +0 -523
  81. package/.aios-core/docs/template-syntax.md +0 -267
  82. package/.aios-core/docs/troubleshooting-guide.md +0 -625
  83. package/.aios-core/infrastructure/tests/utilities-audit-results.json +0 -501
  84. package/.aios-core/manifests/agents.csv +0 -29
  85. package/.aios-core/manifests/tasks.csv +0 -198
  86. package/.aios-core/manifests/workers.csv +0 -204
  87. package/.claude/rules/agent-authority.md +0 -105
  88. package/.claude/rules/coderabbit-integration.md +0 -93
  89. package/.claude/rules/ids-principles.md +0 -112
  90. package/.claude/rules/story-lifecycle.md +0 -139
  91. package/.claude/rules/workflow-execution.md +0 -150
  92. package/scripts/glue/README.md +0 -355
  93. package/scripts/glue/compose-agent-prompt.cjs +0 -362
  94. /package/.claude/hooks/{precompact-session-digest.js → precompact-session-digest.cjs} +0 -0
  95. /package/.claude/hooks/{synapse-engine.js → synapse-engine.cjs} +0 -0
@@ -1,1139 +1,1139 @@
1
- const fs = require('fs').promises;
2
- const path = require('path');
3
- const chalk = require('chalk');
4
- const { parse } = require('@babel/parser');
5
- const traverse = require('@babel/traverse').default;
6
- const generate = require('@babel/generator').default;
7
- const _t = require('@babel/types');
8
-
9
- /**
10
- * Automated refactoring suggestion system
11
- * Analyzes code and suggests refactoring opportunities
12
- */
13
- class RefactoringSuggester {
14
- constructor(options = {}) {
15
- this.rootPath = options.rootPath || process.cwd();
16
- this.suggestions = [];
17
- this.refactoringPatterns = new Map();
18
- this.codeMetrics = new Map();
19
- this.initializePatterns();
20
- }
21
-
22
- /**
23
- * Initialize refactoring patterns
24
- */
25
- initializePatterns() {
26
- // Method extraction pattern
27
- this.refactoringPatterns.set('extract_method', {
28
- name: 'Extract Method',
29
- description: 'Extract long methods into smaller, focused methods',
30
- detector: this.detectLongMethods.bind(this),
31
- suggester: this.suggestMethodExtraction.bind(this),
32
- priority: 'high'
33
- });
34
-
35
- // Variable extraction pattern
36
- this.refactoringPatterns.set('extract_variable', {
37
- name: 'Extract Variable',
38
- description: 'Extract complex expressions into named variables',
39
- detector: this.detectComplexExpressions.bind(this),
40
- suggester: this.suggestVariableExtraction.bind(this),
41
- priority: 'medium'
42
- });
43
-
44
- // Parameter object pattern
45
- this.refactoringPatterns.set('introduce_parameter_object', {
46
- name: 'Introduce Parameter Object',
47
- description: 'Group related parameters into an object',
48
- detector: this.detectLongParameterLists.bind(this),
49
- suggester: this.suggestParameterObject.bind(this),
50
- priority: 'medium'
51
- });
52
-
53
- // Replace conditional with polymorphism
54
- this.refactoringPatterns.set('replace_conditional', {
55
- name: 'Replace Conditional with Polymorphism',
56
- description: 'Replace complex conditionals with polymorphic behavior',
57
- detector: this.detectComplexConditionals.bind(this),
58
- suggester: this.suggestPolymorphism.bind(this),
59
- priority: 'high'
60
- });
61
-
62
- // Inline temp pattern
63
- this.refactoringPatterns.set('inline_temp', {
64
- name: 'Inline Temporary Variable',
65
- description: 'Replace temporary variables used only once',
66
- detector: this.detectSingleUseTempVariables.bind(this),
67
- suggester: this.suggestInlineTemp.bind(this),
68
- priority: 'low'
69
- });
70
-
71
- // Remove dead code
72
- this.refactoringPatterns.set('remove_dead_code', {
73
- name: 'Remove Dead Code',
74
- description: 'Remove unreachable or unused code',
75
- detector: this.detectDeadCode.bind(this),
76
- suggester: this.suggestDeadCodeRemoval.bind(this),
77
- priority: 'high'
78
- });
79
-
80
- // Consolidate duplicate code
81
- this.refactoringPatterns.set('consolidate_duplicates', {
82
- name: 'Consolidate Duplicate Code',
83
- description: 'Extract duplicate code into shared functions',
84
- detector: this.detectDuplicateCode.bind(this),
85
- suggester: this.suggestCodeConsolidation.bind(this),
86
- priority: 'high'
87
- });
88
-
89
- // Simplify nested conditionals
90
- this.refactoringPatterns.set('simplify_conditionals', {
91
- name: 'Simplify Nested Conditionals',
92
- description: 'Flatten deeply nested if-else chains',
93
- detector: this.detectNestedConditionals.bind(this),
94
- suggester: this.suggestConditionalSimplification.bind(this),
95
- priority: 'medium'
96
- });
97
-
98
- // Replace magic numbers
99
- this.refactoringPatterns.set('replace_magic_numbers', {
100
- name: 'Replace Magic Numbers',
101
- description: 'Replace hard-coded numbers with named constants',
102
- detector: this.detectMagicNumbers.bind(this),
103
- suggester: this.suggestConstantExtraction.bind(this),
104
- priority: 'low'
105
- });
106
-
107
- // Decompose complex class
108
- this.refactoringPatterns.set('decompose_class', {
109
- name: 'Decompose Complex Class',
110
- description: 'Split large classes into smaller, focused classes',
111
- detector: this.detectLargeClasses.bind(this),
112
- suggester: this.suggestClassDecomposition.bind(this),
113
- priority: 'high'
114
- });
115
- }
116
-
117
- /**
118
- * Analyze code and suggest refactorings
119
- */
120
- async analyzeCode(filePath, options = {}) {
121
- console.log(chalk.blue(`🔍 Analyzing: ${filePath}`));
122
-
123
- try {
124
- const _content = await fs.readFile(filePath, 'utf-8');
125
- const fileType = path.extname(filePath);
126
-
127
- if (!['.js', '.jsx', '.ts', '.tsx'].includes(fileType)) {
128
- return {
129
- filePath,
130
- suggestions: [],
131
- error: 'Unsupported file type'
132
- };
133
- }
134
-
135
- // Parse code
136
- const _ast = this.parseCode(_content, filePath);
137
-
138
- // Calculate code metrics
139
- const _metrics = this.calculateCodeMetrics(_ast, content);
140
- this.codeMetrics.set(filePath, metrics);
141
-
142
- // Clear previous suggestions
143
- this.suggestions = [];
144
-
145
- // Run all refactoring detectors
146
- for (const [patternId, pattern] of this.refactoringPatterns) {
147
- if (options.patterns && !options.patterns.includes(patternId)) {
148
- continue; // Skip if not in requested patterns
149
- }
150
-
151
- try {
152
- const detected = await pattern.detector(_ast, _content, metrics);
153
- if (detected && detected.length > 0) {
154
- for (const _detection of detected) {
155
- const suggestion = await pattern.suggester(_detection, _ast, content);
156
- if (suggestion) {
157
- this.suggestions.push({
158
- ...suggestion,
159
- patternId,
160
- pattern: pattern.name,
161
- priority: pattern.priority,
162
- filePath
163
- });
164
- }
165
- }
166
- }
167
- } catch (error) {
168
- console.warn(chalk.yellow(`Failed to run ${pattern.name}: ${error.message}`));
169
- }
170
- }
171
-
172
- // Sort suggestions by priority and impact
173
- this.suggestions.sort((a, b) => {
174
- const priorityOrder = { high: 3, medium: 2, low: 1 };
175
- const priorityDiff = priorityOrder[b.priority] - priorityOrder[a.priority];
176
- if (priorityDiff !== 0) return priorityDiff;
177
- return (b.impact || 0) - (a.impact || 0);
178
- });
179
-
180
- return {
181
- filePath,
182
- _metrics,
183
- suggestions: this.suggestions
184
- };
185
-
186
- } catch (error) {
187
- return {
188
- filePath,
189
- suggestions: [],
190
- error: error.message
191
- };
192
- }
193
- }
194
-
195
- /**
196
- * Parse code into AST
197
- */
198
- parseCode(_content, filePath) {
199
- const parserOptions = {
200
- sourceType: 'module',
201
- plugins: [
202
- 'jsx',
203
- 'typescript',
204
- 'decorators-legacy',
205
- 'classProperties',
206
- 'asyncGenerators',
207
- 'dynamicImport',
208
- 'optionalChaining',
209
- 'nullishCoalescingOperator'
210
- ],
211
- errorRecovery: true
212
- };
213
-
214
- try {
215
- return parse(_content, parserOptions);
216
- } catch (error) {
217
- console.warn(chalk.yellow(`Parse error in ${filePath}: ${error.message}`));
218
- // Try with more lenient options
219
- return parse(_content, { ...parserOptions, errorRecovery: true });
220
- }
221
- }
222
-
223
- /**
224
- * Calculate code metrics
225
- */
226
- calculateCodeMetrics(_ast, content) {
227
- const _metrics = {
228
- lines: content.split('\n').length,
229
- functions: 0,
230
- classes: 0,
231
- complexity: 0,
232
- maxNesting: 0,
233
- duplicateBlocks: 0,
234
- comments: 0,
235
- imports: 0
236
- };
237
-
238
- let currentNesting = 0;
239
-
240
- traverse(_ast, {
241
- FunctionDeclaration: () => metrics.functions++,
242
- FunctionExpression: () => metrics.functions++,
243
- ArrowFunctionExpression: () => metrics.functions++,
244
- ClassDeclaration: () => metrics.classes++,
245
- ImportDeclaration: () => metrics.imports++,
246
-
247
- IfStatement: {
248
- enter: () => {
249
- metrics.complexity++;
250
- currentNesting++;
251
- metrics.maxNesting = Math.max(metrics.maxNesting, currentNesting);
252
- },
253
- exit: () => currentNesting--
254
- },
255
-
256
- SwitchStatement: () => metrics.complexity += 2,
257
- ForStatement: () => metrics.complexity++,
258
- WhileStatement: () => metrics.complexity++,
259
- DoWhileStatement: () => metrics.complexity++,
260
- ConditionalExpression: () => metrics.complexity++,
261
- LogicalExpression: (path) => {
262
- if (path.node.operator === '&&' || path.node.operator === '||') {
263
- metrics.complexity++;
264
- }
265
- },
266
-
267
- Comment: () => metrics.comments++
268
- });
269
-
270
- return metrics;
271
- }
272
-
273
- // Refactoring detectors
274
-
275
- async detectLongMethods(_ast, _content, metrics) {
276
- const longMethods = [];
277
- const methodSizeThreshold = 30; // lines
278
-
279
- traverse(_ast, {
280
- 'FunctionDeclaration|FunctionExpression|ArrowFunctionExpression': (path) => {
281
- const start = path.node.loc.start.line;
282
- const end = path.node.loc.end.line;
283
- const methodLines = end - start + 1;
284
-
285
- if (methodLines > methodSizeThreshold) {
286
- const methodName = this.getMethodName(path);
287
- longMethods.push({
288
- type: 'long_method',
289
- node: path.node,
290
- path: path,
291
- name: methodName,
292
- lines: methodLines,
293
- startLine: start,
294
- endLine: end,
295
- complexity: this.calculateMethodComplexity(path)
296
- });
297
- }
298
- }
299
- });
300
-
301
- return longMethods;
302
- }
303
-
304
- async detectComplexExpressions(_ast, _content, metrics) {
305
- const complexExpressions = [];
306
- const complexityThreshold = 3; // nesting/chaining depth
307
-
308
- traverse(_ast, {
309
- Expression: (path) => {
310
- const complexity = this.calculateExpressionComplexity(path.node);
311
- if (complexity > complexityThreshold) {
312
- complexExpressions.push({
313
- type: 'complex_expression',
314
- node: path.node,
315
- path: path,
316
- complexity: complexity,
317
- startLine: path.node.loc?.start.line,
318
- endLine: path.node.loc?.end.line
319
- });
320
- }
321
- }
322
- });
323
-
324
- return complexExpressions;
325
- }
326
-
327
- async detectLongParameterLists(_ast, _content, metrics) {
328
- const longParameterLists = [];
329
- const parameterThreshold = 4;
330
-
331
- traverse(_ast, {
332
- 'FunctionDeclaration|FunctionExpression|ArrowFunctionExpression': (path) => {
333
- const params = path.node.params;
334
- if (params.length > parameterThreshold) {
335
- const methodName = this.getMethodName(path);
336
- longParameterLists.push({
337
- type: 'long_parameter_list',
338
- node: path.node,
339
- path: path,
340
- name: methodName,
341
- parameterCount: params.length,
342
- parameters: params.map(p => p.name || 'unknown'),
343
- startLine: path.node.loc?.start.line
344
- });
345
- }
346
- }
347
- });
348
-
349
- return longParameterLists;
350
- }
351
-
352
- async detectComplexConditionals(_ast, _content, metrics) {
353
- const complexConditionals = [];
354
- const branchThreshold = 4;
355
-
356
- traverse(_ast, {
357
- IfStatement: (path) => {
358
- const branches = this.countConditionalBranches(path);
359
- if (branches > branchThreshold) {
360
- complexConditionals.push({
361
- type: 'complex_conditional',
362
- node: path.node,
363
- path: path,
364
- branches: branches,
365
- startLine: path.node.loc?.start.line,
366
- endLine: path.node.loc?.end.line
367
- });
368
- }
369
- },
370
-
371
- SwitchStatement: (path) => {
372
- const cases = path.node.cases.length;
373
- if (cases > branchThreshold) {
374
- complexConditionals.push({
375
- type: 'complex_switch',
376
- node: path.node,
377
- path: path,
378
- cases: cases,
379
- startLine: path.node.loc?.start.line,
380
- endLine: path.node.loc?.end.line
381
- });
382
- }
383
- }
384
- });
385
-
386
- return complexConditionals;
387
- }
388
-
389
- async detectSingleUseTempVariables(_ast, _content, metrics) {
390
- const singleUseVars = [];
391
- const varUsage = new Map();
392
-
393
- // First pass: collect all variable declarations and usages
394
- traverse(_ast, {
395
- VariableDeclarator: (path) => {
396
- if (path.node.id.type === 'Identifier') {
397
- const varName = path.node.id.name;
398
- if (!varUsage.has(varName)) {
399
- varUsage.set(varName, {
400
- declaration: path,
401
- uses: []
402
- });
403
- }
404
- }
405
- },
406
-
407
- Identifier: (path) => {
408
- if (path.isReferencedIdentifier()) {
409
- const varName = path.node.name;
410
- if (varUsage.has(varName)) {
411
- varUsage.get(varName).uses.push(path);
412
- }
413
- }
414
- }
415
- });
416
-
417
- // Second pass: find single-use variables
418
- for (const [varName, usage] of varUsage) {
419
- if (usage.uses.length === 1 && usage.declaration.node.init) {
420
- singleUseVars.push({
421
- type: 'single_use_temp',
422
- name: varName,
423
- declaration: usage.declaration,
424
- use: usage.uses[0],
425
- startLine: usage.declaration.node.loc?.start.line
426
- });
427
- }
428
- }
429
-
430
- return singleUseVars;
431
- }
432
-
433
- async detectDeadCode(_ast, _content, metrics) {
434
- const deadCode = [];
435
-
436
- traverse(_ast, {
437
- // Unreachable code after return/throw
438
- 'ReturnStatement|ThrowStatement': (path) => {
439
- const parent = path.parent;
440
- if (parent.type === 'BlockStatement') {
441
- const siblings = parent.body;
442
- const currentIndex = siblings.indexOf(path.node);
443
-
444
- for (let i = currentIndex + 1; i < siblings.length; i++) {
445
- deadCode.push({
446
- type: 'unreachable_code',
447
- node: siblings[i],
448
- reason: 'after_return_throw',
449
- startLine: siblings[i].loc?.start.line
450
- });
451
- }
452
- }
453
- },
454
-
455
- // Unused functions
456
- FunctionDeclaration: (path) => {
457
- const functionName = path.node.id?.name;
458
- if (functionName && !this.isFunctionUsed(functionName, ast)) {
459
- deadCode.push({
460
- type: 'unused_function',
461
- node: path.node,
462
- name: functionName,
463
- startLine: path.node.loc?.start.line
464
- });
465
- }
466
- },
467
-
468
- // Always false conditions
469
- IfStatement: (path) => {
470
- if (path.node.test.type === 'BooleanLiteral' && !path.node.test.value) {
471
- deadCode.push({
472
- type: 'dead_branch',
473
- node: path.node.consequent,
474
- reason: 'always_false',
475
- startLine: path.node.loc?.start.line
476
- });
477
- }
478
- }
479
- });
480
-
481
- return deadCode;
482
- }
483
-
484
- async detectDuplicateCode(_ast, _content, metrics) {
485
- const duplicates = [];
486
- const codeBlocks = new Map();
487
- const minBlockSize = 5; // minimum lines for duplicate detection
488
-
489
- traverse(_ast, {
490
- BlockStatement: (path) => {
491
- if (path.node.body.length >= minBlockSize) {
492
- const blockHash = this.hashCodeBlock(path.node);
493
-
494
- if (codeBlocks.has(blockHash)) {
495
- const original = codeBlocks.get(blockHash);
496
- duplicates.push({
497
- type: 'duplicate_code',
498
- original: original,
499
- duplicate: path,
500
- startLine: path.node.loc?.start.line,
501
- endLine: path.node.loc?.end.line,
502
- lines: path.node.loc?.end.line - path.node.loc?.start.line + 1
503
- });
504
- } else {
505
- codeBlocks.set(blockHash, path);
506
- }
507
- }
508
- }
509
- });
510
-
511
- return duplicates;
512
- }
513
-
514
- async detectNestedConditionals(_ast, _content, metrics) {
515
- const nestedConditionals = [];
516
- const nestingThreshold = 3;
517
-
518
- const checkNesting = (path, depth = 0) => {
519
- if (depth > nestingThreshold) {
520
- nestedConditionals.push({
521
- type: 'nested_conditional',
522
- node: path.node,
523
- path: path,
524
- depth: depth,
525
- startLine: path.node.loc?.start.line,
526
- endLine: path.node.loc?.end.line
527
- });
528
- }
529
-
530
- // Check nested ifs
531
- traverse(path.node, {
532
- IfStatement: (innerPath) => {
533
- if (innerPath.node !== path.node) {
534
- checkNesting(innerPath, depth + 1);
535
- innerPath.skip();
536
- }
537
- }
538
- }, path.scope, path);
539
- };
540
-
541
- traverse(_ast, {
542
- IfStatement: (path) => checkNesting(path, 1)
543
- });
544
-
545
- return nestedConditionals;
546
- }
547
-
548
- async detectMagicNumbers(_ast, _content, metrics) {
549
- const magicNumbers = [];
550
- const ignoredNumbers = new Set([0, 1, -1, 2, 10, 100, 1000]);
551
-
552
- traverse(_ast, {
553
- NumericLiteral: (path) => {
554
- const value = path.node.value;
555
-
556
- // Skip common/obvious numbers
557
- if (ignoredNumbers.has(value)) return;
558
-
559
- // Skip array indices
560
- if (path.parent.type === 'MemberExpression' && path.parent.computed) return;
561
-
562
- // Skip in constant declarations
563
- if (path.findParent(p => p.isVariableDeclarator() &&
564
- p.parent.kind === 'const')) return;
565
-
566
- magicNumbers.push({
567
- type: 'magic_number',
568
- node: path.node,
569
- path: path,
570
- value: value,
571
- context: path.parent.type,
572
- startLine: path.node.loc?.start.line
573
- });
574
- }
575
- });
576
-
577
- return magicNumbers;
578
- }
579
-
580
- async detectLargeClasses(_ast, _content, metrics) {
581
- const largeClasses = [];
582
- const methodThreshold = 10;
583
- const propertyThreshold = 15;
584
-
585
- traverse(_ast, {
586
- ClassDeclaration: (path) => {
587
- const methods = path.node.body.body.filter(m =>
588
- m.type === 'ClassMethod' || m.type === 'ClassProperty'
589
- );
590
-
591
- const methodCount = methods.filter(m => m.type === 'ClassMethod').length;
592
- const propertyCount = methods.filter(m => m.type === 'ClassProperty').length;
593
-
594
- if (methodCount > methodThreshold || propertyCount > propertyThreshold) {
595
- largeClasses.push({
596
- type: 'large_class',
597
- node: path.node,
598
- path: path,
599
- name: path.node.id?.name,
600
- methodCount: methodCount,
601
- propertyCount: propertyCount,
602
- totalMembers: methods.length,
603
- startLine: path.node.loc?.start.line,
604
- endLine: path.node.loc?.end.line
605
- });
606
- }
607
- }
608
- });
609
-
610
- return largeClasses;
611
- }
612
-
613
- // Refactoring suggesters
614
-
615
- async suggestMethodExtraction(_detection, _ast, content) {
616
- const suggestion = {
617
- type: 'extract_method',
618
- description: `Extract method '${detection.name}' (${detection.lines} lines)`,
619
- location: {
620
- start: detection.startLine,
621
- end: detection.endLine
622
- },
623
- impact: Math.min(10, Math.floor(detection.lines / 10) + Math.floor(detection.complexity / 5)),
624
- details: `Method has ${detection.lines} lines and complexity of ${detection.complexity}. Consider extracting logical sections into separate methods.`,
625
- suggestedRefactoring: this.generateMethodExtractionSuggestion(_detection)
626
- };
627
-
628
- return suggestion;
629
- }
630
-
631
- async suggestVariableExtraction(_detection, _ast, content) {
632
- const suggestion = {
633
- type: 'extract_variable',
634
- description: 'Extract complex expression into variable',
635
- location: {
636
- start: detection.startLine,
637
- end: detection.endLine
638
- },
639
- impact: Math.min(5, detection.complexity - 2),
640
- details: `Expression has complexity of ${detection.complexity}. Extract into a named variable for better readability.`,
641
- suggestedRefactoring: this.generateVariableExtractionSuggestion(_detection)
642
- };
643
-
644
- return suggestion;
645
- }
646
-
647
- async suggestParameterObject(_detection, _ast, content) {
648
- const suggestion = {
649
- type: 'introduce_parameter_object',
650
- description: `Group ${detection.parameterCount} parameters in '${detection.name}'`,
651
- location: {
652
- start: detection.startLine,
653
- end: detection.startLine
654
- },
655
- impact: Math.min(7, detection.parameterCount - 3),
656
- details: `Method has ${detection.parameterCount} parameters: ${detection.parameters.join(', ')}. Consider grouping related parameters into an object.`,
657
- suggestedRefactoring: this.generateParameterObjectSuggestion(_detection)
658
- };
659
-
660
- return suggestion;
661
- }
662
-
663
- async suggestPolymorphism(_detection, _ast, content) {
664
- const suggestion = {
665
- type: 'replace_conditional',
666
- description: `Replace ${detection.type === 'complex_switch' ? 'switch' : 'conditional'} with polymorphism`,
667
- location: {
668
- start: detection.startLine,
669
- end: detection.endLine
670
- },
671
- impact: Math.min(8, detection.branches || detection.cases),
672
- details: `Complex ${detection.type === 'complex_switch' ? 'switch' : 'conditional'} with ${detection.branches || detection.cases} branches. Consider using polymorphism or strategy pattern.`,
673
- suggestedRefactoring: this.generatePolymorphismSuggestion(_detection)
674
- };
675
-
676
- return suggestion;
677
- }
678
-
679
- async suggestInlineTemp(_detection, _ast, content) {
680
- const suggestion = {
681
- type: 'inline_temp',
682
- description: `Inline temporary variable '${detection.name}'`,
683
- location: {
684
- start: detection.startLine,
685
- end: detection.startLine
686
- },
687
- impact: 2,
688
- details: `Variable '${detection.name}' is used only once. Consider inlining it.`,
689
- suggestedRefactoring: this.generateInlineTempSuggestion(_detection)
690
- };
691
-
692
- return suggestion;
693
- }
694
-
695
- async suggestDeadCodeRemoval(_detection, _ast, content) {
696
- const suggestion = {
697
- type: 'remove_dead_code',
698
- description: `Remove ${detection.type.replace('_', ' ')}${detection.name ? `: ${detection.name}` : ''}`,
699
- location: {
700
- start: detection.startLine,
701
- end: detection.node.loc?.end.line || detection.startLine
702
- },
703
- impact: 5,
704
- details: `${detection.type === 'unreachable_code' ? 'Code is unreachable' : detection.type === 'unused_function' ? 'Function is never called' : 'Code is dead'}`,
705
- suggestedRefactoring: {
706
- action: 'delete',
707
- lines: [detection.startLine, detection.node.loc?.end.line || detection.startLine]
708
- }
709
- };
710
-
711
- return suggestion;
712
- }
713
-
714
- async suggestCodeConsolidation(_detection, _ast, content) {
715
- const suggestion = {
716
- type: 'consolidate_duplicates',
717
- description: `Extract duplicate code block (${detection.lines} lines)`,
718
- location: {
719
- start: detection.startLine,
720
- end: detection.endLine
721
- },
722
- impact: Math.min(9, detection.lines),
723
- details: `Found duplicate code block. Extract into a shared function.`,
724
- suggestedRefactoring: this.generateConsolidationSuggestion(_detection)
725
- };
726
-
727
- return suggestion;
728
- }
729
-
730
- async suggestConditionalSimplification(_detection, _ast, content) {
731
- const suggestion = {
732
- type: 'simplify_conditionals',
733
- description: `Simplify nested conditionals (depth: ${detection.depth})`,
734
- location: {
735
- start: detection.startLine,
736
- end: detection.endLine
737
- },
738
- impact: Math.min(7, detection.depth * 2),
739
- details: `Deeply nested conditionals (${detection.depth} levels). Consider early returns or guard clauses.`,
740
- suggestedRefactoring: this.generateConditionalSimplificationSuggestion(_detection)
741
- };
742
-
743
- return suggestion;
744
- }
745
-
746
- async suggestConstantExtraction(_detection, _ast, content) {
747
- const suggestion = {
748
- type: 'replace_magic_numbers',
749
- description: `Replace magic number ${detection.value}`,
750
- location: {
751
- start: detection.startLine,
752
- end: detection.startLine
753
- },
754
- impact: 3,
755
- details: `Magic number ${detection.value} found in ${detection.context}. Extract to named constant.`,
756
- suggestedRefactoring: this.generateConstantExtractionSuggestion(_detection)
757
- };
758
-
759
- return suggestion;
760
- }
761
-
762
- async suggestClassDecomposition(_detection, _ast, content) {
763
- const suggestion = {
764
- type: 'decompose_class',
765
- description: `Decompose large class '${detection.name}' (${detection.totalMembers} members)`,
766
- location: {
767
- start: detection.startLine,
768
- end: detection.endLine
769
- },
770
- impact: Math.min(10, Math.floor(detection.totalMembers / 5)),
771
- details: `Class has ${detection.methodCount} methods and ${detection.propertyCount} properties. Consider splitting into smaller, focused classes.`,
772
- suggestedRefactoring: this.generateClassDecompositionSuggestion(_detection)
773
- };
774
-
775
- return suggestion;
776
- }
777
-
778
- // Helper methods
779
-
780
- getMethodName(path) {
781
- if (path.node.id) {
782
- return path.node.id.name;
783
- }
784
-
785
- // Check if it's a method in a class
786
- if (path.parent.type === 'ClassMethod') {
787
- return path.parent.key.name;
788
- }
789
-
790
- // Check if it's assigned to a variable
791
- if (path.parent.type === 'VariableDeclarator') {
792
- return path.parent.id.name;
793
- }
794
-
795
- // Check if it's a property
796
- if (path.parent.type === 'ObjectProperty') {
797
- return path.parent.key.name || path.parent.key.value;
798
- }
799
-
800
- return 'anonymous';
801
- }
802
-
803
- calculateMethodComplexity(path) {
804
- let complexity = 1;
805
-
806
- traverse(path.node, {
807
- IfStatement: () => complexity++,
808
- ConditionalExpression: () => complexity++,
809
- SwitchCase: () => complexity++,
810
- WhileStatement: () => complexity++,
811
- ForStatement: () => complexity++,
812
- DoWhileStatement: () => complexity++,
813
- LogicalExpression: (innerPath) => {
814
- if (innerPath.node.operator === '&&' || innerPath.node.operator === '||') {
815
- complexity++;
816
- }
817
- }
818
- }, path.scope, path);
819
-
820
- return complexity;
821
- }
822
-
823
- calculateExpressionComplexity(node, depth = 0) {
824
- if (!node) return depth;
825
-
826
- let maxDepth = depth;
827
-
828
- // Check different expression types
829
- if (node.type === 'CallExpression') {
830
- maxDepth = Math.max(maxDepth, this.calculateExpressionComplexity(node.callee, depth + 1));
831
- for (const arg of node.arguments) {
832
- maxDepth = Math.max(maxDepth, this.calculateExpressionComplexity(arg, depth + 1));
833
- }
834
- } else if (node.type === 'MemberExpression') {
835
- maxDepth = Math.max(maxDepth, this.calculateExpressionComplexity(node.object, depth + 1));
836
- } else if (node.type === 'ConditionalExpression') {
837
- maxDepth = Math.max(maxDepth,
838
- this.calculateExpressionComplexity(node.test, depth + 1),
839
- this.calculateExpressionComplexity(node.consequent, depth + 1),
840
- this.calculateExpressionComplexity(node.alternate, depth + 1)
841
- );
842
- } else if (node.type === 'BinaryExpression' || node.type === 'LogicalExpression') {
843
- maxDepth = Math.max(maxDepth,
844
- this.calculateExpressionComplexity(node.left, depth + 1),
845
- this.calculateExpressionComplexity(node.right, depth + 1)
846
- );
847
- }
848
-
849
- return maxDepth;
850
- }
851
-
852
- countConditionalBranches(path) {
853
- let branches = 1; // Initial if branch
854
-
855
- let current = path.node;
856
- while (current.alternate) {
857
- branches++;
858
- if (current.alternate.type === 'IfStatement') {
859
- current = current.alternate;
860
- } else {
861
- break;
862
- }
863
- }
864
-
865
- return branches;
866
- }
867
-
868
- isFunctionUsed(functionName, ast) {
869
- let used = false;
870
-
871
- traverse(_ast, {
872
- CallExpression: (path) => {
873
- if (path.node.callee.type === 'Identifier' &&
874
- path.node.callee.name === functionName) {
875
- used = true;
876
- path.stop();
877
- }
878
- },
879
- Identifier: (path) => {
880
- if (path.node.name === functionName &&
881
- path.isReferencedIdentifier() &&
882
- !path.isFunction()) {
883
- used = true;
884
- path.stop();
885
- }
886
- }
887
- });
888
-
889
- return used;
890
- }
891
-
892
- hashCodeBlock(node) {
893
- // Simple hash based on code structure
894
- const code = generate(node, { compact: true }).code;
895
- return code.replace(/\s+/g, ' ').trim();
896
- }
897
-
898
- // Suggestion generators
899
-
900
- generateMethodExtractionSuggestion(_detection) {
901
- return {
902
- action: 'extract_method',
903
- extractedMethods: [
904
- {
905
- name: `extracted${detection.name.charAt(0).toUpperCase() + detection.name.slice(1)}Part1`,
906
- description: 'Extract first logical section',
907
- suggestedLines: [detection.startLine + 5, detection.startLine + 15]
908
- },
909
- {
910
- name: `extracted${detection.name.charAt(0).toUpperCase() + detection.name.slice(1)}Part2`,
911
- description: 'Extract second logical section',
912
- suggestedLines: [detection.startLine + 16, detection.endLine - 5]
913
- }
914
- ]
915
- };
916
- }
917
-
918
- generateVariableExtractionSuggestion(_detection) {
919
- return {
920
- action: 'extract_variable',
921
- variableName: 'extractedExpression',
922
- insertBefore: detection.startLine
923
- };
924
- }
925
-
926
- generateParameterObjectSuggestion(_detection) {
927
- return {
928
- action: 'introduce_parameter_object',
929
- objectName: `${detection.name}Options`,
930
- groupedParameters: detection.parameters.slice(2), // Keep first 2 params separate
931
- keepParameters: detection.parameters.slice(0, 2)
932
- };
933
- }
934
-
935
- generatePolymorphismSuggestion(_detection) {
936
- return {
937
- action: 'replace_with_polymorphism',
938
- strategyPattern: true,
939
- suggestedClasses: ['BaseHandler', 'TypeAHandler', 'TypeBHandler'],
940
- interfaceMethod: 'handle'
941
- };
942
- }
943
-
944
- generateInlineTempSuggestion(_detection) {
945
- return {
946
- action: 'inline_variable',
947
- variableName: detection.name,
948
- declarationLine: detection.declaration.node.loc?.start.line,
949
- usageLine: detection.use.node.loc?.start.line
950
- };
951
- }
952
-
953
- generateConsolidationSuggestion(_detection) {
954
- return {
955
- action: 'extract_shared_function',
956
- functionName: 'extractedSharedFunction',
957
- originalLocations: [
958
- {
959
- start: detection.original.node.loc?.start.line,
960
- end: detection.original.node.loc?.end.line
961
- },
962
- {
963
- start: detection.duplicate.node.loc?.start.line,
964
- end: detection.duplicate.node.loc?.end.line
965
- }
966
- ]
967
- };
968
- }
969
-
970
- generateConditionalSimplificationSuggestion(_detection) {
971
- return {
972
- action: 'simplify_nested_conditionals',
973
- techniques: ['early_return', 'guard_clauses', 'extract_condition'],
974
- suggestedStructure: 'Use guard clauses for edge cases and early returns'
975
- };
976
- }
977
-
978
- generateConstantExtractionSuggestion(_detection) {
979
- const constantName = this.suggestConstantName(detection.value, detection.context);
980
- return {
981
- action: 'extract_constant',
982
- constantName: constantName,
983
- value: detection.value,
984
- scope: 'module' // or 'class' depending on context
985
- };
986
- }
987
-
988
- generateClassDecompositionSuggestion(_detection) {
989
- return {
990
- action: 'decompose_class',
991
- suggestedClasses: [
992
- {
993
- name: `${detection.name}Core`,
994
- description: 'Core functionality',
995
- methods: 'Core business logic methods'
996
- },
997
- {
998
- name: `${detection.name}Utils`,
999
- description: 'Utility methods',
1000
- methods: 'Helper and utility methods'
1001
- },
1002
- {
1003
- name: `${detection.name}Config`,
1004
- description: 'Configuration and setup',
1005
- methods: 'Configuration-related methods'
1006
- }
1007
- ]
1008
- };
1009
- }
1010
-
1011
- suggestConstantName(value, context) {
1012
- // Generate meaningful constant names based on value and context
1013
- const contextMap = {
1014
- 'BinaryExpression': 'THRESHOLD',
1015
- 'IfStatement': 'CONDITION',
1016
- 'ForStatement': 'LIMIT',
1017
- 'CallExpression': 'PARAMETER'
1018
- };
1019
-
1020
- const baseContext = contextMap[context] || 'VALUE';
1021
- return `${baseContext}_${Math.abs(value).toString().replace('.', '_')}`;
1022
- }
1023
-
1024
- /**
1025
- * Apply refactoring suggestion
1026
- */
1027
- async applySuggestion(suggestion, options = {}) {
1028
- console.log(chalk.blue(`🔧 Applying ${suggestion.type} refactoring...`));
1029
-
1030
- try {
1031
- // This would integrate with the actual refactoring implementation
1032
- // For now, it's a placeholder showing the structure
1033
-
1034
- const result = {
1035
- success: false,
1036
- changes: [],
1037
- error: null
1038
- };
1039
-
1040
- switch (suggestion.type) {
1041
- case 'extract_method':
1042
- result.changes = await this.applyMethodExtraction(suggestion);
1043
- break;
1044
- case 'extract_variable':
1045
- result.changes = await this.applyVariableExtraction(suggestion);
1046
- break;
1047
- case 'inline_temp':
1048
- result.changes = await this.applyInlineTemp(suggestion);
1049
- break;
1050
- case 'remove_dead_code':
1051
- result.changes = await this.applyDeadCodeRemoval(suggestion);
1052
- break;
1053
- default:
1054
- throw new Error(`Refactoring type ${suggestion.type} not implemented`);
1055
- }
1056
-
1057
- result.success = true;
1058
- return result;
1059
-
1060
- } catch (error) {
1061
- console.error(chalk.red(`Failed to apply refactoring: ${error.message}`));
1062
- return {
1063
- success: false,
1064
- changes: [],
1065
- error: error.message
1066
- };
1067
- }
1068
- }
1069
-
1070
- // Placeholder methods for applying refactorings
1071
- async applyMethodExtraction(suggestion) {
1072
- // Implementation would use AST transformation
1073
- return [{
1074
- type: 'extract_method',
1075
- file: suggestion.filePath,
1076
- description: `Extracted method from lines ${suggestion.location.start}-${suggestion.location.end}`
1077
- }];
1078
- }
1079
-
1080
- async applyVariableExtraction(suggestion) {
1081
- return [{
1082
- type: 'extract_variable',
1083
- file: suggestion.filePath,
1084
- description: `Extracted variable at line ${suggestion.location.start}`
1085
- }];
1086
- }
1087
-
1088
- async applyInlineTemp(suggestion) {
1089
- return [{
1090
- type: 'inline_temp',
1091
- file: suggestion.filePath,
1092
- description: `Inlined variable at line ${suggestion.location.start}`
1093
- }];
1094
- }
1095
-
1096
- async applyDeadCodeRemoval(suggestion) {
1097
- return [{
1098
- type: 'remove_dead_code',
1099
- file: suggestion.filePath,
1100
- description: `Removed dead code at lines ${suggestion.location.start}-${suggestion.location.end}`
1101
- }];
1102
- }
1103
-
1104
- /**
1105
- * Get refactoring statistics
1106
- */
1107
- getStatistics() {
1108
- const stats = {
1109
- totalSuggestions: this.suggestions.length,
1110
- byType: {},
1111
- byPriority: {
1112
- high: 0,
1113
- medium: 0,
1114
- low: 0
1115
- },
1116
- averageImpact: 0
1117
- };
1118
-
1119
- let totalImpact = 0;
1120
-
1121
- for (const suggestion of this.suggestions) {
1122
- // By type
1123
- stats.byType[suggestion.type] = (stats.byType[suggestion.type] || 0) + 1;
1124
-
1125
- // By priority
1126
- stats.byPriority[suggestion.priority]++;
1127
-
1128
- // Impact
1129
- totalImpact += suggestion.impact || 0;
1130
- }
1131
-
1132
- stats.averageImpact = stats.totalSuggestions > 0 ?
1133
- (totalImpact / stats.totalSuggestions).toFixed(2) : 0;
1134
-
1135
- return stats;
1136
- }
1137
- }
1138
-
1
+ const fs = require('fs').promises;
2
+ const path = require('path');
3
+ const chalk = require('chalk');
4
+ const { parse } = require('@babel/parser');
5
+ const traverse = require('@babel/traverse').default;
6
+ const generate = require('@babel/generator').default;
7
+ const _t = require('@babel/types');
8
+
9
+ /**
10
+ * Automated refactoring suggestion system
11
+ * Analyzes code and suggests refactoring opportunities
12
+ */
13
+ class RefactoringSuggester {
14
+ constructor(options = {}) {
15
+ this.rootPath = options.rootPath || process.cwd();
16
+ this.suggestions = [];
17
+ this.refactoringPatterns = new Map();
18
+ this.codeMetrics = new Map();
19
+ this.initializePatterns();
20
+ }
21
+
22
+ /**
23
+ * Initialize refactoring patterns
24
+ */
25
+ initializePatterns() {
26
+ // Method extraction pattern
27
+ this.refactoringPatterns.set('extract_method', {
28
+ name: 'Extract Method',
29
+ description: 'Extract long methods into smaller, focused methods',
30
+ detector: this.detectLongMethods.bind(this),
31
+ suggester: this.suggestMethodExtraction.bind(this),
32
+ priority: 'high'
33
+ });
34
+
35
+ // Variable extraction pattern
36
+ this.refactoringPatterns.set('extract_variable', {
37
+ name: 'Extract Variable',
38
+ description: 'Extract complex expressions into named variables',
39
+ detector: this.detectComplexExpressions.bind(this),
40
+ suggester: this.suggestVariableExtraction.bind(this),
41
+ priority: 'medium'
42
+ });
43
+
44
+ // Parameter object pattern
45
+ this.refactoringPatterns.set('introduce_parameter_object', {
46
+ name: 'Introduce Parameter Object',
47
+ description: 'Group related parameters into an object',
48
+ detector: this.detectLongParameterLists.bind(this),
49
+ suggester: this.suggestParameterObject.bind(this),
50
+ priority: 'medium'
51
+ });
52
+
53
+ // Replace conditional with polymorphism
54
+ this.refactoringPatterns.set('replace_conditional', {
55
+ name: 'Replace Conditional with Polymorphism',
56
+ description: 'Replace complex conditionals with polymorphic behavior',
57
+ detector: this.detectComplexConditionals.bind(this),
58
+ suggester: this.suggestPolymorphism.bind(this),
59
+ priority: 'high'
60
+ });
61
+
62
+ // Inline temp pattern
63
+ this.refactoringPatterns.set('inline_temp', {
64
+ name: 'Inline Temporary Variable',
65
+ description: 'Replace temporary variables used only once',
66
+ detector: this.detectSingleUseTempVariables.bind(this),
67
+ suggester: this.suggestInlineTemp.bind(this),
68
+ priority: 'low'
69
+ });
70
+
71
+ // Remove dead code
72
+ this.refactoringPatterns.set('remove_dead_code', {
73
+ name: 'Remove Dead Code',
74
+ description: 'Remove unreachable or unused code',
75
+ detector: this.detectDeadCode.bind(this),
76
+ suggester: this.suggestDeadCodeRemoval.bind(this),
77
+ priority: 'high'
78
+ });
79
+
80
+ // Consolidate duplicate code
81
+ this.refactoringPatterns.set('consolidate_duplicates', {
82
+ name: 'Consolidate Duplicate Code',
83
+ description: 'Extract duplicate code into shared functions',
84
+ detector: this.detectDuplicateCode.bind(this),
85
+ suggester: this.suggestCodeConsolidation.bind(this),
86
+ priority: 'high'
87
+ });
88
+
89
+ // Simplify nested conditionals
90
+ this.refactoringPatterns.set('simplify_conditionals', {
91
+ name: 'Simplify Nested Conditionals',
92
+ description: 'Flatten deeply nested if-else chains',
93
+ detector: this.detectNestedConditionals.bind(this),
94
+ suggester: this.suggestConditionalSimplification.bind(this),
95
+ priority: 'medium'
96
+ });
97
+
98
+ // Replace magic numbers
99
+ this.refactoringPatterns.set('replace_magic_numbers', {
100
+ name: 'Replace Magic Numbers',
101
+ description: 'Replace hard-coded numbers with named constants',
102
+ detector: this.detectMagicNumbers.bind(this),
103
+ suggester: this.suggestConstantExtraction.bind(this),
104
+ priority: 'low'
105
+ });
106
+
107
+ // Decompose complex class
108
+ this.refactoringPatterns.set('decompose_class', {
109
+ name: 'Decompose Complex Class',
110
+ description: 'Split large classes into smaller, focused classes',
111
+ detector: this.detectLargeClasses.bind(this),
112
+ suggester: this.suggestClassDecomposition.bind(this),
113
+ priority: 'high'
114
+ });
115
+ }
116
+
117
+ /**
118
+ * Analyze code and suggest refactorings
119
+ */
120
+ async analyzeCode(filePath, options = {}) {
121
+ console.log(chalk.blue(`🔍 Analyzing: ${filePath}`));
122
+
123
+ try {
124
+ const _content = await fs.readFile(filePath, 'utf-8');
125
+ const fileType = path.extname(filePath);
126
+
127
+ if (!['.js', '.jsx', '.ts', '.tsx'].includes(fileType)) {
128
+ return {
129
+ filePath,
130
+ suggestions: [],
131
+ error: 'Unsupported file type'
132
+ };
133
+ }
134
+
135
+ // Parse code
136
+ const _ast = this.parseCode(_content, filePath);
137
+
138
+ // Calculate code metrics
139
+ const _metrics = this.calculateCodeMetrics(_ast, content);
140
+ this.codeMetrics.set(filePath, metrics);
141
+
142
+ // Clear previous suggestions
143
+ this.suggestions = [];
144
+
145
+ // Run all refactoring detectors
146
+ for (const [patternId, pattern] of this.refactoringPatterns) {
147
+ if (options.patterns && !options.patterns.includes(patternId)) {
148
+ continue; // Skip if not in requested patterns
149
+ }
150
+
151
+ try {
152
+ const detected = await pattern.detector(_ast, _content, metrics);
153
+ if (detected && detected.length > 0) {
154
+ for (const _detection of detected) {
155
+ const suggestion = await pattern.suggester(_detection, _ast, content);
156
+ if (suggestion) {
157
+ this.suggestions.push({
158
+ ...suggestion,
159
+ patternId,
160
+ pattern: pattern.name,
161
+ priority: pattern.priority,
162
+ filePath
163
+ });
164
+ }
165
+ }
166
+ }
167
+ } catch (error) {
168
+ console.warn(chalk.yellow(`Failed to run ${pattern.name}: ${error.message}`));
169
+ }
170
+ }
171
+
172
+ // Sort suggestions by priority and impact
173
+ this.suggestions.sort((a, b) => {
174
+ const priorityOrder = { high: 3, medium: 2, low: 1 };
175
+ const priorityDiff = priorityOrder[b.priority] - priorityOrder[a.priority];
176
+ if (priorityDiff !== 0) return priorityDiff;
177
+ return (b.impact || 0) - (a.impact || 0);
178
+ });
179
+
180
+ return {
181
+ filePath,
182
+ _metrics,
183
+ suggestions: this.suggestions
184
+ };
185
+
186
+ } catch (error) {
187
+ return {
188
+ filePath,
189
+ suggestions: [],
190
+ error: error.message
191
+ };
192
+ }
193
+ }
194
+
195
+ /**
196
+ * Parse code into AST
197
+ */
198
+ parseCode(_content, filePath) {
199
+ const parserOptions = {
200
+ sourceType: 'module',
201
+ plugins: [
202
+ 'jsx',
203
+ 'typescript',
204
+ 'decorators-legacy',
205
+ 'classProperties',
206
+ 'asyncGenerators',
207
+ 'dynamicImport',
208
+ 'optionalChaining',
209
+ 'nullishCoalescingOperator'
210
+ ],
211
+ errorRecovery: true
212
+ };
213
+
214
+ try {
215
+ return parse(_content, parserOptions);
216
+ } catch (error) {
217
+ console.warn(chalk.yellow(`Parse error in ${filePath}: ${error.message}`));
218
+ // Try with more lenient options
219
+ return parse(_content, { ...parserOptions, errorRecovery: true });
220
+ }
221
+ }
222
+
223
+ /**
224
+ * Calculate code metrics
225
+ */
226
+ calculateCodeMetrics(_ast, content) {
227
+ const _metrics = {
228
+ lines: content.split('\n').length,
229
+ functions: 0,
230
+ classes: 0,
231
+ complexity: 0,
232
+ maxNesting: 0,
233
+ duplicateBlocks: 0,
234
+ comments: 0,
235
+ imports: 0
236
+ };
237
+
238
+ let currentNesting = 0;
239
+
240
+ traverse(_ast, {
241
+ FunctionDeclaration: () => metrics.functions++,
242
+ FunctionExpression: () => metrics.functions++,
243
+ ArrowFunctionExpression: () => metrics.functions++,
244
+ ClassDeclaration: () => metrics.classes++,
245
+ ImportDeclaration: () => metrics.imports++,
246
+
247
+ IfStatement: {
248
+ enter: () => {
249
+ metrics.complexity++;
250
+ currentNesting++;
251
+ metrics.maxNesting = Math.max(metrics.maxNesting, currentNesting);
252
+ },
253
+ exit: () => currentNesting--
254
+ },
255
+
256
+ SwitchStatement: () => metrics.complexity += 2,
257
+ ForStatement: () => metrics.complexity++,
258
+ WhileStatement: () => metrics.complexity++,
259
+ DoWhileStatement: () => metrics.complexity++,
260
+ ConditionalExpression: () => metrics.complexity++,
261
+ LogicalExpression: (path) => {
262
+ if (path.node.operator === '&&' || path.node.operator === '||') {
263
+ metrics.complexity++;
264
+ }
265
+ },
266
+
267
+ Comment: () => metrics.comments++
268
+ });
269
+
270
+ return metrics;
271
+ }
272
+
273
+ // Refactoring detectors
274
+
275
+ async detectLongMethods(_ast, _content, metrics) {
276
+ const longMethods = [];
277
+ const methodSizeThreshold = 30; // lines
278
+
279
+ traverse(_ast, {
280
+ 'FunctionDeclaration|FunctionExpression|ArrowFunctionExpression': (path) => {
281
+ const start = path.node.loc.start.line;
282
+ const end = path.node.loc.end.line;
283
+ const methodLines = end - start + 1;
284
+
285
+ if (methodLines > methodSizeThreshold) {
286
+ const methodName = this.getMethodName(path);
287
+ longMethods.push({
288
+ type: 'long_method',
289
+ node: path.node,
290
+ path: path,
291
+ name: methodName,
292
+ lines: methodLines,
293
+ startLine: start,
294
+ endLine: end,
295
+ complexity: this.calculateMethodComplexity(path)
296
+ });
297
+ }
298
+ }
299
+ });
300
+
301
+ return longMethods;
302
+ }
303
+
304
+ async detectComplexExpressions(_ast, _content, metrics) {
305
+ const complexExpressions = [];
306
+ const complexityThreshold = 3; // nesting/chaining depth
307
+
308
+ traverse(_ast, {
309
+ Expression: (path) => {
310
+ const complexity = this.calculateExpressionComplexity(path.node);
311
+ if (complexity > complexityThreshold) {
312
+ complexExpressions.push({
313
+ type: 'complex_expression',
314
+ node: path.node,
315
+ path: path,
316
+ complexity: complexity,
317
+ startLine: path.node.loc?.start.line,
318
+ endLine: path.node.loc?.end.line
319
+ });
320
+ }
321
+ }
322
+ });
323
+
324
+ return complexExpressions;
325
+ }
326
+
327
+ async detectLongParameterLists(_ast, _content, metrics) {
328
+ const longParameterLists = [];
329
+ const parameterThreshold = 4;
330
+
331
+ traverse(_ast, {
332
+ 'FunctionDeclaration|FunctionExpression|ArrowFunctionExpression': (path) => {
333
+ const params = path.node.params;
334
+ if (params.length > parameterThreshold) {
335
+ const methodName = this.getMethodName(path);
336
+ longParameterLists.push({
337
+ type: 'long_parameter_list',
338
+ node: path.node,
339
+ path: path,
340
+ name: methodName,
341
+ parameterCount: params.length,
342
+ parameters: params.map(p => p.name || 'unknown'),
343
+ startLine: path.node.loc?.start.line
344
+ });
345
+ }
346
+ }
347
+ });
348
+
349
+ return longParameterLists;
350
+ }
351
+
352
+ async detectComplexConditionals(_ast, _content, metrics) {
353
+ const complexConditionals = [];
354
+ const branchThreshold = 4;
355
+
356
+ traverse(_ast, {
357
+ IfStatement: (path) => {
358
+ const branches = this.countConditionalBranches(path);
359
+ if (branches > branchThreshold) {
360
+ complexConditionals.push({
361
+ type: 'complex_conditional',
362
+ node: path.node,
363
+ path: path,
364
+ branches: branches,
365
+ startLine: path.node.loc?.start.line,
366
+ endLine: path.node.loc?.end.line
367
+ });
368
+ }
369
+ },
370
+
371
+ SwitchStatement: (path) => {
372
+ const cases = path.node.cases.length;
373
+ if (cases > branchThreshold) {
374
+ complexConditionals.push({
375
+ type: 'complex_switch',
376
+ node: path.node,
377
+ path: path,
378
+ cases: cases,
379
+ startLine: path.node.loc?.start.line,
380
+ endLine: path.node.loc?.end.line
381
+ });
382
+ }
383
+ }
384
+ });
385
+
386
+ return complexConditionals;
387
+ }
388
+
389
+ async detectSingleUseTempVariables(_ast, _content, metrics) {
390
+ const singleUseVars = [];
391
+ const varUsage = new Map();
392
+
393
+ // First pass: collect all variable declarations and usages
394
+ traverse(_ast, {
395
+ VariableDeclarator: (path) => {
396
+ if (path.node.id.type === 'Identifier') {
397
+ const varName = path.node.id.name;
398
+ if (!varUsage.has(varName)) {
399
+ varUsage.set(varName, {
400
+ declaration: path,
401
+ uses: []
402
+ });
403
+ }
404
+ }
405
+ },
406
+
407
+ Identifier: (path) => {
408
+ if (path.isReferencedIdentifier()) {
409
+ const varName = path.node.name;
410
+ if (varUsage.has(varName)) {
411
+ varUsage.get(varName).uses.push(path);
412
+ }
413
+ }
414
+ }
415
+ });
416
+
417
+ // Second pass: find single-use variables
418
+ for (const [varName, usage] of varUsage) {
419
+ if (usage.uses.length === 1 && usage.declaration.node.init) {
420
+ singleUseVars.push({
421
+ type: 'single_use_temp',
422
+ name: varName,
423
+ declaration: usage.declaration,
424
+ use: usage.uses[0],
425
+ startLine: usage.declaration.node.loc?.start.line
426
+ });
427
+ }
428
+ }
429
+
430
+ return singleUseVars;
431
+ }
432
+
433
+ async detectDeadCode(_ast, _content, metrics) {
434
+ const deadCode = [];
435
+
436
+ traverse(_ast, {
437
+ // Unreachable code after return/throw
438
+ 'ReturnStatement|ThrowStatement': (path) => {
439
+ const parent = path.parent;
440
+ if (parent.type === 'BlockStatement') {
441
+ const siblings = parent.body;
442
+ const currentIndex = siblings.indexOf(path.node);
443
+
444
+ for (let i = currentIndex + 1; i < siblings.length; i++) {
445
+ deadCode.push({
446
+ type: 'unreachable_code',
447
+ node: siblings[i],
448
+ reason: 'after_return_throw',
449
+ startLine: siblings[i].loc?.start.line
450
+ });
451
+ }
452
+ }
453
+ },
454
+
455
+ // Unused functions
456
+ FunctionDeclaration: (path) => {
457
+ const functionName = path.node.id?.name;
458
+ if (functionName && !this.isFunctionUsed(functionName, ast)) {
459
+ deadCode.push({
460
+ type: 'unused_function',
461
+ node: path.node,
462
+ name: functionName,
463
+ startLine: path.node.loc?.start.line
464
+ });
465
+ }
466
+ },
467
+
468
+ // Always false conditions
469
+ IfStatement: (path) => {
470
+ if (path.node.test.type === 'BooleanLiteral' && !path.node.test.value) {
471
+ deadCode.push({
472
+ type: 'dead_branch',
473
+ node: path.node.consequent,
474
+ reason: 'always_false',
475
+ startLine: path.node.loc?.start.line
476
+ });
477
+ }
478
+ }
479
+ });
480
+
481
+ return deadCode;
482
+ }
483
+
484
+ async detectDuplicateCode(_ast, _content, metrics) {
485
+ const duplicates = [];
486
+ const codeBlocks = new Map();
487
+ const minBlockSize = 5; // minimum lines for duplicate detection
488
+
489
+ traverse(_ast, {
490
+ BlockStatement: (path) => {
491
+ if (path.node.body.length >= minBlockSize) {
492
+ const blockHash = this.hashCodeBlock(path.node);
493
+
494
+ if (codeBlocks.has(blockHash)) {
495
+ const original = codeBlocks.get(blockHash);
496
+ duplicates.push({
497
+ type: 'duplicate_code',
498
+ original: original,
499
+ duplicate: path,
500
+ startLine: path.node.loc?.start.line,
501
+ endLine: path.node.loc?.end.line,
502
+ lines: path.node.loc?.end.line - path.node.loc?.start.line + 1
503
+ });
504
+ } else {
505
+ codeBlocks.set(blockHash, path);
506
+ }
507
+ }
508
+ }
509
+ });
510
+
511
+ return duplicates;
512
+ }
513
+
514
+ async detectNestedConditionals(_ast, _content, metrics) {
515
+ const nestedConditionals = [];
516
+ const nestingThreshold = 3;
517
+
518
+ const checkNesting = (path, depth = 0) => {
519
+ if (depth > nestingThreshold) {
520
+ nestedConditionals.push({
521
+ type: 'nested_conditional',
522
+ node: path.node,
523
+ path: path,
524
+ depth: depth,
525
+ startLine: path.node.loc?.start.line,
526
+ endLine: path.node.loc?.end.line
527
+ });
528
+ }
529
+
530
+ // Check nested ifs
531
+ traverse(path.node, {
532
+ IfStatement: (innerPath) => {
533
+ if (innerPath.node !== path.node) {
534
+ checkNesting(innerPath, depth + 1);
535
+ innerPath.skip();
536
+ }
537
+ }
538
+ }, path.scope, path);
539
+ };
540
+
541
+ traverse(_ast, {
542
+ IfStatement: (path) => checkNesting(path, 1)
543
+ });
544
+
545
+ return nestedConditionals;
546
+ }
547
+
548
+ async detectMagicNumbers(_ast, _content, metrics) {
549
+ const magicNumbers = [];
550
+ const ignoredNumbers = new Set([0, 1, -1, 2, 10, 100, 1000]);
551
+
552
+ traverse(_ast, {
553
+ NumericLiteral: (path) => {
554
+ const value = path.node.value;
555
+
556
+ // Skip common/obvious numbers
557
+ if (ignoredNumbers.has(value)) return;
558
+
559
+ // Skip array indices
560
+ if (path.parent.type === 'MemberExpression' && path.parent.computed) return;
561
+
562
+ // Skip in constant declarations
563
+ if (path.findParent(p => p.isVariableDeclarator() &&
564
+ p.parent.kind === 'const')) return;
565
+
566
+ magicNumbers.push({
567
+ type: 'magic_number',
568
+ node: path.node,
569
+ path: path,
570
+ value: value,
571
+ context: path.parent.type,
572
+ startLine: path.node.loc?.start.line
573
+ });
574
+ }
575
+ });
576
+
577
+ return magicNumbers;
578
+ }
579
+
580
+ async detectLargeClasses(_ast, _content, metrics) {
581
+ const largeClasses = [];
582
+ const methodThreshold = 10;
583
+ const propertyThreshold = 15;
584
+
585
+ traverse(_ast, {
586
+ ClassDeclaration: (path) => {
587
+ const methods = path.node.body.body.filter(m =>
588
+ m.type === 'ClassMethod' || m.type === 'ClassProperty'
589
+ );
590
+
591
+ const methodCount = methods.filter(m => m.type === 'ClassMethod').length;
592
+ const propertyCount = methods.filter(m => m.type === 'ClassProperty').length;
593
+
594
+ if (methodCount > methodThreshold || propertyCount > propertyThreshold) {
595
+ largeClasses.push({
596
+ type: 'large_class',
597
+ node: path.node,
598
+ path: path,
599
+ name: path.node.id?.name,
600
+ methodCount: methodCount,
601
+ propertyCount: propertyCount,
602
+ totalMembers: methods.length,
603
+ startLine: path.node.loc?.start.line,
604
+ endLine: path.node.loc?.end.line
605
+ });
606
+ }
607
+ }
608
+ });
609
+
610
+ return largeClasses;
611
+ }
612
+
613
+ // Refactoring suggesters
614
+
615
+ async suggestMethodExtraction(_detection, _ast, content) {
616
+ const suggestion = {
617
+ type: 'extract_method',
618
+ description: `Extract method '${detection.name}' (${detection.lines} lines)`,
619
+ location: {
620
+ start: detection.startLine,
621
+ end: detection.endLine
622
+ },
623
+ impact: Math.min(10, Math.floor(detection.lines / 10) + Math.floor(detection.complexity / 5)),
624
+ details: `Method has ${detection.lines} lines and complexity of ${detection.complexity}. Consider extracting logical sections into separate methods.`,
625
+ suggestedRefactoring: this.generateMethodExtractionSuggestion(_detection)
626
+ };
627
+
628
+ return suggestion;
629
+ }
630
+
631
+ async suggestVariableExtraction(_detection, _ast, content) {
632
+ const suggestion = {
633
+ type: 'extract_variable',
634
+ description: 'Extract complex expression into variable',
635
+ location: {
636
+ start: detection.startLine,
637
+ end: detection.endLine
638
+ },
639
+ impact: Math.min(5, detection.complexity - 2),
640
+ details: `Expression has complexity of ${detection.complexity}. Extract into a named variable for better readability.`,
641
+ suggestedRefactoring: this.generateVariableExtractionSuggestion(_detection)
642
+ };
643
+
644
+ return suggestion;
645
+ }
646
+
647
+ async suggestParameterObject(_detection, _ast, content) {
648
+ const suggestion = {
649
+ type: 'introduce_parameter_object',
650
+ description: `Group ${detection.parameterCount} parameters in '${detection.name}'`,
651
+ location: {
652
+ start: detection.startLine,
653
+ end: detection.startLine
654
+ },
655
+ impact: Math.min(7, detection.parameterCount - 3),
656
+ details: `Method has ${detection.parameterCount} parameters: ${detection.parameters.join(', ')}. Consider grouping related parameters into an object.`,
657
+ suggestedRefactoring: this.generateParameterObjectSuggestion(_detection)
658
+ };
659
+
660
+ return suggestion;
661
+ }
662
+
663
+ async suggestPolymorphism(_detection, _ast, content) {
664
+ const suggestion = {
665
+ type: 'replace_conditional',
666
+ description: `Replace ${detection.type === 'complex_switch' ? 'switch' : 'conditional'} with polymorphism`,
667
+ location: {
668
+ start: detection.startLine,
669
+ end: detection.endLine
670
+ },
671
+ impact: Math.min(8, detection.branches || detection.cases),
672
+ details: `Complex ${detection.type === 'complex_switch' ? 'switch' : 'conditional'} with ${detection.branches || detection.cases} branches. Consider using polymorphism or strategy pattern.`,
673
+ suggestedRefactoring: this.generatePolymorphismSuggestion(_detection)
674
+ };
675
+
676
+ return suggestion;
677
+ }
678
+
679
+ async suggestInlineTemp(_detection, _ast, content) {
680
+ const suggestion = {
681
+ type: 'inline_temp',
682
+ description: `Inline temporary variable '${detection.name}'`,
683
+ location: {
684
+ start: detection.startLine,
685
+ end: detection.startLine
686
+ },
687
+ impact: 2,
688
+ details: `Variable '${detection.name}' is used only once. Consider inlining it.`,
689
+ suggestedRefactoring: this.generateInlineTempSuggestion(_detection)
690
+ };
691
+
692
+ return suggestion;
693
+ }
694
+
695
+ async suggestDeadCodeRemoval(_detection, _ast, content) {
696
+ const suggestion = {
697
+ type: 'remove_dead_code',
698
+ description: `Remove ${detection.type.replace('_', ' ')}${detection.name ? `: ${detection.name}` : ''}`,
699
+ location: {
700
+ start: detection.startLine,
701
+ end: detection.node.loc?.end.line || detection.startLine
702
+ },
703
+ impact: 5,
704
+ details: `${detection.type === 'unreachable_code' ? 'Code is unreachable' : detection.type === 'unused_function' ? 'Function is never called' : 'Code is dead'}`,
705
+ suggestedRefactoring: {
706
+ action: 'delete',
707
+ lines: [detection.startLine, detection.node.loc?.end.line || detection.startLine]
708
+ }
709
+ };
710
+
711
+ return suggestion;
712
+ }
713
+
714
+ async suggestCodeConsolidation(_detection, _ast, content) {
715
+ const suggestion = {
716
+ type: 'consolidate_duplicates',
717
+ description: `Extract duplicate code block (${detection.lines} lines)`,
718
+ location: {
719
+ start: detection.startLine,
720
+ end: detection.endLine
721
+ },
722
+ impact: Math.min(9, detection.lines),
723
+ details: `Found duplicate code block. Extract into a shared function.`,
724
+ suggestedRefactoring: this.generateConsolidationSuggestion(_detection)
725
+ };
726
+
727
+ return suggestion;
728
+ }
729
+
730
+ async suggestConditionalSimplification(_detection, _ast, content) {
731
+ const suggestion = {
732
+ type: 'simplify_conditionals',
733
+ description: `Simplify nested conditionals (depth: ${detection.depth})`,
734
+ location: {
735
+ start: detection.startLine,
736
+ end: detection.endLine
737
+ },
738
+ impact: Math.min(7, detection.depth * 2),
739
+ details: `Deeply nested conditionals (${detection.depth} levels). Consider early returns or guard clauses.`,
740
+ suggestedRefactoring: this.generateConditionalSimplificationSuggestion(_detection)
741
+ };
742
+
743
+ return suggestion;
744
+ }
745
+
746
+ async suggestConstantExtraction(_detection, _ast, content) {
747
+ const suggestion = {
748
+ type: 'replace_magic_numbers',
749
+ description: `Replace magic number ${detection.value}`,
750
+ location: {
751
+ start: detection.startLine,
752
+ end: detection.startLine
753
+ },
754
+ impact: 3,
755
+ details: `Magic number ${detection.value} found in ${detection.context}. Extract to named constant.`,
756
+ suggestedRefactoring: this.generateConstantExtractionSuggestion(_detection)
757
+ };
758
+
759
+ return suggestion;
760
+ }
761
+
762
+ async suggestClassDecomposition(_detection, _ast, content) {
763
+ const suggestion = {
764
+ type: 'decompose_class',
765
+ description: `Decompose large class '${detection.name}' (${detection.totalMembers} members)`,
766
+ location: {
767
+ start: detection.startLine,
768
+ end: detection.endLine
769
+ },
770
+ impact: Math.min(10, Math.floor(detection.totalMembers / 5)),
771
+ details: `Class has ${detection.methodCount} methods and ${detection.propertyCount} properties. Consider splitting into smaller, focused classes.`,
772
+ suggestedRefactoring: this.generateClassDecompositionSuggestion(_detection)
773
+ };
774
+
775
+ return suggestion;
776
+ }
777
+
778
+ // Helper methods
779
+
780
+ getMethodName(path) {
781
+ if (path.node.id) {
782
+ return path.node.id.name;
783
+ }
784
+
785
+ // Check if it's a method in a class
786
+ if (path.parent.type === 'ClassMethod') {
787
+ return path.parent.key.name;
788
+ }
789
+
790
+ // Check if it's assigned to a variable
791
+ if (path.parent.type === 'VariableDeclarator') {
792
+ return path.parent.id.name;
793
+ }
794
+
795
+ // Check if it's a property
796
+ if (path.parent.type === 'ObjectProperty') {
797
+ return path.parent.key.name || path.parent.key.value;
798
+ }
799
+
800
+ return 'anonymous';
801
+ }
802
+
803
+ calculateMethodComplexity(path) {
804
+ let complexity = 1;
805
+
806
+ traverse(path.node, {
807
+ IfStatement: () => complexity++,
808
+ ConditionalExpression: () => complexity++,
809
+ SwitchCase: () => complexity++,
810
+ WhileStatement: () => complexity++,
811
+ ForStatement: () => complexity++,
812
+ DoWhileStatement: () => complexity++,
813
+ LogicalExpression: (innerPath) => {
814
+ if (innerPath.node.operator === '&&' || innerPath.node.operator === '||') {
815
+ complexity++;
816
+ }
817
+ }
818
+ }, path.scope, path);
819
+
820
+ return complexity;
821
+ }
822
+
823
+ calculateExpressionComplexity(node, depth = 0) {
824
+ if (!node) return depth;
825
+
826
+ let maxDepth = depth;
827
+
828
+ // Check different expression types
829
+ if (node.type === 'CallExpression') {
830
+ maxDepth = Math.max(maxDepth, this.calculateExpressionComplexity(node.callee, depth + 1));
831
+ for (const arg of node.arguments) {
832
+ maxDepth = Math.max(maxDepth, this.calculateExpressionComplexity(arg, depth + 1));
833
+ }
834
+ } else if (node.type === 'MemberExpression') {
835
+ maxDepth = Math.max(maxDepth, this.calculateExpressionComplexity(node.object, depth + 1));
836
+ } else if (node.type === 'ConditionalExpression') {
837
+ maxDepth = Math.max(maxDepth,
838
+ this.calculateExpressionComplexity(node.test, depth + 1),
839
+ this.calculateExpressionComplexity(node.consequent, depth + 1),
840
+ this.calculateExpressionComplexity(node.alternate, depth + 1)
841
+ );
842
+ } else if (node.type === 'BinaryExpression' || node.type === 'LogicalExpression') {
843
+ maxDepth = Math.max(maxDepth,
844
+ this.calculateExpressionComplexity(node.left, depth + 1),
845
+ this.calculateExpressionComplexity(node.right, depth + 1)
846
+ );
847
+ }
848
+
849
+ return maxDepth;
850
+ }
851
+
852
+ countConditionalBranches(path) {
853
+ let branches = 1; // Initial if branch
854
+
855
+ let current = path.node;
856
+ while (current.alternate) {
857
+ branches++;
858
+ if (current.alternate.type === 'IfStatement') {
859
+ current = current.alternate;
860
+ } else {
861
+ break;
862
+ }
863
+ }
864
+
865
+ return branches;
866
+ }
867
+
868
+ isFunctionUsed(functionName, ast) {
869
+ let used = false;
870
+
871
+ traverse(_ast, {
872
+ CallExpression: (path) => {
873
+ if (path.node.callee.type === 'Identifier' &&
874
+ path.node.callee.name === functionName) {
875
+ used = true;
876
+ path.stop();
877
+ }
878
+ },
879
+ Identifier: (path) => {
880
+ if (path.node.name === functionName &&
881
+ path.isReferencedIdentifier() &&
882
+ !path.isFunction()) {
883
+ used = true;
884
+ path.stop();
885
+ }
886
+ }
887
+ });
888
+
889
+ return used;
890
+ }
891
+
892
+ hashCodeBlock(node) {
893
+ // Simple hash based on code structure
894
+ const code = generate(node, { compact: true }).code;
895
+ return code.replace(/\s+/g, ' ').trim();
896
+ }
897
+
898
+ // Suggestion generators
899
+
900
+ generateMethodExtractionSuggestion(_detection) {
901
+ return {
902
+ action: 'extract_method',
903
+ extractedMethods: [
904
+ {
905
+ name: `extracted${detection.name.charAt(0).toUpperCase() + detection.name.slice(1)}Part1`,
906
+ description: 'Extract first logical section',
907
+ suggestedLines: [detection.startLine + 5, detection.startLine + 15]
908
+ },
909
+ {
910
+ name: `extracted${detection.name.charAt(0).toUpperCase() + detection.name.slice(1)}Part2`,
911
+ description: 'Extract second logical section',
912
+ suggestedLines: [detection.startLine + 16, detection.endLine - 5]
913
+ }
914
+ ]
915
+ };
916
+ }
917
+
918
+ generateVariableExtractionSuggestion(_detection) {
919
+ return {
920
+ action: 'extract_variable',
921
+ variableName: 'extractedExpression',
922
+ insertBefore: detection.startLine
923
+ };
924
+ }
925
+
926
+ generateParameterObjectSuggestion(_detection) {
927
+ return {
928
+ action: 'introduce_parameter_object',
929
+ objectName: `${detection.name}Options`,
930
+ groupedParameters: detection.parameters.slice(2), // Keep first 2 params separate
931
+ keepParameters: detection.parameters.slice(0, 2)
932
+ };
933
+ }
934
+
935
+ generatePolymorphismSuggestion(_detection) {
936
+ return {
937
+ action: 'replace_with_polymorphism',
938
+ strategyPattern: true,
939
+ suggestedClasses: ['BaseHandler', 'TypeAHandler', 'TypeBHandler'],
940
+ interfaceMethod: 'handle'
941
+ };
942
+ }
943
+
944
+ generateInlineTempSuggestion(_detection) {
945
+ return {
946
+ action: 'inline_variable',
947
+ variableName: detection.name,
948
+ declarationLine: detection.declaration.node.loc?.start.line,
949
+ usageLine: detection.use.node.loc?.start.line
950
+ };
951
+ }
952
+
953
+ generateConsolidationSuggestion(_detection) {
954
+ return {
955
+ action: 'extract_shared_function',
956
+ functionName: 'extractedSharedFunction',
957
+ originalLocations: [
958
+ {
959
+ start: detection.original.node.loc?.start.line,
960
+ end: detection.original.node.loc?.end.line
961
+ },
962
+ {
963
+ start: detection.duplicate.node.loc?.start.line,
964
+ end: detection.duplicate.node.loc?.end.line
965
+ }
966
+ ]
967
+ };
968
+ }
969
+
970
+ generateConditionalSimplificationSuggestion(_detection) {
971
+ return {
972
+ action: 'simplify_nested_conditionals',
973
+ techniques: ['early_return', 'guard_clauses', 'extract_condition'],
974
+ suggestedStructure: 'Use guard clauses for edge cases and early returns'
975
+ };
976
+ }
977
+
978
+ generateConstantExtractionSuggestion(_detection) {
979
+ const constantName = this.suggestConstantName(detection.value, detection.context);
980
+ return {
981
+ action: 'extract_constant',
982
+ constantName: constantName,
983
+ value: detection.value,
984
+ scope: 'module' // or 'class' depending on context
985
+ };
986
+ }
987
+
988
+ generateClassDecompositionSuggestion(_detection) {
989
+ return {
990
+ action: 'decompose_class',
991
+ suggestedClasses: [
992
+ {
993
+ name: `${detection.name}Core`,
994
+ description: 'Core functionality',
995
+ methods: 'Core business logic methods'
996
+ },
997
+ {
998
+ name: `${detection.name}Utils`,
999
+ description: 'Utility methods',
1000
+ methods: 'Helper and utility methods'
1001
+ },
1002
+ {
1003
+ name: `${detection.name}Config`,
1004
+ description: 'Configuration and setup',
1005
+ methods: 'Configuration-related methods'
1006
+ }
1007
+ ]
1008
+ };
1009
+ }
1010
+
1011
+ suggestConstantName(value, context) {
1012
+ // Generate meaningful constant names based on value and context
1013
+ const contextMap = {
1014
+ 'BinaryExpression': 'THRESHOLD',
1015
+ 'IfStatement': 'CONDITION',
1016
+ 'ForStatement': 'LIMIT',
1017
+ 'CallExpression': 'PARAMETER'
1018
+ };
1019
+
1020
+ const baseContext = contextMap[context] || 'VALUE';
1021
+ return `${baseContext}_${Math.abs(value).toString().replace('.', '_')}`;
1022
+ }
1023
+
1024
+ /**
1025
+ * Apply refactoring suggestion
1026
+ */
1027
+ async applySuggestion(suggestion, options = {}) {
1028
+ console.log(chalk.blue(`🔧 Applying ${suggestion.type} refactoring...`));
1029
+
1030
+ try {
1031
+ // This would integrate with the actual refactoring implementation
1032
+ // For now, it's a placeholder showing the structure
1033
+
1034
+ const result = {
1035
+ success: false,
1036
+ changes: [],
1037
+ error: null
1038
+ };
1039
+
1040
+ switch (suggestion.type) {
1041
+ case 'extract_method':
1042
+ result.changes = await this.applyMethodExtraction(suggestion);
1043
+ break;
1044
+ case 'extract_variable':
1045
+ result.changes = await this.applyVariableExtraction(suggestion);
1046
+ break;
1047
+ case 'inline_temp':
1048
+ result.changes = await this.applyInlineTemp(suggestion);
1049
+ break;
1050
+ case 'remove_dead_code':
1051
+ result.changes = await this.applyDeadCodeRemoval(suggestion);
1052
+ break;
1053
+ default:
1054
+ throw new Error(`Refactoring type ${suggestion.type} not implemented`);
1055
+ }
1056
+
1057
+ result.success = true;
1058
+ return result;
1059
+
1060
+ } catch (error) {
1061
+ console.error(chalk.red(`Failed to apply refactoring: ${error.message}`));
1062
+ return {
1063
+ success: false,
1064
+ changes: [],
1065
+ error: error.message
1066
+ };
1067
+ }
1068
+ }
1069
+
1070
+ // Placeholder methods for applying refactorings
1071
+ async applyMethodExtraction(suggestion) {
1072
+ // Implementation would use AST transformation
1073
+ return [{
1074
+ type: 'extract_method',
1075
+ file: suggestion.filePath,
1076
+ description: `Extracted method from lines ${suggestion.location.start}-${suggestion.location.end}`
1077
+ }];
1078
+ }
1079
+
1080
+ async applyVariableExtraction(suggestion) {
1081
+ return [{
1082
+ type: 'extract_variable',
1083
+ file: suggestion.filePath,
1084
+ description: `Extracted variable at line ${suggestion.location.start}`
1085
+ }];
1086
+ }
1087
+
1088
+ async applyInlineTemp(suggestion) {
1089
+ return [{
1090
+ type: 'inline_temp',
1091
+ file: suggestion.filePath,
1092
+ description: `Inlined variable at line ${suggestion.location.start}`
1093
+ }];
1094
+ }
1095
+
1096
+ async applyDeadCodeRemoval(suggestion) {
1097
+ return [{
1098
+ type: 'remove_dead_code',
1099
+ file: suggestion.filePath,
1100
+ description: `Removed dead code at lines ${suggestion.location.start}-${suggestion.location.end}`
1101
+ }];
1102
+ }
1103
+
1104
+ /**
1105
+ * Get refactoring statistics
1106
+ */
1107
+ getStatistics() {
1108
+ const stats = {
1109
+ totalSuggestions: this.suggestions.length,
1110
+ byType: {},
1111
+ byPriority: {
1112
+ high: 0,
1113
+ medium: 0,
1114
+ low: 0
1115
+ },
1116
+ averageImpact: 0
1117
+ };
1118
+
1119
+ let totalImpact = 0;
1120
+
1121
+ for (const suggestion of this.suggestions) {
1122
+ // By type
1123
+ stats.byType[suggestion.type] = (stats.byType[suggestion.type] || 0) + 1;
1124
+
1125
+ // By priority
1126
+ stats.byPriority[suggestion.priority]++;
1127
+
1128
+ // Impact
1129
+ totalImpact += suggestion.impact || 0;
1130
+ }
1131
+
1132
+ stats.averageImpact = stats.totalSuggestions > 0 ?
1133
+ (totalImpact / stats.totalSuggestions).toFixed(2) : 0;
1134
+
1135
+ return stats;
1136
+ }
1137
+ }
1138
+
1139
1139
  module.exports = RefactoringSuggester;