Skip to content

Add Agent gradle task to give all available plugin info including path #2106

@Haehnchen

Description

@Haehnchen

Describe the need of your request

I think most plugin developers running into issues that agents search for api and usages of plugins and intellij code to "understand" it. But they are mostly in the end starting to decompile files. All models starting to do that some faster then others.
Also depending on the knowledge cut they are trying to find also a way out of outdated apis.

That's why i have added a skill (https://github.com/Haehnchen/idea-php-symfony2-plugin/tree/master/.claude/skills/intellij-plugin-development/references) helping them way faster to use local development context.
Not optimal not directly coupled to the gradle deps, because they "grepping" around.

I noticed you have added printBundledPlugins printBundledModules, so am playing with a task for is. Maybe something like this can be integrated.

Proposed solution

                                                                                                                                                                                                           
● Format: type\tid\tname\tpath — one plugin per line. Examples:                                                                                                                                             
                                                                                                                                                                                                            
  # Decompile all compatible plugins with vineflower                                                                                                                                                        
  ./gradlew -q printPluginDependencies | grep "^compatible" | cut -f4 | \                                                                                                                                   
    xargs -I{} java -jar decompiled/vineflower.jar {} decompiled/                                                                                                                                           
                                                                                                                                                                                                            
  # Just get the ml.llm path                                                                                                                                                                                
  ./gradlew -q printPluginDependencies | grep "com.intellij.ml.llm" | cut -f4                                                                                                                               
                                                                                                                                                                                                            
  # All paths for xargs                                                                                                                                                                                     
  ./gradlew -q printPluginDependencies | cut -f4
// Helper: parse plugin.xml InputStream → (pluginId, pluginName)
fun parsePluginXml(stream: java.io.InputStream): Pair<String?, String?> {
    return try {
        val factory = DocumentBuilderFactory.newInstance().also {
            it.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false)
            it.isValidating = false
        }
        val doc = factory.newDocumentBuilder().parse(stream)
        val id = doc.getElementsByTagName("id").item(0)?.textContent?.trim()
        val name = doc.getElementsByTagName("name").item(0)?.textContent?.trim()
        id to name
    } catch (_: Exception) {
        null to null
    }
}

// Helper: find plugin.xml in JARs under plugin dir → (pluginId, pluginName)
fun readPluginXmlFromDir(pluginDir: File): Pair<String?, String?> {
    val libDir = pluginDir.resolve("lib").takeIf { it.exists() } ?: return null to null
    libDir.listFiles { f -> f.extension == "jar" }
        ?.sortedBy { it.name }
        ?.forEach { jar ->
            try {
                JarFile(jar).use { jf ->
                    val entry = jf.getJarEntry("META-INF/plugin.xml") ?: return@use
                    val (id, name) = parsePluginXml(jf.getInputStream(entry))
                    if (id != null) return id to name
                }
            } catch (_: Exception) {}
        }
    return null to null
}


// Print all plugin dependencies (bundled + compatible) with ID, name, and path
tasks.register("printPluginDependencies") {
    group = "intellij"
    description = "Print all bundled and compatible plugins with ID, name, and path"
    notCompatibleWithConfigurationCache("Reads plugin directories and resolves configurations at execution time")

    doLast {
        val extractedAttr = Attribute.of("intellijPlatformExtracted", Boolean::class.javaObjectType)

        // type\tid\tname\tpath  (tab-separated, pipe with -q flag)
        fun printPlugin(type: String, id: String?, name: String?, dir: File) =
            println("$type\t${id ?: ""}\t${name ?: ""}\t${dir.absolutePath}")

        intellijPlatform.platformPath.resolve("plugins").toFile()
            .listFiles()
            ?.filter { it.isDirectory }
            ?.mapNotNull { dir -> readPluginXmlFromDir(dir).let { (id, name) -> if (id != null) Triple(id, name, dir) else null } }
            ?.sortedBy { it.first }
            ?.forEach { (id, name, dir) -> printPlugin("bundled", id, name, dir) }

        configurations.getByName("intellijPlatformPluginDependency")
            .incoming
            .artifactView { attributes { attribute(extractedAttr, true) } }
            .files
            .sortedBy { it.name }
            .forEach { extractedDir ->
                val (id, name) = extractedDir.listFiles()
                    ?.filter { it.isDirectory }
                    ?.firstNotNullOfOrNull { subDir -> readPluginXmlFromDir(subDir).let { (i, n) -> if (i != null) i to n else null } }
                    ?: (null to null)
                printPlugin("compatible", id, name, extractedDir)
            }
    }
}

Alternatives you've considered

No response

Additional context

No response

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions