IPC Socket
Boo exposes a Unix domain socket at ~/.boo/boo.sock (also available as $BOO_SOCK in terminal sessions) for communication between child processes, plugins, and the app.
Protocol
Newline-delimited JSON. One request per line, one response per request. Long-lived connections can subscribe to push events.
Commands
| Category | Commands |
|---|---|
| Query | get_context, get_theme, get_settings, get_workspaces, list_themes, get_state, get_screen |
| Control | set_theme, toggle_sidebar, switch_workspace, new_tab, new_workspace, send_text, send_key, select_pane, select_tab |
| Panes | split_pane, close_pane, focus_pane, resize_pane, swap_pane, zoom_pane, equalize_splits, get_layout |
| Windows | new_window, move_workspace_to_new_window, merge_all_windows |
| Agents | agent_event, agent_idle, agent_inbox, agent_jump, agent_respond, agent_timeline, new_agent_task, review_changes |
| Recipes | run_recipe, list_recipes |
| Process | set_status, clear_status, list_status |
| Events | subscribe, unsubscribe |
| Status Bar | statusbar.set, statusbar.clear, statusbar.list |
| Plugin | <namespace>.<action> |
Examples
# Query current terminal context
echo '{"cmd":"get_context"}' | nc -U "$BOO_SOCK"
# Register as an AI agent
echo '{"cmd":"set_status","pid":'"$$"',"name":"claude","category":"ai"}' | nc -U "$BOO_SOCK"
# Send a command to the active terminal
echo '{"cmd":"send_text","text":"echo hello\n"}' | nc -U "$BOO_SOCK"
# Push a status bar segment
echo '{"cmd":"statusbar.set","id":"ci","text":"CI: passing","icon":"checkmark.circle","tint":"green"}' \
| nc -U "$BOO_SOCK"Workspaces, Tabs and Keys
Without a pane_id, commands go to the frontmost Boo window.
| Command | Arguments | Reply |
|---|---|---|
get_workspaces | workspaces (index, id, name, path, is_active, pane_count) | |
get_state | workspaces, panes of the active workspace with their tabs, agents, theme | |
list_themes | themes (names) | |
switch_workspace | index or id | |
new_tab | optional cwd (default: the workspace folder), command (run in the new shell), env (object of strings; BOO_* keys ignored); opens in the active pane | tab_id |
new_workspace | optional path (default: home) | |
new_window | windows (count) | |
select_pane | id of a pane in the active workspace | |
select_tab | index of a tab in the active pane | |
send_key | key: enter, tab, escape, backspace, up, down, left, right, ctrl_c, ctrl_d, ctrl_z or ctrl_l; goes to the active terminal | |
agent_idle | pane_id: marks activity on that pane's tab, even when it is in front | |
run_recipe | name (file name in ~/.boo/recipes without .json; case-insensitive) or path; opens a recipe as a new workspace | workspace_id, name; an unknown name's error lists the recipes |
list_recipes | recipes (name, path, folder, null when the file is invalid) |
Event Subscriptions
Keep the connection open after subscribe to receive push events:
| Event | Data |
|---|---|
cwd_changed | path, is_remote, pane_id |
title_changed | title, pane_id |
process_changed | name, category, pane_id, pid, metadata |
remote_session_changed | type, host, active, pane_id |
focus_changed | pane_id |
workspace_switched | workspace_id |
theme_changed | name, is_dark |
settings_changed | topic |
cmd_started | command, pane_id |
cmd_ended | command, exit_code, duration, pane_id |
Use "events": ["*"] to subscribe to all events.
send_text and get_screen
send_text writes text directly to the active terminal's PTY — identical to typing it. With pane_id it targets that pane, in any workspace of its window; with submit: true the text is pasted as one input and Return is pressed, which is how to send a prompt to an agent.
get_screen with pane_id and/or lines (max 5000) returns that pane's last lines of output, scrollback included, as text, plus title and cwd; with pane_id but no lines, the visible screen. Without either, it returns the visible screen of the active pane, plus cols, rows and a styled vt snapshot.
echo '{"cmd":"send_text","text":"cd ~/projects/myapp\n"}' | nc -U "$BOO_SOCK"
echo '{"cmd":"send_text","pane_id":"<id>","text":"npm test","submit":true}' | nc -U "$BOO_SOCK"
echo '{"cmd":"get_screen","pane_id":"<id>","lines":50}' | nc -U "$BOO_SOCK"Pane Layout
Commands act on the active workspace of the frontmost Boo window. split_pane, close_pane and get_layout also take a pane_id: the command then goes to the window and workspace holding that pane, even in the background. focus_pane, resize_pane, swap_pane, zoom_pane and equalize_splits take a pane_id too, but only for a pane on screen; in a background workspace they fail with pane is in a background workspace; switch to it first. direction is left, right, up or down.
| Command | Arguments | Reply |
|---|---|---|
split_pane | direction (default right): side of the new pane; pane_id to split (default active); command to run in it; cwd; focus (default true) | pane_id, started |
close_pane | pane_id (default active); never the only pane | active_pane |
focus_pane | direction, or next / previous; pane_id alone focuses that pane, with a direction it moves from there | active_pane |
resize_pane | direction, amount in points (default 10), pane_id | |
swap_pane | direction, pane_id | |
zoom_pane | zoomed (true/false; omit to toggle), pane_id | zoomed |
equalize_splits | optional pane_id | |
get_layout | pane_id: lay out that pane's workspace | tree, panes, windows, workspace_visible |
move_workspace_to_new_window | index (default active) | windows |
merge_all_windows | moves every window's workspaces into the active one | workspaces |
get_layout returns the split tree with ratios, each pane's on-screen frame (top-left origin, points) whether it is visible (hidden while another pane is zoomed), its terminal grid (columns, rows) and its tabs (id, title, type, active, cwd, process). Frames are left out for a background workspace. A pane split into a background workspace (started: false) gets its shell, and runs its command, when the workspace is next shown.
echo '{"cmd":"split_pane","direction":"down"}' | nc -U "$BOO_SOCK"
echo '{"cmd":"get_layout"}' | nc -U "$BOO_SOCK"Agent Inbox
Boo keeps one list of coding agents across all windows: which are working, which wait for an answer, which finished. The status bar shows it; clicking lists every agent and jumps to its tab.
Claude Code feeds it automatically: on launch Boo registers ~/.boo/shell-integration/boo-claude-hook in ~/.claude/settings.json for UserPromptSubmit, PostToolUse, Notification, Stop and SessionEnd (only if that file already exists; your own hooks are kept). For Codex, add to ~/.codex/config.toml:
notify = ["~/.boo/shell-integration/boo-claude-hook", "codex"]Other agents can report directly:
| Command | Arguments | Reply |
|---|---|---|
agent_event | event: working, needs-input, done, error or end; tab_id or pane_id ($BOO_TAB_ID / $BOO_PANE_ID); optional agent, message, tool, session_id, cwd | state |
agent_inbox | attention, items | |
agent_jump | tab_id, or omit for the next agent waiting | |
agent_respond | tab_id, response: approve (types Return) or deny (types Escape); optional updated_at from agent_inbox to refuse if the agent asked something newer |
needs-input waits until the agent works again; done and error wait until you look at the tab. Entering either state sends a notification unless the tab is in front.
echo '{"cmd":"agent_event","pane_id":"'"$BOO_PANE_ID"'","event":"needs-input","agent":"aider","message":"Apply edits?"}' | nc -U "$BOO_SOCK"Agent Timeline
File → Agent Timeline (or Show Timeline in the Agents sidebar and the status bar's agent menu) opens a tab with what a Claude Code session did, read from its transcript in ~/.claude/projects ($CLAUDE_CONFIG_DIR if set): each prompt with its tool calls, duration, tokens and cost, the reply, and the files edited. Calls repeated three or more times in a row fold into one row marked in the warning color, so runaway loops stand out; Loops & failures only hides the rest. The tab refreshes every few seconds while the session runs.
Cost is exact (Claude Code's own figure) once a session has ended; while it runs, Boo estimates it from token counts and public prices, shown with ≈. Subagent tokens count toward the total.
| Command | Arguments | Reply |
|---|---|---|
agent_timeline | tab_id (an inbox agent) or session_id; optional turns (default 10), open: true to show the tab | cost, cost_exact, tokens, prompts, tool_calls, loops, files, last turns |
Agent Tasks
File → New Agent Task… gives an agent its own checkout: Boo creates branch boo/<task> from the HEAD of the repository the active terminal is in (falling back to the workspace folder) in ~/.boo/worktrees/<repo>/<task>, opens it as a workspace and starts the agent with your first message. Several agents can work on one repository without touching each other.
Right-click a task's workspace for Merge Worktree… (git merge --no-ff into the branch checked out in the main repository; refuses with uncommitted changes, aborts on conflicts) or Discard Worktree…. Both can delete the worktree and branch and close the workspace.
File → Review Changes… (or Review Changes… in a task's context menu) opens a diff tab with everything the agent changed: for a task, all commits since it branched off plus uncommitted and untracked files; for any other repository, the changes since the last commit. Click a line to comment, then Send to Agent (⌘↩) pastes all comments as one numbered message into the workspace's agent terminal.
| Command | Arguments | Reply |
|---|---|---|
new_agent_task | name and/or prompt; optional agent (claude, codex, opencode) and repo (default: active terminal's repo, else workspace) | path, workspace_id |
review_changes | optional workspace_id or pane_id (default: active workspace) | — |
echo '{"cmd":"new_agent_task","name":"fix login","prompt":"The login redirect loops, fix it"}' | nc -U "$BOO_SOCK"Handing context to an agent
Terminal → Send to Agent (⌥⌘↩) pastes the selection, or else the output of the last command, into the prompt of the nearest agent, without pressing Return, and brings its tab to the front. The agent in the same workspace wins, then the most recently active one. The text arrives fenced, with a header naming the command, its exit code and the directory. Long output keeps its last 60,000 characters, where errors usually are.
Right-click a terminal for Send Selection to … and Send Output of “…” to …, with a submenu when several agents run. Dropping text or files onto an agent pane pastes them too (files as shell-escaped paths).
Last-command output needs shell integration: Boo cuts it out of the scrollback between the command line and the next prompt.
MCP Server
Boo is also an MCP server, so an agent running in a Boo terminal can arrange its own workspace: start a dev server or test watcher in a pane beside it and read the output later, type into other panes, start parallel agent tasks, or open the review for you. Boo (the installed app, not a bare development binary) writes a launcher to ~/.boo/shell-integration/boo-mcp on every start; register it once:
claude mcp add -s user boo -- ~/.boo/shell-integration/boo-mcp
codex mcp add boo -- ~/.boo/shell-integration/boo-mcp| Tool | What it does |
|---|---|
get_layout | Panes of the agent's workspace with tabs, cwd and running process; your_pane is the agent's own |
split_pane | New pane beside the agent's (direction, command, cwd, pane_id); focus stays with you unless focus |
read_pane | Last lines (default 100) of a pane's output |
send_text | Type into another pane, pressing Return unless submit is false |
close_pane | Close another pane |
list_agents | The agent inbox |
new_agent_task | Worktree task on the agent's repository with a first prompt |
run_recipe | Open a saved recipe by name as a new workspace |
open_review | Review tab for the agent's workspace |
wait_for_events | Blocks until an agent changes state, a task is spawned or finishes, a pane closes or an approval resolves (since, timeout) |
agent_respond | Approve, deny or answer a waiting agent by tab_id |
focus_pane | Bring an agent's tab to the front |
task_status | A task's commits, uncommitted changes, merge conflicts, and whether the main checkout is dirty |
task_diff | A task's changes (stat_only for the summary) |
merge_task | Merge a task into its main checkout (cleanup removes the worktree) |
discard_task | Delete a task's worktree and branch |
The server (Boo --mcp) talks to the running app over $BOO_SOCK and defaults to the agent's pane through $BOO_PANE_ID, which every Boo terminal sets. It refuses to type into or close the agent's own pane.
Status Bar Segments
External processes can publish status bar segments. Tint accepts named colors (red, green, yellow, blue, orange, purple, accent) or #hex. Segments are auto-cleaned on disconnect.
Security
- Socket I/O runs on a dedicated serial queue
- Connecting processes are verified with
LOCAL_PEERCRED - Registered PIDs are checked as descendants of the shell
- Dead processes and disconnected clients are cleaned up automatically
Capability tokens
Every Boo terminal gets a random BOO_TOKEN next to BOO_PANE_ID. A command needs this token when it would give a process power it lacks in its own shell:
agent_respondsend_textorsend_keyinto an agent or a remote sessionclose_paneon an agent pane- merging or discarding a task
Send the token as "token" in the request. The MCP server does this for you.
- A normal terminal's token runs these commands directly.
- The orchestrator's token puts them on an approval card, except writes to agents it started. Merge and discard always need a card. The reply is
{"status":"pending","approval_id":…}. - Without a token, these commands are rejected. Everything else works without a token, so scripts keep working as long as they do not type into agents or remote sessions.
Tokens are kept in memory and change when Boo restarts. They catch mistakes and overreach by a model. They do not stop an attacker running as the same user, who can read another process's environment.
