Up and running in
five minutes.

Bring your Spring Boot app. Add one dependency, copy one file, and ask Claude to dig in. No restarts. No boilerplate.

☕ Java 17+ 🍃 Spring Boot 3.x / 4.x 🤖 AI agent (Claude Code, ECA, …) 🍺 bbin ⚡ clj-nrepl-eval
Just want to try it first?

Run this against any Spring Boot app already running on your machine — no dependency, no code changes, no restart. Great for a quick look before committing to the full install.

jshell
# then at the jshell prompt, paste:
/open https://raw.githubusercontent.com/brdloush/livewire/refs/heads/main/attach.jsh

⚠️ Java 21+: start your app with -XX:+EnableDynamicAgentLoading to allow dynamic agent injection. Once attached you get info(), beans(), eval(), sql(), and demo() at the jshell prompt.
For repeated use, full SQL tracing, and the query-watcher, follow the steps below — the dependency install is the recommended path.

1

Add Livewire to your project

Three small changes: the Maven dependency, one property to enable it locally, and the agent skill file that tells your AI agent exactly what Livewire can do.

pom.xml

Add Livewire as a dependency. Scope it to your dev profile so it never ships to production.

<dependency>
  <groupId>net.brdloush</groupId>
  <artifactId>livewire</artifactId>
  <version>0.12.0</version>
</dependency>
application-local.yml (or .properties)

Enable Livewire in your local-only config. Don't commit this file — it should already be in your .gitignore.

# application-local.yml
livewire:
  enabled: true
  # nrepl.port: 7888  # optional
Install clj-nrepl-eval

The Livewire skill uses clojure-mcp-light's clj-nrepl-eval CLI to talk to the nREPL server. Install it once via bbin (Babashka's binary installer):

# 1. Install bbin itself (once)
brew install babashka/brew/bbin

# 2. Install clj-nrepl-eval (once)
bbin install https://github.com/bhauman/clojure-mcp-light.git \
  --tag v0.2.1 \
  --as clj-nrepl-eval \
  --main-opts '["-m" "clojure-mcp-light.nrepl-eval"]'
Copy the skill into your project

The skill is the agent's cheat-sheet for Livewire's full API. It ships as a directory with three parts: SKILL.md (the lean core reference), bin/ (shell wrapper scripts like lw-start, lw-call-endpoint, lw-jpa-query, and more), and references/ (topic-specific deep-dives the agent loads on demand). Copy or symlink the whole directory.

The skill is designed for agents that support slash-command skills (such as Claude Code or ECA). Place the whole directory at .claude/skills/livewire/ — both tools pick it up from there.

# first, clone the livewire repo (just for the skill files)
git clone https://github.com/brdloush/livewire

# copy the skill directory (SKILL.md + bin/ scripts)
cp -r livewire/skills/livewire <your-project>/.claude/skills/livewire

# or symlink it — git pull to stay up to date
ln -s /path/to/livewire/skills/livewire \
      <your-project>/.claude/skills/livewire
2

Start your Spring Boot app

Start it with the local profile active so Livewire picks up the property you set. The nREPL server starts automatically — look for this line in the logs:

INFO  LivewireBootstrapBean — Livewire nREPL server started on port 7888
Example — bloated-shelf

The bloated-shelf sample app is a Spring Boot library API with 200 books, 30 authors, 50 members, and N+1 problems deliberately baked in. It's the canonical Livewire sandbox.

cd bloated-shelf
mvn spring-boot:run \
  -Dspring-boot.run.profiles=dev,seed,local
3

Open your AI agent and load the skill

Open your agent in your project directory. Then load the Livewire skill — this primes it with the full API reference and the right workflow for exploring a live Spring app.

Works with any agent that supports slash-command skills — including Claude Code, ECA, and others. Once the skill file is in .claude/skills/livewire/, load it with:

/livewire

Type this slash command in the agent chat. It will confirm the skill has loaded, then connect to the running nREPL on port 7888. You're live.

4

Ask questions in plain English

No Clojure knowledge required. Just describe what you want to know about your running app — Claude handles the REPL calls and presents the results. Here are some prompts to get you started.

Explore

Orient yourself in an unfamiliar codebase

What entities does this app have? Describe their relationships and fetch strategies.

Claude calls intro/list-entities and intro/inspect-entity on each one, then summarises the full schema — table names, column mappings, associations, and fetch types — without reading a single source file.

Explore

Map all endpoints and their access rules

