@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,105 @@
1
+ /**
2
+ * Dependency Analyzer Types
3
+ */
4
+ /**
5
+ * Dependency node representing a file
6
+ */
7
+ export interface DependencyNode {
8
+ readonly filePath: string;
9
+ readonly imports: ReadonlyArray<ImportEdge>;
10
+ readonly exports: ReadonlyArray<ExportEdge>;
11
+ readonly size: number;
12
+ readonly loc: number;
13
+ }
14
+ /**
15
+ * Import edge in dependency graph
16
+ */
17
+ export interface ImportEdge {
18
+ readonly from: string;
19
+ readonly to: string;
20
+ readonly importedSymbols: ReadonlyArray<string>;
21
+ readonly isDynamic: boolean;
22
+ readonly isTypeOnly: boolean;
23
+ }
24
+ /**
25
+ * Export edge
26
+ */
27
+ export interface ExportEdge {
28
+ readonly from: string;
29
+ readonly symbols: ReadonlyArray<string>;
30
+ readonly isDefault: boolean;
31
+ readonly isReExport: boolean;
32
+ }
33
+ /**
34
+ * Circular dependency cycle
35
+ */
36
+ export interface CircularDependency {
37
+ readonly cycle: ReadonlyArray<string>;
38
+ readonly length: number;
39
+ readonly severity: 'critical' | 'high' | 'medium' | 'low';
40
+ readonly impact: number;
41
+ readonly description: string;
42
+ }
43
+ /**
44
+ * Coupling metrics for a file
45
+ */
46
+ export interface CouplingMetrics {
47
+ readonly file: string;
48
+ readonly afferentCoupling: number;
49
+ readonly efferentCoupling: number;
50
+ readonly instability: number;
51
+ readonly abstractness: number;
52
+ readonly distanceFromMainSequence: number;
53
+ readonly zone: 'useless' | 'painful' | 'balanced';
54
+ }
55
+ /**
56
+ * Dependency analysis result
57
+ */
58
+ export interface DependencyAnalysisResult {
59
+ readonly graph: DependencyGraph;
60
+ readonly circularDependencies: ReadonlyArray<CircularDependency>;
61
+ readonly couplingMetrics: ReadonlyArray<CouplingMetrics>;
62
+ readonly orphanedFiles: ReadonlyArray<string>;
63
+ readonly hubFiles: ReadonlyArray<string>;
64
+ readonly summary: DependencySummary;
65
+ readonly timestamp: Date;
66
+ }
67
+ /**
68
+ * Summary statistics
69
+ */
70
+ export interface DependencySummary {
71
+ readonly totalFiles: number;
72
+ readonly totalDependencies: number;
73
+ readonly averageAfferentCoupling: number;
74
+ readonly averageEfferentCoupling: number;
75
+ readonly averageInstability: number;
76
+ readonly circularDependencyCount: number;
77
+ readonly maxCycleLength: number;
78
+ readonly healthScore: number;
79
+ }
80
+ /**
81
+ * Dependency graph interface
82
+ */
83
+ export interface DependencyGraph {
84
+ addNode(node: DependencyNode): void;
85
+ addEdge(from: string, to: string): void;
86
+ getNode(file: string): DependencyNode | undefined;
87
+ getNodes(): DependencyNode[];
88
+ getAfferentDependencies(file: string): string[];
89
+ getEfferentDependencies(file: string): string[];
90
+ getTotalEdges(): number;
91
+ hasPath(from: string, to: string): boolean;
92
+ topologicalSort(): {
93
+ sorted: string[];
94
+ hasCycle: boolean;
95
+ };
96
+ getStronglyConnectedComponents(): string[][];
97
+ }
98
+ /**
99
+ * Analysis options
100
+ */
101
+ export interface DependencyAnalysisOptions {
102
+ readonly includeNodeModules?: boolean;
103
+ readonly maxDepth?: number;
104
+ readonly ignorePatterns?: readonly string[];
105
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Dependency Analyzer Types
3
+ */
4
+ export {};
5
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/analyzers/dependency/types.ts"],"names":[],"mappings":"AAAA;;GAEG"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Git Churn Calculator
3
+ *
4
+ * Calculates file churn metrics from git history
5
+ */
6
+ import type { FileChurn, GitAnalysisOptions, ContributorStats } from './types.js';
7
+ export declare class ChurnCalculator {
8
+ private repositoryPath;
9
+ constructor(repositoryPath: string);
10
+ /**
11
+ * Calculate churn for all files
12
+ */
13
+ calculateChurn(options?: GitAnalysisOptions): Promise<FileChurn[]>;
14
+ /**
15
+ * Calculate contributor statistics
16
+ */
17
+ calculateContributorStats(options?: GitAnalysisOptions): Promise<ContributorStats[]>;
18
+ /**
19
+ * Get git log with file stats
20
+ */
21
+ private getGitLog;
22
+ /**
23
+ * Parse git log output
24
+ */
25
+ private parseGitLog;
26
+ /**
27
+ * Check if file should be included based on patterns
28
+ */
29
+ private shouldIncludeFile;
30
+ /**
31
+ * Simple pattern matching (supports * wildcard)
32
+ */
33
+ private matchPattern;
34
+ }
@@ -0,0 +1,214 @@
1
+ /**
2
+ * Git Churn Calculator
3
+ *
4
+ * Calculates file churn metrics from git history
5
+ */
6
+ import { execSync } from 'child_process';
7
+ export class ChurnCalculator {
8
+ repositoryPath;
9
+ constructor(repositoryPath) {
10
+ this.repositoryPath = repositoryPath;
11
+ }
12
+ /**
13
+ * Calculate churn for all files
14
+ */
15
+ async calculateChurn(options = {}) {
16
+ const gitLogOutput = this.getGitLog(options);
17
+ const fileChurnMap = new Map();
18
+ // Parse git log output
19
+ const commits = this.parseGitLog(gitLogOutput);
20
+ for (const commit of commits) {
21
+ for (const file of commit.files) {
22
+ if (!this.shouldIncludeFile(file.path, options))
23
+ continue;
24
+ if (!fileChurnMap.has(file.path)) {
25
+ fileChurnMap.set(file.path, {
26
+ commits: 0,
27
+ additions: 0,
28
+ deletions: 0,
29
+ lastModified: commit.date,
30
+ authors: new Set(),
31
+ });
32
+ }
33
+ const churn = fileChurnMap.get(file.path);
34
+ churn.commits++;
35
+ churn.additions += file.additions;
36
+ churn.deletions += file.deletions;
37
+ churn.authors.add(commit.author);
38
+ // Update last modified date
39
+ if (commit.date > churn.lastModified) {
40
+ churn.lastModified = commit.date;
41
+ }
42
+ }
43
+ }
44
+ // Convert to FileChurn array
45
+ const result = [];
46
+ for (const [filePath, data] of fileChurnMap.entries()) {
47
+ result.push(Object.freeze({
48
+ filePath,
49
+ commitCount: data.commits,
50
+ additions: data.additions,
51
+ deletions: data.deletions,
52
+ totalChurn: data.additions + data.deletions,
53
+ lastModified: data.lastModified,
54
+ authors: Object.freeze(Array.from(data.authors)),
55
+ }));
56
+ }
57
+ // Sort by total churn (descending)
58
+ return result.sort((a, b) => b.totalChurn - a.totalChurn);
59
+ }
60
+ /**
61
+ * Calculate contributor statistics
62
+ */
63
+ async calculateContributorStats(options = {}) {
64
+ const gitLogOutput = this.getGitLog(options);
65
+ const commits = this.parseGitLog(gitLogOutput);
66
+ const contributorMap = new Map();
67
+ for (const commit of commits) {
68
+ if (!contributorMap.has(commit.author)) {
69
+ contributorMap.set(commit.author, {
70
+ commits: 0,
71
+ files: new Set(),
72
+ added: 0,
73
+ deleted: 0,
74
+ firstCommit: commit.date,
75
+ lastCommit: commit.date,
76
+ });
77
+ }
78
+ const stats = contributorMap.get(commit.author);
79
+ stats.commits++;
80
+ for (const file of commit.files) {
81
+ if (this.shouldIncludeFile(file.path, options)) {
82
+ stats.files.add(file.path);
83
+ stats.added += file.additions;
84
+ stats.deleted += file.deletions;
85
+ }
86
+ }
87
+ // Update date range
88
+ if (commit.date < stats.firstCommit) {
89
+ stats.firstCommit = commit.date;
90
+ }
91
+ if (commit.date > stats.lastCommit) {
92
+ stats.lastCommit = commit.date;
93
+ }
94
+ }
95
+ // Convert to ContributorStats array
96
+ const result = [];
97
+ for (const [author, data] of contributorMap.entries()) {
98
+ result.push(Object.freeze({
99
+ author,
100
+ commitCount: data.commits,
101
+ filesChanged: data.files.size,
102
+ linesAdded: data.added,
103
+ linesDeleted: data.deleted,
104
+ firstCommit: data.firstCommit,
105
+ lastCommit: data.lastCommit,
106
+ }));
107
+ }
108
+ // Sort by commit count (descending)
109
+ return result.sort((a, b) => b.commitCount - a.commitCount);
110
+ }
111
+ /**
112
+ * Get git log with file stats
113
+ */
114
+ getGitLog(options) {
115
+ const args = ['log', '--numstat', '--pretty=format:%H|%an|%ad|%s'];
116
+ if (options.since) {
117
+ args.push(`--since="${options.since}"`);
118
+ }
119
+ if (options.until) {
120
+ args.push(`--until="${options.until}"`);
121
+ }
122
+ if (options.branch) {
123
+ args.push(options.branch);
124
+ }
125
+ try {
126
+ return execSync(`git ${args.join(' ')}`, {
127
+ cwd: this.repositoryPath,
128
+ encoding: 'utf-8',
129
+ maxBuffer: 10 * 1024 * 1024, // 10MB buffer
130
+ stdio: ['pipe', 'pipe', 'ignore'], // Ignore stderr to suppress shell warnings
131
+ });
132
+ }
133
+ catch (error) {
134
+ // If git command fails, return empty string
135
+ return '';
136
+ }
137
+ }
138
+ /**
139
+ * Parse git log output
140
+ */
141
+ parseGitLog(output) {
142
+ const commits = [];
143
+ const lines = output.split('\n');
144
+ let currentCommit = null;
145
+ for (const line of lines) {
146
+ if (!line.trim())
147
+ continue;
148
+ // Commit line: hash|author|date|message
149
+ if (line.includes('|')) {
150
+ const parts = line.split('|');
151
+ if (parts.length >= 4) {
152
+ // Save previous commit
153
+ if (currentCommit) {
154
+ commits.push(currentCommit);
155
+ }
156
+ // Start new commit
157
+ currentCommit = {
158
+ hash: parts[0],
159
+ author: parts[1],
160
+ date: new Date(parts[2]),
161
+ message: parts[3],
162
+ files: [],
163
+ };
164
+ }
165
+ }
166
+ else if (currentCommit) {
167
+ // File stat line: additions deletions filename
168
+ const parts = line.split('\t');
169
+ if (parts.length >= 3) {
170
+ const additions = parts[0] === '-' ? 0 : parseInt(parts[0], 10);
171
+ const deletions = parts[1] === '-' ? 0 : parseInt(parts[1], 10);
172
+ const path = parts[2];
173
+ currentCommit.files.push({ path, additions, deletions });
174
+ }
175
+ }
176
+ }
177
+ // Add last commit
178
+ if (currentCommit) {
179
+ commits.push(currentCommit);
180
+ }
181
+ return commits;
182
+ }
183
+ /**
184
+ * Check if file should be included based on patterns
185
+ */
186
+ shouldIncludeFile(filePath, options) {
187
+ // Check exclude patterns
188
+ if (options.excludePatterns) {
189
+ for (const pattern of options.excludePatterns) {
190
+ if (this.matchPattern(filePath, pattern)) {
191
+ return false;
192
+ }
193
+ }
194
+ }
195
+ // Check include patterns
196
+ if (options.includePatterns && options.includePatterns.length > 0) {
197
+ for (const pattern of options.includePatterns) {
198
+ if (this.matchPattern(filePath, pattern)) {
199
+ return true;
200
+ }
201
+ }
202
+ return false;
203
+ }
204
+ return true;
205
+ }
206
+ /**
207
+ * Simple pattern matching (supports * wildcard)
208
+ */
209
+ matchPattern(filePath, pattern) {
210
+ const regex = new RegExp('^' + pattern.replace(/\*/g, '.*') + '$');
211
+ return regex.test(filePath);
212
+ }
213
+ }
214
+ //# sourceMappingURL=churn-calculator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"churn-calculator.js","sourceRoot":"","sources":["../../../src/analyzers/git/churn-calculator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Git Analyzer
3
+ *
4
+ * Main orchestrator for Git history analysis
5
+ */
6
+ import type { GitAnalysisResult, GitAnalysisOptions } from './types.js';
7
+ export declare class GitAnalyzer {
8
+ private churnCalculator;
9
+ private hotspotDetector;
10
+ constructor(repositoryPath: string);
11
+ /**
12
+ * Perform complete Git analysis
13
+ */
14
+ analyze(options?: GitAnalysisOptions): Promise<GitAnalysisResult>;
15
+ /**
16
+ * Calculate summary statistics
17
+ */
18
+ private calculateSummary;
19
+ }
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Git Analyzer
3
+ *
4
+ * Main orchestrator for Git history analysis
5
+ */
6
+ import { ChurnCalculator } from './churn-calculator.js';
7
+ import { HotspotDetector } from './hotspot-detector.js';
8
+ export class GitAnalyzer {
9
+ churnCalculator;
10
+ hotspotDetector;
11
+ constructor(repositoryPath) {
12
+ this.churnCalculator = new ChurnCalculator(repositoryPath);
13
+ this.hotspotDetector = new HotspotDetector();
14
+ }
15
+ /**
16
+ * Perform complete Git analysis
17
+ */
18
+ async analyze(options = {}) {
19
+ const timestamp = new Date();
20
+ // Calculate churn metrics
21
+ const churnMetrics = await this.churnCalculator.calculateChurn(options);
22
+ // Calculate contributor statistics
23
+ const contributors = await this.churnCalculator.calculateContributorStats(options);
24
+ // Detect hotspots
25
+ const hotspots = await this.hotspotDetector.detectHotspots(churnMetrics, options);
26
+ // Calculate summary
27
+ const summary = this.calculateSummary(churnMetrics, hotspots, contributors);
28
+ return Object.freeze({
29
+ hotspots: Object.freeze(hotspots),
30
+ churnMetrics: Object.freeze(churnMetrics),
31
+ contributors: Object.freeze(contributors),
32
+ summary: Object.freeze(summary),
33
+ timestamp,
34
+ });
35
+ }
36
+ /**
37
+ * Calculate summary statistics
38
+ */
39
+ calculateSummary(churnMetrics, hotspots, contributors) {
40
+ const totalCommits = contributors.reduce((sum, c) => sum + c.commitCount, 0);
41
+ const averageChurn = churnMetrics.length > 0
42
+ ? churnMetrics.reduce((sum, c) => sum + c.totalChurn, 0) / churnMetrics.length
43
+ : 0;
44
+ const topContributor = contributors.length > 0
45
+ ? contributors[0].author
46
+ : 'Unknown';
47
+ // Determine date range
48
+ let earliestDate = new Date();
49
+ let latestDate = new Date(0);
50
+ for (const contributor of contributors) {
51
+ if (contributor.firstCommit < earliestDate) {
52
+ earliestDate = contributor.firstCommit;
53
+ }
54
+ if (contributor.lastCommit > latestDate) {
55
+ latestDate = contributor.lastCommit;
56
+ }
57
+ }
58
+ return {
59
+ totalCommits,
60
+ filesAnalyzed: churnMetrics.length,
61
+ hotspotCount: hotspots.length,
62
+ averageChurn: Math.round(averageChurn),
63
+ topContributor,
64
+ dateRange: {
65
+ from: earliestDate,
66
+ to: latestDate,
67
+ },
68
+ };
69
+ }
70
+ }
71
+ //# sourceMappingURL=git-analyzer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git-analyzer.js","sourceRoot":"","sources":["../../../src/analyzers/git/git-analyzer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Hotspot Detector
3
+ *
4
+ * Identifies code hotspots using churn × complexity formula
5
+ */
6
+ import type { FileChurn, CodeHotspot, GitAnalysisOptions } from './types.js';
7
+ export declare class HotspotDetector {
8
+ private astParser;
9
+ constructor();
10
+ /**
11
+ * Detect hotspots from churn metrics
12
+ */
13
+ detectHotspots(churnMetrics: readonly FileChurn[], options?: GitAnalysisOptions): Promise<CodeHotspot[]>;
14
+ /**
15
+ * Get complexity metrics from file
16
+ */
17
+ private getComplexityMetrics;
18
+ /**
19
+ * Normalize complexity to 0-100 scale
20
+ */
21
+ private normalizeComplexity;
22
+ /**
23
+ * Calculate severity based on hotspot score
24
+ */
25
+ private calculateSeverity;
26
+ /**
27
+ * Generate reason for hotspot
28
+ */
29
+ private generateReason;
30
+ /**
31
+ * Generate recommendation
32
+ */
33
+ private generateRecommendation;
34
+ }
@@ -0,0 +1,170 @@
1
+ /**
2
+ * Hotspot Detector
3
+ *
4
+ * Identifies code hotspots using churn × complexity formula
5
+ */
6
+ import { ASTParser } from '../ast/parser.js';
7
+ export class HotspotDetector {
8
+ astParser;
9
+ constructor() {
10
+ this.astParser = new ASTParser();
11
+ }
12
+ /**
13
+ * Detect hotspots from churn metrics
14
+ */
15
+ async detectHotspots(churnMetrics, options = {}) {
16
+ const threshold = options.hotspotThreshold ?? 70;
17
+ const hotspots = [];
18
+ // Calculate max values for normalization
19
+ const maxChurn = Math.max(...churnMetrics.map(c => c.totalChurn), 1);
20
+ const maxCommits = Math.max(...churnMetrics.map(c => c.commitCount), 1);
21
+ for (const churn of churnMetrics) {
22
+ try {
23
+ // Get complexity metrics from AST
24
+ const complexity = await this.getComplexityMetrics(churn.filePath);
25
+ if (!complexity)
26
+ continue;
27
+ // Normalize churn (0-100)
28
+ const normalizedChurn = (churn.totalChurn / maxChurn) * 100;
29
+ const normalizedCommits = (churn.commitCount / maxCommits) * 100;
30
+ const churnScore = (normalizedChurn * 0.6 + normalizedCommits * 0.4);
31
+ // Normalize complexity (0-100)
32
+ const complexityScore = this.normalizeComplexity(complexity.average, complexity.max);
33
+ // Calculate hotspot score using weighted formula
34
+ // Churn: 40%, Complexity: 30%, Commit Frequency: 30%
35
+ const hotspotScore = Math.round(churnScore * 0.4 +
36
+ complexityScore * 0.3 +
37
+ normalizedCommits * 0.3);
38
+ // Only include if above threshold
39
+ if (hotspotScore >= threshold) {
40
+ const severity = this.calculateSeverity(hotspotScore);
41
+ const reason = this.generateReason(churn, complexity, hotspotScore);
42
+ const recommendation = this.generateRecommendation(churn, complexity, severity);
43
+ hotspots.push(Object.freeze({
44
+ filePath: churn.filePath,
45
+ hotspotScore,
46
+ churnScore: Math.round(churnScore),
47
+ complexityScore: Math.round(complexityScore),
48
+ commitCount: churn.commitCount,
49
+ totalChurn: churn.totalChurn,
50
+ averageComplexity: complexity.average,
51
+ maxComplexity: complexity.max,
52
+ severity,
53
+ reason,
54
+ recommendation,
55
+ }));
56
+ }
57
+ }
58
+ catch (error) {
59
+ // Skip files that can't be analyzed
60
+ continue;
61
+ }
62
+ }
63
+ // Sort by hotspot score (descending)
64
+ return hotspots.sort((a, b) => b.hotspotScore - a.hotspotScore);
65
+ }
66
+ /**
67
+ * Get complexity metrics from file
68
+ */
69
+ async getComplexityMetrics(filePath) {
70
+ try {
71
+ const ast = this.astParser.parseFile(filePath);
72
+ const complexities = [];
73
+ // Collect function complexities
74
+ for (const func of ast.functions) {
75
+ complexities.push(func.complexity);
76
+ }
77
+ // Collect method complexities
78
+ for (const cls of ast.classes) {
79
+ for (const method of cls.methods) {
80
+ complexities.push(method.complexity);
81
+ }
82
+ }
83
+ if (complexities.length === 0) {
84
+ return null;
85
+ }
86
+ const average = complexities.reduce((sum, c) => sum + c, 0) / complexities.length;
87
+ const max = Math.max(...complexities);
88
+ return { average, max };
89
+ }
90
+ catch (error) {
91
+ return null;
92
+ }
93
+ }
94
+ /**
95
+ * Normalize complexity to 0-100 scale
96
+ */
97
+ normalizeComplexity(average, max) {
98
+ // McCabe complexity thresholds:
99
+ // 1-10: Simple
100
+ // 11-20: Moderate
101
+ // 21-50: Complex
102
+ // 50+: Very complex
103
+ const avgScore = Math.min(100, (average / 20) * 100);
104
+ const maxScore = Math.min(100, (max / 50) * 100);
105
+ // Weight average more than max
106
+ return avgScore * 0.7 + maxScore * 0.3;
107
+ }
108
+ /**
109
+ * Calculate severity based on hotspot score
110
+ */
111
+ calculateSeverity(score) {
112
+ if (score >= 90)
113
+ return 'CRITICAL';
114
+ if (score >= 80)
115
+ return 'HIGH';
116
+ if (score >= 70)
117
+ return 'MEDIUM';
118
+ return 'LOW';
119
+ }
120
+ /**
121
+ * Generate reason for hotspot
122
+ */
123
+ generateReason(churn, complexity, score) {
124
+ const reasons = [];
125
+ if (churn.commitCount > 20) {
126
+ reasons.push(`high change frequency (${churn.commitCount} commits)`);
127
+ }
128
+ if (churn.totalChurn > 500) {
129
+ reasons.push(`high churn (${churn.totalChurn} lines changed)`);
130
+ }
131
+ if (complexity.average > 10) {
132
+ reasons.push(`high average complexity (${complexity.average.toFixed(1)})`);
133
+ }
134
+ if (complexity.max > 20) {
135
+ reasons.push(`very complex functions (max: ${complexity.max})`);
136
+ }
137
+ if (churn.authors.length > 5) {
138
+ reasons.push(`many contributors (${churn.authors.length})`);
139
+ }
140
+ return `Hotspot (score: ${score}/100): ${reasons.join(', ')}`;
141
+ }
142
+ /**
143
+ * Generate recommendation
144
+ */
145
+ generateRecommendation(churn, complexity, severity) {
146
+ const recommendations = [];
147
+ if (complexity.max > 20) {
148
+ recommendations.push('Reduce complexity by breaking down complex functions');
149
+ }
150
+ if (churn.commitCount > 30) {
151
+ recommendations.push('Stabilize this file - consider refactoring to reduce change frequency');
152
+ }
153
+ if (churn.authors.length > 8) {
154
+ recommendations.push('Establish clear ownership and coding standards');
155
+ }
156
+ if (complexity.average > 15) {
157
+ recommendations.push('Simplify logic and improve code structure');
158
+ }
159
+ if (recommendations.length === 0) {
160
+ recommendations.push('Monitor this file for further changes and consider refactoring');
161
+ }
162
+ const prefix = severity === 'CRITICAL' || severity === 'HIGH'
163
+ ? 'URGENT: '
164
+ : severity === 'MEDIUM'
165
+ ? 'Important: '
166
+ : '';
167
+ return prefix + recommendations.join('. ') + '.';
168
+ }
169
+ }
170
+ //# sourceMappingURL=hotspot-detector.js.map