Skip to main content

Getting started

botctl is a Rust CLI for keeping Claude Code, Codex CLI, OpenCode, Pi, and Antigravity sessions visible and controlled inside tmux.

Claude Code panes support guarded automation. Codex CLI panes are classified from captured terminal screens and can use YOLO for command permission approval. OpenCode panes are discovered passively from their SQLite database. Pi panes are discovered passively from ~/.pi/agent/sessions for dashboard visibility, recent-message context, state classification, and tmux window status. Antigravity (agy) panes are discovered passively for dashboard visibility, state classification, and pane-scrape last-message extraction.

For end-to-end operator flows, see workflows. For command details, see command reference.

Learn these first

Most users only need a few commands:

  • runtime for the single local coordinator that owns live observation and automation
  • dashboard for a live control surface across Claude Code panes plus screen-detected Codex CLI and passive OpenCode/Pi/Antigravity visibility
  • prompt for one-shot Claude prompts that still run through an inspectable tmux TUI
  • yolo for centralized babysitting policy and recovery
  • serve for a runtime-backed observation stream and HTTP facade
  • mcp for persistent Claude sessions exposed through a small JSON-RPC tool API
  • last-message when you need the full latest assistant reply instead of the visible terminal excerpt

For direct blocker recovery, use the canonical names approve, reject, and dismiss-survey. The longer approve-permission and reject-permission names still work as compatibility aliases.

If you just want to get value quickly, install it, open dashboard, then try yolo, serve, prompt, or last-message. dashboard, yolo, and serve auto-start the managed runtime when needed. Everything else supports setup, diagnostics, prompt handoff, and lower-level recovery.

Requirements

  • tmux
  • claude on PATH for Claude Code automation
  • codex panes for Codex CLI screen classification and command permission approval
  • opencode panes with OC | <session title> pane titles for passive OpenCode dashboard visibility
  • agy on PATH (optional) for Antigravity dashboard visibility; the state directory defaults to ~/.gemini/antigravity-cli and can be overridden with ANTIGRAVITY_STATE_DIR
  • botctl installed

Install

cargo install botctl

Verify the binary is on your PATH:

botctl --version
botctl help

Then open the dashboard, which starts the managed runtime automatically when needed:

botctl dashboard

First useful run

  1. Open at least one Claude Code, Codex CLI, OpenCode, Pi, or Antigravity pane inside tmux.

  2. Open the dashboard:

    botctl dashboard
  3. Select a pane in the dashboard and press Enter to jump to it.

  4. If the pane is a Claude Code or Codex pane blocked on a supported permission dialog, enable YOLO from the dashboard or target the pane directly:

    botctl yolo --pane 0:6.0
  5. If you need machine-readable observation for a tmux session, run:

    botctl serve --session demo --format jsonl

Use botctl help <command> when you need terminal-local syntax.

For a one-shot prompt that launches Claude in a new tmux window, submits through the guarded TUI path, and prints only the assistant answer to stdout:

botctl prompt --text "Summarize this repo"
printf 'Say exactly hello' | botctl prompt --stdin --cwd /path/to/project
botctl prompt --text hi -- --model sonnet --name "Just testing"

By default, stdout contains only the assistant answer. prompt uses the shared botctl tmux session unless --session NAME overrides it, creates that session automatically when needed, creates one new window per run, and kills only that captured window after loading a fresh assistant answer. Failed prompt windows remain available for inspection. Pass --verbose to write launch/wait progress to stderr. Arguments after -- pass through to Claude.

The runtime owns live tmux observation, pane classification, yolo supervision, and guarded actions. dashboard, yolo, and serve connect to the same local socket at <state-dir>/runtime.sock, and in managed mode they auto-start a hidden tmux-backed runtime when one is not already running.

Managed versus unmanaged

  • managed is the default for dashboard, yolo, and serve
  • managed mode auto-starts a hidden runtime session when needed
  • managed clients share runtime leases so the last managed listener can keep the runtime alive instead of losing it when an earlier client exits
  • --unmanaged disables auto-start and requires an existing runtime