List all HTTP endpoints. For each one, show the required roles and what parameters it expects.

intro/list-endpoints returns every route with its @PreAuthorize expression, plus resolved :required-roles / :required-authorities vectors so you can construct a lw/run-as call mechanically. Each parameter carries :source (:path, :query, :body, :header), :required, and :default-value when applicable.

Explore

Identify tightly coupled beans

Which beans have the most dependencies? Are there any that look like they could be split up?

Claude calls lw/all-bean-deps to get the full wiring graph, pre-filtered to your own application beans (auto-detected via @SpringBootApplication — no configuration needed). It sorts by dependency count to surface high-fan-out candidates. In bloated-shelf this immediately surfaces gottaInjectThemAllService — a service that injects every single repository in the app. Its name is the punchline; the wiring graph is the proof. Claude also spots architectureBreakingController on the high-dependents side of bookRepository — a controller wiring a repository directly, bypassing the service layer entirely.

Explore

Audit transactional boundaries

Give me the full @Transactional map of the app. Which methods are read-only and which ones write? Are there any surprising configurations?

Claude calls lw/all-bean-tx to map the effective @Transactional configuration of every app bean at runtime — propagation, isolation, read-only flag, rollback rules. No source reading needed. In a well-configured app like bloated-shelf, every get* and find* method comes back read-only: true, and archiveBook stands out as the only write path — exactly one method with read-only: false. The configuration validates cleanly. In a less careful app, this call surfaces the surprises.

Diagnose

Count exactly how many queries an endpoint fires

How many SQL queries does GET /api/books actually execute? Show me the breakdown.

Claude wraps the controller call in trace/trace-sql and reports the exact count with caller attribution. In bloated-shelf the answer is 1,201 queries for 200 books — a number that makes the problem undeniable.

Diagnose

Pinpoint the N+1 suspects

Find the N+1 query problem on the books endpoint. Which queries are repeating?

trace/detect-n+1 groups repeated query templates and flags the worst offenders with counts. Claude names both the query and the likely association that's causing it.

Query

Compare efficient vs. naive endpoints

How many queries does GET /api/admin/most-loaned fire compared to GET /api/books?

The contrast is stark: the admin aggregate fires 1 query via a single GROUP BY JOIN, while the books list fires over a thousand. Side-by-side, it makes a compelling case for the fix.

Query

Ask a business question without writing code

Which genres have the most books in this library? And which ones are borrowed the most?

Claude uses jpa/jpa-query to answer both questions with JPQL aggregations — no raw SQL, no schema guessing. It first runs intro/inspect-entity to discover that the many-to-many lives on Book.genres (not on Genre), then traverses from the Book side correctly. Results come back as named maps: Historical Fiction leads in book count (35), Poetry leads in loans (74).

Query

Surface operational data on the spot

Are there any overdue loans right now? Show me the member name, book title, and how many days overdue.

Claude writes and runs a JPQL query against the live EntityManager — traversing LoanRecord → member.fullName and LoanRecord → book.title by property path, no column name guessing required. In bloated-shelf the answer is 104 overdue loans, ranked oldest first. The kind of operational insight that would normally mean a new repository method, a recompile, and a restart.

Query

Rank data with aggregations

Which authors have the highest average review rating? Show top 10.

Claude writes a JPQL aggregation — AVG(r.rating) grouped by author, joined through Author → books → reviews — and executes it via jpa/jpa-query. Results come back as named Clojure maps with review counts alongside the average, so you can spot authors with suspiciously high ratings from only a handful of reviews. Pure ad-hoc analysis — no Spring code touched.

Query

Run a JPQL query and get readable results

Show me the 5 most-borrowed books using JPQL. Include title and loan count.

Claude uses jpa/jpa-query to execute JPQL directly via the live EntityManager and gets back plain Clojure maps — not opaque Java objects. Scalar projections with AS aliases become named keys. Lazy associations stay "<lazy>" so no surprise queries fire.

Explore

Call a secured endpoint and see exactly what it returns

Call the GET /api/books endpoint as a MEMBER and show me what it returns. How large is the response?

lw-call-endpoint invokes the controller method under a live Spring SecurityContext and serializes the result with the same Jackson ObjectMapper the HTTP layer uses — output is identical to what a real HTTP client would receive. The response metadata tells you both the row count and the raw and gzip-compressed byte size of the full payload.

Diagnose

Find out exactly what a service method changes — without touching the database

