agentsclimarketplace

Codelens jvm analysis

Skill charliek/codelens/skills/codelens-jvm-analysis

Use this skill to answer structural questions about Java/Kotlin (JVM) code — the "where is this used, what connects to what" questions that come up when refactoring, deleting code, or learning a codebase. Trigger it whenever someone asks: where is class X instantiated or referenced (the blast radius before deleting/refactoring it)? which classes implement this interface or extend this base class? what's a type's inheritance hierarchy? which classes carry an annotation like @RestController or @Component? what does this method call and how are things wired together? which types are most depended-on (the core/foundation classes)? Prefer this skill over grep or opening files one-by-one for such questions — it analyzes compiled JVM bytecode and catches relationships that plain-text search misses. Not for editing or reading source, debugging runtime errors/stack traces, git history/blame, formatting/linting, or explaining JVM concepts.From its SKILL.md

Install
npx -y skills add charliek/codelens --skill codelens-jvm-analysis

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

18.3 KB, as published. Nobody here has run it

JVM Analysis

This skill enables exploration and analysis of JVM codebases through bytecode scanning. Its primitives are framework-agnostic — they work the same on a Spring, Micronaut, Ratpack, Android, or plain-Java/Kotlin project.

When to Use

  • Find all classes in a package or matching a pattern
  • Search for methods by name, return type, or annotation
  • Find all implementations of an interface
  • Trace class inheritance hierarchies
  • See what invocations a method makes (calls), with constant arguments and line numbers
  • Find everything that references a type (xref) — callers, fields, params, subtypes, …
  • Analyze dependencies between classes, or the whole project's dependency graph
  • Find usages of a specific annotation

Prerequisites

Compile the target project first. CodeLens analyzes compiled bytecode under build/classes, not source files — so if the project hasn't been built, there is nothing for it to scan. Build it (skipping tests is fine and faster):

cd /path/to/project && ./gradlew build -x test   # or: ./gradlew classes testClasses

Then start the server:

codelens start --project /path/to/project

Note: The first start for a project may take several minutes (3-7 min for large projects) while the server resolves the Gradle classpath and downloads dependencies. Use codelens status to monitor — the server transitions through LOADING (scanning) to READY (fully available). Subsequent starts are fast due to cached dependencies.

Verify the scan actually found your code before analyzing. A project that wasn't compiled still starts cleanly and reports READY — it just finds zero project classes, so every query comes back empty and the emptiness looks like a real answer. Guard against that by checking the counts first:

codelens classes stats --json   # look at projectClassCount

If projectClassCount is 0, the project is almost certainly uncompiled (or you pointed at the wrong directory). codelens start/restart also print a warning: line to stderr in this case. To recover: build the project (command above), then re-scan without a full restart and re-check:

codelens refresh --project /path/to/project
codelens classes stats --json

Output: in a terminal, commands print human-readable tables. Pass --json to get the stable machine-readable payload — it is the documented shape and is also auto-selected when output is piped or captured. When parsing output (e.g. through jq), pass --json explicitly. Several examples below pipe JSON through jq — install it via your package manager if you want to follow those verbatim.

JDK setup and environment

codelens uses two JDKs independently — see JDK Resolution for the full design.

  • Server JVM runs codelens-server-all.jar. Auto-discovered: the newest installed JDK with major in [21, 25] across SDKMAN, mise, Homebrew, and the system JavaVMs / jvm dirs. Override with CODELENS_JAVA_HOME.
  • Project JVM runs the target project's Gradle daemon. Must be declared by the project (or pass --project-java to bypass).

Declaring the project JDK

Use any one of these in the project root (checked in this order):

.sdkmanrc                       java=21.0.9-amzn        # or 21-tem, 25-graal, just "21", etc.
.java-version                   21.0.9-amzn             # or just "21"
gradle.properties               org.gradle.java.home=/abs/path/to/jdk
.mise.toml | .tool-versions     java = "temurin-21.0.9" | java temurin-21.0.9

Discovery sources (in order)

