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, ...)}(active-slashes env)Aggregate :ext/slash-commands from every active extension for env.
Order: extension registration order (stable). Returns a vec.
This is the ONLY source of truth for engine slash dispatch (which
already has a turn env handy). Channels that lack a per-turn env
should use registered-slashes (no activation filtering).
Aggregate `:ext/slash-commands` from every active extension for `env`. Order: extension registration order (stable). Returns a vec. This is the ONLY source of truth for engine slash dispatch (which already has a turn env handy). Channels that lack a per-turn env should use `registered-slashes` (no activation filtering).
(dispatch env ctx text)Dispatch slash text against env. ctx carries channel-side knobs:
:channel/id keyword, required (:tui, :telegram, ...)
:session/id session-soul UUID (optional unless slash
declares :slash/requires #{:session})
:workspace/id workspace UUID (optional unless required)
:db-info persistence handle (always passed when
present in the channel env)
:reply! (fn [ir-or-string]) for channels that want
an immediate side-effect surface
:publish! (fn [event]) bus for cross-channel events
Return shapes documented at the namespace docstring.
Dispatch slash `text` against `env`. `ctx` carries channel-side knobs:
:channel/id keyword, required (:tui, :telegram, ...)
:session/id session-soul UUID (optional unless slash
declares `:slash/requires #{:session}`)
:workspace/id workspace UUID (optional unless required)
:db-info persistence handle (always passed when
present in the channel env)
:reply! (fn [ir-or-string]) for channels that want
an immediate side-effect surface
:publish! (fn [event]) bus for cross-channel events
Return shapes documented at the namespace docstring.(parse text)Tokenise a slash text into {:path :args :raw} or nil.
This function performs PURE tokenisation. It does NOT consult any
slash registry; the engine resolves the longest matching prefix at
dispatch time once it has the env. This keeps parse pure and
testable in isolation.
Tokenise a slash `text` into `{:path :args :raw}` or nil.
This function performs PURE tokenisation. It does NOT consult any
slash registry; the engine resolves the longest matching prefix at
dispatch time once it has the env. This keeps `parse` pure and
testable in isolation.(registered-slashes)Walk every globally registered extension and return the union of
their :ext/slash-commands specs. Activation-fn filtering is NOT
applied here — channels that surface slash UX before a session is
running (TUI palette overlay, Telegram setMyCommands) use this
env-less view. The engine dispatch path itself goes through
active-slashes env so per-session activation-fn rules still hold.
Walk every globally registered extension and return the union of their `:ext/slash-commands` specs. Activation-fn filtering is NOT applied here — channels that surface slash UX before a session is running (TUI palette overlay, Telegram `setMyCommands`) use this env-less view. The engine dispatch path itself goes through `active-slashes env` so per-session activation-fn rules still hold.
(slash-by-path env path)Return the slash spec whose full path = path, or nil. path is a
non-empty vec of names like ["workspace" "apply"]. When
multiple specs share the path (per-channel partitioning), returns
the first registered.
Return the slash spec whose full path = `path`, or nil. `path` is a non-empty vec of names like `["workspace" "apply"]`. When multiple specs share the path (per-channel partitioning), returns the first registered.
(slash-children env)(slash-children env parent)Return the vec of slash specs whose :slash/parent = parent vec.
parent defaults to [] (top-level commands).
Return the vec of slash specs whose `:slash/parent` = `parent` vec. `parent` defaults to `[]` (top-level commands).
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 |