Liking cljdoc? Tell your friends :D

toolnexus.tool


add-toolsclj/s

(add-tools tk tools)
source

all-toolsclj/s

(all-tools tk)
source

code-pointsclj/s

s as a vector of Unicode code points — koine.text/code-points.

Lived here first: this port found the divergence (nine sites sorting differently on the two hosts) and carried the fix locally until koine 0.11.0 lifted it into the seam, where a clojure.core divergence belongs. The vars stay so call sites and tests read tool/…, but there is ONE implementation.

`s` as a vector of Unicode code points — `koine.text/code-points`.

Lived here first: this port found the divergence (nine sites sorting
differently on the two hosts) and carried the fix locally until koine 0.11.0
lifted it into the seam, where a clojure.core divergence belongs. The vars
stay so call sites and tests read `tool/…`, but there is ONE implementation.
sourceraw docstring

compare-stringsclj/s

Code-point string comparison, identical on both hosts — koine.text/compare-strings. See code-points for why this delegates.

Code-point string comparison, identical on both hosts — `koine.text/compare-strings`.
See `code-points` for why this delegates.
sourceraw docstring

executeclj/s

(execute tk name args)
(execute tk name args ctx)

Run a tool by name. A THROW from a tool becomes an error ToolResult — a misbehaving tool must not take down the loop (SPEC §0.8).

Run a tool by name. A THROW from a tool becomes an error ToolResult — a
misbehaving tool must not take down the loop (SPEC §0.8).
sourceraw docstring

failureclj/s

(failure output)
(failure output metadata)

A failed ToolResult. Note a tool ERROR is a VALUE handed back to the model, not a thrown exception — the model is meant to see it and try again. NOT named err, for the reason given on success.

A failed ToolResult. Note a tool ERROR is a VALUE handed back to the model,
not a thrown exception — the model is meant to see it and try again.
NOT named `err`, for the reason given on `success`.
sourceraw docstring

last-index-of-charclj/s

(last-index-of-char s ch)

Index of the last ch in s, or nil — scanned with count/nth instead of clojure.string/last-index-of.

THIS IS A HOST DIVERGENCE, and it belongs in the seam beside compare-strings for the same reason that one does. On the cljgo host last-index-of returns a BYTE offset while subs, count and nth work in RUNES, so for any string containing a non-ASCII character the index and the slice disagree and the cut lands mid-character-sequence:

(parent-dir "/base/\uE000dir/SKILL.md")  =>  "/base/\uE000dir/S"   on cljgo
(base-name  "/docs/\uD83D\uDE00.pdf")       =>  ".pdf" mis-sliced   on cljgo

U+E000 is 3 UTF-8 bytes and 1 rune, so the index runs 2 too high; an astral character is 4 bytes and 1 rune, so it runs 3 too high. On the JVM host last-index-of agrees with subs and nothing is wrong, which is exactly why this survived: every path in every fixture was ASCII. classifier.cljc had already recorded that last-index-of was never proven on cljgo.

count, nth and subs are all in the SAME units as each other on each host (UTF-16 units on the JVM, runes on cljgo), so a scan is correct on both without either host needing to know which unit it is in.

Index of the last `ch` in `s`, or nil — scanned with `count`/`nth` instead of
`clojure.string/last-index-of`.

THIS IS A HOST DIVERGENCE, and it belongs in the seam beside `compare-strings`
for the same reason that one does. On the cljgo host `last-index-of` returns a
BYTE offset while `subs`, `count` and `nth` work in RUNES, so for any string
containing a non-ASCII character the index and the slice disagree and the cut
lands mid-character-sequence:

    (parent-dir "/base/\uE000dir/SKILL.md")  =>  "/base/\uE000dir/S"   on cljgo
    (base-name  "/docs/\uD83D\uDE00.pdf")       =>  ".pdf" mis-sliced   on cljgo

U+E000 is 3 UTF-8 bytes and 1 rune, so the index runs 2 too high; an astral
character is 4 bytes and 1 rune, so it runs 3 too high. On the JVM host
`last-index-of` agrees with `subs` and nothing is wrong, which is exactly why
this survived: every path in every fixture was ASCII. `classifier.cljc` had
already recorded that `last-index-of` was never proven on cljgo.

`count`, `nth` and `subs` are all in the SAME units as each other on each host
(UTF-16 units on the JVM, runes on cljgo), so a scan is correct on both
without either host needing to know which unit it is in.
sourceraw docstring

sanitizeclj/s

(sanitize s)

SPEC §0.2 — replace [^a-zA-Z0-9_-] with _.

SPEC §0.2 — replace [^a-zA-Z0-9_-] with _.
sourceraw docstring

sort-stringsclj/s

(sort-strings coll)

coll sorted by compare-strings — koine.text/sort-strings, returned as a vector because every caller here treats tool lists as vectors.

`coll` sorted by `compare-strings` — `koine.text/sort-strings`, returned as a
vector because every caller here treats tool lists as vectors.
sourceraw docstring