Direct runtime commands:

botctl runtime
botctl runtime stop
botctl runtime --foreground

First session

Use this path when you want botctl to launch Claude Code for you.

  1. Start a managed Claude session:

    botctl start --session demo --cwd /path/to/project
  2. Check what botctl sees:

    botctl list
    botctl doctor --session demo
    botctl status --pane %19
  3. Attach with tmux when you want the full terminal UI:

    tmux attach -t demo

Use --plain on attach, list, status, and doctor when scripts need the line-oriented output contract.

For scripts, use JSON on one-shot inspection commands:

botctl list --json
botctl status --pane %19 --json
botctl doctor --session demo --json

Pane-targeted commands also accept tmux's explicit pane syntax:

botctl status --pane 0:2.3

If Claude is already running in tmux

If Claude was already started inside tmux, resolve and verify the pane explicitly before acting:

botctl list --all
botctl attach --pane %19

If you already know the tmux pane target, you can use it directly:

botctl attach --pane 0:2.3

Pane targets

Prefer raw tmux pane IDs such as %19 for automation. They identify one pane directly.

botctl also accepts tmux's explicit session:window.pane syntax, such as 0:2.3, when that target identifies exactly one pane. Session names are useful for setup and inspection, but guarded workflows are safest when you pass an explicit pane target.

Dashboard and YOLO

Open the cross-workspace dashboard TUI:

botctl dashboard

The dashboard uses one emoji per pane to show the current classified state at a glance. It also shows pane PID, process-tree average CPU, memory, and observed active Cook time so long-running or resource-heavy panes stay visible.

EmojiStateMeaningAutomation
💤ChatReadyReady for inputsubmit-prompt can run
💬PromptEditingReady with unsubmitted inputmanual submit or clear
🤔UserQuestionPromptWaiting for an operator decision or follow-upmanual review
⚙️BusyRespondingStill workingwait
🔐PermissionDialogWaiting on permission approval or rejectionapprove, reject, or YOLO when safe
PlanApprovalPromptWaiting on plan approvalmanual review
📁FolderTrustPromptWaiting on folder trust confirmationapprove sends raw Enter
📝SurveyPromptWaiting on a feedback promptdismiss-survey or YOLO
✏️ExternalEditorActiveWaiting on an external editor flowmanual review
🧾DiffDialogWaiting on a diff review choicemanual review
UnknownNot classified safelyinspect before acting

OpenCode panes can appear in the same dashboard without terminal capture or keypress automation. They are included only when the tmux command is opencode, the pane title is OC | <session title>, and OpenCode's SQLite database has exactly one session row whose directory matches the pane cwd and whose title matches the stripped pane title. If OpenCode truncates the pane title with ..., botctl accepts that title as a prefix only when it is still unique within the same cwd. If that match is missing, duplicated, ambiguous, or unreadable, the pane is ignored rather than guessed. Resolved OpenCode panes show a bounded recent-message excerpt from SQLite message/part rows and can show PermissionDialog when a running file/glob tool targets an absolute path outside the session directory. OpenCode and Pi panes are navigable and contribute window-title emojis, but YOLO and recovery actions are not enabled for them.

Codex CLI panes can also appear in the same dashboard. They are included only after botctl captures a likely Codex pane and sees Codex screen text such as the OpenAI Codex header, Codex approval prompt language, or a /statusline with run-state. With that statusline enabled, Ready maps to ChatReady, while Working and Thinking map to BusyResponding. Codex panes are navigable and classified from their screen, and Codex YOLO can approve command permission dialogs by sending y for Yes, proceed. Broader recovery actions remain Claude-only.

