agterm commands
← home docs GitHub ↗
Control API

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

agtermctl tree [--json] [--window W]
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

agtermctl workspace new [name] [--window W]
workspace.new

Create a workspace. The name defaults to an auto-generated one. Returns result.id.

agtermctl workspace rename <name> [--target T] [--window W]
workspace.rename

Rename the target workspace.

agtermctl workspace delete [--target T] [--window W]
workspace.delete

Delete the target workspace. Keep-at-least-one: deleting the last workspace is an error. Unlike the GUI, nothing blocks on a confirm dialog.

agtermctl workspace select [--target T] [--window W]
workspace.select

Select the target workspace.

agtermctl workspace move --to up|down|top|bottom [--target T] [--window W]
workspace.move

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.

agtermctl workspace focus [on|off|toggle] [--target T] [--window W]
workspace.focus

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

agtermctl session new [--cwd DIR] [--workspace W | --workspace-name NAME [--create-workspace]] [--command CMD] [--name NAME] [--after SID | --before SID] [--window W]
session.new

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.

agtermctl session close [--target T] [--window W]
session.close

Close the target session.

agtermctl session select [--target T] [--window W]
session.select

Select the target session. Selecting one outside a focused workspace auto-unfocuses to reveal it.

agtermctl session rename <name> [--target T] [--window W]
session.rename

Set the session's custom sidebar label.

agtermctl session move <workspace> | --to up|down|top|bottom | --after SID | --before SID [--target T] [--window W]
session.move

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.

agtermctl session go --to next|prev|first|last|next-attention|prev-attention [--window W]
session.go

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.

agtermctl session type <text> [--stdin] [--select] [--pane left|right|scratch] [--target T] [--window W]
session.type

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.

agtermctl session copy [--target T] [--window W]
session.copy

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.

agtermctl session paste [--target T] [--window W]
session.paste

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.

agtermctl session select-all [--target T] [--window W]
session.selectall

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.

agtermctl session text [--all] [--lines N] [--pane left|right|scratch] [--target T] [--window W]
session.text

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.

agtermctl session search [needle] [--next | --prev | --close] [--target T] [--window W]
session.search

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.

agtermctl session split [on|off|toggle] [--target T] [--window W]
session.split

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.

agtermctl session scratch [on|off|toggle] [--command CMD] [--target T] [--window W]
session.scratch

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.

agtermctl session focus [left|right|other] [--target T] [--window W]
session.focus

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.

agtermctl session resize (--split-ratio R | --grow-left D | --grow-right D) [--target T] [--window W]
session.resize

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.

agtermctl session status <idle|active|completed|blocked> [--blink] [--auto-reset] [--sound NAME] [--color #rrggbb] [--pane left|right|scratch] [--target T] [--window W]
session.status

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.

agtermctl session flag [on|off|toggle|clear] [--target T] [--window W]
session.flag

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.

agtermctl session seen [--target T] [--window W]
session.seen

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 image <path> [--opacity F] [--fit contain|cover|stretch|none] [--position P] [--repeat]
agtermctl session background text <text> [--color #rrggbb] [--opacity F] [--fit …] [--position …]
agtermctl session background color <#rrggbb>
agtermctl session background clear
session.background

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.

agtermctl session overlay open <command> [--cwd DIR] [--wait] [--block] [--size-percent N] [--background-color #rrggbb] [--follow] [--target T] [--window W]
session.overlay.open

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 '…'".

agtermctl session overlay resize (--size-percent N | --full) [--target T] [--window W]
session.overlay.resize

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.

agtermctl session overlay close [--target T] [--window W]
session.overlay.close

Close and destroy the overlay.

agtermctl session overlay result [--target T] [--window W]
session.overlay.result

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.

agtermctl window new [name]
window.new

Create and open a window. Returns result.id.

agtermctl window list
window.list

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.

agtermctl window select <id>
window.select

Raise the window if open, else open it.

agtermctl window close <id>
window.close

Close the on-screen window; the bundle is kept, so window select reopens it.

agtermctl window rename <id> <name>
window.rename

Rename the window.

agtermctl window delete <id>
window.delete

Delete the window bundle entirely. Keep-at-least-one: deleting the last errors.

agtermctl window resize <id> --width W --height H
window.resize

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).

agtermctl window move <id> --x X --y Y [--display N]
window.move

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.

agtermctl window zoom <id>
window.zoom

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.

agtermctl window fullscreen <id>
window.fullscreen

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.

agtermctl quick [show|hide|toggle]
quick

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.

agtermctl quick type <text> [--stdin]
quick.type

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.

agtermctl quick text [--all] [--lines N]
quick.text

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

agtermctl notify <body> [--title T] [--target T] [--window W]
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

agtermctl font inc|dec|reset [--target T] [--window W]
font.inc · font.dec · font.reset

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.

agtermctl theme list
theme.list

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.

agtermctl theme set [name] [--light NAME] [--dark NAME|none]
theme.set

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

agtermctl keymap reload
keymap.reload

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

agtermctl config reload
config.reload

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

agtermctl restore clear
restore.clear

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.

notFound (target resolution) ambiguous (target resolution) no such session session not realized invalid split mode invalid scratch mode session has no split session has no scratch terminal no selection (copy) overlay already open no overlay still running (overlay) no result (overlay) invalid flag mode invalid fit invalid position invalid opacity invalid color text too long unsupported image (PNG or JPEG only) no such image file invalid background mode invalid sidebar mode invalid focus mode no open window quick terminal not open quick terminal not realized failed to read surface buffer window not open unknown theme: <name> unknown sound: <name> invalid color (expected #rrggbb) --pane must be left, right, or scratch
← agterm.com
GitHub Issues Discussions