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. 0 success, 1 general error, 64 bad arguments, 65 database error, 66 no daemon, 67 not found, 68 conflict. Branch on the code rather than scraping stderr. 67 in 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 --json reports 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 wantUse
Run an agent session as a durable jobdispatch
Run multi-step work with approval gatessequence
Write that multi-step work down as your ownsequence authoring
Fire work on a timerscheduler
Store and recall what was learnedmemory
Store and search documentslibrary
Ask what a codebase containscode index
Resolve a directory to a known projectprojects
Notify a human and waitmessaging

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.