@girardelli/architect 2.2.0 → 5.0.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 (212) hide show
  1. package/README.md +105 -116
  2. package/architect-run.sh +431 -0
  3. package/assets/banner-v3.html +561 -0
  4. package/dist/agent-generator/context-enricher.d.ts +58 -0
  5. package/dist/agent-generator/context-enricher.d.ts.map +1 -0
  6. package/dist/agent-generator/context-enricher.js +613 -0
  7. package/dist/agent-generator/context-enricher.js.map +1 -0
  8. package/dist/agent-generator/domain-inferrer.d.ts +52 -0
  9. package/dist/agent-generator/domain-inferrer.d.ts.map +1 -0
  10. package/dist/agent-generator/domain-inferrer.js +585 -0
  11. package/dist/agent-generator/domain-inferrer.js.map +1 -0
  12. package/dist/agent-generator/framework-detector.d.ts +40 -0
  13. package/dist/agent-generator/framework-detector.d.ts.map +1 -0
  14. package/dist/agent-generator/framework-detector.js +611 -0
  15. package/dist/agent-generator/framework-detector.js.map +1 -0
  16. package/dist/agent-generator/index.d.ts +47 -0
  17. package/dist/agent-generator/index.d.ts.map +1 -0
  18. package/dist/agent-generator/index.js +545 -0
  19. package/dist/agent-generator/index.js.map +1 -0
  20. package/dist/agent-generator/stack-detector.d.ts +14 -0
  21. package/dist/agent-generator/stack-detector.d.ts.map +1 -0
  22. package/dist/agent-generator/stack-detector.js +124 -0
  23. package/dist/agent-generator/stack-detector.js.map +1 -0
  24. package/dist/agent-generator/templates/core/agents.d.ts +17 -0
  25. package/dist/agent-generator/templates/core/agents.d.ts.map +1 -0
  26. package/dist/agent-generator/templates/core/agents.js +1256 -0
  27. package/dist/agent-generator/templates/core/agents.js.map +1 -0
  28. package/dist/agent-generator/templates/core/architecture-rules.d.ts +7 -0
  29. package/dist/agent-generator/templates/core/architecture-rules.d.ts.map +1 -0
  30. package/dist/agent-generator/templates/core/architecture-rules.js +274 -0
  31. package/dist/agent-generator/templates/core/architecture-rules.js.map +1 -0
  32. package/dist/agent-generator/templates/core/general-rules.d.ts +8 -0
  33. package/dist/agent-generator/templates/core/general-rules.d.ts.map +1 -0
  34. package/dist/agent-generator/templates/core/general-rules.js +301 -0
  35. package/dist/agent-generator/templates/core/general-rules.js.map +1 -0
  36. package/dist/agent-generator/templates/core/hooks-generator.d.ts +21 -0
  37. package/dist/agent-generator/templates/core/hooks-generator.d.ts.map +1 -0
  38. package/dist/agent-generator/templates/core/hooks-generator.js +233 -0
  39. package/dist/agent-generator/templates/core/hooks-generator.js.map +1 -0
  40. package/dist/agent-generator/templates/core/index-md.d.ts +7 -0
  41. package/dist/agent-generator/templates/core/index-md.d.ts.map +1 -0
  42. package/dist/agent-generator/templates/core/index-md.js +246 -0
  43. package/dist/agent-generator/templates/core/index-md.js.map +1 -0
  44. package/dist/agent-generator/templates/core/orchestrator.d.ts +8 -0
  45. package/dist/agent-generator/templates/core/orchestrator.d.ts.map +1 -0
  46. package/dist/agent-generator/templates/core/orchestrator.js +422 -0
  47. package/dist/agent-generator/templates/core/orchestrator.js.map +1 -0
  48. package/dist/agent-generator/templates/core/preflight.d.ts +8 -0
  49. package/dist/agent-generator/templates/core/preflight.d.ts.map +1 -0
  50. package/dist/agent-generator/templates/core/preflight.js +213 -0
  51. package/dist/agent-generator/templates/core/preflight.js.map +1 -0
  52. package/dist/agent-generator/templates/core/quality-gates.d.ts +11 -0
  53. package/dist/agent-generator/templates/core/quality-gates.d.ts.map +1 -0
  54. package/dist/agent-generator/templates/core/quality-gates.js +254 -0
  55. package/dist/agent-generator/templates/core/quality-gates.js.map +1 -0
  56. package/dist/agent-generator/templates/core/security-rules.d.ts +7 -0
  57. package/dist/agent-generator/templates/core/security-rules.d.ts.map +1 -0
  58. package/dist/agent-generator/templates/core/security-rules.js +528 -0
  59. package/dist/agent-generator/templates/core/security-rules.js.map +1 -0
  60. package/dist/agent-generator/templates/core/skills-generator.d.ts +19 -0
  61. package/dist/agent-generator/templates/core/skills-generator.d.ts.map +1 -0
  62. package/dist/agent-generator/templates/core/skills-generator.js +546 -0
  63. package/dist/agent-generator/templates/core/skills-generator.js.map +1 -0
  64. package/dist/agent-generator/templates/core/workflow-fix-bug.d.ts +7 -0
  65. package/dist/agent-generator/templates/core/workflow-fix-bug.d.ts.map +1 -0
  66. package/dist/agent-generator/templates/core/workflow-fix-bug.js +237 -0
  67. package/dist/agent-generator/templates/core/workflow-fix-bug.js.map +1 -0
  68. package/dist/agent-generator/templates/core/workflow-new-feature.d.ts +8 -0
  69. package/dist/agent-generator/templates/core/workflow-new-feature.d.ts.map +1 -0
  70. package/dist/agent-generator/templates/core/workflow-new-feature.js +321 -0
  71. package/dist/agent-generator/templates/core/workflow-new-feature.js.map +1 -0
  72. package/dist/agent-generator/templates/core/workflow-review.d.ts +7 -0
  73. package/dist/agent-generator/templates/core/workflow-review.d.ts.map +1 -0
  74. package/dist/agent-generator/templates/core/workflow-review.js +104 -0
  75. package/dist/agent-generator/templates/core/workflow-review.js.map +1 -0
  76. package/dist/agent-generator/templates/domain/index.d.ts +22 -0
  77. package/dist/agent-generator/templates/domain/index.d.ts.map +1 -0
  78. package/dist/agent-generator/templates/domain/index.js +1176 -0
  79. package/dist/agent-generator/templates/domain/index.js.map +1 -0
  80. package/dist/agent-generator/templates/stack/index.d.ts +8 -0
  81. package/dist/agent-generator/templates/stack/index.d.ts.map +1 -0
  82. package/dist/agent-generator/templates/stack/index.js +695 -0
  83. package/dist/agent-generator/templates/stack/index.js.map +1 -0
  84. package/dist/agent-generator/templates/template-helpers.d.ts +75 -0
  85. package/dist/agent-generator/templates/template-helpers.d.ts.map +1 -0
  86. package/dist/agent-generator/templates/template-helpers.js +726 -0
  87. package/dist/agent-generator/templates/template-helpers.js.map +1 -0
  88. package/dist/agent-generator/types.d.ts +196 -0
  89. package/dist/agent-generator/types.d.ts.map +1 -0
  90. package/dist/agent-generator/types.js +27 -0
  91. package/dist/agent-generator/types.js.map +1 -0
  92. package/dist/analyzer.d.ts +5 -0
  93. package/dist/analyzer.d.ts.map +1 -1
  94. package/dist/analyzer.js +46 -5
  95. package/dist/analyzer.js.map +1 -1
  96. package/dist/analyzers/forecast.d.ts +85 -0
  97. package/dist/analyzers/forecast.d.ts.map +1 -0
  98. package/dist/analyzers/forecast.js +337 -0
  99. package/dist/analyzers/forecast.js.map +1 -0
  100. package/dist/analyzers/git-cache.d.ts +7 -0
  101. package/dist/analyzers/git-cache.d.ts.map +1 -0
  102. package/dist/analyzers/git-cache.js +41 -0
  103. package/dist/analyzers/git-cache.js.map +1 -0
  104. package/dist/analyzers/git-history.d.ts +113 -0
  105. package/dist/analyzers/git-history.d.ts.map +1 -0
  106. package/dist/analyzers/git-history.js +333 -0
  107. package/dist/analyzers/git-history.js.map +1 -0
  108. package/dist/analyzers/index.d.ts +10 -0
  109. package/dist/analyzers/index.d.ts.map +1 -0
  110. package/dist/analyzers/index.js +7 -0
  111. package/dist/analyzers/index.js.map +1 -0
  112. package/dist/analyzers/temporal-scorer.d.ts +72 -0
  113. package/dist/analyzers/temporal-scorer.d.ts.map +1 -0
  114. package/dist/analyzers/temporal-scorer.js +140 -0
  115. package/dist/analyzers/temporal-scorer.js.map +1 -0
  116. package/dist/anti-patterns.d.ts +7 -0
  117. package/dist/anti-patterns.d.ts.map +1 -1
  118. package/dist/anti-patterns.js +25 -6
  119. package/dist/anti-patterns.js.map +1 -1
  120. package/dist/cli.d.ts +2 -3
  121. package/dist/cli.d.ts.map +1 -1
  122. package/dist/cli.js +275 -113
  123. package/dist/cli.js.map +1 -1
  124. package/dist/config.d.ts +6 -0
  125. package/dist/config.d.ts.map +1 -1
  126. package/dist/config.js +48 -11
  127. package/dist/config.js.map +1 -1
  128. package/dist/html-reporter.d.ts +3 -1
  129. package/dist/html-reporter.d.ts.map +1 -1
  130. package/dist/html-reporter.js +248 -12
  131. package/dist/html-reporter.js.map +1 -1
  132. package/dist/index.d.ts +16 -3
  133. package/dist/index.d.ts.map +1 -1
  134. package/dist/index.js +63 -4
  135. package/dist/index.js.map +1 -1
  136. package/dist/project-summarizer.d.ts +38 -0
  137. package/dist/project-summarizer.d.ts.map +1 -0
  138. package/dist/project-summarizer.js +463 -0
  139. package/dist/project-summarizer.js.map +1 -0
  140. package/dist/refactor-reporter.js +1 -1
  141. package/dist/scanner.d.ts +8 -2
  142. package/dist/scanner.d.ts.map +1 -1
  143. package/dist/scanner.js +153 -113
  144. package/dist/scanner.js.map +1 -1
  145. package/dist/scorer.d.ts.map +1 -1
  146. package/dist/scorer.js +24 -11
  147. package/dist/scorer.js.map +1 -1
  148. package/dist/types.d.ts +29 -0
  149. package/dist/types.d.ts.map +1 -1
  150. package/package.json +12 -3
  151. package/src/agent-generator/context-enricher.ts +672 -0
  152. package/src/agent-generator/domain-inferrer.ts +635 -0
  153. package/src/agent-generator/framework-detector.ts +669 -0
  154. package/src/agent-generator/index.ts +634 -0
  155. package/src/agent-generator/stack-detector.ts +115 -0
  156. package/src/agent-generator/templates/core/agents.ts +1296 -0
  157. package/src/agent-generator/templates/core/architecture-rules.ts +287 -0
  158. package/src/agent-generator/templates/core/general-rules.ts +306 -0
  159. package/src/agent-generator/templates/core/hooks-generator.ts +242 -0
  160. package/src/agent-generator/templates/core/index-md.ts +260 -0
  161. package/src/agent-generator/templates/core/orchestrator.ts +459 -0
  162. package/src/agent-generator/templates/core/preflight.ts +215 -0
  163. package/src/agent-generator/templates/core/quality-gates.ts +256 -0
  164. package/src/agent-generator/templates/core/security-rules.ts +543 -0
  165. package/src/agent-generator/templates/core/skills-generator.ts +585 -0
  166. package/src/agent-generator/templates/core/workflow-fix-bug.ts +239 -0
  167. package/src/agent-generator/templates/core/workflow-new-feature.ts +323 -0
  168. package/src/agent-generator/templates/core/workflow-review.ts +106 -0
  169. package/src/agent-generator/templates/domain/index.ts +1201 -0
  170. package/src/agent-generator/templates/stack/index.ts +705 -0
  171. package/src/agent-generator/templates/template-helpers.ts +776 -0
  172. package/src/agent-generator/types.ts +232 -0
  173. package/src/analyzer.ts +51 -5
  174. package/src/analyzers/forecast.ts +496 -0
  175. package/src/analyzers/git-cache.ts +52 -0
  176. package/src/analyzers/git-history.ts +488 -0
  177. package/src/analyzers/index.ts +33 -0
  178. package/src/analyzers/temporal-scorer.ts +227 -0
  179. package/src/anti-patterns.ts +29 -6
  180. package/src/cli.ts +316 -117
  181. package/src/config.ts +52 -11
  182. package/src/html-reporter.ts +263 -13
  183. package/src/index.ts +93 -10
  184. package/src/project-summarizer.ts +521 -0
  185. package/src/refactor-reporter.ts +1 -1
  186. package/src/scanner.ts +136 -90
  187. package/src/scorer.ts +26 -11
  188. package/src/types.ts +27 -0
  189. package/tests/agent-generator.test.ts +427 -0
  190. package/tests/analyzers-integration.test.ts +174 -0
  191. package/tests/architect-adapter-enrichment.test.ts +9 -0
  192. package/tests/context-enricher.test.ts +971 -0
  193. package/tests/fixtures/monorepo/package.json +6 -0
  194. package/tests/fixtures/monorepo/packages/app/package.json +12 -0
  195. package/tests/fixtures/monorepo/packages/app/src/index.ts +6 -0
  196. package/tests/fixtures/monorepo/packages/core/package.json +7 -0
  197. package/tests/fixtures/monorepo/packages/core/src/index.ts +7 -0
  198. package/tests/forecast.test.ts +509 -0
  199. package/tests/framework-detector.test.ts +1172 -0
  200. package/tests/git-history.test.ts +254 -0
  201. package/tests/monorepo-scan.test.ts +170 -0
  202. package/tests/scanner.test.ts +7 -8
  203. package/tests/scorer.test.ts +594 -0
  204. package/tests/stack-detector.test.ts +241 -0
  205. package/tests/template-generation.test.ts +706 -0
  206. package/tests/template-helpers.test.ts +1152 -0
  207. package/tests/temporal-scorer.test.ts +307 -0
  208. package/dist/agent-generator.d.ts +0 -106
  209. package/dist/agent-generator.d.ts.map +0 -1
  210. package/dist/agent-generator.js +0 -1398
  211. package/dist/agent-generator.js.map +0 -1
  212. package/src/agent-generator.ts +0 -1526