Antigravity (agy) panes appear in the same dashboard when the pane command is agy and the secondary signal passes. The dashboard shows the glyph, the current classified state (ChatReady/BusyResponding/AgyCommandPermissionPrompt/AgyFolderTrustPrompt/AgySettingsPersistPrompt/Unknown), and cook time derived from BusyResponding. Antigravity panes are navigable; YOLO command-permission auto-approve is opt-in per pane with botctl yolo start --pane <agy-pane> and fires only on the captured > 1. Yes default. Folder-trust and settings-persist prompts are detected separately but require manual review. Direct approve/reject/auto-unstick actions are not enabled for Antigravity panes.

Dump the latest assistant message

Use last-message when you need the full latest assistant text as Markdown instead of a bounded screen capture or dashboard excerpt:

botctl last-message --pane 0:4.1
botctl last-message --pane 0:4.1 --out last-agent-message.md
botctl last-message --pane 0:4.1 --out -

Without --out, botctl writes MESSAGE_<provider-session-id>.md in the current directory and prints:

Dumped last message of 951 lines out to file:
MESSAGE_91399d1d-79e5-48a2-b355-c8b751fe9333.md

Use --out - to stream the raw Markdown body to stdout instead of writing a file. The command supports Claude, Codex, OpenCode, Pi, and Antigravity transcript sources. Claude is resolved from ~/.claude/projects, Codex from ~/.codex/sessions, OpenCode from its SQLite message/part rows after the same pane title plus cwd resolution used by the dashboard, Pi from JSONL sessions under ~/.pi/agent/sessions (or PI_CODING_AGENT_SESSION_DIR), and Antigravity from pane-scrape extraction (requires three horizontal-rule lines to be visible in the pane scrollback: one above the last assistant turn, plus the two that bracket the live input box). It writes assistant-visible text only; tool calls and provider metadata are not included.

If you use tmux, this is the recommended way to launch the dashboard as a popup and keep it alive:

bind-key C-c display-popup -E -w 80% -h 40% botctl dashboard --persistent

That uses --persistent, which keeps the dashboard alive in a dedicated tmux-backed popup session:

botctl dashboard --persistent

When opened from tmux, the dashboard selects the row for the pane that launched the popup when that pane is visible. That makes the fast path prefix C-c, then y, to toggle YOLO for the current Claude pane.

Enable YOLO for one explicit pane:

botctl yolo --pane 0:6.0

Enable YOLO for all panes in one workspace:

botctl yolo --all --workspace .

Tail the shared runtime events for that yolo policy:

botctl yolo --pane 0:6.0 --follow

Observe versus serve

  • observe is bounded and returns a fixed snapshot/event sample.
  • runtime is the long-lived owner of observation and automation.
  • serve is the runtime-backed facade for one tmux session.
  • serve does not run its own observer loop anymore.

If you are not sure which one you want, you probably want managed serve, not observe.

Run serve against one tmux session:

botctl serve --session demo

Watch one specific pane only:

botctl serve --session demo --pane %19

Machine-readable stream:

botctl serve --session demo --format jsonl

Safety rules

  • Prefer explicit pane IDs for automation.
  • session:window.pane is also accepted when it identifies one pane exactly.
  • Never automate an ambiguous target.
  • Claude ownership is validated before automation runs.
  • Unknown is a refusal state, not a guess.
  • Folder trust approval is special and uses raw Enter.
  • Codex YOLO is limited to command permission dialogs.
  • OpenCode support is passive dashboard and status visibility.
  • Antigravity support is dashboard/status visibility plus opt-in YOLO command-permission auto-approve. Folder-trust, settings-persist, direct approve/reject, and auto-unstick remain manual.

For blocker recovery, prompt handoff, and recovery examples, see workflows and prompt handoff.

Current limits

  • live classification is still built around capture-pane, with the central runtime using a best-effort merged stream model when that helps break Unknown states
  • the classifier is conservative and keyword-based
  • serve is a runtime-backed foreground facade, not the full daemon/SSE control plane yet
  • the HTTP surface is still polling-based; there is no SSE endpoint yet