@codexa/cli 8.5.0 → 8.6.9
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/commands/architect.ts +1 -137
- package/commands/clear.ts +1 -5
- package/commands/discover.ts +1071 -999
- package/commands/product.ts +0 -2
- package/commands/research.ts +1 -1
- package/commands/standards.ts +0 -23
- package/commands/task.ts +623 -623
- package/db/schema.ts +0 -69
- package/detectors/README.md +109 -109
- package/detectors/dotnet.ts +357 -357
- package/detectors/flutter.ts +350 -350
- package/detectors/go.ts +324 -324
- package/detectors/index.ts +387 -387
- package/detectors/jvm.ts +433 -433
- package/detectors/loader.ts +128 -140
- package/detectors/node.ts +493 -493
- package/detectors/python.ts +423 -423
- package/detectors/rust.ts +348 -348
- package/package.json +5 -4
- package/protocol/subagent-protocol.ts +0 -10
- package/workflow.ts +783 -800
package/detectors/jvm.ts
CHANGED
|
@@ -1,434 +1,434 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* JVM Ecosystem Detector (Java/Kotlin/Scala)
|
|
3
|
-
*
|
|
4
|
-
* Detects JVM projects including:
|
|
5
|
-
* - Build tools: Maven, Gradle, sbt
|
|
6
|
-
* - Frameworks: Spring Boot, Quarkus, Micronaut, Ktor
|
|
7
|
-
* - ORMs: Hibernate, JPA, Exposed, jOOQ
|
|
8
|
-
* - Testing: JUnit, TestNG, Kotest, Mockito
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import { join } from "path";
|
|
12
|
-
import {
|
|
13
|
-
registerDetector,
|
|
14
|
-
Detector,
|
|
15
|
-
DetectorResult,
|
|
16
|
-
DetectedTechnology,
|
|
17
|
-
fileExists,
|
|
18
|
-
dirExists,
|
|
19
|
-
findFiles,
|
|
20
|
-
readText,
|
|
21
|
-
} from "./index";
|
|
22
|
-
|
|
23
|
-
interface MavenDependency {
|
|
24
|
-
groupId: string;
|
|
25
|
-
artifactId: string;
|
|
26
|
-
version?: string;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function parsePomXml(content: string): { dependencies: MavenDependency[]; parent?: MavenDependency } {
|
|
30
|
-
const result: { dependencies: MavenDependency[]; parent?: MavenDependency } = { dependencies: [] };
|
|
31
|
-
|
|
32
|
-
// Parse parent
|
|
33
|
-
const parentMatch = content.match(/<parent>([\s\S]*?)<\/parent>/);
|
|
34
|
-
if (parentMatch) {
|
|
35
|
-
const groupIdMatch = parentMatch[1].match(/<groupId>([^<]+)<\/groupId>/);
|
|
36
|
-
const artifactIdMatch = parentMatch[1].match(/<artifactId>([^<]+)<\/artifactId>/);
|
|
37
|
-
const versionMatch = parentMatch[1].match(/<version>([^<]+)<\/version>/);
|
|
38
|
-
if (groupIdMatch && artifactIdMatch) {
|
|
39
|
-
result.parent = {
|
|
40
|
-
groupId: groupIdMatch[1],
|
|
41
|
-
artifactId: artifactIdMatch[1],
|
|
42
|
-
version: versionMatch?.[1],
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// Parse dependencies
|
|
48
|
-
const depsMatch = content.match(/<dependencies>([\s\S]*?)<\/dependencies>/);
|
|
49
|
-
if (depsMatch) {
|
|
50
|
-
const depRegex = /<dependency>([\s\S]*?)<\/dependency>/g;
|
|
51
|
-
let match;
|
|
52
|
-
while ((match = depRegex.exec(depsMatch[1])) !== null) {
|
|
53
|
-
const groupIdMatch = match[1].match(/<groupId>([^<]+)<\/groupId>/);
|
|
54
|
-
const artifactIdMatch = match[1].match(/<artifactId>([^<]+)<\/artifactId>/);
|
|
55
|
-
const versionMatch = match[1].match(/<version>([^<]+)<\/version>/);
|
|
56
|
-
if (groupIdMatch && artifactIdMatch) {
|
|
57
|
-
result.dependencies.push({
|
|
58
|
-
groupId: groupIdMatch[1],
|
|
59
|
-
artifactId: artifactIdMatch[1],
|
|
60
|
-
version: versionMatch?.[1],
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
return result;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
function parseGradleDependencies(content: string): string[] {
|
|
70
|
-
const deps: string[] = [];
|
|
71
|
-
|
|
72
|
-
// Match various dependency patterns
|
|
73
|
-
const patterns = [
|
|
74
|
-
/implementation\s*['"(]([^'")\s]+)['")\s]/g,
|
|
75
|
-
/api\s*['"(]([^'")\s]+)['")\s]/g,
|
|
76
|
-
/compileOnly\s*['"(]([^'")\s]+)['")\s]/g,
|
|
77
|
-
/runtimeOnly\s*['"(]([^'")\s]+)['")\s]/g,
|
|
78
|
-
/testImplementation\s*['"(]([^'")\s]+)['")\s]/g,
|
|
79
|
-
/implementation\s*\(\s*['"]([^'"]+)['"]\s*\)/g,
|
|
80
|
-
];
|
|
81
|
-
|
|
82
|
-
for (const pattern of patterns) {
|
|
83
|
-
let match;
|
|
84
|
-
while ((match = pattern.exec(content)) !== null) {
|
|
85
|
-
deps.push(match[1]);
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
return deps;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
const jvmDetector: Detector = {
|
|
93
|
-
name: "jvm",
|
|
94
|
-
ecosystem: "java",
|
|
95
|
-
priority: 70,
|
|
96
|
-
markers: [
|
|
97
|
-
{ type: "file", pattern: "pom.xml", weight: 1.0 },
|
|
98
|
-
{ type: "file", pattern: "build.gradle", weight: 1.0 },
|
|
99
|
-
{ type: "file", pattern: "build.gradle.kts", weight: 1.0 },
|
|
100
|
-
{ type: "file", pattern: "settings.gradle", weight: 0.9 },
|
|
101
|
-
{ type: "file", pattern: "settings.gradle.kts", weight: 0.9 },
|
|
102
|
-
{ type: "file", pattern: "build.sbt", weight: 1.0 },
|
|
103
|
-
{ type: "file", pattern: "gradlew", weight: 0.8 },
|
|
104
|
-
{ type: "file", pattern: "mvnw", weight: 0.8 },
|
|
105
|
-
{ type: "glob", pattern: "**/*.java", weight: 0.7 },
|
|
106
|
-
{ type: "glob", pattern: "**/*.kt", weight: 0.7 },
|
|
107
|
-
{ type: "glob", pattern: "**/*.scala", weight: 0.7 },
|
|
108
|
-
{ type: "directory", pattern: "src/main/java", weight: 0.8 },
|
|
109
|
-
{ type: "directory", pattern: "src/main/kotlin", weight: 0.8 },
|
|
110
|
-
],
|
|
111
|
-
|
|
112
|
-
async detect(cwd: string): Promise<DetectorResult | null> {
|
|
113
|
-
const technologies: DetectedTechnology[] = [];
|
|
114
|
-
const structure: Record<string, string> = {};
|
|
115
|
-
const configFiles: string[] = [];
|
|
116
|
-
|
|
117
|
-
// Detect build tool and collect dependencies
|
|
118
|
-
const allDeps = new Set<string>();
|
|
119
|
-
let buildTool = "";
|
|
120
|
-
let language = "Java";
|
|
121
|
-
|
|
122
|
-
// Check for Maven
|
|
123
|
-
if (fileExists(join(cwd, "pom.xml"))) {
|
|
124
|
-
buildTool = "Maven";
|
|
125
|
-
configFiles.push("pom.xml");
|
|
126
|
-
if (fileExists(join(cwd, "mvnw"))) configFiles.push("mvnw");
|
|
127
|
-
|
|
128
|
-
const pomContent = readText(join(cwd, "pom.xml"));
|
|
129
|
-
if (pomContent) {
|
|
130
|
-
const pom = parsePomXml(pomContent);
|
|
131
|
-
|
|
132
|
-
// Check parent for Spring Boot
|
|
133
|
-
if (pom.parent?.artifactId === "spring-boot-starter-parent") {
|
|
134
|
-
allDeps.add("spring-boot");
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
for (const dep of pom.dependencies) {
|
|
138
|
-
allDeps.add(`${dep.groupId}:${dep.artifactId}`);
|
|
139
|
-
allDeps.add(dep.artifactId);
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
technologies.push({
|
|
144
|
-
name: "Maven",
|
|
145
|
-
confidence: 1.0,
|
|
146
|
-
source: "pom.xml",
|
|
147
|
-
category: "build",
|
|
148
|
-
});
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
// Check for Gradle
|
|
152
|
-
const gradleFile = fileExists(join(cwd, "build.gradle.kts"))
|
|
153
|
-
? "build.gradle.kts"
|
|
154
|
-
: fileExists(join(cwd, "build.gradle"))
|
|
155
|
-
? "build.gradle"
|
|
156
|
-
: null;
|
|
157
|
-
|
|
158
|
-
if (gradleFile) {
|
|
159
|
-
buildTool = "Gradle";
|
|
160
|
-
configFiles.push(gradleFile);
|
|
161
|
-
if (fileExists(join(cwd, "gradlew"))) configFiles.push("gradlew");
|
|
162
|
-
if (fileExists(join(cwd, "settings.gradle.kts"))) configFiles.push("settings.gradle.kts");
|
|
163
|
-
else if (fileExists(join(cwd, "settings.gradle"))) configFiles.push("settings.gradle");
|
|
164
|
-
|
|
165
|
-
const gradleContent = readText(join(cwd, gradleFile));
|
|
166
|
-
if (gradleContent) {
|
|
167
|
-
const deps = parseGradleDependencies(gradleContent);
|
|
168
|
-
for (const dep of deps) {
|
|
169
|
-
allDeps.add(dep);
|
|
170
|
-
// Also add artifact name
|
|
171
|
-
const parts = dep.split(":");
|
|
172
|
-
if (parts.length >= 2) {
|
|
173
|
-
allDeps.add(parts[1]);
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
// Check for Kotlin
|
|
178
|
-
if (gradleContent.includes("kotlin(") || gradleContent.includes("org.jetbrains.kotlin")) {
|
|
179
|
-
language = "Kotlin";
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
technologies.push({
|
|
184
|
-
name: "Gradle",
|
|
185
|
-
confidence: 1.0,
|
|
186
|
-
source: gradleFile,
|
|
187
|
-
category: "build",
|
|
188
|
-
});
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
// Check for sbt (Scala)
|
|
192
|
-
if (fileExists(join(cwd, "build.sbt"))) {
|
|
193
|
-
buildTool = "sbt";
|
|
194
|
-
language = "Scala";
|
|
195
|
-
configFiles.push("build.sbt");
|
|
196
|
-
|
|
197
|
-
technologies.push({
|
|
198
|
-
name: "sbt",
|
|
199
|
-
confidence: 1.0,
|
|
200
|
-
source: "build.sbt",
|
|
201
|
-
category: "build",
|
|
202
|
-
});
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
// No build tool found - check for source files
|
|
206
|
-
if (!buildTool) {
|
|
207
|
-
const javaFiles = findFiles(cwd, "**/*.java");
|
|
208
|
-
const ktFiles = findFiles(cwd, "**/*.kt");
|
|
209
|
-
const scalaFiles = findFiles(cwd, "**/*.scala");
|
|
210
|
-
|
|
211
|
-
if (javaFiles.length === 0 && ktFiles.length === 0 && scalaFiles.length === 0) {
|
|
212
|
-
return null;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
if (ktFiles.length > javaFiles.length) language = "Kotlin";
|
|
216
|
-
else if (scalaFiles.length > javaFiles.length) language = "Scala";
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
// Detect language from files
|
|
220
|
-
if (dirExists(join(cwd, "src/main/kotlin")) || findFiles(cwd, "**/*.kt").length > 0) {
|
|
221
|
-
if (language !== "Scala") language = "Kotlin";
|
|
222
|
-
}
|
|
223
|
-
if (dirExists(join(cwd, "src/main/scala")) || findFiles(cwd, "**/*.scala").length > 0) {
|
|
224
|
-
language = "Scala";
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
// Add language as runtime
|
|
228
|
-
technologies.push({
|
|
229
|
-
name: language,
|
|
230
|
-
confidence: 1.0,
|
|
231
|
-
source: "Source files",
|
|
232
|
-
category: "runtime",
|
|
233
|
-
});
|
|
234
|
-
|
|
235
|
-
// Web Framework Detection
|
|
236
|
-
const webFrameworks = [
|
|
237
|
-
{ deps: ["spring-boot", "spring-boot-starter-web", "org.springframework.boot:spring-boot-starter-web"], name: "Spring Boot", confidence: 1.0 },
|
|
238
|
-
{ deps: ["spring-webflux", "spring-boot-starter-webflux"], name: "Spring WebFlux", confidence: 1.0 },
|
|
239
|
-
{ deps: ["io.quarkus", "quarkus-core", "quarkus-resteasy"], name: "Quarkus", confidence: 1.0 },
|
|
240
|
-
{ deps: ["io.micronaut", "micronaut-core", "micronaut-http-server"], name: "Micronaut", confidence: 1.0 },
|
|
241
|
-
{ deps: ["io.ktor", "ktor-server-core", "ktor-server"], name: "Ktor", confidence: 1.0 },
|
|
242
|
-
{ deps: ["io.vertx", "vertx-core", "vertx-web"], name: "Vert.x", confidence: 1.0 },
|
|
243
|
-
{ deps: ["io.helidon", "helidon-webserver"], name: "Helidon", confidence: 1.0 },
|
|
244
|
-
{ deps: ["dropwizard", "io.dropwizard"], name: "Dropwizard", confidence: 1.0 },
|
|
245
|
-
{ deps: ["spark-core", "com.sparkjava:spark-core"], name: "Spark Java", confidence: 1.0 },
|
|
246
|
-
{ deps: ["javalin", "io.javalin"], name: "Javalin", confidence: 1.0 },
|
|
247
|
-
];
|
|
248
|
-
|
|
249
|
-
for (const fw of webFrameworks) {
|
|
250
|
-
const found = fw.deps.find(d => allDeps.has(d));
|
|
251
|
-
if (found) {
|
|
252
|
-
technologies.push({
|
|
253
|
-
name: fw.name,
|
|
254
|
-
confidence: fw.confidence,
|
|
255
|
-
source: `Dependency: ${found}`,
|
|
256
|
-
category: "backend",
|
|
257
|
-
});
|
|
258
|
-
break;
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
// ORM/Database Detection
|
|
263
|
-
const orms = [
|
|
264
|
-
{ deps: ["hibernate-core", "org.hibernate"], name: "Hibernate", confidence: 1.0 },
|
|
265
|
-
{ deps: ["spring-data-jpa", "spring-boot-starter-data-jpa"], name: "Spring Data JPA", confidence: 1.0 },
|
|
266
|
-
{ deps: ["mybatis", "org.mybatis"], name: "MyBatis", confidence: 1.0 },
|
|
267
|
-
{ deps: ["exposed", "org.jetbrains.exposed"], name: "Exposed", confidence: 1.0 },
|
|
268
|
-
{ deps: ["jooq", "org.jooq"], name: "jOOQ", confidence: 1.0 },
|
|
269
|
-
{ deps: ["ebean", "io.ebean"], name: "Ebean", confidence: 1.0 },
|
|
270
|
-
{ deps: ["jdbi", "org.jdbi"], name: "JDBI", confidence: 1.0 },
|
|
271
|
-
{ deps: ["slick", "com.typesafe.slick"], name: "Slick", confidence: 1.0 },
|
|
272
|
-
];
|
|
273
|
-
|
|
274
|
-
for (const orm of orms) {
|
|
275
|
-
const found = orm.deps.find(d => allDeps.has(d));
|
|
276
|
-
if (found) {
|
|
277
|
-
technologies.push({
|
|
278
|
-
name: orm.name,
|
|
279
|
-
confidence: orm.confidence,
|
|
280
|
-
source: `Dependency: ${found}`,
|
|
281
|
-
category: "orm",
|
|
282
|
-
});
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
// Database Drivers
|
|
287
|
-
const dbDrivers = [
|
|
288
|
-
{ deps: ["postgresql", "org.postgresql"], name: "PostgreSQL", confidence: 0.9 },
|
|
289
|
-
{ deps: ["mysql-connector-java", "mysql-connector-j", "com.mysql"], name: "MySQL", confidence: 0.9 },
|
|
290
|
-
{ deps: ["h2", "com.h2database"], name: "H2", confidence: 0.9 },
|
|
291
|
-
{ deps: ["mongodb-driver", "org.mongodb"], name: "MongoDB", confidence: 0.9 },
|
|
292
|
-
{ deps: ["jedis", "lettuce-core", "redis.clients"], name: "Redis", confidence: 0.9 },
|
|
293
|
-
{ deps: ["elasticsearch-java", "elasticsearch-rest-client"], name: "Elasticsearch", confidence: 0.9 },
|
|
294
|
-
{ deps: ["cassandra-driver", "datastax"], name: "Cassandra", confidence: 0.9 },
|
|
295
|
-
];
|
|
296
|
-
|
|
297
|
-
const detectedDbs = new Set<string>();
|
|
298
|
-
for (const db of dbDrivers) {
|
|
299
|
-
const found = db.deps.find(d => allDeps.has(d));
|
|
300
|
-
if (found && !detectedDbs.has(db.name)) {
|
|
301
|
-
technologies.push({
|
|
302
|
-
name: db.name,
|
|
303
|
-
confidence: db.confidence,
|
|
304
|
-
source: `Dependency: ${found}`,
|
|
305
|
-
category: "database",
|
|
306
|
-
});
|
|
307
|
-
detectedDbs.add(db.name);
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
// Testing Frameworks
|
|
312
|
-
const testingTools = [
|
|
313
|
-
{ deps: ["junit-jupiter", "org.junit.jupiter", "junit"], name: "JUnit", confidence: 1.0 },
|
|
314
|
-
{ deps: ["testng", "org.testng"], name: "TestNG", confidence: 1.0 },
|
|
315
|
-
{ deps: ["kotest", "io.kotest"], name: "Kotest", confidence: 1.0 },
|
|
316
|
-
{ deps: ["mockito", "org.mockito"], name: "Mockito", confidence: 0.95 },
|
|
317
|
-
{ deps: ["mockk", "io.mockk"], name: "MockK", confidence: 0.95 },
|
|
318
|
-
{ deps: ["assertj", "org.assertj"], name: "AssertJ", confidence: 0.9 },
|
|
319
|
-
{ deps: ["spock", "org.spockframework"], name: "Spock", confidence: 1.0 },
|
|
320
|
-
{ deps: ["scalatest", "org.scalatest"], name: "ScalaTest", confidence: 1.0 },
|
|
321
|
-
];
|
|
322
|
-
|
|
323
|
-
for (const test of testingTools) {
|
|
324
|
-
const found = test.deps.find(d => allDeps.has(d));
|
|
325
|
-
if (found) {
|
|
326
|
-
technologies.push({
|
|
327
|
-
name: test.name,
|
|
328
|
-
confidence: test.confidence,
|
|
329
|
-
source: `Dependency: ${found}`,
|
|
330
|
-
category: "testing",
|
|
331
|
-
});
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
// Security/Auth
|
|
336
|
-
const authTools = [
|
|
337
|
-
{ deps: ["spring-security", "spring-boot-starter-security"], name: "Spring Security", confidence: 1.0 },
|
|
338
|
-
{ deps: ["keycloak", "org.keycloak"], name: "Keycloak", confidence: 1.0 },
|
|
339
|
-
{ deps: ["nimbus-jose-jwt", "com.nimbusds"], name: "Nimbus JOSE JWT", confidence: 0.9 },
|
|
340
|
-
{ deps: ["java-jwt", "com.auth0:java-jwt"], name: "Auth0 JWT", confidence: 0.9 },
|
|
341
|
-
{ deps: ["pac4j", "org.pac4j"], name: "PAC4J", confidence: 1.0 },
|
|
342
|
-
];
|
|
343
|
-
|
|
344
|
-
for (const auth of authTools) {
|
|
345
|
-
const found = auth.deps.find(d => allDeps.has(d));
|
|
346
|
-
if (found) {
|
|
347
|
-
technologies.push({
|
|
348
|
-
name: auth.name,
|
|
349
|
-
confidence: auth.confidence,
|
|
350
|
-
source: `Dependency: ${found}`,
|
|
351
|
-
category: "auth",
|
|
352
|
-
});
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
// DevOps/Observability
|
|
357
|
-
const devopsTools = [
|
|
358
|
-
{ deps: ["lombok", "org.projectlombok"], name: "Lombok", confidence: 0.9 },
|
|
359
|
-
{ deps: ["micrometer", "io.micrometer"], name: "Micrometer", confidence: 0.9 },
|
|
360
|
-
{ deps: ["spring-boot-actuator", "spring-boot-starter-actuator"], name: "Spring Actuator", confidence: 0.95 },
|
|
361
|
-
{ deps: ["logback", "ch.qos.logback"], name: "Logback", confidence: 0.85 },
|
|
362
|
-
{ deps: ["log4j", "org.apache.logging.log4j"], name: "Log4j", confidence: 0.85 },
|
|
363
|
-
{ deps: ["opentelemetry", "io.opentelemetry"], name: "OpenTelemetry", confidence: 0.9 },
|
|
364
|
-
];
|
|
365
|
-
|
|
366
|
-
for (const tool of devopsTools) {
|
|
367
|
-
const found = tool.deps.find(d => allDeps.has(d));
|
|
368
|
-
if (found) {
|
|
369
|
-
technologies.push({
|
|
370
|
-
name: tool.name,
|
|
371
|
-
confidence: tool.confidence,
|
|
372
|
-
source: `Dependency: ${found}`,
|
|
373
|
-
category: "devops",
|
|
374
|
-
});
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
// Structure detection (standard Maven/Gradle layout)
|
|
379
|
-
const structurePaths = [
|
|
380
|
-
{ key: "main", paths: ["src/main/java", "src/main/kotlin", "src/main/scala"] },
|
|
381
|
-
{ key: "test", paths: ["src/test/java", "src/test/kotlin", "src/test/scala"] },
|
|
382
|
-
{ key: "resources", paths: ["src/main/resources"] },
|
|
383
|
-
{ key: "testResources", paths: ["src/test/resources"] },
|
|
384
|
-
{ key: "controllers", paths: ["src/main/java/controller", "src/main/java/controllers", "src/main/kotlin/controller"] },
|
|
385
|
-
{ key: "services", paths: ["src/main/java/service", "src/main/java/services", "src/main/kotlin/service"] },
|
|
386
|
-
{ key: "repository", paths: ["src/main/java/repository", "src/main/java/repositories", "src/main/kotlin/repository"] },
|
|
387
|
-
{ key: "models", paths: ["src/main/java/model", "src/main/java/models", "src/main/java/entity", "src/main/kotlin/model"] },
|
|
388
|
-
{ key: "config", paths: ["src/main/java/config", "src/main/java/configuration", "src/main/kotlin/config"] },
|
|
389
|
-
];
|
|
390
|
-
|
|
391
|
-
for (const { key, paths } of structurePaths) {
|
|
392
|
-
for (const p of paths) {
|
|
393
|
-
if (dirExists(join(cwd, p))) {
|
|
394
|
-
structure[key] = p;
|
|
395
|
-
break;
|
|
396
|
-
}
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
// Config files
|
|
401
|
-
const configPatterns = [
|
|
402
|
-
"application.properties",
|
|
403
|
-
"application.yml",
|
|
404
|
-
"application.yaml",
|
|
405
|
-
"application-dev.properties",
|
|
406
|
-
"application-dev.yml",
|
|
407
|
-
"bootstrap.properties",
|
|
408
|
-
"bootstrap.yml",
|
|
409
|
-
"lombok.config",
|
|
410
|
-
"checkstyle.xml",
|
|
411
|
-
"spotbugs.xml",
|
|
412
|
-
"Dockerfile",
|
|
413
|
-
"docker-compose.yml",
|
|
414
|
-
];
|
|
415
|
-
|
|
416
|
-
for (const pattern of configPatterns) {
|
|
417
|
-
if (fileExists(join(cwd, pattern)) || fileExists(join(cwd, "src/main/resources", pattern))) {
|
|
418
|
-
configFiles.push(pattern);
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
return {
|
|
423
|
-
ecosystem: "java",
|
|
424
|
-
technologies,
|
|
425
|
-
structure,
|
|
426
|
-
configFiles: [...new Set(configFiles)],
|
|
427
|
-
};
|
|
428
|
-
},
|
|
429
|
-
};
|
|
430
|
-
|
|
431
|
-
// Register the detector
|
|
432
|
-
registerDetector(jvmDetector);
|
|
433
|
-
|
|
1
|
+
/**
|
|
2
|
+
* JVM Ecosystem Detector (Java/Kotlin/Scala)
|
|
3
|
+
*
|
|
4
|
+
* Detects JVM projects including:
|
|
5
|
+
* - Build tools: Maven, Gradle, sbt
|
|
6
|
+
* - Frameworks: Spring Boot, Quarkus, Micronaut, Ktor
|
|
7
|
+
* - ORMs: Hibernate, JPA, Exposed, jOOQ
|
|
8
|
+
* - Testing: JUnit, TestNG, Kotest, Mockito
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { join } from "path";
|
|
12
|
+
import {
|
|
13
|
+
registerDetector,
|
|
14
|
+
Detector,
|
|
15
|
+
DetectorResult,
|
|
16
|
+
DetectedTechnology,
|
|
17
|
+
fileExists,
|
|
18
|
+
dirExists,
|
|
19
|
+
findFiles,
|
|
20
|
+
readText,
|
|
21
|
+
} from "./index";
|
|
22
|
+
|
|
23
|
+
interface MavenDependency {
|
|
24
|
+
groupId: string;
|
|
25
|
+
artifactId: string;
|
|
26
|
+
version?: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function parsePomXml(content: string): { dependencies: MavenDependency[]; parent?: MavenDependency } {
|
|
30
|
+
const result: { dependencies: MavenDependency[]; parent?: MavenDependency } = { dependencies: [] };
|
|
31
|
+
|
|
32
|
+
// Parse parent
|
|
33
|
+
const parentMatch = content.match(/<parent>([\s\S]*?)<\/parent>/);
|
|
34
|
+
if (parentMatch) {
|
|
35
|
+
const groupIdMatch = parentMatch[1].match(/<groupId>([^<]+)<\/groupId>/);
|
|
36
|
+
const artifactIdMatch = parentMatch[1].match(/<artifactId>([^<]+)<\/artifactId>/);
|
|
37
|
+
const versionMatch = parentMatch[1].match(/<version>([^<]+)<\/version>/);
|
|
38
|
+
if (groupIdMatch && artifactIdMatch) {
|
|
39
|
+
result.parent = {
|
|
40
|
+
groupId: groupIdMatch[1],
|
|
41
|
+
artifactId: artifactIdMatch[1],
|
|
42
|
+
version: versionMatch?.[1],
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Parse dependencies
|
|
48
|
+
const depsMatch = content.match(/<dependencies>([\s\S]*?)<\/dependencies>/);
|
|
49
|
+
if (depsMatch) {
|
|
50
|
+
const depRegex = /<dependency>([\s\S]*?)<\/dependency>/g;
|
|
51
|
+
let match;
|
|
52
|
+
while ((match = depRegex.exec(depsMatch[1])) !== null) {
|
|
53
|
+
const groupIdMatch = match[1].match(/<groupId>([^<]+)<\/groupId>/);
|
|
54
|
+
const artifactIdMatch = match[1].match(/<artifactId>([^<]+)<\/artifactId>/);
|
|
55
|
+
const versionMatch = match[1].match(/<version>([^<]+)<\/version>/);
|
|
56
|
+
if (groupIdMatch && artifactIdMatch) {
|
|
57
|
+
result.dependencies.push({
|
|
58
|
+
groupId: groupIdMatch[1],
|
|
59
|
+
artifactId: artifactIdMatch[1],
|
|
60
|
+
version: versionMatch?.[1],
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return result;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function parseGradleDependencies(content: string): string[] {
|
|
70
|
+
const deps: string[] = [];
|
|
71
|
+
|
|
72
|
+
// Match various dependency patterns
|
|
73
|
+
const patterns = [
|
|
74
|
+
/implementation\s*['"(]([^'")\s]+)['")\s]/g,
|
|
75
|
+
/api\s*['"(]([^'")\s]+)['")\s]/g,
|
|
76
|
+
/compileOnly\s*['"(]([^'")\s]+)['")\s]/g,
|
|
77
|
+
/runtimeOnly\s*['"(]([^'")\s]+)['")\s]/g,
|
|
78
|
+
/testImplementation\s*['"(]([^'")\s]+)['")\s]/g,
|
|
79
|
+
/implementation\s*\(\s*['"]([^'"]+)['"]\s*\)/g,
|
|
80
|
+
];
|
|
81
|
+
|
|
82
|
+
for (const pattern of patterns) {
|
|
83
|
+
let match;
|
|
84
|
+
while ((match = pattern.exec(content)) !== null) {
|
|
85
|
+
deps.push(match[1]);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return deps;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const jvmDetector: Detector = {
|
|
93
|
+
name: "jvm",
|
|
94
|
+
ecosystem: "java",
|
|
95
|
+
priority: 70,
|
|
96
|
+
markers: [
|
|
97
|
+
{ type: "file", pattern: "pom.xml", weight: 1.0 },
|
|
98
|
+
{ type: "file", pattern: "build.gradle", weight: 1.0 },
|
|
99
|
+
{ type: "file", pattern: "build.gradle.kts", weight: 1.0 },
|
|
100
|
+
{ type: "file", pattern: "settings.gradle", weight: 0.9 },
|
|
101
|
+
{ type: "file", pattern: "settings.gradle.kts", weight: 0.9 },
|
|
102
|
+
{ type: "file", pattern: "build.sbt", weight: 1.0 },
|
|
103
|
+
{ type: "file", pattern: "gradlew", weight: 0.8 },
|
|
104
|
+
{ type: "file", pattern: "mvnw", weight: 0.8 },
|
|
105
|
+
{ type: "glob", pattern: "**/*.java", weight: 0.7 },
|
|
106
|
+
{ type: "glob", pattern: "**/*.kt", weight: 0.7 },
|
|
107
|
+
{ type: "glob", pattern: "**/*.scala", weight: 0.7 },
|
|
108
|
+
{ type: "directory", pattern: "src/main/java", weight: 0.8 },
|
|
109
|
+
{ type: "directory", pattern: "src/main/kotlin", weight: 0.8 },
|
|
110
|
+
],
|
|
111
|
+
|
|
112
|
+
async detect(cwd: string): Promise<DetectorResult | null> {
|
|
113
|
+
const technologies: DetectedTechnology[] = [];
|
|
114
|
+
const structure: Record<string, string> = {};
|
|
115
|
+
const configFiles: string[] = [];
|
|
116
|
+
|
|
117
|
+
// Detect build tool and collect dependencies
|
|
118
|
+
const allDeps = new Set<string>();
|
|
119
|
+
let buildTool = "";
|
|
120
|
+
let language = "Java";
|
|
121
|
+
|
|
122
|
+
// Check for Maven
|
|
123
|
+
if (fileExists(join(cwd, "pom.xml"))) {
|
|
124
|
+
buildTool = "Maven";
|
|
125
|
+
configFiles.push("pom.xml");
|
|
126
|
+
if (fileExists(join(cwd, "mvnw"))) configFiles.push("mvnw");
|
|
127
|
+
|
|
128
|
+
const pomContent = readText(join(cwd, "pom.xml"));
|
|
129
|
+
if (pomContent) {
|
|
130
|
+
const pom = parsePomXml(pomContent);
|
|
131
|
+
|
|
132
|
+
// Check parent for Spring Boot
|
|
133
|
+
if (pom.parent?.artifactId === "spring-boot-starter-parent") {
|
|
134
|
+
allDeps.add("spring-boot");
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
for (const dep of pom.dependencies) {
|
|
138
|
+
allDeps.add(`${dep.groupId}:${dep.artifactId}`);
|
|
139
|
+
allDeps.add(dep.artifactId);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
technologies.push({
|
|
144
|
+
name: "Maven",
|
|
145
|
+
confidence: 1.0,
|
|
146
|
+
source: "pom.xml",
|
|
147
|
+
category: "build",
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Check for Gradle
|
|
152
|
+
const gradleFile = fileExists(join(cwd, "build.gradle.kts"))
|
|
153
|
+
? "build.gradle.kts"
|
|
154
|
+
: fileExists(join(cwd, "build.gradle"))
|
|
155
|
+
? "build.gradle"
|
|
156
|
+
: null;
|
|
157
|
+
|
|
158
|
+
if (gradleFile) {
|
|
159
|
+
buildTool = "Gradle";
|
|
160
|
+
configFiles.push(gradleFile);
|
|
161
|
+
if (fileExists(join(cwd, "gradlew"))) configFiles.push("gradlew");
|
|
162
|
+
if (fileExists(join(cwd, "settings.gradle.kts"))) configFiles.push("settings.gradle.kts");
|
|
163
|
+
else if (fileExists(join(cwd, "settings.gradle"))) configFiles.push("settings.gradle");
|
|
164
|
+
|
|
165
|
+
const gradleContent = readText(join(cwd, gradleFile));
|
|
166
|
+
if (gradleContent) {
|
|
167
|
+
const deps = parseGradleDependencies(gradleContent);
|
|
168
|
+
for (const dep of deps) {
|
|
169
|
+
allDeps.add(dep);
|
|
170
|
+
// Also add artifact name
|
|
171
|
+
const parts = dep.split(":");
|
|
172
|
+
if (parts.length >= 2) {
|
|
173
|
+
allDeps.add(parts[1]);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Check for Kotlin
|
|
178
|
+
if (gradleContent.includes("kotlin(") || gradleContent.includes("org.jetbrains.kotlin")) {
|
|
179
|
+
language = "Kotlin";
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
technologies.push({
|
|
184
|
+
name: "Gradle",
|
|
185
|
+
confidence: 1.0,
|
|
186
|
+
source: gradleFile,
|
|
187
|
+
category: "build",
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Check for sbt (Scala)
|
|
192
|
+
if (fileExists(join(cwd, "build.sbt"))) {
|
|
193
|
+
buildTool = "sbt";
|
|
194
|
+
language = "Scala";
|
|
195
|
+
configFiles.push("build.sbt");
|
|
196
|
+
|
|
197
|
+
technologies.push({
|
|
198
|
+
name: "sbt",
|
|
199
|
+
confidence: 1.0,
|
|
200
|
+
source: "build.sbt",
|
|
201
|
+
category: "build",
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// No build tool found - check for source files
|
|
206
|
+
if (!buildTool) {
|
|
207
|
+
const javaFiles = findFiles(cwd, "**/*.java");
|
|
208
|
+
const ktFiles = findFiles(cwd, "**/*.kt");
|
|
209
|
+
const scalaFiles = findFiles(cwd, "**/*.scala");
|
|
210
|
+
|
|
211
|
+
if (javaFiles.length === 0 && ktFiles.length === 0 && scalaFiles.length === 0) {
|
|
212
|
+
return null;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (ktFiles.length > javaFiles.length) language = "Kotlin";
|
|
216
|
+
else if (scalaFiles.length > javaFiles.length) language = "Scala";
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Detect language from files
|
|
220
|
+
if (dirExists(join(cwd, "src/main/kotlin")) || findFiles(cwd, "**/*.kt").length > 0) {
|
|
221
|
+
if (language !== "Scala") language = "Kotlin";
|
|
222
|
+
}
|
|
223
|
+
if (dirExists(join(cwd, "src/main/scala")) || findFiles(cwd, "**/*.scala").length > 0) {
|
|
224
|
+
language = "Scala";
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Add language as runtime
|
|
228
|
+
technologies.push({
|
|
229
|
+
name: language,
|
|
230
|
+
confidence: 1.0,
|
|
231
|
+
source: "Source files",
|
|
232
|
+
category: "runtime",
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
// Web Framework Detection
|
|
236
|
+
const webFrameworks = [
|
|
237
|
+
{ deps: ["spring-boot", "spring-boot-starter-web", "org.springframework.boot:spring-boot-starter-web"], name: "Spring Boot", confidence: 1.0 },
|
|
238
|
+
{ deps: ["spring-webflux", "spring-boot-starter-webflux"], name: "Spring WebFlux", confidence: 1.0 },
|
|
239
|
+
{ deps: ["io.quarkus", "quarkus-core", "quarkus-resteasy"], name: "Quarkus", confidence: 1.0 },
|
|
240
|
+
{ deps: ["io.micronaut", "micronaut-core", "micronaut-http-server"], name: "Micronaut", confidence: 1.0 },
|
|
241
|
+
{ deps: ["io.ktor", "ktor-server-core", "ktor-server"], name: "Ktor", confidence: 1.0 },
|
|
242
|
+
{ deps: ["io.vertx", "vertx-core", "vertx-web"], name: "Vert.x", confidence: 1.0 },
|
|
243
|
+
{ deps: ["io.helidon", "helidon-webserver"], name: "Helidon", confidence: 1.0 },
|
|
244
|
+
{ deps: ["dropwizard", "io.dropwizard"], name: "Dropwizard", confidence: 1.0 },
|
|
245
|
+
{ deps: ["spark-core", "com.sparkjava:spark-core"], name: "Spark Java", confidence: 1.0 },
|
|
246
|
+
{ deps: ["javalin", "io.javalin"], name: "Javalin", confidence: 1.0 },
|
|
247
|
+
];
|
|
248
|
+
|
|
249
|
+
for (const fw of webFrameworks) {
|
|
250
|
+
const found = fw.deps.find(d => allDeps.has(d));
|
|
251
|
+
if (found) {
|
|
252
|
+
technologies.push({
|
|
253
|
+
name: fw.name,
|
|
254
|
+
confidence: fw.confidence,
|
|
255
|
+
source: `Dependency: ${found}`,
|
|
256
|
+
category: "backend",
|
|
257
|
+
});
|
|
258
|
+
break;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// ORM/Database Detection
|
|
263
|
+
const orms = [
|
|
264
|
+
{ deps: ["hibernate-core", "org.hibernate"], name: "Hibernate", confidence: 1.0 },
|
|
265
|
+
{ deps: ["spring-data-jpa", "spring-boot-starter-data-jpa"], name: "Spring Data JPA", confidence: 1.0 },
|
|
266
|
+
{ deps: ["mybatis", "org.mybatis"], name: "MyBatis", confidence: 1.0 },
|
|
267
|
+
{ deps: ["exposed", "org.jetbrains.exposed"], name: "Exposed", confidence: 1.0 },
|
|
268
|
+
{ deps: ["jooq", "org.jooq"], name: "jOOQ", confidence: 1.0 },
|
|
269
|
+
{ deps: ["ebean", "io.ebean"], name: "Ebean", confidence: 1.0 },
|
|
270
|
+
{ deps: ["jdbi", "org.jdbi"], name: "JDBI", confidence: 1.0 },
|
|
271
|
+
{ deps: ["slick", "com.typesafe.slick"], name: "Slick", confidence: 1.0 },
|
|
272
|
+
];
|
|
273
|
+
|
|
274
|
+
for (const orm of orms) {
|
|
275
|
+
const found = orm.deps.find(d => allDeps.has(d));
|
|
276
|
+
if (found) {
|
|
277
|
+
technologies.push({
|
|
278
|
+
name: orm.name,
|
|
279
|
+
confidence: orm.confidence,
|
|
280
|
+
source: `Dependency: ${found}`,
|
|
281
|
+
category: "orm",
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// Database Drivers
|
|
287
|
+
const dbDrivers = [
|
|
288
|
+
{ deps: ["postgresql", "org.postgresql"], name: "PostgreSQL", confidence: 0.9 },
|
|
289
|
+
{ deps: ["mysql-connector-java", "mysql-connector-j", "com.mysql"], name: "MySQL", confidence: 0.9 },
|
|
290
|
+
{ deps: ["h2", "com.h2database"], name: "H2", confidence: 0.9 },
|
|
291
|
+
{ deps: ["mongodb-driver", "org.mongodb"], name: "MongoDB", confidence: 0.9 },
|
|
292
|
+
{ deps: ["jedis", "lettuce-core", "redis.clients"], name: "Redis", confidence: 0.9 },
|
|
293
|
+
{ deps: ["elasticsearch-java", "elasticsearch-rest-client"], name: "Elasticsearch", confidence: 0.9 },
|
|
294
|
+
{ deps: ["cassandra-driver", "datastax"], name: "Cassandra", confidence: 0.9 },
|
|
295
|
+
];
|
|
296
|
+
|
|
297
|
+
const detectedDbs = new Set<string>();
|
|
298
|
+
for (const db of dbDrivers) {
|
|
299
|
+
const found = db.deps.find(d => allDeps.has(d));
|
|
300
|
+
if (found && !detectedDbs.has(db.name)) {
|
|
301
|
+
technologies.push({
|
|
302
|
+
name: db.name,
|
|
303
|
+
confidence: db.confidence,
|
|
304
|
+
source: `Dependency: ${found}`,
|
|
305
|
+
category: "database",
|
|
306
|
+
});
|
|
307
|
+
detectedDbs.add(db.name);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// Testing Frameworks
|
|
312
|
+
const testingTools = [
|
|
313
|
+
{ deps: ["junit-jupiter", "org.junit.jupiter", "junit"], name: "JUnit", confidence: 1.0 },
|
|
314
|
+
{ deps: ["testng", "org.testng"], name: "TestNG", confidence: 1.0 },
|
|
315
|
+
{ deps: ["kotest", "io.kotest"], name: "Kotest", confidence: 1.0 },
|
|
316
|
+
{ deps: ["mockito", "org.mockito"], name: "Mockito", confidence: 0.95 },
|
|
317
|
+
{ deps: ["mockk", "io.mockk"], name: "MockK", confidence: 0.95 },
|
|
318
|
+
{ deps: ["assertj", "org.assertj"], name: "AssertJ", confidence: 0.9 },
|
|
319
|
+
{ deps: ["spock", "org.spockframework"], name: "Spock", confidence: 1.0 },
|
|
320
|
+
{ deps: ["scalatest", "org.scalatest"], name: "ScalaTest", confidence: 1.0 },
|
|
321
|
+
];
|
|
322
|
+
|
|
323
|
+
for (const test of testingTools) {
|
|
324
|
+
const found = test.deps.find(d => allDeps.has(d));
|
|
325
|
+
if (found) {
|
|
326
|
+
technologies.push({
|
|
327
|
+
name: test.name,
|
|
328
|
+
confidence: test.confidence,
|
|
329
|
+
source: `Dependency: ${found}`,
|
|
330
|
+
category: "testing",
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// Security/Auth
|
|
336
|
+
const authTools = [
|
|
337
|
+
{ deps: ["spring-security", "spring-boot-starter-security"], name: "Spring Security", confidence: 1.0 },
|
|
338
|
+
{ deps: ["keycloak", "org.keycloak"], name: "Keycloak", confidence: 1.0 },
|
|
339
|
+
{ deps: ["nimbus-jose-jwt", "com.nimbusds"], name: "Nimbus JOSE JWT", confidence: 0.9 },
|
|
340
|
+
{ deps: ["java-jwt", "com.auth0:java-jwt"], name: "Auth0 JWT", confidence: 0.9 },
|
|
341
|
+
{ deps: ["pac4j", "org.pac4j"], name: "PAC4J", confidence: 1.0 },
|
|
342
|
+
];
|
|
343
|
+
|
|
344
|
+
for (const auth of authTools) {
|
|
345
|
+
const found = auth.deps.find(d => allDeps.has(d));
|
|
346
|
+
if (found) {
|
|
347
|
+
technologies.push({
|
|
348
|
+
name: auth.name,
|
|
349
|
+
confidence: auth.confidence,
|
|
350
|
+
source: `Dependency: ${found}`,
|
|
351
|
+
category: "auth",
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// DevOps/Observability
|
|
357
|
+
const devopsTools = [
|
|
358
|
+
{ deps: ["lombok", "org.projectlombok"], name: "Lombok", confidence: 0.9 },
|
|
359
|
+
{ deps: ["micrometer", "io.micrometer"], name: "Micrometer", confidence: 0.9 },
|
|
360
|
+
{ deps: ["spring-boot-actuator", "spring-boot-starter-actuator"], name: "Spring Actuator", confidence: 0.95 },
|
|
361
|
+
{ deps: ["logback", "ch.qos.logback"], name: "Logback", confidence: 0.85 },
|
|
362
|
+
{ deps: ["log4j", "org.apache.logging.log4j"], name: "Log4j", confidence: 0.85 },
|
|
363
|
+
{ deps: ["opentelemetry", "io.opentelemetry"], name: "OpenTelemetry", confidence: 0.9 },
|
|
364
|
+
];
|
|
365
|
+
|
|
366
|
+
for (const tool of devopsTools) {
|
|
367
|
+
const found = tool.deps.find(d => allDeps.has(d));
|
|
368
|
+
if (found) {
|
|
369
|
+
technologies.push({
|
|
370
|
+
name: tool.name,
|
|
371
|
+
confidence: tool.confidence,
|
|
372
|
+
source: `Dependency: ${found}`,
|
|
373
|
+
category: "devops",
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// Structure detection (standard Maven/Gradle layout)
|
|
379
|
+
const structurePaths = [
|
|
380
|
+
{ key: "main", paths: ["src/main/java", "src/main/kotlin", "src/main/scala"] },
|
|
381
|
+
{ key: "test", paths: ["src/test/java", "src/test/kotlin", "src/test/scala"] },
|
|
382
|
+
{ key: "resources", paths: ["src/main/resources"] },
|
|
383
|
+
{ key: "testResources", paths: ["src/test/resources"] },
|
|
384
|
+
{ key: "controllers", paths: ["src/main/java/controller", "src/main/java/controllers", "src/main/kotlin/controller"] },
|
|
385
|
+
{ key: "services", paths: ["src/main/java/service", "src/main/java/services", "src/main/kotlin/service"] },
|
|
386
|
+
{ key: "repository", paths: ["src/main/java/repository", "src/main/java/repositories", "src/main/kotlin/repository"] },
|
|
387
|
+
{ key: "models", paths: ["src/main/java/model", "src/main/java/models", "src/main/java/entity", "src/main/kotlin/model"] },
|
|
388
|
+
{ key: "config", paths: ["src/main/java/config", "src/main/java/configuration", "src/main/kotlin/config"] },
|
|
389
|
+
];
|
|
390
|
+
|
|
391
|
+
for (const { key, paths } of structurePaths) {
|
|
392
|
+
for (const p of paths) {
|
|
393
|
+
if (dirExists(join(cwd, p))) {
|
|
394
|
+
structure[key] = p;
|
|
395
|
+
break;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// Config files
|
|
401
|
+
const configPatterns = [
|
|
402
|
+
"application.properties",
|
|
403
|
+
"application.yml",
|
|
404
|
+
"application.yaml",
|
|
405
|
+
"application-dev.properties",
|
|
406
|
+
"application-dev.yml",
|
|
407
|
+
"bootstrap.properties",
|
|
408
|
+
"bootstrap.yml",
|
|
409
|
+
"lombok.config",
|
|
410
|
+
"checkstyle.xml",
|
|
411
|
+
"spotbugs.xml",
|
|
412
|
+
"Dockerfile",
|
|
413
|
+
"docker-compose.yml",
|
|
414
|
+
];
|
|
415
|
+
|
|
416
|
+
for (const pattern of configPatterns) {
|
|
417
|
+
if (fileExists(join(cwd, pattern)) || fileExists(join(cwd, "src/main/resources", pattern))) {
|
|
418
|
+
configFiles.push(pattern);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
return {
|
|
423
|
+
ecosystem: "java",
|
|
424
|
+
technologies,
|
|
425
|
+
structure,
|
|
426
|
+
configFiles: [...new Set(configFiles)],
|
|
427
|
+
};
|
|
428
|
+
},
|
|
429
|
+
};
|
|
430
|
+
|
|
431
|
+
// Register the detector
|
|
432
|
+
registerDetector(jvmDetector);
|
|
433
|
+
|
|
434
434
|
export default jvmDetector;
|