I suspect .archiveBook is modifying more fields than it should on Book 100. Show me exactly what it changes, but don't actually persist anything.

Claude calls q/diff-entity, which runs the service method inside a transaction that is always rolled back. It captures the entity state before and after the call and returns a structured :changed map — only the keys that differ, with old and new values side by side. The database is never touched.

Explore

Map the full domain model in one shot

Give me a full overview of all entities, their tables, columns, and how they relate to each other. Draw an ASCII ER diagram.

Claude calls intro/inspect-all-entities once to get the complete Hibernate metamodel — every entity, every column, every relation — and builds the diagram from that. One round-trip instead of one per entity.

Fix

Fix the N+1 live, without restarting

Fix the N+1 on GET /api/books/by-genre/{genreId}. Don't restart the app — hot-swap the query, verify it worked, then write the fix to source.

The payoff. Claude uses hq/hot-swap-query! to patch findByGenreId live — adding JOIN FETCH for author, genres, reviews, and reviewer — re-runs trace/trace-sql to confirm the query count drops from 123 → 1, writes the fix to BookRepository.java, then calls hq/reset-all!. Zero restarts.

Fix

Write an integration test with no sample data

I want to write an integration test for BookService#getReviewsForBook. The database is empty — can you generate realistic test data and write the test?

Claude runs lw-build-test-recipe to build a full Review → Book → Author and LibraryMember graph, captures every generated scalar value (names, rating, comment, dates) into a recipe map, then prototypes the service call in the REPL to validate the happy path — before writing a single line of Java. The final test uses the exact faker-generated values for setup and assertions, so nothing is invented.

Explore

Know what breaks before you change it

I'm about to optimise the query in adminService/getSystemStats. What HTTP endpoints and scheduled jobs call it — directly or via other services?

Claude calls cg/blast-radius, which walks the live bytecode upward from the target method and cross-references every caller against registered HTTP handlers and @Scheduled jobs. For this method it surfaces both GET /api/admin/stats and a nightly @Scheduled reporter running at 0 0 2 * * * — a cron job you might never have thought to connect to an AdminService change. The call-graph index is built once and cached for the rest of the session.

Explore

Spot an architecture violation in the wiring graph

Are there any controllers that bypass the service layer and wire directly to repositories?

Claude calls lw/all-bean-deps, filters for controllers with repository dependencies, and surfaces the violation immediately — no source reading required. In bloated-shelf this reveals architectureBreakingController injecting bookRepository directly, bypassing the service layer entirely. The name makes the intent obvious; the wiring graph makes it provable.

Explore

Find methods with no callers before a refactor

Which public methods on bookService have no known callers? Are any truly dead, or just called internally?

Claude calls cg/dead-methods, which checks every public method against the full blast-radius index. Results split into :dead (no callers anywhere — delete candidates) and :internal-only (called only by sibling methods — public by accident, refactoring candidates). Automatically warns if messaging beans or db-scheduler tasks are present, since their callers are not statically visible.

Explore

Map which deps each method actually uses

For each method on adminService, which of its injected dependencies does it actually touch? Which deps appear in the most methods?

Claude calls cg/method-dep-map, which walks the bean's bytecode and returns the exact dependency footprint per method. The :dep-frequency ranking immediately shows load-bearing deps (used by many methods) vs. extraction candidates (used by only one). With :intra-calls? and :callers?, the full internal call graph is visible in one shot — no source reading required.

Explore

See what @Query methods the watcher is monitoring

Which @Query methods is the query watcher currently tracking? Are any queries hot-swapped right now?

Claude calls qw/status to list every @Query method the watcher knows about — their current JPQL as it sits on disk — and hq/list-swapped to check whether any are currently patched. In bloated-shelf the watcher tracks 7 queries across 4 repositories. A good orientation call before any hot-swap session: confirms the baseline and ensures nothing is unexpectedly already swapped.

Dig deeper

Once you're comfortable with the basics, the full skill reference covers every function, pitfall, and edge case.

📖

Full skill reference

The SKILL.md you copied into your project is the authoritative API reference — every namespace, every function signature, known pitfalls, and worked examples. Read it once; Claude reads it every session.

⚠️

A word of caution

Livewire gives Claude (and you) full code execution power inside the JVM. Use anonymized seed data — never point it at a database with real user data. And keep livewire.enabled out of any profile that ships to staging or production.

← Back to overview GitHub ↗ bloated-shelf ↗