claudeos-core 2.1.1 → 2.3.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 (62) hide show
  1. package/CHANGELOG.md +1649 -481
  2. package/CONTRIBUTING.md +92 -92
  3. package/README.de.md +64 -5
  4. package/README.es.md +64 -5
  5. package/README.fr.md +64 -5
  6. package/README.hi.md +64 -5
  7. package/README.ja.md +64 -5
  8. package/README.ko.md +1018 -959
  9. package/README.md +1020 -960
  10. package/README.ru.md +66 -5
  11. package/README.vi.md +1019 -960
  12. package/README.zh-CN.md +64 -5
  13. package/bin/cli.js +152 -148
  14. package/bin/commands/init.js +1673 -1518
  15. package/bin/commands/lint.js +62 -0
  16. package/bin/commands/memory.js +438 -438
  17. package/bin/lib/cli-utils.js +206 -206
  18. package/claude-md-validator/index.js +184 -0
  19. package/claude-md-validator/reporter.js +66 -0
  20. package/claude-md-validator/structural-checks.js +528 -0
  21. package/content-validator/index.js +666 -436
  22. package/lib/env-parser.js +317 -0
  23. package/lib/expected-guides.js +23 -23
  24. package/lib/expected-outputs.js +90 -90
  25. package/lib/language-config.js +35 -35
  26. package/lib/memory-scaffold.js +1058 -1052
  27. package/lib/plan-parser.js +165 -165
  28. package/lib/staged-rules.js +118 -118
  29. package/manifest-generator/index.js +174 -174
  30. package/package.json +90 -87
  31. package/pass-json-validator/index.js +337 -337
  32. package/pass-prompts/templates/angular/pass3.md +28 -13
  33. package/pass-prompts/templates/common/claude-md-scaffold.md +686 -0
  34. package/pass-prompts/templates/common/pass3-footer.md +402 -39
  35. package/pass-prompts/templates/common/pass3b-core-header.md +43 -0
  36. package/pass-prompts/templates/common/pass4.md +375 -302
  37. package/pass-prompts/templates/common/staging-override.md +26 -26
  38. package/pass-prompts/templates/java-spring/pass3.md +31 -21
  39. package/pass-prompts/templates/kotlin-spring/pass3.md +34 -22
  40. package/pass-prompts/templates/node-express/pass3.md +30 -21
  41. package/pass-prompts/templates/node-fastify/pass3.md +28 -14
  42. package/pass-prompts/templates/node-nestjs/pass3.md +29 -14
  43. package/pass-prompts/templates/node-nextjs/pass3.md +34 -21
  44. package/pass-prompts/templates/node-vite/pass1.md +117 -117
  45. package/pass-prompts/templates/node-vite/pass2.md +78 -78
  46. package/pass-prompts/templates/node-vite/pass3.md +30 -13
  47. package/pass-prompts/templates/python-django/pass3.md +32 -21
  48. package/pass-prompts/templates/python-fastapi/pass3.md +33 -21
  49. package/pass-prompts/templates/python-flask/pass1.md +119 -119
  50. package/pass-prompts/templates/python-flask/pass2.md +85 -85
  51. package/pass-prompts/templates/python-flask/pass3.md +31 -13
  52. package/pass-prompts/templates/vue-nuxt/pass3.md +32 -13
  53. package/plan-installer/domain-grouper.js +76 -76
  54. package/plan-installer/index.js +137 -129
  55. package/plan-installer/prompt-generator.js +188 -128
  56. package/plan-installer/scanners/scan-frontend.js +505 -473
  57. package/plan-installer/scanners/scan-java.js +226 -226
  58. package/plan-installer/scanners/scan-node.js +57 -57
  59. package/plan-installer/scanners/scan-python.js +85 -85
  60. package/plan-installer/stack-detector.js +482 -466
  61. package/plan-installer/structure-scanner.js +65 -65
  62. package/sync-checker/index.js +177 -177