package/src/scanner.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { globSync } from 'glob';
2
- import { readFileSync, lstatSync } from 'fs';
3
- import { join, relative, extname } from 'path';
4
- import { FileNode, ProjectInfo, ArchitectConfig } from './types.js';
2
+ import { readFileSync, lstatSync, existsSync } from 'fs';
3
+ import { join, relative, extname, resolve } from 'path';
4
+ import { FileNode, ProjectInfo, ArchitectConfig, WorkspaceInfo } from './types.js';
5
5
 
6
6
  export class ProjectScanner {
7
7
  private projectPath: string;
@@ -17,14 +17,19 @@ export class ProjectScanner {
17
17
  const files = this.scanDirectory();
18
18
  const fileTree = this.buildFileTree(files);
19
19
 
20
- // Detect frameworks from scanned files AND parent package.json
21
- const parentPackageJsons = this.findParentPackageJsons();
22
- const allFilesForDetection = [...files, ...parentPackageJsons];
23
- const frameworks = this.detectFrameworks(allFilesForDetection);
20
+ // Detect workspaces from root package.json
21
+ const workspaces = this.detectWorkspaces();
24
22
 
23
+ // Detect frameworks ONLY from root + workspace package.json files (never from node_modules)
24
+ const workspacePkgJsonPaths = [
25
+ join(this.projectPath, 'package.json'),
26
+ ...workspaces.map(ws => join(ws.path, 'package.json')),
27
+ ].filter(p => existsSync(p));
28
+
29
+ const frameworks = this.detectFrameworks(workspacePkgJsonPaths);
25
30
  const languages = this.detectLanguages(files);
26
31
  const totalLines = this.countTotalLines(files);
27
- const projectName = this.resolveProjectName(parentPackageJsons);
32
+ const projectName = this.resolveProjectName(workspacePkgJsonPaths);
28
33
 
29
34
  return {
30
35
  path: this.projectPath,
@@ -34,31 +39,66 @@ export class ProjectScanner {
34
39
  totalLines,
35
40
  primaryLanguages: languages,
36
41
  fileTree,
42
+ workspaces: workspaces.length > 0 ? workspaces : undefined,
37
43
  };
38
44
  }
39
45
 
40
46
  /**
41
- * Walk up directory tree to find package.json files for project name and framework detection
47
+ * Detect npm/yarn/pnpm workspaces from root package.json.
48
+ * Reads the "workspaces" field and resolves each workspace to its package.json.
42
49
  */
43
- private findParentPackageJsons(): string[] {
44
- const found: string[] = [];
45
- let dir = this.projectPath;
46
- const root = '/';
47
- let depth = 0;
48
-
49
- while (dir !== root && depth < 5) {
50
- const pkgPath = join(dir, 'package.json');
51
- try {
52
- readFileSync(pkgPath, 'utf-8');
53
- found.push(pkgPath);
54
- } catch {
55
- // no package.json here
50
+ private detectWorkspaces(): WorkspaceInfo[] {
51
+ const rootPkgPath = join(this.projectPath, 'package.json');
52
+ if (!existsSync(rootPkgPath)) return [];
53
+
54
+ try {
55
+ const rootPkg = JSON.parse(readFileSync(rootPkgPath, 'utf-8'));
56
+ let workspaceGlobs: string[] = [];
57
+
58
+ if (Array.isArray(rootPkg.workspaces)) {
59
+ workspaceGlobs = rootPkg.workspaces;
60
+ } else if (rootPkg.workspaces?.packages && Array.isArray(rootPkg.workspaces.packages)) {
61
+ workspaceGlobs = rootPkg.workspaces.packages;
62
+ }
63
+
64
+ if (workspaceGlobs.length === 0) return [];
65
+
66
+ const workspaces: WorkspaceInfo[] = [];
67
+
68
+ for (const pattern of workspaceGlobs) {
69
+ // Resolve glob patterns like "packages/*"
70
+ const dirs = globSync(pattern, {
71
+ cwd: this.projectPath,
72
+ absolute: true,
73
+ });
74
+
75
+ for (const dir of dirs) {
76
+ const pkgPath = join(dir, 'package.json');
77
+ if (!existsSync(pkgPath)) continue;
78
+
79
+ try {
80
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
81
+ workspaces.push({
82
+ name: pkg.name || relative(this.projectPath, dir),
83
+ path: dir,
84
+ relativePath: relative(this.projectPath, dir),
85
+ description: pkg.description || '',
86
+ version: pkg.version || '0.0.0',
87
+ dependencies: pkg.dependencies || {},
88
+ devDependencies: pkg.devDependencies || {},
89
+ bin: pkg.bin || undefined,
90
+ main: pkg.main || undefined,
91
+ });
92
+ } catch {
93
+ // Skip unparseable package.json
94
+ }
95
+ }
56
96
  }
57
- dir = join(dir, '..');
58
- depth++;
59
- }
60
97
 
61
- return found;
98
+ return workspaces;
99
+ } catch {
100
+ return [];
101
+ }
62
102
  }
63
103
 
64
104
  /**
@@ -81,7 +121,6 @@ export class ProjectScanner {
81
121
 
82
122
  private scanDirectory(): string[] {
83
123
  const ignorePatterns = this.config.ignore || [];
84
- const negatedPatterns = ignorePatterns.map((p) => `!**/${p}/**`);
85
124
 
86
125
  const files = globSync('**/*', {
87
126
  cwd: this.projectPath,
@@ -167,82 +206,89 @@ export class ProjectScanner {
167
206
  return root;
168
207
  }
169
208
 
170
- private detectFrameworks(files: string[]): Set<string> {
209
+ /**
210
+ * Detect frameworks ONLY from specified package.json files.
211
+ * Never reads package.json from node_modules.
212
+ * No string-matching fallback — only structured dependency key detection.
213
+ */
214
+ private detectFrameworks(packageJsonPaths: string[]): Set<string> {
171
215
  const frameworks = new Set<string>();
172
216
 
173
- for (const file of files) {
174
- if (file.endsWith('package.json')) {
175
- try {
176
- const content = readFileSync(file, 'utf-8');
177
- const parsed = JSON.parse(content);
178
- const allDeps = {
179
- ...parsed.dependencies,
180
- ...parsed.devDependencies,
181
- };
217
+ for (const file of packageJsonPaths) {
218
+ if (!file.endsWith('package.json')) continue;
182
219
 
183
- // Detect from actual dependency keys
184
- if (allDeps['@nestjs/core'] || allDeps['@nestjs/common']) frameworks.add('NestJS');
185
- if (allDeps['react'] || allDeps['react-dom']) frameworks.add('React');
186
- if (allDeps['@angular/core']) frameworks.add('Angular');
187
- if (allDeps['vue'] || allDeps['@vue/core']) frameworks.add('Vue.js');
188
- if (allDeps['express']) frameworks.add('Express.js');
189
- if (allDeps['next']) frameworks.add('Next.js');
190
- if (allDeps['fastify']) frameworks.add('Fastify');
191
- if (allDeps['typeorm']) frameworks.add('TypeORM');
192
- if (allDeps['prisma'] || allDeps['@prisma/client']) frameworks.add('Prisma');
193
- if (allDeps['sequelize']) frameworks.add('Sequelize');
194
- if (allDeps['mongoose']) frameworks.add('Mongoose');
195
- } catch {
196
- // Fallback: simple string matching
197
- try {
198
- const content = readFileSync(file, 'utf-8');
199
- if (content.includes('@nestjs')) frameworks.add('NestJS');
200
- if (content.includes('react')) frameworks.add('React');
201
- if (content.includes('angular')) frameworks.add('Angular');
202
- if (content.includes('vue')) frameworks.add('Vue.js');
203
- if (content.includes('express')) frameworks.add('Express.js');
204
- if (content.includes('next')) frameworks.add('Next.js');
205
- } catch {
206
- // skip
207
- }
208
- }
209
- }
220
+ // Safety: skip any path that includes node_modules
221
+ if (file.includes('node_modules')) continue;
210
222
 
211
- if (file.includes('pom.xml')) {
212
- try {
213
- const content = readFileSync(file, 'utf-8');
214
- if (content.includes('spring-boot')) frameworks.add('Spring Boot');
215
- if (content.includes('spring')) frameworks.add('Spring');
216
- } catch {
217
- // skip
218
- }
223
+ try {
224
+ const content = readFileSync(file, 'utf-8');
225
+ const parsed = JSON.parse(content);
226
+ const allDeps = {
227
+ ...parsed.dependencies,
228
+ ...parsed.devDependencies,
229
+ };
230
+
231
+ // Detect from actual dependency keys — no string fallback
232
+ if (allDeps['@nestjs/core'] || allDeps['@nestjs/common']) frameworks.add('NestJS');
233
+ if (allDeps['react'] || allDeps['react-dom']) frameworks.add('React');
234
+ if (allDeps['@angular/core']) frameworks.add('Angular');
235
+ if (allDeps['vue'] || allDeps['@vue/core']) frameworks.add('Vue.js');
236
+ if (allDeps['express']) frameworks.add('Express.js');
237
+ if (allDeps['next']) frameworks.add('Next.js');
238
+ if (allDeps['fastify']) frameworks.add('Fastify');
239
+ if (allDeps['typeorm']) frameworks.add('TypeORM');
240
+ if (allDeps['prisma'] || allDeps['@prisma/client']) frameworks.add('Prisma');
241
+ if (allDeps['sequelize']) frameworks.add('Sequelize');
242
+ if (allDeps['mongoose']) frameworks.add('Mongoose');
243
+ if (allDeps['@modelcontextprotocol/sdk']) frameworks.add('MCP SDK');
244
+ if (allDeps['probot']) frameworks.add('Probot');
245
+ if (allDeps['hono']) frameworks.add('Hono');
246
+ } catch {
247
+ // Skip unparseable files — NO fallback string matching
219
248
  }
249
+ }
220
250
 
221
- if (file.includes('requirements.txt')) {
222
- try {
223
- const content = readFileSync(file, 'utf-8');
224
- if (content.includes('django')) frameworks.add('Django');
225
- if (content.includes('flask')) frameworks.add('Flask');
226
- if (content.includes('fastapi')) frameworks.add('FastAPI');
227
- } catch {
228
- // skip
229
- }
251
+ // Check for pom.xml only at project root
252
+ const pomPath = join(this.projectPath, 'pom.xml');
253
+ if (existsSync(pomPath)) {
254
+ try {
255
+ const content = readFileSync(pomPath, 'utf-8');
256
+ if (content.includes('spring-boot')) frameworks.add('Spring Boot');
257
+ if (content.includes('spring') && !content.includes('spring-boot')) frameworks.add('Spring');
258
+ } catch {
259
+ // skip
230
260
  }
261
+ }
231
262
 
232
- if (file.includes('Gemfile')) {
233
- try {
234
- const content = readFileSync(file, 'utf-8');
235
- if (content.includes('rails')) frameworks.add('Ruby on Rails');
236
- } catch {
237
- // skip
238
- }
263
+ // Check for requirements.txt only at project root
264
+ const reqPath = join(this.projectPath, 'requirements.txt');
265
+ if (existsSync(reqPath)) {
266
+ try {
267
+ const content = readFileSync(reqPath, 'utf-8');
268
+ if (content.includes('django')) frameworks.add('Django');
269
+ if (content.includes('flask')) frameworks.add('Flask');
270
+ if (content.includes('fastapi')) frameworks.add('FastAPI');
271
+ } catch {
272
+ // skip
239
273
  }
274
+ }
240
275
 
241
- if (file.includes('go.mod')) {
242
- frameworks.add('Go');
276
+ // Check for Gemfile only at project root
277
+ const gemPath = join(this.projectPath, 'Gemfile');
278
+ if (existsSync(gemPath)) {
279
+ try {
280
+ const content = readFileSync(gemPath, 'utf-8');
281
+ if (content.includes('rails')) frameworks.add('Ruby on Rails');
282
+ } catch {
283
+ // skip
243
284
  }
244
285
  }
245
286
 
287
+ // Check for go.mod only at project root
288
+ if (existsSync(join(this.projectPath, 'go.mod'))) {
289
+ frameworks.add('Go');
290
+ }
291
+
246
292
  return frameworks;
247
293
  }
248
294
 
package/src/scorer.ts CHANGED
@@ -24,7 +24,7 @@ export class ArchitectureScorer {
24
24
  this.calculateModularity(edges, totalFiles);
25
25
  this.calculateCoupling(edges, totalFiles);
26
26
  this.calculateCohesion(edges);
27
- this.calculateLayering(antiPatterns);
27
+ this.calculateLayering(antiPatterns, totalFiles);
28
28
 
29
29
  const components = [
30
30
  {
@@ -213,7 +213,7 @@ export class ArchitectureScorer {
213
213
  return fromTopLevel === toTopLevel;
214
214
  }
215
215
 
216
- private calculateLayering(antiPatterns: AntiPattern[]): void {
216
+ private calculateLayering(antiPatterns: AntiPattern[], totalFiles?: number): void {
217
217
  const layeringViolations = antiPatterns.filter(
218
218
  (p) =>
219
219
  p.name === 'Leaky Abstraction' ||
@@ -223,16 +223,31 @@ export class ArchitectureScorer {
223
223
 
224
224
  if (layeringViolations === 0) {
225
225
  this.layering = 95;
226
- } else if (layeringViolations === 1) {
227
- this.layering = 85;
228
- } else if (layeringViolations === 2) {
229
- this.layering = 70;
230
- } else if (layeringViolations === 3) {
231
- this.layering = 55;
232
- } else if (layeringViolations <= 5) {
233
- this.layering = 40;
226
+ return;
227
+ }
228
+
229
+ // Use ratio-based scoring: violations per 100 files
230
+ // This makes scoring fair regardless of project size
231
+ const fileCount = Math.max(totalFiles || 50, 10);
232
+ const violationRatio = layeringViolations / fileCount;
233
+
234
+ if (violationRatio < 0.02) {
235
+ // < 2% — e.g. 1 violation in 50 files
236
+ this.layering = 90;
237
+ } else if (violationRatio < 0.05) {
238
+ // < 5% — e.g. 2-3 violations in 50 files
239
+ this.layering = 80;
240
+ } else if (violationRatio < 0.1) {
241
+ // < 10% — e.g. 5 violations in 50 files
242
+ this.layering = 65;
243
+ } else if (violationRatio < 0.2) {
244
+ // < 20% — significant issues
245
+ this.layering = 50;
246
+ } else if (violationRatio < 0.35) {
247
+ this.layering = 35;
234
248
  } else {
235
- this.layering = 25;
249
+ // > 35% — severe layering problems
250
+ this.layering = 20;
236
251
  }
237
252
  }
238
253
  }
package/src/types.ts CHANGED
@@ -52,6 +52,18 @@ export interface ArchitectureScore {
52
52
  };
53
53
  }
54
54
 
55
+ export interface WorkspaceInfo {
56
+ name: string;
57
+ path: string;
58
+ relativePath: string;
59
+ description: string;
60
+ version: string;
61
+ dependencies: Record<string, string>;
62
+ devDependencies: Record<string, string>;
63
+ bin?: Record<string, string>;
64
+ main?: string;
65
+ }
66
+
55
67
  export interface ProjectInfo {
56
68
  path: string;
57
69
  name: string;
@@ -60,6 +72,7 @@ export interface ProjectInfo {
60
72
  totalLines: number;
61
73
  primaryLanguages: string[];
62
74
  fileTree?: FileNode;
75
+ workspaces?: WorkspaceInfo[];
63
76
  }
64
77
 
65
78
  export interface AnalysisReport {
@@ -82,6 +95,16 @@ export interface AnalysisReport {
82
95
  mermaid: string;
83
96
  type: 'component' | 'layer' | 'dependency';
84
97
  };
98
+ projectSummary?: ProjectSummary;
99
+ }
100
+
101
+ export interface ProjectSummary {
102
+ description: string;
103
+ purpose: string;
104
+ modules: { name: string; files: number; description: string }[];
105
+ techStack: string[];
106
+ entryPoints: string[];
107
+ keywords: string[];
85
108
  }
86
109
 
87
110
  export interface ArchitectConfig {
@@ -104,6 +127,10 @@ export interface ArchitectConfig {
104
127
  cohesion?: number;
105
128
  layering?: number;
106
129
  };
130
+ monorepo?: {
131
+ enabled?: boolean;
132
+ treatPackagesAsModules?: boolean;
133
+ };
107
134
  }
108
135
 
109
136
  export interface ParsedImport {