@girardelli/architect-core 8.1.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 (217) hide show
  1. package/dist/src/core/analyzer.d.ts +42 -0
  2. package/dist/src/core/analyzer.js +431 -0
  3. package/dist/src/core/analyzer.js.map +1 -0
  4. package/dist/src/core/analyzers/forecast.d.ts +84 -0
  5. package/dist/src/core/analyzers/forecast.js +338 -0
  6. package/dist/src/core/analyzers/forecast.js.map +1 -0
  7. package/dist/src/core/analyzers/index.d.ts +9 -0
  8. package/dist/src/core/analyzers/index.js +7 -0
  9. package/dist/src/core/analyzers/index.js.map +1 -0
  10. package/dist/src/core/analyzers/temporal-scorer.d.ts +71 -0
  11. package/dist/src/core/analyzers/temporal-scorer.js +141 -0
  12. package/dist/src/core/analyzers/temporal-scorer.js.map +1 -0
  13. package/dist/src/core/anti-patterns.d.ts +28 -0
  14. package/dist/src/core/anti-patterns.js +264 -0
  15. package/dist/src/core/anti-patterns.js.map +1 -0
  16. package/dist/src/core/ast/ast-parser.interface.d.ts +20 -0
  17. package/dist/src/core/ast/ast-parser.interface.js +2 -0
  18. package/dist/src/core/ast/ast-parser.interface.js.map +1 -0
  19. package/dist/src/core/ast/path-resolver.d.ts +13 -0
  20. package/dist/src/core/ast/path-resolver.js +54 -0
  21. package/dist/src/core/ast/path-resolver.js.map +1 -0
  22. package/dist/src/core/ast/tree-sitter-parser.d.ts +10 -0
  23. package/dist/src/core/ast/tree-sitter-parser.js +142 -0
  24. package/dist/src/core/ast/tree-sitter-parser.js.map +1 -0
  25. package/dist/src/core/config.d.ts +11 -0
  26. package/dist/src/core/config.js +112 -0
  27. package/dist/src/core/config.js.map +1 -0
  28. package/dist/src/core/diagram.d.ts +9 -0
  29. package/dist/src/core/diagram.js +101 -0
  30. package/dist/src/core/diagram.js.map +1 -0
  31. package/dist/src/core/i18n.d.ts +14 -0
  32. package/dist/src/core/i18n.js +54 -0
  33. package/dist/src/core/i18n.js.map +1 -0
  34. package/dist/src/core/locales/en.d.ts +2 -0
  35. package/dist/src/core/locales/en.js +337 -0
  36. package/dist/src/core/locales/en.js.map +1 -0
  37. package/dist/src/core/locales/pt-BR.d.ts +172 -0
  38. package/dist/src/core/locales/pt-BR.js +337 -0
  39. package/dist/src/core/locales/pt-BR.js.map +1 -0
  40. package/dist/src/core/locales/types.d.ts +86 -0
  41. package/dist/src/core/locales/types.js +2 -0
  42. package/dist/src/core/locales/types.js.map +1 -0
  43. package/dist/src/core/plugin-loader.d.ts +11 -0
  44. package/dist/src/core/plugin-loader.js +67 -0
  45. package/dist/src/core/plugin-loader.js.map +1 -0
  46. package/dist/src/core/project-summarizer.d.ts +16 -0
  47. package/dist/src/core/project-summarizer.js +37 -0
  48. package/dist/src/core/project-summarizer.js.map +1 -0
  49. package/dist/src/core/refactor-engine.d.ts +18 -0
  50. package/dist/src/core/refactor-engine.js +87 -0
  51. package/dist/src/core/refactor-engine.js.map +1 -0
  52. package/dist/src/core/rules/barrel-optimizer.d.ts +13 -0
  53. package/dist/src/core/rules/barrel-optimizer.js +76 -0
  54. package/dist/src/core/rules/barrel-optimizer.js.map +1 -0
  55. package/dist/src/core/rules/dead-code-detector.d.ts +21 -0
  56. package/dist/src/core/rules/dead-code-detector.js +116 -0
  57. package/dist/src/core/rules/dead-code-detector.js.map +1 -0
  58. package/dist/src/core/rules/hub-splitter.d.ts +13 -0
  59. package/dist/src/core/rules/hub-splitter.js +117 -0
  60. package/dist/src/core/rules/hub-splitter.js.map +1 -0
  61. package/dist/src/core/rules/import-organizer.d.ts +13 -0
  62. package/dist/src/core/rules/import-organizer.js +84 -0
  63. package/dist/src/core/rules/import-organizer.js.map +1 -0
  64. package/dist/src/core/rules/module-grouper.d.ts +13 -0
  65. package/dist/src/core/rules/module-grouper.js +116 -0
  66. package/dist/src/core/rules/module-grouper.js.map +1 -0
  67. package/dist/src/core/rules-engine.d.ts +7 -0
  68. package/dist/src/core/rules-engine.js +89 -0
  69. package/dist/src/core/rules-engine.js.map +1 -0
  70. package/dist/src/core/scorer.d.ts +15 -0
  71. package/dist/src/core/scorer.js +165 -0
  72. package/dist/src/core/scorer.js.map +1 -0
  73. package/dist/src/core/summarizer/keyword-extractor.d.ts +6 -0
  74. package/dist/src/core/summarizer/keyword-extractor.js +38 -0
  75. package/dist/src/core/summarizer/keyword-extractor.js.map +1 -0
  76. package/dist/src/core/summarizer/module-inferrer.d.ts +11 -0
  77. package/dist/src/core/summarizer/module-inferrer.js +171 -0
  78. package/dist/src/core/summarizer/module-inferrer.js.map +1 -0
  79. package/dist/src/core/summarizer/package-reader.d.ts +3 -0
  80. package/dist/src/core/summarizer/package-reader.js +33 -0
  81. package/dist/src/core/summarizer/package-reader.js.map +1 -0
  82. package/dist/src/core/summarizer/purpose-inferrer.d.ts +8 -0
  83. package/dist/src/core/summarizer/purpose-inferrer.js +179 -0
  84. package/dist/src/core/summarizer/purpose-inferrer.js.map +1 -0
  85. package/dist/src/core/summarizer/readme-reader.d.ts +3 -0
  86. package/dist/src/core/summarizer/readme-reader.js +24 -0
  87. package/dist/src/core/summarizer/readme-reader.js.map +1 -0
  88. package/dist/src/core/types/architect-rules.d.ts +27 -0
  89. package/dist/src/core/types/architect-rules.js +2 -0
  90. package/dist/src/core/types/architect-rules.js.map +1 -0
  91. package/dist/src/core/types/core.d.ts +87 -0
  92. package/dist/src/core/types/core.js +2 -0
  93. package/dist/src/core/types/core.js.map +1 -0
  94. package/dist/src/core/types/infrastructure.d.ts +38 -0
  95. package/dist/src/core/types/infrastructure.js +2 -0
  96. package/dist/src/core/types/infrastructure.js.map +1 -0
  97. package/dist/src/core/types/plugin.d.ts +12 -0
  98. package/dist/src/core/types/plugin.js +2 -0
  99. package/dist/src/core/types/plugin.js.map +1 -0
  100. package/dist/src/core/types/rules.d.ts +53 -0
  101. package/dist/src/core/types/rules.js +2 -0
  102. package/dist/src/core/types/rules.js.map +1 -0
  103. package/dist/src/core/types/summarizer.d.ts +12 -0
  104. package/dist/src/core/types/summarizer.js +2 -0
  105. package/dist/src/core/types/summarizer.js.map +1 -0
  106. package/dist/src/infrastructure/git-cache.d.ts +6 -0
  107. package/dist/src/infrastructure/git-cache.js +41 -0
  108. package/dist/src/infrastructure/git-cache.js.map +1 -0
  109. package/dist/src/infrastructure/git-history.d.ts +112 -0
  110. package/dist/src/infrastructure/git-history.js +340 -0
  111. package/dist/src/infrastructure/git-history.js.map +1 -0
  112. package/dist/src/infrastructure/logger.d.ts +20 -0
  113. package/dist/src/infrastructure/logger.js +57 -0
  114. package/dist/src/infrastructure/logger.js.map +1 -0
  115. package/dist/src/infrastructure/scanner.d.ts +31 -0
  116. package/dist/src/infrastructure/scanner.js +334 -0
  117. package/dist/src/infrastructure/scanner.js.map +1 -0
  118. package/dist/tests/analyzers-integration.test.d.ts +7 -0
  119. package/dist/tests/analyzers-integration.test.js +140 -0
  120. package/dist/tests/analyzers-integration.test.js.map +1 -0
  121. package/dist/tests/anti-patterns.test.d.ts +1 -0
  122. package/dist/tests/anti-patterns.test.js +81 -0
  123. package/dist/tests/anti-patterns.test.js.map +1 -0
  124. package/dist/tests/ast-parser.test.d.ts +1 -0
  125. package/dist/tests/ast-parser.test.js +94 -0
  126. package/dist/tests/ast-parser.test.js.map +1 -0
  127. package/dist/tests/fixtures/monorepo/packages/app/src/index.d.ts +1 -0
  128. package/dist/tests/fixtures/monorepo/packages/app/src/index.js +9 -0
  129. package/dist/tests/fixtures/monorepo/packages/app/src/index.js.map +1 -0
  130. package/dist/tests/fixtures/monorepo/packages/core/src/index.d.ts +2 -0
  131. package/dist/tests/fixtures/monorepo/packages/core/src/index.js +11 -0
  132. package/dist/tests/fixtures/monorepo/packages/core/src/index.js.map +1 -0
  133. package/dist/tests/forecast.test.d.ts +7 -0
  134. package/dist/tests/forecast.test.js +380 -0
  135. package/dist/tests/forecast.test.js.map +1 -0
  136. package/dist/tests/git-history.test.d.ts +7 -0
  137. package/dist/tests/git-history.test.js +193 -0
  138. package/dist/tests/git-history.test.js.map +1 -0
  139. package/dist/tests/i18n.test.d.ts +1 -0
  140. package/dist/tests/i18n.test.js +39 -0
  141. package/dist/tests/i18n.test.js.map +1 -0
  142. package/dist/tests/monorepo-scan.test.d.ts +11 -0
  143. package/dist/tests/monorepo-scan.test.js +143 -0
  144. package/dist/tests/monorepo-scan.test.js.map +1 -0
  145. package/dist/tests/plugin-loader.test.d.ts +1 -0
  146. package/dist/tests/plugin-loader.test.js +31 -0
  147. package/dist/tests/plugin-loader.test.js.map +1 -0
  148. package/dist/tests/rules-engine.test.d.ts +1 -0
  149. package/dist/tests/rules-engine.test.js +112 -0
  150. package/dist/tests/rules-engine.test.js.map +1 -0
  151. package/dist/tests/scanner.test.d.ts +1 -0
  152. package/dist/tests/scanner.test.js +44 -0
  153. package/dist/tests/scanner.test.js.map +1 -0
  154. package/dist/tests/scorer.test.d.ts +1 -0
  155. package/dist/tests/scorer.test.js +610 -0
  156. package/dist/tests/scorer.test.js.map +1 -0
  157. package/dist/tests/temporal-scorer.test.d.ts +7 -0
  158. package/dist/tests/temporal-scorer.test.js +239 -0
  159. package/dist/tests/temporal-scorer.test.js.map +1 -0
  160. package/package.json +29 -0
  161. package/src/core/analyzer.ts +499 -0
  162. package/src/core/analyzers/forecast.ts +497 -0
  163. package/src/core/analyzers/index.ts +33 -0
  164. package/src/core/analyzers/temporal-scorer.ts +227 -0
  165. package/src/core/anti-patterns.ts +324 -0
  166. package/src/core/ast/ast-parser.interface.ts +21 -0
  167. package/src/core/ast/path-resolver.ts +61 -0
  168. package/src/core/ast/tree-sitter-parser.ts +158 -0
  169. package/src/core/config.ts +125 -0
  170. package/src/core/diagram.ts +129 -0
  171. package/src/core/i18n.ts +64 -0
  172. package/src/core/locales/en.ts +340 -0
  173. package/src/core/locales/pt-BR.ts +341 -0
  174. package/src/core/locales/types.ts +95 -0
  175. package/src/core/plugin-loader.ts +80 -0
  176. package/src/core/project-summarizer.ts +42 -0
  177. package/src/core/refactor-engine.ts +112 -0
  178. package/src/core/rules/barrel-optimizer.ts +99 -0
  179. package/src/core/rules/dead-code-detector.ts +134 -0
  180. package/src/core/rules/hub-splitter.ts +135 -0
  181. package/src/core/rules/import-organizer.ts +100 -0
  182. package/src/core/rules/module-grouper.ts +133 -0
  183. package/src/core/rules-engine.ts +100 -0
  184. package/src/core/scorer.ts +181 -0
  185. package/src/core/summarizer/keyword-extractor.ts +53 -0
  186. package/src/core/summarizer/module-inferrer.ts +194 -0
  187. package/src/core/summarizer/package-reader.ts +34 -0
  188. package/src/core/summarizer/purpose-inferrer.ts +197 -0
  189. package/src/core/summarizer/readme-reader.ts +24 -0
  190. package/src/core/types/architect-rules.ts +29 -0
  191. package/src/core/types/core.ts +94 -0
  192. package/src/core/types/infrastructure.ts +41 -0
  193. package/src/core/types/plugin.ts +19 -0
  194. package/src/core/types/rules.ts +51 -0
  195. package/src/core/types/summarizer.ts +8 -0
  196. package/src/infrastructure/git-cache.ts +52 -0
  197. package/src/infrastructure/git-history.ts +496 -0
  198. package/src/infrastructure/logger.ts +68 -0
  199. package/src/infrastructure/scanner.ts +349 -0
  200. package/tests/analyzers-integration.test.ts +174 -0
  201. package/tests/anti-patterns.test.ts +95 -0
  202. package/tests/ast-parser.test.ts +102 -0
  203. package/tests/fixtures/monorepo/package.json +6 -0
  204. package/tests/fixtures/monorepo/packages/app/package.json +12 -0
  205. package/tests/fixtures/monorepo/packages/app/src/index.ts +6 -0
  206. package/tests/fixtures/monorepo/packages/core/package.json +7 -0
  207. package/tests/fixtures/monorepo/packages/core/src/index.ts +7 -0
  208. package/tests/forecast.test.ts +504 -0
  209. package/tests/git-history.test.ts +254 -0
  210. package/tests/i18n.test.ts +47 -0
  211. package/tests/monorepo-scan.test.ts +170 -0
  212. package/tests/plugin-loader.test.ts +40 -0
  213. package/tests/rules-engine.test.ts +131 -0
  214. package/tests/scanner.test.ts +54 -0
  215. package/tests/scorer.test.ts +675 -0
  216. package/tests/temporal-scorer.test.ts +306 -0
  217. package/tsconfig.json +9 -0
