announcements

Filipe Silva 2026-01-21T16:29:51.704089Z

https://github.com/filipesilva/invoker v0.3.6 is out! This is the first public release of Invoker, and feedback/issues/usecases are very welcome. Invoker is a zero config CLI, HTTP, and REPL interface for Clojure. Invoked vars run in Clojure if there's a deps.edn, otherwise in Babashka. Commands will automatically connect to an existing nREPL server if available using .nrepl-port. The nvk httpand nvk repl commands start a nREPL server that can be connected to. Invoker aims to make the following usecases easy: • making a simple webapp from scratch, possibly with Datomic • running functions and inspecting atoms inside an existing process • running targetted tests with reloaded code • allowing agents to interact with your clojure process • adding dependencies and reloading code without restarting the process

🎊 6
❗ 1
👀 2
🤩 1
🎉 14
teodorlu 2026-02-01T16:39:31.759069Z

I somehow read this, didn't think more about it, then came back and figured how awesome what you've just done is. We can now effectively write shell script that interacts reasonably with one clojure REPL process! Thank you for your foresight. 🙌

teodorlu 2026-02-01T16:41:04.027769Z

One thing — I would love to include this in my projects as a babashka task, eg bb nvk. That would let me track the nvk version in bb.edn, instead of out-of-band bbin. Thanks again!

Filipe Silva 2026-02-01T16:44:54.506709Z

heya! yeah it's really meant to support that case, everything (including the add-lib stuff) really runs on the single process if there's one up

❤️ 1
Filipe Silva 2026-02-01T16:45:25.132009Z

hmmm I think you can put what's in the bbin key to run through bb?

Filipe Silva 2026-02-01T16:45:41.794679Z

{:bbin/bin       {nvk {:main-opts ["-m" "invoker.nvk"]}}
 :min-bb-version "1.12.208"
 :deps           {io.github.filipesilva/invoker {:local/root "."}}
 :paths          ["src" "test" "resources"]}

Filipe Silva 2026-02-01T16:46:09.308289Z

I don't remember right now what the bb task syntax is for this, but it looks very possible

Filipe Silva 2026-02-01T16:48:43.364839Z

something like this maybe?

{:tasks
 {:requires ([invoker.nvk :as nvk])
  nvk (nvk/-main *command-line-args*)}}

Filipe Silva 2026-02-01T16:50:03.547359Z

let me know if it works or if you hit any snag trying to make it work

teodorlu 2026-02-01T17:22:09.355099Z

sorry for the "fire question and disappear"! I tried this, which looks correct to me:

{:deps {io.github.teodorlu/bb-timemachine {:git/tag "v1.0.1" :git/sha "e684c"}
        io.github.filipesilva/invoker
        {:git/url "",
         :git/tag "v0.4.7",
         :git/sha "2d77d54869d8694a74fcafabea2536f4868a8387"}}
 :tasks
 {:requires ([invoker.nvk :as nvk])
  nvk (nvk/-main *command-line-args*)}}
but I'm hitting a CLI parsing error:
$ bb nvk
----- Error --------------------------------------------------------------------
Type:     java.lang.NullPointerException
Location: /Users/teodorlu/.gitlibs/libs/io.github.filipesilva/invoker/2d77d54869d8694a74fcafabea2536f4868a8387/src/invoker/nvk.clj:219:72

