In this post I'll give updates about open source I worked on during May and June 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. If you work for a company that uses my OSS, please ask your employer, that would be even better. Thank you!
A lot happened in the past two months! Not just coding but also...
Three years after the initial installment, Babashka Conf 2026 happened on May 8th at the OBA Oosterdok library in Amsterdam, with David Nolen, primary maintainer of ClojureScript, as our keynote speaker. Thanks to our sponsors Nubank, Exoscale, Bob, Flexiana and Itonomi, to Wendy Randolph for hosting, and to all the speakers, volunteers and attendees who made it such an inspiring day. You can watch all the videos here. Thanks to Ray for recording! The day after, Dutch Clojure Days 2026 rounded out a full weekend of Clojure in Amsterdam, where I did a presentation about ClojureScript and async/await. The video of that is hopefully coming soon.

Babashka Conf 2026. From left to right: David Nolen, Jen Myers, Adrian Smith, Josh Glover, Rahul Dé, Arne Brasseur, Christoph Neumann, Timo Kramer, Jynn Nelson, Wendy Randolph.
I'm pleased to announce that Rahul Dé and I will be hosting a babashka workshop at the Clojure Conj 2026. The workshop will showcase various use cases of babashka. This hands-on workshop covers the whole lifecycle of a babashka tool, from a quick script to a published, installable CLI app. We assume you know the basics of Clojure and won't explain the language itself. Topics include:
bb.edn)bbinEvery concept comes with an exercise, building toward one culminating CLI app. There will be lots of interaction and fun!
Besides this update I published two blog posts in the past two months:
and a ClojureScript reference on async functions:
Babashka CLI got the most attention this cycle. I added automatic --help generation for dispatch-based CLIs and shell tab completion for bash, zsh, fish, PowerShell and Nushell. There's a dedicated post with a "build your own git" walkthrough linked above. I also made Babashka CLI Squint compatible, so CLIs built with it run on Node.js and in the browser, published as the @babashka/cli npm package. Also ClojureDart support for Babashka CLI got added.
Squint saw a large amount of work that kept going right into early July: a browser nREPL, dynamic vars and binding that survive across separately-compiled ESM modules, an EDN reader, cached lazy seqs, defrecord and a wide set of core protocols, and a big compatibility push to make it pass jank's clojure-test-suite. Replicant now runs on Squint too. I added key diffing to Reagami and did some benchmarks, showing that Reagami on squint performs in the ballpark of React. The benchmark also shows that Replicant on Squint performs even a tad better than on ClojureScript. Not that this makes a huge difference in practice, but it's nice to validate the idea that Squint, for typical apps, can be a valid CLJS replacement while not giving up that much in terms of Clojure features.
A security issue in SCI deserves a callout. A string type-hint could bypass the :classes allowlist and statically initialize any class on the classpath at analysis time. If you sandbox untrusted code with SCI, upgrade to 0.13.53. ClojureDart support and fine-grained interop control (which was needed for cljd support since it has no reflection) also got added. You can now make REPLs for your mobile apps!
Since porting was a theme these past months, I'll mention another one: babashka.fs now runs on Node.js via ClojureScript and squint, published as the @babashka/fs npm package.
Here are some highlights per project. See each project's CHANGELOG.md for the full list.
babashka CLI: Turn Clojure functions into CLIs!
--help generation for dispatch CLIs, plus shell completions for bash, zsh, fish, PowerShell and Nushell (#112, #24, #95). I wrote a full post on it with a "write your own git" walkthrough: babashka CLI: automatic --help and shell completionsparse-opts*, coerce-opts, validate-opts, apply-defaults, table->treedispatch now accepts a tree directly (as returned by table->tree), and subcommand order is preserved in printed help and completions@babashka/cli npm packageopts->table accepts :columns to override the auto-detected columns (#148, thanks Jan Seeger)--no-foo on a non-boolean option errors instead of silently coercing, and :edn :coerce now requires an explicit value (#166, #174)Squint: CLJS syntax to JS compiler
str wrapping tripping esbuild), #819 (macro changes not picked up in watch mode), #820 (:macros option ignored from JS callers) and #832 (nREPL server hanging on advertised-but-unimplemented ops)--help, usage and error handling from babashka.cli's dispatch, plus shell tab completionbinding now work via a mutable box, safe across separately-compiled ESM modules; syntax-quote resolves symbols through the current namespace and aliases like Clojure. defprotocol got :extend-via-metadata support.reify addedclojure.walk addedsquint.edn/clojure.edn with a ~300-line EDN reader*print-fn*, print, pr and with-out-str, like CLJSsorted-map, hash-map, subvec, pop, merge, keys/vals, peek, transducers, = on dates/regexes/lazy seqs, and more) now throw or behave exactly like CLJS instead of the old loose JS semantics, alongside full built-in cljs.test support... spreaddefrecord, record? and the IRecord marker protocol. Records store their fields as own string-keyed properties and implement the map-facing protocols, so keyword lookup, keys, seq, assoc, conj and = all work through the regular core functions; the generated implementations are shared runtime functions imported only by files that use defrecordILookup, IAssociative, IMap, ICounted, ICollection, IEquiv, ISet, the transient protocols, and IAtom/IDeref/IReset/ISwap/IWatchable (so a reagent-style reactive atom can be a plain deftype)cljs.analyzer.api/resolve now sees vars of built-in library namespaces like clojure.string, plus :squint/compile-time forms and fixes for macro self-useclj-kondo: static analyzer and linter for Clojure code that sparks joy.
defmacro (plus any supporting defn/defn-/def) tagged with {:clj-kondo/macroexpand-hook true} is automatically extracted into .clj-kondo/ and registered as a :macroexpand hook on the next run. See doc/hooks.mdasync/await in ClojureScript: bumped built-in CLJS analysis to 1.12.145 and added the :await-without-async-fn and :misplaced-async-metadata linters:alias-same-as-ns, warns when an alias equals the namespace it aliases (default :off) (@tomdl89):conditional-build-up, warns on successive (if pred (assoc m ...) m) rebinding and suggests cond-> (default :off) (@walber-araujo):if-x-x-y, suggests (or x y) instead of (if x x y) (default :off) (@jramosg):redefined-var false positive across files declaring the same namespace:protocol-method-arity-mismatch false positive for definterface declaring the same method with multiple arities (@jramosg)recur inside a vector, map or set literal, since recur is never in tail position there:invalid-arity false positive when an inner binding or fn param shadows a local function name (@yuhan0)get-in/select-keys, faster sexpr, leaner node allocation (@alexander-yakushev):keys!/:syms!/:strs!), including inferring required keys and reporting them at call sites (#2870)SCI: Configurable Clojure/Script interpreter suitable for scripting
:closed allowlist for :classes, giving fine-grained control over host interop; see the interop control docs. Also 1.6x faster instance-method interop on babashka^"some.Class" x) bypassed the :classes allowlist, loading and static-initializing any class on the classpath at analysis time. Only affects sandboxing of untrusted code via :classes; upgrade to 0.13.53:interrupt-fn option: a zero-arg function called on every interpreted fn entry, so host code can interrupt or cancel a running SCI eval (thanks @whilo)sci.interrupt/interrupt! to throw an interrupt that sandboxed try/catch cannot catch, and gate finally and the regex functions (re-matches/re-find/re-seq, JVM) through :interrupt-fn too, closing off ways to mask an interrupt and escape the sandbox #1044copy-var incorrectly marking a function as inlined when its unqualified name collided with a clojure.core/cljs.core inlined var (e.g. a custom get), silently breaking with-redefs (@verberktstan)defrecord/deftype type symbol resolution via alias (e.g. (instance? r/Foo x)), fixing nbb#410fs: file system utility library for Clojure
@babashka/fs npm package. Most functions are supported. The JVM behavior is the reference implementation so all operations are synchronous, and the glob syntax is reimplemented from scratch to match the JVM. File times are BigInt nanoseconds to preserve sub-millisecond precision. zip is left out since Node.js has no native support for itspit and slurp on both the JVM and Node.jsexec-paths returns [] when PATH is unset or blank instead of throwingcopy, copy-tree, delete-tree, zip/unzip, gunzip and the setters explicit and documented/tested (#197)Babashka: native, fast starting Clojure interpreter for scripting.
bb.edn.with-redefs on copied vars (e.g. org.httpkit.client/get) incorrectly treated as inlinedorg.jline.keymap.BindingReader for reading key bindings in terminal applications, completing the input side of the bundled JLine APIclojure.lang.ChunkedCons, clojure.lang.APersistentVector$SubVector, clojure.lang.ArraySeq, clojure.lang.PersistentVector$ChunkedSeq, java.util.AbstractCollection and java.util.Queue to :instance-checks (@paintparty)examples/tetris.clj) built on JLine's Display and AttributedString, showing off the new terminal APIsReagami: A minimal zero-deps Reagent-like for Squint and CLJS
:key on children for stable node identity, so diffing reuses nodes instead of recreating them:lite-mode compatibility and added it to CI (#41)Cream: Clojure + GraalVM Crema native binary
html: Html generation library inspired by squint's html tag
style maps emitting a literal \n between declarations via pr-str, which produced invalid CSS and dropped every declaration after the first (@cycl1st)style; other map-like values (e.g. records) now render via str (@telekid)Edamame: configurable EDN and Clojure parser with location metadata and more
:auto-resolve-ns, bare syntax-quoted symbols now resolve to the current namespace, matching Clojure's behaviorNeil: A CLI to add common aliases and features to deps.edn-based projects
neil dep upgrade now upgrades unstable deps (e.g. release candidates) to a newer unstable version when no newer stable version existsbrew trust for users who installed neil before Homebrew introduced tap trustNbb: Scripting in Clojure on Node.js using SCI
deps.clj: a faithful port of the clojure CLI bash script to Clojure
Pod-babashka-gozxing: a babashka pod for QR code and barcode decoding/encoding, backed by gozxing
Graal-build-time: initialize Clojure classes at build time for GraalVM native-image
Contributions to third party projects:
async/await support from last cycle on the ClojureScript site, including an enhanced reference (#423, #424)dom.cljs to .cljc, adjusting core.cljc for portability), added babashka/squint test runners and wired them into CI, and fixed a multi-root render bug under squint by switching DOM state tracking to a node-map (#71, #72, merged)These are (some of the) other projects I'm involved with but little to no activity happened in the past two months.
Published: 2026-07-06
Tagged: clojure oss updates