@@ -0,0 +1,158 @@
1
+ import path from 'path';
2
+ import { ASTParser } from './ast-parser.interface.js';
3
+
4
+ export class TreeSitterParser implements ASTParser {
5
+ private parsers: Map<string, any> = new Map();
6
+ private queries: Map<string, any> = new Map();
7
+
8
+ // Store the classes dynamically to avoid static C++ addon evaluation leaks in Jest ESM VMs
9
+ private ParserClass: any = null;
10
+ private QueryClass: any = null;
11
+
12
+ async initialize(): Promise<void> {
13
+ try {
14
+ // Defer loading the native C++ bindings until absolutely needed.
15
+ // This prevents "Cannot read properties of undefined (reading 'tree')"
16
+ // when Jest loads the file across multiple test suites concurrently.
17
+ const treeSitterCore = await import('tree-sitter');
18
+ this.ParserClass = treeSitterCore.default;
19
+ this.QueryClass = treeSitterCore.Query;
20
+
21
+ // Dynamic ESM imports to handle native bindings gracefully
22
+ const tsMod = await import('tree-sitter-typescript').catch(() => null);
23
+ const jsMod = await import('tree-sitter-javascript').catch(() => null);
24
+ const pyMod = await import('tree-sitter-python').catch(() => null);
25
+ const goMod = await import('tree-sitter-go').catch(() => null);
26
+ const javaMod = await import('tree-sitter-java').catch(() => null);
27
+ const rustMod = await import('tree-sitter-rust').catch(() => null);
28
+
29
+ if (tsMod) {
30
+ // Handle CJS vs ESM interop structures
31
+ const tsLang = tsMod.default?.typescript || tsMod.typescript || tsMod;
32
+ const tsxLang = tsMod.default?.tsx || tsMod.tsx;
33
+ this.initParser('.ts', tsLang);
34
+ this.initParser('.tsx', tsxLang);
35
+ }
36
+
37
+ if (jsMod) {
38
+ const jsLang = jsMod.default || jsMod;
39
+ this.initParser('.js', jsLang);
40
+ this.initParser('.jsx', jsLang);
41
+ }
42
+
43
+ if (pyMod) {
44
+ this.initParser('.py', pyMod.default || pyMod);
45
+ }
46
+
47
+ if (goMod) {
48
+ this.initParser('.go', goMod.default || goMod);
49
+ }
50
+
51
+ if (javaMod) {
52
+ this.initParser('.java', javaMod.default || javaMod);
53
+ }
54
+
55
+ if (rustMod) {
56
+ this.initParser('.rs', rustMod.default || rustMod);
57
+ }
58
+
59
+ if (this.parsers.size === 0) {
60
+ throw new Error('No Tree-Sitter grammars were successfully loaded.');
61
+ }
62
+ } catch (err) {
63
+ throw new Error(`AST Parsers failed to initialize: ${(err as Error).message}`);
64
+ }
65
+ }
66
+
67
+ private initParser(ext: string, languageOption: any): void {
68
+ if (!languageOption || !this.ParserClass) return;
69
+ try {
70
+ const parser = new this.ParserClass();
71
+ parser.setLanguage(languageOption);
72
+ this.parsers.set(ext, parser);
73
+
74
+ // Pre-compile queries for performance based on language
75
+ let queryStr = '';
76
+ if (ext === '.ts' || ext === '.tsx' || ext === '.js' || ext === '.jsx') {
77
+ // Matches "import X from 'path'", "import('path')", "require('path')", "export * from 'path'"
78
+ queryStr = `
79
+ (import_statement source: (string) @path)
80
+ (export_statement source: (string) @path)
81
+ (call_expression
82
+ function: [(identifier) (import)] @fn
83
+ arguments: (arguments (string) @path)
84
+ (#match? @fn "^(require|import)$")
85
+ )
86
+ `;
87
+ } else if (ext === '.py') {
88
+ // python imports
89
+ queryStr = `
90
+ (import_from_statement module_name: (_) @module)
91
+ (import_statement name: (_) @module)
92
+ `;
93
+ } else if (ext === '.go') {
94
+ queryStr = `
95
+ (import_spec path: (interpreted_string_literal) @path)
96
+ `;
97
+ } else if (ext === '.java') {
98
+ queryStr = `
99
+ (import_declaration (scoped_identifier) @path)
100
+ `;
101
+ } else if (ext === '.rs') {
102
+ queryStr = `
103
+ (use_declaration argument: (scoped_identifier) @path)
104
+ `;
105
+ }
106
+
107
+ if (queryStr) {
108
+ const query = new this.QueryClass(languageOption, queryStr);
109
+ this.queries.set(ext, query);
110
+ }
111
+ } catch (e) {
112
+ console.error(`[TreeSitter] Query compilation failed for ${ext}:`, e);
113
+ // Ignore parser failure for a specific extension silently, letting fallback take over
114
+ }
115
+ }
116
+
117
+ parseImports(content: string, filePath: string): string[] {
118
+ const ext = path.extname(filePath);
119
+ const parser = this.parsers.get(ext);
120
+ const query = this.queries.get(ext);
121
+
122
+ if (!parser || !query) {
123
+ throw new Error(`Tree-Sitter parser not mapped/loaded for extension ${ext}`);
124
+ }
125
+
126
+ const tree = parser.parse(content);
127
+ const matches = query.matches(tree.rootNode);
128
+
129
+ const imports: string[] = [];
130
+
131
+ for (const match of matches) {
132
+ // Extract the captured module paths/names
133
+ for (const capture of match.captures) {
134
+ if (capture.name === 'path' || capture.name === 'module') {
135
+ // Remove quotes around literal strings (e.g., "'./module'" -> "./module")
136
+ let value = capture.node.text.replace(/['"]/g, '');
137
+
138
+ if (ext === '.py') {
139
+ value = value.split(' as ')[0].trim();
140
+ }
141
+
142
+ imports.push(value);
143
+ }
144
+ }
145
+ }
146
+
147
+ // Anti-GC trick for V8 + node-tree-sitter:
148
+ // If the 'tree' object is not referenced after 'query.matches()', V8's aggressive GC
149
+ // might destroy the Tree before 'query.matches' fully executes its C++ bindings,
150
+ // resulting in "Cannot read properties of undefined (reading 'tree')" inside marshalNode.
151
+ // Calling tree.delete() explicitly fixes memory leaks AND keeps the reference alive.
152
+ if (typeof (tree as any).delete === 'function') {
153
+ (tree as any).delete();
154
+ }
155
+
156
+ return imports;
157
+ }
158
+ }
@@ -0,0 +1,125 @@
1
+ import { readFileSync } from 'fs';
2
+ import { join } from 'path';
3
+ import { ArchitectConfig } from './types/core.js';
4
+
5
+ const DEFAULT_CONFIG: ArchitectConfig = {
6
+ ignore: [
7
+ '**/node_modules/**',
8
+ '**/dist/**',
9
+ '**/build/**',
10
+ '**/coverage/**',
11
+ '**/.git/**',
12
+ '**/.next/**',
13
+ '**/venv/**',
14
+ '**/__pycache__/**',
15
+ '**/target/**',
16
+ '**/out/**',
17
+ '**/.cache/**',
18
+ ],
19
+ frameworks: {
20
+ detect: true,
21
+ },
22
+ antiPatterns: {
23
+ godClass: {
24
+ linesThreshold: 500,
25
+ methodsThreshold: 10,
26
+ },
27
+ shotgunSurgery: {
28
+ changePropagationThreshold: 5,
29
+ },
30
+ },
31
+ score: {
32
+ modularity: 0.4,
33
+ coupling: 0.25,
34
+ cohesion: 0.2,
35
+ layering: 0.15,
36
+ },
37
+ monorepo: {
38
+ enabled: true,
39
+ treatPackagesAsModules: true,
40
+ },
41
+ plugins: [],
42
+ };
43
+
44
+ /**
45
+ * Normalize ignore patterns to use proper glob syntax.
46
+ * Simple names like "node_modules" are expanded to cover nested directories:
47
+ * "node_modules" → ["node_modules", "node_modules/**", "** /node_modules", "** /node_modules/**"]
48
+ */
49
+ export function normalizeIgnorePatterns(patterns: string[]): string[] {
50
+ const normalized = new Set<string>();
51
+
52
+ for (const p of patterns) {
53
+ if (p.includes('*') || p.includes('/')) {
54
+ // Already a glob pattern — keep as-is
55
+ normalized.add(p);
56
+ } else {
57
+ // Simple directory name — expand to cover all nesting levels
58
+ normalized.add(p);
59
+ normalized.add(`${p}/**`);
60
+ normalized.add(`**/${p}`);
61
+ normalized.add(`**/${p}/**`);
62
+ }
63
+ }
64
+
65
+ return [...normalized];
66
+ }
67
+
68
+ export class ConfigLoader {
69
+ static loadConfig(projectPath: string): ArchitectConfig {
70
+ const configPath = join(projectPath, '.architect.json');
71
+
72
+ try {
73
+ const content = readFileSync(configPath, 'utf-8');
74
+ const userConfig = JSON.parse(content) as ArchitectConfig;
75
+ return this.mergeConfigs(DEFAULT_CONFIG, userConfig);
76
+ } catch {
77
+ return { ...DEFAULT_CONFIG };
78
+ }
79
+ }
80
+
81
+ private static mergeConfigs(
82
+ defaults: ArchitectConfig,
83
+ user: ArchitectConfig
84
+ ): ArchitectConfig {
85
+ // If user provides ignore patterns, normalize them to proper glob format
86
+ const userIgnore = user.ignore
87
+ ? normalizeIgnorePatterns(user.ignore)
88
+ : defaults.ignore;
89
+
90
+ return {
91
+ ignore: userIgnore,
92
+ plugins: user.plugins ?? defaults.plugins,
93
+ frameworks: {
94
+ detect: user.frameworks?.detect ?? defaults.frameworks?.detect,
95
+ },
96
+ antiPatterns: {
97
+ godClass: {
98
+ linesThreshold:
99
+ user.antiPatterns?.godClass?.linesThreshold ??
100
+ defaults.antiPatterns?.godClass?.linesThreshold,
101
+ methodsThreshold:
102
+ user.antiPatterns?.godClass?.methodsThreshold ??
103
+ defaults.antiPatterns?.godClass?.methodsThreshold,
104
+ },
105
+ shotgunSurgery: {
106
+ changePropagationThreshold:
107
+ user.antiPatterns?.shotgunSurgery?.changePropagationThreshold ??
108
+ defaults.antiPatterns?.shotgunSurgery?.changePropagationThreshold,
109
+ },
110
+ },
111
+ score: {
112
+ modularity: user.score?.modularity ?? defaults.score?.modularity,
113
+ coupling: user.score?.coupling ?? defaults.score?.coupling,
114
+ cohesion: user.score?.cohesion ?? defaults.score?.cohesion,
115
+ layering: user.score?.layering ?? defaults.score?.layering,
116
+ },
117
+ monorepo: {
118
+ enabled: user.monorepo?.enabled ?? defaults.monorepo?.enabled,
119
+ treatPackagesAsModules:
120
+ user.monorepo?.treatPackagesAsModules ??
121
+ defaults.monorepo?.treatPackagesAsModules,
122
+ },
123
+ };
124
+ }
125
+ }
@@ -0,0 +1,129 @@
1
+ import { DependencyEdge, Layer } from './types/core.js';
2
+
3
+ const LAYER_THEME: Record<string, { color: string; style: string }> = {
4
+ API: { color: '#FFB6C1', style: 'apiStyle' },
5
+ Service: { color: '#87CEEB', style: 'serviceStyle' },
6
+ Data: { color: '#90EE90', style: 'dataStyle' },
7
+ UI: { color: '#FFD700', style: 'uiStyle' },
8
+ Infrastructure: { color: '#D3D3D3', style: 'infraStyle' },
9
+ };
10
+
11
+ export class DiagramGenerator {
12
+ generateComponentDiagram(
13
+ edges: DependencyEdge[],
14
+ layers: Layer[]
15
+ ): string {
16
+ const nodes = new Set<string>();
17
+ edges.forEach((e) => {
18
+ nodes.add(e.from);
19
+ nodes.add(e.to);
20
+ });
21
+
22
+ const nodeColors: Record<string, string> = {};
23
+ for (const layer of layers) {
24
+ for (const file of layer.files) {
25
+ const moduleName = file.split('/').pop() || file;
26
+ nodeColors[moduleName] = LAYER_THEME[layer.name]?.color || '#FFFFFF';
27
+ }
28
+ }
29
+
30
+ let mermaid = 'graph TB\n';
31
+
32
+ for (const node of Array.from(nodes).slice(0, 20)) {
33
+ const moduleName = node.split('/').pop() || node;
34
+ const color = nodeColors[moduleName] || '#FFFFFF';
35
+ mermaid += ` ${this.sanitizeNodeName(node)}["${moduleName}"]:::${this.getStyleClass(color)}\n`;
36
+ }
37
+
38
+ mermaid += '\n';
39
+
40
+ for (const edge of edges.slice(0, 30)) {
41
+ mermaid += ` ${this.sanitizeNodeName(edge.from)} --> ${this.sanitizeNodeName(edge.to)}\n`;
42
+ }
43
+
44
+ return this.appendCommonClassDefs(mermaid);
45
+ }
46
+
47
+ generateLayerDiagram(layers: Layer[]): string {
48
+ let mermaid = 'graph LR\n';
49
+
50
+ const layerOrder = ['UI', 'API', 'Service', 'Data', 'Infrastructure'];
51
+
52
+ for (const layerName of layerOrder) {
53
+ const layer = layers.find((l) => l.name === layerName);
54
+ if (layer && layer.files.length > 0) {
55
+ const nodeId = layerName.replace(/\s+/g, '_');
56
+ const fileCount = layer.files.length;
57
+ const style = LAYER_THEME[layerName]?.style || 'defaultStyle';
58
+ mermaid += ` ${nodeId}["${layerName}<br/>(${fileCount} files)"]:::${style}\n`;
59
+ }
60
+ }
61
+
62
+ for (let i = 0; i < layerOrder.length - 1; i++) {
63
+ const from = layerOrder[i].replace(/\s+/g, '_');
64
+ const to = layerOrder[i + 1].replace(/\s+/g, '_');
65
+ mermaid += ` ${from} --> ${to}\n`;
66
+ }
67
+
68
+ return this.appendCommonClassDefs(mermaid);
69
+ }
70
+
71
+ generateDependencyFlowDiagram(edges: DependencyEdge[]): string {
72
+ const flowMap: Record<string, number> = {};
73
+
74
+ for (const edge of edges) {
75
+ const flowKey = `${edge.from} -> ${edge.to}`;
76
+ flowMap[flowKey] = (flowMap[flowKey] || 0) + edge.weight;
77
+ }
78
+
79
+ const topFlows = Object.entries(flowMap)
80
+ .sort((a, b) => b[1] - a[1])
81
+ .slice(0, 15);
82
+
83
+ let mermaid = 'graph LR\n';
84
+
85
+ const nodes = new Set<string>();
86
+ for (const [flow] of topFlows) {
87
+ const [from, _, to] = flow.split(' ');
88
+ nodes.add(from);
89
+ nodes.add(to);
90
+ }
91
+
92
+ for (const node of nodes) {
93
+ const moduleName = node.split('/').pop() || node;
94
+ mermaid += ` ${this.sanitizeNodeName(node)}["${moduleName}"]\n`;
95
+ }
96
+
97
+ mermaid += '\n';
98
+
99
+ for (const [flow, weight] of topFlows) {
100
+ const [from, _, to] = flow.split(' ');
101
+ const label = weight > 1 ? ` | ${weight}` : '';
102
+ mermaid += ` ${this.sanitizeNodeName(from)} -->|${label}| ${this.sanitizeNodeName(to)}\n`;
103
+ }
104
+
105
+ return mermaid; // specific diagram that doesn't use standard classDefs
106
+ }
107
+
108
+ private sanitizeNodeName(path: string): string {
109
+ return path
110
+ .replace(/[^a-zA-Z0-9]/g, '_')
111
+ .replace(/_+/g, '_')
112
+ .toLowerCase();
113
+ }
114
+
115
+ private getStyleClass(color: string): string {
116
+ const match = Object.values(LAYER_THEME).find(t => t.color === color);
117
+ return match ? match.style : 'defaultStyle';
118
+ }
119
+
120
+ private appendCommonClassDefs(mermaid: string): string {
121
+ let result = mermaid + '\n';
122
+ result += ' classDef apiStyle fill:#FFB6C1,stroke:#333,color:#000\n';
123
+ result += ' classDef serviceStyle fill:#87CEEB,stroke:#333,color:#000\n';
124
+ result += ' classDef dataStyle fill:#90EE90,stroke:#333,color:#000\n';
125
+ result += ' classDef uiStyle fill:#FFD700,stroke:#333,color:#000\n';
126
+ result += ' classDef infraStyle fill:#D3D3D3,stroke:#333,color:#000\n';
127
+ return result;
128
+ }
129
+ }
@@ -0,0 +1,64 @@
1
+ import { en } from './locales/en.js';
2
+ import { ptBR, AppTranslation } from './locales/pt-BR.js';
3
+
4
+ type Locale = 'en' | 'pt-BR';
5
+
6
+ class I18nEngine {
7
+ private currentLocale: Locale = 'en';
8
+
9
+ setLocale(locale: Locale) {
10
+ if (locale === 'en' || locale === 'pt-BR') {
11
+ this.currentLocale = locale;
12
+ } else {
13
+ this.currentLocale = 'en';
14
+ }
15
+ }
16
+
17
+ getLocale(): Locale {
18
+ return this.currentLocale;
19
+ }
20
+
21
+ private getDict(): AppTranslation {
22
+ return this.currentLocale === 'pt-BR' ? ptBR : en;
23
+ }
24
+
25
+ /**
26
+ * Obtém uma string de tradução navegando pela dot notation da AppTranslation.
27
+ * Exemplo: t('agents.backend.title', { lang: 'TypeScript' })
28
+ */
29
+ t(path: string, params?: Record<string, string | number>): string {
30
+ const keys = path.split('.');
31
+ let current: any = this.getDict();
32
+
33
+ for (const key of keys) {
34
+ if (current[key] === undefined) {
35
+ // Fallback to EN if missing in current dict
36
+ let fallback: any = en;
37
+ for (const fKey of keys) {
38
+ if (fallback[fKey] === undefined) return `[Missing translation: ${path}]`;
39
+ fallback = fallback[fKey];
40
+ }
41
+ current = fallback;
42
+ break;
43
+ }
44
+ current = current[key];
45
+ }
46
+
47
+ if (typeof current !== 'string') {
48
+ return `[Invalid translation path: ${path}]`;
49
+ }
50
+
51
+ let text = current as string;
52
+
53
+ // Replace params: {fw} -> value
54
+ if (params) {
55
+ for (const [key, value] of Object.entries(params)) {
56
+ text = text.replace(new RegExp(`{${key}}`, 'g'), String(value));
57
+ }
58
+ }
59
+
60
+ return text;
61
+ }
62
+ }
63
+
64
+ export const i18n = new I18nEngine();