vis-language-clojure — Clojure language handlers for Vis.
Format/test/REPL are exposed through the generic language facade
(format, test, repl_eval, repl_start, repl_stop) —
format here does parinfer delimiter repair + cljfmt. The pack also registers
a cross-cutting op-hook that auto repairs+formats .clj files after the
foundation's struct_patch / patch / write, so no separate repair step is
needed.
vis-language-clojure — Clojure language handlers for Vis. Format/test/REPL are exposed through the generic language facade (`format`, `test`, `repl_eval`, `repl_start`, `repl_stop`) — `format` here does parinfer delimiter repair + cljfmt. The pack also registers a cross-cutting op-hook that auto repairs+formats `.clj` files after the foundation's struct_patch / patch / write, so no separate repair step is needed.
(available-aliases env)Alias names declared in the workspace deps.edn — surfaced to the UI so the
user picks REAL aliases (:dev, :test, …) instead of guessing. Returns a
sorted vec of strings WITHOUT the leading colon; empty on any read/parse
failure or a non-deps project.
Alias names declared in the workspace `deps.edn` — surfaced to the UI so the user picks REAL aliases (`:dev`, `:test`, …) instead of guessing. Returns a sorted vec of strings WITHOUT the leading colon; empty on any read/parse failure or a non-deps project.
(clj-edit-repair-hook env _op-kw args result)An :after op-hook (registered on struct_patch / patch / write): after a
SUCCESSFUL edit, paren-repair + cljfmt every Clojure file it touched, so the
model never needs a separate repair/format step and a raw patch that left
delimiters unbalanced is auto-corrected.
It then makes the RESULT TRUTHFUL: the foundation rendered each file's
diff from what the raw edit wrote, BEFORE this repair/format rewrote the
bytes on disk, so without this the model would be shown its INTENT, not the
file. Using the pre-edit content the foundation stashes on
:metadata :file-befores, we re-diff every touched summary against the
FINAL on-disk bytes, and when parinfer CHANGED STRUCTURE (moved delimiters,
which can silently re-nest code) we flag the summary loudly
(repaired/note) so a scope shift can never hide behind a stale diff.
A throwing repair is caught + logged by the op-hook runner, never breaking the underlying edit.
An :after op-hook (registered on struct_patch / patch / write): after a SUCCESSFUL edit, paren-repair + cljfmt every Clojure file it touched, so the model never needs a separate repair/format step and a raw `patch` that left delimiters unbalanced is auto-corrected. It then makes the RESULT TRUTHFUL: the foundation rendered each file's `diff` from what the raw edit wrote, BEFORE this repair/format rewrote the bytes on disk, so without this the model would be shown its INTENT, not the file. Using the pre-edit content the foundation stashes on `:metadata :file-befores`, we re-diff every touched summary against the FINAL on-disk bytes, and when parinfer CHANGED STRUCTURE (moved delimiters, which can silently re-nest code) we flag the summary loudly (`repaired`/`note`) so a scope shift can never hide behind a stale diff. A throwing repair is caught + logged by the op-hook runner, never breaking the underlying edit.
(clj-eval-fn env arg)Evaluate Clojure over the session's nREPL. Target resolution (autostart is ON):
port → dial it directly (escape hatch; no autostart/recovery);id/repl_id → the REPL registered under that id in THIS session;AUTO-RECOVERY: a managed REPL whose OS process is alive but whose nREPL socket
is dead/wedged (a boot that never bound its port, or a crashed server thread)
makes the resolver hand back a port nothing listens on. On that connect
failure we restart THIS session's REPL for the target dir and retry the eval
ONCE — so an eval Just Works instead of surfacing a dead-port error. A pinned
port, or a real eval/timeout error, is never retried.
Evaluate Clojure over the session's nREPL. Target resolution (autostart is ON): - explicit `port` → dial it directly (escape hatch; no autostart/recovery); - `id`/`repl_id` → the REPL registered under that id in THIS session; - no id, 0 REPLs → AUTOSTART one in the workspace root (:dev :test) and use it; - no id, 1 REPL → use it (the implicit default); - no id, >1 REPLs → error listing ids (the model must pick one). The just-autostarted REPL is mirrored into the session resources on the next ctx render (footer + stop/restart). AUTO-RECOVERY: a managed REPL whose OS process is alive but whose nREPL socket is dead/wedged (a boot that never bound its port, or a crashed server thread) makes the resolver hand back a port nothing listens on. On that connect failure we restart THIS session's REPL for the target dir and retry the eval ONCE — so an eval Just Works instead of surfacing a dead-port error. A pinned `port`, or a real eval/timeout error, is never retried.
(clj-format-fn arg)(clj-format-fn env arg)Format Clojure source via the language facade (format_code). Accepts:
Format Clojure source via the language facade (`format_code`). Accepts:
- a raw code string / {"code": ...} -> report changed? + char delta (NO text)
- {"path": "src/foo.clj"} -> format that file IN PLACE
- {"paths": ["src" "test" ...]} -> format those paths IN PLACE; a
DIRECTORY is walked RECURSIVELY (every .clj/.cljs/.cljc/.cljx under it)
- nothing / {} -> format the whole project's source
roots (every deps.edn module's :paths + test), skipping build/vendor
dirs (target, dist, node_modules, .clj-kondo, .clojure-lsp, .cpcache…)
Paths are resolved against the workspace root when relative.(clj-lint-fn env arg)clj-kondo lint via the language facade (lint_code). Accepts:
clj-kondo lint via the language facade (`lint_code`). Accepts:
- a raw code string / {"code": ...} -> lint it on stdin
- {"path": "src/foo.clj"} -> lint that file
- {"paths": ["src", "test"]} -> lint those paths
- nothing / {} -> lint the whole project's source roots
(every deps.edn module's :paths + test), skipping build/vendor dirs
Paths are resolved against the workspace root when relative. Finding "file"
paths are reported RELATIVE to the workspace root (absolute only when outside).(clj-patch-no-fail-around env _op-kw args next)MIDDLEWARE (:around) on patch — the anchored-edit mirror of
clj-struct-patch-no-fail-around. A raw patch whose :replace leaves a
cleanly-parsing .clj file with unbalanced delimiters is REFUSED by the
foundation's re-parse guard (a {:success? false … :reason :syntax-error}
envelope, nothing written). The refusal's :metadata carries the
WHOLE-BATCH :candidate-plans (every planned file with its edits applied,
unwritten) plus :broken-paths.
Here we parinfer-repair each broken candidate as a WHOLE SOURCE — fragment
repair cannot fix CONTEXTUAL imbalance (a replacement that is locally
balanced but swallows or duplicates an enclosing closer only shows up in
the full file) — and, when every broken file is Clojure and every one
repairs clean, COMMIT the whole batch ourselves and return a success
envelope with the SAME per-file diff/changed result shape as a normal
successful patch. The :after repair+format hook then cljfmt-polishes the
files and re-diffs from :metadata :file-befores, so the displayed
collapsible changes describe the FINAL bytes on disk. If any broken file is
not Clojure, or any repair fails, the ORIGINAL refusal is surfaced untouched
— we never bury a real failure. The baseline is always 'nothing written'.
MIDDLEWARE (:around) on patch — the anchored-edit mirror of
`clj-struct-patch-no-fail-around`. A raw `patch` whose `:replace` leaves a
cleanly-parsing `.clj` file with unbalanced delimiters is REFUSED by the
foundation's re-parse guard (a `{:success? false … :reason :syntax-error}`
envelope, nothing written). The refusal's `:metadata` carries the
WHOLE-BATCH `:candidate-plans` (every planned file with its edits applied,
unwritten) plus `:broken-paths`.
Here we parinfer-repair each broken candidate as a WHOLE SOURCE — fragment
repair cannot fix CONTEXTUAL imbalance (a replacement that is locally
balanced but swallows or duplicates an enclosing closer only shows up in
the full file) — and, when every broken file is Clojure and every one
repairs clean, COMMIT the whole batch ourselves and return a success
envelope with the SAME per-file `diff`/`changed` result shape as a normal
successful patch. The `:after` repair+format hook then cljfmt-polishes the
files and re-diffs from `:metadata :file-befores`, so the displayed
collapsible changes describe the FINAL bytes on disk. If any broken file is
not Clojure, or any repair fails, the ORIGINAL refusal is surfaced untouched
— we never bury a real failure. The baseline is always 'nothing written'.(clj-repair+format code)(clj-repair+format code path)The combined Clojure tidy used by BOTH format and the post-edit hook:
parinfer delimiter repair FIRST (so unbalanced ( [ { from a raw edit are
fixed), THEN indentation via the config-driven formatter (fmt/format-source
picks zprint when a .zprint.edn/.zprintrc is near path, else cljfmt).
Total — returns code unchanged on any failure of either step.
The combined Clojure tidy used by BOTH `format` and the post-edit hook:
parinfer delimiter repair FIRST (so unbalanced ( [ { from a raw edit are
fixed), THEN indentation via the config-driven formatter (`fmt/format-source`
picks zprint when a `.zprint.edn`/`.zprintrc` is near `path`, else cljfmt).
Total — returns `code` unchanged on any failure of either step.(clj-repl-fn env)(clj-repl-fn env op)(clj-repl-fn env op opts)Manage THIS session's workspace nREPL(s). Positional op (default "status") +
optional opts dict {"dir": <path>, "aliases": ["dev", "test"]}:
"status" — managed-process view for this session (always allowed) "start" — self-start a project nREPL subprocess (always allowed, autostart is the norm; the model rarely needs to call this by hand) "restart" — stop then start (always allowed) "stop" — stop this session's Vis-managed nREPL (always allowed)
"dir" runs the REPL in a subdir (e.g. an extension) instead of the workspace
root — that's how MULTIPLE REPLs coexist, each addressed by its id. "aliases"
default to [:dev :test] (full deps/paths, user :main-opts dropped). Live nREPL
state already rides in ctx under :session/env :languages :clojure :nrepl;
this tool acts on it.
Manage THIS session's workspace nREPL(s). Positional op (default "status") +
optional opts dict `{"dir": <path>, "aliases": ["dev", "test"]}`:
"status" — managed-process view for this session (always allowed)
"start" — self-start a project nREPL subprocess (always allowed, autostart
is the norm; the model rarely needs to call this by hand)
"restart" — stop then start (always allowed)
"stop" — stop this session's Vis-managed nREPL (always allowed)
"dir" runs the REPL in a subdir (e.g. an extension) instead of the workspace
root — that's how MULTIPLE REPLs coexist, each addressed by its id. "aliases"
default to [:dev :test] (full deps/paths, user :main-opts dropped). Live nREPL
state already rides in ctx under `:session/env :languages :clojure :nrepl`;
this tool acts on it.(clj-struct-patch-no-fail-around _env _op-kw args next)MIDDLEWARE (:around) on struct_patch so a Clojure structural edit does NOT
fail on unbalanced delimiters. If the call throws and it targeted a .clj
file with a :code form, parinfer-repair the code and retry ONCE. If the
repair changes nothing — or the retried edit still fails — the ORIGINAL error
is surfaced (we never bury a real structural failure). Non-clj / non-code
calls pass straight through to next.
MIDDLEWARE (:around) on struct_patch so a Clojure structural edit does NOT fail on unbalanced delimiters. If the call throws and it targeted a `.clj` file with a `:code` form, parinfer-repair the code and retry ONCE. If the repair changes nothing — or the retried edit still fails — the ORIGINAL error is surfaced (we never bury a real structural failure). Non-clj / non-code calls pass straight through to `next`.
(register-repl-resource! session-id dir aliases result)Mirror a session's managed nREPL into the session-scoped resource registry so
it shows in ctx (resources) + the footer, and can be stopped/restarted by id
from the agent or the UI. No-op without a session or a live spawn. The
stop-fn/restart-fn thunks ARE the canonical lifecycle — the footer and
resource_stop both drive repl-manager through them, scoped to session-id.
Mirror a session's managed nREPL into the session-scoped resource registry so it shows in ctx (resources) + the footer, and can be stopped/restarted by id from the agent or the UI. No-op without a session or a live spawn. The stop-fn/restart-fn thunks ARE the canonical lifecycle — the footer and resource_stop both drive repl-manager through them, scoped to `session-id`.
(ui-start-repl! env aliases)Channel-invokable nREPL start for the Resources UI (web modal / TUI F4).
Resolves the workspace dir from env, starts a managed nREPL with aliases
(vec/seq of keyword-or-string names, or nil → [:dev :test]), and mirrors it
into the session resource registry (ctx + footer + stop/restart). ALWAYS
allowed: a user clicking Start is explicit consent. Returns the start result.
Channel-invokable nREPL start for the Resources UI (web modal / TUI F4). Resolves the workspace dir from `env`, starts a managed nREPL with `aliases` (vec/seq of keyword-or-string names, or nil → [:dev :test]), and mirrors it into the session resource registry (ctx + footer + stop/restart). ALWAYS allowed: a user clicking Start is explicit consent. Returns the start result.
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |