From codelens
Use this skill for any request to examine, search, inventory, count, or map an existing Ratpack codebase, or to scope, size, order, or estimate the effort of migrating one off Ratpack onto another JVM framework (Spring, Micronaut, Helidon, etc.). It covers: finding route handlers and Chain routing, building a route inventory, measuring Promise/Blocking.get/fork/async usage, spotting handlers that block the compute thread, locating Guice modules and Registry bindings, and listing external integrations. Ratpack hides handlers and routes inside lambdas, invokedynamic, and bytecode that plain grep and generic search miss, so reach for this skill even when the request sounds like a simple "find the handlers," "count the Promise usage," or grep-style lookup over the code. Skip it for writing or scaffolding new Ratpack code, conceptual "how does Ratpack work" questions, and analyzing apps not built on Ratpack (plain Spring, Struts, etc.). It is a worked example of framework-specific analysis built entirely from CodeLens's general primitives — `classes`, `calls`, `xref`, `deps`, `methods`, `annotations`, `source` — with the Ratpack knowledge supplied by this skill's reference docs.
How this skill is triggered — by the user, by Claude, or both
Slash command
/codelens:codelens-ratpack-analysisThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
CodeLens deliberately ships no Ratpack-specific features. Instead it exposes a small set
CodeLens deliberately ships no Ratpack-specific features. Instead it exposes a small set of framework-agnostic primitives that return exhaustive bytecode facts. This skill is the worked example of turning those facts into a Ratpack migration assessment: you supply the Ratpack knowledge (which FQNs are handlers, what the Promise API looks like — see the reference docs), CodeLens supplies the data, and you supply the judgment (complexity, ordering, severity). Nothing here is a tool verdict; you read raw facts and explain your reasoning.
Build the project first — CodeLens scans compiled bytecode (build/classes), so an
uncompiled project scans to zero classes and every query comes back empty:
cd /path/to/ratpack-project && ./gradlew build -x test. Then:
codelens start --project /path/to/ratpack-project
The first start may take a few minutes while CodeLens resolves the Gradle classpath and
scans bytecode; codelens status reports LOADING → READY. Sanity-check with
codelens classes stats --json — a projectClassCount of 0 (also flagged by a
warning: line from start) means rebuild, then codelens refresh. In a terminal, commands
print human-readable tables; pass --json to get the stable machine-readable payload
(it is also auto-selected when output is piped or captured, as in tool execution). The
recipes below pass --json explicitly so they parse reliably with jq.
Read these reference docs for the Ratpack knowledge the recipes rely on — they hold the FQNs, handler shapes, anti-pattern catalog, and the complexity factors you weigh by hand:
references/RATPACK-CONCEPTS.md — handler types, the Promise/exec API, Chain routing, Guice.references/ANTIPATTERNS.md — what complicates a migration, how to spot it, how to fix it.references/COMPLEXITY-FACTORS.md — factors to weigh when judging effort and ordering.Ratpack request handlers implement known interfaces. Find them with implementations:
codelens classes implementations ratpack.handling.Handler
codelens classes implementations ratpack.func.Action # Action<Chain> route configurers
codelens classes implementations ratpack.groovy.handling.GroovyHandler
Inline/lambda handlers compile to invokedynamic and have no class of their own — find
them inside the chain that registers them (see Routes). Their bodies live in synthetic
lambda$… methods of the enclosing class, which calls resolves via implMethodName, so
you can read a lambda handler's body too. To classify a handler (see RATPACK-CONCEPTS.md
for the taxonomy) and judge its complexity, read its body:
codelens source show com.example.UserHandler # full source if available
codelens calls com.example.UserHandler --method handle # what it invokes, from bytecode
codelens calls com.example.ApiChain --method 'lambda$execute$0' # an inline lambda handler's body
calls is the key signal: it returns every invocation handle makes with constant
arguments and line numbers, so you see real Blocking.get / Promise / repository calls
rather than guessing from names.
An Action<Chain> builds the route table in its execute(Chain) method. calls extracts
those route registrations directly from bytecode:
codelens calls com.example.ApiChain --method execute --json
In the result, each route is a call whose ownerType is ratpack.handling.Chain and
whose methodName is a routing method (get, post, put, patch, delete, options,
head, all, prefix, path). The path is the STRING entry in that call's
constantArgs; a class-literal (CLASS) constant arg, when present, is the handler. For
example:
# Path + method for every route the chain registers:
codelens calls com.example.ApiChain --method execute --json \
| jq -r '.methods[].calls[]
| select(.ownerType=="ratpack.handling.Chain")
| "\(.methodName | ascii_upcase) " +
((.constantArgs[]? | select(.kind=="STRING") | .value) // "(no literal path)")'
prefix(path, …) nests a sub-chain. If the second arg is a class literal
(prefix("admin", AdminChain.class)), follow the CLASS constant arg by running calls on
that nested Action<Chain> and prepend the prefix path. If it's an inline lambda
(prefix("admin", c -> { … })), the sub-chain lives in a synthetic lambda$… method —
resolve it like any inline handler (below) and recurse into it, since it registers its own
routes (which may themselves use inline lambdas). To preview the target shape, map each
METHOD path to the destination framework's annotation by hand (e.g. GET /users/:id →
Spring @GetMapping("/users/{id}")).
Inline lambda handlers (chain.post(ctx -> …)) resolve too. The lambda is an
"invokeDynamic": true call site, and the reliable pairing signal is position: the indy
site sits immediately before the route call it feeds and shares its lineNumber. Its
implMethodName (e.g. lambda$execute$0) names the handler body, which is its own method —
read it with another calls. So walk the call list in program order: a route call
(ownerType == "ratpack.handling.Chain") whose handler was inline is preceded by an indy
site on the same line; pair them, then read the body:
# Pair adjacent indy→route entries WITHIN one method (the indy handler and its Chain route):
codelens calls com.example.ApiChain --method execute --json \
| jq -r '.methods[].calls as $c | range(0; ($c|length)-1) as $i
| select($c[$i].invokeDynamic and $c[$i+1].ownerType=="ratpack.handling.Chain")
| "\($c[$i+1].methodName | ascii_upcase)\t\($c[$i].implMethodName)"'
# Read an inline handler's body (or a prefix's sub-chain), then recurse if it registers routes:
codelens calls com.example.ApiChain --method 'lambda$execute$0'
Treat that jq as a per-method starting point, not the whole answer: it pairs sites within
a single method, so run it again on each lambda$… sub-chain to cover nested routes, and
confirm pairings against the raw calls output (the indy and its route share a lineNumber).
Known limit: computed (non-literal) paths show no string constant. (Lambda and method-reference handlers are resolved — via the
invokeDynamiccall site'simplMethodName, as above.)
Promise-heavy code is usually the hardest to migrate. Three complementary lenses:
# Methods whose signature returns a Promise:
codelens methods search --return-type ratpack.exec.Promise
# Every project class that touches the blocking/exec API (who, and where):
codelens xref ratpack.exec.Blocking
codelens xref ratpack.exec.Execution
codelens xref ratpack.exec.Promise
# Which domain types flow through promises? xref spans type arguments, so a type
# returned as Promise<Order> (or held as Promise<List<Order>>) is found here too:
codelens xref com.example.model.Order --kind RETURN
# Exactly what a class's method does with promises (operators, Blocking.get, fork):
codelens calls com.example.UserHandler --method handle --json \
| jq '.methods[].calls[] | select(.ownerType | test("ratpack.exec"))'
# Or scope across ALL Promise-returning methods of a class at once (one query,
# no manual methods-search × calls intersection):
codelens calls com.example.OrderService --in-methods-returning ratpack.exec.Promise --json \
| jq '.methods[].calls[] | select(.ownerType | test("ratpack.exec"))'
xref of ratpack.exec.Promise finds every method that returns or takes a Promise<…>;
because references span type arguments, xref of a domain type also surfaces it where it
only ever appears wrapped in a Promise<…>. xref of ratpack.exec.Blocking returns each
reference as a CALL_RECEIVER with the method (get/on) and line number — the real
blocking call sites. Judge intensity by counting these and reading the chains in source.
See RATPACK-CONCEPTS.md for the operator/blocking taxonomy.
calls --in-methods-returning ratpack.exec.Promise scopes call-sites to a class's
Promise-returning methods in one pass (and --in-methods-annotated <fqn> by annotation) — but
only direct call-sites, by the enclosing method's declared signature. It does not reach the
synthetic lambda$execute$N handler bodies where Ratpack hides most routing/Promise logic (read
those with --method 'lambda$…', as above) nor transitive callees.
# Modules:
codelens classes implementations com.google.inject.AbstractModule
codelens classes implementations com.google.inject.Module
# Provider methods and their constant-arg construction:
codelens methods search --annotation com.google.inject.Provides
codelens calls com.example.AppModule --method configure # bind(...).to(...) calls
# Constructor / field / method injection sites in one call (#43). @Inject usually sits on the
# constructor (target=CONSTRUCTOR, <init> + a derived param-type signature); --scope all also
# catches field/method injection. Pair with @Singleton-scoped @Provides methods (--scope method).
codelens annotations usages javax.inject.Inject --scope all
codelens annotations usages com.google.inject.Singleton --scope method # @Provides @Singleton factories
# Where a given type is bound / who depends on it:
codelens xref com.example.UserRepository
calls … --method configure surfaces the bind/to/toInstance invocations (with any
class-literal/string constants); xref <type> shows everywhere a bound type is used.
There is no integration detector — xref a library type and classify it yourself using
the catalog in ANTIPATTERNS.md / RATPACK-CONCEPTS.md. Examples:
codelens xref ratpack.http.client.HttpClient # Ratpack HTTP client
codelens xref software.amazon.awssdk.services.dynamodb.DynamoDbClient
codelens xref org.apache.kafka.clients.producer.KafkaProducer
codelens xref javax.sql.DataSource # JDBC
Each result lists the project classes (and members/line numbers) that reference the type —
your integration inventory, grouped by countsByKind and countsByPackage.
ANTIPATTERNS.md is the catalog (what each is, how to confirm, how to fix). Surface
candidates with xref / calls, then read context with source and judge:
# Blocking I/O that may sit on the compute thread:
codelens xref java.sql.Connection
codelens xref java.net.HttpURLConnection
codelens xref java.io.FileInputStream
# Thread.sleep and console logging in a class body:
codelens calls com.example.SlowHandler --json \
| jq '.methods[].calls[]
| select((.ownerType=="java.lang.Thread" and .methodName=="sleep")
or (.ownerType=="java.io.PrintStream" and (.methodName=="println" or .methodName=="print")))'
Whether a java.sql.* call is an anti-pattern depends on whether it runs inside
Blocking.get — confirm by reading the method body (source) or its calls ordering.
The tool finds candidates; you make the call.
Derive the inputs from the recipes above plus the dependency graph; then rank and explain
using COMPLEXITY-FACTORS.md as guidance (these are factors to weigh, not a formula):
# Foundation classes — most depended-on; usually migrate first:
codelens deps foundation
# Full project dependency graph (json or dot for visualization):
codelens deps --format dot -o deps.dot
# Per-class blast radius:
codelens classes dependencies com.example.UserService --json | jq '.incoming | length'
A reasonable ordering: migrate high-in-degree foundation classes first, then handlers in
increasing complexity (size from source, async intensity from the Promise lenses,
integration count from xref, anti-pattern count). Show the factors behind each ranking
rather than a single opaque score.
classes stats; handlers via implementations; routes via calls.methods search / xref / calls); anti-pattern
xref/calls + source.deps foundation and deps for the graph; Guice modules.COMPLEXITY-FACTORS.md, foundation-first, and
justify the order from the collected facts.codelens-jvm-analysis — the general primitives this skill builds on (calls, xref, deps, …).codelens-source-lookup — read handler/service bodies and library/JDK source.Provides UI/UX resources: 50+ styles, color palettes, font pairings, guidelines, charts for web/mobile across React, Next.js, Vue, Svelte, Tailwind, React Native, Flutter. Aids planning, building, reviewing interfaces.
Fetches up-to-date documentation from Context7 for libraries and frameworks like React, Next.js, Prisma. Use for setup questions, API references, and code examples.
npx claudepluginhub charliek/codelens --plugin codelens