Skip to content

Code tools

zkit/ai/tools/code ships the standard coding toolset. Every tool is workspace-rooted — paths resolve inside the workspace, not the host filesystem — and each one is shaped by watching agents work, not by API symmetry. Two more tools live in sibling packages: web_fetch (zkit/ai/tools/fetch) and web_search (zkit/ai/tools/search).

ToolNotes
readFile reads with offset/limit + stable line hashes for anchored edits. Pure → cached by MemoSource.
writeCreates only — refuses to overwrite. Existing files are rejected with a validation error naming edit as the recovery path.
write_appendAppend-only writes, so intent is explicit. Also the structural fix for large files: scaffold with write(path, ""), then chunk via repeated appends.
editLine/hash anchored edits from read output. Anchors usually survive line-number shifts from earlier edits; stale-anchor failures tell the model to re-read and retry with fresh anchors.
grepWorkspace-rooted search with glob filtering. Pure → cached.
globWorkspace path enumeration by glob pattern. Returns labelled or JSON output.
lsStructured single-level directory listing. Pure → cached.
bashShell execution, backed by the process manager (below). Foreground commands return inline; long-running commands return a process ID.
bash_outputRead accumulated stdout/stderr from a background process, with cursor-based polling.
stop_processTerminate a background process (SIGTERM with grace, then SIGKILL).
list_processesInventory of tracked processes — running and recently exited.
save_planPersist a plan-mode artifact to .zarlcode/plans/<name>.md. Scaffolds the plan file.
save_plan_appendAppend chunks to a plan file — the structural partner to write_append for plan documents.
update_planUpdate the live structured plan rendered in the TUI plan pane. Drives step tracking (pending → in_progress → completed).

This is a runtime invariant, not prompt guidance: the tool physically cannot clobber an existing file, so a model whose instinct is “rewrite the whole file” is forced through the read → edit loop instead. Small-model benchmark runs show the refusal fires on a substantial fraction of exercises and consistently improves correctness — wholesale rewrites of existing files are rarely the right move and frequently destroy code the model never read.

bash doesn’t just exec and block — that would wedge the loop the first time the model starts a dev server.

  1. Short-lived commands run synchronously and return output inline.
  2. Commands still running at the timeout get a process ID; output is teed to a ring buffer and the tool returns “process started, use bash_output to read”.
  3. bash_output(id) reads the buffer; stop_process(id) terminates (SIGTERM, then SIGKILL after grace); list_processes is the inventory.

The agent can run npm run dev or a long test loop without losing the conversation. ProcessManager owns every goroutine and KillAll() on shutdown means no orphan children outlive the host.

Shell execution composes with zkit/agent/sandbox: kernel-enforced confinement via a Landlock filesystem allow-list plus an optional empty network namespace. The sandbox is enforced by the kernel, not by string-matching the command — bash runs whatever it runs, and the kernel says no to anything outside the allow-list. (Linux only; on other platforms the policy guardrail below is the only line of defence.)

The shell-policy guardrail is the advisory layer in front of this: it rejects the obviously destructive patterns before they execute, sandbox or not.

Three tools form a plan persistence and display pipeline:

  • save_plan creates .zarlcode/plans/<name>.md — a markdown artifact the user can revisit, edit, or share. Writes are locked to the plans directory.
  • save_plan_append appends chunks — the structural fix for plans that exceed the single-call content cap, analogous to write_append for code files.
  • update_plan feeds the TUI’s structured plan pane: an ordered list of steps each in pending, in_progress, or completed state. The plan pane renders live as the agent works.

Together they let the agent produce a plan in PLAN mode, then track execution step-by-step in BUILD mode — all visible to the user through the TUI without reading raw tool output.

These live in sibling packages under zkit/ai/tools/ rather than code/ because they aren’t workspace-scoped, but they register into the same registry:

  • web_fetch (zkit/ai/tools/fetch) — HTTP GET with automatic chromedp fallback for JavaScript-heavy pages. SSRF protection via transport-level IP dial guard. Capped output (200k chars).
  • web_search (zkit/ai/tools/search) — uses one stable tool contract with selectable SearXNG and Brave Search API backends. Both return the same labelled or JSON output. Default 10 results, hard cap 25. SearXNG remains the self-hosted default; Brave avoids scraper throttling through its independent licensed index.