@cfleming any plans for resolving https://github.com/cursive-ide/cursive/issues/1621 ? Back in 6/2024 you mentioned ” in the next version of Cursive I’m going to rewrite the formatter to be (by default, at least) completely compatible with cljfmt” but this seems to have gone nowhere?! 🙏
My apologies, I've been distracted with a bunch of other things, but this is still right near the top of my list, I promise.
Well, if you list behaves anything like mine, I do not cross my fingers 😅
@cfleming Simple request: Ability to specify the nrepl port file created by a Local REPL. Use-case: on the AI coding front I have an IntelliJ integration problem that you could probably solve pretty easily: I run two REPLs (one for tests and one for dev, bevcause tests can run integration things that allocate ports and can screw up my dev flow). Unfortunately, the REPL always writes .nrepl-port as a file. My workaround at the moment is to have a little custom code where I can start a REPL with a named port file (e.g. .nrepl-dev-port .nrepl-test-port). This is great if I want IntelliJ Cursive to connect to that via a file (already supported) but I want to go in the reverse direction: I want my MCP or AI to start a REPL, but I need to know where it is. using a constant value “works” but I run 2-3 sessions in parallel on the same project where I need to isolate all of the resources, and therefore need alternate ports (and auto-generating them is idea, obviously). Manually messing with all the manual ports is a pain, so I build a workaround for that. But now I’m noticing that many times what I want to do is tell the AI “I have a dev and test REPL already started in intellij” so that I’m in control of easily restarting the REPL process, etc, but I cannot specify the file I want nREPL to WRITE to (it always uses .nrepl-port). If there’s already a way, glad to hear it. I’ve looked for env variables, but didn’t find a way.
Unfortunately I don't think there's a way to do this right now. Cursive uses nrepl.cmdline to start the REPL because it takes care of a bunch of details, e.g. allowing middleware to be specified. Unfortunately it seems that the file name to use is hard-coded there: https://github.com/nrepl/nrepl/blob/a77bfa73135acdda094e95b7e83d227aa3739599/src/clojure/nrepl/cmdline.clj#L470-L480
sad. I had claude code make an entry point to start nrepl and call it from deps where I can pass the filename…guess it’s time for a PR to the nrepl lib?
(ns custom-nrepl
"Custom nREPL entry point that allows specifying a custom port file name.
This wraps the standard nrepl.cmdline functionality but adds the ability
to write the port to a custom-named file instead of just .nrepl-port."
(:require
[clojure.java.io :as io]
[nrepl.server :as server]))
(defn write-port-file
"Write the server port to a custom port file and print startup message.
The message format matches nrepl.cmdline exactly, as tools like CIDER
and clojure-mcp parse this output to extract connection details."
[port bind port-file-name]
(let [port-file (io/file port-file-name)
uri (format "nrepl://%s:%d" bind port)]
(.deleteOnExit ^java.io.File port-file)
(spit port-file port)
;; This format MUST match nrepl.cmdline/server-started-message exactly
(println (format "nREPL server started on port %d on host %s - %s" port bind uri))))
(defn start-server-with-port-file
"Start an nREPL server and write its port to a custom port file.
Options:
:port - Port to listen on (default: 0 for random port)
:bind - Bind address (default: 127.0.0.1)
:port-file - Name of the port file to create (default: .nrepl-port)
:handler - nREPL handler (default: nrepl.server/default-handler)
All other options are passed through to nrepl.server/start-server."
[& {:keys [port bind port-file handler]
:or {port 0
bind "127.0.0.1"
port-file ".nrepl-port"}
:as opts}]
(let [server-opts (dissoc opts :port-file)
server (apply server/start-server (mapcat identity server-opts))
actual-port (:port server)
actual-bind (or bind "127.0.0.1")]
(write-port-file actual-port actual-bind port-file)
server))
(defn -main
"Main entry point that parses command-line arguments and starts the server.
Usage:
clojure -M:alias -m custom-nrepl --port-file .nrepl-test-port
clojure -M:alias -m custom-nrepl --port 7888 --port-file .nrepl-dev-port
Arguments:
--port PORT - Port to listen on (default: random)
--bind ADDRESS - Bind address (default: 127.0.0.1)
--port-file FILENAME - Port file name (default: .nrepl-port)"
[& args]
(let [opts (loop [args args
opts {}]
(if (empty? args)
opts
(let [[flag value & rest] args]
(case flag
"--port" (recur rest (assoc opts :port (Long/parseLong value)))
"--bind" (recur rest (assoc opts :bind value))
"--port-file" (recur rest (assoc opts :port-file value))
(do
(println (format "Unknown option: %s" flag))
(recur rest opts))))))
server (apply start-server-with-port-file (mapcat identity opts))]
;; Keep the server running
@(promise)))
and use that from deps.edn…it inverts the system so that Cursive has to read the port file…but like I said, be nice if it was the other way around. I don’t really understand the middleware pipeline issue, but I get itYes, this would be nice. I think that the nREPL team would probably be open to a PR, and I can update the version of nREPL that Cursive uses once it's in.
I made an MR…they are essentially asking me over and over to justify the need. Feel free to comment 😄 https://github.com/nrepl/nrepl/pull/400