On this pageOverview
Automation

The tori CLI

Scripting Tori from a shell.

The tori binary doubles as a CLI: run from any shell, including a terminal tab Tori itself spawned, it talks to Tori’s daemon over the same protocol its MCP server and phone app use. The daemon keeps running after you quit, so tori works with the window closed. Every shell Tori opens has tori on its PATH already; for a terminal outside Tori, click Add under Settings > Advanced > Terminal command.

Terminal window
tori sessions [--live] [--cwd <path>] [--limit <n>] [--json]
tori projects [--json]
tori ports [--json]
tori session tail <id> [--lines <n>] [--agent <id>] [--json]
tori wait <id> [--timeout <secs>] [--json]
tori session wait <id> [--timeout <secs>] [--json]
tori check <id> [--json]
tori handoff <id> --to <agent> [--account <id>] [--model <id>] [--note <text>] [--json]
tori session pending <id> [--json]
tori session answer <session> <id> <text>...
tori session answer <session> <id> --each <text>...
tori events [--topic <topic>]...
tori whoami [--json]
tori steer <id> <text>...
tori interrupt <id>
tori worktree new <branch> [--project <path>] [--from <ref>]
tori checkpoints <id> [--json]
tori checkpoint diff <id> <n> [<m>] [--why] [--json]
tori checkpoint revert <id> <n> [--force]
tori spawn [--agent <id>] [--account <id>] [--model <id>] [--mode <id>] [--effort <level>]
[--folder <path> | --new-worktree <branch> [--project <path>] [--from <ref>]]
[--prompt <text>] [--attach <path>]... [--background] [--json]
tori open <path> [--line <n>]
tori open --browser [<branch>]
tori budget [<id>] [--folder <path>] [--item <id>] [--pr <n|url>] [--json]
tori ask <question>... [--option <text>]... [--timeout <secs>] [--approval <json> [--project <path>]]
tori ask --wait <id> [--timeout <secs>]
tori ask --answer <id> <text>...
tori pr create --head <branch> --head-sha <sha> --base <branch> --title <text>
[--body <text>] [--draft] [--project <path>] [--approval <id>] [--json]
tori pr review <number> --event approve|comment|request-changes --head-sha <sha>
[--body <text>] [--comments <json>] [--project <path>] [--approval <id>]
tori pr merge <number> --method merge|squash|rebase --head-sha <sha>
[--project <path>] [--approval <id>]
tori autopilot state [--json]
tori autopilot start|stop [--json]
tori autopilot item [<id>] [--kind ship|review --issue <key> | --pr <number> --repo <owner/name>]
[--project <path>] [--state <state>] [--worktree <path>] [--session <id>]
[--pr-url <url>] [--note <text>] [--title <text>] [--contract <text>] [--json]
tori autopilot project [--project <path>] [--ships pr|local]
[--autonomy ask-everything|auto-until-outward] [--pickup ask|auto]
[--agent <id>] [--account <id>] [--model <id>] [--issues <json>] [--json]
tori autopilot hold resolve <id> [--json]
tori autopilot schedule add --name <text> --cron <expr> (--prompt <text> | --issue <key>)
[--project <path>] [--json]
tori autopilot schedule list | remove <id> | resume <id> [--json]
tori loop --prompt <text> [--project <path>] [<entry>...] [--json]
tori mcp
tori attach <session|tab>
tori daemon [--uninstall]

--json is available on read commands for scripting. Exit codes: 0 on success, 2 for tori ask specifically when it returns a question’s id with no answer yet, distinct from a real failure so a script can tell the two apart, and 1 for everything else that went wrong.

Who is calling

Every terminal tab and every chat Tori starts gets a token of its own, so the app knows which session a command came from and fills in its project, folder, agent and account. That is why tori worktree new fix/retry needs no --project when you run it inside a project’s tab. tori whoami prints what Tori thinks you are.

A command run from a terminal outside Tori is an outside caller: it works, with no defaults, so pass --project or --folder.

Looking

Terminal window
$ tori sessions --live
ID AGENT ACCOUNT STATE BRANCH FOLDER TITLE
7c1e9a2f-4b0d-4e2a-9f61-3d8c2b7a5e14 claude work needs you feat/webhooks ~/work/api-webhooks Sign webhook payloads
d4b80c13-9e7f-4a55-8c02-61f0e3a9b7d2 codex default working fix/rate-limit ~/work/api-rate-limit Cap the rate limiter
  • tori sessions lists sessions, newest first. --live keeps only the running ones and --cwd only those in a folder.
  • tori projects prints the tree the sidebar draws: spaces, projects and branch rows, then Topics with their members. A Topic in a space has the space’s name after its branch.
  • tori ports lists each worktree’s dev servers: branch, folder, port block and the ports listening.
  • tori session tail <id> prints the last events of a conversation, with tool output capped.
  • tori wait <id> (or tori session wait <id>) blocks until a session stops working, then prints why it came back, the question it is waiting on if any, and its last message. See Waiting on a session.
  • tori check <id> prints what a session’s work came to: the verdict of its latest turn that changed code, each check that turn ran, and a diff stat over all its turns. See Checking a session’s work.
  • tori session pending <id> lists what a session is waiting on: questions and permission prompts, each with the id session answer takes.
  • tori events streams what happens in the app as lines of JSON, until you stop it. --topic narrows it, and can be repeated.
  • tori budget prints spend for a session and its project against your ceilings, and the agent’s quota windows. --item adds what an autopilot item cost, and --pr what a pull request’s branch cost (see What work costs).
  • tori checkpoints <id> lists a session’s checkpoints, numbered by turn. tori checkpoint diff <id> <n> prints what turn n changed, and <n> <m> spans a run of turns. --why says who wrote each hunk (below), and --json prints the answer as data.

