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