@defai.digital/ax-cli 3.4.5 → 3.5.2

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 (211) hide show
  1. package/LICENSE +2 -6
  2. package/README.md +91 -3
  3. package/dist/analyzers/ast/parser.d.ts +59 -0
  4. package/dist/analyzers/ast/parser.js +293 -0
  5. package/dist/analyzers/ast/parser.js.map +1 -0
  6. package/dist/analyzers/ast/types.d.ts +107 -0
  7. package/dist/analyzers/ast/types.js +7 -0
  8. package/dist/analyzers/ast/types.js.map +1 -0
  9. package/dist/analyzers/code-smells/base-smell-detector.d.ts +30 -0
  10. package/dist/analyzers/code-smells/base-smell-detector.js +44 -0
  11. package/dist/analyzers/code-smells/base-smell-detector.js.map +1 -0
  12. package/dist/analyzers/code-smells/code-smell-analyzer.d.ts +30 -0
  13. package/dist/analyzers/code-smells/code-smell-analyzer.js +167 -0
  14. package/dist/analyzers/code-smells/code-smell-analyzer.js.map +1 -0
  15. package/dist/analyzers/code-smells/detectors/data-clumps-detector.d.ts +11 -0
  16. package/dist/analyzers/code-smells/detectors/data-clumps-detector.js +66 -0
  17. package/dist/analyzers/code-smells/detectors/data-clumps-detector.js.map +1 -0
  18. package/dist/analyzers/code-smells/detectors/dead-code-detector.d.ts +11 -0
  19. package/dist/analyzers/code-smells/detectors/dead-code-detector.js +53 -0
  20. package/dist/analyzers/code-smells/detectors/dead-code-detector.js.map +1 -0
  21. package/dist/analyzers/code-smells/detectors/duplicate-code-detector.d.ts +11 -0
  22. package/dist/analyzers/code-smells/detectors/duplicate-code-detector.js +51 -0
  23. package/dist/analyzers/code-smells/detectors/duplicate-code-detector.js.map +1 -0
  24. package/dist/analyzers/code-smells/detectors/feature-envy-detector.d.ts +11 -0
  25. package/dist/analyzers/code-smells/detectors/feature-envy-detector.js +64 -0
  26. package/dist/analyzers/code-smells/detectors/feature-envy-detector.js.map +1 -0
  27. package/dist/analyzers/code-smells/detectors/inappropriate-intimacy-detector.d.ts +11 -0
  28. package/dist/analyzers/code-smells/detectors/inappropriate-intimacy-detector.js +56 -0
  29. package/dist/analyzers/code-smells/detectors/inappropriate-intimacy-detector.js.map +1 -0
  30. package/dist/analyzers/code-smells/detectors/large-class-detector.d.ts +13 -0
  31. package/dist/analyzers/code-smells/detectors/large-class-detector.js +58 -0
  32. package/dist/analyzers/code-smells/detectors/large-class-detector.js.map +1 -0
  33. package/dist/analyzers/code-smells/detectors/long-method-detector.d.ts +12 -0
  34. package/dist/analyzers/code-smells/detectors/long-method-detector.js +52 -0
  35. package/dist/analyzers/code-smells/detectors/long-method-detector.js.map +1 -0
  36. package/dist/analyzers/code-smells/detectors/long-parameter-list-detector.d.ts +12 -0
  37. package/dist/analyzers/code-smells/detectors/long-parameter-list-detector.js +50 -0
  38. package/dist/analyzers/code-smells/detectors/long-parameter-list-detector.js.map +1 -0
  39. package/dist/analyzers/code-smells/detectors/magic-numbers-detector.d.ts +12 -0
  40. package/dist/analyzers/code-smells/detectors/magic-numbers-detector.js +54 -0
  41. package/dist/analyzers/code-smells/detectors/magic-numbers-detector.js.map +1 -0
  42. package/dist/analyzers/code-smells/detectors/nested-conditionals-detector.d.ts +13 -0
  43. package/dist/analyzers/code-smells/detectors/nested-conditionals-detector.js +71 -0
  44. package/dist/analyzers/code-smells/detectors/nested-conditionals-detector.js.map +1 -0
  45. package/dist/analyzers/code-smells/index.d.ts +16 -0
  46. package/dist/analyzers/code-smells/index.js +19 -0
  47. package/dist/analyzers/code-smells/index.js.map +1 -0
  48. package/dist/analyzers/code-smells/types.d.ts +82 -0
  49. package/dist/analyzers/code-smells/types.js +30 -0
  50. package/dist/analyzers/code-smells/types.js.map +1 -0
  51. package/dist/analyzers/dependency/circular-detector.d.ts +17 -0
  52. package/dist/analyzers/dependency/circular-detector.js +71 -0
  53. package/dist/analyzers/dependency/circular-detector.js.map +1 -0
  54. package/dist/analyzers/dependency/coupling-calculator.d.ts +24 -0
  55. package/dist/analyzers/dependency/coupling-calculator.js +86 -0
  56. package/dist/analyzers/dependency/coupling-calculator.js.map +1 -0
  57. package/dist/analyzers/dependency/dependency-analyzer.d.ts +40 -0
  58. package/dist/analyzers/dependency/dependency-analyzer.js +214 -0
  59. package/dist/analyzers/dependency/dependency-analyzer.js.map +1 -0
  60. package/dist/analyzers/dependency/dependency-graph.d.ts +57 -0
  61. package/dist/analyzers/dependency/dependency-graph.js +186 -0
  62. package/dist/analyzers/dependency/dependency-graph.js.map +1 -0
  63. package/dist/analyzers/dependency/index.d.ts +8 -0
  64. package/dist/analyzers/dependency/index.js +8 -0
  65. package/dist/analyzers/dependency/index.js.map +1 -0
  66. package/dist/analyzers/dependency/types.d.ts +105 -0
  67. package/dist/analyzers/dependency/types.js +5 -0
  68. package/dist/analyzers/dependency/types.js.map +1 -0
  69. package/dist/analyzers/git/churn-calculator.d.ts +34 -0
  70. package/dist/analyzers/git/churn-calculator.js +214 -0
  71. package/dist/analyzers/git/churn-calculator.js.map +1 -0
  72. package/dist/analyzers/git/git-analyzer.d.ts +19 -0
  73. package/dist/analyzers/git/git-analyzer.js +71 -0
  74. package/dist/analyzers/git/git-analyzer.js.map +1 -0
  75. package/dist/analyzers/git/hotspot-detector.d.ts +34 -0
  76. package/dist/analyzers/git/hotspot-detector.js +170 -0
  77. package/dist/analyzers/git/hotspot-detector.js.map +1 -0
  78. package/dist/analyzers/git/index.d.ts +7 -0
  79. package/dist/analyzers/git/index.js +7 -0
  80. package/dist/analyzers/git/index.js.map +1 -0
  81. package/dist/analyzers/git/types.d.ts +88 -0
  82. package/dist/analyzers/git/types.js +5 -0
  83. package/dist/analyzers/git/types.js.map +1 -0
  84. package/dist/analyzers/metrics/halstead-calculator.d.ts +30 -0
  85. package/dist/analyzers/metrics/halstead-calculator.js +150 -0
  86. package/dist/analyzers/metrics/halstead-calculator.js.map +1 -0
  87. package/dist/analyzers/metrics/index.d.ts +9 -0
  88. package/dist/analyzers/metrics/index.js +9 -0
  89. package/dist/analyzers/metrics/index.js.map +1 -0
  90. package/dist/analyzers/metrics/maintainability-calculator.d.ts +17 -0
  91. package/dist/analyzers/metrics/maintainability-calculator.js +46 -0
  92. package/dist/analyzers/metrics/maintainability-calculator.js.map +1 -0
  93. package/dist/analyzers/metrics/metrics-analyzer.d.ts +32 -0
  94. package/dist/analyzers/metrics/metrics-analyzer.js +140 -0
  95. package/dist/analyzers/metrics/metrics-analyzer.js.map +1 -0
  96. package/dist/analyzers/metrics/types.d.ts +67 -0
  97. package/dist/analyzers/metrics/types.js +5 -0
  98. package/dist/analyzers/metrics/types.js.map +1 -0
  99. package/dist/analyzers/security/base-detector.d.ts +58 -0
  100. package/dist/analyzers/security/base-detector.js +104 -0
  101. package/dist/analyzers/security/base-detector.js.map +1 -0
  102. package/dist/analyzers/security/detectors/command-injection-detector.d.ts +12 -0
  103. package/dist/analyzers/security/detectors/command-injection-detector.js +84 -0
  104. package/dist/analyzers/security/detectors/command-injection-detector.js.map +1 -0
  105. package/dist/analyzers/security/detectors/hardcoded-secrets-detector.d.ts +16 -0
  106. package/dist/analyzers/security/detectors/hardcoded-secrets-detector.js +140 -0
  107. package/dist/analyzers/security/detectors/hardcoded-secrets-detector.js.map +1 -0
  108. package/dist/analyzers/security/detectors/insecure-deserialization-detector.d.ts +12 -0
  109. package/dist/analyzers/security/detectors/insecure-deserialization-detector.js +109 -0
  110. package/dist/analyzers/security/detectors/insecure-deserialization-detector.js.map +1 -0
  111. package/dist/analyzers/security/detectors/insecure-random-detector.d.ts +12 -0
  112. package/dist/analyzers/security/detectors/insecure-random-detector.js +61 -0
  113. package/dist/analyzers/security/detectors/insecure-random-detector.js.map +1 -0
  114. package/dist/analyzers/security/detectors/path-traversal-detector.d.ts +12 -0
  115. package/dist/analyzers/security/detectors/path-traversal-detector.js +82 -0
  116. package/dist/analyzers/security/detectors/path-traversal-detector.js.map +1 -0
  117. package/dist/analyzers/security/detectors/sql-injection-detector.d.ts +12 -0
  118. package/dist/analyzers/security/detectors/sql-injection-detector.js +88 -0
  119. package/dist/analyzers/security/detectors/sql-injection-detector.js.map +1 -0
  120. package/dist/analyzers/security/detectors/weak-crypto-detector.d.ts +12 -0
  121. package/dist/analyzers/security/detectors/weak-crypto-detector.js +104 -0
  122. package/dist/analyzers/security/detectors/weak-crypto-detector.js.map +1 -0
  123. package/dist/analyzers/security/detectors/xss-detector.d.ts +12 -0
  124. package/dist/analyzers/security/detectors/xss-detector.js +90 -0
  125. package/dist/analyzers/security/detectors/xss-detector.js.map +1 -0
  126. package/dist/analyzers/security/index.d.ts +16 -0
  127. package/dist/analyzers/security/index.js +18 -0
  128. package/dist/analyzers/security/index.js.map +1 -0
  129. package/dist/analyzers/security/security-analyzer.d.ts +38 -0
  130. package/dist/analyzers/security/security-analyzer.js +215 -0
  131. package/dist/analyzers/security/security-analyzer.js.map +1 -0
  132. package/dist/analyzers/security/types.d.ts +95 -0
  133. package/dist/analyzers/security/types.js +7 -0
  134. package/dist/analyzers/security/types.js.map +1 -0
  135. package/dist/commands/memory.js +1 -1
  136. package/dist/commands/memory.js.map +1 -1
  137. package/dist/commands/setup.js +6 -1
  138. package/dist/commands/setup.js.map +1 -1
  139. package/dist/hooks/use-enhanced-input.d.ts +0 -1
  140. package/dist/hooks/use-enhanced-input.js.map +1 -1
  141. package/dist/mcp/health.js +4 -2
  142. package/dist/mcp/health.js.map +1 -1
  143. package/dist/mcp/validation.js +12 -6
  144. package/dist/mcp/validation.js.map +1 -1
  145. package/dist/tools/analysis-tools.d.ts +73 -0
  146. package/dist/tools/analysis-tools.js +422 -0
  147. package/dist/tools/analysis-tools.js.map +1 -0
  148. package/dist/tools/bash.js +2 -1
  149. package/dist/tools/bash.js.map +1 -1
  150. package/dist/ui/components/chat-history.js +1 -1
  151. package/dist/ui/components/chat-history.js.map +1 -1
  152. package/dist/ui/components/chat-interface.js +3 -2
  153. package/dist/ui/components/chat-interface.js.map +1 -1
  154. package/dist/ui/components/confirmation-dialog.js +1 -1
  155. package/dist/ui/components/confirmation-dialog.js.map +1 -1
  156. package/dist/ui/components/welcome-panel.js +1 -1
  157. package/dist/ui/components/welcome-panel.js.map +1 -1
  158. package/dist/ui/hooks/use-chat-reducer.d.ts +61 -0
  159. package/dist/ui/hooks/use-chat-reducer.js +118 -0
  160. package/dist/ui/hooks/use-chat-reducer.js.map +1 -0
  161. package/dist/ui/hooks/use-enhanced-input.d.ts +40 -0
  162. package/dist/ui/hooks/use-enhanced-input.js +254 -0
  163. package/dist/ui/hooks/use-enhanced-input.js.map +1 -0
  164. package/dist/ui/hooks/use-input-handler.d.ts +46 -0
  165. package/dist/ui/hooks/use-input-handler.js +1434 -0
  166. package/dist/ui/hooks/use-input-handler.js.map +1 -0
  167. package/dist/ui/hooks/use-input-history.d.ts +9 -0
  168. package/dist/ui/hooks/use-input-history.js +117 -0
  169. package/dist/ui/hooks/use-input-history.js.map +1 -0
  170. package/dist/utils/config-loader.js +3 -3
  171. package/dist/utils/config-loader.js.map +1 -1
  172. package/dist/utils/parallel-analyzer.js +7 -11
  173. package/dist/utils/parallel-analyzer.js.map +1 -1
  174. package/dist/utils/paste-collapse.d.ts +46 -0
  175. package/dist/utils/paste-collapse.js +77 -0
  176. package/dist/utils/paste-collapse.js.map +1 -0
  177. package/dist/utils/settings-manager.js +16 -2
  178. package/dist/utils/settings-manager.js.map +1 -1
  179. package/dist/utils/streaming-analyzer.js +9 -21
  180. package/dist/utils/streaming-analyzer.js.map +1 -1
  181. package/package.json +5 -5
  182. package/vitest.config.ts +1 -0
  183. package/dist/commands/weather.d.ts +0 -8
  184. package/dist/commands/weather.js +0 -160
  185. package/dist/commands/weather.js.map +0 -1
  186. package/dist/grok/client.d.ts +0 -144
  187. package/dist/grok/client.js +0 -237
  188. package/dist/grok/client.js.map +0 -1
  189. package/dist/grok/tools.d.ts +0 -8
  190. package/dist/grok/tools.js +0 -318
  191. package/dist/grok/tools.js.map +0 -1
  192. package/dist/grok/types.d.ts +0 -291
  193. package/dist/grok/types.js +0 -127
  194. package/dist/grok/types.js.map +0 -1
  195. package/dist/tools/morph-editor.d.ts +0 -36
  196. package/dist/tools/morph-editor.js +0 -308
  197. package/dist/tools/morph-editor.js.map +0 -1
  198. package/dist/ui/components/session-recovery.d.ts +0 -12
  199. package/dist/ui/components/session-recovery.js +0 -93
  200. package/dist/ui/components/session-recovery.js.map +0 -1
  201. package/dist/utils/model-config.d.ts +0 -28
  202. package/dist/utils/model-config.js +0 -43
  203. package/dist/utils/model-config.js.map +0 -1
  204. package/dist/utils/tool-helpers.d.ts +0 -25
  205. package/dist/utils/tool-helpers.js +0 -79
  206. package/dist/utils/tool-helpers.js.map +0 -1
  207. /package/{automatosx.config.json → ax.config.json} +0 -0
  208. /package/{config → config-defaults}/messages.yaml +0 -0
  209. /package/{config → config-defaults}/models.yaml +0 -0
  210. /package/{config → config-defaults}/prompts.yaml +0 -0
  211. /package/{config → config-defaults}/settings.yaml +0 -0
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Large Class Detector
3
+ *
4
+ * Detects classes that are too large (lines or method count)
5
+ */
6
+ import { BaseSmellDetector } from '../base-smell-detector.js';
7
+ import { type CodeSmell, type DetectorConfig } from '../types.js';
8
+ export declare class LargeClassDetector extends BaseSmellDetector {
9
+ constructor(config?: DetectorConfig);
10
+ detect(filePath: string): Promise<CodeSmell[]>;
11
+ private getSeverityByRatio;
12
+ private updateSeverity;
13
+ }
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Large Class Detector
3
+ *
4
+ * Detects classes that are too large (lines or method count)
5
+ */
6
+ import { BaseSmellDetector } from '../base-smell-detector.js';
7
+ import { SmellType, SmellSeverity } from '../types.js';
8
+ export class LargeClassDetector extends BaseSmellDetector {
9
+ constructor(config = { enabled: true, thresholds: { maxLines: 300, maxMethods: 20 } }) {
10
+ super(SmellType.LARGE_CLASS, config);
11
+ }
12
+ async detect(filePath) {
13
+ if (!this.isEnabled())
14
+ return [];
15
+ const smells = [];
16
+ const maxLines = this.getThreshold('maxLines', 300);
17
+ const maxMethods = this.getThreshold('maxMethods', 20);
18
+ try {
19
+ const ast = this.astParser.parseFile(filePath);
20
+ for (const cls of ast.classes) {
21
+ const issues = [];
22
+ let severity = SmellSeverity.LOW;
23
+ // Check line count
24
+ if (cls.length > maxLines) {
25
+ issues.push(`${cls.length} lines (threshold: ${maxLines})`);
26
+ severity = this.updateSeverity(severity, this.getSeverityByRatio(cls.length, maxLines));
27
+ }
28
+ // Check method count
29
+ if (cls.methods.length > maxMethods) {
30
+ issues.push(`${cls.methods.length} methods (threshold: ${maxMethods})`);
31
+ severity = this.updateSeverity(severity, this.getSeverityByRatio(cls.methods.length, maxMethods));
32
+ }
33
+ if (issues.length > 0) {
34
+ smells.push(this.createSmell(filePath, cls.startLine, cls.endLine, `Class '${cls.name}' is too large: ${issues.join(', ')}`, `Consider splitting this class into smaller, more focused classes following the Single Responsibility Principle.`, severity, { className: cls.name, lines: cls.length, methods: cls.methods.length }));
35
+ }
36
+ }
37
+ }
38
+ catch (error) {
39
+ // Skip files that can't be parsed
40
+ }
41
+ return smells;
42
+ }
43
+ getSeverityByRatio(value, threshold) {
44
+ const ratio = value / threshold;
45
+ if (ratio >= 3)
46
+ return SmellSeverity.CRITICAL;
47
+ if (ratio >= 2)
48
+ return SmellSeverity.HIGH;
49
+ if (ratio >= 1.5)
50
+ return SmellSeverity.MEDIUM;
51
+ return SmellSeverity.LOW;
52
+ }
53
+ updateSeverity(current, newSev) {
54
+ const order = { LOW: 0, MEDIUM: 1, HIGH: 2, CRITICAL: 3 };
55
+ return order[newSev] > order[current] ? newSev : current;
56
+ }
57
+ }
58
+ //# sourceMappingURL=large-class-detector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"large-class-detector.js","sourceRoot":"","sources":["../../../../src/analyzers/code-smells/detectors/large-class-detector.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Long Method Detector
3
+ *
4
+ * Detects functions/methods that exceed a threshold line count
5
+ */
6
+ import { BaseSmellDetector } from '../base-smell-detector.js';
7
+ import { type CodeSmell, type DetectorConfig } from '../types.js';
8
+ export declare class LongMethodDetector extends BaseSmellDetector {
9
+ constructor(config?: DetectorConfig);
10
+ detect(filePath: string): Promise<CodeSmell[]>;
11
+ private getSeverity;
12
+ }
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Long Method Detector
3
+ *
4
+ * Detects functions/methods that exceed a threshold line count
5
+ */
6
+ import { BaseSmellDetector } from '../base-smell-detector.js';
7
+ import { SmellType, SmellSeverity } from '../types.js';
8
+ export class LongMethodDetector extends BaseSmellDetector {
9
+ constructor(config = { enabled: true, thresholds: { maxLines: 50 } }) {
10
+ super(SmellType.LONG_METHOD, config);
11
+ }
12
+ async detect(filePath) {
13
+ if (!this.isEnabled())
14
+ return [];
15
+ const smells = [];
16
+ const maxLines = this.getThreshold('maxLines', 50);
17
+ try {
18
+ const ast = this.astParser.parseFile(filePath);
19
+ // Check functions
20
+ for (const func of ast.functions) {
21
+ if (func.length > maxLines) {
22
+ const severity = this.getSeverity(func.length, maxLines);
23
+ smells.push(this.createSmell(filePath, func.startLine, func.endLine, `Function '${func.name}' is too long (${func.length} lines)`, `Consider breaking this function into smaller, focused functions. Aim for functions under ${maxLines} lines.`, severity, { functionName: func.name, lines: func.length, threshold: maxLines }));
24
+ }
25
+ }
26
+ // Check methods in classes
27
+ for (const cls of ast.classes) {
28
+ for (const method of cls.methods) {
29
+ if (method.length > maxLines) {
30
+ const severity = this.getSeverity(method.length, maxLines);
31
+ smells.push(this.createSmell(filePath, method.startLine, method.endLine, `Method '${cls.name}.${method.name}' is too long (${method.length} lines)`, `Consider extracting parts of this method into smaller helper methods.`, severity, { className: cls.name, methodName: method.name, lines: method.length, threshold: maxLines }));
32
+ }
33
+ }
34
+ }
35
+ }
36
+ catch (error) {
37
+ // Skip files that can't be parsed
38
+ }
39
+ return smells;
40
+ }
41
+ getSeverity(lines, threshold) {
42
+ const ratio = lines / threshold;
43
+ if (ratio >= 4)
44
+ return SmellSeverity.CRITICAL;
45
+ if (ratio >= 2)
46
+ return SmellSeverity.HIGH;
47
+ if (ratio >= 1.5)
48
+ return SmellSeverity.MEDIUM;
49
+ return SmellSeverity.LOW;
50
+ }
51
+ }
52
+ //# sourceMappingURL=long-method-detector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"long-method-detector.js","sourceRoot":"","sources":["../../../../src/analyzers/code-smells/detectors/long-method-detector.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Long Parameter List Detector
3
+ *
4
+ * Detects functions with too many parameters
5
+ */
6
+ import { BaseSmellDetector } from '../base-smell-detector.js';
7
+ import { type CodeSmell, type DetectorConfig } from '../types.js';
8
+ export declare class LongParameterListDetector extends BaseSmellDetector {
9
+ constructor(config?: DetectorConfig);
10
+ detect(filePath: string): Promise<CodeSmell[]>;
11
+ private getSeverity;
12
+ }
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Long Parameter List Detector
3
+ *
4
+ * Detects functions with too many parameters
5
+ */
6
+ import { BaseSmellDetector } from '../base-smell-detector.js';
7
+ import { SmellType, SmellSeverity } from '../types.js';
8
+ export class LongParameterListDetector extends BaseSmellDetector {
9
+ constructor(config = { enabled: true, thresholds: { maxParams: 5 } }) {
10
+ super(SmellType.LONG_PARAMETER_LIST, config);
11
+ }
12
+ async detect(filePath) {
13
+ if (!this.isEnabled())
14
+ return [];
15
+ const smells = [];
16
+ const maxParams = this.getThreshold('maxParams', 5);
17
+ try {
18
+ const ast = this.astParser.parseFile(filePath);
19
+ // Check functions
20
+ for (const func of ast.functions) {
21
+ if (func.parameters.length > maxParams) {
22
+ smells.push(this.createSmell(filePath, func.startLine, func.endLine, `Function '${func.name}' has too many parameters (${func.parameters.length})`, `Consider using a parameter object or builder pattern to reduce the parameter count. Aim for ${maxParams} or fewer parameters.`, this.getSeverity(func.parameters.length, maxParams), { functionName: func.name, paramCount: func.parameters.length, threshold: maxParams }));
23
+ }
24
+ }
25
+ // Check methods
26
+ for (const cls of ast.classes) {
27
+ for (const method of cls.methods) {
28
+ if (method.parameters.length > maxParams) {
29
+ smells.push(this.createSmell(filePath, method.startLine, method.endLine, `Method '${cls.name}.${method.name}' has too many parameters (${method.parameters.length})`, `Consider using a parameter object or refactoring to reduce dependencies.`, this.getSeverity(method.parameters.length, maxParams), { className: cls.name, methodName: method.name, paramCount: method.parameters.length, threshold: maxParams }));
30
+ }
31
+ }
32
+ }
33
+ }
34
+ catch (error) {
35
+ // Skip files that can't be parsed
36
+ }
37
+ return smells;
38
+ }
39
+ getSeverity(count, threshold) {
40
+ const ratio = count / threshold;
41
+ if (ratio >= 2.5)
42
+ return SmellSeverity.CRITICAL;
43
+ if (ratio >= 2)
44
+ return SmellSeverity.HIGH;
45
+ if (ratio >= 1.5)
46
+ return SmellSeverity.MEDIUM;
47
+ return SmellSeverity.LOW;
48
+ }
49
+ }
50
+ //# sourceMappingURL=long-parameter-list-detector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"long-parameter-list-detector.js","sourceRoot":"","sources":["../../../../src/analyzers/code-smells/detectors/long-parameter-list-detector.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Magic Numbers Detector
3
+ *
4
+ * Detects hardcoded numeric literals (excluding common values like 0, 1, -1, 100)
5
+ */
6
+ import { BaseSmellDetector } from '../base-smell-detector.js';
7
+ import { type CodeSmell, type DetectorConfig } from '../types.js';
8
+ export declare class MagicNumbersDetector extends BaseSmellDetector {
9
+ private readonly ALLOWED_NUMBERS;
10
+ constructor(config?: DetectorConfig);
11
+ detect(filePath: string): Promise<CodeSmell[]>;
12
+ }
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Magic Numbers Detector
3
+ *
4
+ * Detects hardcoded numeric literals (excluding common values like 0, 1, -1, 100)
5
+ */
6
+ import { BaseSmellDetector } from '../base-smell-detector.js';
7
+ import { SmellType, SmellSeverity } from '../types.js';
8
+ import { SyntaxKind } from 'ts-morph';
9
+ export class MagicNumbersDetector extends BaseSmellDetector {
10
+ ALLOWED_NUMBERS = new Set([0, 1, -1, 2, 10, 100, 1000]);
11
+ constructor(config = { enabled: true }) {
12
+ super(SmellType.MAGIC_NUMBERS, config);
13
+ }
14
+ async detect(filePath) {
15
+ if (!this.isEnabled())
16
+ return [];
17
+ const smells = [];
18
+ try {
19
+ const sourceFile = this.astParser.getSourceFile(filePath);
20
+ const magicNumbers = new Map();
21
+ // Find all numeric literals
22
+ sourceFile.forEachDescendant((node) => {
23
+ if (node.getKind() === SyntaxKind.NumericLiteral) {
24
+ const value = Number(node.getText());
25
+ // Skip allowed numbers
26
+ if (this.ALLOWED_NUMBERS.has(value))
27
+ return;
28
+ // Skip if in const declaration (already named)
29
+ const parent = node.getParent();
30
+ if (parent && parent.getKind() === SyntaxKind.VariableDeclaration) {
31
+ return;
32
+ }
33
+ const line = node.getStartLineNumber();
34
+ if (!magicNumbers.has(value)) {
35
+ magicNumbers.set(value, []);
36
+ }
37
+ magicNumbers.get(value).push({ line, text: node.getText() });
38
+ }
39
+ });
40
+ // Create smells for magic numbers
41
+ for (const [value, occurrences] of magicNumbers.entries()) {
42
+ if (occurrences.length > 0) {
43
+ const firstOccurrence = occurrences[0];
44
+ smells.push(this.createSmell(filePath, firstOccurrence.line, firstOccurrence.line, `Magic number '${value}' found (${occurrences.length} occurrence${occurrences.length > 1 ? 's' : ''})`, `Replace this magic number with a named constant to improve code readability and maintainability.`, occurrences.length >= 3 ? SmellSeverity.MEDIUM : SmellSeverity.LOW, { value, occurrences: occurrences.length, lines: occurrences.map(o => o.line) }));
45
+ }
46
+ }
47
+ }
48
+ catch (error) {
49
+ // Skip files that can't be parsed
50
+ }
51
+ return smells;
52
+ }
53
+ }
54
+ //# sourceMappingURL=magic-numbers-detector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"magic-numbers-detector.js","sourceRoot":"","sources":["../../../../src/analyzers/code-smells/detectors/magic-numbers-detector.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Nested Conditionals Detector
3
+ *
4
+ * Detects deeply nested if/else/switch statements
5
+ */
6
+ import { BaseSmellDetector } from '../base-smell-detector.js';
7
+ import { type CodeSmell, type DetectorConfig } from '../types.js';
8
+ export declare class NestedConditionalsDetector extends BaseSmellDetector {
9
+ constructor(config?: DetectorConfig);
10
+ detect(filePath: string): Promise<CodeSmell[]>;
11
+ private getMaxNestingDepth;
12
+ private getSeverity;
13
+ }
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Nested Conditionals Detector
3
+ *
4
+ * Detects deeply nested if/else/switch statements
5
+ */
6
+ import { BaseSmellDetector } from '../base-smell-detector.js';
7
+ import { SmellType, SmellSeverity } from '../types.js';
8
+ import { SyntaxKind } from 'ts-morph';
9
+ export class NestedConditionalsDetector extends BaseSmellDetector {
10
+ constructor(config = { enabled: true, thresholds: { maxDepth: 3 } }) {
11
+ super(SmellType.NESTED_CONDITIONALS, config);
12
+ }
13
+ async detect(filePath) {
14
+ if (!this.isEnabled())
15
+ return [];
16
+ const smells = [];
17
+ const maxDepth = this.getThreshold('maxDepth', 3);
18
+ try {
19
+ const sourceFile = this.astParser.getSourceFile(filePath);
20
+ // Check each function/method
21
+ const functions = sourceFile.getFunctions();
22
+ for (const func of functions) {
23
+ const depth = this.getMaxNestingDepth(func);
24
+ if (depth > maxDepth) {
25
+ smells.push(this.createSmell(filePath, func.getStartLineNumber(), func.getEndLineNumber(), `Function '${func.getName() || 'anonymous'}' has deeply nested conditionals (depth: ${depth})`, `Reduce nesting by using early returns, extracting methods, or simplifying logic.`, this.getSeverity(depth, maxDepth), { functionName: func.getName() || 'anonymous', depth, threshold: maxDepth }));
26
+ }
27
+ }
28
+ // Check methods
29
+ const classes = sourceFile.getClasses();
30
+ for (const cls of classes) {
31
+ for (const method of cls.getMethods()) {
32
+ const depth = this.getMaxNestingDepth(method);
33
+ if (depth > maxDepth) {
34
+ smells.push(this.createSmell(filePath, method.getStartLineNumber(), method.getEndLineNumber(), `Method '${cls.getName()}.${method.getName()}' has deeply nested conditionals (depth: ${depth})`, `Reduce nesting by using guard clauses or extracting sub-methods.`, this.getSeverity(depth, maxDepth), { className: cls.getName(), methodName: method.getName(), depth, threshold: maxDepth }));
35
+ }
36
+ }
37
+ }
38
+ }
39
+ catch (error) {
40
+ // Skip files that can't be parsed
41
+ }
42
+ return smells;
43
+ }
44
+ getMaxNestingDepth(node) {
45
+ let maxDepth = 0;
46
+ const traverse = (n, currentDepth) => {
47
+ const kind = n.getKind();
48
+ // Increment depth for conditional statements
49
+ const isConditional = kind === SyntaxKind.IfStatement ||
50
+ kind === SyntaxKind.SwitchStatement ||
51
+ kind === SyntaxKind.ConditionalExpression;
52
+ const newDepth = isConditional ? currentDepth + 1 : currentDepth;
53
+ maxDepth = Math.max(maxDepth, newDepth);
54
+ // Traverse children
55
+ n.forEachChild((child) => traverse(child, newDepth));
56
+ };
57
+ traverse(node, 0);
58
+ return maxDepth;
59
+ }
60
+ getSeverity(depth, threshold) {
61
+ const ratio = depth / threshold;
62
+ if (ratio >= 2.5)
63
+ return SmellSeverity.CRITICAL;
64
+ if (ratio >= 2)
65
+ return SmellSeverity.HIGH;
66
+ if (ratio >= 1.5)
67
+ return SmellSeverity.MEDIUM;
68
+ return SmellSeverity.LOW;
69
+ }
70
+ }
71
+ //# sourceMappingURL=nested-conditionals-detector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nested-conditionals-detector.js","sourceRoot":"","sources":["../../../../src/analyzers/code-smells/detectors/nested-conditionals-detector.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Code Smell Analyzer Module
3
+ */
4
+ export { CodeSmellAnalyzer } from './code-smell-analyzer.js';
5
+ export { BaseSmellDetector } from './base-smell-detector.js';
6
+ export { LongMethodDetector } from './detectors/long-method-detector.js';
7
+ export { LargeClassDetector } from './detectors/large-class-detector.js';
8
+ export { LongParameterListDetector } from './detectors/long-parameter-list-detector.js';
9
+ export { MagicNumbersDetector } from './detectors/magic-numbers-detector.js';
10
+ export { NestedConditionalsDetector } from './detectors/nested-conditionals-detector.js';
11
+ export { DeadCodeDetector } from './detectors/dead-code-detector.js';
12
+ export { DuplicateCodeDetector } from './detectors/duplicate-code-detector.js';
13
+ export { FeatureEnvyDetector } from './detectors/feature-envy-detector.js';
14
+ export { DataClumpsDetector } from './detectors/data-clumps-detector.js';
15
+ export { InappropriateIntimacyDetector } from './detectors/inappropriate-intimacy-detector.js';
16
+ export { SmellType, SmellSeverity, type CodeSmell, type DetectorConfig, type CodeSmellAnalysisOptions, type CodeSmellAnalysisResult, type CodeSmellSummary, type SmellDetector, } from './types.js';
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Code Smell Analyzer Module
3
+ */
4
+ export { CodeSmellAnalyzer } from './code-smell-analyzer.js';
5
+ export { BaseSmellDetector } from './base-smell-detector.js';
6
+ // Detectors
7
+ export { LongMethodDetector } from './detectors/long-method-detector.js';
8
+ export { LargeClassDetector } from './detectors/large-class-detector.js';
9
+ export { LongParameterListDetector } from './detectors/long-parameter-list-detector.js';
10
+ export { MagicNumbersDetector } from './detectors/magic-numbers-detector.js';
11
+ export { NestedConditionalsDetector } from './detectors/nested-conditionals-detector.js';
12
+ export { DeadCodeDetector } from './detectors/dead-code-detector.js';
13
+ export { DuplicateCodeDetector } from './detectors/duplicate-code-detector.js';
14
+ export { FeatureEnvyDetector } from './detectors/feature-envy-detector.js';
15
+ export { DataClumpsDetector } from './detectors/data-clumps-detector.js';
16
+ export { InappropriateIntimacyDetector } from './detectors/inappropriate-intimacy-detector.js';
17
+ // Types
18
+ export { SmellType, SmellSeverity, } from './types.js';
19
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/analyzers/code-smells/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAE7D,YAAY;AACZ,OAAO,EAAE,kBAAkB,EAAE,MAAM,qCAAqC,CAAC;AACzE,OAAO,EAAE,kBAAkB,EAAE,MAAM,qCAAqC,CAAC;AACzE,OAAO,EAAE,yBAAyB,EAAE,MAAM,6CAA6C,CAAC;AACxF,OAAO,EAAE,oBAAoB,EAAE,MAAM,uCAAuC,CAAC;AAC7E,OAAO,EAAE,0BAA0B,EAAE,MAAM,6CAA6C,CAAC;AACzF,OAAO,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AACrE,OAAO,EAAE,qBAAqB,EAAE,MAAM,wCAAwC,CAAC;AAC/E,OAAO,EAAE,mBAAmB,EAAE,MAAM,sCAAsC,CAAC;AAC3E,OAAO,EAAE,kBAAkB,EAAE,MAAM,qCAAqC,CAAC;AACzE,OAAO,EAAE,6BAA6B,EAAE,MAAM,gDAAgD,CAAC;AAE/F,QAAQ;AACR,OAAO,EACL,SAAS,EACT,aAAa,GAOd,MAAM,YAAY,CAAC"}
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Code Smell Detector Types
3
+ */
4
+ /**
5
+ * Type of code smell
6
+ */
7
+ export declare enum SmellType {
8
+ LONG_METHOD = "LONG_METHOD",
9
+ LARGE_CLASS = "LARGE_CLASS",
10
+ LONG_PARAMETER_LIST = "LONG_PARAMETER_LIST",
11
+ DUPLICATE_CODE = "DUPLICATE_CODE",
12
+ DEAD_CODE = "DEAD_CODE",
13
+ MAGIC_NUMBERS = "MAGIC_NUMBERS",
14
+ NESTED_CONDITIONALS = "NESTED_CONDITIONALS",
15
+ FEATURE_ENVY = "FEATURE_ENVY",
16
+ DATA_CLUMPS = "DATA_CLUMPS",
17
+ INAPPROPRIATE_INTIMACY = "INAPPROPRIATE_INTIMACY"
18
+ }
19
+ /**
20
+ * Severity of code smell
21
+ */
22
+ export declare enum SmellSeverity {
23
+ LOW = "LOW",
24
+ MEDIUM = "MEDIUM",
25
+ HIGH = "HIGH",
26
+ CRITICAL = "CRITICAL"
27
+ }
28
+ /**
29
+ * Code smell detection result
30
+ */
31
+ export interface CodeSmell {
32
+ readonly type: SmellType;
33
+ readonly severity: SmellSeverity;
34
+ readonly filePath: string;
35
+ readonly startLine: number;
36
+ readonly endLine: number;
37
+ readonly message: string;
38
+ readonly suggestion: string;
39
+ readonly metadata: Readonly<Record<string, unknown>>;
40
+ }
41
+ /**
42
+ * Detector configuration
43
+ */
44
+ export interface DetectorConfig {
45
+ readonly enabled: boolean;
46
+ readonly thresholds?: Readonly<Record<string, number>>;
47
+ }
48
+ /**
49
+ * Analysis options
50
+ */
51
+ export interface CodeSmellAnalysisOptions {
52
+ readonly detectorConfigs?: Readonly<Record<SmellType, DetectorConfig>>;
53
+ readonly ignorePatterns?: readonly string[];
54
+ }
55
+ /**
56
+ * Analysis result
57
+ */
58
+ export interface CodeSmellAnalysisResult {
59
+ readonly smells: ReadonlyArray<CodeSmell>;
60
+ readonly summary: CodeSmellSummary;
61
+ readonly timestamp: Date;
62
+ }
63
+ /**
64
+ * Summary statistics
65
+ */
66
+ export interface CodeSmellSummary {
67
+ readonly totalSmells: number;
68
+ readonly smellsByType: Readonly<Record<SmellType, number>>;
69
+ readonly smellsBySeverity: Readonly<Record<SmellSeverity, number>>;
70
+ readonly filesAnalyzed: number;
71
+ readonly filesWithSmells: number;
72
+ readonly averageSmellsPerFile: number;
73
+ readonly codeHealthScore: number;
74
+ }
75
+ /**
76
+ * Base detector interface
77
+ */
78
+ export interface SmellDetector {
79
+ readonly type: SmellType;
80
+ readonly config: DetectorConfig;
81
+ detect(filePath: string): Promise<CodeSmell[]>;
82
+ }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Code Smell Detector Types
3
+ */
4
+ /**
5
+ * Type of code smell
6
+ */
7
+ export var SmellType;
8
+ (function (SmellType) {
9
+ SmellType["LONG_METHOD"] = "LONG_METHOD";
10
+ SmellType["LARGE_CLASS"] = "LARGE_CLASS";
11
+ SmellType["LONG_PARAMETER_LIST"] = "LONG_PARAMETER_LIST";
12
+ SmellType["DUPLICATE_CODE"] = "DUPLICATE_CODE";
13
+ SmellType["DEAD_CODE"] = "DEAD_CODE";
14
+ SmellType["MAGIC_NUMBERS"] = "MAGIC_NUMBERS";
15
+ SmellType["NESTED_CONDITIONALS"] = "NESTED_CONDITIONALS";
16
+ SmellType["FEATURE_ENVY"] = "FEATURE_ENVY";
17
+ SmellType["DATA_CLUMPS"] = "DATA_CLUMPS";
18
+ SmellType["INAPPROPRIATE_INTIMACY"] = "INAPPROPRIATE_INTIMACY";
19
+ })(SmellType || (SmellType = {}));
20
+ /**
21
+ * Severity of code smell
22
+ */
23
+ export var SmellSeverity;
24
+ (function (SmellSeverity) {
25
+ SmellSeverity["LOW"] = "LOW";
26
+ SmellSeverity["MEDIUM"] = "MEDIUM";
27
+ SmellSeverity["HIGH"] = "HIGH";
28
+ SmellSeverity["CRITICAL"] = "CRITICAL";
29
+ })(SmellSeverity || (SmellSeverity = {}));
30
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/analyzers/code-smells/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;GAEG;AACH,MAAM,CAAN,IAAY,SAWX;AAXD,WAAY,SAAS;IACnB,wCAA2B,CAAA;IAC3B,wCAA2B,CAAA;IAC3B,wDAA2C,CAAA;IAC3C,8CAAiC,CAAA;IACjC,oCAAuB,CAAA;IACvB,4CAA+B,CAAA;IAC/B,wDAA2C,CAAA;IAC3C,0CAA6B,CAAA;IAC7B,wCAA2B,CAAA;IAC3B,8DAAiD,CAAA;AACnD,CAAC,EAXW,SAAS,KAAT,SAAS,QAWpB;AAED;;GAEG;AACH,MAAM,CAAN,IAAY,aAKX;AALD,WAAY,aAAa;IACvB,4BAAW,CAAA;IACX,kCAAiB,CAAA;IACjB,8BAAa,CAAA;IACb,sCAAqB,CAAA;AACvB,CAAC,EALW,aAAa,KAAb,aAAa,QAKxB"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Circular Dependency Detector
3
+ *
4
+ * Uses Tarjan's Strongly Connected Components algorithm
5
+ */
6
+ import type { DependencyGraph } from './dependency-graph.js';
7
+ import type { CircularDependency } from './types.js';
8
+ export declare class CircularDependencyDetector {
9
+ /**
10
+ * Detect all circular dependencies
11
+ */
12
+ detectCycles(graph: DependencyGraph): CircularDependency[];
13
+ /**
14
+ * Create circular dependency object from SCC
15
+ */
16
+ private createCircularDependency;
17
+ }
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Circular Dependency Detector
3
+ *
4
+ * Uses Tarjan's Strongly Connected Components algorithm
5
+ */
6
+ export class CircularDependencyDetector {
7
+ /**
8
+ * Detect all circular dependencies
9
+ */
10
+ detectCycles(graph) {
11
+ const sccs = graph.getStronglyConnectedComponents();
12
+ const cycles = [];
13
+ for (const scc of sccs) {
14
+ if (scc.length > 1) {
15
+ cycles.push(this.createCircularDependency(scc, graph));
16
+ }
17
+ }
18
+ // Sort by severity and impact
19
+ return cycles.sort((a, b) => {
20
+ const severityOrder = { critical: 0, high: 1, medium: 2, low: 3 };
21
+ const severityDiff = severityOrder[a.severity] - severityOrder[b.severity];
22
+ return severityDiff !== 0 ? severityDiff : b.impact - a.impact;
23
+ });
24
+ }
25
+ /**
26
+ * Create circular dependency object from SCC
27
+ */
28
+ createCircularDependency(cycle, graph) {
29
+ const length = cycle.length;
30
+ // Determine severity based on cycle length
31
+ let severity;
32
+ if (length === 2) {
33
+ severity = 'medium'; // Simple A->B->A cycle
34
+ }
35
+ else if (length <= 4) {
36
+ severity = 'high'; // Complex but manageable
37
+ }
38
+ else {
39
+ severity = 'critical'; // Very complex, hard to refactor
40
+ }
41
+ // Calculate impact based on:
42
+ // 1. Number of files in cycle
43
+ // 2. Total LOC in cycle
44
+ // 3. Coupling of files in cycle
45
+ let totalLOC = 0;
46
+ let totalCoupling = 0;
47
+ for (const file of cycle) {
48
+ const node = graph.getNode(file);
49
+ if (node) {
50
+ totalLOC += node.loc;
51
+ totalCoupling += node.imports.length + node.exports.length;
52
+ }
53
+ }
54
+ // Impact formula: weighted combination of factors
55
+ const impact = Math.min(100, Math.round((length / 10) * 30 + // Cycle length contribution (0-30)
56
+ (totalLOC / 500) * 40 + // LOC contribution (0-40)
57
+ (totalCoupling / 20) * 30 // Coupling contribution (0-30)
58
+ ));
59
+ // Generate description
60
+ const fileNames = cycle.map(f => f.split('/').pop() || f);
61
+ const description = `Circular dependency detected: ${fileNames.join(' → ')} → ${fileNames[0]}`;
62
+ return Object.freeze({
63
+ cycle: Object.freeze(cycle),
64
+ length,
65
+ severity,
66
+ impact,
67
+ description,
68
+ });
69
+ }
70
+ }
71
+ //# sourceMappingURL=circular-detector.js.map