OSS updates September and October 2026

In this post I'll give updates about open source I worked on during September and October 2026.

To see previous OSS updates, go here.

Sponsors

I'd like to thank all the sponsors and contributors who make this work possible. Without you, the projects below would not be as mature or would not exist or be maintained at all! So a sincere thank you to everyone who contributes to the sustainability of these projects.

gratitude

Current top tier sponsors:

Open the details section for more info about sponsoring.

Sponsor info

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!

Intro

A lot happened again in the last two months! One major highlight was my visit to the Clojure Conj together with Rahul Dé, where we organized a Babashka workshop. It was great seeing lots of familiar and new faces over there. To give people at the workshop the best experience possible, lots of improvements went into babashka prior to the Conj... workshop driven development I guess. The major milestones I focused on during the last 6-8 months were:

  • Automatic command line help and completions in babashka tasks via babashka.cli
  • Resolving dependencies without a JVM (by vendoring tools.deps and rewriting the maven part)
  • Babashka FFI: a foreign function library to call C-ABI functions from libraries
  • Better nREPL support: support popular nREPL middleware like CIDER's test middleware, the CIDER inspector, etc
  • TUI support via JLine

All of this combined made for a great workshop, where we developed a stateless password manager that used cryptography either by shelling out or by FFI. This project combined the most-used libraries in babashka, like babashka.fs, babashka.process, babashka.cli, babashka.ffi, etc. so even if you didn't end up using this password manager (most people probably didn't) you certainly were exposed to what you would typically use in a bb script or project. Rahul and I will give this workshop at EuroClojure 2027 as well, so in case you missed it, you'll get one more chance!

Babashka workshop at Clojure/conj 2026, seen from the back of the roomRich Hickey and Rahul Dé getting his hamster hammock signed at Clojure/conj 2026

Highlights

Here are some personal favorite highlights from the last two months.

  • Squint

  • Antq, a tool to update outdated deps, now runs with bb!

  • You can now run build.clj in babashka with tools.build (which comes bundled with bb) and get --help and shell completions for your build tasks. Sean Corfield has been adopting this in projects he maintains. See for example: next.jdbc, HoneySQL, logging4j2 and rephrase

  • Babashka.ffi

    • First Clojars release on 2026-09-18, for the JVM, native image, CLJS and nbb. Node.js 26.1+ through node:ffi
    • JVM performance PRs by @Ramblurr (#36, #37)
    • Adopted by sqlite4clj, which moved from coffi to babashka.ffi (#26, @Ramblurr)
  • Clj-kondo

    • Type system improvements: (when (and (string? x) (pos? n)) (inc x)) will now warn since (string? x) implies x is a string and (inc x) will crash on that input
    • :unknown-map-key linter that warns when requesting keys from maps that provably aren't there: given (defn opts [] {:host "localhost"}), (:hots (opts)) will warn.

Blog posts

Blog posts that I published in this period:

Updates

Here are some highlights per project. See each project's CHANGELOG.md for the full list.

  • Babashka: native, fast-starting Clojure interpreter for scripting.

    • Resolve dependencies without a JVM by default. Set :deps-resolver :jvm in bb.edn or the add-deps map, or BABASHKA_DEPS_RESOLVER=jvm, to use Java as before. See the blog post
    • Make clojure.tools.deps and its script namespaces, including clojure.tools.deps.script.make-classpath2, available to scripts
    • Bundle clojure.tools.build.api, with patched install and javac for babashka. A tools.build dependency on the classpath takes precedence and runs from source
    • Add bb repl --connect [addr] to connect to an nREPL server with completion, eldoc, documentation lookup and Ctrl-C interruption. Accepts host:port, a port or unix://path. Reads .nrepl-port by default.
    • Bind the nREPL server to 127.0.0.1 by default instead of 0.0.0.0. Use bb --nrepl-server 0.0.0.0:1667 in Docker
    • Bundle nREPL ops for the CIDER inspector, test runner (C-c C-t) and cider-version
    • Add the nREPL interrupt op. Interruption is cooperative and does not stop tight loops
    • Smaller binary: the bundled Clojure sources are stored gzipped, and jline's class files are no longer embedded a second time as resources.
    • Add the CIDER debugger to the nREPL server: #dbg, #break, C-u C-M-x and the step commands
    • Add CIDER's track-state to the nREPL server: font-lock of loaded vars and the box around instrumented functions
    • Add babashka.deps.maven with find-versions, with-repository-credentials, active-proxy and model-repos
    • Block the certificates of the eMudhra intermediate CA compromise (@stathissideris)
    • The macOS amd64 binary requires macOS 11.0 or later
    • Full changelog
  • babashka.ffi: call C functions in shared libraries from Clojure.

    • 0.1.1: First standalone release of babashka.ffi, available on Clojars for JVM Clojure, ClojureScript and nbb
    • Call C libraries from JVM Clojure on JDK 25 or newer, or from applications compiled with GraalVM 25 or newer. ClojureScript, nbb and shadow-cljs can call C libraries on Node.js 26.1 or newer through node:ffi
    • #63: callback accepts struct layouts as argument and return types
    • JVM call performance by @Ramblurr (#36, #37)
    • New examples: JavaScriptCore, pinball, SDL3, libusb and QuickJS
  • babashka.esbuild: use esbuild from babashka and the JVM

    • New project. Bundle and transform JavaScript, TypeScript, JSX and CSS through babashka.ffi, without Node.js
    • Expose esbuild's metafile and metafile analysis (#3, @lread)
    • Used by cljdoc and hyper
  • deps-deploy: deploy jars to Clojars and Maven Central from babashka

    • New project. Takes the options of slipset/deps-deploy, with transfers through babashka.http-client
    • GPG signing, local install, snapshots and several jars in one deploy
    • Maven Central through the Publisher Portal
  • SCI: Configurable Clojure/Script interpreter suitable for scripting

    • Clojure 1.13 map destructuring as of clojure/clojure 33fb064d (1.13.0-alpha11): :req!, :excess and :missing directives, :defaults removed. Adds the selector macro to clojure.core.
    • Speed up binding (about 4x on the JVM, 2x in native bb) and try without a catch (about 4x)
    • ns-map is 6x faster and ns-refers 60x
    • Fix #1094: a call to a multi-arity fn created a new fn for the called arity on every call, 25x slower than a single-arity call in CLJS
    • CLJS: creating a closure inside a jitted fn is 4x to 9x faster, and creating a multi-arity fn compiles instead of escaping to the interpreter
    • Fix instance? on a protocol returning true for a type the protocol is extended to, such as nil or Object, on the JVM
    • Full changelog
  • clj-kondo: static analyzer and linter for Clojure code that sparks joy.

    • NEW linter :unknown-map-key: warn on a keyword lookup of a key not present in a map. E.g. (:hots (opts)) will warn when opts returns {:host "localhost"}.
    • Catch up with Clojure 1.13.0-alpha9 destructuring
    • Type checker: a type predicate narrows the type of a local in if-not, cond and and. E.g. (when (and (string? x) (pos? n)) (inc x)) will warn.
    • Type checker: merge and merge-with require a map or nil as first map argument. E.g. (merge [1 2] [3 4]) will warn.
    • #2774: :warn-on-reflection also warns on Java constructor calls. E.g. (Date. x) will warn.
    • #3006, #3014, #3015, #3016: fixes for hooks generated with :clj-kondo/macroexpand-hook: referred macros, helper namespaces, requires without alias and nested hooks (@imrekoszo)
    • #2945: support :order-by, :having and :timeout in Datalevin and Datahike queries
    • #2998: analyze Java 25 class files (@skylightis666)
    • Full changelog
  • Squint: CLJS syntax to JS compiler

    • #453: Add source maps for compiled files
    • #594: support for await in doseq, with ^:await on a binding, (doseq [^:await x xs] ...), or on the binding vector for every binding (#1053, @shaunlebron)
    • Add eduction, implements? and keyword-identical?
    • Optimize compiled output for or, and and reify
    • Optimize compiled output for core predicates such as even? and string? in if, and, or and not
    • #1032: #html: escape attribute values from {:& m} (@mskyan)
    • Playground: Implement draggable resizing of editor and compile panes (#1045, @paintparty)
    • Playground: Support codemirror dark theme across editor, repl output, and compiled code instances (#1045, @paintparty)
    • Full changelog
  • neil: A CLI to add common aliases and features to deps.edn-based projects

    • 0.3.71: neil requires babashka 1.13.229 or later
    • #254: auto-generated CLI help and shell completions via babashka.cli. neil dep --help no longer throws
    • #272: make babashka.neil/-main easier to call from code (@lread)
    • #268: neil dep upgrade LIB upgrades only LIB instead of all deps
    • #267: neil dep add, dep upgrade and dep versions find versions in the :mvn/repos of deps.edn. dep versions no longer lists snapshots
  • babashka CLI: Turn Clojure functions into CLIs!

    • #226: use metadata and docstrings from :fn and :exec-fn vars in babashka and ClojureDart
    • dispatch: add :supplied to the opts map's :org.babashka/cli metadata with the set of option keys given on the command line
    • #233: Generated help no longer prints extra spaces when :prog is not specified (@lread)
    • Full changelog
  • nbb: Scripting in Clojure on Node.js using SCI

    • Add babashka.ffi built-in library for calling C functions. Requires Node.js 26.1 or newer
    • Bump SCI: faster multi-arity function dispatch, bumps edamame to 1.6.44
  • Edamame: configurable EDN and Clojure parser with location metadata and more

    • Performance: speed up parsing plain decimal numbers on the JVM #155
    • Performance: build map literals with transients #159
    • Require Clojure 1.10 or newer #158
  • deps.clj: a faithful port of the Clojure CLI Bash script to Clojure

    • Allow --version, --help, -Sdescribe and a cached -Spath to run without Java.
    • Add *make-classpath-fn* to allow in-process classpath resolution.
    • Catch up with Clojure CLI 1.12.6.1673
  • rewrite-edn: Utility lib on top of rewrite-clj

    • #39: Support namespaced maps like #:a{:x 1}
    • neil#239: Read and update maps with metadata like ^{:x 1} {:a 1}
    • Fix get, assoc, update and dissoc when a map value equals the key
  • http-client: HTTP client for Clojure and babashka

    • #86: uri-with-query no longer decodes the existing query, fragment and user info when :query-params are used (@dylanpulver)
    • #91: decode-body passes a nil body through, e.g. of a 407 to CONNECT
  • fs: file system utility library for Clojure

    • #284: non-recursive match and glob now return immediate child directories, not only files
    • #269: match and glob no longer drop pattern text after a 2nd colon on Unix (@marksto)
    • #279: minimum supported version of Clojure is now 1.11 (@lread)
  • html: HTML generation library inspired by squint's HTML tag

    • Omit attributes whose value is nil, or false in html mode: [:input {:disabled false}] rendered disabled="false", which disables the input. Matches hiccup (#19, @telekid)
    • Render true and false as "true" and "false" for aria-*, data-*, contenteditable, draggable, focusable and spellcheck. Names match in any case, so :spellCheck works as well
  • quickblog: lightweight static blog engine for Clojure and babashka

    • Add :pre-process-markdown-fn and :post-process-html-fn options to transform each post's Markdown and HTML (README) (@jmglov)
  • gh-release-artifact: Upload artifacts to GitHub releases idempotently

    • Throw when Github does not accept an upload. A failed upload no longer leaves a green build
    • Upload the new bytes before touching the existing asset when replacing one
  • Buzz: write a web application with the JVM or babashka only

    • New observe, atom-source and buzz.source/Source API. :watch and invalidate! are gone (#20)
  • raylib-bb-ffi: raylib bindings for babashka via babashka.ffi

    • New experiment, ported with an LLM. Tests whether the examples of raylib-jlt and raylib-clj run in babashka and on the JVM through babashka.ffi
  • cljbang: a Clojure-like language that runs as Emacs Lisp

    • Add for (#12) and juxt (#9, @kpassapk)
    • Fix #10: str/join with a separator and an empty coll returns ""
  • Cherry: Experimental ClojureScript to ES6 module compiler

    • Bump squint: don't crash the nREPL server on a SyntaxError from eval (#208)
  • Cream: Clojure + GraalVM Crema native binary

    • Compare load times, tooling and GraalVM build with babashka (#7)
  • lein-clj-kondo and clj-kondo-bb: released alongside each clj-kondo release

Contributions to third party projects:

  • antq: antq runs on babashka (#316, #380) and is installable through bbin (#386)
  • hyper: compile h/expr against a served squint core, tree-shaken with esbuild. See the blog post. Serve source maps for defc components behind :source-map?
  • nextjournal/markdown: #71: render the link title in the default :link renderer
  • clojure-mode: bind Option+S to unwrap on macOS (#62)

Other projects

These are some other projects I'm involved with, but little to no activity happened in the past two months.

Click for more details

Published: 2026-10-30

Tagged: clojure oss updates

Tree shaking squint on the server with esbuild

Hyper is a server-rendered web framework for Clojure. You can write pages using hiccup, and hyper renders them on the server to HTML. Incremental updates to the HTML reach the browser through server-sent events (SSE), using Datastar.

Here is a basic counter example:

(defn home-page [req]
  (let [count* (h/tab-cursor :count 0)]
    [:div
     [:h1 "Count: " @count*]
     [:button {:data-on:click (h/action (swap! count* inc))}
      "Increment"]]))

Some interactions can be handled directly in the browser. A hint below a search field, for example, can update as you type without a round trip to the server. Datastar stores client state in so called signals and evaluates JavaScript expressions in data-* attributes. With hyper's h/expr macro, you can write those expressions in Clojure syntax:

(let [query* (h/signal :query "")]
  [:div
   [:input {:data-bind query*}]
   [:span {:data-text (h/expr (if (zero? (.-length @query*))
                                "Type to search"
                                (str "Searching for " (subs @query* 0 20))))}]])

The :data-bind attribute binds the input to the query signal. Inside h/expr, @query* compiles to Datastar's $query expression. During macro expansion, Squint compiles the expression to JavaScript:

((($query.length === 0)) ? ("Type to search") : (`${"Searching for "}${hyper_sc.subs($query, 0, 20)}`))

Tree-shaking on the server

The compiled expression calls subs from squint's core library, which hyper loads as window.hyper_sc. The full library is about 128 KB, or 33 KB gzipped. With esbuild, hyper can remove unused core functions through tree shaking and serve a smaller bundle. Although esbuild is written in Go, it is usually invoked through its npm package. For hyper, it would be convenient to call it directly from Clojure.

Enter babashka.esbuild! This library calls esbuild as a shared library through babashka.ffi. It works on the JVM and in babashka:

{:deps {io.github.squint-cljs/squint {:mvn/version "0.14.210"}
        org.babashka/esbuild {:mvn/version "0.1.1"}}}

Here is how to compile a squint function to a JavaScript module and bundle it with esbuild:

(require '[squint.compiler :as squint]
         '[babashka.esbuild :as esbuild]
         '[clojure.java.io :as io])

(spit "core.js" (slurp (io/resource "squint/core.js")))

(spit "main.js"
      (squint/compile-string
       "(defn search-hint [query]
          (if (zero? (.-length query))
            \"Type to search\"
            (str \"Searching for \" (subs query 0 20))))"
       {:import-maps {"squint-cljs/core.js" "./core.js"}}))

(-> (esbuild/build {:entry-points ["main.js"]
                    :bundle true
                    :format :esm
                    :minify true})
    :outputs first :contents)

The compiled main.js looks like this:

import * as squint_core from './core.js';
var search_hint = function (query) {
if ((query.length === 0)) {
return "Type to search"} else {
return `${"Searching for "}${squint_core.subs(query, 0, 20)}`};

};

export { search_hint }

After bundling and minification, esbuild returns:

function e(t,n,r){return t.substring(n,r)}var u=function(t){return t.length===0?"Type to search":`Searching for ${e(t,0,20)}`};export{u as search_hint};

The bundle contains just subs and the search-hint function: 152 bytes in total (145 gzipped). The full core library alone was 128,117 bytes (32,650 gzipped).

In hyper

In hyper, the compiled h/expr expressions end up in HTML attributes. To find out which core functions these expressions need, hyper uses information returned by the squint compiler. Since version 0.14.210, squint includes the names of the core functions used during compilation:

(:used-core-vars (squint/compile* "(str \"Searching for \" (subs query 0 20))"))
;;=> #{"subs"}

Hyper collects these names from every h/expr and every defc component in the application. At startup (when using the :tree-shake? option), it passes esbuild an entry module that re-exports those functions:

export { subs } from './core.js';

Hyper serves the resulting bundle at /hyper/squint-core.js and exposes it as window.hyper_sc. The defc components use the same object:

const $sc = window.hyper_sc;

The URL's v parameter is a hash of core.js and the names of the core functions used by the application. The bundle stays the same for a given hash, so hyper serves it with a one-year cache lifetime:

/hyper/squint-core.js?v=3f9a1c2b7d4e
Cache-Control: public, max-age=31536000, immutable

When a deployment changes the set of core functions in use, the hash changes. The page records its current version in a data attribute:

<div id="hyper-app" data-hyper-squint-version="1a2b3c4d">

If a server update contains a different version, the page reloads to fetch the new bundle.

To enable tree shaking in hyper, add the esbuild dependency and enable native access for the JVM:

{:deps {org.babashka/esbuild {:mvn/version "0.1.1"}}
 :aliases {:run {:jvm-opts ["--enable-native-access=ALL-UNNAMED"]}}}

Then pass :tree-shake? true when creating the handler:

(h/create-handler #'routes :tree-shake? true)

Since the dependency is optional, hyper serves squint's full core.js by default.

Note that hyper has h/defc to define webcomponents using Squint too, which also work with tree-shaking, but I left that out of this blog to keep it short and readable.

Wrapping up

As you can see, we can write server-side rendered HTML applications and have some fun using ClojureScript in the form of Squint too, while getting very reasonable JS compilation sizes. And we don't have to think about it at all, just deploy to production with :tree-shake? true and done! No build process.

Published: 2026-09-25

Tagged: clojure hyper babashka squint

Babashka 1.13.222: the conj release

Babashka 1.13.222 is the "conj" release! I'll be giving a babashka workshop at Clojure/conj together with Rahul De. Hope to see you there!

Dependencies without a JVM

Babashka now resolves dependencies without a JVM by default. Here's a demo: We will use an alternative :mvn/local-repo such that we force downloading deps.

deps-example.clj:

(require '[babashka.deps :as deps]
         '[babashka.fs :as fs])

(deps/add-deps
 {:mvn/local-repo (str (fs/create-temp-dir {:prefix "bb-mvn-"}))
  :deps '{medley/medley {:mvn/version "1.4.0"}}})

(require '[medley.core :as medley])

(prn (medley/index-by :id [{:id 1 :name "Ada"}
                           {:id 2 :name "Grace"}]))

To make sure this example runs without Java, we will remove it from the PATH and set JAVA_HOME to a non-existing directory as well:

# Find bb before clearing PATH.
bb_executable=$(command -v bb)
env PATH= JAVA_HOME=/does-not-exist "$bb_executable" -Sforce deps-example.clj
# {1 {:id 1, :name "Ada"}, 2 {:id 2, :name "Grace"}}

If you encounter a bug in the new native resolver, switch back to the JVM resolver:

export BABASHKA_DEPS_RESOLVER=jvm
bb deps-example.clj

To select the JVM resolver per project, set this in bb.edn:

{:deps-resolver :jvm}

Bundles tools.deps

The native resolver is built on top of clojure.tools.deps. You can now use that directly from bb without adding a dependency:

(require '[clojure.tools.deps :as deps]
         '[babashka.fs :as fs])

(def basis
  (deps/create-basis
   {:root nil :user nil :project nil
    :extra '{:deps {medley/medley {:mvn/version "1.4.0"}}}}))

(mapv fs/file-name (:classpath-roots basis))
;;=> ["medley-1.4.0.jar" "clojure-1.9.0.jar"
;;    "core.specs.alpha-0.1.24.jar" "spec.alpha-0.1.143.jar"]

Bundled tools.build

In this release, we're going one step further. Since tools.build is built on tools.deps and we now have it in bb, it was only a small leap to also add tools.build.

This example packages a src directory with a build.clj running in babashka:

(ns build
  (:require [clojure.tools.build.api :as b]))

(def class-dir "target/classes")

(defn jar [_]
  (b/copy-dir {:src-dirs ["src"]
               :target-dir class-dir})
  (b/jar {:class-dir class-dir
          :jar-file "target/app.jar"}))

Add a task in bb.edn:

{:paths ["."]
 :tasks {jar {:exec-fn build/jar}}}
bb jar

Everything runs in babashka and we did not need to add a dependency on tools.build. When invoking tasks that require Java compilation, of course, a JVM will be necessary still.

deps.deploy

To make sure typical build.clj files will run fully in babashka, a bb-compatible variant of the excellent slipset/deps-deploy library was made: babashka/deps-deploy. I consulted with Eric Assum to perhaps make this library bb-compatible, but his reply was: what if people just used yours from now on... oops ;).

{:paths ["."]
 :deps {io.github.babashka/deps-deploy {:mvn/version "0.0.1"}}
 :tasks {jar {:exec-fn build/jar}
         deploy {:exec-fn build/deploy}}}

CLI support

Add these requires and the deploy function to build.clj. The :org.babashka/cli metadata supplies option parsing, help and completions:

(ns build
  (:require [babashka.deps-deploy :as dd]
            [clojure.tools.build.api :as b]))

(def lib 'io.github.yourname/app)
(def version [0 1 0])
(def class-dir "target/classes")

(defn deploy
  {:org.babashka/cli
   {:spec {:bump {:coerce :boolean
                  :desc "Increment the patch version before deploying"}}}}
  [{:keys [bump]}]
  (let [version (cond-> version bump (update 2 inc))]
    (b/write-pom {:class-dir class-dir
                  :lib lib
                  :version (apply format "%s.%s.%s" version)
                  :basis (b/create-basis {:project {:paths ["src"]}})
                  :src-dirs ["src"]})
    (jar nil)
    (dd/deploy {:artifact "target/app.jar"
                :pom-file (b/pom-path {:lib lib
                                       :class-dir class-dir})})))

Like with slipset/deps-deploy, set CLOJARS_USERNAME and CLOJARS_PASSWORD to your Clojars username and deploy token.

bb deploy --help
bb deploy
bb deploy --bump

To enable completions in zsh after compinit:

source <(bb org.babashka.cli/completions snippet --shell zsh)

For other shells, check the completions section in babashka.cli's docs. Completions should now show this:

$ bb deploy --<TAB>
--bump  -- Increment the patch version before deploying
--help  -- Show this help

A few fixes went into babashka to help with task :exec-fns that depend on each other which should now work smoother as well.

nREPL

Babashka already had nREPL support, but this release extends it to the wider nREPL Clojure ecosystem. As you were used to, you can start a bb nREPL like this:

bb nrepl-server 1667

You can now connect from another terminal and you'll get a nice jline REPL in which you can talk to your nREPL server:

bb repl --connect 1667

Completion, eldoc, documentation lookup and Ctrl-C interruption all should work. Of course you can talk to any nREPL server, including a normal JVM Clojure one.

The built-in nREPL server now runs nREPL 1.7.0, with CIDER inspector and test runner middleware support (the most frequently missing feature in bb nREPL). It now binds to 127.0.0.1 by default instead of 0.0.0.0.

Programmers (or LLM agents) can use the bundled nrepl.core client to evaluate code in a running REPL. Save as eval.clj:

(require '[nrepl.core :as nrepl])

(with-open [conn (nrepl/connect :port 1667)]
  (prn (-> (nrepl/client conn 1000)
           (nrepl/message {:op "eval"
                          :code "(reduce + (range 10))"})
           nrepl/response-values)))
bb eval.clj
# [45]

Here's an example of custom middleware. Log the code your editor or client evaluates. Save as middleware.clj:

(require '[nrepl.server :as server]
         '[nrepl.middleware :refer [set-descriptor!]])

(defn wrap-log-eval [handler]
  (fn [{:keys [op code] :as msg}]
    (when (= "eval" op)
      (spit "nrepl-eval.log" (str code "\n") :append true))
    (handler msg)))

(set-descriptor! #'wrap-log-eval
                 {:requires #{"clone"}
                  :expects #{"eval"}
                  :handles {}})

(def nrepl-server
  (server/start-server :bind "127.0.0.1"
                       :port 1667
                       :handler (server/default-handler #'wrap-log-eval)))

Start the server:

bb middleware.clj

Connect from another terminal:

bb repl --connect 1667
user=> (reduce + (range 10))
45

Inspect the log on the server:

tail -f nrepl-eval.log
# (reduce + (range 10))

Wrapping up

Hope you like these additions to babashka:

  • Fetching dependencies without a JVM
  • Bundled tools.deps and tools.build support
  • babashka.deps-deploy which completes the gap of running build.clj fully in bb now
  • You can get completions for your build.clj functions by adding them as :exec-fn in bb.edn. You can get enhanced option parsing and completions by adding :org.babashka/cli metadata to your build.clj functions.
  • Extended nREPL support, including custom middleware and CIDER inspector + test runner middleware.
  • nrepl.core and related namespaces are now exposed and used in babashka's own nREPL server. You can use it to connect to otherservers as well. Babashka's repl has a new --connect option which uses this through its own jline REPL.

Full changelog.

Published: 2026-09-14

Tagged: clojure babashka

Archive