neorepl 0.4: Tests, Inspector, Tracing and Agents
neorepl 0.4 came out last week, and since I never got around to writing about 0.2 and 0.3, it feels like a good moment to look back at what happened since the original announcement. Three releases in two weeks, and neorepl now has most of what I listed under “What’s next” back then - a test runner, a full-screen inspector and stacktrace browser, an MCP server and a skill for coding agents - plus a few things that weren’t on the list at all, like tracing and peeking into your coding agent’s REPL session.
Running tests

,test in the REPL, or neorepl test in your shell, runs the tests in the
namespaces you name, the tests you name or, without either, the whole
project’s. Failures come with their file and line, and errors with a stack
trace that stops at the test that hit them, instead of wandering off into
clojure.test’s guts. ,retest (neorepl retest) reruns only what didn’t
pass, which is exactly what you want while you’re fixing things.
As usual, the server does the heavy lifting. cider-nrepl runs the tests when
it’s around, and so does babashka’s nREPL server, which bundles CIDER’s test
ops these days. Without either, neorepl falls back to plain clojure.test,
as long as you tell it which namespaces to test. And in a ClojureScript
session the same commands run your ClojureScript tests.
$ neorepl test
$ neorepl test my.app-test my.app.db-test
$ neorepl test --format json my.app-test/start-test
Stack traces and the inspector
,stacktrace shows the last error full-screen. Pick a frame, press Enter,
and its source opens in your $EDITOR at that line (as a copy, for code in a
jar), and when you quit the editor you’re back where you were. By default you
see only the frames that matter, and a brings back the rest - the REPL’s own
machinery and the repeated frames of the same function. With cider-nrepl, i
inspects the cause you’re looking at and d its ex-data. After a test run
with errors, ,stacktrace shows those, each under the test it came from.
(You can see that at the end of the GIF above.)
,inspect puts cider-nrepl’s inspector (babashka has one too) in full-screen:

The arrow keys (or j and k) move around, Enter digs into a value and
Backspace goes back up, [ and ] page through big collections, v shows
an object’s fields, P toggles pretty printing and g refreshes values that
change, like an atom’s. The handiest one is d, which defines whatever
you’re looking at as a var. Dig five levels deep into some big API response, press
d, give it a name, and you can play with that piece in the REPL right away.
neorepl inspect does the same one step at a time, for scripts and agents,
since the inspector keeps its state in the session:
$ neorepl inspect '(System/getProperties)'
$ neorepl inspect --push 3
$ neorepl inspect --def props
Tracing

