@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,53 @@
1
+ // import { existsSync, readFileSync } from 'fs';
2
+ // import { join, basename } from 'path';
3
+ import { AnalysisReport } from '../types/core.js';
4
+ import { ProjectSummary } from '../types/summarizer.js';
5
+ // import { FileNode, WorkspaceInfo } from '../types/infrastructure.js';
6
+
7
+ export class KeywordExtractor {
8
+ public static readonly KEYWORD_BLACKLIST = new Set([
9
+ 'node_modules', 'dist', 'build', '.git', '.next', 'coverage',
10
+ '__tests__', '__mocks__', 'src', 'lib', 'index', 'main',
11
+ 'out', 'tmp', '.cache', 'vendor', '.vscode', '.idea',
12
+ ]);
13
+
14
+ public extractKeywords(
15
+ packageInfo: Record<string, unknown>,
16
+ _readme: string,
17
+ modules: ProjectSummary['modules'],
18
+ report: AnalysisReport,
19
+ ): string[] {
20
+ const keywords = new Set<string>();
21
+
22
+ // From package.json keywords
23
+ if (Array.isArray(packageInfo.keywords)) {
24
+ for (const kw of packageInfo.keywords) {
25
+ if (typeof kw === 'string') keywords.add(kw.toLowerCase());
26
+ }
27
+ }
28
+
29
+ // From module names (only clean names)
30
+ for (const mod of modules) {
31
+ const name = mod.name.toLowerCase();
32
+ if (!KeywordExtractor.KEYWORD_BLACKLIST.has(name)) {
33
+ keywords.add(name);
34
+ }
35
+ }
36
+
37
+ // From frameworks detected
38
+ for (const fw of report.projectInfo.frameworks) {
39
+ keywords.add(fw.toLowerCase());
40
+ }
41
+
42
+ // From languages
43
+ for (const lang of report.projectInfo.primaryLanguages) {
44
+ keywords.add(lang.toLowerCase());
45
+ }
46
+
47
+ // Filter out blacklisted and generic entries
48
+ return [...keywords]
49
+ .filter(kw => !KeywordExtractor.KEYWORD_BLACKLIST.has(kw) && kw.length > 1)
50
+ .slice(0, 20);
51
+ }
52
+
53
+ }
@@ -0,0 +1,194 @@
1
+ import { existsSync, readFileSync } from 'fs';
2
+ import { join, basename } from 'path';
3
+ import { AnalysisReport } from '../types/core.js';
4
+ import { ProjectSummary } from '../types/summarizer.js';
5
+ import { WorkspaceInfo } from '../types/infrastructure.js';
6
+
7
+ export class ModuleInferrer {
8
+ public inferModules(report: AnalysisReport, _projectPath: string): ProjectSummary['modules'] {
9
+ const workspaces = report.projectInfo.workspaces;
10
+
11
+ // If we have workspaces, use them as the authoritative module list
12
+ if (workspaces && workspaces.length > 0) {
13
+ return this.inferModulesFromWorkspaces(workspaces, report);
14
+ }
15
+
16
+ // Fallback: infer from directory structure
17
+ return this.inferModulesFromStructure(report);
18
+ }
19
+
20
+ public inferModulesFromWorkspaces(
21
+ workspaces: WorkspaceInfo[],
22
+ report: AnalysisReport,
23
+ ): ProjectSummary['modules'] {
24
+ return workspaces
25
+ .map((ws) => {
26
+ // Count files belonging to this workspace
27
+ const wsPrefix = ws.relativePath + '/';
28
+ const fileCount = report.dependencyGraph.nodes.filter(
29
+ (n) => n.startsWith(wsPrefix) || n.startsWith(ws.relativePath),
30
+ ).length;
31
+
32
+ // Get description: prefer package.json description, then README, then heuristic
33
+ const description = this.getWorkspaceDescription(ws);
34
+
35
+ // Use the short name (last segment of npm scope or dir name)
36
+ const displayName = ws.name.includes('/')
37
+ ? ws.name.split('/').pop() || ws.name
38
+ : basename(ws.relativePath);
39
+
40
+ return {
41
+ name: displayName,
42
+ files: fileCount || this.countFilesInDir(ws.path),
43
+ description,
44
+ };
45
+ })
46
+ .filter((m) => m.files > 0)
47
+ .sort((a, b) => b.files - a.files);
48
+ }
49
+
50
+ public getWorkspaceDescription(ws: WorkspaceInfo): string {
51
+ // 1. package.json description (most reliable)
52
+ if (ws.description && ws.description.trim().length > 5) {
53
+ return ws.description.trim();
54
+ }
55
+
56
+ // 2. README.md first paragraph
57
+ const readmePath = join(ws.path, 'README.md');
58
+ if (existsSync(readmePath)) {
59
+ try {
60
+ const lines = readFileSync(readmePath, 'utf-8').split('\n');
61
+ for (const line of lines) {
62
+ const trimmed = line.trim();
63
+ if (!trimmed || trimmed.startsWith('#') || trimmed.startsWith('[')
64
+ || trimmed.startsWith('<') || trimmed.startsWith('!')
65
+ || trimmed.length < 15) continue;
66
+ return trimmed.slice(0, 200);
67
+ }
68
+ } catch {
69
+ // skip
70
+ }
71
+ }
72
+
73
+ // 3. Heuristic from name and deps
74
+ return this.describeModule(basename(ws.relativePath), new Set(Object.keys(ws.dependencies)));
75
+ }
76
+
77
+ public countFilesInDir(dirPath: string): number {
78
+ try {
79
+ const { globSync } = require('glob');
80
+ return globSync('**/*.{ts,tsx,js,jsx,py,java,go}', {
81
+ cwd: dirPath,
82
+ ignore: ['**/node_modules/**', '**/dist/**'],
83
+ nodir: true,
84
+ }).length;
85
+ } catch {
86
+ return 0;
87
+ }
88
+ }
89
+
90
+ public inferModulesFromStructure(report: AnalysisReport): ProjectSummary['modules'] {
91
+ const modules: Map<string, { files: Set<string>; hints: Set<string> }> = new Map();
92
+
93
+ for (const node of report.dependencyGraph.nodes) {
94
+ // Safety: skip any node_modules paths that leaked through
95
+ if (node.includes('node_modules')) continue;
96
+
97
+ const parts = node.split('/');
98
+ // Skip root-level files
99
+ if (parts.length < 2) continue;
100
+
101
+ // Get the module directory (2nd level, or 1st level for flat projects)
102
+ let moduleName: string;
103
+ const firstDir = parts[0];
104
+
105
+ // Common source directories — go one level deeper
106
+ if (['src', 'lib', 'app', 'packages', 'modules', 'features', 'apps'].includes(firstDir)) {
107
+ moduleName = parts.length > 2 ? parts[1] : firstDir;
108
+ } else if (firstDir === 'tests' || firstDir === 'test' || firstDir === '__tests__') {
109
+ continue; // Skip test directories for module inference
110
+ } else {
111
+ moduleName = firstDir;
112
+ }
113
+
114
+ if (!modules.has(moduleName)) {
115
+ modules.set(moduleName, { files: new Set(), hints: new Set() });
116
+ }
117
+ const mod = modules.get(moduleName)!;
118
+ mod.files.add(node);
119
+
120
+ // Extract hints from filenames
121
+ const filename = basename(node).replace(/\.(ts|js|py|dart|go|java|rb|php|cs|tsx|jsx)$/, '');
122
+ if (filename !== 'index' && filename !== 'main' && filename !== '__init__') {
123
+ mod.hints.add(filename.toLowerCase());
124
+ }
125
+ }
126
+
127
+ return [...modules.entries()]
128
+ .filter(([, data]) => data.files.size > 0)
129
+ .sort((a, b) => b[1].files.size - a[1].files.size)
130
+ .slice(0, 15) // Top 15 modules
131
+ .map(([name, data]) => ({
132
+ name,
133
+ files: data.files.size,
134
+ description: this.describeModule(name, data.hints),
135
+ }));
136
+ }
137
+
138
+ public describeModule(name: string, hints: Set<string>): string {
139
+ const n = name.toLowerCase();
140
+ const h = [...hints].join(' ');
141
+
142
+ // Known module patterns
143
+ const patterns: [RegExp, string][] = [
144
+ [/auth/, 'Autenticação e autorização'],
145
+ [/user/, 'Gestão de usuários'],
146
+ [/payment|billing|charge|invoice/, 'Pagamentos e faturamento'],
147
+ [/order|cart|checkout/, 'Pedidos e checkout'],
148
+ [/product|catalog|item/, 'Catálogo de produtos'],
149
+ [/notif/, 'Sistema de notificações'],
150
+ [/email|mail|smtp/, 'Envio de emails'],
151
+ [/report|analytics|dashboard/, 'Relatórios e analytics'],
152
+ [/config|setting/, 'Configuração do sistema'],
153
+ [/util|helper|common|shared/, 'Utilitários compartilhados'],
154
+ [/middleware|interceptor|guard|pipe/, 'Middleware e interceptors'],
155
+ [/database|db|migration|seed/, 'Banco de dados e migrations'],
156
+ [/api|controller|route|endpoint/, 'API endpoints'],
157
+ [/service|business|domain/, 'Lógica de negócio'],
158
+ [/model|entity|schema/, 'Modelos de dados'],
159
+ [/test|spec|fixture/, 'Testes'],
160
+ [/validator|validation|sanitiz/, 'Validação de dados'],
161
+ [/security|crypto|encrypt/, 'Segurança e criptografia'],
162
+ [/cache|redis/, 'Cache e performance'],
163
+ [/queue|worker|job|task/, 'Processamento assíncrono'],
164
+ [/log|monitor|trace|metric/, 'Logging e monitoramento'],
165
+ [/file|upload|storage|s3/, 'Armazenamento de arquivos'],
166
+ [/search|elastic|index/, 'Busca e indexação'],
167
+ [/chat|message|websocket|socket/, 'Comunicação em tempo real'],
168
+ [/i18n|locale|translation/, 'Internacionalização'],
169
+ [/theme|style|design/, 'Design system e temas'],
170
+ [/component|widget|ui/, 'Componentes de UI'],
171
+ [/page|screen|view/, 'Páginas/telas'],
172
+ [/hook|composable/, 'Hooks/composables reutilizáveis'],
173
+ [/store|state|redux|bloc/, 'Gerenciamento de estado'],
174
+ [/navigation|router|routing/, 'Navegação e rotas'],
175
+ [/bridge/, 'Camada de integração'],
176
+ [/core/, 'Núcleo e orquestração'],
177
+ [/event/, 'Sistema de eventos'],
178
+ [/type/, 'Definições de tipos'],
179
+ [/mcp/, 'MCP Server'],
180
+ [/cli/, 'Interface de linha de comando'],
181
+ [/cloud/, 'Serviço cloud / API'],
182
+ [/autonomy/, 'Automação e self-healing'],
183
+ [/app/, 'Aplicação principal'],
184
+ ];
185
+
186
+ const combined = `${n} ${h}`;
187
+ for (const [regex, desc] of patterns) {
188
+ if (regex.test(combined)) return desc;
189
+ }
190
+
191
+ return `Módulo ${name}`;
192
+ }
193
+
194
+ }
@@ -0,0 +1,34 @@
1
+ import { existsSync, readFileSync } from 'fs';
2
+ import { join, basename } from 'path';
3
+ // import { AnalysisReport } from '../types/core.js';
4
+ // import { ProjectSummary } from '../types/summarizer.js';
5
+ // import { FileNode, WorkspaceInfo } from '../types/infrastructure.js';
6
+
7
+ export class PackageReader {
8
+ public readPackageJson(projectPath: string): Record<string, unknown> {
9
+ const candidates = [
10
+ join(projectPath, 'package.json'),
11
+ join(projectPath, 'pyproject.toml'),
12
+ join(projectPath, 'pubspec.yaml'),
13
+ join(projectPath, 'Cargo.toml'),
14
+ join(projectPath, 'pom.xml'),
15
+ join(projectPath, 'build.gradle'),
16
+ ];
17
+
18
+ for (const candidate of candidates) {
19
+ if (existsSync(candidate)) {
20
+ try {
21
+ if (candidate.endsWith('.json')) {
22
+ return JSON.parse(readFileSync(candidate, 'utf-8'));
23
+ }
24
+ // For non-JSON, return raw content as 'raw' field
25
+ return { raw: readFileSync(candidate, 'utf-8'), type: basename(candidate) };
26
+ } catch {
27
+ // ignore
28
+ }
29
+ }
30
+ }
31
+ return {};
32
+ }
33
+
34
+ }
@@ -0,0 +1,197 @@
1
+ import { existsSync, readFileSync } from 'fs';
2
+ import { join} from 'path';
3
+ import { AnalysisReport } from '../types/core.js';
4
+ import { ProjectSummary } from '../types/summarizer.js';
5
+ // import { FileNode, WorkspaceInfo } from '../types/infrastructure.js';
6
+
7
+ export class PurposeInferrer {
8
+ public buildTechStack(report: AnalysisReport, packageInfo: Record<string, unknown>): string[] {
9
+ const stack: string[] = [];
10
+
11
+ // Languages
12
+ stack.push(...report.projectInfo.primaryLanguages);
13
+
14
+ // Frameworks from report
15
+ stack.push(...report.projectInfo.frameworks);
16
+
17
+ // Dependencies from package.json
18
+ const deps = { ...(packageInfo.dependencies as Record<string, string> || {}), ...(packageInfo.devDependencies as Record<string, string> || {}) };
19
+ const notable = [
20
+ 'express', 'fastify', 'nestjs', '@nestjs/core', 'koa', 'hapi',
21
+ 'react', 'next', 'angular', 'vue', 'svelte',
22
+ 'prisma', '@prisma/client', 'typeorm', 'sequelize', 'mongoose', 'knex',
23
+ 'jest', 'mocha', 'vitest', 'cypress', 'playwright',
24
+ 'tailwindcss', 'styled-components', 'emotion',
25
+ 'redis', 'ioredis', 'bull', 'bullmq',
26
+ 'graphql', 'apollo', '@apollo/server',
27
+ 'socket.io', 'ws',
28
+ 'passport', 'jsonwebtoken', 'bcrypt',
29
+ 'winston', 'pino',
30
+ 'swagger', '@nestjs/swagger',
31
+ 'docker', 'kubernetes',
32
+ 'aws-sdk', '@aws-sdk',
33
+ 'stripe', 'paypal',
34
+ ];
35
+
36
+ for (const dep of Object.keys(deps)) {
37
+ const cleaned = dep.replace('@', '').split('/')[0];
38
+ if (notable.some(n => dep.includes(n))) {
39
+ if (!stack.some(s => s.toLowerCase() === cleaned.toLowerCase())) {
40
+ stack.push(dep);
41
+ }
42
+ }
43
+ }
44
+
45
+ return [...new Set(stack)].slice(0, 15);
46
+ }
47
+
48
+ public buildDescription(packageInfo: Record<string, unknown>, readme: string, report: AnalysisReport): string {
49
+ // Priority 1: package.json description
50
+ if (typeof packageInfo.description === 'string' && packageInfo.description.trim()) {
51
+ return packageInfo.description.trim();
52
+ }
53
+
54
+ // Priority 2: First paragraph of README (skip badges, titles)
55
+ if (readme) {
56
+ const lines = readme.split('\n');
57
+ for (const line of lines) {
58
+ const trimmed = line.trim();
59
+ // Skip empty lines, headers, badges, links, HTML tags
60
+ if (!trimmed) continue;
61
+ if (trimmed.startsWith('#')) continue;
62
+ if (trimmed.startsWith('[!') || trimmed.startsWith('[![')) continue;
63
+ if (trimmed.startsWith('<')) continue;
64
+ if (trimmed.startsWith('---') || trimmed.startsWith('===')) continue;
65
+ if (trimmed.startsWith('![')) continue;
66
+ if (trimmed.length < 20) continue;
67
+
68
+ // Found a real text paragraph
69
+ return trimmed.slice(0, 300);
70
+ }
71
+ }
72
+
73
+ // Priority 3: Infer from project name and structure
74
+ const name = report.projectInfo.name || 'Unknown';
75
+ const langs = report.projectInfo.primaryLanguages.join(', ');
76
+ const files = report.projectInfo.totalFiles;
77
+ return `Projeto ${name} — ${files} arquivos em ${langs}`;
78
+ }
79
+
80
+ public inferPurpose(
81
+ keywords: string[],
82
+ modules: ProjectSummary['modules'],
83
+ report: AnalysisReport,
84
+ ): string {
85
+ const allSignals = [
86
+ ...keywords,
87
+ ...modules.map(m => m.name.toLowerCase()),
88
+ ...modules.map(m => m.description.toLowerCase()),
89
+ // Only use project nodes, never leaked node_modules paths
90
+ ...report.dependencyGraph.nodes
91
+ .filter(n => !n.includes('node_modules'))
92
+ .map(n => n.toLowerCase()),
93
+ ].join(' ');
94
+
95
+ // Infer project type from signals
96
+ const types: [RegExp, string][] = [
97
+ [/api.*(rest|graph|endpoint)|controller.*route|swagger|openapi/, 'API Backend'],
98
+ [/cli|command.*line|bin\/|yargs|commander/, 'CLI Tool'],
99
+ [/component.*ui|react|angular|vue|frontend|page.*screen/, 'Frontend Application'],
100
+ [/mobile|flutter|dart|react.native|ionic/, 'Mobile App'],
101
+ [/library|lib|package|npm|pub|sdk|module/, 'Library / Package'],
102
+ [/test|spec|validator|lint|analyz|check/, 'Tool de Análise / Validação'],
103
+ [/microservice|service|worker|queue/, 'Microservice'],
104
+ [/monorepo|workspace|packages\//, 'Monorepo'],
105
+ [/bot|scraper|crawler|automation/, 'Bot / Automação'],
106
+ [/game|canvas|webgl|three/, 'Game / Visualização'],
107
+ [/e-?commerce|shop|cart|product|catalog/, 'E-commerce'],
108
+ [/blog|cms|content|post|article/, 'CMS / Blog'],
109
+ [/auth|login|oauth|sso|identity/, 'Sistema de Autenticação'],
110
+ [/chat|message|realtime|socket/, 'Comunicação Real-time'],
111
+ [/dashboard|admin|panel|analytics/, 'Dashboard / Admin Panel'],
112
+ [/payment|billing|fintech|finance|bank/, 'Fintech / Pagamentos'],
113
+ [/health|medical|patient|clinic/, 'Healthcare'],
114
+ [/education|course|learn|student/, 'EdTech'],
115
+ ];
116
+
117
+ const matched: string[] = [];
118
+ for (const [regex, type] of types) {
119
+ if (regex.test(allSignals)) {
120
+ matched.push(type);
121
+ }
122
+ }
123
+
124
+ if (matched.length > 0) {
125
+ return matched.slice(0, 3).join(' + ');
126
+ }
127
+
128
+ // Fallback
129
+ const langs = report.projectInfo.primaryLanguages.join('/');
130
+ return `Projeto ${langs}`;
131
+ }
132
+
133
+ public findEntryPoints(report: AnalysisReport, projectPath: string): string[] {
134
+ const entries: string[] = [];
135
+
136
+ // 1. From workspace bin fields
137
+ const workspaces = report.projectInfo.workspaces;
138
+ if (workspaces) {
139
+ for (const ws of workspaces) {
140
+ if (ws.bin) {
141
+ if (typeof ws.bin === 'string') {
142
+ entries.push(`${ws.relativePath}/${ws.bin}`);
143
+ } else {
144
+ for (const [, binPath] of Object.entries(ws.bin)) {
145
+ const fullPath = `${ws.relativePath}/${binPath}`;
146
+ entries.push(fullPath);
147
+ }
148
+ }
149
+ }
150
+ if (ws.main) {
151
+ entries.push(`${ws.relativePath}/${ws.main}`);
152
+ }
153
+ }
154
+ }
155
+
156
+ // 2. From root package.json bin/main
157
+ const rootPkgPath = join(projectPath, 'package.json');
158
+ if (existsSync(rootPkgPath)) {
159
+ try {
160
+ const pkg = JSON.parse(readFileSync(rootPkgPath, 'utf-8'));
161
+ if (pkg.bin) {
162
+ if (typeof pkg.bin === 'string') {
163
+ entries.push(pkg.bin);
164
+ } else {
165
+ for (const [, binPath] of Object.entries(pkg.bin)) {
166
+ entries.push(binPath as string);
167
+ }
168
+ }
169
+ }
170
+ if (pkg.main && !entries.includes(pkg.main)) {
171
+ entries.push(pkg.main);
172
+ }
173
+ } catch {
174
+ // skip
175
+ }
176
+ }
177
+
178
+ // 3. Pattern-based detection (fallback)
179
+ const entryPatterns = [
180
+ /^(src\/)?(main|index|app|server|cli)\.(ts|js|py|dart|go|java)$/,
181
+ /^(src\/)?bin\//,
182
+ /^packages\/[^/]+\/src\/(main|index|app|server|cli)\.(ts|js)$/,
183
+ /^packages\/[^/]+\/src\/bin\//,
184
+ /manage\.py$/,
185
+ /^main\.go$/,
186
+ ];
187
+
188
+ const patternEntries = report.dependencyGraph.nodes
189
+ .filter(node => entryPatterns.some(p => p.test(node)))
190
+ .filter(node => !entries.includes(node));
191
+
192
+ entries.push(...patternEntries);
193
+
194
+ return [...new Set(entries)].slice(0, 10);
195
+ }
196
+
197
+ }
@@ -0,0 +1,24 @@
1
+ import { existsSync, readFileSync } from 'fs';
2
+ import { join} from 'path';
3
+ // import { AnalysisReport } from '../types/core.js';
4
+ // import { ProjectSummary } from '../types/summarizer.js';
5
+ // import { FileNode, WorkspaceInfo } from '../types/infrastructure.js';
6
+
7
+ export class ReadmeReader {
8
+ public readReadme(projectPath: string): string {
9
+ const candidates = ['README.md', 'readme.md', 'README.txt', 'README', 'README.rst'];
10
+ for (const name of candidates) {
11
+ const path = join(projectPath, name);
12
+ if (existsSync(path)) {
13
+ try {
14
+ // Read first 3000 chars — enough for description, skip excessive content
15
+ return readFileSync(path, 'utf-8').slice(0, 3000);
16
+ } catch {
17
+ // ignore
18
+ }
19
+ }
20
+ }
21
+ return '';
22
+ }
23
+
24
+ }
@@ -0,0 +1,29 @@
1
+ export interface ArchitectRules {
2
+ version: string;
3
+ project?: {
4
+ name: string;
5
+ description?: string;
6
+ };
7
+ quality_gates?: {
8
+ min_overall_score?: number;
9
+ max_critical_anti_patterns?: number;
10
+ max_high_anti_patterns?: number;
11
+ };
12
+ boundaries?: {
13
+ allow_circular_dependencies?: boolean;
14
+ banned_imports?: string[];
15
+ };
16
+ }
17
+
18
+ export interface RuleViolation {
19
+ level: 'error' | 'warning';
20
+ rule: string;
21
+ message: string;
22
+ actual?: number | string | string[];
23
+ expected?: number | string | string[];
24
+ }
25
+
26
+ export interface ValidationResult {
27
+ success: boolean;
28
+ violations: RuleViolation[];
29
+ }
@@ -0,0 +1,94 @@
1
+ import { ProjectInfo } from './infrastructure.js';
2
+ import { ProjectSummary } from './summarizer.js';
3
+
4
+ export interface DependencyEdge {
5
+ from: string;
6
+ to: string;
7
+ type: 'import' | 'export' | 'inheritance' | 'composition';
8
+ weight: number;
9
+ }
10
+
11
+ export interface Layer {
12
+ name: 'API' | 'Service' | 'Data' | 'UI' | 'Infrastructure';
13
+ files: string[];
14
+ description: string;
15
+ }
16
+
17
+ export interface AntiPattern {
18
+ name: string;
19
+ severity: 'CRITICAL' | 'HIGH' | 'MEDIUM' | 'LOW';
20
+ location: string;
21
+ description: string;
22
+ suggestion: string;
23
+ affectedFiles?: string[];
24
+ metrics?: Record<string, number | string>;
25
+ }
26
+
27
+ export interface ScoreComponent {
28
+ name: string;
29
+ score: number;
30
+ maxScore: number;
31
+ weight: number;
32
+ explanation: string;
33
+ }
34
+
35
+ export interface ArchitectureScore {
36
+ overall: number;
37
+ components: ScoreComponent[];
38
+ breakdown: {
39
+ modularity: number;
40
+ coupling: number;
41
+ cohesion: number;
42
+ layering: number;
43
+ };
44
+ }
45
+
46
+ export interface AnalysisReport {
47
+ timestamp: string;
48
+ projectInfo: ProjectInfo;
49
+ score: ArchitectureScore;
50
+ antiPatterns: AntiPattern[];
51
+ layers: Layer[];
52
+ dependencyGraph: {
53
+ nodes: string[];
54
+ edges: DependencyEdge[];
55
+ };
56
+ suggestions: {
57
+ priority: 'CRITICAL' | 'HIGH' | 'MEDIUM' | 'LOW';
58
+ title: string;
59
+ description: string;
60
+ impact: string;
61
+ }[];
62
+ diagram: {
63
+ mermaid: string;
64
+ type: 'component' | 'layer' | 'dependency';
65
+ };
66
+ projectSummary?: ProjectSummary;
67
+ }
68
+
69
+ export interface ArchitectConfig {
70
+ ignore?: string[];
71
+ frameworks?: {
72
+ detect?: boolean;
73
+ };
74
+ antiPatterns?: {
75
+ godClass?: {
76
+ linesThreshold?: number;
77
+ methodsThreshold?: number;
78
+ };
79
+ shotgunSurgery?: {
80
+ changePropagationThreshold?: number;
81
+ };
82
+ };
83
+ score?: {
84
+ modularity?: number;
85
+ coupling?: number;
86
+ cohesion?: number;
87
+ layering?: number;
88
+ };
89
+ monorepo?: {
90
+ enabled?: boolean;
91
+ treatPackagesAsModules?: boolean;
92
+ };
93
+ plugins?: string[];
94
+ }
@@ -0,0 +1,41 @@
1
+ export interface FileNode {
2
+ path: string;
3
+ name: string;
4
+ type: 'file' | 'directory';
5
+ extension?: string;
6
+ lines?: number;
7
+ language?: string;
8
+ children?: FileNode[];
9
+ imports?: string[];
10
+ exports?: string[];
11
+ }
12
+
13
+ export interface WorkspaceInfo {
14
+ name: string;
15
+ path: string;
16
+ relativePath: string;
17
+ description: string;
18
+ version: string;
19
+ dependencies: Record<string, string>;
20
+ devDependencies: Record<string, string>;
21
+ bin?: Record<string, string>;
22
+ main?: string;
23
+ }
24
+
25
+ export interface ProjectInfo {
26
+ path: string;
27
+ name: string;
28
+ frameworks: string[];
29
+ totalFiles: number;
30
+ totalLines: number;
31
+ primaryLanguages: string[];
32
+ fileTree?: FileNode;
33
+ workspaces?: WorkspaceInfo[];
34
+ }
35
+
36
+ export interface ParsedImport {
37
+ source: string;
38
+ names: string[];
39
+ isDefault: boolean;
40
+ isNamespace: boolean;
41
+ }
@@ -0,0 +1,19 @@
1
+ import { AntiPattern, ArchitectConfig } from './core.js';
2
+ import { FileNode } from './infrastructure.js';
3
+
4
+ export interface PluginContext {
5
+ projectPath: string;
6
+ config: ArchitectConfig;
7
+ }
8
+
9
+ export type CustomAntiPatternDetector = (
10
+ fileTree: FileNode,
11
+ dependencies: Map<string, Set<string>>,
12
+ context: PluginContext
13
+ ) => AntiPattern[] | Promise<AntiPattern[]>;
14
+
15
+ export interface ArchitectPlugin {
16
+ name: string;
17
+ version: string;
18
+ detectAntiPatterns?: CustomAntiPatternDetector;
19
+ }