| name | occtl |
| description | Manage OpenCode sessions from the CLI using occtl. Use when the user wants to list sessions, read messages, watch sessions, send prompts, answer permission requests, inspect todos/status/diffs, abort sessions, create worktrees, or orchestrate autonomous OpenCode work. |
occtl
occtl controls OpenCode sessions from shell. Use it for health checks, session inspection, live streaming, permission approval, automation, and multi-session orchestration.
Basics
Prereq: OpenCode server running. If discovery fails, set OPENCODE_SERVER_HOST and OPENCODE_SERVER_PORT. Standard proxy env vars (HTTP_PROXY / HTTPS_PROXY plus NO_PROXY) are honored.
Session IDs can be omitted (most recent), full IDs, partial IDs, or title substrings.
Most commands support --json. Prefer JSON for scripts.
occtl ping
occtl list
occtl list --all --active
occtl list --orphans
occtl create -q
occtl new -q
occtl create -q -d /repo -t task
occtl get <id>
occtl show <id>
occtl rm <id>
occtl messages <id> --text-only
occtl msgs <id>
occtl last <id>
occtl watch <id> --text-only
occtl summary <id>
occtl todo <id>
occtl diff <id>
occtl status <id>
occtl abort <id>
occtl share <id>
occtl unshare <id>
occtl children <id>
occtl models --grep gpt-5.5
occtl models --enabled --grep opus
occtl install-skill --force
occtl view-skill --path
occtl help <command>
Aliases: ls=list, new=create, rm=delete, show=get, msgs=messages, prompt=send, wt=worktree, wt ls=wt list, wt rm=wt remove.
Send Work
occtl send "prompt"
occtl prompt "prompt"
occtl send -s <id> "prompt"
occtl send --async -s <id> "prompt"
occtl send -w -s <id> "prompt"
occtl send -w --timeout 600 -s <id> "prompt"
occtl send --stdin -s <id> < prompt.md
occtl send --no-reply -s <id> "context"
occtl stream -s <id> "prompt"
occtl stream --json -s <id> "prompt"
occtl stream -s <id> "prompt" --timeout 600
occtl stream -s <id> "prompt" --wait-children
Which command to use
| Goal | Prefer | Avoid |
|---|
| Ask an existing session and need the answer (agents, consults, automation) | stream or send -w | send --async then status/is-idle without --require-busy |
| Long / multi-tool turns where you want live progress | stream | default sync send (HTTP may time out) |
| Short Q&A on a quiet session | default send or send -w | — |
| Fire-and-forget, wait later with a barrier | send --async then wait-for-idle --require-busy or wait-for-text | bare wait-for-idle right after async |
Agent rule: when consulting another OpenCode session, use occtl stream --stdin -s <id> < prompt.md (or send -w --timeout <seconds> if you only need the final text). Do not send without wait and immediately status/last — that races. Do not treat empty stdout + exit 0 as success without checking last.
send -w and stream share the same completion model: snapshot assistant message IDs before send, then poll session.messages until a new assistant message is finalized (time.completed / terminal error / stable-content fallback). That never exits on pre-send idle and does not print a prior turn's message. Idle SSE is best-effort only (rescues abandoned turns). --timeout <seconds> exits 124; read the result with occtl last.
stream additionally prints live text/tool events (NDJSON with --json). By default it tracks only the main agent; add --wait-children for tree-aware wait (same idea as wait-for-idle / is-idle defaults). Parent sessions with active sub-agents report waiting; pass --main-agent to status, wait-for-idle, or is-idle only when parent-only status is intended.
Persist worker defaults at create time:
occtl create -q --model anthropic/claude-opus-4-6 --variant high --agent build
Later send/stream inherit stored model/variant/agent unless explicitly overridden.
One-shot runs create a session, send prompt, wait, then write response. --model required:
occtl run --model anthropic/claude-opus-4-6 "prompt"
occtl run --model openai/gpt-5.5 -f prompt.md --message "extra"
occtl run --model openai/gpt-5.5 --spawn --ephemeral --timeout 600000
occtl run --model openai/gpt-5.5 -o answer.txt --raw answer.json --stderr run.err
Waiting And Permissions
occtl respond <id> -p <permission-id> -r once
occtl respond <id> --wait -r always
occtl respond <id> --auto-approve --wait
occtl wait-for-idle <id> --timeout 600
occtl wait-for-idle <id> --require-busy --timeout 600
occtl wait-for-idle <id> --main-agent --timeout 600
occtl wait-for-text "DONE" <id> --timeout 600
occtl wait-any <id1> <id2> <id3> --timeout 600
occtl wait-all <id1> <id2> <id3> --require-busy --timeout 600
occtl is-idle <id> --require-busy
occtl is-idle <id> --main-agent
Race rule: session status can report idle before an async prompt starts, and some servers omit status maps while a turn is running. Never chain send --async → bare wait-for-idle / is-idle. Prefer stream or send -w (turn-complete). If you must separate send and wait, use wait-for-idle --require-busy, wait-all --require-busy, or wait-for-text.
Permission rule: occtl respond --wait handles requests that were already pending before the command started, then watches for new requests. Use --auto-approve --wait only when approving every request with once is acceptable for that session and repository.
Use wait-any when you want to react to whichever worker finishes first. Use wait-all as a barrier before verification, merge, or final report.
Timeout rule: do not blindly retry. Check occtl is-idle <id> and occtl summary <id>. If still busy, wait longer or abort deliberately. Retry in a fresh session, not same session.
Worktrees
Use worktrees for parallel or conflicting file changes. They live under .occtl/worktrees/<name> and normally create a matching OpenCode session.
occtl wt list --json
occtl wt ls
occtl wt create auth -q
occtl wt create auth -b branch --base main
occtl wt create auth --no-session
occtl wt run auth "implement auth"
occtl wt run auth -w --auto-approve "implement auth"
occtl wt remove auth
occtl wt rm auth
occtl wt rm auth --force
Review and merge branches yourself after workers finish.
Ralph Mode
Ralph Mode = you orchestrate many fresh OpenCode sessions until project complete. Use when user asks for Ralph Mode or autonomous multi-task work.
Core loop:
- Inspect repo and requirements.
- Create/maintain
tasks.md with atomic, verifiable tasks.
- Create/maintain
progress.txt with short worker summaries.
- For each task, create fresh session.
- Send prompt telling worker to pick one task, implement, verify, update
tasks.md/progress.txt, and commit if user wants commits.
- Auto-approve only when appropriate.
- Wait, then inspect
summary, last, todo, and changed files.
- Adapt: split bad tasks, clarify prompt, retry fresh session, or dispatch next task.
- Stop when tasks done; report result and verification.
Single-worker skeleton:
SID=$(occtl create -q -t "ralph-1-task-name")
occtl send --async -s "$SID" "Read tasks.md and progress.txt. Pick one incomplete task. Implement it. Run verification. Update tasks.md and progress.txt. Output RALPH_DONE when finished."
occtl respond "$SID" --auto-approve --wait &
APPROVER=$!
occtl wait-for-text "RALPH_DONE" "$SID" --timeout 600 || occtl summary "$SID"
kill "$APPROVER" 2>/dev/null || true
occtl summary "$SID"
occtl last "$SID"
Parallel pattern:
SID1=$(occtl create -q -t task-a)
SID2=$(occtl create -q -t task-b)
occtl send --async -s "$SID1" "Do task A per tasks.md"
occtl send --async -s "$SID2" "Do task B per tasks.md"
DONE=$(occtl wait-any "$SID1" "$SID2" --timeout 600)
occtl summary "$DONE"
Barrier pattern:
occtl wait-all "$SID1" "$SID2" --require-busy --timeout 600
occtl summary "$SID1"
occtl summary "$SID2"
Cross-project pattern:
API=$(occtl create -q -d /repo/api -t api-work)
WEB=$(occtl create -q -d /repo/web -t web-work)
occtl send --async -s "$API" "Implement endpoints per shared spec"
occtl send --async -s "$WEB" "Implement client per shared spec"
occtl wait-any "$API" "$WEB"
occtl wait-all "$API" "$WEB" --require-busy
Ralph rules:
- One meaningful task per worker session.
- You plan/evaluate; workers implement.
- Read worker output before next dispatch.
- Use parallel sessions only for independent work.
- Use worktrees when file conflicts are likely.
- Keep
progress.txt concise; git history is real memory.
- Never resend same prompt to same failed session; create a fresh one.
Failure Triage
occtl summary <id> --json
occtl last <id>
occtl messages <id> --text-only --limit 20
occtl diff <id>
occtl abort <id>
If task too big, split tasks.md. If worker misunderstood, refine prompt. If dependency missing, reorder. If transient, retry fresh session.