Automation
botctl automation is intentionally guarded. It only acts when it has an explicit pane target, a Claude-owned pane, a supported classified state, and the required keybindings.
Model
Automation is split into separate checks:
- Targeting — resolve an explicit pane id, or a session/window target that resolves to one active pane.
- Ownership — refuse if the pane is not currently running
claude. - Classification — inspect the pane state before sending keys.
- Policy — only execute a workflow from the state it supports.
- Keybinding routing — resolve the user's Claude keybindings instead of assuming a hard-coded map.
This is why raw send-keys success is not enough: a keypress can land in the wrong app, the wrong Claude state, or the wrong workflow.
Explicit targeting and ownership
Prefer --pane %ID for all automation. --pane session:window.pane is also accepted when you already have tmux's explicit pane target. Session-based targeting is only a convenience for resolving the active pane.
Before any guarded workflow runs, botctl checks that the pane is owned by Claude. If the pane is running anything else, automation refuses.
Keybinding policy
botctl reads ~/.claude/keybindings.json and routes actions from the user's bindings.
bindingsprints the recommended mapping.install-bindingsis non-destructive.- If a Claude keybinding file already exists,
install-bindingsmerges in missing required bindings when it can. - If the existing file is invalid or a required key is already used for something else, install fails clearly instead of overwriting the file.
- Missing bindings are surfaced by
doctor, and action routing fails clearly when required bindings are absent.
Guarded workflows
Guarded workflows validate state before acting:
submit-promptrequiresChatReadyapproveacceptsPermissionDialogandFolderTrustPrompt;approve-permissionis a compatibility aliasrejectrequiresPermissionDialog;reject-permissionis a compatibility aliasdismiss-surveyrequiresSurveyPrompt
Refusal states are part of the safety model:
Unknown— manual review requiredUserQuestionPrompt— manual review requiredPlanApprovalPrompt— manual review requiredDiffDialog— manual review requiredExternalEditorActive— manual review required
FolderTrustPrompt is a special case. Approval uses raw Enter, not the normal confirm-yes binding.
SurveyPrompt is also special. Dismissal uses the raw 0 key, not a Claude keybinding-resolved action.
State and action matrix
| State | Safe automated action | Notes |
|---|---|---|
ChatReady | submit-prompt; keep-going loop submission | Prompt submission verifies that the pane transitions after the submit sequence. |
PromptEditing | none | Submit or clear the unsubmitted prompt manually. submit-prompt refuses this state because it could replace typed input. |
BusyResponding | wait | continue-session can report the pane as already usable. |
PermissionDialog | approve, reject, YOLO | Approval is allowed only when the prompt is classified as a permission dialog and policy allows the action. |
FolderTrustPrompt | approve; auto-unstick; keep-going with YOLO enabled | Uses raw Enter because Claude's folder trust prompt confirms the selected default directly. |
SurveyPrompt | dismiss-survey, YOLO, auto-unstick, keep-going with YOLO enabled | Uses raw 0. |
PlanApprovalPrompt | none | Review manually. |
DiffDialog | none | Review manually. |
ExternalEditorActive | editor-helper only when you are intentionally completing a prompt handoff | General automation refuses this state. |
UserQuestionPrompt | none | Answer the question manually or submit a new prompt after the pane returns to ChatReady. |
Unknown | none | Inspect the pane or improve classifier coverage. |
Refusal semantics
botctl refuses when:
- the target is ambiguous
- the pane is not Claude-owned
- classification is
Unknown - the workflow does not match the current state
- required Claude keybindings are missing or invalid
That refusal is deliberate: it is safer to stop than to guess.
Refusals use exit code 2 when the command was understood but the guarded workflow intentionally declined to act. Runtime failures such as tmux, filesystem, state database, or keybinding read errors use exit code 1.
keep-going, auto-unstick, and yolo
These commands are recovery loops, not magic fixers.
auto-unstickwalks a pane through a bounded number of safe recovery steps.keep-goingwatches a pane, submits the built-in audit prompt by default or a custom loop prompt through--source/--text, and can optionally respond to supported blockers.keep-goingexpects each loop reply to end with exactly one terminal token:OKIE_DOKIE,ALL_DONE, orPANIC.- the built-in
keep-goingprompt tells Claude to verify completion, commit beforeALL_DONE, push on branches other thanmain,master,develop,dev,trunk, orrelease/*, and open a PR only when the user explicitly asked for one --no-yoloturnsPermissionDialogandFolderTrustPromptinto manual-review exits.yolois the long-running loop for supported permission dialogs and survey dismissal.- for Codex panes,
yoloonly approves command permission dialogs by sending rawyforYes, proceed
Limits:
auto-unstickandkeep-goingstill require Claude-owned panes- they still refuse on ambiguous or unsupported states
- they do not bypass classifier or keybinding checks
yolodoes not auto-handleFolderTrustPrompt- Codex YOLO only approves command permission dialogs; it does not submit prompts or run Claude keybinding workflows
keep-goingwill stop on manual-review blockers when--no-yolois set