CLI daily workflow
Every orboto CLI command, grouped by task, with realistic examples - the ticket loop, sessions, bulk ops, and generic API access.
This page assumes the CLI is installed and configured.
Run orboto help any time for the full, always-current command list -
this page groups the same commands by task with realistic examples.
For multiple accounts or instances, choose a connection profile
before starting work: orboto --profile codex whoami. Keep the same profile
for the whole work session, including its timer and inbox.
Every command supports the same operations as the agent skill - instructions written for one apply to the other.
Orientation
orboto whoami # your identity, roles, and permissions
orboto primer ACME # project conventions: stack, commands, gotchas
orboto rules # the workspace's binding agent rulessession-start (below) is the recommended way to begin a work session -
it bundles identity, rules, and your in-progress work into one call.
The ticket loop
Reading a ticket
orboto ticket ACME-42By default this prints a compact, human-readable card - not raw JSON - built from the same data the web UI shows:
ACME-42 [bug/high] In Progress
Title: fix(auth): session expires early
Assignees: Jane Doe
Milestone: Sprint 5
Branch: acme-42-fix-auth-session-expires-early
Comments: 3 (orboto ticket comments ACME-42)
URL: https://acme.orbo.to/projects/acme?ticket=acme-42
## Problem
...Added in v0.219.1 The card is followed by the full description as
text, so an agent reads a ticket with one call and no --full; the fields
are the ones a reader acts on (type, priority, status, parent, milestone,
assignees, labels, due date, branch, open blockers, checklist progress,
comment count, link) and never internal ids, avatars or duplicate-review
bookkeeping. Add --full to get the complete, raw JSON response instead -
for scripts that need every field, or for piping into jq:
orboto ticket ACME-42 --full | jq '.description'Listing tickets
orboto ticket list ACME # open tickets, one line each
orboto ticket list ACME --status todo --limit 20
orboto ticket list ACME --status all --json # every category, as the JSON row array
orboto ticket mine --project ACMEAdded in v0.219.1 Both commands print one line per ticket
(KEY status priority title @assignees) and a count line, and show
only open tickets (todo, in_progress, in_review) unless --status
says otherwise: --status all adds done and wont_fix, a category
filters that one. --json prints the row array (key, title, status,
priority, assignees) that earlier releases printed by default; scripts
that parsed the old output add the flag. ticket list requires a
project key and defaults to --limit 50; pass --all to remove the cap
entirely (it walks every page of the cursor internally, so a very large
project may take a moment). --status filters on the workflow category,
not a specific per-project status name - use
query with OQL's statusName field if
you need to filter on a custom status name instead. --parent <key>
narrows to one ticket's direct children, which is the fast way to see
everything filed under an epic.
ticket mine is the same idea scoped to your own assignments; --project
is optional there - omit it to see your tickets across every project
you're a member of.
Creating a ticket
orboto ticket create ACME "fix(auth): session expires early" \
--type bug --priority high --label backend --label auth \
--assign jane@example.com --milestone "Sprint 5" --due 2026-09-15Every flag ticket create accepts:
| Flag | Effect |
|---|---|
--type | task / bug / story / epic. |
--priority | blocker / high / normal / low / trivial. |
--milestone | Milestone by name or key. |
--parent | Parent ticket key/UUID - files this ticket as a child. |
--label | Repeatable - pass it once per label. |
--assign | Repeatable - one or more emails to assign immediately. |
--due | ISO date (YYYY-MM-DD). |
--private | Marks the ticket private on creation. |
--description "..." | Inline description text. |
--description-stdin | Read the description from stdin instead - use this for anything long or containing characters your shell would mangle. |
--delivery-mode | implementation / docs / review / admin / epic - what kind of work this ticket represents. |
--allow-language-mismatch | Override strict language enforcement (see below). |
--allow-duplicate + --duplicate-justification "..." | Override the duplicate block (see below). |
--quiet | Added in v0.202.0 Print only the new ticket key on stdout - for scripts. |
Added in v0.202.0 A successful create always ends with one line on stderr: ✓ Created ACME-42 - <link>. When similar tickets were found the line adds "created despite the warnings above - do not re-run": a warning is never a refusal. A refused create exits with code 1 and says "Blocked - nothing was created".
Two guardrails can block a create outright, and both print a clear recipe for getting past them intentionally rather than a bare error:
If the workspace enforces one language and your title/description reads as a different one:
⛔ Blocked - strict ticket-language enforcement is on.
This content reads as "en" but the workspace language is "de".
Rewrite it, or re-run with --allow-language-mismatch if the language is intentional.If the content looks like an existing ticket:
⛔ Blocked - nothing was created. This looks like a duplicate, decided by an AI embedding match on open ticket ACME-38. Existing tickets it overlaps with:
- ACME-38 (96% AI match): session cookie expires before the configured TTL
Extend one of these instead, or re-run with --allow-duplicate --duplicate-justification "why".Added in v0.203.0 Only an AI embedding match on an open ticket can refuse a create. A full-text candidate is listed as "text match" without a percentage and never blocks, and a match on a closed ticket stays a warning. How an admin picks the threshold is on Duplicate block.
Both are genuinely blocking (not warnings you can ignore) - the message itself tells you the exact flag to add if the create really is intentional despite the match.
Claiming, commenting, moving, and closing
orboto ticket claim ACME-42claim does three things in one call - assigns you to the ticket, moves
it to in_progress if it isn't already there, and starts your timer -
and prints what it actually did:
{
"key": "ACME-42",
"status": "In Progress",
"timerStarted": true
}Flags: --sole unassigns everyone else first (a genuine take-over, not
just adding yourself); --force allows claiming a ticket that's
currently done (without it, claiming a done ticket is refused outright
- reopening is a deliberate action, not a side effect);
--no-timerassigns and moves the ticket without touching the timer.
Added in v0.230.0 The timer starts only when you hold time:log in the
ticket's project. Without it, claim still assigns you and moves the ticket,
and reports "timerStarted": false; the plain-text line ends with
timer not started, and the skill wrapper adds the warning
no time:log on this project - claimed without a timer. orboto timer start ACME-42
without time:log answers 403 (errors.time.log_permission_required), and a
ticket you cannot open answers 404. If you lose time:log or access to the ticket
while a timer runs, orboto timer stop prints "bookingRefused": true with
"durationMinutes": 0 - the timer is gone and nothing was booked (see
Who can book time).
orboto ticket comment ACME-42 "found it - session TTL was set in minutes, not seconds"
orboto ticket move ACME-42 in_review
orboto ticket close ACME-42 --comment "fixed in the last commit, verified by the test suite"move <key> <category> moves between the four workflow categories -
todo / in_progress / in_review / done - and, like the UI, only
succeeds if that transition is actually allowed by the project's
configured workflow; an unreachable transition is rejected, not silently
adjusted to the nearest valid one. close is move ... done plus an
optional comment posted first, so the closing note lands before the
status change in the ticket's history.
Added in v0.203.0 Move a ticket by the status you see on the board. orboto ticket move ACME-42 "In Review" works just like orboto ticket move ACME-42 in_review; upper and lower case do not matter. If the name does not exist, orboto lists the project's statuses instead of guessing.
Reading and writing comments
Added in v0.203.0 orboto ticket comments ACME-42 prints the thread with the author, the time in your own date format and the body. A comment that external users never see is marked [internal]. Decisions usually live in comments - an agent that picks up a ticket reads them before it asks.
Added in v0.219.1 The thread comes newest first. Every comment is listed with a header line (date, author, length, id); the three newest print in full, older ones are cut to their first 300 characters with a pointer to --id, so a long thread stays readable without losing the older decisions. --all prints every comment in full, --id <comment id> one comment (an 8-character prefix is enough when it is unique), --limit N caps the list.
orboto ticket comments ACME-42 # newest first, the 3 newest in full, older ones shortened
orboto ticket comments ACME-42 --all # every comment in full
orboto ticket comments ACME-42 --id 1f3c9a2b # one comment, by id prefix
orboto ticket comments ACME-42 --since 2026-09-20 # only what is newer
orboto ticket comments ACME-42 --json | jq length # the raw rows as one JSON arrayA long comment no longer has to fit on the command line. Next to the plain text, comment takes its body from a file or from standard input, so tables, code fences and lines starting with a dash arrive exactly as written:
orboto ticket comment ACME-42 --body-file report.md
orboto ticket comment ACME-42 --body-stdin < report.md
orboto ticket comment ACME-42 --body-file report.md --attach screenshot.png:"the failing dialog" --quiet--attach <path>[:alt] uploads the file and attaches it to the same comment in one step, and can be repeated; the file is only ever visible as part of that comment. --quiet prints only the new comment's id, which is what a script wants.
For your own scripts: GET /tickets/{ticketId}/comments accepts the ticket key (ACME-42, case does not matter) as well as the internal id - for reading, writing, editing and deleting alike. Nothing was removed; what you already send keeps working.
Before creating a ticket that might already exist, check explicitly:
orboto ticket check-similar ACME "session expires early"
orboto search acme --types p,m # projects and milestones by key or name (0.204.0)This runs the same similarity check ticket create runs automatically -
useful to check before you've even written a full description.
Added in v0.203.0 orboto search "session expires" --json prints the hits as one JSON array - type, key, title, excerpt, score and project - so a script can look for an existing ticket before it creates one. And orboto ticket create --help now shows the two things you have to pass, the key and the title, and every value --type, --priority and --delivery-mode accept.
Link a dependency (the second ticket blocks the first):
orboto ticket deps ACME-42 ACME-40Bulk operations
Apply one change to many tickets instead of looping:
orboto ticket bulk-close ACME-10,ACME-11,ACME-12 --comment "superseded by ACME-42"orboto ticket bulk-create ACME --from=@drafts.json --milestone "Sprint 5"drafts.json is a JSON array of draft objects (at minimum a title
per draft; the same optional fields ticket create takes apply per
draft). On completion it prints a summary, not a per-row echo:
{
"created": ["ACME-51", "ACME-52", "ACME-53"],
"failed": 0,
"duplicateFlagged": 1
}Each failed row prints its own line to stderr (✗ draft 2 "some title": <error>)
without stopping the rest of the batch - a bad row is isolated, not
fatal to the whole run. duplicateFlagged counts drafts that matched an
existing ticket closely enough to warn about (also echoed to stderr per
row: ⚠ ACME-53 may be a duplicate - review before treating as new work)
without blocking the create - unlike ticket create's single-item
duplicate guard, a bulk create doesn't stop for you to decide, since
there's no one there to answer the question; it flags for a later
review pass instead. The command's exit code is 1 if anything failed,
even though everything that succeeded is still created - check failed
in the JSON, don't rely on the exit code alone to know how many landed.
orboto ticket bulk-deps --from=@blocks.txt # lines "ACME-42 ACME-40" = 42 blocked by 40Accepts either the plain-text "A B"-per-line form shown above, or a
JSON array of [a, b] pairs - useful when the pairs come from another
command's output. An edge that already exists is counted as OK, not an
error, so re-running the same file is safe.
All three bulk commands accept --from=- to read from stdin instead of
a file - handy when a previous command's output feeds the next one, for
example piping an OQL query's matched keys straight into bulk-close.
Milestones, docs, and search
orboto milestone list ACME
orboto milestone create ACME "Sprint 5"
orboto milestone close ACME "Sprint 5"
orboto search "login timeout" --types ticket --project ACME
orboto query "assignee = currentUser() AND statusCategory != done" --limit 25
orboto doc get ACME-D3Added in v0.211.1 doc get returns the page's webUrl as the short link
/spaces/<SPACE-KEY>/docs/<DOC-KEY>, for example /spaces/ACME-S1/docs/ACME-D3.
A doc or space without a key keeps its UUID in that part of the link. Post the
link exactly as returned.
Writing docs from the shell
Added in v0.221.11 The doc group reads and writes pages without a UI:
orboto doc list ACME-S1
orboto doc get ACME-D12 --content
orboto doc edit ACME-D12 --old "port 3000" --new "port 3100"
orboto doc edit ACME-D12 --section "Setup > Credentials" --content-stdin < creds.md
orboto doc edit ACME-D12 --title "Deploy runbook v2" --file notes.md
orboto doc append ACME-D12 --section "Changelog" --file entry.md
orboto doc edit ACME-D12 --replace --file runbook.md
orboto doc restore ACME-D12 <revisionId>Content comes from exactly one of --content "...", --content-stdin or
--file <path>. A write never replaces a page by accident: doc edit with
content and without --old or --section appends the content as a section
and keeps the page. Only --replace writes a new whole body; it prints the
revision that holds the previous body and the command to roll back,
orboto doc restore <doc> <revisionId>. A passage that matches nothing or
several times, a stale --base <revisionId> or a heading path that matches no
single section exits 1 with the next step. doc append without --section is
a no-op for content already on the page.
The API enforces the same rule for every client: a PATCH /docs/:id whose
content drops more than half of the page without replace: true is refused with
409 errors.docs.replace_drops_content. The MCP tool orboto_update_doc
appends unless replace: true.
query runs OQL directly - the same language the
in-app search palette and the MCP orboto_query tool use. Add
--syntax jql to paste JQL instead.
Changing a project key
Added in v0.230.10 Needs permission to edit the project.
orboto project change-key PROJ NEW --rewrite-existing-tickets --dry-run
orboto project change-key PROJ NEW --rewrite-existing-tickets --confirm-project-key PROJThe dry run changes nothing and prints what the change touches: ticket, doc, milestone and space keys, the smart-link tokens naming the old prefix (and in how many docs, tickets and comments), the commits, branches and pull requests naming PROJ-N, and the aliases it writes.
The real run needs the current key typed back as --confirm-project-key PROJ. Without it the command prints the dry run and exits with code 1; a different key is refused (exit 1, errors.projects.change_key_confirmation_mismatch), also when someone changed the key in the meantime. Nothing changes in either case.
With --rewrite-existing-tickets, every old ticket key keeps resolving as an alias: orboto ticket PROJ-12, search, smart links and commit, branch and pull request references find the ticket under NEW-12. Without the flag only the project key changes and every existing key stays. --json prints the API answer.
The REST call is POST /projects/<id>/change-key with key, rewriteExistingTickets, dryRun and confirmProjectKey. A refused call without the confirmation answers 409 with the dry-run counts and the expected key in errorParams. GET /projects/<id>/change-key/check?key=NEW answers only whether a key is free. MCP clients reach it through orboto_api_call; the in-app chat does not change keys.
Sessions and work routing
Sessions are how an agent claims and hands back work with an atomic lease rather than a plain assign - read that page for the full model (what a lease guarantees, the four "nothing ready" reasons, pausing). This section is the command-by-command mechanics.
orboto session-start --project ACME # orientation digest: rules, your work, timerAdded in v0.200.0 A session also declares what it is responsible for, so
that several sessions under one account stop waking each other. Pass the scope
on session-start, or change it on a heartbeat while the session runs:
orboto session-start --project ACME --role integrator --scope-projects ACME
orboto agent-heartbeat --role review --scope-projects ACME,BETA --scope-tickets ACME-42
orboto agent-heartbeat --clear-scope--role is one of integrator, lead, worker, spec, review,
coordinator and is descriptive; only --scope-projects and --scope-tickets
filter what the inbox, the live stream and the wake channel deliver. The full
model is Sessions, scope and the wake ledger.
Added in v0.230.0 Several sessions of one account run in one checkout when
their scopes share no project, no ticket, no ticket inside the other's project and
no keyless control role. A session-start whose scope overlaps with a live session
of the checkout, or that declares none, is refused with the overlapping keys and
every overlapping session id. Run it again with a scope that shares none of them
to run both side by side, or with --adopt (their scope, unread mail and leases
move to this session and they end) or --end-previous (they end and their leases
are released). Details: Session origin and state.
session-start is the recommended way to begin any work session - one
call bundles your identity, the workspace's current agent rules (or a
compact "unchanged" acknowledgment if you already have them - see
Rules delivery), your
in-progress tickets, and your timer state. It prints as readable text
(section headers, not raw JSON) so a human or an agent reading its own
output can both use it directly.
orboto work-next ACMEwork-next pulls the best available ticket in a project and reserves it
under a lease. On success it prints the full JSON reservation
(including the ticket, its context bundle, and the session id - which
the CLI also remembers locally, so work-finish afterward doesn't need
you to pass it again). Add --role review to pull from the review lane
instead of open work, and --agent-tag <tag> to prefer
agent:<tag>-labeled tickets (see
Work routing).
When there's nothing to pull, work-next exits with code 3 (never an
error code) and explains exactly why on stderr:
Nothing ready (all-leased). Candidates considered: 4. Retry in ~90s (earliest known free: 2026-08-30T14:05:00Z).or, if a pause switch is on:
Autonomous work is PAUSED for this agent (operator decision, per-bot or workspace-wide). Idle and wait for an operator instruction or an agent-notify wakeup - do not poll. Explicitly named work-start still works.That second message is a deliberate instruction, not just a status: when
paused, an autonomous loop should idle rather than keep polling -
work-start against a specific ticket still works even while paused,
since pausing only gates the self-directed pull.
orboto work-start ACME-42 --role implementationwork-start leases one specific ticket instead of pulling whatever's
best. Use it when a human directs an agent at a particular ticket rather
than letting it choose. --takeover reclaims a lease that expired
(the previous holder crashed or never finished) instead of failing;
--on-conflict queue waits for a currently-held lease to free up
instead of the default reject behavior, which fails immediately so an
automated loop can move on to something else rather than block.
orboto work-finish --commit abc1234 --status done --tests --notes "added regression test"work-finish releases the lease and records the evidence that closes
the loop. Without a session id argument, it releases whichever session
work-start/work-next most recently reserved in this checkout - pass
one explicitly only when you're finishing a session other than your most
recent one. --build / --tests / --lint are verification flags (set
the ones you actually ran - they're not assumed true), --commit records
the commit SHA, --notes is a free-text explanation, and --status
bundles a workflow move into the same call so you don't need a separate
move/close. Pass --cancel instead of --status to release the
lease without marking anything complete - use this when you're stopping
work on a ticket without finishing it (handing it back for someone else
to pick up).
orboto work-sessions --ticket ACME-42 # who (if anyone) currently holds this ticket
orboto work-sessions --mine # every session you currently hold
orboto work-sessions --mine --include-closed # ...plus your finished/cancelled historyConnecting an AI client
Added in v0.205.1 Since 0.205.1 orboto connect and orboto mcp install write a channel-only connection: the MCP server carries the wake channel and no tools, and the agent works through the commands on this page. --toolset minimal|curated|full brings the tools back for a client that needs them; orboto mcp status shows the toolset and what it costs per message.
Added in v0.200.0 orboto connect is the whole first setup of one directory
in a single command. It checks Node and Claude Code, confirms who you are signed
in as, writes the orboto server entry, offers the context-size status line,
and prints the exact start command - one step at a time, saying what it found
after each one.
orboto connect # the guided walk
orboto connect --yes # every question answered with its default - the form an agent runs
orboto connect --replace # overwrite an "orboto" entry that is already there
orboto connect --verify # after the restart: probe the session, report how the probe arrived
orboto connect --verify --json # the same as a recordAdded in v0.211.0 orboto connect now ends with a checklist (one line per item, pass or FAIL, the fix under a failing line) and exits 0 only when every line passes; --json prints it as { directory, pass, items }. It asks for the project when none resolves, and --project KEY answers without a terminal (--yes without it stops with the list, exit 2). Added in v0.213.0 The question is a picker that filters by typing (a server-side search over key, name, description and customer), pages 20 projects at a time, shows a project's description, members and tickets with ?<number or key>, and creates a missing project with new; --project new:KEY[:name] creates it without a prompt (the name defaults to the key; exit 2 for an invalid or used key and for a missing project:create permission), and --project KEY for a key that does not exist offers the same in a terminal. A repository linked to several projects opens the picker on exactly those. The full description is The project picker. --identity bot|person names the account kind the folder works as. orboto doctor prints the same checklist, orboto doctor --all runs it for every folder you started a session in under that folder's own login, and orboto doctor --fix repairs what it can. See Connect your agent.
--statusline / --no-statusline, --scope project|user, --toolset minimal|curated|full and --profile decide the rest without a prompt, so
nothing waits for input under --yes. --verify also takes --session with a
session's short id when several are running, and --timeout. It exits 1 with
the cause on stderr and 0 on a verified channel.
The step-by-step guide for people is Connect your agent.
The local MCP proxy
The CLI installs, versions and starts the MCP proxy that any AI client spawns - no repository checkout and no shell function:
orboto mcp install --profile work # ./.mcp.json (project scope)
orboto mcp install --profile work --scope user # the client's user-wide config
orboto mcp install --profile work --toolset full --replace
orboto mcp install --statusline # also report this session's context size
orboto mcp status --json # instance version, cached proxies, the entry here
orboto mcp uninstall [--scope project|user]
orboto mcp serve --profile work # the stdio entry point a client spawnsmcp install writes the entry under the server name orboto and keeps every
other server, unknown key and the key order of the file; an existing orboto
entry is only overwritten with --replace, and the command prints what it
replaced. The entry never contains a credential.
mcp serve reads the instance version and runs @orboto/mcp at exactly that
version under ~/.orboto/mcp/, so the tool schemas always match the API. The
three newest versions are kept. ORBOTO_NPM_REGISTRY, ORBOTO_NODE_BIN,
ORBOTO_NPM_BIN and ORBOTO_CLAUDE_BIN override the registry and the external
binaries.
Starting Claude Code with the wake channel
orboto claude # new session, wake channel on
orboto claude --continue # continue the most recent conversation here
orboto claude --resume <session id> # a specific conversation
orboto claude --yolo # Claude Code's full-permission mode (--skip and --skip-permissions are the same)
orboto claude --role coordinator # a control session: no project, works across projects
orboto claude -- --print "status?" # everything after -- reaches claude untouchedAdded in v0.200.0 The command refuses to start, naming the fix, when the
current directory has no CLI-form orboto entry. Added in v0.211.0 It also
refuses a folder without a project: it asks in a terminal, and otherwise prints
the checklist and exits 2 unless you pass --no-scope or --role.
--strict-identity refuses to start when a claude.ai connector would give the
session a second identity. Claude Code asks on every such
start to confirm the local-development use of the channel.
Added in v0.213.0 orboto claude --role coordinator starts a control session without a project, in any directory: it declares coordinator with no project list, is reached as coordinator@KEY on every project your account is a member of, and does not take the checkout's project unless --scope-projects names one. coordinator is the only control role; lead stays a project role. The picker's 0 / none choice remembers the role for the folder as ORBOTO_SESSION_ROLE in .claude/settings.local.json; the same variable in the environment is read first, and flags override both. What a control session receives and how it hands work to project agents - a worker session in the project checkout, orboto agent-notify and ticket assignment; and orboto agent start for a supervised project agent in another checkout - is on Sessions, scope and the wake ledger.
Every launcher takes the same flags. Added in v0.213.0 orboto claude, codex, gemini, kimi and ollama translate --yolo (or --skip), --continue and --resume <id> into the harness's own form, read them before a bare -- and pass everything after it untouched:
orboto codex --yolo --continue # codex --dangerously-bypass-approvals-and-sandbox resume --last
orboto gemini --resume <session id> # gemini --resume <session id>
orboto kimi --yolo # kimi --auto
orboto ollama --yolo # refused, exit 2: Ollama has no permission modeThe per-harness forms, what each launcher provides and what it does not yet (the thin launchers for Codex, Gemini CLI and Kimi have no project gate, no scope declaration, no orboto agent session and no wake channel) are on Launchers and the uniform flags.
Login profiles keep several accounts of one harness on the machine:
orboto claude accounts add work --login # sign in, then capture that account
orboto claude accounts login work # sign the profile in again
orboto claude accounts list # identity, plan, limit, 5-hour and 7-day quota and the live login
orboto claude accounts disable second # hold a profile out of every switch (enable puts it back)
orboto claude accounts config --threshold 85 --strategy most-quota # stored proactive switch settings
orboto claude accounts rotate --once --json # cron: switch the active profile once; exit 0, 1, 2 or 3
orboto claude accounts add second --login --parallel # a thin second config dir, two accounts at once
orboto codex accounts add work --login # Codex and Kimi the same; Gemini CLI has no login stepAdded in v0.213.0 Every profile records and shows its account; a capture of an account another profile already holds is refused without --force, and accounts refresh runs the same guard. See Login profiles.
Added in v0.215.0 accounts list shows the quota of every profile (--cached for the last stored read), accounts disable|enable holds a profile out of rotation, accounts config stores the switch threshold and strategy, and accounts rotate [--once] [--json] runs the proactive switch from cron (exit 0 switched or nothing to do, 1 error, 2 usage error, 3 no viable target). orboto claude --on-limit switch:a,b switches before the limit; see Usage and proactive switching.
Added in v0.230.0 Switch subscriptions on and off, also while sessions run. Automatic switching has a stored on/off for new starts, and the policy of a running session changes without a restart:
orboto claude accounts config --auto-switch off # new starts without --on-limit do not switch
orboto claude accounts config --auto-switch on # back to the default: switch over every profile
orboto claude switching status # every supervised session on this machine: its policy and who set it
orboto claude switching off --all # running sessions stop switching, no restart
orboto claude switching on --session 1a2b3c4d # switch over every profile again
orboto claude switching switch:work,second --threshold 85 --strategy order --session 1a2b3c4d
orboto claude switching stop --resume --all # stop at the limit, continue at the reset
orboto live switching 0c4f6a10 off # the same for a session on any machineon switches over every login profile: a profile you add while sessions run joins their rotation at the next decision, and one you disable with accounts disable leaves every rotation at once. An explicit switch:<names> keeps its list. --threshold and --strategy tune a switch, --resume lets ask and stop continue at the reset. A change needs --session or --all once more than one session runs. Every session answers applied, refused, superseded (a newer request replaced yours), pending (no answer within 6 seconds) or older-cli (a session started with an orboto older than 0.230.0); the newest request wins. Exit code 0 when every session applied the change, 1 for any other outcome, 2 for a usage error. orboto claude --sessions and the status line show each session's policy, and Admin -> Agents -> Live shows and changes it under At the usage limit. The values, the outcomes, what a change withdraws and the remote input set_limit_policy are on Switching on and off.
Standing arguments keep the commands an agent hands over short and correct:
orboto claude --set-default-args -- --dangerously-skip-permissions
orboto claude --show-default-args
orboto claude --clear-default-args
orboto claude --no-default-args # start once without themThey live in ~/.orboto/claude.json; ORBOTO_CLAUDE_ARGS overrides them for one
shell. Every start names them on stderr and orboto mcp status lists them, so a
permission-free start is never silent.
The status line:
orboto claude-statusline # draws the status line from the data on stdin
orboto claude-statusline --preview # shows it in your terminal without Claude Code
orboto claude-statusline --fields model,context,dir,branch # fewer fields; --fields default shows all
orboto claude-statusline --wrap "<command>" # your existing status line keeps its outputAdded in v0.204.0 orboto claude-statusline draws the whole Claude Code status line itself, on macOS, Linux and Windows, with nothing else to install: model, how full the context is, folder and git branch (a * when there are uncommitted changes), session time, effort level, the ticket you are working on, and your 5-hour and weekly usage with their reset times. Added in v0.230.0 The ticket is the one the session in whose terminal the line is drawn works on, so two sessions in one checkout each show their own; a process that belongs to no session shows none instead of a marker another process left on the machine.
Fable 5.1 │ ✍️ 51% │ orboto (develop) │ ⏱ 1h51m │ ● high │ ACME-42
current ●●●○○○○○○○ 36% ⟳ 12:00
weekly ●●●●●●○○○○ 67% ⟳ sep 28, 8:00orboto mcp install --session-reporting sets it up; if you already have your own status line, orboto keeps it and shows it unchanged. Use --plain if colours look wrong, or --no-color for none. The status line never goes online and never reads another program's passwords or tokens; if Claude Code does not send usage figures, the usage lines are simply not shown.
Added in v0.200.0 Whichever line is shown, the command records the session's context size under
~/.orboto/claude-context/ and the proxy reports it with its heartbeat, so the
wake ledger can say what a wrong wake cost.
The full walkthrough is Claude Code with orboto.
Start a project agent: agent start
Added in v0.213.1 A control session, or any shell, starts a supervised project agent in another checkout without a terminal:
orboto agent start ACME --ticket ACME-1234 --yolo # Claude Code worker on the ACME checkout, the ticket is its start prompt
orboto agent start ~/src/my-project --harness codex --prompt "Review the open PRs"
orboto agent list [--json] [--prune] # the starts of this machine with their state
orboto agent stop 3f9a1c2e [--wait 30s] # session id, live session id, start id or pidThe session appears under Admin -> Agents -> Live, and the starting session receives a copy of its complete mail. Refusals (checkout not wired, harness not installed, the login cannot see the project, --ticket of another project, --prompt next to --ticket, any prompt for kimi) exit with code 2; a start that registers no session within 60 seconds exits with code 1. All flags and the rules are on Start a project agent from a control session.
Supervising a headless coding agent
orboto pi-runner --project ACME --session-name my-agent \
--bootstrap "Run orboto session-start, then work tickets via the orboto CLI." \
-- --provider anthropic --model "*sonnet*"Runs a headless coding-agent session and turns your orboto agent inbox into live wakeups for it: message the bot's identity with a project scope, and the running session receives it as a prompt (or a mid-turn steering message) - no MCP server needed, the session talks to orboto through this same binary.
Live sessions
Added in v0.210.0 Every session a runner starts (pi-runner, agent-runner,
codex-runner) streams itself to Admin -> Agents -> Live, where operators
read the transcript, answer the agent's questions and type into the session.
ORBOTO_LIVE_SESSION=off switches the stream off for one runner. What operators
can do with a session is on Watch and steer live agent sessions;
how the adapters work is on Publish an agent session live.
Publish a session: live-session
Added in v0.210.0 For a session no runner owns, start the stream yourself:
orboto live-session pty --tmux agent --log /var/log/agent/session.log # any program in a tmux pane
orboto live-session run --harness claude_code -- claude --name "review" # Claude Code through stream-json
orboto live-session run --harness codex # codex app-server over stdio
orboto live-session run --harness pi # pi --mode rpc
orboto live-session login --harness claude_code # a headless login: the URL goes to the operator, the code comes back
orboto live-session login --harness codex # codex login --device-auth: URL and one-time code| Subcommand | Flags | Effect |
|---|---|---|
pty | --tmux <target> (required), --log <file> | Streams a tmux pane: its output as text, operator messages and keystrokes typed into it. Without --log the pane is piped into ~/.orboto/live/; with it, the file the pane already pipes into is read. Ends when the pane is gone. |
run | --harness claude_code|codex|pi, optional -- <command...> | Starts the harness and streams it through its adapter. Without a command it starts claude, codex app-server --listen stdio:// or pi --mode rpc. |
login | --harness claude_code|codex, --bin <path> | Runs only the harness login, headless, and hands the login URL (and for Codex the one-time code) to the operators as a login question. |
Common flags: --label <text> (how operators see the session), --project <KEY>,
--ticket <KEY> (who may watch it), --external-id <id> (a restarted stream
resumes the same session).
Reply to a session and open its pull request
Added in v0.212.0 orboto live is the command line of the
Remote console. Every command takes a session by its
full id or the prefix orboto session list prints and prints the answer of
orboto with --json:
orboto live transcript 0c4f6a10 --follow # the conversation: messages, tool groups, turn ends, usage, context
orboto live transcript 0c4f6a10 --after-seq 120 # only the lines after a sequence number (--poll checks every two seconds)
orboto live say 0c4f6a10 "run the tests" # type one line into the session, as the message field does
orboto live say 0c4f6a10 /compact keep the plan # a slash command of the agent tool
orboto live answer 0c4f6a10 toolu_01 allow # allow, allow-always or deny an open tool approval
orboto live diff 0c4f6a10 # the change of its checkout against the base branch, and the pull request state
orboto live pr 0c4f6a10 # open the pull request of its branchsay and answer need the rights to steer the session; say takes your
takeover first and refuses with the holder's name when someone else has it.
answer finds the ask by its id, by at least four characters of it, or by the
question id the transcript shows. pr also needs edit access to the project and
a repository connected to it; it puts the ticket key in the title and links the
pull request to the ticket.
Added in v0.230.0 orboto live switching 0c4f6a10 on|off|ask|stop|switch:<names>|status changes, or with status reads, what the session does at its usage limit, without a restart; see Switching on and off.
Watch and steer a session from the terminal
Added in v0.210.0 orboto session is the terminal side of Admin -> Agents -> Live:
orboto session list # live sessions you may see
orboto session list --all --json # live and ended, as JSON (--ended for ended only, --limit N)
orboto session attach 0c4f6a10 # a session with a terminal: its screen, like tmux attach; otherwise the transcript
orboto session attach 0c4f6a10 --transcript # the transcript view: open questions, typed input
orboto session attach 0c4f6a10 --take # hold the takeover until you detach
orboto session attach 0c4f6a10 --keys # raw keystrokes into a terminal session, Ctrl-] detaches
orboto session release 0c4f6a10 # give your takeover back
orboto session revoke 0c4f6a10 --reason "operator left" # end whoever holds it (admin:agents:write)
orboto session grant 0c4f6a10 --user alex@example.org [--operate] # share a session (0.210.1)
orboto session grants 0c4f6a10 # who it is shared with
orboto session revoke 0c4f6a10 --user alex@example.org # take a share back
orboto session list --shared # sessions shared with youlist shows the id, account, lane, ticket (or project), agent tool, host, state
with the number of open questions, and who holds the takeover. attach,
release and revoke take the full id or the prefix list prints.
Added in v0.211.0 On a session that runs in a terminal (see
Watch a session as its terminal),
attach shows that terminal in yours: the alternate screen and raw mode, the
current screen first and then the live stream, read-only unless you add
--take. Ctrl-] detaches and restores your terminal, and the session's
status goes to the window title. When the session's terminal is resized, the
title shows the new size and the picture is redrawn. --transcript keeps the
transcript view described below.
attach in transcript mode prints the transcript from its first line, then follows the session
live over the agent WebSocket; without the socket, or with --poll, it checks
every two seconds. Tool calls render as ->, tool results as <-. An open
question shows numbered answers with the option key in brackets:
| The agent asks | Answer with |
|---|---|
| a question | a number or key; several separated by commas where more than one is allowed; your own text where the question allows it |
| a tool approval | a number or key, or y (allow once), a (allow always), n (deny) |
| a usage limit | a number or key, or w (wait), s (stop) |
| a login | the code the login page shows (the echo is off), or ok once a device-code login is done |
Any other line goes to the agent as a message. Commands start with a colon:
| Command | Effect |
|---|---|
:q | Detach |
:take / :release | Take over / give back |
:text <message> | Send a message while a question is open |
:keys <keys> | Send keystrokes; escapes \r \n \t \e \\ \xHH |
:switch <profile> | Switch the agent to another login profile (only where the runner has profiles) |
:questions / :refresh | Show the open questions again / reload the session |
:help | List the commands |
A line starting with :: sends the rest with one colon. A question someone else
answered first is reported, and the questions still open are shown again.
Typing takes or renews the takeover; while another operator holds it you get
their name. The takeover this attach acquired, by --take or by typing, is
given back when you detach (:q, end of input, Ctrl-C); a takeover you already
held on another device is renewed, never given back. When you lose access to the
session (you left the project, the ticket turned private) attach says so and
ends. --keys needs a session that accepts keystrokes (the terminal fallback)
and a Unix terminal.
What you type into a credential prompt - a login code, or the line after a
password, passphrase, PIN, one-time code or token prompt - is read with the echo
off, taken as data even when it starts with : (only an exact :q still
detaches), and shown as ***** in everything attach prints and in its journal
~/.orboto/live/attach-<id>.jsonl (--journal <file> puts it elsewhere).
Values shorter than four characters are not masked.
Run any command as a live terminal: live-session terminal
Added in v0.211.0 orboto claude, orboto codex, orboto gemini and orboto kimi
started in a terminal stream that terminal to the Live panel automatically. For
any other program:
orboto live-session terminal -- htop # any command in a PTY of its own: its screen streams
orboto live-session terminal --cols 160 --rows 48 -- ./long-job.sh # headless: the person who took over may resize itThe command runs in your terminal as usual and viewers see the same screen.
ORBOTO_TERMINAL_CAPTURE=off starts the harnesses without the capture, so they
share your terminal directly and the session shows no terminal.
Update the agent harness: update
Added in v0.210.1 orboto claude|codex|gemini|kimi|ollama update updates the
coding harness through its own installer and checks the result. The page
Update Claude Code, Codex and the other harnesses
has the full behaviour; the flags:
orboto claude update --check # installed and available release, nothing changes
orboto claude update # install the newest release
orboto claude update --to 2.1.284 # install exactly this release
orboto codex update --restart # also restart running sessions on the old release| Exit code | Meaning |
|---|---|
| 0 | updated, or already current (--check always 0) |
| 1 | the update failed |
| 2 | not installed, or no updater known for this install |
| 3 | orboto claude update only: a session is still busy, the update runs when it is idle |
Runners take --auto-update never|on-idle|nightly[=HH:MM] to keep their harness
current between tasks.
Build capacity
Added in v0.212.0 orboto capacity claims, queues, holds and books the shared
build hosts, runners and Unity machines of your workspace. The concepts, the
priorities, the windows and the admin page are on Build capacity.
orboto capacity claim build-host:runner-1 --for 45m --priority release --reason "release candidate"
orboto capacity wait <claim-id> --timeout 30m # block until granted; exit 5 on timeout
orboto capacity claim build-host:runner-1 --for 45m --reason "e2e" --hold -- pnpm test:e2e
orboto capacity wait <claim-id> --hold -- ./deploy.sh # the same for an existing claim or reservation
orboto capacity renew <claim-id> --for 30m
orboto capacity release <claim-id>
orboto capacity plan build-host:runner-1 --for 45m # the next free slot
orboto capacity plan build-host:runner-1 --for 45m --book --priority release --reason "release window"
orboto capacity list build-host:runner-1 --table # holders, queue and windows
orboto capacity claims --mine --active # --resource, --state, --holder-kind, --holder-id
orboto capacity windows --resource unity:runner-1
orboto capacity stats --table # wait, hold, expirations, yields per resource
orboto capacity seed nightly-unity # admin: the nightly window, onceclaim prints the claim as JSON on stdout and its progress on stderr. The exit
code is 0 granted or done, 4 queued, 5 timeout, 6 denied or ended, 7
yielded and 130 or 143 interrupted; a queued or interrupted claim is
released. --hold -- <command...> renews the claim while the command runs,
releases it when the command ends and returns the command's own exit code.
orboto session-check releases the granted claims an ending turn leaves behind.
The lists take --limit and --cursor.
Agent inbox and time tracking
For the Claude Code wake channel, live delivery into an interactive Codex conversation, host output monitors, managed runners and an end-to-end receipt test, see Agent inbox and wake-up delivery.
orboto messages --project ACME # your agent inbox
orboto messages --all # the whole account, read and unread
orboto messages --ack <id>,<id> # handled - marks read
orboto agent-notify teammate@example.com "ready for review" --project ACME
orboto timer # current running timer, if any
orboto timer start ACME-42
orboto timer stopDismiss what is not yours or no longer true Added in v0.200.0 instead of leaving it lying in the inbox:
orboto messages --dismiss <id>,<id> --reason not-mine
orboto messages --dismiss <id> --reason obsolete --note "closed in ACME-42"
orboto messages --dismiss <id> --reason duplicate --duplicate-of <original id>not-mine hides the message for this session only and tells the sender once
that it was misrouted; obsolete and duplicate close it for the whole account
and need the --note or the --duplicate-of. Operators sweep unanswered mail
with orboto messages --stale [--stale-hours 48] and
orboto messages --close-stale <id>,<id> --note "...".
Address exactly one session Added in v0.200.0 instead of the whole account, using the short session id every rendered message prints:
orboto agent-notify teammate@example.com "reviewed" --session <short id>Notify every connected session Added in v0.216.0 before a host restart or a deploy, instead of one agent-notify per account:
orboto agent-broadcast live "host restart" --at +5m --ack --reason "kernel update"
orboto agent-broadcast live "deploy core" --at 14:30 --project ACME --role integrator
orboto agent-broadcast live "runner maintenance" --host build-1 --deadline +10m --ack
orboto agent-broadcast status <notice id> [--json]--at and --deadline take +5m (s, m, h, d), an ISO 8601 instant or HH:MM in your account's time zone; --deadline needs --ack, otherwise the command exits with 2. The recipients are the live sessions you may see in orboto who (an operator reaches all), your own session excluded; --project, --role and --host narrow them, and the receipt names how many matching sessions lay outside your reach. The same subject and --at within a minute exits with 1 and names the earlier notice. A notice is information, an acknowledgement is a receipt, and work goes as a request to a role address. Details, the status columns and the Admin banner are on Sessions, scope and the wake ledger.
Send files and folders with a message Added in v0.217.0:
orboto agent-notify worker@ACME "build output" --attach ./dist
orboto agent-notify worker@ACME "sources" --attach ./src --include '*.ts' --exclude 'fixtures/**'
orboto messages --download <message id> --list
orboto messages --download <message id> --to ./incoming [--only <name>] [--overwrite]--attach is repeatable. Files alone travel as they are (up to 50); a folder packs everything into one tar.zst with a manifest, honouring .gitignore and never following symlinks. A secret in any file stops the send, names the file and the rule, and exits with 2. --download verifies size and SHA-256, refuses .., absolute paths and link entries, and replaces existing files only with --overwrite; extraction is not atomic. The recipient's inbox shows only the count and size. The limit is 200 MB per message by default (instance setting, 1 MB to 5 GB). Details are on Sessions, scope and the wake ledger.
See what woke a session, and whether that was right Added in v0.200.0:
orboto agent-wakes # every message that woke one of your sessions
orboto agent-wakes --project ACME --outcome not_mine # the misrouted ones
orboto agent-wakes --session <uuid> --limit 50 --json
orboto agent-wakes --stats --hours 24 # precision per session and per sender--outcome takes pending, handled, not_mine, obsolete, duplicate or
expired. --stats reads the operator view and needs admin:agents:read.
Credential vault
The vault itself, its kinds, grants and audit are on Secrets and credentials. The commands that need more than a flag:
orboto credential create acme-login --title "ACME login" --kind login \
--username build@example.org --totp-prompt # prompts for the password and the TOTP secret with the input hidden
orboto credential update acme-login --totp-qr qr.png # the QR code, decoded here; only the parsed fields are sent
orboto credential totp acme-login # the current one-time code
orboto credential list --sort rotation_due --table # a table with a rotation column instead of the JSON pageA secret is never passed as an argument: the CLI reads it from a file, from STDIN, or asks with the input hidden.
SSH key pairs
Added in v0.211.0 A key pair from the vault is loaded into an ssh-agent for one shell, one command or one session, and never written to disk.
| Command | What it does |
|---|---|
eval "$(orboto credential ssh-agent KEY [--ttl 8h])" | Starts an ssh-agent for this shell and loads the key pair into it. The key reaches ssh-add on its input, never a file; the passphrase is answered once by a script that deletes itself. |
orboto credential ssh-agent KEY --print-public | Prints the public key. Needs view access, no reveal. |
orboto credential ssh-agent --stop [KEY] | Stops the agents of this user on this host that no running orboto command owns any more, for the key or all keys. --all also stops those a running orboto ssh, session or runner still uses. |
orboto ssh --credential KEY [--ttl 8h] [ssh options] user@host [command] | Runs ssh with the key loaded for this one command; the exit code is ssh's. |
orboto claude --ssh-credential KEY, orboto codex --ssh-credential KEY | Keeps the key in an agent for the whole session and stops it when the session ends. |
orboto pi-runner ... --ssh-credential KEY, orboto lanes assign LANE --ssh-credential KEY | A runner loads the key for every job, uses it for git and stops it when the job ends, also on failure. |
On Windows the key goes into the OpenSSH Authentication Agent service, which every session of the machine shares: stopping an agent removes the key from it, also while another command still uses the same key pair. Without an agent the command exits with code 2. Every load is recorded as a reveal with the purpose "SSH agent".
A runner's git calls get GIT_SSH_COMMAND set for the loaded key, which replaces any GIT_SSH_COMMAND you set yourself for those calls.
Encryption
Added in v0.203.0 Everything on Admin -> Encryption is available from the command line. Commands that change something name each point you have to confirm and ask for it; --yes is for scripts that have read them.
orboto encryption status # areas, state, key fingerprint, encrypted and plaintext counts
orboto encryption enable # files; add --scope backups for backup archives
orboto encryption disable --scope backups # the other direction, always allowed
orboto encryption flow # where a half-finished guided change stands
orboto encryption sweep --watch # convert what is already stored and follow it
orboto encryption sweep --dry-run # count without writing anything
orboto encryption confirmations # who confirmed which point, when, in which text version
orboto encryption rotate # move secrets, files and archives onto the current key
orboto encryption drill # rehearse a revoked key on a test fileAdded in v0.204.0 With AWS KMS, status also shows the AWS identity orboto calls with, the role in your account, the external ID and whether taking the role on last worked. The key settings themselves are changed in the admin page, not from the command line.
While your key does not answer, status opens with KEY UNAVAILABLE since ... and what the key service said. What the areas mean and what each change costs is on Security and encryption.
Full API access (generic verbs)
The named commands above cover the frequent operations. The entire REST API stays reachable through generic verbs, so a new endpoint never needs a new CLI subcommand before it's usable:
orboto get /users/me
orboto post /v1/agent/notify '{"targetEmail":"bot@example.com","subject":"ready"}'
echo '{"title":"A"}' | orboto post /projects/<id>/tickets -
orboto patch /tickets/<id> '{"priority":"high"}'
orboto delete /tickets/<id>/labels/<labelId>The trailing - reads the JSON body from stdin instead of an inline
argument - useful for larger payloads or piping from another command.
Discover available endpoints and their schemas with the authenticated catalog:
orboto api-search "milestone burndown"
orboto api-search --path /projects/{id}/milestones --method POSTapi-search returns endpoint details and required permissions. The interactive
/docs and /docs/json OpenAPI routes are closed by default, even with a
token, unless the operator explicitly enables public API documentation.
Full command reference
| Group | Commands |
|---|---|
| Identity + orientation | whoami, primer <projectKey>, rules |
| Connection profiles | profile list, profile show, profile import, profile remove, global --profile |
| Tickets | ticket, ticket list, ticket mine, ticket create, ticket claim, ticket comment, ticket move, ticket close, ticket check-similar, ticket deps, ticket bulk-close, ticket bulk-create, ticket bulk-deps |
| Milestones + docs + search | milestone list, milestone create, milestone close, search, query, doc get |
| Sessions | session-start, work-start, work-next, work-finish, work-sessions |
| Connecting an AI client | connect, connect --verify, mcp install, mcp serve, mcp status, mcp uninstall, claude, claude-statusline |
| Harness launchers | claude, codex, gemini, kimi, ollama (uniform --yolo, --continue, --resume), <launcher> accounts list|add|login|refresh|remove|disable|enable|config|rotate, <launcher> switch, Added in v0.230.0 claude switching, claude accounts config --auto-switch |
| Project agents | agent start, agent list, agent stop |
| pi supervisor | pi-runner |
| Live sessions | live-session pty, live-session run, live-session login, session list, session attach, session release, session revoke |
| Agent inbox + time | messages, messages --dismiss, messages --download, agent-notify, agent-notify --attach, agent-broadcast live, agent-broadcast status, agent-heartbeat, agent-presence, agent-wakes, timer, timer start, timer stop |
| Generic API access | api-search, get, post, patch, put, delete |
| Other | version, help, self-update |
Updating
Added in v0.205.1 After a command, an older CLI tells you at most once a day, in one line on stderr, that a newer version exists: orboto 0.200.0 - 0.204.0 is available: orboto update. orboto update (also orboto upgrade or orboto self-update) installs it; the binary is verified and replaced, never without being asked. orboto version --check shows you the newest version right away. Silence the hint with ORBOTO_NO_UPDATE_HINT=1, or per call with --quiet or --json; the normal output on stdout is never touched. A mistyped command now suggests the closest one: unknown command "updat" - did you mean \update`?`.
Troubleshooting
| Symptom | Fix |
|---|---|
work-next exits with code 3 | Not an error - it means no ticket is currently ready to pull (empty backlog, or the project/workspace pause switch is on). Retry later, or check Agents administration for pause state. |
claim succeeds but no timer starts | Passed --no-timer, or a different agent instance under the same account already owns a running timer, or Added in v0.230.0 you hold no time:log in the ticket's project (the answer says timerStarted: false). Check orboto timer. |
| A ticket won't move to the status you expect | move/close enforce the project's configured workflow transitions, same as the UI - list the project's statuses with orboto get /projects/<id>/ticket-statuses to see what's actually reachable from the current one. |
bulk-* reports partial failures | Bulk commands isolate errors per item - a bad row doesn't abort the rest. Re-run with just the failed keys/rows after fixing the input. |
Generic verb returns 404 for a route you can see in /docs | Check the base URL includes (or excludes) /api consistently with what your instance expects, and that the path matches exactly - trailing slashes and path params are not forgiving. |
CLI connection profiles
Separate accounts and servers across CLI commands, agent processes, the Node wrapper, and inbox hooks.
CLI JSON output
The one convention behind every --json flag of the orboto CLI: a single document on stdout, an items envelope for lists, ISO times, and which commands follow it.