⚡ Dev only  ·  You've been warned

Stop guessing.
Ask the live Spring app.

Livewire embeds a Clojure nREPL server inside your running Spring Boot app — giving your AI agent a live, stateful probe into the JVM. Beans, queries, transactions, security context, and all.

Recorded live against bloated-shelf — from first connection to confirmed fix, zero restarts.

The restart tax is killing your loop

Modern Spring Boot development has a fundamental feedback problem. And AI agents make it worse.

edit restart (30–120s) observe repeat
edit restart (30–120s) observe repeat ⚡ zero restarts

You and your agent deserve better.

🤖

Agents guess. They don't ask.

AI coding agents reason statically about your Spring app. They read the code, form a hypothesis, and apply a fix — but they can't observe the running system. They're flying blind every time.

⏱️

Restarts destroy state and time

Every restart costs 30 seconds to 2 minutes and destroys all in-memory state accumulated during a debugging session. You lose context, momentum, and time.

Livewire closes the gap

With a live nREPL wired into the JVM, your agent can probe beans, run queries, trace SQL, and hot-swap fixes — all against the actual running app. Hypotheses are tested in seconds, not minutes.

🔁

The loop you actually want

observe hypothesise hot-swap verify recompile

Recompile and the watcher applies your @Query changes live — no REPL call needed.

One wire. Everything inside.

Livewire auto-configures when livewire.enabled=true is set. No code changes. No Spring profiles to configure. Just a JAR dependency.

Spring Boot App (running)
  └── nREPL server (embedded, dev only · port 7888)
      └── Livewire namespaces
            ├── core   · beans, transactions, run-as
            ├── trace  · SQL tracing, N+1 detection
            ├── query  · raw SQL execution
            ├── jpa-query · JPQL with smart entity serialization
            ├── mvc   · controller probing, JSON serialization
            ├── introspect · entities, endpoints, schema
            ├── hot-queries · live @Query swap
            ├── query-watcher · auto-apply @Query on recompile
            ├── faker  · realistic test entity generation
            └── callgraph · blast radius, dead methods, dep fingerprinting

AI agent (Claude Code, ECA, …) → clj-nrepl-eval nREPL live app

Everything you need to probe a live Spring app

A growing suite of namespaces. Require and go.

🔍

Live Bean & Property Inspection

Query any Spring bean, map the full dependency wiring graph, and inspect @Transactional boundaries — propagation, isolation, and read-only flags for every method. Find coupling smells and transaction misconfigurations in one call. All against the running app, no source reading required.

;; wiring graph — auto-filtered to your own classes
(lw/all-bean-deps)

;; @Transactional surface of all app beans
(lw/all-bean-tx)
;; => [{:bean "bookService" :methods [{:method "archiveBook" :read-only false …}  …]}  …]
🗄️

SQL & JPQL Execution

Run JPQL via the live EntityManager or raw SQL through the DataSource — with Hibernate type converters, active @Filters, and real transaction semantics. Prefer JPQL for entity data; raw SQL for DB-level metadata (indexes, constraints).

;; JPQL — no schema prefix, entity property names, paged by default
(jpa/jpa-query
  "SELECT b.title AS title, COUNT(lr) AS loans
   FROM Book b JOIN b.loanRecords lr
   GROUP BY b.id, b.title
   ORDER BY COUNT(lr) DESC"
  :page 0 :page-size 5)
;; => [{:title "The Green Bay Tree",          :loans 7}
;;     {:title "Let Us Now Praise Famous Men", :loans 7}
;;     {:title "Vanity Fair",                  :loans 7} ...]
🔬

SQL Tracing & N+1 Detection

Wrap any call with trace-sql and see every SQL it fires. detect-n+1 automatically groups repeated queries and flags suspects — in one call.

(trace/detect-n+1
  (trace/trace-sql
    (lw/in-readonly-tx
      (.getAllBooks (lw/bean "bookService")))))
;; => {:suspicious-queries
;;      [{:sql "select ... from book_genre ...", :count 200}
;;       {:sql "select ... from review ...",     :count 200}]
;;     :total-queries 481, :duration-ms 226}
🔐