JDKs are located across these sources for the project JVM resolution: SDKMAN (~/.sdkman/candidates/java) → Homebrew (openjdk@<major> keg) → JavaVMs (/Library/Java/JavaVirtualMachines/* on macOS, /usr/lib/jvm/* on Linux — catches Homebrew casks and DMG installers) → mise. If the exact declared version isn't installed, a same-major JDK is substituted and codelens prints a one-line note: to stderr explaining the swap (e.g. declared 21-tem → used installed 21.0.9-amzn). Cross-major substitution is never performed.

Common pitfalls

  • gradle.properties::org.gradle.java.home=/abs/path pointing at a deleted JDK → error names the path explicitly. Fix by updating the path or installing the JDK there.
  • Declaring a vendor you don't have installed (e.g. 21-tem with only Corretto 21) → resolves silently to the same-major install and prints the substitution note. If exact vendor matters, install it (sdk install java 21-tem, brew install --cask temurin).
  • Homebrew cask installs (brew install --cask temurin) ARE discoverable — they land under /Library/Java/JavaVirtualMachines/. If codelens claims a JDK is missing, run ls /Library/Java/JavaVirtualMachines/ to confirm what's actually there.

Useful env vars

VariablePurpose
CODELENS_JAVA_HOMEForce the JDK that runs the server
JAVA_HOMEFallback for the server JVM when nothing in range was found
CODELENS_JAVA_OPTSExtra JVM options (e.g. -Xmx4g), whitespace-split
CODELENS_SERVER_JARForce the server JAR path
CODELENS_REPO_PATHHint the codelens repo root for server/app/build/libs/... discovery
--project-java <path> flagBypass project-JDK declaration / resolution entirely

Class Discovery

List Classes

codelens classes list [options]

Filtering options:

  • --package <pattern> - Filter by package (supports wildcards)
  • --name <pattern> - Filter by class name
  • --annotation <fqn> - Classes annotated with specific annotation
  • --extends <fqn> - Classes extending a specific class
  • --implements <fqn> - Classes implementing a specific interface
  • --interfaces, -i - Show only interfaces
  • --include-libraries - Include library classes (project-only by default)

Examples:

# All classes in a package
codelens classes list --package com.example.handlers

# Find all @Singleton classes
codelens classes list --annotation javax.inject.Singleton

# Find classes implementing an interface
codelens classes list --implements com.example.api.RequestHandler

# Classes extending a base class
codelens classes list --extends com.example.AbstractService

View Class Details

codelens classes show <fully-qualified-class-name>

Shows:

  • Class type (class, interface, enum, annotation)
  • Modifiers (public, abstract, final)
  • Superclass and interfaces
  • Declared methods and fields
  • Annotations

Scan Statistics

codelens classes stats

Shows summary of scanned classes, methods, packages.

Method Search

codelens methods search [options]

Filtering options:

  • --name <pattern> - Method name pattern
  • --return-type <fqn> - Methods returning specific type
  • --annotation <fqn> - Methods with specific annotation
  • --class <fqn> - Methods in specific class
  • --package <pattern> - Methods in classes within package
  • --include-libraries - Include library methods

Examples:

# Find all methods named "handle"
codelens methods search --name handle

# Find methods returning a specific type
codelens methods search --return-type java.util.concurrent.CompletableFuture

# Find methods carrying an annotation
codelens methods search --annotation org.springframework.web.bind.annotation.GetMapping

# Methods in a specific class
codelens methods search --class com.example.UserService

Annotation attribute values are typed. Wherever classes show / methods search --json surface a class's or method's annotations, each entry in parameters is a typed value carrying a kind discriminator (STRING/BOOLEAN/INT/…/CLASS/ENUM/ANNOTATION/ARRAY): arrays are real arrays and enums, class literals, and nested annotations are tagged. Read them structurally — a multi-value attribute is .parameters.<attr>.items[].value, an enum is {kind:"ENUM", value, enumType}, a class literal is {kind:"CLASS", value:"<dotted-fqn>"} (no .class suffix) — rather than parsing stringified text. Absent optional fields are omitted.

Inheritance Analysis

Find Implementations

Find all classes implementing an interface or extending a class:

codelens classes implementations <fully-qualified-name>

Examples:

# Project classes implementing an interface (incl. transitive)
codelens classes implementations com.example.api.RequestHandler

# Project repositories extending a library interface
codelens classes implementations org.springframework.data.jpa.repository.JpaRepository

View Hierarchy

Trace the inheritance chain for a class:

codelens classes hierarchy <fully-qualified-name>

Shows parent classes and implemented interfaces up the chain.

Example:

codelens classes hierarchy com.example.MyHandler

Output:

com.example.MyHandler
  └── implements: com.example.api.RequestHandler
  └── extends: java.lang.Object

Dependency Analysis

Analyze class-level dependencies:

codelens classes dependencies <fully-qualified-name>

A single call returns both directions in one response: the JSON body has separate incoming and outgoing arrays. Use jq to pick the side you want.

Examples:

# Both directions in one call (default)
codelens classes dependencies com.example.UserService

# What does UserService depend on?
codelens classes dependencies com.example.UserService --json | jq '.outgoing'

# What depends on UserService?
codelens classes dependencies com.example.UserService --json | jq '.incoming'

Annotation Usages

Find every place an annotation is applied — across class, method, constructor, field, and parameter targets — with the matched annotation's typed attribute values inline:

codelens annotations usages <annotation-fqn> [--scope class|method|field|param|all] [--page N --size N] [--include-libraries]

--scope selects the declaration sites; the default is all. Each usage carries a target (CLASS/METHOD/CONSTRUCTOR/FIELD/PARAMETER), the member identity, and the matched annotation (typed parameters, same shape as on classes show). --scope method also surfaces constructors (as <init>, target=CONSTRUCTOR); for parameters you also get parameterIndex / parameterName / parameterType. The response is paginated (totalCount + countsByTarget cover the full result). Invalid scope or bad pagination is a usage error (exit 2).

Meta-expansion: matching is meta-expanded, like methods search --annotation — querying a meta-annotation (e.g. org.springframework.web.bind.annotation.RequestMapping) also matches @GetMapping/@PostMapping methods and returns the synthesized instance's attributes (e.g. method=[GET]). Note: source-retained annotations (@Override, Lombok, etc.) aren't in bytecode and never appear; and querying a meta-annotation under --scope class can also surface annotation-type declarations (library types, excluded unless --include-libraries).

Examples:

# Every @Singleton site (classes, provider methods, …) with attributes inline (default --scope all):
codelens annotations usages javax.inject.Singleton

# Every @Path resource method and its path value:
codelens annotations usages javax.ws.rs.Path --scope method --json \
  | jq -r '.usages[] | "\(.classSimpleName).\(.method)\t\(.annotation.parameters.value.value)"'

# Every field carrying a config annotation, with its key:
codelens annotations usages org.springframework.beans.factory.annotation.Value --scope field --json

Call Sites (forward)

Extract, straight from bytecode, every invocation a class's methods make — the forward view of "what does this code call":

codelens calls <fully-qualified-name> [--method <name>] [--descriptor <jvm-descriptor>] \
  [--in-methods-returning <fqn>] [--in-methods-annotated <fqn>]

Each call reports its ownerType (the callee's declaring type), methodName, descriptor, the constantArgs (LDC string/number/class literals passed near the call), and lineNumber. --method scopes to one method; --descriptor disambiguates overloads. --in-methods-returning <fqn> / --in-methods-annotated <fqn> keep only call-sites whose enclosing method returns the given type or carries the given annotation (meta-expanded) — ANDed when both are set, and they compose with --method. These are post-extraction filters over the enclosing method's declared signature, so they scope direct call-sites in matching methods, not lambda$… bodies or transitive callees.

Lambdas and method references appear as call sites with "invokeDynamic": true: there ownerType/methodName name the functional interface (SAM) being implemented, while implMethodOwner/implMethodName point at the implementation — a synthetic lambda$… body for a lambda, or the referenced method for a method reference. To read a lambda body, follow implMethodName: codelens calls <implMethodOwner> --method <implMethodName>.

Examples:

# Everything UserService's methods invoke
codelens calls com.example.UserService

# Just one method, e.g. to see the constant args it passes to a builder
codelens calls com.example.config.DbConfig --method dataSource

# Pull out the calls to a particular API
codelens calls com.example.UserService --method handle --json \
  | jq '.methods[].calls[] | select(.ownerType | startswith("java.sql"))'

# Only call-sites inside methods that return a given type — e.g. blocking calls
# sitting directly in reactive (Mono/Flux) handlers, without a manual intersection
codelens calls com.example.web.ReactiveController \
  --in-methods-returning reactor.core.publisher.Mono --json

Known limit (Tier-1 scan): computed (non-constant) arguments don't appear in constantArgs. (Lambda/method-reference targets are resolved — see the invokeDynamic call sites above.)

Cross-Reference (inverse)

Find everything across the project that references a type — the inverse of calls:

codelens xref <type-fqn> [--kind <KIND>] [--scope-implementing <fqn>] [--include-libraries]

References are grouped by kind — EXTENDS, IMPLEMENTS, FIELD, PARAM, RETURN, ANNOTATION, INSTANTIATION, CALL_RECEIVER — with countsByKind and countsByPackage aggregates so large fan-outs stay summarized. --scope-implementing intersects ("classes that implement X and reference Y").

References span generic type arguments, not just the container: a field/return/param of type List<Foo>, Mono<Foo>, or Repository<Foo, Long> is counted as a reference to Foo (under FIELD/RETURN/PARAM), and extends Base<Foo> / implements Marker<Foo> count as references to Foo (under EXTENDS/IMPLEMENTS). So xref com.example.Foo finds the type even where it only ever appears wrapped in a Promise<…> / Mono<…> / collection.

Examples:

# Who references this service, and how?
codelens xref com.example.UserService

# Only the classes that hold it as a field
codelens xref com.example.UserService --kind FIELD

# Who calls into the JDBC DataSource API (the blocking surface)?
codelens xref javax.sql.DataSource

# Who returns/holds a Reactor Mono (the reactive surface)?
codelens xref reactor.core.publisher.Mono

calls + xref are duals: use calls to see what a method does; use xref to see who depends on a type.

Project Dependency Graph

Beyond per-class dependencies, view the whole project's structure and its most depended-on classes:

# Most depended-on classes (high in-degree) — the project's "foundation"
codelens deps foundation [--min-dependents N]

# The full project dependency graph
codelens deps --format json
codelens deps --format dot -o deps.dot   # then: dot -Tpng deps.dot -o deps.png

deps foundation ranks classes by how many project classes depend on them — useful for finding shared/core types, refactoring targets, or a sensible order to tackle work.

Common Workflows

Understanding a New Codebase

# 1. Get overview statistics
codelens classes stats

# 2. List top-level packages
codelens classes list --package "com.example.*" | head -20

# 3. Find entry points (controllers, handlers) by their framework interface/annotation
codelens classes list --annotation org.springframework.web.bind.annotation.RestController

Finding Where Something is Used

# 1. Find implementations
codelens classes implementations com.example.UserRepository

# 2. Find incoming dependencies (one call returns both directions)
codelens classes dependencies com.example.UserRepository --json | jq '.incoming'

# 3. View the source (use codelens-source-lookup skill)
codelens source show com.example.UserRepositoryImpl

Mapping Annotations

# Find all Guice modules
codelens classes list --extends com.google.inject.AbstractModule

# Find all injectable services
codelens annotations usages javax.inject.Singleton

Tips

  • Use --include-libraries sparingly - it can return many results
  • Combine with codelens-source-lookup to view discovered classes
  • Pattern matching uses glob syntax (* for any characters)
  • Results are paginated by default; use --page and --size for large result sets

Related Skills

  • codelens-source-lookup - View source for discovered classes
  • codelens-ratpack-analysis - A worked example of framework-migration analysis built entirely on these primitives (calls/xref/deps)

Keep looking

Skills are one crate of 627,801. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.