Building on toryo
toryo is an engine you can build on. Every app exposes a command-line interface as its integration contract, so your own program can queue agent jobs, run sequences, and read and write the knowledge plane, while keeping its own interface and its own data.
The tool you build stays yours. toryo is the part underneath that runs agents and remembers things.
The contract is the CLI#
There is no SDK to install and no library to import. Integration means running a binary and reading its output, which means any language that can spawn a process can build on toryo: TypeScript, Python, Go, or a shell script.
Four conventions hold across every app, and they are what make this dependable rather than fragile:
- One JSON document per read command. Exactly one, on a single line with a
trailing newline. Parse the last non-empty line of stdout and you have your
answer. Large payloads can be routed to a file with
--out <path>instead. - Shared exit codes.
0success,1general error,64bad arguments,65database error,66no daemon,67not found,68conflict. Branch on the code rather than scraping stderr.67in particular is a real answer, not a failure: it means the thing you asked for does not exist. - The surface is frozen and versioned. A
CONTRACT_VERSION, separate from the product version, covers the commands, their flags, the JSON shapes, the exit codes, and the environment variables. Tests fail the build if a command registry drifts from the published manifest, so the surface cannot change by accident. - Every app can tell you which version it is.
toryo-<app> version --jsonreports the product and contract version, so your tool can assert compatibility on startup instead of discovering it mid-run.
Invoke either through the umbrella binary, toryo <app> <command>, or directly
as toryo-<app> <command>. Both live in ~/.toryo/bin after installation.
Every command, flag, exit code, and environment variable is listed in the CLI & contract reference, which is generated from that frozen contract and cannot drift from the binaries you have installed.
What you can build on#
| You want | Use |
|---|---|
| Run an agent session as a durable job | dispatch |
| Run multi-step work with approval gates | sequence |
| Write that multi-step work down as your own | sequence authoring |
| Fire work on a timer | scheduler |
| Store and recall what was learned | memory |
| Store and search documents | library |
| Ask what a codebase contains | code index |
| Resolve a directory to a known project | projects |
| Notify a human and wait | messaging |
One worth calling out on its own: completion runs a single structured-output
model call on the operator's Claude subscription rather than a metered API key,
returning an object validated against a JSON Schema you supply. If your tool
would otherwise reach for an SDK and its own billing, this is the alternative.
A worked example#
github.com/ForceBuilders/toryo-pr-example is a complete one you can read: a GitHub pull request reviewer with its own sequence, its own database, and its own interface. It is deliberately small, and it is the fastest way to see the shape of all of this in real code.
Reviewing pull requests was built this way first: as a separate app on top of toryo, not as a feature inside it. It was folded into toryo for a while, and then moved back out, because building it outside had produced the better tool. The rubric, the severity tiers, the finding store, the triage screen and the posting rules are all opinions about how one team wants to review code, and those belong to the team that holds them.
Everything domain-specific lives in that repo. What it takes from toryo is:
- A sequence of its own, as a YAML file in its own
.toryo/sequences/directory. No fork, no registration, no code compiled into toryo. See Writing your own sequences for the format and the four ways to produce one. - Dispatch, to run the review session as tracked work rather than a subprocess it has to supervise.
- The project registry, to turn a directory into a stable id.
The integration is unremarkable, which is the point: about sixty lines that spawn a toryo CLI, check the exit code, and parse stdout.
// Resolve a repository handle to a project id.
const found = spawnSync('toryo', ['project', 'get', repoSlug], {encoding: 'utf8'});
if (found.status !== 0) return null; // 67 means no such project
const {id} = JSON.parse(found.stdout);
The same pattern writes back what an operator taught the system:
// Record what the reviewer just taught us, against that project.
spawnSync('toryo', [
'memory', 'remember',
'--source-type', 'learning',
'--scope', 'project',
'--project', id,
'--tags', tags.join(','),
'--content', content,
]);
That is the whole integration surface. No imports, no shared database, no build step tying the two repositories together. The tool can be rewritten in another language without toryo noticing.
Rules worth knowing#
- Never read another app's database. Apps own their schema and exchange data only through their CLIs. A tool that queries toryo's Postgres directly is coupled to internals that carry no compatibility promise, unlike the CLI, which does.
- Pass paths explicitly. toryo always operates from its own working directory and reaches into target directories through parameters, so nothing depends on your process's current directory.
- Assert the contract version at startup. It is one call, and it turns a confusing mid-run failure into a clear message on launch.