Command reference
All 57 commands of the agterm control API, with arguments and return values. Every one is reachable from the agtermctl CLI and from the local control socket.
For the narrative introduction — what the socket is, how to install the CLI, and worked recipes — see the agtermctl section of the docs.
Overview
agterm listens on a local unix-domain socket. Each connection carries one newline-delimited JSON request and receives one response, then closes. The scope is personal scripting — fire-and-forget commands. There is no scrollback streaming and no event subscription.
Socket resolution. With --socket omitted, agtermctl resolves the same rendezvous the app bound: <AGTERM_STATE_DIR>/agterm.sock, else ~/Library/Application Support/agterm/agterm.sock. A spawned shell also sees the bound path in $AGTERM_SOCKET; passing --socket "$AGTERM_SOCKET" is the safe explicit form.
Response shape. {"ok": true, "result": {…}} or {"ok": false, "error": "<message>"}. The process exit code is non-zero when ok is false. result carries one of: id (affected/new session, workspace, or window), text (session copy/text), exitCode (overlay result), count (search matches, keymap/config diagnostics), ratio (session resize), tree, or windows.
Options go after the subcommand. agtermctl session type "ls" --target active, never before it. Add --json to print the raw response object — without it, mutations print ok and tree / window list print a human listing. Use --json when you need to read ids or values back.
Addressing
--target defaults to active (the selected session, or the current workspace). It accepts a full UUID (case-insensitive) or a unique, git-style prefix. Zero matches gives notFound; an ambiguous prefix gives ambiguous, listing the candidates.
--window <id|prefix|active> (on session, workspace, tree, font, and notify commands) picks which window's tree to act on; the default is the frontmost. With it set, that window must be open; without it, an id/prefix session target is matched across all open windows. The window.* commands instead take the window selector as a positional argument, defaulting to active (frontmost). A window need not be open to be a window.* target — window select opens a closed one.
For an agent, active is the user's GUI-selected session, not yours. Your own shell is $AGTERM_SESSION_ID. Pass --target "$AGTERM_SESSION_ID" on any session-scoped command that must act on the session you run in — otherwise it hits whatever the user has selected.
tree
Print the workspace/session tree. This is the read side of most of the API — nearly every state-mutating command has a matching field here, so a script can record a value, change it, and restore it.
Session node: id, name, cwd, title (raw OSC terminal title, omitted when none), active, split, splitRatio, splitFocused, overlay, overlaySizePercent, scratch, flagged, status, statusPane, statusBlink, statusColor, foreground / splitForeground (each pane's live foreground argv, omitted at the shell prompt), background, and unseen.
Workspace node: id, name, active, sessions, and focused (the read side of workspace focus, distinct from active).
Top level: idleMs (ms since last user input; live, so tree-only), autoFollowMs, sidebarVisible, sidebarMode, and quickVisible. All five are read-only projections of GUI state.
workspace
Create a workspace. The name defaults to an auto-generated one. Returns result.id.
Rename the target workspace.
Delete the target workspace. Keep-at-least-one: deleting the last workspace is an error. Unlike the GUI, nothing blocks on a confirm dialog.
Select the target workspace.
Reorder the workspace among its siblings. A missing or invalid --to is an error.
--target active resolves to the current workspace, which with no selected session falls back to the last workspace — address a specific workspace by id to step the same one repeatedly.
Collapse the sidebar tree to a single workspace's subtree, hiding the others, or restore the full tree. on focuses the target, off unfocuses it only when it is the currently focused one, and toggle (the default) flips. Per-window and persisted; orthogonal to sidebar mode. An unknown mode errors. Returns result.id.
While a workspace is focused, session go navigation is scoped to its sessions. An explicit session select of a session outside it auto-unfocuses. Read back via the workspace node's focused field.
session
Create a session and focus it. --cwd sets the start directory (default $HOME). The destination workspace is addressed either by --workspace (id / prefix / active) or by --workspace-name (the sidebar label) — the latter errors when no workspace has that name, unless --create-workspace is also passed, which reuses or creates it idempotently. --name seeds the sidebar label. Returns result.id.
--after SID / --before SID place the new session directly after or before an anchor session instead of appending. The anchor carries its own workspace, so it names the destination itself — these are mutually exclusive with each other and with --workspace / --workspace-name. The headline case: session new --after active.
--command runs a program as the session's process instead of the login shell (no echoed command line; the session closes when it exits). It runs argv-style — tokenized with quotes respected, but no shell, so ;, &&, $VAR, redirects and globs are not interpreted. It also inherits the app's GUI PATH (the launchd default — no /opt/homebrew/bin), so a bare Homebrew binary fails with exit 127. Use an absolute path, or wrap it: --command "zsh -lc 'htop'". The command is persisted and re-runs on restore when Restore running commands on restart is enabled.
Close the target session.
Select the target session. Selecting one outside a focused workspace auto-unfocuses to reveal it.
Set the session's custom sidebar label.
Three mutually-exclusive placement intents, exactly one required. A positional <workspace> relocates the session there (appending). --to reorders it within its own workspace. --after / --before place it directly after or before an anchor session — the anchor carries its own workspace, so cross-workspace placement falls out for free.
Move the selection relative to the current one — there is no --target. It operates over the visible, filtered set: the flagged sessions in flagged mode, the focused workspace's sessions when one is focused, else all. next/prev wrap at the ends; next-attention / prev-attention step only through sessions needing attention (status blocked or completed), also wrapping. Returns the newly selected result.id.
The attention variants only step the selection; unlike the GUI attention-nav they do not themselves move focus into a tagged pane.
Inject text as real keystrokes — printable runs plus a Return for each newline, with no bracketed-paste markers. So a trailing newline submits the command. --stdin reads the text from stdin instead of the argument. --select selects and realizes a never-shown session first (the main pane only); any realized session is normally typable without it.
--pane left is the main pane (the default), right the split pane, and scratch the scratch terminal even while hidden. Note the shell quoting: a literal \n inside plain single quotes reaches the CLI as two characters — use $'make test\n' or pipe a real newline via --stdin.
Returns result.text with the session's current selection. It does not touch the system clipboard — pipe the returned text into another session type. Selection is surface state independent of focus, so any realized session can be read. No or empty selection gives a no selection error.
Paste the system clipboard into the session's main pane — the socket analogue of ⌘V / Edit ▸ Paste. It runs libghostty's paste_from_clipboard as a bracketed paste with no prompt, so the text lands at the prompt without auto-submitting.
Read it back with session text. A never-shown session gives session not realized.
Select the session's entire terminal buffer (main pane) — the socket analogue of ⌘A / Edit ▸ Select All, running libghostty's select_all.
Read the resulting selection back with session copy. A never-shown session gives session not realized.
Returns result.text with the terminal buffer as plain text (no ANSI or color). By default it reads the visible screen of the on-screen pane. --all adds scrollback; --lines N keeps only the last N content lines. The two are mutually exclusive and N must be greater than 0 (enforced server-side too).
A genuinely blank screen is not an error — it returns ok with an empty string, unlike session copy's no selection. A failed read is an error (failed to read surface buffer). Plain text only; there is no --ansi.
Search the session's live scrollback. It selects the target first, so the search bar and match highlights render. With a needle it sets the query (opening the bar if needed); with no needle and no flag it just opens an empty bar. The three flags are mutually exclusive. Returns result.count (total matches) and result.text (the counter string — "N of M", "M matches", or "no matches").
The count settles asynchronously, so the command waits briefly for it.
A side-by-side second shell in the same session. off hides it but keeps the shell alive (mirroring ⌘D); the pane's surface is torn down only when its own shell exits. Idempotent; an unknown mode errors.
A third, full-coverage shell that renders like a full overlay but behaves like the split. off hides it keep-alive; typing exit closes it and the next on spawns a fresh shell. on selects the target first. Not persisted; an unknown mode errors.
--command (only when showing) runs a program instead of a login shell — argv-style with the same GUI-PATH exit-127 caveat as session new --command, and run-once. A scratch is expendable, so passing --command while one is open respawns it.
Move keyboard focus between the two split panes; other toggles and is the default. Errors when the session has no split. It works whether the split is shown side-by-side or hidden — when hidden, focusing a pane swaps which one shows maximized. Read back via the session node's splitFocused.
Move the split divider. Provide exactly one form: --split-ratio sets the absolute left-pane fraction (0–1); --grow-left D / --grow-right D nudge it by the fraction D. The result is clamped to 0.05–0.95 and persisted. Returns the applied fraction as result.ratio. Errors when the session has no split.
Control-native: the divider is otherwise mouse-drag only, with no GUI, menu, or keymap action — bind a key by mapping a command "agtermctl session resize …" custom action. Resizing a hidden split updates the stored fraction, applied when it is next shown.
Set the sidebar agent-status glyph. Setting a non-idle status is for agents and hooks; idle clears it (also available in the GUI). An unknown state errors. --blink pulses the glyph; --auto-reset clears it back to idle once the session is visited (a one-shot completion flash).
--sound plays a one-shot sound: default or a system sound name (Basso, Glass, Ping, … plus any custom sound in ~/Library/Sounds); an unknown name errors. Without it, a blocked status plays the user's configured Blocked sound, if any. --color #rrggbb overrides the glyph tint for this call only; the next status set without it reverts.
--pane (default left) records which pane set the status. It makes keystroke-clear pane-scoped — a status set from a background pane survives typing in a different pane — and it makes any user-initiated GUI selection reveal and focus the tagged pane. An agent running in a split or scratch should set its own pane. Read back as the session node's statusPane, statusBlink, and statusColor.
Flag or unflag a session for the flagged working-set view — a durable, persisted membership. on/off/toggle act on the target and are idempotent; clear ignores the target and unflags every session in the window. Unknown mode errors. Pair with sidebar mode flagged. Read back via the node's flagged field.
Clear the session's unseen-notification badge without changing the selection, focus, or agent status — the focus-free counterpart to notify. Idempotent. Read the current count from the node's unseen field.
Lets an orchestrator acknowledge a driven session's notifications over the socket, keeping the badge a real attention signal on the sessions a human tends.
agtermctl session background text <text> [--color #rrggbb] [--opacity F] [--fit …] [--position …]
agtermctl session background color <#rrggbb>
agtermctl session background clear
Set or clear a per-session background composited behind the terminal grid. Persisted, so it survives a relaunch. All four forms accept [--target T] [--window W].
image — PNG or JPEG only; libghostty auto-fits it and re-fits on resize. --opacity is 0.0–1.0 (default 1.0), --fit defaults to contain, --position is center or an edge/corner anchor, and --repeat tiles. text — rasterizes a word or two (capped at 256 characters); --color defaults to the terminal foreground.
color — a solid terminal background color, taking no opacity: it is drawn at the Settings window translucency, so it honors your opacity and blur. An image/text watermark instead forces the pane opaque. Read the current spec back from the node's background object.
session overlay
An ephemeral terminal running one program on top of a session. It closes when the program exits.
Full-size by default, hiding the session; --size-percent N (1–100) makes it a floating framed panel with the session visible behind. Returns the overlay's result.id. --background-color gives the overlay pane its own solid color, independent of the session's.
It does not switch the active session by default — both variants open on --target and run in the background. Pass --follow to select the target too. An automated caller should pass --target "$AGTERM_SESSION_ID", or a blocking full-pane overlay lands on whatever session the user has selected.
--wait keeps the overlay open after the command exits (press a key to close). --block waits for the command and makes agtermctl exit with its status — it cannot combine with --wait. The program's output is its own concern; the control channel does not capture stdout.
Unlike session new --command, the overlay command runs through sh -c, so shell operators do work — but it still inherits the app's GUI PATH, so a bare Homebrew binary fails with exit 127 (the overlay flashes open then vanishes). Give an absolute path or wrap in "zsh -lc '…'".
Resize an already-open overlay in place. Exactly one of --size-percent N (1–100, floating) or --full is required; both, neither, or an out-of-range percent errors. The program keeps running across the resize — it is a layout re-flow, never a re-spawn. Errors no overlay when none is open.
Record the node's overlaySizePercent first to restore the exact size after a zoom to --full.
Close and destroy the overlay.
Returns result.exitCode once the overlay has closed. Errors still running while it is up, and no result if none ran.
window
These take the window selector as a positional argument (default active, the frontmost). A window need not be open to be a target.
Create and open a window. Returns result.id.
Returns result.windows, each with id, name, open, active, autoFollowMs, sidebarVisible, geometry ({x, y, width, height, display}, the read side of window move/resize, in the same units they take), plus fullscreen and zoomed. The last three are omitted for a closed window.
geometry/fullscreen/zoomed stay current across hand-drags and GUI toggles. Unlike tree, this does not carry idleMs — a live metric would freeze in the cache.
Raise the window if open, else open it.
Close the on-screen window; the bundle is kept, so window select reopens it.
Rename the window.
Delete the window bundle entirely. Keep-at-least-one: deleting the last errors.
Frame size in points. The window must be open. The size is clamped into the window's minimum and the display's visible frame, so an oversized request is bounded rather than applied verbatim. Control-native (the title bar already drags-to-resize).
Top-left position in points relative to display N (default the window's current display; y measured from the display top). The window must be open. The origin is clamped so an off-screen request keeps a grabbable strip on the target display. Control-native.
Toggle between the normal frame and a maximized, fill-screen frame — not native full screen. A second call restores the prior frame. The window must be open. This is the control half of the double-click-on-header gesture. Read back via window list's zoomed.
Toggle native macOS full screen — a separate Space with an auto-hidden menu bar. A second call exits. The window must be open. The control half of View ▸ Toggle Full Screen (⌃⌘F) and the green traffic-light button; distinct from zoom. Read back via window list's fullscreen.
quick
The frontmost window's quick terminal — a single scratch terminal at 90% of the window, not in the tree, whose shell stays alive across hides. All three are frontmost-window-only: no --target, --window, or --pane.
Show, hide, or toggle it. Delta-computed, so it is idempotent. Errors no open window when none is open. Read its visibility back from the tree's top-level quickVisible.
Inject literal keystrokes into the quick terminal — the twin of session type. It polls briefly for the surface, so quick show; quick type back-to-back is reliable. Typing into a shown-then-hidden quick terminal still works.
Errors: quick terminal not open (never shown), quick terminal not realized, no open window.
Print the quick-terminal buffer as plain text — the read-back for quick type. It does not touch the system clipboard. --all and --lines N are mutually exclusive. Polls for the surface like quick type.
notify
Post a macOS desktop notification attributed to a session (default: the active session of the frontmost window). --title defaults to the session name. Clicking the banner reveals that session. It raises the session's unseen badge; clear it with session seen. Control-native.
For agentic attention — waiting on input, or a finished result — prefer session status. A notification is a one-shot banner with no lasting state, while a status is typed and persistent, and drives the attention list, the title-bar bell, and attention navigation. Keep notify for a one-off nudge.
font
Increase, decrease, or reset the font size on the target session's surface — three separate commands sharing one CLI subcommand. The per-session zoom is persisted. The GUI half is ⌘+ / ⌘− / ⌘0.
theme
App-global — no --window. The out-of-the-box default is the bundled agterm theme; a separate default ghostty entry means "no theme" — ghostty's own built-in colors.
Returns result.themes (the bundled names), result.theme (the current plain theme; absent means ghostty's built-in), and result.sync with result.light/result.dark. While syncing, result.theme is absent — the state rides the three sync fields.
Set and persist the terminal theme app-wide, per slot — the same change as Settings ▸ Appearance. A positional name (or --light, its alias) sets the light/single theme, keeping a dark theme if one is set. Omit the name for ghostty's built-in default — with a dark theme set, that clears both.
--dark NAME sets the dark theme and turns on appearance syncing: the terminal then tracks the macOS Light/Dark setting, applying the matching side automatically. --dark none clears it, stopping the tracking. The response always echoes the full state. An unknown name errors; a positional name combined with --light is a usage error. Over the socket this is the commit — there is no live preview.
keymap
Re-read and apply keymap.conf. Returns result.count — the number of parse diagnostics (0 is a clean reload). App-global; the same path as File ▸ Reload Keymap. See Customizing keys for the file format.
config
Re-read and apply the ghostty config. Returns result.count — the ghostty config-diagnostic count (0 is clean). App-global; the same path as File ▸ Reload Config.
The count spans all config sources, not just the agterm-scoped ghostty.conf — libghostty diagnostics do not record which file they came from — so do not read a non-zero count as proof that ghostty.conf is the culprit.
restore
Clear every session's saved captured foreground command and persist, so the next restart restores plain shells for those panes. App-global; prints ok.
It does not clear a session new --command session's own command — that is the durable creation identity and still re-runs on restore when the setting is on. This is the counterpart to the opt-in Restore running commands on restart setting.
Errors
Every failure comes back as {"ok": false, "error": "…"} with a non-zero exit code. An unknown command fails to decode and returns a structured error — never a crash.