run-as — Call Secured Beans Directly

Set a Spring SecurityContext for the duration of a REPL call. Call @PreAuthorize-guarded controllers and services directly — no more bypassing to the service layer.

(lw/run-as ["alice" "ROLE_MEMBER"]
  (.getBookById
    (lw/bean "bookController")
    (long 25)))
🔥

Live @Query Hot-Swap

Replace any Spring Data JPA @Query live — no restart. Fix it, verify it, write it back to source. Works by patching three internal layers of Spring Data JPA + Hibernate in concert.

(hq/hot-swap-query!
  "bookRepository"
  "findAllWithAuthorAndGenres"
  "SELECT DISTINCT b FROM Book b
   JOIN FETCH b.author
   LEFT JOIN FETCH b.genres
   LEFT JOIN FETCH b.loanRecords")
;; [hot-queries] hot-swapped ✓
🏗️

Hibernate Entity Introspection

Inspect any entity's live Hibernate metamodel: table name, column mappings, relation definitions, fetch strategies. No annotation parsing — the live metamodel is the source of truth. Use inspect-entity for one entity or inspect-all-entities to get the full domain model in a single call — perfect for agents building ER diagrams.

(intro/inspect-all-entities)
;; => {"Book"   {:table-name "book", :identifier {...}, :properties [...]}
;;     "Author" {:table-name "author", ...}
;;     ...}
🗺️

HTTP Endpoint Introspection

Discover every registered route with its auth requirements, parameter sources, and required/optional flags — all from the live RequestMappingHandlerMapping. Resolved :required-roles and :required-authorities let you construct a lw/run-as call mechanically, without parsing the raw SpEL string.

(intro/list-endpoints)
;; => [{:paths ["/api/books/{id}"], :methods ["GET"],
;;      :required-roles ["MEMBER"],
;;      :parameters [{:source :path, :required true,
;;                    :type "java.lang.Long"}]}
;;     ...]
👁️

Query Watcher — Auto-apply on Recompile

Livewire watches your compiled output directory and automatically applies any changed @Query JPQL to the live JVM within 500 ms of a recompile — no REPL call needed. Last one wins: a recompile overrides a REPL swap, and a new REPL swap overrides the last recompile.

;; just recompile — watcher applies the change
;; [query-watcher] detected change
;; bookRepository#findByIdWithDetails
;; [hot-queries] watcher re-swapped ✓

(qw/status)
;; => {:running? true, :disk-state-size 8}
🧩

JPQL with Smart Entity Serialization

Run any JPQL query via the live EntityManager and get back plain Clojure maps — no .toString() surprises. Lazy collections render as "<lazy>" rather than firing surprise queries. Scalar projections (SELECT expr, …) are unpacked into maps using AS aliases or positional :col0, :col1 keys. Paged by default.

(jpa/jpa-query
  "SELECT b.title AS title, COUNT(lr) AS loans
   FROM Book b JOIN b.loanRecords lr
   GROUP BY b.id, b.title
   ORDER BY COUNT(lr) DESC"
  :page 0 :page-size 5)
;; => [{:title "Vanity Fair",  :loans 7}
;;     {:title "The Green Bay Tree", :loans 7} ...]
🎯

Controller Endpoint Probing

Call any Spring bean method under a live SecurityContext and get back proper JSON — serialized with the exact same Jackson ObjectMapper Spring MVC uses. List results are capped at 20 by default and returned with {:total :returned :content-size :content-size-gzip} metadata so you always know the full picture.

lw-call-endpoint bookController getBooks ROLE_MEMBER
;; ^{:total 200, :returned 20,
;;   :content-size 515299, :content-size-gzip 82995}
;; [{"id" 1, "title" "All the King's Men",
;;   "author" {"id" 6, ...}, "genres" [...]} ...]

;; or from the REPL:
(mvc/serialize
  (lw/run-as ["u" "ROLE_MEMBER"]
    (.getBooks (lw/bean "bookController")))
  :limit 3)
🔎

Mutation Observer — what did that service call write?

Snapshot an entity before and after calling a service method. The thunk runs in a transaction that always rolls back — the database is never touched — but the diff captures exactly what changed.

Useful for discovering unintended writes, verifying a fix changed exactly the right fields, or systematically calling suspect service methods until the guilty one confesses.

(q/diff-entity "Book" 100
  (fn [] (.archiveBook (lw/bean "bookService") 100)))
;; => {:before {:archived false, :archivedAt nil, ...}
;;     :after  {:archived true,  :archivedAt "2026-03-28T16:16:20", ...}
;;     :changed {:archived   [false true]
;;               :archivedAt [nil "2026-03-28T16:16:20"]}}
🗺️

Method-level Dependency Fingerprinting

For each method on a bean, see exactly which injected dependencies it touches in bytecode. :dep-frequency ranks deps by how many methods use them — count-1 deps are prime extraction candidates. :intra-calls? adds which siblings each method calls; :callers? adds the inverse. Kotlin $default synthetic call variants resolved automatically.

(cg/method-dep-map "adminService")
;; :methods    [{:method "getSystemStats"
;;               :deps ["authorRepository" "bookRepository" ...]
;;               :orchestrator? false} ...]
;; :dep-frequency [{:dep "bookRepository"
;;                  :used-by-count 2} ...]

;; Who has no external callers? Split :dead from :internal-only
(cg/dead-methods "bookService")
;; {:dead [] :internal-only [{:method "archiveBook"
;;                             :intra-callers ["adminHelper"]}]
;;  :warnings ["2 @EventListener beans detected — ...]}
🎲

Test Data Generation — build valid entities on the fly

Construct realistic, persistable Hibernate entity instances from the REPL — no fixture files, no test annotations, no restart. 54+ heuristic patterns match property names and types to net.datafaker providers (person, address, financial, content, and more). jakarta.validation.constraints annotations are respected at generation time — @Min/@Max clamp numeric ranges, @Email ensures valid addresses, @Size clamps string length — and overrides that violate @NotNull or @Min/@Max throw immediately with a clear message instead of a cryptic Hibernate error at flush time. Lookup tables (like Genre) are fetched from the DB to avoid unique-constraint violations. Use build-test-recipe to capture every generated value with its Java type and repo bean name — the exact seed for @BeforeEach setup and assertions.

;; Simple entity — all scalar fields filled with realistic data
(faker/build-entity "Author")

;; Capture full graph as typed recipe — seed for integration tests
(faker/build-test-recipe "Review")
;; => {:Review {:repo "reviewRepository"
;;              :fields {:rating {:type "short" :value 5} …}}}

;; Speculative: get a real id, call your service, roll everything back
(let [review (faker/build-entity "Review"
                {:auto-deps? true :persist? true :rollback? true})]
  (.computeRating (lw/bean "ratingService") (.getId review)))
🗂️

Repository → Entity Mapping — no convention guessing

Spring Data repository names follow a convention that breaks down with custom names, multi-module projects, or unfamiliar codebases. repo-entity and all-repo-entities give the authoritative answer at runtime — entity class, fully-qualified name, and ID type — in one call. No source reading, no guessing.

;; What entity does this repo manage?
(lw/repo-entity "bookRepository")
;; => {:bean "bookRepository" :entity "Book"
;;     :entity-fqn "com.example.domain.Book" :id-type "Long"}

;; Full repo → entity map for the whole application
(lw/all-repo-entities)
;; => [{:bean "authorRepository" :entity "Author" …}
;;     {:bean "bookRepository"   :entity "Book"   …} …]

Agents welcome. Humans too.

An nREPL server embedded in your app — bring your own client.

🤖

AI agent

Claude, Cursor, or any coding assistant with an nREPL tool can connect, query beans, trace SQL, and hot-swap queries — autonomously, in a feedback loop.

🧑‍💻

You

Open Calva, CIDER, or any nREPL client and explore the live app yourself. Poke at beans, run transactions, trace a suspicious endpoint — no AI in the loop unless you want one.

The experience is richer with an agent driving it, but the wire is open to everyone. Your curiosity is the only prerequisite.

From "I heard it's slow"
to confirmed fix. 72 seconds.

"Call the getBooksByGenre endpoint — I heard it's a slow one. Investigate." The agent connected via Livewire, listed all endpoints to orient itself, then grabbed a valid genre ID from the live database and started tracing.

First attempt hit an AuthorizationDeniedException — the endpoint requires ROLE_MEMBER, not admin. The agent corrected its run-as call and retried. detect-n+1 came back with 98 queries for 20 books: genres, reviews, authors, and each reviewer's member record all loading separately in a loop.

hq/list-queries showed the culprit immediately — findByGenreId had a bare JPQL with no JOIN FETCH at all. The agent wrote a fix and hot-swapped it live. First attempt bounced with a JPQL grammar error; the agent self-corrected and swapped again. Query count: 98 → 1. Verified across five genres. Fix written to BookRepository.java, hot-swap reset.

72 seconds. Zero restarts. Root cause found, verified, and fixed.
98→1
SQL queries per request
0
App restarts needed
72s
Total investigation time

See it happen

A developer runs the app locally with anonymized seed data. The AI agent connects via Livewire and gets to work.

Connecting to the nREPL
Connecting to the live app via nREPL
Listing endpoints
Listing all endpoints from the REPL
N+1 detection
Detecting the N+1 query problem
Hypothesis verification and fix
Verifying the hypothesis and hot-fixing live

Know what you're wiring up.

⚠ Data sensitivity

When an AI agent drives Livewire, everything it observes gets sent to a remote model — query results, entity field values, stack traces, bean state. If your dev database contains real customer data, PII, credentials, or anything sensitive, the AI will see it and transmit it. Use anonymized seed data. Always.

The screenshots on this page were taken against a synthetic dataset built specifically for this purpose. Build yours too.

⚠ Execution power

nREPL is not a read-only probe. It is a full code execution environment — equivalent to shell access to your running JVM. An agent (or a human) can read files, write to the database, call external services, or do anything the JVM process is allowed to do.

Bring your own security. Bring your own sandbox. Bring your own brain. Think about what you expose, to whom, and why.

Dev only. You have been warned.

Try it first — no install needed.

Run this against any Spring Boot app already on your machine. No dependency, no code changes, no restart.

$ jshell
jshell> /open https://raw.githubusercontent.com/brdloush/livewire/refs/heads/main/attach.jsh

Downloads a small agent bundle, lists running JVMs, lets you pick one.
Once attached: info(), beans(), eval(), sql(), demo()

⚠️ Java 21+ note: start your target app with -XX:+EnableDynamicAgentLoading to allow dynamic agent injection.
This path is great for quick exploration. For regular use — repeatable runs, full SQL tracing, query-watcher — the dependency install below is the recommended way.

Three steps. One property.

No code changes to your app. No Spring profiles to configure.

1

Add the dependency

Add to your pom.xml or build.gradle.

The JAR is 36 KB. It brings in the Clojure runtime and nREPL — everything else (Spring, Hibernate, ASM) is already in your app. Compatible with Spring Boot 3.x (Hibernate 6) and Spring Boot 4.x (Hibernate 7). Requires Java 17+.

<!-- Maven -->
<dependency>
  <groupId>net.brdloush</groupId>
  <artifactId>livewire</artifactId>
  <version>0.12.0</version>
</dependency>
2

Enable it locally

One property in your local profile. Never committed, never shipped.

# application-local.properties
livewire.enabled=true

# optional: change the port (default 7888)
livewire.nrepl.port=7888
3

Connect & probe

From Claude Code, ECA, or any nREPL client.

# all aliases are pre-loaded at startup
# lw, q, intro, trace, qw, hq, jpa, mvc, faker, cg

# lw-start discovers the nREPL + prints app info
lw-start
;; Discovered nREPL servers:
;;   localhost:7888 (clj) - /your-project
;; => {:application-name "your-app", ...}