In this post I'll give updates about open source I worked on during March and April 2026.
To see previous OSS updates, go here.
I'd like to thank all the sponsors and contributors that make this work possible. Without you, the below projects would not be as mature or wouldn't exist or be maintained at all! So a sincere thank you to everyone who contributes to the sustainability of these projects.

Current top tier sponsors:
Open the details section for more info about sponsoring.
If you want to ensure that the projects I work on are sustainably maintained, you can sponsor this work in the following ways. Thank you!
Babashka Conf 2026 is happening on May 8th in the OBA Oosterdok library in Amsterdam! David Nolen, primary maintainer of ClojureScript, will be our keynote speaker. We're excited to have Nubank, Exoscale, Bob, Flexiana and Itonomi as sponsors. Nubank and Exoscale are hiring. Wendy Randolph will be our event host. For the schedule and other info, see babashka.org/conf. Join the babashka-conf Slack channel on Clojurians Slack for last minute communication. The day after babashka conf, Dutch Clojure Days 2026 will be happening, so you can enjoy a whole weekend of Clojure in Amsterdam. Hope to see many of you there!
In the last two months I spent significant time organizing babashka conf, but made progress in several projects as well.
My upstream work to enable async/await in ClojureScript was merged in the beginning of March. The implementation mirrors squint. Thanks David for reviewing and merging. Also deftest now supports an ^:async annotation so you can use async/await and don't need to mess around with the cljs.test/async macro anymore:
I'll be presenting this work at the Dutch Clojure Days.
Rebel-readline is now bb compatible. The work involved mainly exposing more JLine stuff and making sure rebel-readline didn't hit any internal JLine APIs. One step to drive this to completion was to make a dependency, compliment, bb compatible. Thanks both to Bruce and Alexander for the cooperation.
Squint now supports cljs.test and multimethods! clojure-mode was ported to use the new cljs.test.
On the cream front, I put in effort to make the binary smaller and have been keeping up with the new GraalVM EA releases. I've been posting bug reports to the crema maintainer. Currently there's still an unfixed bug around core.async that I have trouble reproducing in pure Java. I also added lots of library tests to CI so I can ensure stability in the long run. For now it remains experimental, but the direction is promising.
A performance PR to weavejester/dependency speeds up depend, depends? and topo-sort significantly, so clerk notebooks render faster.
The cljfmt library, also by @weavejester, now fully runs from source in babashka. The Java diff library that wasn't bb-compatible was replaced with text-diff, but only for the babashka path. The JVM build of cljfmt still uses the original Java diff library, with a possible switch later once text-diff has matured.
Several SCI fixes were made to improve Clojure compatibility between babashka and Clojure. E.g. records can now support extending to IFn which was a blocker for some Clojure libs that tried to run in bb so far.
Clj-kondo 2026.04.15 got a few new linters thanks to @jramosg for stewarding most of these. It also has better out of the box potemkin support, and @alexander-yakushev contributed a wave of performance improvements.
Updates per project below. Bullets are highlights; see each project's CHANGELOG.md for the full list.
babashka: native, fast starting Clojure interpreter for scripting.
Completer, Highlighter, ParsedLine, Writer, Readerclojure.repl/special-doc and clojure.repl/set-break-handler!clojure.main/repl-readorg.jline.reader.Buffer to class allowlistclojure.java.javadoc namespace with javadoc available in REPL #1933(doc var), (doc set!) and other special forms #1932(source inc) and (source babashka.fs/exists?) for built-in vars #1935BABASHKA_REPL_HISTORY env var for configurable REPL history location #1930deftype and defrecord inside non-top-level forms (e.g. let, testing) #1936java.util.HexFormat interop support:as-alias-version as an alias for --versionclojure.lang.EdnReader$ReaderException--prepare flag skipping next tokenclojure.data.xml.tree/{flatten-elements,event-tree}, clojure.data.xml.event record constructors, and clojure.data.xml.jvm.parse/string-sourcejava.net.Proxy and java.net.Proxy$Type Java classes (@jeeger)java.lang.reflect.Constructor, java.lang.reflect.Executable, java.util.stream.Collectors, java.util.Comparator (for reify), and morenextjournal.markdown to 0.7.255, edamame to 1.5.39, data.xml to 0.2.0-alpha11, jsoup to 1.22.2, rewrite-clj to 1.2.54, tools.cli to 1.4.256, transit-clj to 1.1.357, fs to 0.5.32SCI: Configurable Clojure/Script interpreter suitable for scripting
recur with 20+ args in loop (#1035)recur arity, throw when it doesn't match (#1034)IFn on defrecord, deftype and reify (#808, #1036)IPrintWithWriter as protocol (#1032)doc macrons-mapclj-kondo: static analyzer and linter for Clojure code that sparks joy.
:not-nil? which suggests (some? x) instead of (not (nil? x)), and similar patterns with when-not and if-not (default level: :off):protocol-method-arity-mismatch which warns when a protocol method is implemented with an arity that doesn't match any arity declared in the protocol (@jramosg):missing-protocol-method-arity (off by default) which warns when a protocol method is implemented but not all declared arities are covered:redundant-declare which warns when declare is used after a var is already defined (@jramosg)import-fn, import-macro, and import-defimport-vars :refer and :rename syntaxpmap and future-related functions (future, future-call, future-done?, future-cancel, future-cancelled?) (@jramosg)throw with string in CLJS no longer warns about type mismatch (@jramosg)/bin/clj-kondo (@harryzcy)StackOverflowError occurs during analysiscream: Clojure + GraalVM Crema native binary
clojure.core.async-testhttpkit, nextjournal/markdown, clj-yaml, core.async ioc-macrossquint: CLJS syntax to JS compiler
defmulti, defmethod, get-method, methods, remove-method, remove-all-methods, prefer-method, prefers, plus hierarchy ops isa?, derive, underive, make-hierarchy, parents, ancestors, descendants (#806)cljs.test/report is now a multimethod, extensible via defmethod. test-var now fires :begin-test-var / :end-test-var events.await in async functions, in anticipation of CLJS next. The legacy js-await and js/await forms continue to work as aliases for now.cljs.test / clojure.test support: deftest, is, testing, are, use-fixtures, async, run-testswith-meta now preserves callability when applied to a function.cljc files via :require (no need for :require-macros); resolve qualified symbols from macro expansionssquint.compiler/compile* and squint.compiler/transpile* which accept either a string or a sequence of pre-parsed forms, skipping the forms -> string -> forms roundtrip for SSR use cases#html / #jsx were erased when an attrs map was present without a :class keycherry: Experimental ClojureScript to ES6 module compiler
await as a special form, in anticipation of CLJS next:require-macros clauses with :refer now properly accumulate instead of overwriting each othercherry.test with clojure.test-compatible testing API: deftest, is, testing, are, use-fixtures, async, run-tests. Macros are compiler built-ins (shared with squint), so no :require-macros plumbing is needed in user code.nbb: Scripting in Clojure on Node.js using SCI
IFn on defrecord and reifyp/then results)fs: file system utility library for Clojure
touch fn (@lread & @borkdude)Coercions and Returns / Argument Naming Conventions sections to README (@lread):nofollow-links option (@lread)split-ext and extension on dotfiles with parent dirs (e.g. foo/.gitignore)gzip & gunzip now honor dest dir when specified (@lread)umask on created files and directories (@lread)clerk: Moldable Live Programming for Clojure
weavejester/dependency (#808)v0.12.51 (#793), enables async/await in viewer functionspresent+reset! (#809)build-graph crash on non-Clojure source files (#810)edamame: configurable EDN and Clojure parser with location metadata and more
Nextjournal Markdown: A cross-platform Clojure/Script parser for Markdown
:disable-footnotes true to disable parsing footnotes #67quickdoc: Quick and minimal API doc generation for Clojure
grasp: Grep Clojure code using clojure.spec regexes
grasp.implbabashka.nrepl: The nREPL server from babashka as a library
send to prevent interleaved bencode frames from concurrent writesinfo and lookup op refinements: lookup carries nested info map whereas info is a flatmappod-babashka-instaparse: instaparse from babashka
add-line-and-column-info-to-metadata--features=clj_easy.graal_build_time.InitClojureClasses to native-imageinstaparse-bb: Use instaparse from babashka
add-line-and-column-info-to-metadata and get-failureparser (e.g. :output-format :enlive)java.net.URL for grammarsbabashka-sql-pods: babashka pods for SQL databases
next.jdbc, cheshire (Jackson 2.12 -> 2.20), PostgreSQL, MSSQL, HSQLDB, MySQL Connector/J drivershttp-client: HTTP client built on java.net.http
httpstat.us examples with httpbin.org in testsneil: A CLI to add common aliases and features to deps.edn-based projects
deps.clj: a faithful port of the clojure CLI bash script to Clojure
Contributions to third party projects:
depend, depends?, and topo-sort:bb reader conditionals to replace the AutoFlattenSeq deftype with plain vectors plus metadata markers, swap the Segment deftype for a reify-based CharSequence, and add a CI test runner. Open, awaiting review.These are (some of the) other projects I'm involved with but little to no activity happened in the past two months.
Published: 2026-05-04
Tagged: clojure oss updates
In this post I'll give updates about open source I worked on during January and February 2026.
To see previous OSS updates, go here.
I'd like to thank all the sponsors and contributors that make this work possible. Without you, the below projects would not be as mature or wouldn't exist or be maintained at all! So a sincere thank you to everyone who contributes to the sustainability of these projects.

Current top tier sponsors:
Open the details section for more info about sponsoring.
If you want to ensure that the projects I work on are sustainably maintained, you can sponsor this work in the following ways. Thank you!
Babashka Conf 2026 is happening on May 8th in the OBA Oosterdok library in Amsterdam! David Nolen, primary maintainer of ClojureScript, will be our keynote speaker! We're excited to have Nubank, Exoscale, Bob and Itonomi as sponsors. Wendy Randolph will be our event host / MC / speaker liaison :-). The CfP is now closed. More information here. Get your ticket via Meetup.com (there is a waiting list, but more places may become available). The day after babashka conf, Dutch Clojure Days 2026 will be happening, so you can enjoy a whole weekend of Clojure in Amsterdam. Hope to see many of you there!
I spent a lot of time making SCI's deftype, case, and macroexpand-1 match JVM Clojure more closely. As a result, libraries like riddley, cloverage, specter, editscript, and compliment now work in babashka.
After seeing charm.clj, a terminal UI library, I decided to incorporate JLine3 into babashka so people can build terminal UIs. The goal is to be able to run rebel-readline + nREPL from source in babashka, but this is still work in progress (e.g. the compliment PR is still pending). Since I had JLine anyway, babashka also got a major REPL upgrade with multi-line editing, tab completion, ghost text, and persistent history.
On the CLJS side, SCI, scittle, and nbb all gained async/await support.
Last but not least, I started cream, an experimental native binary that runs full JVM Clojure with fast startup using GraalVM's Crema. Unlike babashka, it supports runtime bytecode generation (definterface, deftype, gen-class). It currently depends on a fork of Clojure and GraalVM EA, so it's not production-ready yet.
Here are updates about the projects/libraries I've worked on in the last two months in detail.
NEW: cream: Clojure + GraalVM Crema native binary
eval, require, and library loadingdefinterface, deftype, gen-class, and other constructs that generate JVM bytecode at runtime.java source files directly, as a fast alternative to JBangbabashka: native, fast starting Clojure interpreter for scripting.
bb repl) improvements: multi-line editing, tab completion, ghost text, eldoc, doc-at-point (C-x C-d), persistent historydeftype with map interfaces (e.g. IPersistentMap, ILookup, Associative). Libraries like core.cache and linked now work in babashka.babashka.terminal namespace that exposes tty?deftype supports Object + hashCodereify with java.time.temporal.TemporalQueryreify with methods returning int/short/byte/floatSCI: Configurable Clojure/Script interpreter suitable for scripting
deftype now macroexpands to deftype*, matching JVM Clojure, enabling code walkers like riddleycase now macroexpands to JVM-compatible case* format, enabling tools like riddley and cloverageasync/await in ClojureScript. See docs.defrecord now expands to deftype* (like Clojure), with factory fns emitted directly in the macro expansionmacroexpand-1 now accepts an optional env map as first argumentproxy-super, proxy-call-with-super, update-proxy and proxy-mappingsthis-as in ClojureScriptclj-kondo: static analyzer and linter for Clojure code that sparks joy.
@jramosg, @tomdl89 and @hugod have been on fire with contributions this period. Six new linters!
:duplicate-refer which warns on duplicate entries in :refer of :require (@jramosg):aliased-referred-var, which warns when a var is both referred and accessed via an alias in the same namespace (@jramosg):is-message-not-string which warns when clojure.test/is receives a non-string message argument (@jramosg):redundant-format to warn when format strings contain no format specifiers (@jramosg):redundant-primitive-coercion to warn when primitive coercion functions are applied to expressions already of that type (@hugod)array, class, inst and type checking support for related functions (@jramosg)clojure.test functions and macros (@jramosg):condition-always-true linter to check first argument of clojure.test/is (@jramosg):redundant-declare which warns when declare is used after a var is already defined (@jramosg)pmap and future-related functions (@jramosg)squint: CLJS syntax to JS compiler @tonsky and @willcohen contributed several improvements this period.
squint.math, also available as clojure.math namespacecompare-and-swap!, swap-vals! and reset-vals! (@tonsky)dotimes with _ binding (@tonsky)shuffle not working on lazy sequences (@tonsky):require-macros with :refer now accumulate instead of overwriting (@willcohen)-0.0)prn js/undefined as nilyield* IIFEscittle: Execute Clojure(Script) directly from browser script tags via SCI
async/await. See docs.js/import not using evalthis-as#<Promise value> when a promise is evaluatednbb: Scripting in Clojure on Node.js using SCI
(js/Promise.resolve 1) ;;=> #<Promise 1>fs - File system utility library for Clojure
clerk: Moldable Live Programming for Clojure
neil: A CLI to add common aliases and features to deps.edn-based projects.
neil test now exits with non-zero exit code when tests failcherry: Experimental ClojureScript to ES6 module compiler
:require-macros clauses with :refer now properly accumulate instead of overwriting each otherContributions to third party projects:
cli/cli to cli/parse-opts, bumped riddleyThese are (some of the) other projects I'm involved with but little to no activity happened in the past month.
Published: 2026-03-05
Tagged: clojure oss updates
Babashka is a fast-starting native Clojure scripting runtime. It uses SCI to interpret Clojure and compiles to a native binary via GraalVM, giving you Clojure's power with near-instant startup. It's commonly used for shell scripting, build tooling, and small CLI applications. If you don't yet have bb installed, you can with brew:
brew install borkdude/brew/babashka
or bash:
bash <(curl -s https://raw.githubusercontent.com/babashka/babashka/master/install)
This release is, in my opinion, a game changer. With JLine3 bundled, you can now build full terminal user interfaces in babashka. The bb repl has been completely overhauled with multi-line editing, completions, and eldoc. deftype now supports map interfaces, making bb more compatible with existing libraries like core.cache. SCI has had many small improvements, making riddley compatible too. Riddley is used in Cloverage, a code coverage library for Clojure, which now also works with babashka (Cloverage PR pending).
But first, let me mention an exciting upcoming event! Babashka conf is happening again for the second time! The first time was 2023 in Berlin. This time it's in Amsterdam. The Call for Proposals is open until the end of February, so there is still time to submit your talk or workshop. We are also looking for one last gold sponsor (500 euros) to cover all costs.
Babashka now bundles JLine3, a Java library for building interactive terminal applications. You get terminals, line readers with history and tab completion, styled output, keyboard bindings, and the ability to reify custom completers, parsers, and widgets — all from bb scripts.
JLine3 works on all platforms, including Windows PowerShell and cmd.exe.
Here's a simple interactive prompt that reads lines from the user until EOF (Ctrl+D):
(import '[org.jline.terminal TerminalBuilder]
'[org.jline.reader LineReaderBuilder])
(let [terminal (-> (TerminalBuilder/builder) (.build))
reader (-> (LineReaderBuilder/builder)
(.terminal terminal)
(.build))]
(try
(loop []
(when-let [line (.readLine reader "prompt> ")]
(println "You typed:" line)
(recur)))
(catch org.jline.reader.EndOfFileException _
(println "Goodbye!"))
(finally
(.close terminal))))
A new babashka.terminal namespace exposes a tty? function to detect whether stdin, stdout, or stderr is connected to a terminal:
(require '[babashka.terminal :refer [tty?]])
(when (tty? :stdout)
(println "Interactive terminal detected, enabling colors"))
This accepts :stdin, :stdout, or :stderr as argument. It uses JLine3's terminal provider under the hood.
This is useful for scripts that want to behave differently when piped vs. run interactively, for example enabling colored output or progress bars only in a terminal.
charm.clj is a new Clojure library for building terminal user interfaces using the Elm architecture (Model-Update-View). It provides components like spinners, text inputs, lists, paginators, and progress bars, with support for ANSI/256/true color styling and keyboard/mouse input handling.
charm.clj is now compatible with babashka (or rather, babashka is now compatible with charm.clj), enabled by the combination of JLine3 support and other interpreter improvements in this release. This means you can build rich TUI applications that start instantly as native binaries.
Here's a complete counter example you can save as a single file and run with bb:
#!/usr/bin/env bb
(babashka.deps/add-deps
'{:deps {io.github.TimoKramer/charm.clj {:git/sha "cf7a6c2fcfcccc44fcf04996e264183aa49a70d6"}}})
(require '[charm.core :as charm])
(def title-style
(charm/style :fg charm/magenta :bold true))
(def count-style
(charm/style :fg charm/cyan
:padding [0 1]
:border charm/rounded-border))
(defn update-fn [state msg]
(cond
(or (charm/key-match? msg "q")
(charm/key-match? msg "ctrl+c"))
[state charm/quit-cmd]
(or (charm/key-match? msg "k")
(charm/key-match? msg :up))
[(update state :count inc) nil]
(or (charm/key-match? msg "j")
(charm/key-match? msg :down))
[(update state :count dec) nil]
:else
[state nil]))
(defn view [state]
(str (charm/render title-style "Counter App") "\n\n"
(charm/render count-style (str (:count state))) "\n\n"
"j/k or arrows to change\n"
"q to quit"))
(charm/run {:init {:count 0}
:update update-fn
:view view
:alt-screen true})

More examples can be found here.
Until now, deftype in babashka couldn't implement JVM interfaces like IPersistentMap, ILookup, or Associative. This meant libraries that define custom map-like types, a very common Clojure pattern, couldn't work in babashka.
Starting with this release, deftype supports map interfaces. Your deftype must declare IPersistentMap to signal that you want a full map type. Other map-related interfaces like ILookup, Associative, Counted, Seqable, and Iterable are accepted freely since the underlying class already implements them.
This unlocks several libraries that were previously incompatible:
Riddley is a Clojure library for code walking that many other libraries depend on. Previously, SCI's deftype and case did not macroexpand to the same special forms as JVM Clojure, which broke riddley's walker. Several changes now align SCI's behavior with Clojure: deftype macroexpands to deftype*, case to case*, and macroexpand-1 now accepts an optional env map as second argument (inspired by how the CLJS analyzer API works). Together these changes enable riddley and tools built on it, like cloverage and Specter, to work with bb.
Riddley has moved to clj-commons, thanks to Zach Tellman for transferring it. I'd like to thank Zach for all his contributions to the Clojure community over the years. Version 0.2.2 includes bb compatibility, which was one of the first PRs merged after the transfer. Cloverage compatibility has been submitted upstream, all 75 cloverage tests pass on both JVM and babashka.
The bb repl experience has been significantly improved with JLine3 integration. You no longer need rlwrap to get a comfortable console REPL:
#_=> continuation prompt:foo, ::foo, ::alias/foo)

(map |), the arglists are displayed below the prompt~/.bb_repl_historyMany of these features were inspired by rebel-readline, Leiningen's REPL, and Node.js's REPL.
Under the hood, SCI (the interpreter powering babashka) received many improvements in this cycle:
(let [^Predicate p even?] (.test p 42)) and SCI will adapt the Clojure function to the functional interface automatically.let binding values to binding names, reducing the need for explicit type hints in interop-heavy code.read with nil/false as eof-value, letfn with duplicate function names, ns-map not reflecting shadowed vars, NPE in resolve, and .method on class objects routing incorrectly.catch clauses in combination with ^:sci/errorsatisfies? on protocols with proxyreify with java.time.temporal.TemporalQueryreify with methods returning int/short/byte/float primitives@(promise)clojure.test.junit as built-in source namespaceAddAllCharsets. More charsets can be added on request.For the full list of changes including new Java classes and library bumps, see the changelog.
Thank you to all the contributors who helped make this release possible. Special thanks to everyone who reported issues, tested pre-release builds from babashka-dev-builds, and provided feedback.
Thanks to Clojurists Together and all babashka sponsors and contributors for their ongoing support. Your sponsorship makes it possible to keep developing babashka.
And thanks to all babashka users: you make this project what it is. Happy scripting!
Published: 2026-02-17