Who wrote a hunk

tori checkpoint diff <id> <n> --why prints the same diff with # why lines above each hunk, one per run of lines that one turn wrote, the same answer the hunk panel gives:

# why lines 12 to 14: turn 3 of fix the parser at 20261008T140200Z, Edit /repo/src/parse.ts
# said: Splitting the tokenizer so the error carries its position.
# why line 20: turn 3 of fix the parser at 20261008T140200Z, shell: Bash sed -i 's/old/new/' src/parse.ts
@@ -10,6 +10,9 @@

The time is the prompt’s, in UTC. # said: is the first line of what the agent wrote before the call. Where a turn wrote the lines in several calls it says one of: and lists them, the ones naming the file first, three at most. Where nobody can be named the line gives the reason instead:

  • no session Tori recorded wrote this, it was there at the first checkpoint
  • older than the changes Tori walks back
  • written in a turn of <session>, which Tori ran but cannot read back
  • written while <session>, <session> were each in a turn that could have written it
  • written while no session Tori knows of was in a turn: by hand, or by a session Tori did not see

--json prints the whole answer instead, the diff plus a why list of each file’s hunks and their claims. A chat running in a Topic’s home is refused with why reads one worktree, and this session runs across a Topic's worktrees.

Over the socket, provenance.hunks answers the same question for any file in a worktree, against the working tree.

Acting

  • tori spawn starts a session and prints its id. --new-worktree makes the worktree first (and waits for the project’s setup command when that is turned on). --prompt is its first message and --attach adds files to it.
  • tori steer <id> <text> sends a message to a live chat: a steer if a turn is running, its next turn if not. Run by an agent, a message to a chat it is not allowed to reach prints queued and waits for you to accept it.
  • tori interrupt <id> stops a chat’s current turn.
  • tori handoff <id> --to <agent> hands a live chat over to another agent and prints the new session’s id. See Handing a session over.
  • tori session answer answers a question or a permission prompt by id. --each answers a question that has several parts, one answer per part.
  • tori worktree new <branch> creates a worktree and prints its path.
  • tori checkpoint revert <id> <n> puts the session’s folder back to turn n. A backstop is taken first.
  • tori open <path> --line <n> opens a file in Tori’s editor.
  • tori open --browser [<branch>] opens the current worktree’s dev server, or another branch’s in the same project, in your default browser.
Terminal window
$ tori spawn --agent codex --new-worktree fix/retry --prompt "retry 5xx deliveries"
0f3b7d5e-8c2a-4b1e-9d6f-2a7c4e1b9f30
$ tori steer 0f3b7d5e-8c2a-4b1e-9d6f-2a7c4e1b9f30 "also cover 429s"

Waiting on a session

tori wait <id> waits up to --timeout seconds (60 by default, an hour at most) and prints the reason on its first line:

  • idle: the session finished its turn.
  • asking: it is waiting on a question, printed on the next line as asking <id>: <question>. Answer it with tori session answer.
  • needs_you: it is waiting on a permission prompt or a question of its agent’s own. tori session pending <id> lists it.
  • limited until <time>: its turn was refused by a usage limit, which resets at that time. Claude only for now.
  • ended: the session is gone. A session that was handed over says where, as handed_to in --json.
  • timeout: it was still working when the wait ran out.

Then comes the session’s last message. --json prints the whole answer: state, reason, resets_at when limited, handed_to when handed over, question and last.

Checking a session’s work

Terminal window
$ tori check 0f3b7d5e-8c2a-4b1e-9d6f-2a7c4e1b9f30
failed
failed cargo test
3 files, +48 -6

The first line is the verification verdict of the session’s latest turn that changed code: verified, failed or unverified, or no code changed when no turn did. Each check that turn ran follows with how it came back. The last line counts every turn’s changes, only in files the session wrote itself, so another session’s edits in the same worktree are left out. With Mark unverified turns off in Settings > Chat it says verification off instead of a verdict.

A worker may run tori check, so a reviewer can read its writer’s verdict before reading the diff.

Handing a session over

