cancho-tools

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.

11 toolsJSON outrefusals with a ruleno networkEUPL-1.2
cancho-tools: a rock with crossed tools

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

notes.txt
agent A
agent B

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
prompt for your coding agent
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.

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.

toolreads filesreads stdinchanges filesstarts programsreads the clockuses the networkif it goes wrong
seekreversible-cheap
listreversible-cheap
peekreversible-cheap
jsonqreversible-cheap
tallyreversible-cheap
hashreversible-cheap
writeirreversible-bounded
replaceirreversible-bounded
moveirreversible-bounded
archiveirreversible-bounded
runirreversible-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.

11tools: 10 by default, and run, which is opt-in
254named refusals, each with an exit status and a repair kind
0tools that can reach the network, by the authority the compiler derived
11published JSON schemas the outputs are checked against

Time to answer, 64 MiB (shorter is faster)

seek
seek 0.49 s
grep 0.51 s
rg 0.39 s
peek
peek 0.08 s
sed+wc 0.13 s
tally
tally 0.16 s
cut|sort|uniq 0.68 s
jsonq
jsonq 0.14 s
jq 0.73 s
list
list 0.144 s
find 0.146 s
hash
hash 0.52 s
sha256sum 0.17 s

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.