@@ -1,226 +1,226 @@
1
- /**
2
- * ClaudeOS-Core — Java Structure Scanner
3
- *
4
- * Scans Java project directory structure to discover backend domains.
5
- * Supports 5 patterns:
6
- * A: controller/{domain}/*.java (layer-first)
7
- * B: {domain}/controller/*.java (domain-first)
8
- * C: controller/DomainController.java (flat, extract from class name)
9
- * D: {module}/{domain}/controller/ (module/domain — auto-upgrade from B on conflict)
10
- * E: {domain}/adapter/in/web/*.java (DDD/Hexagonal)
11
- * Also includes supplementary service-only scan (all patterns) and full fallback.
12
- */
13
-
14
- const path = require("path");
15
- const { glob } = require("glob");
16
-
17
- // Normalize backslash paths from glob on Windows to forward slashes
18
- const norm = (p) => p.replace(/\\/g, "/");
19
-
20
- async function scanJavaDomains(stack, ROOT) {
21
- const backendDomains = [];
22
- let rootPackage = null;
23
-
24
- const javaFiles = (await glob("src/main/java/**/*.java", { cwd: ROOT })).map(norm);
25
- for (const f of javaFiles) {
26
- const m = f.match(/src\/main\/java\/(.+?)\/(controller|aggregator|facade|usecase|orchestrator|service|mapper|dao|dto|entity|repository|adapter)/);
27
- if (m) { rootPackage = m[1].replace(/\//g, "."); break; }
28
- }
29
- const domainMap = {};
30
- let detectedPattern = null;
31
-
32
- // Pattern A: controller/{domain}/*.java (layer-first — domain under controller)
33
- const controllersA = (await glob("src/main/java/**/controller/*/*.java", { cwd: ROOT })).map(norm);
34
- for (const f of controllersA) {
35
- const m = f.match(/controller\/([^/]+)\//);
36
- if (m) {
37
- const d = m[1];
38
- if (!domainMap[d]) domainMap[d] = { controllers: 0, services: 0, mappers: 0, dtos: 0, xmlMappers: 0, pattern: "A" };
39
- domainMap[d].controllers++;
40
- }
41
- }
42
- if (Object.keys(domainMap).length > 0) detectedPattern = "A";
43
-
44
- // Pattern B/D: {domain}/controller/*.java (domain-first — controller under domain)
45
- // D extends B: {module}/{domain}/controller/ — auto-upgrade to module/domain on name conflict
46
- if (!detectedPattern) {
47
- const controllersB = (await glob("src/main/java/**/*/controller/*.java", { cwd: ROOT })).map(norm);
48
- const domainPaths = {};
49
- for (const f of controllersB) {
50
- const m = f.match(/\/([^/]+)\/controller\/[^/]+\.java$/);
51
- if (m) {
52
- const d = m[1];
53
- const parentMatch = f.match(/\/([^/]+)\/([^/]+)\/controller\//);
54
- const parentModule = parentMatch ? parentMatch[1] : null;
55
- if (!domainPaths[d]) domainPaths[d] = [];
56
- domainPaths[d].push({ file: f, module: parentModule });
57
- }
58
- }
59
-
60
- // If same domain name found in multiple modules, use module/domain form (Pattern D)
61
- for (const [d, entries] of Object.entries(domainPaths)) {
62
- const modules = [...new Set(entries.map(e => e.module).filter(Boolean))];
63
- if (modules.length > 1) {
64
- // Pattern D: conflict — register as module/domain
65
- for (const entry of entries) {
66
- const fullName = entry.module ? `${entry.module}/${d}` : d;
67
- if (!domainMap[fullName]) domainMap[fullName] = { controllers: 0, services: 0, mappers: 0, dtos: 0, xmlMappers: 0, pattern: "D", modulePath: entry.module, domainName: d };
68
- domainMap[fullName].controllers++;
69
- }
70
- } else {
71
- if (!domainMap[d]) domainMap[d] = { controllers: 0, services: 0, mappers: 0, dtos: 0, xmlMappers: 0, pattern: "B" };
72
- domainMap[d].controllers += entries.length;
73
- }
74
- }
75
- if (Object.keys(domainMap).length > 0) {
76
- // Determine pattern by majority vote (B vs D)
77
- const patternCounts = {};
78
- for (const v of Object.values(domainMap)) patternCounts[v.pattern] = (patternCounts[v.pattern] || 0) + 1;
79
- detectedPattern = Object.entries(patternCounts).sort((a, b) => b[1] - a[1])[0][0];
80
- }
81
- }
82
-
83
- // Pattern E: DDD/Hexagonal — {domain}/adapter/in/web/*.java or {domain}/adapter/in/rest/*.java
84
- if (!detectedPattern) {
85
- const controllersE = (await glob("src/main/java/**/adapter/in/{web,rest}/*.java", { cwd: ROOT })).map(norm);
86
- for (const f of controllersE) {
87
- const m = f.match(/\/([^/]+)\/adapter\/in\/(web|rest)\/[^/]+\.java$/);
88
- if (m) {
89
- const d = m[1];
90
- if (!domainMap[d]) domainMap[d] = { controllers: 0, services: 0, mappers: 0, dtos: 0, xmlMappers: 0, pattern: "E" };
91
- domainMap[d].controllers++;
92
- }
93
- }
94
- if (Object.keys(domainMap).length > 0) detectedPattern = "E";
95
- }
96
-
97
- // Pattern C: Flat structure — controller/*.java (no domain directory, extract domain from class name)
98
- if (!detectedPattern) {
99
- const controllersC = (await glob("src/main/java/**/controller/*.java", { cwd: ROOT })).map(norm);
100
- for (const f of controllersC) {
101
- const m = f.match(/\/([A-Z][a-zA-Z]*)Controller\.java$/);
102
- if (m) {
103
- const d = m[1].toLowerCase();
104
- if (!domainMap[d]) domainMap[d] = { controllers: 0, services: 0, mappers: 0, dtos: 0, xmlMappers: 0, pattern: "C" };
105
- domainMap[d].controllers++;
106
- }
107
- }
108
- if (Object.keys(domainMap).length > 0) detectedPattern = "C";
109
- }
110
-
111
- // ── Supplementary scan: detect domains without controllers (service/dao/aggregator/facade/usecase only) ──
112
- // Runs for ALL detected patterns (A/B/C/D/E) to catch core-only domains
113
- {
114
- const serviceDirs = (await glob("src/main/java/**/*/service/*.java", { cwd: ROOT })).map(norm);
115
- const mapperDirs = (await glob("src/main/java/**/*/{mapper,repository,dao}/*.java", { cwd: ROOT })).map(norm);
116
- const orchestrationDirs = (await glob("src/main/java/**/*/{aggregator,facade,usecase,orchestrator}/*.java", { cwd: ROOT })).map(norm);
117
- const allServiceFiles = [...serviceDirs, ...mapperDirs, ...orchestrationDirs];
118
- const skipDomains = ["common", "config", "util", "utils", "base", "core", "shared", "global", "framework", "infra", "front", "admin", "back", "internal", "external", "web", "app", "test", "tests", "main", "generated", "build"];
119
- for (const f of allServiceFiles) {
120
- const m = f.match(/\/([^/]+)\/(service|mapper|repository|dao|aggregator|facade|usecase|orchestrator)\/[^/]+\.java$/);
121
- if (m) {
122
- const d = m[1];
123
- if (!domainMap[d] && !skipDomains.includes(d) && !/^v\d+$/.test(d)) {
124
- domainMap[d] = { controllers: 0, services: 0, mappers: 0, dtos: 0, xmlMappers: 0, pattern: detectedPattern || "B" };
125
- }
126
- }
127
- }
128
- }
129
-
130
- // Scan service/mapper/dao/aggregator/facade/usecase/dto/xml files for each domain
131
- for (const d of Object.keys(domainMap)) {
132
- const p = domainMap[d].pattern;
133
- const dn = domainMap[d].domainName || d;
134
- let svcGlob, mprGlob, dtoGlob, aggGlob;
135
-
136
- if (p === "A") {
137
- svcGlob = `src/main/java/**/service/${d}/*.java`;
138
- mprGlob = `src/main/java/**/{mapper,repository,dao}/${d}/*.java`;
139
- dtoGlob = `src/main/java/**/dto/${d}/**/*.java`;
140
- aggGlob = `src/main/java/**/{aggregator,facade,usecase,orchestrator}/${d}/*.java`;
141
- } else if (p === "B" || p === "D") {
142
- svcGlob = `src/main/java/**/${dn}/service/*.java`;
143
- mprGlob = `src/main/java/**/${dn}/{mapper,repository,dao}/*.java`;
144
- dtoGlob = `src/main/java/**/${dn}/dto/**/*.java`;
145
- aggGlob = `src/main/java/**/${dn}/{aggregator,facade,usecase,orchestrator}/*.java`;
146
- } else if (p === "E") {
147
- svcGlob = `src/main/java/**/${d}/{application,domain}/**/*.java`;
148
- mprGlob = `src/main/java/**/${d}/{adapter/out/{persistence,repository},infrastructure}/*.java`;
149
- dtoGlob = `src/main/java/**/${d}/**/{dto,command,query}/**/*.java`;
150
- aggGlob = null; // DDD/Hexagonal typically doesn't use aggregator layer
151
- } else {
152
- // Pattern C: Flat — match domain name from file name
153
- const cap = d.charAt(0).toUpperCase() + d.slice(1);
154
- svcGlob = `src/main/java/**/service/${cap}*.java`;
155
- mprGlob = `src/main/java/**/{mapper,repository,dao}/${cap}*.java`;
156
- dtoGlob = `src/main/java/**/dto/${cap}*.java`;
157
- aggGlob = `src/main/java/**/{aggregator,facade,usecase,orchestrator}/${cap}*.java`;
158
- }
159
- // Pattern C (flat): XML may be in flat directory without domain subdirectory (e.g., mapper/OrderMapper.xml)
160
- // Other patterns: XML is in domain subdirectory (e.g., mapper/order/OrderMapper.xml)
161
- const capDn = dn.charAt(0).toUpperCase() + dn.slice(1);
162
- const xmlGlob = p === "C"
163
- ? `src/main/resources/{mapper,mybatis}/**/{${dn}/${capDn}*.xml,${capDn}*.xml}`
164
- : `src/main/resources/{mapper,mybatis}/**/${dn}/*.xml`;
165
-
166
- const svc = await glob(svcGlob, { cwd: ROOT });
167
- const mpr = await glob(mprGlob, { cwd: ROOT });
168
- const dto = await glob(dtoGlob, { cwd: ROOT });
169
- const xml = await glob(xmlGlob, { cwd: ROOT });
170
- const agg = aggGlob ? await glob(aggGlob, { cwd: ROOT }) : [];
171
- domainMap[d].services = svc.length + agg.length;
172
- domainMap[d].mappers = mpr.length;
173
- domainMap[d].dtos = dto.length;
174
- domainMap[d].xmlMappers = xml.length;
175
- const totalFiles = svc.length + agg.length + mpr.length + dto.length + xml.length + domainMap[d].controllers;
176
- backendDomains.push({ name: d, type: "backend", ...domainMap[d], totalFiles });
177
- }
178
-
179
- // ── Java fallback: extract domains directly from all .java files when glob returns 0 ──
180
- if (backendDomains.length === 0) {
181
- const allJava = (await glob("**/*.java", { cwd: ROOT, ignore: ["**/node_modules/**", "**/build/**", "**/target/**", "**/test/**", "**/generated/**"] })).map(norm);
182
- const javaDomains = {};
183
- const skipNames = ["common", "config", "util", "utils", "base", "shared", "global", "framework", "infra", "api", "main", "front", "admin", "back", "internal", "external", "web", "app", "test", "tests", "generated", "build"];
184
- const versionPattern = /^v\d+$/;
185
- const layerNames = ["controller", "aggregator", "facade", "usecase", "orchestrator", "service", "mapper", "repository", "dao", "dto", "vo", "entity", "adapter"];
186
-
187
- for (const f of allJava) {
188
- const parts = f.replace(/\\/g, "/").split("/");
189
- for (let i = 0; i < parts.length - 1; i++) {
190
- if (layerNames.includes(parts[i])) {
191
- const prevDir = parts[i - 1];
192
- const nextDir = parts[i + 1];
193
-
194
- // {domain}/layer/ pattern (domain before layer)
195
- if (i > 0 && !skipNames.includes(prevDir) && !layerNames.includes(prevDir) && !prevDir.includes(".") && !versionPattern.test(prevDir)) {
196
- if (!javaDomains[prevDir]) javaDomains[prevDir] = { controllers: 0, services: 0, mappers: 0, dtos: 0, xmlMappers: 0, pattern: "B" };
197
- if (parts[i] === "controller") javaDomains[prevDir].controllers++;
198
- else if (["aggregator", "facade", "usecase", "orchestrator", "service"].includes(parts[i])) javaDomains[prevDir].services++;
199
- else if (["mapper", "repository", "dao"].includes(parts[i])) javaDomains[prevDir].mappers++;
200
- else if (["dto", "vo"].includes(parts[i])) javaDomains[prevDir].dtos++;
201
- }
202
- // layer/{domain}/ pattern (layer before domain)
203
- if (nextDir && !nextDir.endsWith(".java") && !skipNames.includes(nextDir) && !layerNames.includes(nextDir) && !versionPattern.test(nextDir)) {
204
- if (!javaDomains[nextDir]) javaDomains[nextDir] = { controllers: 0, services: 0, mappers: 0, dtos: 0, xmlMappers: 0, pattern: "A" };
205
- if (parts[i] === "controller") javaDomains[nextDir].controllers++;
206
- else if (["aggregator", "facade", "usecase", "orchestrator", "service"].includes(parts[i])) javaDomains[nextDir].services++;
207
- else if (["mapper", "repository", "dao"].includes(parts[i])) javaDomains[nextDir].mappers++;
208
- else if (["dto", "vo"].includes(parts[i])) javaDomains[nextDir].dtos++;
209
- }
210
- break;
211
- }
212
- }
213
- }
214
-
215
- for (const [d, data] of Object.entries(javaDomains)) {
216
- const total = data.controllers + data.services + data.mappers + data.dtos;
217
- if (total > 0) {
218
- backendDomains.push({ name: d, type: "backend", ...data, totalFiles: total });
219
- }
220
- }
221
- }
222
-
223
- return { backendDomains, rootPackage };
224
- }
225
-
226
- module.exports = { scanJavaDomains };
1
+ /**
2
+ * ClaudeOS-Core — Java Structure Scanner
3
+ *
4
+ * Scans Java project directory structure to discover backend domains.
5
+ * Supports 5 patterns:
6
+ * A: controller/{domain}/*.java (layer-first)
7
+ * B: {domain}/controller/*.java (domain-first)
8
+ * C: controller/DomainController.java (flat, extract from class name)
9
+ * D: {module}/{domain}/controller/ (module/domain — auto-upgrade from B on conflict)
10
+ * E: {domain}/adapter/in/web/*.java (DDD/Hexagonal)
11
+ * Also includes supplementary service-only scan (all patterns) and full fallback.
12
+ */
13
+
14
+ const path = require("path");
15
+ const { glob } = require("glob");
16
+
17
+ // Normalize backslash paths from glob on Windows to forward slashes
18
+ const norm = (p) => p.replace(/\\/g, "/");
19
+
20
+ async function scanJavaDomains(stack, ROOT) {
21
+ const backendDomains = [];
22
+ let rootPackage = null;
23
+
24
+ const javaFiles = (await glob("src/main/java/**/*.java", { cwd: ROOT })).map(norm);
25
+ for (const f of javaFiles) {
26
+ const m = f.match(/src\/main\/java\/(.+?)\/(controller|aggregator|facade|usecase|orchestrator|service|mapper|dao|dto|entity|repository|adapter)/);
27
+ if (m) { rootPackage = m[1].replace(/\//g, "."); break; }
28
+ }
29
+ const domainMap = {};
30
+ let detectedPattern = null;
31
+
32
+ // Pattern A: controller/{domain}/*.java (layer-first — domain under controller)
33
+ const controllersA = (await glob("src/main/java/**/controller/*/*.java", { cwd: ROOT })).map(norm);
34
+ for (const f of controllersA) {
35
+ const m = f.match(/controller\/([^/]+)\//);
36
+ if (m) {
37
+ const d = m[1];
38
+ if (!domainMap[d]) domainMap[d] = { controllers: 0, services: 0, mappers: 0, dtos: 0, xmlMappers: 0, pattern: "A" };
39
+ domainMap[d].controllers++;
40
+ }
41
+ }
42
+ if (Object.keys(domainMap).length > 0) detectedPattern = "A";
43
+
44
+ // Pattern B/D: {domain}/controller/*.java (domain-first — controller under domain)
45
+ // D extends B: {module}/{domain}/controller/ — auto-upgrade to module/domain on name conflict
46
+ if (!detectedPattern) {
47
+ const controllersB = (await glob("src/main/java/**/*/controller/*.java", { cwd: ROOT })).map(norm);
48
+ const domainPaths = {};
49
+ for (const f of controllersB) {
50
+ const m = f.match(/\/([^/]+)\/controller\/[^/]+\.java$/);
51
+ if (m) {
52
+ const d = m[1];
53
+ const parentMatch = f.match(/\/([^/]+)\/([^/]+)\/controller\//);
54
+ const parentModule = parentMatch ? parentMatch[1] : null;
55
+ if (!domainPaths[d]) domainPaths[d] = [];
56
+ domainPaths[d].push({ file: f, module: parentModule });
57
+ }
58
+ }
59
+
60
+ // If same domain name found in multiple modules, use module/domain form (Pattern D)
61
+ for (const [d, entries] of Object.entries(domainPaths)) {
62
+ const modules = [...new Set(entries.map(e => e.module).filter(Boolean))];
63
+ if (modules.length > 1) {
64
+ // Pattern D: conflict — register as module/domain
65
+ for (const entry of entries) {
66
+ const fullName = entry.module ? `${entry.module}/${d}` : d;
67
+ if (!domainMap[fullName]) domainMap[fullName] = { controllers: 0, services: 0, mappers: 0, dtos: 0, xmlMappers: 0, pattern: "D", modulePath: entry.module, domainName: d };
68
+ domainMap[fullName].controllers++;
69
+ }
70
+ } else {
71
+ if (!domainMap[d]) domainMap[d] = { controllers: 0, services: 0, mappers: 0, dtos: 0, xmlMappers: 0, pattern: "B" };
72
+ domainMap[d].controllers += entries.length;
73
+ }
74
+ }
75
+ if (Object.keys(domainMap).length > 0) {
76
+ // Determine pattern by majority vote (B vs D)
77
+ const patternCounts = {};
78
+ for (const v of Object.values(domainMap)) patternCounts[v.pattern] = (patternCounts[v.pattern] || 0) + 1;
79
+ detectedPattern = Object.entries(patternCounts).sort((a, b) => b[1] - a[1])[0][0];
80
+ }
81
+ }
82
+
83
+ // Pattern E: DDD/Hexagonal — {domain}/adapter/in/web/*.java or {domain}/adapter/in/rest/*.java
84
+ if (!detectedPattern) {
85
+ const controllersE = (await glob("src/main/java/**/adapter/in/{web,rest}/*.java", { cwd: ROOT })).map(norm);
86
+ for (const f of controllersE) {
87
+ const m = f.match(/\/([^/]+)\/adapter\/in\/(web|rest)\/[^/]+\.java$/);
88
+ if (m) {
89
+ const d = m[1];
90
+ if (!domainMap[d]) domainMap[d] = { controllers: 0, services: 0, mappers: 0, dtos: 0, xmlMappers: 0, pattern: "E" };
91
+ domainMap[d].controllers++;
92
+ }
93
+ }
94
+ if (Object.keys(domainMap).length > 0) detectedPattern = "E";
95
+ }
96
+
97
+ // Pattern C: Flat structure — controller/*.java (no domain directory, extract domain from class name)
98
+ if (!detectedPattern) {
99
+ const controllersC = (await glob("src/main/java/**/controller/*.java", { cwd: ROOT })).map(norm);
100
+ for (const f of controllersC) {
101
+ const m = f.match(/\/([A-Z][a-zA-Z]*)Controller\.java$/);
102
+ if (m) {
103
+ const d = m[1].toLowerCase();
104
+ if (!domainMap[d]) domainMap[d] = { controllers: 0, services: 0, mappers: 0, dtos: 0, xmlMappers: 0, pattern: "C" };
105
+ domainMap[d].controllers++;
106
+ }
107
+ }
108
+ if (Object.keys(domainMap).length > 0) detectedPattern = "C";
109
+ }
110
+
111
+ // ── Supplementary scan: detect domains without controllers (service/dao/aggregator/facade/usecase only) ──
112
+ // Runs for ALL detected patterns (A/B/C/D/E) to catch core-only domains
113
+ {
114
+ const serviceDirs = (await glob("src/main/java/**/*/service/*.java", { cwd: ROOT })).map(norm);
115
+ const mapperDirs = (await glob("src/main/java/**/*/{mapper,repository,dao}/*.java", { cwd: ROOT })).map(norm);
116
+ const orchestrationDirs = (await glob("src/main/java/**/*/{aggregator,facade,usecase,orchestrator}/*.java", { cwd: ROOT })).map(norm);
117
+ const allServiceFiles = [...serviceDirs, ...mapperDirs, ...orchestrationDirs];
118
+ const skipDomains = ["common", "config", "util", "utils", "base", "core", "shared", "global", "framework", "infra", "front", "admin", "back", "internal", "external", "web", "app", "test", "tests", "main", "generated", "build"];
119
+ for (const f of allServiceFiles) {
120
+ const m = f.match(/\/([^/]+)\/(service|mapper|repository|dao|aggregator|facade|usecase|orchestrator)\/[^/]+\.java$/);
121
+ if (m) {
122
+ const d = m[1];
123
+ if (!domainMap[d] && !skipDomains.includes(d) && !/^v\d+$/.test(d)) {
124
+ domainMap[d] = { controllers: 0, services: 0, mappers: 0, dtos: 0, xmlMappers: 0, pattern: detectedPattern || "B" };
125
+ }
126
+ }
127
+ }
128
+ }
129
+
130
+ // Scan service/mapper/dao/aggregator/facade/usecase/dto/xml files for each domain
131
+ for (const d of Object.keys(domainMap)) {
132
+ const p = domainMap[d].pattern;
133
+ const dn = domainMap[d].domainName || d;
134
+ let svcGlob, mprGlob, dtoGlob, aggGlob;
135
+
136
+ if (p === "A") {
137
+ svcGlob = `src/main/java/**/service/${d}/*.java`;
138
+ mprGlob = `src/main/java/**/{mapper,repository,dao}/${d}/*.java`;
139
+ dtoGlob = `src/main/java/**/dto/${d}/**/*.java`;
140
+ aggGlob = `src/main/java/**/{aggregator,facade,usecase,orchestrator}/${d}/*.java`;
141
+ } else if (p === "B" || p === "D") {
142
+ svcGlob = `src/main/java/**/${dn}/service/*.java`;
143
+ mprGlob = `src/main/java/**/${dn}/{mapper,repository,dao}/*.java`;
144
+ dtoGlob = `src/main/java/**/${dn}/dto/**/*.java`;
145
+ aggGlob = `src/main/java/**/${dn}/{aggregator,facade,usecase,orchestrator}/*.java`;
146
+ } else if (p === "E") {
147
+ svcGlob = `src/main/java/**/${d}/{application,domain}/**/*.java`;
148
+ mprGlob = `src/main/java/**/${d}/{adapter/out/{persistence,repository},infrastructure}/*.java`;
149
+ dtoGlob = `src/main/java/**/${d}/**/{dto,command,query}/**/*.java`;
150
+ aggGlob = null; // DDD/Hexagonal typically doesn't use aggregator layer
151
+ } else {
152
+ // Pattern C: Flat — match domain name from file name
153
+ const cap = d.charAt(0).toUpperCase() + d.slice(1);
154
+ svcGlob = `src/main/java/**/service/${cap}*.java`;
155
+ mprGlob = `src/main/java/**/{mapper,repository,dao}/${cap}*.java`;
156
+ dtoGlob = `src/main/java/**/dto/${cap}*.java`;
157
+ aggGlob = `src/main/java/**/{aggregator,facade,usecase,orchestrator}/${cap}*.java`;
158
+ }
159
+ // Pattern C (flat): XML may be in flat directory without domain subdirectory (e.g., mapper/OrderMapper.xml)
160
+ // Other patterns: XML is in domain subdirectory (e.g., mapper/order/OrderMapper.xml)
161
+ const capDn = dn.charAt(0).toUpperCase() + dn.slice(1);
162
+ const xmlGlob = p === "C"
163
+ ? `src/main/resources/{mapper,mybatis}/**/{${dn}/${capDn}*.xml,${capDn}*.xml}`
164
+ : `src/main/resources/{mapper,mybatis}/**/${dn}/*.xml`;
165
+
166
+ const svc = await glob(svcGlob, { cwd: ROOT });
167
+ const mpr = await glob(mprGlob, { cwd: ROOT });
168
+ const dto = await glob(dtoGlob, { cwd: ROOT });
169
+ const xml = await glob(xmlGlob, { cwd: ROOT });
170
+ const agg = aggGlob ? await glob(aggGlob, { cwd: ROOT }) : [];
171
+ domainMap[d].services = svc.length + agg.length;
172
+ domainMap[d].mappers = mpr.length;
173
+ domainMap[d].dtos = dto.length;
174
+ domainMap[d].xmlMappers = xml.length;
175
+ const totalFiles = svc.length + agg.length + mpr.length + dto.length + xml.length + domainMap[d].controllers;
176
+ backendDomains.push({ name: d, type: "backend", ...domainMap[d], totalFiles });
177
+ }
178
+
179
+ // ── Java fallback: extract domains directly from all .java files when glob returns 0 ──
180
+ if (backendDomains.length === 0) {
181
+ const allJava = (await glob("**/*.java", { cwd: ROOT, ignore: ["**/node_modules/**", "**/build/**", "**/target/**", "**/test/**", "**/generated/**"] })).map(norm);
182
+ const javaDomains = {};
183
+ const skipNames = ["common", "config", "util", "utils", "base", "shared", "global", "framework", "infra", "api", "main", "front", "admin", "back", "internal", "external", "web", "app", "test", "tests", "generated", "build"];
184
+ const versionPattern = /^v\d+$/;
185
+ const layerNames = ["controller", "aggregator", "facade", "usecase", "orchestrator", "service", "mapper", "repository", "dao", "dto", "vo", "entity", "adapter"];
186
+
187
+ for (const f of allJava) {
188
+ const parts = f.replace(/\\/g, "/").split("/");
189
+ for (let i = 0; i < parts.length - 1; i++) {
190
+ if (layerNames.includes(parts[i])) {
191
+ const prevDir = parts[i - 1];
192
+ const nextDir = parts[i + 1];
193
+
194
+ // {domain}/layer/ pattern (domain before layer)
195
+ if (i > 0 && !skipNames.includes(prevDir) && !layerNames.includes(prevDir) && !prevDir.includes(".") && !versionPattern.test(prevDir)) {
196
+ if (!javaDomains[prevDir]) javaDomains[prevDir] = { controllers: 0, services: 0, mappers: 0, dtos: 0, xmlMappers: 0, pattern: "B" };
197
+ if (parts[i] === "controller") javaDomains[prevDir].controllers++;
198
+ else if (["aggregator", "facade", "usecase", "orchestrator", "service"].includes(parts[i])) javaDomains[prevDir].services++;
199
+ else if (["mapper", "repository", "dao"].includes(parts[i])) javaDomains[prevDir].mappers++;
200
+ else if (["dto", "vo"].includes(parts[i])) javaDomains[prevDir].dtos++;
201
+ }
202
+ // layer/{domain}/ pattern (layer before domain)
203
+ if (nextDir && !nextDir.endsWith(".java") && !skipNames.includes(nextDir) && !layerNames.includes(nextDir) && !versionPattern.test(nextDir)) {
204
+ if (!javaDomains[nextDir]) javaDomains[nextDir] = { controllers: 0, services: 0, mappers: 0, dtos: 0, xmlMappers: 0, pattern: "A" };
205
+ if (parts[i] === "controller") javaDomains[nextDir].controllers++;
206
+ else if (["aggregator", "facade", "usecase", "orchestrator", "service"].includes(parts[i])) javaDomains[nextDir].services++;
207
+ else if (["mapper", "repository", "dao"].includes(parts[i])) javaDomains[nextDir].mappers++;
208
+ else if (["dto", "vo"].includes(parts[i])) javaDomains[nextDir].dtos++;
209
+ }
210
+ break;
211
+ }
212
+ }
213
+ }
214
+
215
+ for (const [d, data] of Object.entries(javaDomains)) {
216
+ const total = data.controllers + data.services + data.mappers + data.dtos;
217
+ if (total > 0) {
218
+ backendDomains.push({ name: d, type: "backend", ...data, totalFiles: total });
219
+ }
220
+ }
221
+ }
222
+
223
+ return { backendDomains, rootPackage };
224
+ }
225
+
226
+ module.exports = { scanJavaDomains };
@@ -1,57 +1,57 @@
1
- /**
2
- * ClaudeOS-Core — Node.js Structure Scanner
3
- *
4
- * Scans Node.js backend (Express/NestJS/Fastify) project structure to discover domains.
5
- * Supports monorepo layouts (apps/*, packages/*) in addition to single-project src/.
6
- */
7
-
8
- const path = require("path");
9
- const { glob } = require("glob");
10
-
11
- async function scanNodeDomains(stack, ROOT) {
12
- const backendDomains = [];
13
- const skipDirs = ["common", "shared", "config", "utils", "lib", "core", "main", "interfaces", "types", "constants", "guards", "decorators", "pipes", "filters", "interceptors"];
14
-
15
- // Collect candidate directories: standard src/ + monorepo apps/*/src/
16
- const nestModules = await glob("src/modules/*/", { cwd: ROOT });
17
- let srcDirs = nestModules.length > 0 ? nestModules : await glob("src/*/", { cwd: ROOT });
18
-
19
- // Monorepo: scan apps/*/src/ and packages/*/src/ when standard src/ yields nothing backend-relevant
20
- if (stack.monorepo || srcDirs.length === 0) {
21
- const monoModules = await glob("{apps,packages}/*/src/modules/*/", { cwd: ROOT, ignore: ["**/node_modules/**"] });
22
- if (monoModules.length > 0) {
23
- srcDirs = [...srcDirs, ...monoModules];
24
- } else {
25
- const monoDirs = await glob("{apps,packages}/*/src/*/", { cwd: ROOT, ignore: ["**/node_modules/**"] });
26
- srcDirs = [...srcDirs, ...monoDirs];
27
- }
28
- }
29
-
30
- for (let dir of srcDirs) {
31
- if (!dir.endsWith("/")) dir += "/";
32
- const name = path.basename(dir.replace(/\/$/, ""));
33
- if (skipDirs.includes(name)) continue;
34
- const files = await glob(`${dir.replace(/\\/g, "/")}**/*.{ts,js}`, { cwd: ROOT, ignore: ["**/*.spec.*", "**/*.test.*"] });
35
- if (files.length > 0) {
36
- const controllers = files.filter(f => /controller|router|route|handler/.test(f)).length;
37
- const services = files.filter(f => /service/.test(f)).length;
38
- const dtos = files.filter(f => /dto|schema|type/.test(f)).length;
39
- const entities = files.filter(f => /entity|model/.test(f) && !/controller|service|dto/.test(f)).length;
40
- const modules = files.filter(f => /\.module\./.test(f)).length;
41
- const guards = files.filter(f => /guard/.test(f)).length;
42
- const pipes = files.filter(f => /pipe/.test(f)).length;
43
- const interceptors = files.filter(f => /interceptor/.test(f)).length;
44
- const domain = { name, type: "backend", controllers, services, dtos, totalFiles: files.length };
45
- if (entities > 0) domain.entities = entities;
46
- if (modules > 0) domain.modules = modules;
47
- if (guards > 0) domain.guards = guards;
48
- if (pipes > 0) domain.pipes = pipes;
49
- if (interceptors > 0) domain.interceptors = interceptors;
50
- backendDomains.push(domain);
51
- }
52
- }
53
-
54
- return { backendDomains };
55
- }
56
-
57
- module.exports = { scanNodeDomains };
1
+ /**
2
+ * ClaudeOS-Core — Node.js Structure Scanner
3
+ *
4
+ * Scans Node.js backend (Express/NestJS/Fastify) project structure to discover domains.
5
+ * Supports monorepo layouts (apps/*, packages/*) in addition to single-project src/.
6
+ */
7
+
8
+ const path = require("path");
9
+ const { glob } = require("glob");
10
+
11
+ async function scanNodeDomains(stack, ROOT) {
12
+ const backendDomains = [];
13
+ const skipDirs = ["common", "shared", "config", "utils", "lib", "core", "main", "interfaces", "types", "constants", "guards", "decorators", "pipes", "filters", "interceptors"];
14
+
15
+ // Collect candidate directories: standard src/ + monorepo apps/*/src/
16
+ const nestModules = await glob("src/modules/*/", { cwd: ROOT });
17
+ let srcDirs = nestModules.length > 0 ? nestModules : await glob("src/*/", { cwd: ROOT });
18
+
19
+ // Monorepo: scan apps/*/src/ and packages/*/src/ when standard src/ yields nothing backend-relevant
20
+ if (stack.monorepo || srcDirs.length === 0) {
21
+ const monoModules = await glob("{apps,packages}/*/src/modules/*/", { cwd: ROOT, ignore: ["**/node_modules/**"] });
22
+ if (monoModules.length > 0) {
23
+ srcDirs = [...srcDirs, ...monoModules];
24
+ } else {
25
+ const monoDirs = await glob("{apps,packages}/*/src/*/", { cwd: ROOT, ignore: ["**/node_modules/**"] });
26
+ srcDirs = [...srcDirs, ...monoDirs];
27
+ }
28
+ }
29
+
30
+ for (let dir of srcDirs) {
31
+ if (!dir.endsWith("/")) dir += "/";
32
+ const name = path.basename(dir.replace(/\/$/, ""));
33
+ if (skipDirs.includes(name)) continue;
34
+ const files = await glob(`${dir.replace(/\\/g, "/")}**/*.{ts,js}`, { cwd: ROOT, ignore: ["**/*.spec.*", "**/*.test.*"] });
35
+ if (files.length > 0) {
36
+ const controllers = files.filter(f => /controller|router|route|handler/.test(f)).length;
37
+ const services = files.filter(f => /service/.test(f)).length;
38
+ const dtos = files.filter(f => /dto|schema|type/.test(f)).length;
39
+ const entities = files.filter(f => /entity|model/.test(f) && !/controller|service|dto/.test(f)).length;
40
+ const modules = files.filter(f => /\.module\./.test(f)).length;
41
+ const guards = files.filter(f => /guard/.test(f)).length;
42
+ const pipes = files.filter(f => /pipe/.test(f)).length;
43
+ const interceptors = files.filter(f => /interceptor/.test(f)).length;
44
+ const domain = { name, type: "backend", controllers, services, dtos, totalFiles: files.length };
45
+ if (entities > 0) domain.entities = entities;
46
+ if (modules > 0) domain.modules = modules;
47
+ if (guards > 0) domain.guards = guards;
48
+ if (pipes > 0) domain.pipes = pipes;
49
+ if (interceptors > 0) domain.interceptors = interceptors;
50
+ backendDomains.push(domain);
51
+ }
52
+ }
53
+
54
+ return { backendDomains };
55
+ }
56
+
57
+ module.exports = { scanNodeDomains };