Process-local channel event bus.
Extensions use this to talk to mounted channels without depending on their implementation namespaces. Channels subscribe while running and translate events into their local state/events. No listener failure may take down the publisher or sibling listeners.
Process-local channel event bus. Extensions use this to talk to mounted channels without depending on their implementation namespaces. Channels subscribe while running and translate events into their local state/events. No listener failure may take down the publisher or sibling listeners.
Backend for file-picking UIs (the @ mention picker, TUI + web).
Everything here rides the ONE canonical pooled fff index
(internal.fff-index) that the grep tool use: fff owns the
tree walk, the gitignore policy, the git-status metadata and the
frecency-ranked fuzzy match. This namespace only leases that index and turns
fff rows into display rows.
There is deliberately NO Clojure-side directory walk, git-status subprocess, ignore matcher or scoring heuristic left in here — reintroducing one means the picker and the search tools would rank and see different files.
Backend for file-picking UIs (the `@` mention picker, TUI + web). Everything here rides the ONE canonical pooled fff index (`internal.fff-index`) that the `grep` tool use: fff owns the tree walk, the gitignore policy, the git-status metadata and the frecency-ranked fuzzy match. This namespace only leases that index and turns fff rows into display rows. There is deliberately NO Clojure-side directory walk, git-status subprocess, ignore matcher or scoring heuristic left in here — reintroducing one means the picker and the search tools would rank and see different files.
The canonical per-form DISPLAY contract — ONE source of truth for the fields a channel reads to render an executed form, live (via the gateway) and restored (via the DB).
Why this exists: the SAME field set used to be hand-listed in independent
allowlists across the loop, persistence, gateway, progress, and restored display
paths. Now every layer projects the WHOLE set through ->display (outbound) /
<-wire (inbound), so a new display field is a ONE-line change to
display-keys and form-roundtrip-test fails if a boundary stops carrying it.
Transformed fields (:stdout/:error bounded, :silent/:duration_ms
renamed) stay as explicit gateway overrides — they are not carried verbatim, so
they are NOT in this set.
The canonical per-form DISPLAY contract — ONE source of truth for the fields a channel reads to render an executed form, live (via the gateway) and restored (via the DB). Why this exists: the SAME field set used to be hand-listed in independent allowlists across the loop, persistence, gateway, progress, and restored display paths. Now every layer projects the WHOLE set through `->display` (outbound) / `<-wire` (inbound), so a new display field is a ONE-line change to `display-keys` and `form-roundtrip-test` fails if a boundary stops carrying it. Transformed fields (`:stdout`/`:error` bounded, `:silent`/`:duration_ms` renamed) stay as explicit gateway overrides — they are not carried verbatim, so they are NOT in this set.
Channel-agnostic header layout & content spec.
Every channel — the terminal TUI and future surfaces — renders the same conceptual header band:
[LEFT 30%] [CENTER 40% workspace switcher] [RIGHT 30%]
The decisions a channel cannot make on its own (slot ratios, workspace switcher sizing/visibility caps, default labels, copy id length, glyphs) live here as plain Clojure data. A channel imports this namespace, reads the values, and projects them onto its medium — TextGraphics cells for Lanterna, and so on.
No graphics. No I/O. No channel-specific deps. Pure data + tiny
pure helpers, written as .cljc so a future ClojureScript client can
require it directly.
Channel-agnostic header layout & content spec.
Every channel — the terminal TUI and future surfaces —
renders the same conceptual header band:
[LEFT 30%] [CENTER 40% workspace switcher] [RIGHT 30%]
The decisions a channel cannot make on its own (slot ratios, workspace
switcher sizing/visibility caps, default labels, copy id length, glyphs)
live here as plain Clojure data. A channel imports this namespace,
reads the values, and projects them onto its medium — TextGraphics
cells for Lanterna, and so on.
No graphics. No I/O. No channel-specific deps. Pure data + tiny
pure helpers, written as `.cljc` so a future ClojureScript client can
require it directly.Cross-channel ephemeral notifications.
A single in-memory pub-sub the host runtime, every extension, and every channel can use to surface a transient signal - "copied to clipboard", "tests passed", "provider switched" - without embedding it in the answer body or polluting Telemere logs.
Surface (re-exported on com.blockether.vis.core):
(notify! text) (notify! text :level :info|:success|:warn|:error :ttl-ms <long>|nil) (notifications) ;; vec of currently-active entries (dismiss! id) ;; force-clear by id (clear-expired!) ;; prune; called on every read (watch! key (fn [vec] ...)) ;; cross-channel reactivity (unwatch! key)
Entry shape: {:id <uuid> :text <string> :level :info | :success | :warn | :error :created-at <inst> :until <epoch-ms> | nil ;; nil = sticky / manual dismiss
Levels are advisory metadata for channels: TUI uses them for
colour, the CLI could prefix [notice] /
[warn]. The host stores them but never interprets them.
Why a flat module instead of a generic event bus: notifications are a single, narrow concern. A 50-line atom + watcher map serves it without introducing a generic pub-sub abstraction nobody asked for. If we ever grow more event types, this becomes one consumer of a richer system.
Cross-channel ephemeral notifications.
A single in-memory pub-sub the host runtime, every extension, and
every channel can use to surface a transient signal - "copied to
clipboard", "tests passed", "provider switched" - without
embedding it in the answer body or polluting Telemere logs.
Surface (re-exported on `com.blockether.vis.core`):
(notify! text)
(notify! text :level :info|:success|:warn|:error
:ttl-ms <long>|nil)
(notifications) ;; vec of currently-active entries
(dismiss! id) ;; force-clear by id
(clear-expired!) ;; prune; called on every read
(watch! key (fn [vec] ...)) ;; cross-channel reactivity
(unwatch! key)
Entry shape:
{:id <uuid>
:text <string>
:level :info | :success | :warn | :error
:created-at <inst>
:until <epoch-ms> | nil ;; nil = sticky / manual dismiss
Levels are advisory metadata for channels: TUI uses them for
colour, the CLI could prefix `[notice]` /
`[warn]`. The host stores them but never interprets them.
Why a flat module instead of a generic event bus: notifications
are a single, narrow concern. A 50-line atom + watcher map serves
it without introducing a generic pub-sub abstraction nobody asked
for. If we ever grow more event types, this becomes one consumer
of a richer system.Transient Markdown parsing and renderer projections.
Canonical answers are role-labelled, string-keyed content blocks from
com.blockether.vis.internal.content. Parsed Markdown trees are created only
inside renderers and are never transported or persisted.
markdown->ast parses prose for renderer-local layout. render,
extract-code, extract-text, and session->markdown are disposable
projections; none of their intermediate trees are canonical message data.
Transient Markdown parsing and renderer projections. Canonical answers are role-labelled, string-keyed content blocks from `com.blockether.vis.internal.content`. Parsed Markdown trees are created only inside renderers and are never transported or persisted. `markdown->ast` parses prose for renderer-local layout. `render`, `extract-code`, `extract-text`, and `session->markdown` are disposable projections; none of their intermediate trees are canonical message data.
Channel-agnostic slash dispatch.
Slashes are DECLARATIVE: every extension carries :ext/slash-commands
on its manifest; the engine derives the active slash set by walking
(active-extensions environment) at lookup time. NO global atom, NO
register-slash! imperative call.
Public surface (re-exported through core.clj):
(active-slashes env) -> vec of slash specs
(slash-by-path env path) -> slash spec or nil
(slash-children env parent) -> vec of slash specs whose
:slash/parent = parent
(parse text) -> {:path :args :raw} | nil
(raw tokenisation only;
does NOT consult any registry)
(dispatch env ctx text) -> envelope (see below)
The dispatch envelope is the contract every channel renders against:
{:handled? true :result <slash result map> :path path} {:handled? true :error msg :reason :unknown :tokens tokens} {:handled? true :error msg :reason :requires-failed :missing #{} :path} {:handled? true :error msg :reason :unavailable :path} {:handled? true :error msg :reason :no-run-fn :path} {:handled? true :error msg :reason :run-failed :ex t :path} {:handled? false} -- text was not a slash; channel forwards to LLM.
A slash text is any non-blank string starting with / followed by
at least one word. Plain prose without the leading / is ALWAYS
{:handled? false}.
Slash run-fns may return an EXTENDED :slash/* envelope that carries
a rendered result card back to the channel:
{:slash/status :ok | :error | :nothing-to-commit | :ff-failed :slash/title short headline (string, plain) :slash/body IR (vector starting with :ir ...) OR Markdown string :slash/actions [{:label :slash}] ;; optional follow-ups :slash/data arbitrary payload (workspace-id, sha, ...)}
Channel-agnostic slash dispatch.
Slashes are DECLARATIVE: every extension carries `:ext/slash-commands`
on its manifest; the engine derives the active slash set by walking
`(active-extensions environment)` at lookup time. NO global atom, NO
`register-slash!` imperative call.
Public surface (re-exported through `core.clj`):
(active-slashes env) -> vec of slash specs
(slash-by-path env path) -> slash spec or nil
(slash-children env parent) -> vec of slash specs whose
`:slash/parent` = parent
(parse text) -> {:path :args :raw} | nil
(raw tokenisation only;
does NOT consult any registry)
(dispatch env ctx text) -> envelope (see below)
The dispatch envelope is the contract every channel renders against:
{:handled? true :result <slash result map> :path path}
{:handled? true :error msg :reason :unknown :tokens tokens}
{:handled? true :error msg :reason :requires-failed :missing #{} :path}
{:handled? true :error msg :reason :unavailable :path}
{:handled? true :error msg :reason :no-run-fn :path}
{:handled? true :error msg :reason :run-failed :ex t :path}
{:handled? false} -- text was not a slash; channel forwards to LLM.
A slash text is any non-blank string starting with `/` followed by
at least one word. Plain prose without the leading `/` is ALWAYS
{:handled? false}.
Slash run-fns may return an EXTENDED `:slash/*` envelope that carries
a rendered result card back to the channel:
{:slash/status :ok | :error | :nothing-to-commit | :ff-failed
:slash/title short headline (string, plain)
:slash/body IR (vector starting with :ir ...) OR Markdown string
:slash/actions [{:label :slash}] ;; optional follow-ups
:slash/data arbitrary payload (workspace-id, sha, ...)}Internal, channel-agnostic Vis theme data.
Keep this namespace pure data: no Lanterna, Swing, browser, or terminal backend imports. Channels adapt these tokens to their own render types.
Internal, channel-agnostic Vis theme data. Keep this namespace pure data: no Lanterna, Swing, browser, or terminal backend imports. Channels adapt these tokens to their own render types.
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 |