Babashka CLI is a library to write command line tools. It is available in babashka by default. This library was born out of some frustration with clojure -X's functionality where people have to write raw EDN on the command line, which to me isn't a good user experience (especially not for people using Powershell where quoting rules are different than in bash and zsh). Babashka wants to give Clojure users a good scripting experience, no matter what OS or shell you are using.
While Babashka CLI had all the ingredients for parsing and formatting options (for help) and for multi-command (or subcommand) style (e.g. git remote show origin) invocations, you still had to write your own --help functionality. Also Babashka CLI didn't offer anything for getting shell completions. These two gaps existed as open Github issues for about three years now. What held me back in implementing these features was: A) I found help output for multi-command CLIs always a bit too opinionated. Every CLI I knew was doing it differently. Which one should I pick for bb.cli? and B) to implement shell completions I actually had to know something about shells I did not use personally. After looking at a couple of other libs like Howard M. Lewis Ship's cli-tools, and Lambdaisland CLI and a couple more non-Clojure libraries, I decided I should just pick a help output that looks reasonable and offer an API to do your own thing if you want to do that. For implementing the completion support I re-used the branch that Sohalt and I worked on in 2024. Additionally I used Claude Code to get this work over the hump. Studying how Powershell or nushell completions work in detail just isn't that interesting to me and I was happy to defer most of the shell-specific nitty-gritty. One extra bonus feature is the nested command notation instead of the "table". This already existed in Babashka CLI for a while, but it's now exposed for users.
The features described in this post are available as of Babashka CLI v0.11.73:
org.babashka/cli {:mvn/version "0.11.73"}
Let's dig into an example to learn more about the new features!
Yeah, we're going to write our own git, but don't worry, we'll not write our own VCS! We'll leave that up to Zach Oakes. Just the CLI interface this time and we'll let ourselves off the hook with println to fake the implementation. So here's a bit of code for you to look at. There's a bunch of functions like clone, log, checkout etc. that just print some info to stdout. The tree describes the command structure. And the dispatch call at the end dispatches the command line arguments over the tree.
#!/usr/bin/env bb
(require '[babashka.cli :as cli]
'[clojure.string :as str])
;; stand-ins; a real tool would shell out to git
(def ^:private branches ["main" "develop" "feature/login" "release/2.0"])
(def ^:private remotes ["origin" "upstream" "fork"])
(defn clone [{:keys [opts]}]
(println "Cloning" (:url opts)
(when (:depth opts) (str "(depth " (:depth opts) ")"))))
(defn log [{:keys [opts]}]
(println "Showing" (or (:max-count opts) "all") (name (:format opts)) "log entries"))
(defn checkout [{:keys [opts]}]
(println (if (:create opts) "Creating and switching to" "Switching to") (:branch opts)))
(defn remote-add [{:keys [opts]}]
(println "Added remote" (:name opts) "->" (:url opts)))
(defn remote-remove [{:keys [opts]}]
(println "Removed remote" (:name opts)))
(defn remote-list [_]
(run! println remotes))
(def tree
{:spec {:verbose {:coerce :boolean :desc "Be verbose" :alias :v}}
:cmd
{"clone"
{:fn clone :doc "Clone a repository into a new directory"
:spec {:url {:desc "Repository to clone from" :require true}
:depth {:desc "Create a shallow clone with N commits" :coerce :long}}
:args->opts [:url]}
"log"
{:fn log :doc "Show commit logs"
:spec {:format {:desc "Output format" :coerce :keyword
:validate #{:oneline :short :full}
:default :short}
:max-count {:desc "Limit the number of commits" :coerce :long :alias :n}}}
"checkout"
{:fn checkout :doc "Switch branches"
:spec {:branch {:desc "Branch to switch to" :coerce :string
:complete-fn (fn [{:keys [to-complete]}]
(filter #(str/starts-with? % to-complete) branches))
:require true}
:create {:desc "Create the branch before switching" :coerce :boolean :alias :b}}
:args->opts [:branch]}
"remote"
{:doc "Manage the set of tracked repositories"
:cmd-order ["add" "remove" "list"]
:cmd
{"add"
{:fn remote-add :doc "Add a remote"
:spec {:name {:desc "Remote name" :require true}
:url {:desc "Remote URL" :require true}}
:args->opts [:name :url]}
"remove"
{:fn remote-remove :doc "Remove a remote"
:spec {:name {:desc "Remote name" :coerce :string
:complete-fn (fn [{:keys [to-complete]}]
(filter #(str/starts-with? % to-complete) remotes))
:require true}}
:args->opts [:name]}
"list" {:fn remote-list :doc "List the existing remotes"}}}}
:epilog "Docs: https://example.com/mygit"})
(defn -main [& args]
(cli/dispatch tree args {:prog "mygit" :help true}))
(apply -main *command-line-args*)
The :prog value is used in help output and represents the program name. The :help true setting activates automatic help support. The automatic help support re-uses the already existing :desc (for options) /:doc (for commands) documentation values. When :validate is a set of keywords, auto-completion will pick up on this to autocomplete that option's value.
Save this code as mygit.clj and make it executable.
chmod +x mygit.clj
Note that at the time of writing, Babashka CLI version 0.11.73 isn't part of the newly released bb yet. This is coming soon, but there's more work to be done in babashka, to make babashka tasks even more awesome, which is going to be using part of the new CLI functionality. Stay tuned. For now you can add this snippet to the top of your code to make a bb script pick up on the newest CLI version:
(require '[babashka.deps :as deps])
(deps/add-deps '{:deps {org.babashka/cli {:mvn/version "0.11.73"}}})
(require '[babashka.cli] :reload)
Now we can invoke this script with ./mygit.clj. The usage line below will display mygit, because of the :prog setting, its display name, independent of how the script is invoked.
So let's invoke it in a couple of different ways:
$ ./mygit.clj clone https://example.com/repo.git --depth 1
Cloning https://example.com/repo.git (depth 1)
$ ./mygit.clj checkout -b feature/login
Creating and switching to feature/login
$ ./mygit.clj log -n 5 --format oneline
Showing 5 oneline log entries
$ ./mygit.clj remote add origin https://example.com/repo.git
Added remote origin -> https://example.com/repo.git
The :help true option to dispatch enriches the command tree with --help / -h options at every level, including the top level of the tree. It will also include a terse error message when invalid command line options are provided. So this is the opinionated help support that you can use as a good default, but don't have to use if you want to do your own thing. When --help/-h is invoked explicitly, the exit code will be 0 and help output is printed to stdout. On invalid input, output is printed to stderr and the exit code will be 1.
This is what top level help output looks like: ./mygit.clj --help:
Usage: mygit [options] <command>
Commands:
clone Clone a repository into a new directory
log Show commit logs
checkout Switch branches
remote Manage the set of tracked repositories
Options:
-v, --verbose Be verbose
-h, --help Show this help
Run "mygit <command> --help" for more information on a command.
Docs: https://example.com/mygit
The per line description of a command comes from the :doc key, and the per line description of an option comes from the :desc key. Trailing prose can be provided via the :epilog key, which here is "Docs: https://example.com/mygit".
Every individual command also supports --help in a similar way:
Usage: mygit checkout [options] <branch>
Switch branches
Options:
--branch Branch to switch to (required)
-b, --create Create the branch before switching
-h, --help Show this help
Run "mygit --help" for global options.
Babashka CLI supports the :args->opts option to coalesce arguments into options. This is why we see <branch> printed as a supported argument. The (required) suffix comes from :require true in an option's spec and the short -b comes from the :alias setting.
In our git implementation (unlike the real one), the remote command does not invoke a function on its own. It just provides a :doc value, describing what the group of child commands are for.
Running ./mygit.clj remote --help lists the group's children:
Usage: mygit remote [options] <command>
Manage the set of tracked repositories
Commands:
add Add a remote
remove Remove a remote
list List the existing remotes
Options:
-h, --help Show this help
Run "mygit remote <command> --help" for more information on a command.
Run "mygit --help" for global options.
Invoking ./mygit.clj remote add --help shows the help of remote add, with both positional arguments in the usage line:
Usage: mygit remote add [options] <name> <url>
Add a remote
Options:
--name Remote name (required)
--url Remote URL (required)
-h, --help Show this help
Run "mygit --help" for global options.
A mistyped or missing command gives a terse error and exits with exit code 1:
$ ./mygit.clj statys
Unknown command: statys
Commands:
clone Clone a repository into a new directory
log Show commit logs
checkout Switch branches
remote Manage the set of tracked repositories
Run "mygit --help" for more information.
In Babashka CLI shell completions are produced dynamically by letting the shell call back into the CLI. As of today, Babashka CLI supports bash, zsh, fish, powershell and nushell.
We're just going to show here how to get completions for zsh but the process is very similar for other shells.
The ./mygit.clj org.babashka.cli/completions snippet --shell zsh invocation spits out a zsh snippet to stdout specific to this CLI. The org.babashka.cli/completions is inserted by Babashka CLI.
To enable completions in zsh (after compinit), run:
source <(./mygit.clj org.babashka.cli/completions snippet --shell zsh)
This enables completions for commands and options, showing descriptions on the side. Already used options are not suggested again, unless they are expected to be used multiple times.
$ ./mygit.clj remote <TAB>
add -- Add a remote
remove -- Remove a remote
list -- List the existing remotes
The :validate set on log --format doubles as its completion source without adding extra config:
$ ./mygit.clj log --format <TAB>
full oneline short
Dynamic values can be supplied with :complete-fn. In our git example, branch names and remotes are completed by :complete-fn.
$ ./mygit.clj remote remove <TAB>
origin upstream fork
$ ./mygit.clj checkout <TAB>
main develop feature/login release/2.0
To see what the completer returns without a shell, you can call the completions command directly:
$ ./mygit.clj org.babashka.cli/completions complete --shell zsh -- remote ''
add Add a remote
remove Remove a remote
list List the existing remotes
--help Show this help
-h Show this help
After holding off and thinking about these issues for a couple of years, I finally bit the bullet and added help and completion support to Babashka CLI. Hope you'll enjoy it!
More exciting related stuff is coming soon. The new Babashka CLI will be integrated into babashka of course, but also babashka tasks will be pimped with automatic help and completions. I'm not yet done with that work though.
Meanwhile I've been porting squint and neil over to the automatic help already.
A special shout-out to @lread for a ton of documentation review and improvements, and general maintenance. Thanks to @sohalt for the initial shell completions work back in 2024 that I picked up again for this release. Thanks to @plexus for his excellent Lambdaisland CLI talk at Babashka Conf 2026. Thanks also to Nextjournal whose commercial app I'm taking as a case study for this work, and last but not least to Clojurists Together and Sponsors on Github for giving me the time to work on this.
Published: 2026-06-18
Today fellow Clojurian Søren Knudsen asked the following question on Clojurians Slack:
Say I'd like an overview of which fns in my Clojurescript app don't have
:xmetadata and aren't children of functions that have:x. I'd love this overview as data.Anyone know a relevant analysis tool for this purpose?
Let's represent this problem in code form. Read it from bottom to top.
(defn grandchild [] ; no :x, but reachable via child: ignore
:leaf)
(defn child [] ; no :x, called by ^:x grandparent: ignore
(grandchild))
(defn ^:x grandparent [] ;; has :x metadata, ignore
(child))
(defn standalone [] ;; has no :x metadata and not reachable from anything with :x metadata, include
:other)
It turns out that clj-kondo analysis data is well suited to solve this problem. In this blog post, let's write a babashka script, that uses the clj-kondo pod. This bit of setup lets you do that. Of course you could also use clj-kondo as a regular JVM dependency, but we're going for ease here, since it's just a tiny script at this point.
#!/usr/bin/env bb
(require '[babashka.pods :as pods]
'[clojure.set :as set])
(pods/load-pod 'clj-kondo/clj-kondo "2025.06.05")
(require '[pod.borkdude.clj-kondo :as clj-kondo])
Clj-kondo lets you find var-definitions and var-usages. Clj-kondo can also include var metadata. The arguments to clj-kondo's run! API function then should look like this:
(def analysis
(-> (clj-kondo/run! {:lint ["src"]
:config {:analysis {:var-definitions {:meta [:x]}
:var-usages true}}})
:analysis))
To illustrate how it works, we'll introduce a multi-namespace project:
;; src/app/core.cljs
(ns app.core
(:require [app.util :as util]))
(defn ^:x grandparent []
(util/child))
(defn standalone []
:other)
;; a top level var usage, not inside any var definition:
(util/child)
;; src/app/util.cljs
(ns app.util)
(defn grandchild []
:leaf)
(defn child []
(grandchild))
To illustrate what a var usage looks like in clj-kondo's analysis data, let's look at the usage in app.core of util/child:
{:from app.core
:from-var grandparent
:to app.util
:name child
...}
The :from key describes from which namespace the reference was used. The :from-var key describes in which var definition the var was used, and this is the key ingredient of tracking transitive var usages. The :to + :name keys describe which var was used.
In clj-kondo's analysis you can request metadata from vars with :meta [:x] (or all metadata with true). To distinguish all project vars from those that have :x metadata we can do the following:
(defn fq [ns name] (symbol (str ns) (str name)))
(def defs (:var-definitions analysis))
(def project-vars (set (map #(fq (:ns %) (:name %)) defs)))
(def with-x (set (keep #(when (-> % :meta :x) (fq (:ns %) (:name %))) defs)))
Here project-vars is a set of symbols of all the project vars and with-x are only those that have :x metadata.
Now we're ready to build the call graph that lets us solve our problem. In the following we're making a map that looks like: caller -> callees, but we limit callees only to project vars since we're not interested in vars like cljs.core/assoc, reagent.core/atom etc.
(def graph
(reduce (fn [g {:keys [from from-var to name]}]
(let [callee (fq to name)]
(if (and from-var (contains? project-vars callee))
(update g (fq from from-var) (fnil conj #{}) callee)
g)))
{}
(:var-usages analysis)))
The from-var condition leaves out any top level var usages. The (contains? project-vars callee) takes care of filtering only on project vars. After running this, we'll end up with a graph (map) that looks like:
{app.core/grandparent #{app.util/child}
app.util/child #{app.util/grandchild}}
So app.core/grandparent calls app.util/child and app.util/child calls app.util/grandchild.
Next we write a function to find out what vars are reachable from a set of vars starts.
(defn reachable [starts]
(loop [seen #{}
todo (set starts)]
(if (empty? todo)
seen
(let [seen (into seen todo)
used-vars (set (mapcat graph todo))
unvisited (set/difference used-vars seen)]
(recur seen unvisited)))))
(def children (set/difference (reachable with-x) with-x))
(prn {:graph graph
:with-x with-x
:children-of-x children
:without-x (set/difference project-vars with-x children)})
The reachable function just calculates the transitive closure of the graph, given a set of starting nodes (vars). The children var is the set of reachable vars without the starting points (the vars with :x metadata).
{:graph {app.core/grandparent #{app.util/child}
app.util/child #{app.util/grandchild}}
:with-x #{app.core/grandparent}
:children-of-x #{app.util/child app.util/grandchild}
:without-x #{app.core/standalone}}
So the answer we were looking for is #{app.core/standalone}. This function is neither a transitive child of any function with :x metadata, nor does it have any :x metadata itself.
Here's the full script once again.
#!/usr/bin/env bb
(require '[babashka.pods :as pods]
'[clojure.set :as set])
(pods/load-pod 'clj-kondo/clj-kondo "2025.06.05")
(require '[pod.borkdude.clj-kondo :as clj-kondo])
(def analysis
(-> (clj-kondo/run! {:lint ["src"]
:config {:analysis {:var-definitions {:meta [:x]}
:var-usages true}}})
:analysis))
(defn fq [ns name] (symbol (str ns) (str name)))
(def defs (:var-definitions analysis))
(def project-vars (set (map #(fq (:ns %) (:name %)) defs)))
(def with-x (set (keep #(when (-> % :meta :x) (fq (:ns %) (:name %))) defs)))
;; caller -> callees, project vars only
(def graph
(reduce (fn [g {:keys [from from-var to name]}]
(let [callee (fq to name)]
(if (and from-var (contains? project-vars callee))
(update g (fq from from-var) (fnil conj #{}) callee)
g)))
{}
(:var-usages analysis)))
(defn reachable [starts]
(loop [seen #{} todo (set starts)]
(if (empty? todo)
seen
(let [seen (into seen todo)
used-vars (set (mapcat graph todo))
unvisited (set/difference used-vars seen)]
(recur seen unvisited)))))
(def children (set/difference (reachable with-x) with-x))
(prn {:graph graph
:with-x with-x
:children-of-x children
:without-x (set/difference project-vars with-x children)})
I hope you learned how useful clj-kondo analysis data can be for tracking relations between vars and that you can use this data in casual babashka scripts as well!
Published: 2026-06-10
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