claudeos-core 2.5.0 → 2.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +88 -0
- package/README.de.md +4 -2
- package/README.es.md +4 -2
- package/README.fr.md +4 -2
- package/README.hi.md +4 -2
- package/README.ja.md +4 -2
- package/README.ko.md +4 -2
- package/README.md +4 -2
- package/README.ru.md +4 -2
- package/README.vi.md +4 -2
- package/README.zh-CN.md +4 -2
- package/lib/env-parser.js +198 -4
- package/package.json +92 -92
- package/pass-json-validator/index.js +1 -1
- package/pass-prompts/templates/java-spring/pass1.md +10 -2
- package/pass-prompts/templates/java-spring/pass3.md +2 -1
- package/plan-installer/index.js +17 -1
- package/plan-installer/jvm-detect.js +562 -0
- package/plan-installer/scanners/scan-java.js +82 -14
- package/plan-installer/stack-detector.js +471 -34
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
const path = require("path");
|
|
15
15
|
const { glob } = require("glob");
|
|
16
|
+
const { readFileSafe, existsSafe } = require("../../lib/safe-fs");
|
|
16
17
|
|
|
17
18
|
// Normalize backslash paths from glob on Windows to forward slashes
|
|
18
19
|
const norm = (p) => p.replace(/\\/g, "/");
|
|
@@ -29,7 +30,26 @@ const norm = (p) => p.replace(/\\/g, "/");
|
|
|
29
30
|
// convention plugins are not application modules.
|
|
30
31
|
const JAVA_ROOT_IGNORE = ["**/node_modules/**", "**/build/**", "**/target/**", "**/out/**", "**/.gradle/**", "**/generated/**", "**/.git/**", "**/src/test/**", "**/buildSrc/**"];
|
|
31
32
|
|
|
32
|
-
|
|
33
|
+
// v2.5.x — Source roots, not module prefixes. Every pattern below is written
|
|
34
|
+
// against the Maven/Gradle convention (`src/main/java`, `src/main/resources`)
|
|
35
|
+
// and is rewritten per discovered root, so the pattern set stays a single
|
|
36
|
+
// source of truth while legacy layouts become scannable:
|
|
37
|
+
//
|
|
38
|
+
// modern [<module>/]src/main/java (unchanged behaviour)
|
|
39
|
+
// Eclipse <classpathentry kind="src" path="src"/> ← consulted first
|
|
40
|
+
// Ant <javac srcdir="src"> (with <property> resolution)
|
|
41
|
+
// bare src/java, src, JavaSource, java, WebContent/WEB-INF/src holding *.java
|
|
42
|
+
//
|
|
43
|
+
// Candidates are tried in that order and every candidate that holds *.java
|
|
44
|
+
// becomes a root, except one nested inside (or enclosing) a root already
|
|
45
|
+
// accepted — the FIRST-listed of a nested pair wins, which is why the bare
|
|
46
|
+
// list names `src/java` before `src`.
|
|
47
|
+
//
|
|
48
|
+
// Legacy roots are consulted ONLY when no `src/main/java` exists anywhere in
|
|
49
|
+
// the tree, so a modern project with a stray top-level `src/` cannot be
|
|
50
|
+
// mis-rooted. For legacy roots the resources root is the java root itself:
|
|
51
|
+
// iBatis-era projects keep sqlmap XML next to the classes.
|
|
52
|
+
async function discoverSourceRoots(ROOT) {
|
|
33
53
|
const javaRoots = (await glob("**/src/main/java/", { cwd: ROOT, ignore: JAVA_ROOT_IGNORE })).map(norm);
|
|
34
54
|
const resRoots = (await glob("**/src/main/resources/", { cwd: ROOT, ignore: JAVA_ROOT_IGNORE })).map(norm);
|
|
35
55
|
const prefixes = new Set();
|
|
@@ -37,16 +57,57 @@ async function discoverModulePrefixes(ROOT) {
|
|
|
37
57
|
const m = r.replace(/\/$/, "").match(/^(.*?)src\/main\/(?:java|resources)$/);
|
|
38
58
|
if (m) prefixes.add(m[1]); // "" for root, "api/" for a module
|
|
39
59
|
}
|
|
40
|
-
|
|
60
|
+
if (prefixes.size) {
|
|
61
|
+
return [...prefixes].sort().map(pre => ({ prefix: pre, javaRoot: pre + "src/main/java", resRoot: pre + "src/main/resources", legacy: false }));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// ── legacy fallbacks ──
|
|
65
|
+
const candidates = [];
|
|
66
|
+
const cpXml = readFileSafe(path.join(ROOT, ".classpath"));
|
|
67
|
+
if (cpXml) {
|
|
68
|
+
for (const m of cpXml.matchAll(/<classpathentry\b[^>]*\bkind\s*=\s*["']src["'][^>]*\bpath\s*=\s*["']([^"']+)["']/g)) {
|
|
69
|
+
const p = norm(m[1]).replace(/^\/|\/$/g, "");
|
|
70
|
+
if (p && !/(^|\/)test(s)?(\/|$)/i.test(p)) candidates.push(p);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
const bx = readFileSafe(path.join(ROOT, "build.xml"));
|
|
74
|
+
if (bx) {
|
|
75
|
+
const props = {};
|
|
76
|
+
for (const m of bx.matchAll(/<property\b[^>]*\bname\s*=\s*["']([^"']+)["'][^>]*\bvalue\s*=\s*["']([^"']+)["']/g)) props[m[1]] = m[2];
|
|
77
|
+
for (const m of bx.matchAll(/<javac\b[^>]*\bsrcdir\s*=\s*["']([^"']+)["']/g)) {
|
|
78
|
+
const p = norm(m[1].replace(/\$\{([^}]+)\}/g, (_, k) => props[k] ?? "")).replace(/^\.?\/|\/$/g, "");
|
|
79
|
+
if (p && !/test/i.test(p)) candidates.push(p);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
for (const c of ["src/java", "src", "JavaSource", "java", "WebContent/WEB-INF/src"]) candidates.push(c);
|
|
83
|
+
|
|
84
|
+
const roots = [];
|
|
85
|
+
const seen = new Set();
|
|
86
|
+
for (const c of candidates) {
|
|
87
|
+
if (seen.has(c)) continue;
|
|
88
|
+
seen.add(c);
|
|
89
|
+
if (!existsSafe(path.join(ROOT, c))) continue;
|
|
90
|
+
const hasJava = (await glob(c + "/**/*.java", { cwd: ROOT, ignore: JAVA_ROOT_IGNORE, nodir: true })).length > 0;
|
|
91
|
+
if (!hasJava) continue;
|
|
92
|
+
// Nested pair (`src` vs `src/java`) → keep the first-accepted one only.
|
|
93
|
+
if (roots.some(r => c.startsWith(r.javaRoot + "/") || r.javaRoot.startsWith(c + "/"))) continue;
|
|
94
|
+
roots.push({ prefix: "", javaRoot: c, resRoot: c, legacy: true });
|
|
95
|
+
}
|
|
96
|
+
return roots;
|
|
41
97
|
}
|
|
42
98
|
|
|
43
|
-
// Run one `src/main/...`-relative pattern against every discovered
|
|
44
|
-
//
|
|
45
|
-
|
|
99
|
+
// Run one `src/main/...`-relative pattern against every discovered source
|
|
100
|
+
// root — rewriting the conventional leading segment to that root's actual
|
|
101
|
+
// directory — and return the merged, normalized, de-duplicated file list.
|
|
102
|
+
function makeModuleGlob(ROOT, roots) {
|
|
46
103
|
return async (pattern) => {
|
|
47
104
|
const out = new Set();
|
|
48
|
-
for (const
|
|
49
|
-
|
|
105
|
+
for (const r of roots) {
|
|
106
|
+
// Replacement FUNCTIONS so a `$` in a discovered root path is literal.
|
|
107
|
+
const p = pattern
|
|
108
|
+
.replace(/^src\/main\/java(?=\/|$)/, () => r.javaRoot)
|
|
109
|
+
.replace(/^src\/main\/resources(?=\/|$)/, () => r.resRoot);
|
|
110
|
+
for (const f of await glob(p, { cwd: ROOT })) out.add(norm(f));
|
|
50
111
|
}
|
|
51
112
|
return [...out];
|
|
52
113
|
};
|
|
@@ -56,8 +117,16 @@ async function scanJavaDomains(stack, ROOT) {
|
|
|
56
117
|
const backendDomains = [];
|
|
57
118
|
let rootPackage = null;
|
|
58
119
|
|
|
59
|
-
const
|
|
60
|
-
const
|
|
120
|
+
const sourceRoots = await discoverSourceRoots(ROOT);
|
|
121
|
+
const rootsInUse = sourceRoots.length ? sourceRoots : [{ prefix: "", javaRoot: "src/main/java", resRoot: "src/main/resources", legacy: false }];
|
|
122
|
+
const modulePrefixes = rootsInUse.map(r => r.prefix).filter(Boolean);
|
|
123
|
+
const gj = makeModuleGlob(ROOT, rootsInUse);
|
|
124
|
+
// Regex fragment matching any java root — used where file PATHS (not
|
|
125
|
+
// glob patterns) are inspected below. Modern roots collapse to the
|
|
126
|
+
// conventional `src/main/java`; legacy roots contribute their own dir.
|
|
127
|
+
const escRe = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
128
|
+
const JAVA_ROOT_ALT = [...new Set(rootsInUse.map(r => r.legacy ? r.javaRoot : "src/main/java"))].map(escRe).join("|");
|
|
129
|
+
if (stack && rootsInUse.some(r => r.legacy)) stack.sourceLayout = "legacy";
|
|
61
130
|
|
|
62
131
|
const javaFiles = (await gj("src/main/java/**/*.java"));
|
|
63
132
|
|
|
@@ -80,7 +149,7 @@ async function scanJavaDomains(stack, ROOT) {
|
|
|
80
149
|
// not the minority `<root>.misc.*` location (no longer first-match).
|
|
81
150
|
const pkgCounts = new Map();
|
|
82
151
|
for (const f of javaFiles) {
|
|
83
|
-
const m = f.match(/
|
|
152
|
+
const m = f.match(new RegExp(`(?:${JAVA_ROOT_ALT})/(.+?)/(controller|aggregator|facade|usecase|orchestrator|service|mapper|dao|dto|entity|repository|adapter)`));
|
|
84
153
|
if (!m) continue;
|
|
85
154
|
const segs = m[1].split("/");
|
|
86
155
|
for (let len = Math.min(4, segs.length); len >= 1; len--) {
|
|
@@ -134,12 +203,11 @@ async function scanJavaDomains(stack, ROOT) {
|
|
|
134
203
|
// controller/`), where the module's own sub-package plays the role of the
|
|
135
204
|
// Initializr base package and the domains again come from class names.
|
|
136
205
|
const rootPkgPath = rootPackage ? rootPackage.replace(/\./g, "/") : null;
|
|
137
|
-
const escRe = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
138
206
|
const flatDirCache = new Map();
|
|
139
207
|
const LAYER_CLASS_RE = /^(?:controller|service|mapper|repository|dao|dto)\/([A-Za-z0-9]+?)(?:Controller|Service|Mapper|Repository|Dao|Dto)\.java$/;
|
|
140
208
|
const isFlatBase = (base) => {
|
|
141
209
|
if (!flatDirCache.has(base)) {
|
|
142
|
-
const dirRe = new RegExp(`(^|/)
|
|
210
|
+
const dirRe = new RegExp(`(^|/)(?:${JAVA_ROOT_ALT})/${escRe(base)}/`);
|
|
143
211
|
const under = [];
|
|
144
212
|
for (const x of javaFiles) {
|
|
145
213
|
const m = x.match(dirRe);
|
|
@@ -157,7 +225,7 @@ async function scanJavaDomains(stack, ROOT) {
|
|
|
157
225
|
// lives, independent of `rootPackage` (which is capped at 4 segments and
|
|
158
226
|
// therefore misses `kr/co/<org>/<proj>/<app>` style base packages).
|
|
159
227
|
const appBases = [...new Set(javaFiles
|
|
160
|
-
.map(f => (f.match(/
|
|
228
|
+
.map(f => (f.match(new RegExp(`(?:${JAVA_ROOT_ALT})/(.+)/[A-Za-z0-9]*Application\\.java$`)) || [])[1])
|
|
161
229
|
.filter(Boolean))];
|
|
162
230
|
const isFlatLayerPath = (f, layerSegment) => {
|
|
163
231
|
if (!rootPkgPath && appBases.length === 0) return false;
|
|
@@ -165,7 +233,7 @@ async function scanJavaDomains(stack, ROOT) {
|
|
|
165
233
|
const pre = modulePrefixes.find(p => p && f.startsWith(p));
|
|
166
234
|
if (pre && rootPkgPath) bases.push(`${rootPkgPath}/${pre.replace(/\/$/, "").split("/").pop()}`);
|
|
167
235
|
for (const base of bases) {
|
|
168
|
-
const layerRe = new RegExp(`(^|/)
|
|
236
|
+
const layerRe = new RegExp(`(^|/)(?:${JAVA_ROOT_ALT})/${escRe(base)}/${escRe(layerSegment)}/[^/]+\\.java$`);
|
|
169
237
|
if (layerRe.test(f) && isFlatBase(base)) return true;
|
|
170
238
|
}
|
|
171
239
|
return false;
|