Evaluate Clojure code in the running app (written by Claude) A developer has created eval-app, a tool that connects to a running Clojure application's nREPL to evaluate code for inspecting app state, querying databases, or debugging issues. The tool accepts Clojure code via stdin and returns pretty-printed results, with configurable host, port, and timeout options. | /usr/bin/env bb | | | ;; ============================================================================= | | | ;; eval-app - Evaluate Clojure code in the running app | | | ;; ============================================================================= | | | ;; | | | ;; This tool connects to the nREPL of the app running evaluates Clojure code. | | | ;; Useful for inspecting app state, querying the database, or debugging issues | | | ;; in the running system. | | | ;; | | | ;; USAGE: | | | ;; cat <<'EOF' | agent-tools/eval-app | | | ;; your-code-here | | | ;; EOF | | | ;; | | | ;; cat code.clj | agent-tools/eval-app | | | ;; | | | ;; OPTIONS: | | | ;; --host HOST nREPL host default: localhost | | | ;; --port PORT nREPL port default: 4000 | | | ;; --timeout MS Timeout in milliseconds default: 30000 | | | ;; --help, -h Show help | | | ;; | | | ;; EXAMPLES: | | | ;; | | | ;; Get the current database value | | | ;; cat <<'EOF' | agent-tools/eval-app | | | ;; dev/db | | | ;; EOF | | | ;; | | | ;; Query an entity | | | ;; cat <<'EOF' | agent-tools/eval-app | | | ;; dev/show-entity 12345 | | | ;; EOF | | | ;; | | | ;; Check system state | | | ;; cat <<'EOF' | agent-tools/eval-app | | | ;; keys @app.system/state | | | ;; EOF | | | ;; | | | ;; Multi-line code | | | ;; cat <<'EOF' | agent-tools/eval-app | | | ;; d/q ' :find ?e :where ?e :user/email dev/db | | | ;; EOF | | | ;; | | | ;; OUTPUT: | | | ;; Returns the result of evaluating the Clojure code, pretty-printed. | | | ;; Errors are printed to stderr. | | | ;; | | | ;; ============================================================================= | | | require ' babashka.cli :as cli | | | ' clojure.string :as str | | | ' bencode.core :as bencode | | | import ' java.net Socket | | | ' java.io PushbackInputStream | | | def cli-spec | | | {:host {:desc "nREPL host" | | | :default "localhost"} | | | :port {:desc "nREPL port" | | | :default 4000 | | | :coerce :int} | | | :timeout {:desc "Timeout in milliseconds" | | | :default 30000 | | | :coerce :int} | | | :help {:desc "Show help" | | | :alias :h}} | | | defn print-help | | | println "eval-app - Evaluate Clojure code in the running app" | | | println | | | println "Usage: cat <<'EOF' | agent-tools/eval-app OPTIONS " | | | println " your-code-here " | | | println " EOF" | | | println | | | println " cat code.clj | agent-tools/eval-app OPTIONS " | | | println | | | println "Options:" | | | println " --host HOST nREPL host default: localhost " | | | println " --port PORT nREPL port default: 4000 " | | | println " --timeout MS Timeout in milliseconds default: 30000 " | | | println " --help, -h Show this help" | | | println | | | println "Examples:" | | | println " cat <<'EOF' | agent-tools/eval-app" | | | println " dev/db " | | | println " EOF" | | | defn bytes- str x | | | if bytes? x | | | String. ^bytes x "UTF-8" | | | x | | | defn parse-response response | | | into {} | | | map fn k v | | | keyword bytes- str k | | | if sequential? v | | | mapv bytes- str v | | | bytes- str v | | | response | | | defn nrepl-eval {:keys host port timeout } code | | | let socket Socket. ^String host ^int port | | | .setSoTimeout socket timeout | | | out .getOutputStream socket | | | in PushbackInputStream. .getInputStream socket | | | session-id str random-uuid | | | msg {"op" "eval" | | | "code" code | | | "id" session-id} | | | try | | | bencode/write-bencode out msg | | | loop has-error false | | | let response parse-response bencode/read-bencode in | | | when-let out-str :out response | | | print out-str | | | flush | | | when-let err-str :err response | | | binding out err | | | print err-str | | | flush | | | when-let value :value response | | | println value | | | when-let ex :ex response | | | binding out err | | | println "Exception:" ex | | | let status set :status response | | | has-error or has-error | | | contains? status "error" | | | contains? status "eval-error" | | | if contains? status "done" | | | if has-error 1 0 | | | recur has-error | | | finally | | | .close socket | | | defn eval-code opts code | | | try | | | nrepl-eval opts code | | | catch java.net.ConnectException e | | | binding out err | | | println "Error: Cannot connect to nREPL at" str :host opts ":" :port opts | | | println "Make sure the app is running bin/dev up " | | | 1 | | | catch java.net.SocketTimeoutException e | | | binding out err | | | println "Error: nREPL request timed out" | | | 1 | | | catch Exception e | | | binding out err | | | println "Error:" .getMessage e | | | 1 | | | let {:keys opts } cli/parse-args command-line-args {:spec cli-spec} | | | opts-with-defaults merge {:host "localhost" :port 4000 :timeout 30000} opts | | | if :help opts | | | print-help | | | let code slurp in | | | if str/blank? code | | | do binding out err | | | println "Error: No code provided on stdin" | | | println "Usage: cat <<'EOF' | agent-tools/eval-app" | | | println " dev/db " | | | println " EOF" | | | println "Run with --help for more information" | | | System/exit 1 | | | System/exit eval-code opts-with-defaults code |