Filesystem tools exposed as bare symbols in the Python sandbox.
Two layers:
Structured helpers for tree / search / structure:
(ls dir) ; a DIRECTORY -> [{name path type size}], directories first;
(ls dir, depth=2) ; nested rows sit in children. A SANDBOX helper, not a
; native tool: it is called inside a python_execution block.
; A nil or blank path throws before any I/O.
(grep query) ; -> ONE anchored TEXT block, never a map: a summary line,
; then <line>:<hash>| <text> rows under each path;
; query = a term or list of terms (OR), smart-case
; substring — or a REGEX with is_regex.
; Opts: paths/include/limit/is_hidden/is_regex
(struct_index paths) ; -> per-file skeleton: imports, definitions, signatures
Cwd-safe wrappers over the babashka.fs file API. Code is edited by NAME with
struct_patch and anything else by ADDRESS with cat/patch — ONE patch
call carries every edit for one file and writes once; plain Python owns
whole-file creation and deletion:
(create-dirs path) (copy src dest) (move src dest) (delete path) (delete-if-exists path) (exists? path)
Hard guard: every path must stay inside the session's working
directory (fs/cwd); .. traversal is rejected before any I/O.
Filesystem tools exposed as bare symbols in the Python sandbox.
Two layers:
1. Structured helpers for tree / search / structure:
(ls dir) ; a DIRECTORY -> [{name path type size}], directories first;
(ls dir, depth=2) ; nested rows sit in `children`. A SANDBOX helper, not a
; native tool: it is called inside a python_execution block.
; A nil or blank path throws before any I/O.
(grep query) ; -> ONE anchored TEXT block, never a map: a summary line,
; then ` <line>:<hash>| <text>` rows under each path;
; query = a term or list of terms (OR), smart-case
; substring — or a REGEX with `is_regex`.
; Opts: paths/include/limit/is_hidden/is_regex
(struct_index paths) ; -> per-file skeleton: imports, definitions, signatures
2. Cwd-safe wrappers over the babashka.fs file API. Code is edited by NAME with
`struct_patch` and anything else by ADDRESS with `cat`/`patch` — ONE `patch`
call carries every edit for one file and writes once; plain Python owns
whole-file creation and deletion:
(create-dirs path)
(copy src dest)
(move src dest)
(delete path)
(delete-if-exists path)
(exists? path)
Hard guard: every path must stay inside the session's working
directory (`fs/cwd`); `..` traversal is rejected before any I/O.Default editing symbol set for docs/tests. A delay so the language/env
scan it triggers runs on first deref (tests, docs) and NEVER at namespace
load — a load-time call reaches git/run-git's future, which starts the
agent thread-pool and native-image refuses that started thread in the image
heap. Deref with @editing-symbols.
Default editing symbol set for docs/tests. A `delay` so the language/env scan it triggers runs on first deref (tests, docs) and NEVER at namespace load — a load-time call reaches `git/run-git`'s `future`, which starts the agent thread-pool and native-image refuses that started thread in the image heap. Deref with `@editing-symbols`.
(list-directories env args)List directories for the sandbox's ls helper. args is the string-keyed
request — {"paths" [dir | {"path" dir, …}, …], "depth" n, "is_hidden" b},
an entry's own options overriding the shared ones — and the answer is one row
per requested directory IN REQUEST ORDER.
Listing a directory is not a wire round trip: it is a call inside the Python
block the model was already running, so it costs no tool result and no native
tool slot. What it may never lose by leaving the tool layer is the boundary,
so the :fs/access gate is asked here exactly as the native readers ask it —
an extension that hides a tree hides it from the listing too.
Everything that can go wrong throws ex-info and the shim maps its :type
onto the Python exception a caller can actually catch: a gate refusal is a
PermissionError, a path that does not exist a FileNotFoundError (naming
the nearest existing directory), a FILE path a NotADirectoryError.
List directories for the sandbox's `ls` helper. `args` is the string-keyed
request — `{"paths" [dir | {"path" dir, …}, …], "depth" n, "is_hidden" b}`,
an entry's own options overriding the shared ones — and the answer is one row
per requested directory IN REQUEST ORDER.
Listing a directory is not a wire round trip: it is a call inside the Python
block the model was already running, so it costs no tool result and no native
tool slot. What it may never lose by leaving the tool layer is the boundary,
so the `:fs/access` gate is asked here exactly as the native readers ask it —
an extension that hides a tree hides it from the listing too.
Everything that can go wrong throws `ex-info` and the shim maps its `:type`
onto the Python exception a caller can actually catch: a gate refusal is a
`PermissionError`, a path that does not exist a `FileNotFoundError` (naming
the nearest existing directory), a FILE path a `NotADirectoryError`.(refresh-file-summary summary before after)Recompute a per-file summary's "diff"/"lines"/"changed" from the ORIGINAL
before and the FINAL on-disk after. A language pack that rewrites a
just-edited file in an :after op-hook (parinfer paren-repair + cljfmt) calls
this so the MODEL-FACING diff and counts show the bytes actually written, not
the pre-hook intermediate the raw edit produced. All other summary keys are
preserved.
Recompute a per-file summary's "diff"/"lines"/"changed" from the ORIGINAL `before` and the FINAL on-disk `after`. A language pack that rewrites a just-edited file in an :after op-hook (parinfer paren-repair + cljfmt) calls this so the MODEL-FACING diff and counts show the bytes actually written, not the pre-hook intermediate the raw edit produced. All other summary keys are preserved.
(structural-supported? _env)Whether the STRUCTURAL editors should be advertised for the current project:
true when its language scan finds at least one file in a structurally-supported
language. FAILS OPEN — a scan error, an empty/new repo, or an all-unrecognized
tree all return true, so a useful editor is NEVER hidden on uncertainty. Only a
project that scanned cleanly AND contains code, NONE of it structurally supported
(a pure docs/config repo, or an unsupported-language project), returns false.
env is ignored — the answer comes from the cached env snapshot, not per-call
runtime state.
Whether the STRUCTURAL editors should be advertised for the current project: true when its language scan finds at least one file in a structurally-supported language. FAILS OPEN — a scan error, an empty/new repo, or an all-unrecognized tree all return true, so a useful editor is NEVER hidden on uncertainty. Only a project that scanned cleanly AND contains code, NONE of it structurally supported (a pure docs/config repo, or an unsupported-language project), returns false. `env` is ignored — the answer comes from the cached env snapshot, not per-call runtime state.
(write-safe args)Whole-file write primitive: create a new file OR overwrite an
existing one with :content. Returns a structured result; never
throws on normal failure paths (file exists with is_overwrite false,
stale mtime/size, path escape).
Required keys: :path, :content (string).
Optional keys:
:is_overwrite default true; when false and target exists
→ :reason :exists
:expected_mtime staleness guard; mismatch → :reason :stale
:expected_size staleness guard; mismatch → :reason :stale
Success shape: {:success? true :plan {:path :before :after :op} :checks [<check>]}
Failure shape: {:success? false :failures [failure-with-:reason] :checks [<check>] :loop-hint <string-or-nil> :message <human-readable>}
Whole-file write primitive: create a new file OR overwrite an
existing one with `:content`. Returns a structured result; **never
throws on normal failure paths** (file exists with is_overwrite false,
stale mtime/size, path escape).
Required keys: `:path`, `:content` (string).
Optional keys:
:is_overwrite default true; when false and target exists
→ :reason :exists
:expected_mtime staleness guard; mismatch → :reason :stale
:expected_size staleness guard; mismatch → :reason :stale
Success shape:
{:success? true
:plan {:path :before :after :op}
:checks [<check>]}
Failure shape:
{:success? false
:failures [<failure-with-:reason>]
:checks [<check>]
:loop-hint <string-or-nil>
:message <human-readable>}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 |