tori handoff <id> --to <agent> starts a new session of that agent in the same folder and closes the old one. The new session’s first message is a brief Tori writes from the old one: its first prompt, its last reply, what it was waiting on, its tori check result, your --note, and a reference to the old session that the new agent reads the rest of with its session_history tool. Nothing spends a turn summarising, so it works when the old session hit its usage limit. --account and --model pick the new session’s account and model.

The old session closes once it is idle, never while it waits on a question or a permission prompt. A session can hand itself over: its own turn finishes first. The new session takes the old one’s place: it belongs to the same spawner and runs in the background if the old one did. Waiting on the old id answers ended, with the new id as handed_to.

Only a live chat can be handed over. A worker cannot hand anything over, and a session the autopilot is driving is refused, the autopilot included.

A writer and a reviewer

A chat writing a change can ask a second agent to review it, wait for the verdict, and hand itself over when it runs out of room. From the writer’s shell:

Terminal window
$ me=$(tori whoami --json | jq -r .caller.id)
$ reviewer=$(tori spawn --agent codex --prompt "Review the change in this folder. \
Run tori check $me first, then read the diff. Reply with blocking issues only.")
$ tori wait "$reviewer" --timeout 600
idle
Blocking: the retry loop never stops on a 4xx.

The reviewer is a worker: it runs tori check on the writer and reads the diff, and it cannot spawn, steer, wait or hand off. If it asks a question, tori wait comes back with asking and the writer answers it with tori session answer.

When the writer’s context is nearly full, it hands the fix to a fresh agent and finishes its turn:

Terminal window
$ tori handoff "$me" --to claude --note "Stop retrying on a 4xx, the reviewer's one blocking issue"
6a1d0c94-2f7e-4b3a-8e51-9c0d7f2b4a68

Asking you

tori ask puts a question card in the chat that called it, raises the needs you dot and a notification, and waits. It prints only the answer, so a script can branch on it:

Terminal window
if [ "$(tori ask "Deploy to staging?" --option yes --option no)" = "yes" ]; then
./deploy.sh staging
fi

If the wait runs out it prints the question’s id and exits 2. A wait lasts an hour at most, whatever --timeout says. tori ask --wait <id> collects the answer later, and tori ask --answer <id> <text> answers one on your behalf from another shell.

From a terminal tab or a shell outside Tori, tori ask takes --approval only, and the card draws in the tab or in a dialog. See Approvals asked outside a chat.

Pull requests

tori pr create, tori pr review and tori pr merge each take the exact commit they act on as --head-sha. pr create pushes that commit to the head branch, never forced, and then opens the pull request; it is refused if the local branch has moved past that commit. A review or a merge is refused the same way if the pull request’s head is no longer the one named.

From a background session these three need --approval <id>: the id of an approval you gave for exactly this draft. See Autopilot.

From a terminal tab or a shell outside Tori they need your Approve too, but tori asks for it itself: it prints tori: waiting for approval in Tori with what it would do, the card shows in the tab or in a dialog, and the command goes on once you click Approve or exits with rejected in Tori. tori attach, and tori spawn --mode with a mode that runs tools without asking, wait the same way.

Autopilot

tori autopilot state prints the queue. start and stop turn the autopilot on and off. item adds or updates a queue item, project sets a project’s contract, and hold resolve withdraws a pending approval card (it never approves one; only you do, on the card).

schedule add makes a schedule: at each --cron time in your local time, a ship item for the autopilot with --prompt as its work, or --issue to work on that issue. schedule list prints each schedule with its next time, remove deletes one, and resume starts one that three failed runs paused. Only a person can resume a schedule.

tori loop --prompt <text> <entry>... queues one ship item per entry, each with the prompt as its task: {item} in the prompt is replaced by the entry, or the entry is appended when there is no {item}. With no entries it reads one per line from stdin, so a list pipes in:

Terminal window
$ ls packages | tori loop --prompt "Move {item} to the new logger"
queued item-3f1c8e20d5a94b7c api
queued item-9a0e47b1c2d6f853 web
skipped cli (already open)

An entry whose item is still open in the project is skipped, so running the same loop twice queues nothing new. A loop never starts work itself: the autopilot picks the items up, each in its own worktree, up to its worker limit. With the autopilot off they wait, and tori loop says so. See Loops.

project --issues takes a JSON list of issue sources and replaces the project’s list. Each source has a repo and optional labels, exclude_labels, milestone, assignee and extra, and its repo has to be one of the project’s issue repos. Pass [] to go back to the issues assigned to you in those repos. A contract printed by project shows each source as the search it runs.

What a caller may do

Not every caller gets every command. A session another session spawned (a worker) can ask you questions and run tori check, but cannot spawn, steer, wait, hand off or queue a loop. No agent session, worker or not, can turn the autopilot on or off: that takes a person at a shell or on a paired phone. A paired phone can read, steer, interrupt, answer and approve, and start a plain chat in a folder, but cannot pair another device. A chat can steer or read only the chats it is related to. The full table is on the MCP server page, since both fronts share it.