import { parametersSchema as z, defineCustomTool, CustomToolContext } from "@roo-code/types" // @ts-ignore - Node built-ins import { readFileSync } from "fs" // @ts-ignore - Node built-ins import path from "path" export default defineCustomTool({ name: "pom_inspect", description: "Parse a pom.xml and return structured JSON with groupId, artifactId, version, modules, properties, parent, and optionally dependencies. Saves reading raw XML just to extract Maven coordinates.", parameters: z.object({ pomPath: z.string().describe("Absolute or relative path to pom.xml"), includeDependencies: z.boolean().optional().describe("Include list in output (default: false)"), }), async execute({ pomPath, includeDependencies = false }, context: CustomToolContext) { try { // @ts-ignore - installed dependency const { XMLParser } = require("fast-xml-parser") // Resolve relative paths against the task's working directory // @ts-ignore - task.cwd exists at runtime const cwd = context?.task?.cwd ?? process.cwd() const absPath = path.isAbsolute(pomPath) ? pomPath : path.resolve(cwd, pomPath) const xml = readFileSync(absPath, "utf-8") const parser = new XMLParser({ ignoreAttributes: false, attributeNamePrefix: "@_", textNodeName: "#text", isArray: (name: string) => ["module", "dependency"].includes(name), }) const doc = parser.parse(xml) const project = doc.project if (!project) { return JSON.stringify({ error: "No root element found in POM" }, null, 2) } // Extract properties for placeholder resolution const properties: Record = {} if (project.properties) { for (const [key, val] of Object.entries(project.properties)) { if (typeof val === "string" || typeof val === "number") { properties[key] = String(val) } } } const resolvePlaceholder = (val: any): string | null => { if (val == null) return null let s = String(val) const match = s.match(/^\$\{(.+)\}$/) if (match) { const propKey = match[1] if (properties[propKey]) return properties[propKey] // Check project-level refs if (propKey === "project.version" && project.version) return String(project.version) if (propKey === "project.groupId" && project.groupId) return String(project.groupId) } return s } // Parent let parent = null if (project.parent) { parent = { groupId: String(project.parent.groupId || ""), artifactId: String(project.parent.artifactId || ""), version: resolvePlaceholder(project.parent.version), } } // Modules let modules: string[] = [] if (project.modules?.module) { modules = project.modules.module.map((m: any) => String(m)) } // Dependencies let dependencies: any[] | undefined = undefined if (includeDependencies && project.dependencies?.dependency) { dependencies = project.dependencies.dependency.map((d: any) => ({ groupId: String(d.groupId || ""), artifactId: String(d.artifactId || ""), version: resolvePlaceholder(d.version), scope: d.scope ? String(d.scope) : "compile", })) } // Also check dependencyManagement if (includeDependencies && project.dependencyManagement?.dependencies?.dependency) { const managed = project.dependencyManagement.dependencies.dependency.map((d: any) => ({ groupId: String(d.groupId || ""), artifactId: String(d.artifactId || ""), version: resolvePlaceholder(d.version), scope: d.scope ? String(d.scope) : "managed", })) if (!dependencies) dependencies = managed else dependencies = [...dependencies, ...managed] } const result: any = { groupId: resolvePlaceholder(project.groupId) || parent?.groupId || "", artifactId: String(project.artifactId || ""), version: resolvePlaceholder(project.version) || parent?.version || "", packaging: String(project.packaging || "jar"), name: project.name ? String(project.name) : null, parent, modules, properties, } if (includeDependencies && dependencies) { result.dependencies = dependencies } return JSON.stringify(result, null, 2) } catch (err: any) { return JSON.stringify({ error: err.message ?? String(err) }, null, 2) } }, })