----- Context ------------------------------------------------------------------
215:     [k (assoc m :default default)]
216:     [k m]))
217:
218: (defn spec-with-defaults [base-spec args]
219:   (let [config-path      (-> [{:cmds [] :fn identity :spec base-spec}] (cli/dispatch args) :opts :config)
                                                                            ^---
220:         _                (when (and (not= config-path "nvk.edn")
221:                                     (not (fs/exists? config-path)))
222:                            (utils/print-err-exit true 2 (ex-info "Config path does not exist"
223:                                                                  {:config-path config-path})))
224:         dynamic-defaults {:dialect      (if (fs/exists? "deps.edn") :clj :bb)

----- Stack trace --------------------------------------------------------------
clojure.string/starts-with?               - 
babashka.cli/parse-cmds/fn--27323         - 
clojure.core/take-while/fn--6007          - 
clojure.core/seq--5488                    - 
babashka.cli/parse-opts                   - 
... (run with --debug to see elided elements)
invoker.nvk/spec-with-defaults            - /Users/teodorlu/.gitlibs/libs/io.github.filipesilva/invoker/2d77d54869d8694a74fcafabea2536f4868a8387/src/invoker/nvk.clj:218:1
invoker.nvk/-main                         - /Users/teodorlu/.gitlibs/libs/io.github.filipesilva/invoker/2d77d54869d8694a74fcafabea2536f4868a8387/src/invoker/nvk.clj:240:20
invoker.nvk/-main                         - /Users/teodorlu/.gitlibs/libs/io.github.filipesilva/invoker/2d77d54869d8694a74fcafabea2536f4868a8387/src/invoker/nvk.clj:247:9
invoker.nvk/-main                         - /Users/teodorlu/.gitlibs/libs/io.github.filipesilva/invoker/2d77d54869d8694a74fcafabea2536f4868a8387/src/invoker/nvk.clj:232:1
user-74598898-a28d-49e3-8cb1-bf4074face07 - NO_SOURCE_PATH:38:1

teodorlu 2026-02-01T17:23:04.782379Z

(my motivation for asking was, I can try just call some function in your library from my task, but I would prefer to use what you consider to be the public API!)

teodorlu 2026-02-01T17:24:36.137529Z

wait, I think its (apply f command-line-args), let me check

teodorlu 2026-02-01T17:25:21.857799Z

apply it is!

{:deps {io.github.teodorlu/bb-timemachine {:git/tag "v1.0.1" :git/sha "e684c"}
        io.github.filipesilva/invoker
        {:git/url "",
         :git/tag "v0.4.7",
         :git/sha "2d77d54869d8694a74fcafabea2536f4868a8387"}}
 :tasks
 {:requires ([invoker.nvk :as nvk])
  nvk (apply nvk/-main *command-line-args*)}}

Filipe Silva 2026-02-01T17:27:09.163189Z

oh sweet, was gonna get ready to debug it, but it seems no need hahah

❤️ 1
teodorlu 2026-02-01T17:28:09.104309Z

I had a similar recipie lying around for https://github.com/teodorlu/bb-timemachine, so it was quick to spot the difference!

Filipe Silva 2026-02-01T17:28:23.857989Z

the version check seems to be working, which was what I was really worried about... it runs some git commands so I thought maybe they wouldn't work right

Filipe Silva 2026-02-01T17:28:43.371519Z

so now stuff like bb nvk clojure core inc 1 works?

teodorlu 2026-02-01T17:29:31.907179Z

it does

$ bb nvk clojure core inc 1
2

Filipe Silva 2026-02-01T17:32:01.634679Z

bb-timemachine looks cool, kinda makes me think it should enable function-level dependencies like in the Speculation (I think that was the one) Rich Hickey talk

Filipe Silva 2026-02-01T17:32:57.495759Z

so it checks stuff out in a temporary dir or in the project dir?

teodorlu 2026-02-01T17:33:22.466649Z

temp dir, uses a worktree

Filipe Silva 2026-02-01T17:33:29.614499Z

nice

teodorlu 2026-02-01T17:34:36.012969Z

then deletes the dir when the thing completes. We're running branchless at work, and we had a few instances of people pushing partial code! This feels safer to run before pushing.

Filipe Silva 2026-02-01T17:35:28.312269Z

oh yeah I see how that's really bad on branchless hahah

😄 1
teodorlu 2026-02-04T14:31:16.418409Z

Just setup invoker at our work codebase. After adding clj-reload hooks to start and stop our system, I saw substantial performance improvements! Cold Kaocha test run in a fresh process: 24 s First invoker test run after the system has been started: 14 s Second invoker test run: 8 s. I copied our bb task installation to an issue, in case you're interested in supporting installation via babashka task: https://github.com/filipesilva/invoker/issues/1

Filipe Silva 2026-02-04T14:38:03.892739Z

haha yeah I was thinking of adding it to the readme!

Filipe Silva 2026-02-04T14:38:19.424679Z

clj-reload is awesome, kudos to @tonsky for it

🙏 1
Filipe Silva 2026-02-04T14:39:01.431939Z

I should also add a small primer about it to the nvk readme, or at least about using defonce for atoms, was using nvk today and state atoms with reloads really need it

teodorlu 2026-02-04T14:45:22.601179Z

I just read clj-reload's README proper, and got all I needed from there. But yeah, a note of sorts about assumptions (if you have state, it must play nicely with clj-reload) is probably a good addition.

teodorlu 2026-02-03T19:03:50.637959Z

Just had to mention that seeing the path to invoker's README in the bottom of invoker's helptext gave me yet another spark of joy! Github not needed, I have Emacs. Such a nice little tool. Thanks again. 🙇

Filipe Silva 2026-02-03T19:07:46.405269Z

I tried to make sure docs were available locally, so it would work well even without internet. The version stuff (used for injecting if needed) also uses the local install if modified, to support hacking on it for your own use cases.

💯 1
☺️ 1