successclj/s

(success output)
(success output metadata)

A successful ToolResult (SPEC §0.1).

NOT named ok: cljgo's clojure.core HAS ok and err (the JVM's does not), so (defn ok ...) shadows a core name on one host only — measured 2026-07-31. It warns today; per cljgo's static interop scan a bare core-shaped symbol can reject the WHOLE namespace, and a hazard that fires on one host and not the other is the exact thing this port exists to avoid.

A successful ToolResult (SPEC §0.1).

NOT named `ok`: cljgo's clojure.core HAS `ok` and `err` (the JVM's does not),
so `(defn ok ...)` shadows a core name on one host only — measured 2026-07-31.
It warns today; per cljgo's static interop scan a bare core-shaped symbol can
reject the WHOLE namespace, and a hazard that fires on one host and not the
other is the exact thing this port exists to avoid.
sourceraw docstring

toolclj/s

(tool {:keys [name description input-schema source execute hints context?]})

Build a Tool. execute takes (args) or (args ctx) and returns a ToolResult.

Two optional keys (SPEC §4C, change add-go-host-asks):

:hints {:read-only :destructive :open-world} — advisory behaviour hints for a host permission gate; passed on :before-tool events as :hints. nil = unknown (treat as mutating). Never serialized into any adapter output. :context? true when execute has the (args ctx) arity and wants the loop's ToolContext on EVERY call — {:call-id :cancel} (plus :answer on a §10 re-run). Absent, the loop calls (execute args) exactly as before: there is no portable way to ask a fn its arity, and a single-arity tool handed a ctx would fail.

Build a Tool. `execute` takes (args) or (args ctx) and returns a ToolResult.

Two optional keys (SPEC §4C, change add-go-host-asks):

  :hints     {:read-only :destructive :open-world} — advisory behaviour hints
             for a host permission gate; passed on `:before-tool` events as
             `:hints`. nil = unknown (treat as mutating). Never serialized into
             any adapter output.
  :context?  true when `execute` has the (args ctx) arity and wants the loop's
             ToolContext on EVERY call — `{:call-id :cancel}` (plus `:answer`
             on a §10 re-run). Absent, the loop calls (execute args) exactly as
             before: there is no portable way to ask a fn its arity, and a
             single-arity tool handed a ctx would fail.
sourceraw docstring

tool-namesclj/s

(tool-names tk)

Sorted, always. Two runtimes must not disagree on order — which is why this uses compare-strings and not sort; see there.

Sorted, always. Two runtimes must not disagree on order — which is why this
uses `compare-strings` and not `sort`; see there.
sourceraw docstring

toolkitclj/s

(toolkit tools)
(toolkit tools sources)

A toolkit is {:tools {name -> Tool} :sources {source -> status}}. Later tools win on a name collision, which is how SPEC §0.11's MCP precedence over builtins is expressed: register builtins first, MCP after.

Each toolkit carries an IDENTITY in its metadata (toolkit-id), kept by add-tools/assoc/update and ignored by =. A toolkit is a value, so a later registration is a new value — the identity is what lets a client freeze one SPEC §4C tool-tier plan per toolkit across those values.

A toolkit is {:tools {name -> Tool} :sources {source -> status}}.
Later tools win on a name collision, which is how SPEC §0.11's MCP precedence
over builtins is expressed: register builtins first, MCP after.

Each toolkit carries an IDENTITY in its metadata (`toolkit-id`), kept by
`add-tools`/`assoc`/`update` and ignored by `=`. A toolkit is a value, so a
later registration is a new value — the identity is what lets a client freeze
one SPEC §4C tool-tier plan per toolkit across those values.
sourceraw docstring

toolkit-idclj/s

(toolkit-id tk)

The toolkit's identity (see toolkit), or nil for a hand-built map.

The toolkit's identity (see `toolkit`), or nil for a hand-built map.
sourceraw docstring

with-partsclj/s

(with-parts result parts)

SPEC §1B — attach non-text parts to a ToolResult.

cond->, so an EMPTY or absent parts list leaves the result byte-identical to what success/failure produced. That is the whole compatibility story: the 52 construction sites in this port are untouched, and a text-only tool's JSON does not move by a byte.

output stays REQUIRED and stays what the transcript, compaction, token estimation and any text-only provider see — a tool returning an image sets output to a description and parts to the image.

SPEC §1B — attach non-text `parts` to a ToolResult.

`cond->`, so an EMPTY or absent parts list leaves the result byte-identical to
what `success`/`failure` produced. That is the whole compatibility story: the
52 construction sites in this port are untouched, and a text-only tool's JSON
does not move by a byte.

`output` stays REQUIRED and stays what the transcript, compaction, token
estimation and any text-only provider see — a tool returning an image sets
`output` to a description and `parts` to the image.
sourceraw docstring

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
←Move to previous article
→Move to next article
Ctrl+/Jump to the search field
× close