An agent toolbox, written in cancho
Small tools an AI agent can rely on.
grep, sed, jq, mv and tar, rebuilt so every answer is JSON, every refusal names a rule, and no binary can do more than it says.

Two agents, one file
Both read notes.txt and each adds a line. Everything on the left is what the built tools printed.
What happens to the file
The demo above as a video you can share. It is a recording of this page, not a live session. Download.
Built for two readers
The same binaries, told twice: what a person gets, and what an agent can parse.
Safe by default
Writes are atomic and only land if the file is what you said it was. Nothing outside --root can be reached. move never overwrites; there is no delete.
The same every time
No locale, no clock, no environment: the same input gives the same bytes. Memory is bounded, about 1.7 MB for a 1 MiB file or a 256 MiB one.
Easy to check
Each binary embeds what the compiler found it may do. CI fails if a tool grows past its ceiling. The table below is that data, not a promise.
# you need git, Rust and clang; the first build takes a few minutes git clone https://github.com/alpibrusl/cancho git clone https://github.com/alpibrusl/cancho-tools && cd cancho-tools REV=$(sed -n 's/^cancho *= *"\([0-9a-f]*\)".*/\1/p' cancho.toml) (cd ../cancho && git fetch -q origin && git checkout "$REV" && cargo build --release -p cancho) export PATH=$PWD/../cancho/target/release:$PWD/build:$PATH cancho build # every tool into build/ seek --root . --format text 'fn main' tools/seek/seek.cho
# a real refusal (write, exit status 5), trimmed { "ok": false, "command": "write", "error": { "code": "CONFLICT", "rule": "precondition.hash-mismatch", "hint": "re-read the file, then decide", "repair": { "kind": "none", "reason": "the file changed since it was read; re-read, then decide" }, "detail": { "path": "notes.txt", "expected_sha256": "936bb8ff57a0…", "actual_sha256": "7bde7db5f30a…", "size": 31 } } } # the tool describes itself, from the tables its parser runs on seek introspect # flags, rules, exit codes, limits, schema, authority seek skill # a guide for an agent, printed by the binary build/mcp --root /work # the same tools over MCP, stdio
Try cancho-tools on one small task. Work in a new empty directory, never use sudo, and report honestly: do not retry a failing command in a loop and do not hide a failure. 1. Check that git, cargo (Rust) and clang exist. If one is missing, stop and tell me which; do not install system packages yourself. 2. Get the sources and build, as the Quick start of the README says: git clone https://github.com/alpibrusl/cancho git clone https://github.com/alpibrusl/cancho-tools && cd cancho-tools REV=$(sed -n 's/^cancho *= *"\([0-9a-f]*\)".*/\1/p' cancho.toml) (cd ../cancho && git fetch -q origin && git checkout "$REV" && cargo build --release -p cancho) export PATH=$PWD/../cancho/target/release:$PWD/build:$PATH cancho build 3. Ask a tool what it is: `seek introspect` (JSON: flags, rules, exit codes, authority). 4. Reproduce the race on a scratch file, with the tools only: printf 'shopping list\n' > notes.txt hash --root . notes.txt # note the hex: H1 printf 'shopping list\n- milk\n' | write --root . --if-sha256 H1 --stdin notes.txt # expect changed:true printf 'shopping list\n- bread\n' | write --root . --if-sha256 H1 --stdin notes.txt # expect exit 5, rule precondition.hash-mismatch Read actual_sha256 from the refusal's detail, re-apply both lines and retry with that hash. 5. Tell me: the exit status and the `rule` of the refusal in step 4, the final contents of notes.txt, and anything that failed, with the exact error. Docs: README.md, docs/reference.md, docs/agent-first.md.
Each tool page has its flags, every rule, the exit codes, the JSON schemas and a ready-made SKILL.md. Not claimed: that agents do better with these tools than with the incumbents; that needs an agent-in-the-loop evaluation.
11 tools
Each one stays small, and says what it reads, what it changes, and what it cannot undo.
Find literal text in files. A stream of matches, with a cursor when it is cut short.
listinstead of ls, find, treeThe entries beneath a directory, sorted and depth-bounded. Links are never followed.
peekinstead of sed -n, head, tailA range of lines or bytes, with numbers and a cursor to carry on from.
jsonqinstead of jqOne value from one strict JSON document, by RFC 6901 pointer.
tallyinstead of sort | uniq -cDistinct lines or fields, counted and ranked.
hashinstead of sha256sumSHA-256 or SHA-512 of files of any size, and --verify.
writeinstead of cp, >, teeReplace a file atomically, only if it holds what you say it does.
replaceinstead of sed -iReplace exact text an expected number of times, atomically.
moveinstead of mvRename a file or directory in place, never over an existing name.
archiveinstead of tar, gzip, zipList, extract and create tar, zip, gzip and zstd; read bzip2 and xz. Checked whole before a file is written.
runinstead of exec, timeoutOpt-in. Start one program from a fixed directory with exact arguments, a fixed environment, a deadline and an output cap.
What each binary may do
Derived from the source by the compiler and embedded in the binary. CI fails if a tool goes past its ceiling.
| tool | reads files | reads stdin | changes files | starts programs | reads the clock | uses the network | if it goes wrong |
|---|---|---|---|---|---|---|---|
| seek | yes | no | no | no | no | no | reversible-cheap |
| list | yes | no | no | no | no | no | reversible-cheap |
| peek | yes | no | no | no | no | no | reversible-cheap |
| jsonq | yes | yes | no | no | no | no | reversible-cheap |
| tally | yes | yes | no | no | no | no | reversible-cheap |
| hash | yes | no | no | no | no | no | reversible-cheap |
| write | yes | yes | yes | no | no | no | irreversible-bounded |
| replace | yes | no | yes | no | no | no | irreversible-bounded |
| move | yes | no | yes | no | no | no | irreversible-bounded |
| archive | yes | no | yes | no | no | no | irreversible-bounded |
| run | no | yes | no | yes | yes | no | irreversible-consequential |
Dots are what the compiler found each program may do; a row is read from the binary's own introspect. None of them can reach the network. run is opt-in and the only one that starts programs or reads the clock. Not shown: how much of each it may touch (every label is bounded; see a tool's page).
Measured, not promised
What the repository can show today.
A 64 MiB text file through a pipe, minimum of 7 rounds, Linux x86-64 on a shared machine. These tools are not built to win on speed; speed is reported, not gated. sha256sum leads on hash because of OpenSSL's hand-written assembly. All the numbers.