,trace traces a function, or every function in a namespace, and from then
on each call shows its arguments and what it returned, nested in the calls
it’s part of. It’s often the quickest way to see what some code is actually
doing, and there are no printlns to forget to remove afterwards. Calls on
other threads, like an HTTP handler’s or a future’s, show up above the prompt
too. ,trace without arguments lists what’s traced and ,untrace stops
tracing. Scripts and agents get neorepl trace, with the calls showing up in
the output of the neorepl eval that made them. Tracing needs cider-nrepl
on the server.
(Fun fact: tracing a function that the tracing itself calls, like
clojure.core/map, used to take the whole server down. neorepl refuses to
trace those, and as of orchard 0.48 the server no longer falls over on them
anyway.)
Coding agents
0.3 was all about coding agents. The CLI was usable by agents from day one, but now they get a lot more help:
-
neorepl mcpserves neorepl’s features as MCP tools over stdio, for agents that would rather call tools than run commands. With Claude Code it’s a one-liner:$ claude mcp add neorepl -- neorepl mcpThe tools are
eval,load,doc,source,where,apropos,macroexpand,reload,test,retest,pst,interrupt,checkandports. That’s fewer than the CLI’s commands on purpose: an agent pays for every tool’s description in its context on every turn, whether it uses the tool or not. Evaluations get cut at 10,000 characters and interrupted after 60 seconds by default, so a stray(range)or an infinite loop can’t eat the agent’s context or its session. - A skill teaches agents the workflow I’d like them to follow: after editing a file, check its parens, load it, run its tests, and read the stack trace when something fails. It costs nothing until the agent actually works on Clojure code. doc/agents.md explains how to set it up with Claude Code, Codex, Gemini CLI, Cursor and VS Code.
-
neorepl check --fixrepairs unbalanced delimiters the way parinfer’s indent mode would, trusting the indentation over the parens. Mismatched parens are still the most common way for an agent to break a Clojure file, and now fixing them doesn’t take another trip through the model:$ neorepl check src/demo/orders.clj src/demo/orders.clj:3:1: Unclosed ( $ neorepl check --fix src/demo/orders.clj src/demo/orders.clj:3:1: Unclosed (, repaired from the indentation
The CLI and the MCP tools share named sessions, the one called agent unless
told otherwise, which brings me to my favourite addition in 0.4.
Looking over your agent’s shoulder

,sessions lists the server’s named sessions, and ,sessions agent steps
into the agent’s, with its name in the prompt. Everything the agent left
behind is right there: its namespace, its *1, its *e. So when an agent
tells you that a test fails with a NullPointerException, you can look at
the exception yourself instead of asking it to paste the stack trace.
,sessions - takes you back to your own session.
The lookups take --session too, so neorepl doc --session agent foo looks
up foo the way that session sees it, in its namespace and its language,
ClojureScript included.
The REPL, round two
0.4 also spent a lot of time on the REPL itself:
,nsswitches namespaces, loading the one you name if it isn’t loaded yet, and lists the project’s namespaces when you don’t name one.- C-x C-s shows the source of the symbol at the cursor and C-x C-a the vars whose names contain it, next to the C-x C-d for docs that was there from the start. All three come from rebel-readline.
,docand the arglists that show up as you type cover Java classes, methods and fields too, with their Javadoc as text (that one needs cider-nrepl).,editand the stacktrace browser open definitions that live in jars, instead of telling you they can’t.- Ctrl-C got a lot more reliable. An interrupt sent right after an evaluation could get lost when the server hadn’t started running it yet, the forms after an interrupted one ran anyway, and whatever an interrupted evaluation printed later could show up in the next one’s output. All fixed.
- neorepl sends the forms you enter to the server one at a time now, which is a big part of the interrupt fixes, and as a bonus piggieback evaluates all of them, not only the first.
- On Linux, every evaluation used to wait 40ms on the server’s socket, courtesy of TCP’s delayed ACKs. neorepl turns those off now, on the JVM and on babashka 1.13.227 or newer, and evaluating a file with 100 forms went from 4.5 seconds to about 150ms.
- Lots of error messages got friendlier: a build tool that isn’t installed, a server that dies while starting, loading a file with unbalanced parens, a named session the server lost when it restarted, and so on.
So much for keeping neorepl small, by the way - the code has about doubled since 0.1. In my defense, it does a lot more than twice as much.
Making the neighbours better, round two
Just like last time, building neorepl turned up problems elsewhere, and a good part of these two weeks went into fixing them at the source:
- I filed a batch of issues for babashka’s nREPL server: test runs that hung
or killed the session,
:project?being ignored, a session that never answered again after its client disconnected, a couple of JLine classes neorepl’s line editor needed. Michiel fixed every single one of them within days, and they’re all in babashka 1.13.227. Thanks, Michiel! - cider-nrepl 0.64 and orchard 0.48 came out this week with fixes for the problems I ran into: what tests print now comes back with the test results (it used to go to the connection of the session’s previous message, which for a CLI is long gone), the test op no longer goes silent for a namespace that isn’t loaded or runs every test when the one you asked for doesn’t exist, ClojureScript tests run on shadow-cljs, a traced call that throws shows up as such, and Javadoc no longer comes with stray spaces in it. CIDER gets all of that too, which is my favourite kind of side effect. The next neorepl release will use cider-nrepl 0.64 when it starts a server.
- In nREPL, the fix for an interrupted evaluation’s last response going to
the wrong connection is merged, and so is
TCP_NODELAYon the server’s sockets, which takes 100 evaluations with nREPL’s own client from 4.4 seconds to 90ms on Linux. Both will be in the next nREPL release. - The piggieback improvements I mentioned last time are a bit behind schedule. A lot of ClojureScript REPLs run on top of piggieback, so a mistake there has a pretty big blast radius, and evaluating every form of a message is something we never found a good way to do in the past. I’d rather take my time with those than rush them out. neorepl isn’t waiting on them, at least, since it sends the forms one at a time now.
What’s next
0.5 is about more runtimes and better navigation:
- integration tests against real servers in CI: cider-nrepl and babashka first, then shadow-cljs and piggieback with Node
- nbb and Basilisp in CI, with their gaps documented
- cross-references (
,refs), on top of cider-nrepl’sfn-refsandfn-deps - a full-screen namespace browser
- a couple of ideas borrowed from clojure-cli.repl: evaluating the form at the cursor, and the last evaluation’s outcome and time in the prompt
- indenting continuation lines, and a way to pick the vi keymap
Better Windows support, the one item from the 0.1 list that’s still open, is planned for 0.6, together with easier ways to install neorepl.
Try it
$ bbin install io.github.nrepl/neorepl
$ cd my-project
$ neorepl
The CHANGELOG has everything I skipped here, and the README has the details. Please keep the issues and ideas coming!
Keep hacking!