# Introduction URL: https://www.termio.sh/docs Markdown: https://www.termio.sh/docs/index.md Termio is a native Mac app that gives your AI coding agents a home. Instead of juggling agents across scattered terminal tabs, you run them side by side — each in a real terminal — and switch between them instantly. Everything runs on hardware you own: this Mac, or a box you can `ssh` to. ## What Termio gives you - **A real terminal per agent.** Claude Code, Codex, Amp, Droid, Cursor, Copilot, Antigravity, Crush, Cline and the rest each run in a genuine PTY — not a re-rendered chat log. What you’d see in iTerm is exactly what you see here. - **A sidebar of sessions.** Every agent and shell is one click away. Statuses update live, so you can see which agent is working, which is idle, and which needs your attention. - **Split panes.** Put an agent, a dev server, and a shell in the same window, arranged the way you think. - **Any machine you own.** A workspace can live on a VPS or the Mac mini across the room. Termio copies one binary there over SSH and sets up the agents; the sessions run on that box and keep running whether or not you’re attached. - **A built-in inspector.** Browse the working directory, edit files with syntax highlighting, read the git diff, and search across the project — all without leaving the window. - **An orchestration API.** The `termio sessions` CLI lets one agent — or your scripts — spawn siblings, answer their prompts, and supervise the fleet over a local socket, with pinned JSON shapes and honest exit codes. - **Your phone as a remote.** Pair the iPhone app once and check in on a running session from anywhere, over your own network or a tunnel you control. ## Local-first, always Termio has no account and no sign-in. Your terminals, code and agent conversations never pass through servers of ours — there are none in the product’s data path. Reaching another machine goes through the system `ssh` and your own `~/.ssh/config`; Termio provisions nothing and relays nothing. The agents you run talk to their own providers under your own accounts, exactly as they would from a plain terminal. Termio is **free to use** — no account, no license keys, no payment backend. ## Pick your path Most people arrive here for one of three reasons. Start where you actually are: Install, open a project, start Claude Code or Codex in a real terminal, and split the window. Ten minutes, no concepts required. The part that pays off: live status per session, worktrees to keep two agents out of each other's files, and the sidebar as a fleet dashboard. A VPS, a Mac mini, a devbox. Termio copies one binary there over your own SSH config and the sessions run on that machine, attached or not. ## Or let an agent read the docs for you You already have a coding agent. These docs are published in a form it can read directly, so you can hand it the setup instead of doing it yourself: ```text Read https://termio.sh/llms.txt and then help me set up Termio for my project. ``` Every page also answers with plain Markdown at its own URL, and [llms-full.txt](https://termio.sh/llms-full.txt) is the whole set in one file — so an agent can answer from the docs rather than improvising. ## Start here Download the Mac app and get it updating itself. No account required. Open a project, launch an agent in a real terminal, and split the window. Keep a fleet side by side and read each one’s live status at a glance. Put a workspace on another box and set up the session host there. Close, quit, update, reboot — exactly what happens to a running agent. Let agents spawn, drive, and supervise each other with `termio sessions`. Pair your phone and check in on a running agent from anywhere. --- # Installation URL: https://www.termio.sh/docs/installation Markdown: https://www.termio.sh/docs/installation.md Termio runs on Apple silicon and Intel Macs from the same download. Installation is a normal drag-to-Applications — there’s no account to create and no license key to enter. Termio needs macOS 14 or later, on Apple silicon or Intel. It hosts the agent CLIs you already have installed — it doesn’t bundle them. ## What you need | | | | --- | --- | | **macOS** | 14 (Sonoma) or later, Apple silicon or Intel — one universal download | | **An account** | None. There is no sign-in, no license key, and no payment backend | | **Agent CLIs** | Whichever you already use — `claude`, `codex`, and the rest. Termio hosts them, it doesn’t ship them | | **For another machine** | Anything you can already `ssh` to. Termio reads your `~/.ssh/config` and installs nothing as root | ## Download and install 1. Download the latest build from [downloads.termio.sh](https://downloads.termio.sh/termio.dmg). The link always serves the newest notarized release. 2. Open the `.dmg` and drag **Termio** into your **Applications** folder. 3. Launch it from Applications. Because the app is notarized by Apple, it opens without a Gatekeeper warning. ## Check it worked Launch Termio and you should land on the welcome screen with **Open Project** as the primary action. That’s the whole install — there is no setup wizard, no account step, and nothing to configure before the first session. On first launch Termio asks once whether to let your agents coordinate — that’s **Session control**, which installs a small skill teaching them the `termio sessions` commands. **Live agent status**, the hooks behind the sidebar’s status dots, is a separate switch on the same tab; the sidebar offers it when it sees an agent running. Both are optional and reversible; see [Status hooks & session control](/docs/session-control). If you already use [Ghostty](https://ghostty.org), there’s nothing to pick: Termio reads your `~/.config/ghostty/config` on first launch and starts with the font and theme you chose there. ## Install with Homebrew Prefer the command line? Termio is a [Homebrew](https://brew.sh) cask: ```sh brew install --cask termio-sh/tap/termio ``` This installs the same notarized build. Termio still updates itself through Sparkle, so `brew upgrade` leaves it alone unless you pass `--greedy`. ## Staying up to date Termio updates itself through Sparkle. It periodically checks the download server for a newer version and offers to install it in place. The check carries no account or device identifier — the server sees only an IP address and the version being asked about. You can trigger a check any time from **Termio ▸ Check for Updates**. ## Bring your own agents Termio doesn’t bundle the coding agents — it hosts the ones you already have on your Mac. Install whichever CLIs you use (for example `claude`, `codex`, or `amp`) and make sure they’re on your `PATH`. Termio launches each one in a login shell, so anything your shell profile sets up will be available. Once an agent is installed, it shows up as a launchable option when you start a session. The same is true of the agents on another machine — Termio looks for them there and installs its hooks on that box. See [Devices](/docs/devices). ## The `termio` command (optional) Termio ships a small command-line tool. Open **Settings ▸ Server** and turn on **Command-line tool** to symlink `termio` onto your `PATH`. From then on `termio` in any directory opens it as a project, and `termio sessions …` lets your agents coordinate with each other. It’s entirely optional — see [Session control & the CLI](/docs/session-control). ## Where Termio keeps things Nothing is hidden, and nothing lives on a server of ours: | Path | What’s in it | | --- | --- | | `~/.termio/` | Your config: `keybindings.json`, custom agent manifests under `config/agents/`, and the `chats/` scratch directory | | `~/Library/Application Support/termio/` | App state and your `Themes/` folder | | `~/.local/bin/` | The session host on machines you set up over SSH — `termiod`, plus `termio` on Linux. On this Mac it ships inside the app bundle and is never installed here | | Each agent’s own config | Termio’s status hooks and its `termio` skill, written per agent | ## Uninstalling Quit Termio and drag it to the Trash. To remove what it wrote outside the bundle, turn off **Live agent status** and **Session control** under **Settings ▸ Agents** first — both toggles remove the hooks and the skill they installed on **this Mac**, leaving anything you wrote yourself alone. Then delete `~/.termio` and `~/Library/Application Support/termio` if you want the state gone too. Every other machine keeps what was installed on it until you clean it up there. Stop the server first, because removing the service alone leaves a daemon that something else started still running: ```bash termiod stop termiod service uninstall rm -f ~/.local/bin/termiod ~/.local/bin/termio ``` ## Next steps Open a project and launch your first agent in a real terminal. See which agents Termio recognizes out of the box. --- # Your first session URL: https://www.termio.sh/docs/first-session Markdown: https://www.termio.sh/docs/first-session.md A *session* in Termio is a real terminal running one thing — an agent, a dev server, or a plain shell. Here’s the path from a fresh launch to a working setup. ## Open a project From the welcome screen, choose **Open Project** and pick a folder. Termio adds it to the sidebar and remembers it under **Recent** for next time. A project is just a working directory — the agents you launch inside it start there. The project is filed under the workspace you’re in, and opens on the machine that workspace is on. With one machine there is nothing to choose; see [Workspaces](/docs/workspaces) and [Devices](/docs/devices) once there are two. ## Launch an agent With a project selected, start a new session and pick an agent — Claude Code, Codex, Antigravity, or any other CLI you have installed. Termio opens it in a real terminal at the project root. You type into it exactly as you would in any terminal; the agent’s TUI renders natively. The sidebar shows the session’s live status — **working**, **idle**, **done**, or **needs you**. [Concepts](/docs/concepts#status) explains all four; the one to watch for is **needs you**, which means the agent is blocked on a prompt or a permission and can’t continue without you. Kick off a long run in one session, switch to another, and let the sidebar tell you the moment the first agent needs you back. ## Split the window Most real work wants more than one pane. Split the current pane to the right with `⌘D` or downward with `⇧⌘D`, then drop another session into the new pane — an agent in one, your dev server in another, a shell in a third. Panes tile as a tree, so you can nest splits into any layout. To rearrange a layout, hover a pane’s header and drag the grab handle that appears. A highlight previews where the pane will land: drop on another pane’s left, right, top, or bottom half to place it on that side, or on the center to swap the two panes. Move focus between panes with `⌥⌘` + an arrow key, and temporarily zoom the focused pane to fill the window with `⇧⌘↩`. `⌘W` **closes** the focused session, the way it closes a browser tab, and its pane collapses with it. Every shortcut is rebindable — see [Keyboard shortcuts](/docs/keyboard). ## A note on session lifetime Sessions are live processes, not saved transcripts — and the process doesn’t belong to the window. Every session runs inside `termiod`, the session host on the machine that owns it, and Termio attaches to it. Close the window and the agent keeps working; reopening from the Dock finds it where you left it. Long runs survive a relaunch, too. The daemon keeps the process; the app reattaches to the session it left rather than starting a new one — same process, same output. Ending a session is a deliberate act: `⌘W` closes the focused one. Everything else — closing the window, quitting Termio, losing an SSH connection, relaunching the app — detaches rather than kills. [What survives](/docs/persistence) lays out every case, including the ones that do cost you a process. ## Next steps Read live status across a fleet and switch without losing your place. Give each line of parallel agent work its own isolated checkout. Browse files, read the diff, and search the project beside the terminal. Check in on a running session from your phone. --- # Concepts URL: https://www.termio.sh/docs/concepts Markdown: https://www.termio.sh/docs/concepts.md Termio is a terminal for running several coding agents at once. Everything in the app is built from six nouns and one status model. Learn these and the rest of the docs read quickly. ## Device A device is a machine you can reach: this Mac, the Mac mini on the same desk, a VPS. Termio reads `~/.ssh/config` to find them and shells out to the system `ssh` to get there — it never provisions a machine and never routes your traffic. This Mac is a machine like any other, so the level stays invisible until you add a second one. See [Devices](/docs/devices). ## Workspace A workspace is a named scope in the sidebar, holding projects and loose terminals. Switching to one swaps what the sidebar lists, and each workspace remembers the session you left it on. Every workspace belongs to exactly one machine, which is how the machine question gets answered once, at the top, instead of again for every project. `⌘1` through `⌘9` switch between them. See [Workspaces](/docs/workspaces). ## Project A project is a folder on a machine — usually a repository. It’s the unit the sidebar groups by, and the working directory every session inside it starts in. Opening a project doesn’t copy or index anything. Termio remembers the path and reads git for the rest. ## Session A session is one real terminal running one thing: an agent, a dev server, or a plain shell. Each session owns a PTY — the same kind of terminal `ssh` or Terminal.app gives a process — and Termio renders it with [libghostty](https://ghostty.org), Ghostty’s terminal core. Three properties matter: - **A session keeps running when you look away.** Switch to another session, hide the window, or use another app; the agent keeps working. Its output is buffered and repainted when you come back. - **A session lives on its machine, not in the app.** Every session — including the ones on this Mac — runs inside `termiod`, the session host, and the app is a viewer that attaches to it. Closing the window leaves the agent working, and a dropped SSH connection detaches you rather than killing anything. Only **Close Session** ends one on purpose. [What survives](/docs/persistence) is the full table. - **A session is addressable.** Every session has a stable URL, `termio://session/`, which is what the menu-bar tray, a notification, and the `termio` CLI all use to bring one to the front. ## Pane A pane is a session’s slot on screen. One session normally fills the window, and splitting *groups* another session beside it — an agent on the left, a dev server and a shell on the right. Panes are a view concern, not a second kind of session: the thing in a pane is a full session with its own sidebar row and its own status. That’s why the verbs are **Group with** and **Ungroup** rather than “split” and “close pane” — grouping changes how sessions are arranged, not what they are. See [Keyboard shortcuts](/docs/keyboard#panes) for the bindings. ## Worktree A git worktree is a second checkout of the same repository on its own branch. When two agents work one repo at once, worktrees are what keep them out of each other’s files. Termio reads them from git (`git worktree list`) rather than tracking its own copy, and shows each one as a nested folder under the project. Create one in the app or with `git worktree add` on the command line — either way both agree, because git is the source of truth. See [Git worktrees](/docs/worktrees). A repository checked out on a machine is a *project* here, and a second checkout of it is a *worktree*. Workspace is reserved for the named scope in the sidebar, even though some other tools use it for the folder. ## Status Every session reports what it’s doing right now. There are four states, and the distinction between the last two is the point of the whole model: | Status | Meaning | How it looks | | --- | --- | --- | | `idle` | Nothing pending, or you’re already looking at it. | No mark | | `working` | The agent is processing a turn. | The comet replaces the session’s icon | | `done` | The agent finished while you were elsewhere. | A green dot — *ready for you* | | `needs-you` | The agent is blocked on you: a permission prompt, a question. | An orange ring — *waiting on you* | A finished turn is `done`, never `needs-you`. Conflating the two is what makes a fleet of agents feel like a pile of alarms: if everything demands attention, nothing does. Termio keeps “ready” calm and reserves the loud state for an agent that genuinely cannot continue without you. These statuses roll up: a project’s row summarizes its sessions, and the menu-bar tray summarizes everything, so you can watch a fleet from another app. ### Where the signal comes from Termio doesn’t guess from pixels where it doesn’t have to. It reads, in order of authority: 1. **The agent’s own hooks.** On first run Termio writes its status hooks into the config of each agent that supports them, so the agent reports its own turns. Nothing for you to configure. See [Session control](/docs/session-control). 2. **In-band terminal signals.** Progress and title sequences an agent already emits (`OSC 9;4`, `OSC 777`) are read straight off the stream — that’s how Grok reports busy and idle without hooks. 3. **The screen, as a last resort.** For an agent with neither, Termio watches the pane for the shape of a prompt waiting on input, and promotes a status only after the reading holds. Open a project, start an agent, split the window. The sidebar as a control surface for a fleet. The scope that decides which machine a project opens on. Put a workspace on another box and set up the session host there. --- # The sidebar URL: https://www.termio.sh/docs/sidebar Markdown: https://www.termio.sh/docs/sidebar.md Everything you run in Termio lives in the sidebar on the left. It’s one scrollable list, grouped into sections, where every row is a live session reporting what it’s doing right now. Click a row to bring it forward; the rest keep running in the background. ## One workspace at a time The sidebar is scoped to a workspace: the switcher in its toolbar names the one you’re in, and the list below holds only what’s filed under it. Switch with `⌘1` through `⌘9`, from the switcher, or from `File ▸ Workspace`. Each workspace comes back to the session you left it on, with its panes and its inspector tab. A workspace belongs to one machine, so switching workspaces is also how you move between machines — the switcher names the machine beside any workspace whose own name doesn’t already say it. See [Workspaces](/docs/workspaces) and [Devices](/docs/devices). ## Sections The sidebar groups your sessions by what they are: - **Terminals** — standalone shells and agents you start with `⌘T`, not tied to a project. Your scratchpad. - **Chats** — throwaway agent sessions for quick questions (see below). - **Projects** — the folders you’ve opened. Each project expands to the sessions running inside it, and any git worktrees nest one level deeper. The tree is three levels: project → worktree → session. See [Git worktrees](/docs/worktrees). Empty sections stay hidden, so the list only shows what you’re actually using. Sessions the machine is hosting that Termio didn’t start — one you began over plain `ssh`, or launched with `termiod` on the box — appear in the list automatically as ordinary sessions, filed into the project whose folder they run in, or under **Terminals** otherwise. There is no separate section to learn: a session is a session, wherever it came from. If a machine can’t be reached, the device switcher says so in `ssh`’s own words rather than going quiet. ## Status Every session row carries a status dot that updates live. There are four states: | Status | Meaning | | --- | --- | | **working** | The agent is actively producing output. | | **idle** | Nothing pending, or you’re already looking at it. | | **done** | Finished a task while you were on another session — a calm “ready” cue, so you notice a completed run without it shouting for attention. | | **needs you** | Blocked on a question or a permission prompt. Go back. | The distinction between **needs you** and **done** is the point: *needs you* means an agent is stuck and can’t proceed without you; *done* means it finished on its own and is just letting you know. [Concepts](/docs/concepts#status) covers the model and where each signal comes from. Start a long run, switch away, and let the status dots do the watching. When a row flips to **needs you** you know to jump back; when it flips to **done** the work landed while you were elsewhere. ## Chats The **Chats** section holds scratch agent sessions — for when you want to ask an agent something without opening a project. Press `⌘N` for a **New Chat** and it starts in a shared scratch directory (`~/.termio/chats`), never in whatever folder you happen to be in. Close them individually, or clear the lot from the section’s menu. Treat them as disposable: quick questions, throwaway experiments, nothing you need to keep. ## Switching without losing your place Because each session is a real process running on its machine, an agent doesn’t pause when it’s off-screen — or when you switch workspaces, or close the window. Click between rows freely, or — once you have more than a handful — jump straight to any session by name with `⇧⌘O`. See [Navigation](/docs/navigation). Rows move by drag. Dropping one in the middle of another groups the two sessions into one split; dropping on a row’s top or bottom edge reorders it into that gap. A line means *between these*, a lifted row means *into this one*. Dragging a row onto a pane groups it in there instead. --- # Workspaces URL: https://www.termio.sh/docs/workspaces Markdown: https://www.termio.sh/docs/workspaces.md A workspace is a named scope in the sidebar. It holds projects and loose terminals, and switching to it swaps what the sidebar lists — the projects filed under it, its worktrees, its sessions, and nothing else. It’s the level between a [device](/docs/devices) and a project: every workspace belongs to one machine, and everything filed under the workspace inherits it. ## Switching Switch with `⌘1` through `⌘9`, or from the switcher in the sidebar’s toolbar. `File ▸ Workspace` carries the same rows. The digits are positional: they follow the order of the list, so what `⌘2` reaches changes when the list does. That’s also why they aren’t rebindable — a binding whose target moves isn’t one you can keep. The switcher draws each digit beside its name so you read the number rather than memorize it. A workspace remembers the session you left it on, with its panes, its split group, and its inspector tab. Coming back to a scope puts your own work back on screen rather than whatever sorts first. Where a workspace’s name doesn’t already say which machine it’s on, the switcher names the machine beside it — so switching workspaces is also how you move between machines. ## Managing them **Settings ▸ Workspaces** lists every workspace with the machine it’s on and how many sessions it holds. Renaming and removing live there, beside the workspace they act on, rather than in the switcher — a menu that shows the list one checked row at a time can only ever offer those verbs for the workspace you’re already in. Removing a workspace closes its sessions. The folders on disk are left alone. New workspaces come from `File ▸ Workspace ▸ New Workspace…` or the same row in the switcher. With more than one machine known, that row asks which machine the new workspace is on. A workspace can’t span two machines. A checkout already lives on exactly one box, so a workspace covering both was never more than a visual grouping over leaves that disagreed. Work that touches a repo here and a repo on the VPS is two workspaces. Add a machine, set up the session host, and see what reaches across. What a workspace scopes: projects, worktrees, and every session’s status. --- # Command palette & Open Quickly URL: https://www.termio.sh/docs/navigation Markdown: https://www.termio.sh/docs/navigation.md Termio has two overlay panels that keep your hands on the keyboard. One runs *actions*, the other jumps to *things*. Both are fuzzy-matched, so you type a few characters and pick from the narrowed list. ## Command Palette — `⇧⌘P` Press `⇧⌘P` to open the Command Palette: a searchable list of every action in the app, each showing its own keyboard shortcut. Type to filter, arrow to a command, and press Return to run it — splitting a pane, opening Settings, toggling the inspector, starting an agent, whatever the app can do. Hold `⌘` while pressing Return (`⌘↩`) to run a command **and keep the palette open**, so you can fire several actions in a row without reopening it. It doubles as a way to *learn* the shortcuts: every command lists its key equivalent right there, so the palette you reach for today teaches you the binding you’ll reach for tomorrow. ## Open Quickly — `⇧⌘O` Press `⇧⌘O` to jump somewhere. Open Quickly searches across three kinds of target at once, grouped under headers: - **Sessions** — every running session, by title. The fastest way to switch when the sidebar is full. - **Recent** — projects and folders you’ve opened before, to reopen in a keystroke. - **Files** — once you start typing, the current project’s files (from `git ls-files`, so ignored paths stay out), opened straight into the editor. It’s the “jump to anything” companion to the Command Palette’s “do anything.” For searching *inside* files rather than by name, use the inspector’s [Search tab](/docs/inspector#search). ## Everything is rebindable `⇧⌘P` and `⇧⌘O` are the defaults, but like every shortcut in Termio they can be changed under **Settings ▸ Keyboard**. See [Keyboard shortcuts](/docs/keyboard). --- # The inspector URL: https://www.termio.sh/docs/inspector Markdown: https://www.termio.sh/docs/inspector.md The inspector is Termio’s side panel for everything *around* the terminal: files, changes, and search. It sits beside your sessions so you can glance at code without switching apps. Every pane reads the project on the machine the selected session runs on. Point at a session on a VPS and the tree, the search and the diff are that box’s — read over the session host, not copied down. ## Files The file tree mirrors the working directory. Single-click a file to open it in a built-in editor with syntax highlighting, soft-wrap, and a live line/column readout. Edits auto-save — there’s no save button, and a file read from another machine is written back to that machine. Images, PDFs, and HTML open in a Quick Look preview instead. ### Editing The editor is a real one, not a viewer: - **Find and replace** with the Mac find keys — `⌘F`, `⌘G`, `⇧⌘G`, `⌘E`, and replace or replace all. - **Comment lines**, and **move** or **copy** a line or a selection up and down. - **Tab** indents a block and **Return** keeps the indent you were on, with indentation guides drawn behind the code. - Other occurrences of the word under the caret get a quiet wash, so you can see where a name is used without searching for it. ## Changes The git pane has three read-only tabs: - **Changes** — the current diff, rendered as a clean unified view. - **Compare** — this branch against the branch it would merge into: the files and commits a pull request from it would carry, diffed from the merge base so the list matches what the forge will show. - **History** — recent commits with author avatars, expandable file lists, and per-commit diffs. It’s deliberately read-only: writing to git (commit, push, PR) stays in the terminal, where the agent and your own commands already live. ## Search The inspector’s **Search** tab runs a content search across the project (backed by `git grep`) and jumps you straight to the matching line in the editor — it searches inside files, not just filenames. This is the “find in files” you know from an editor. Results read as excerpts, painted where the matcher hit, so you can tell which match you want before opening anything. Search follows the session too: a checkout on another machine is searched there, and a `cd` in that session moves the search with it. Looking for a file *by name* instead? That’s [Open Quickly](/docs/navigation) (`⇧⌘O`), which also jumps between sessions and recent projects. ## Opening files from the terminal Cmd-click a file path in terminal output — even inside an agent’s TUI — and Termio opens it in the editor. Cmd-click a URL to open it in your browser, or a `file://` link for a read-only preview overlay. --- # Appearance URL: https://www.termio.sh/docs/appearance Markdown: https://www.termio.sh/docs/appearance.md Open **Settings** with `⌘,`. It’s a standard macOS settings window with a sidebar of tabs; **Appearance** is where the terminal’s look lives. ## Theme Termio uses Ghostty-format themes, so anything from the wider Ghostty theme ecosystem works. Pick a light and a dark theme from **Settings ▸ Appearance** and Termio follows the system between them. The two slots list the themes you have — Termio’s own canvas plus whatever is in your Themes folder — not a catalog of hundreds you never chose. If you already use Ghostty, there’s nothing to pick on a fresh install: Termio reads your `~/.config/ghostty/config` on first launch and starts with the font and theme you chose there. ## Getting more themes **Browse Themes…** opens a store of 69 well-known schemes — Dracula, Catppuccin, Gruvbox, Rose Pine, Solarized, and the rest — each one picked to stay readable through a working day, and no two unrelated ones close enough to arrive twice under different names. Flavors of one family are the exception: all four Catppuccins are there, because the flavor name is exactly what its users pick between. **Install** writes that theme into your Themes folder and selects it in the light or dark slot it belongs to. Nothing is downloaded: the schemes ship inside Termio. If a file of the same name is already in your Themes folder, Install stops and asks before replacing it — a theme you dropped in yourself is yours. **Remove** deletes the file, and asks first if you have edited it. To switch between the themes you have with your terminals as the preview, open the Command Palette (`⇧⌘P`) and pick **Change Theme…** — arrowing through the list restyles your open terminals live, Return keeps the theme, Escape puts it back. To add a theme the store doesn’t carry, drop a Ghostty-format theme file into: ``` ~/Library/Application Support/termio/Themes/ ``` The **Open Themes Folder** button takes you straight there, and **Reload** picks up new files without a restart. ## Font Choose any monospaced font installed on your Mac, set its size, and toggle **Thicken glyphs** if you like a heavier weight. Font size also has live shortcuts while you work — `⌘=` to grow, `⌘-` to shrink, `⌘0` to reset. ## Cursor & window The same tab tunes the cursor (block, bar, or underline; steady or blinking) and the window itself — padding around the terminal, and background opacity and blur for a translucent window over your desktop. The app’s own chrome lives here too: the sidebar’s font and density. ## The rest of Settings The other tabs cover the rest of the app: - **General** — language, the GitHub integration behind the inspector’s Issues pane, and a Privacy switch for anonymous daily usage statistics. - **Terminal** — scrollback history and text selection. - **Server** — this Mac’s own session host, and the switches for the `termio` [command-line tool](/docs/cli). See [The Termio server](/docs/server). - **Remote Hosts** — every machine you reach from this Mac, one row each, drilling into how that machine is reached and what Termio has installed on it. See [Devices](/docs/devices). - **Workspaces** — every workspace with the machine it’s on, and the place to rename or remove one. See [Workspaces](/docs/workspaces). - **Keyboard** — rebind any shortcut; see [Keyboard shortcuts](/docs/keyboard). - **Agents** — add agents, enable them, turn on [session control](/docs/session-control), and set task-completion notifications. Values that differ per machine — where a CLI lives, whether it’s there — appear as one row per machine on the agent’s own page, never behind a picker. - **Usage** — token usage for agents you sign in to (Claude Code, Codex). Termio reuses each CLI’s own credentials to show you where you stand against your limits; it stores nothing of its own. - **Mobile** — pair your iPhone with a machine, and publish an address it can reach. See the [iOS app](/docs/iphone). - **Community** — where to find the project: GitHub, Discord, and the WeChat group. Machines are rows, never a picker. A setting that differs per machine lists one row per machine where it belongs — the agent’s own page for a command path — and a machine’s whole story is reached by navigation: **Server** for this Mac, a row under **Remote Hosts** for any other. --- # Keyboard shortcuts URL: https://www.termio.sh/docs/keyboard Markdown: https://www.termio.sh/docs/keyboard.md Termio is built to run from the keyboard. The tables below are generated from the app’s command catalog, so they list exactly what this version ships. Every command is rebindable — see [Customizing](#customizing). ## Sessions and projects New Terminal opens the shell where you already are — the working directory of the focused session — and puts the new session beside it, in the same project. New Terminal at Home always starts at `~`; it ships unbound, so bind it under [Customizing](#customizing) if that is the one you reach for. Cycling follows the sidebar’s visual order, so `⇧⌘]` moves to the next session as you see it listed, not in the order the sessions were created. ## Workspaces `⌘1` through `⌘9` switch to the first nine workspaces, in the order the switcher lists them. They aren’t in the table below and they aren’t rebindable, because they’re positional: what `⌘2` reaches changes when the list does, and a binding whose target moves isn’t one you can keep. The switcher draws the digit beside each name so you read the number instead of remembering it. See [Workspaces](/docs/workspaces). ## Finding things Both panels are fuzzy-matched. The Command Palette runs *actions*; Open Quickly jumps to *things* — sessions, projects, and files. See [Command palette & Open Quickly](/docs/navigation). ## Panes Split Left and Split Up ship unbound, the way Ghostty ships them: the two directions people reach for constantly get the keys, and the mirrored pair stays a menu verb until you bind it yourself. Both are always available from the Command Palette and the pane’s context menu. `⌘W` is **Close Session**, the way `⌘W` closes a tab in a browser: it ends the focused session, and its pane collapses with it. Once no session is left, `⌘W` closes the window — the app keeps running with every other session alive. **Ungroup** is the other half, and it ships unbound: it takes the focused pane out of the group and leaves the session running on its own. It’s in the View menu, the Command Palette, and the pane’s context menu, and you can give it a key in Settings ▸ Keyboard. Panes can also be rearranged with the mouse: hover a pane’s header, grab the handle that appears, and drop the pane on a neighbour. A highlight previews the drop — an edge half places the pane on that side, the center swaps the two. ## Branches and worktrees Termio takes these two bindings from GitHub Desktop’s Branch menu verbatim, where New Branch is `⇧⌘N`. Termio’s branch-creation verb is the worktree. ## View The system shortcuts you’d expect work as usual — `⌘C` / `⌘V` to copy and paste, `⌘A` to select all, `⌘,` for Settings, `⌘Q` to quit. The Mac find keys belong to the editor: `⌘F` opens the find bar, `⌘G` and `⇧⌘G` step through matches, and `⌘E` makes the selection the query. They’re fixed rather than rebindable — they only mean anything while a file is open, and the terminal never sees them. ## Customizing Open **Settings ▸ Keyboard** to rebind any command. The editor lists every action with its current binding and flags conflicts as you assign them. Your overrides are saved to: ``` ~/.termio/keybindings.json ``` Only the shortcuts you change are written there — the defaults ship with the app, so the file stays small and easy to read. A binding must include `⌘`. Without it, the chord would collide with what the terminal itself needs: an agent’s TUI expects `⌃C`, `⌥←`, and the rest to reach it untouched. Not sure what a command is called? Open the [Command Palette](/docs/navigation) (`⇧⌘P`) — it lists every action alongside its current shortcut, so you can find and run things before you commit them to muscle memory. --- # Running multiple agents URL: https://www.termio.sh/docs/agents Markdown: https://www.termio.sh/docs/agents.md Termio is built for running more than one agent at once. The sidebar is your control surface: every session is a row, and every row reports what its agent is doing right now. Any terminal will let you open several agents at once; what this buys you is not having to watch them. ## Built-in agents Termio recognizes the common coding agents out of the box: | Agent | CLI | | --- | --- | | Claude Code | `claude` | | Codex | `codex` | | OpenCode | `opencode` | | OpenCode 2 | `opencode2` | | Pi | `pi` | | Amp | `amp` | | Droid | `droid` | | Cursor | `cursor-agent` | | Copilot | `copilot` | | Kimi | `kimi` | | Qwen Code | `qwen` | | Antigravity | `agy` | | Crush | `crush` | | Cline | `cline` | | Hermes | `hermes` | | Grok | `grok` | Any of these that’s installed on your `PATH` appears as a launch option. A plain shell is always available too. Running something else? Any CLI can become a first-class agent — see [Custom agents](/docs/custom-agents). `PATH` means the machine’s own `PATH`: Termio looks for these on whichever machine the workspace is on, so a VPS offers what is installed there. **Settings ▸ Agents** says so in the row — *Not installed on `devbox`* — rather than hiding it behind a machine picker, and the machine’s own page under [Remote hosts](/docs/remote-hosts) is where you fix it. Termio launches each agent through your real login shell, so everything your shell profile sets up — version managers, `PATH` additions, credentials — is there exactly as it would be in iTerm. If an agent is installed but Termio says it isn’t found, that is almost always a `PATH` question; the Agents tab says so and takes an absolute path in the command field. ## What each agent reports Status isn’t guessed the same way for every agent, and the difference is worth knowing when a dot doesn’t move. Each agent declares its own channels in its [manifest](/docs/atp): **hooks** are the agent reporting its own turns and are authoritative wherever they exist; **title** is an in-band signal read off the terminal title; **progress** is the `OSC 9;4` progress sequence. An agent that declares none of these is read from the pane itself — from whether its output is still moving, not from matching words on the screen, which only happens for an agent whose manifest spells the patterns out. | Agent | Status signal | Resumes its conversation | | --- | --- | --- | | Claude Code | Hooks + title | Yes | | Codex | Hooks + title | Yes | | Grok | Hooks + title + progress | Yes | | Qwen Code | Hooks + title | Yes | | Pi | Hooks | Yes | | OpenCode | Hooks (plugin) | Yes | | OpenCode 2 | Hooks (plugin) | — | | Amp | Hooks | — | | Cursor | Hooks | — | | Copilot | Hooks | — | | Kimi | Hooks | — | | Cline | Hooks | — | | Droid | Hooks | — | | Crush | Screen rules + progress | — | | Antigravity | Output activity | — | | Hermes | Output activity | — | “Resumes its conversation” means reopening the session puts the agent back in the *same* conversation rather than a blank one — see [What survives](/docs/persistence). Agents without it come back as a fresh run in the same directory. Hooks are installed for you, per machine, when **Live agent status** is on. An agent on a box you set up before turning that on has none, which is the usual reason a remote session’s dot never moves — see [Status hooks & session control](/docs/session-control). ## Reading status at a glance Every session row carries a live status dot — **working**, **idle**, **done**, or **needs you**. The [sidebar guide](/docs/sidebar#status) explains what each state means, and [Concepts](/docs/concepts#status) covers where the signal comes from. The distinction that matters is **done** versus **needs you**. A finished turn is *done* — a calm cue that work landed while you were elsewhere. *Needs you* is reserved for an agent that genuinely cannot continue: a permission prompt, a question, a choice. Conflating the two is what turns a fleet into a pile of alarms, so Termio keeps “ready” quiet and saves the loud state for the one case where walking away costs you time. Statuses roll up. A project’s row summarizes the sessions inside it, and the menu-bar tray summarizes everything — so you can be in another app entirely and still know the moment something needs you. ## Skipping permission prompts Many agents ship a flag that stops them asking before each tool call. Where one exists, Termio wires it to a toggle rather than making you remember the spelling: turn it on for an agent in **Settings ▸ Agents** and Termio appends that agent’s own flag when it launches. It is a standing decision about that agent, so unlike its command path it is **not** per machine — the flag goes on every launch of that agent, on every machine. Read the flag before you turn the switch on: these are the vendors’ own escape hatches and some of them give up more than the prompt. Codex’s is `--dangerously-bypass-approvals-and-sandbox`, which drops its sandbox along with the approvals. Termio appends what the vendor documents; it adds no sandbox of its own. An agent running unattended is exactly the one you want a real status channel on. Turn on hooks before you turn off prompts, or you lose the signal that would have told you it stopped. ## Switching without losing your place Clicking a session brings it forward instantly; the others keep running in the background. Because each session is a real process hosted on its machine, an agent doesn’t pause when it’s off-screen — or when you switch workspaces, or close the window. Kick off a long run in one, switch to another, and come back when the first raises its hand. When you have more than a handful of sessions, `⇧⌘O` jumps straight to any of them by name — see [Navigation](/docs/navigation). ## Keeping two agents out of each other’s files Two agents in one checkout will collide: same working tree, same index, same branch. The fix isn’t coordination, it’s separation — give each line of work its own git worktree and point an agent at each. Termio reads worktrees from git itself and nests them under the project, so this is a right-click rather than a setup step. See [Git worktrees](/docs/worktrees). ## Letting agents drive each other Once a fleet is normal, the next thing you want is for one agent to run the fleet. `termio sessions` is that API: an agent can list its siblings, spawn new ones on a prompt, answer a session that’s blocked, and block until a turn settles — all over a local socket, scoped to its own project. ```bash termio sessions list termio sessions spawn "update the changelog for 0.54" --agent codex termio sessions watch ``` It’s opt-in, and turning it on also installs a small skill that teaches your agents the commands exist. See [Command-line tool](/docs/cli) for the full verb reference and [Session control](/docs/session-control) for the switch. Teach Termio any CLI with a small JSON manifest. What happens to a running agent when you close, quit, or reboot. The `termio sessions` orchestration API, verb by verb. --- # Custom agents URL: https://www.termio.sh/docs/custom-agents Markdown: https://www.termio.sh/docs/custom-agents.md Termio ships with the common coding agents built in, but the catalog is open: any command-line tool can become a first-class agent. You describe it in a small JSON manifest, and from then on it appears in the new-session menu, the Settings ▸ Agents list, and the sidebar with its own name, icon, and live status — exactly like Claude Code or Codex. You don’t *need* a manifest to run an arbitrary CLI — you can always launch a plain shell and run it there. A manifest is what promotes it to a real agent with an icon and status detection. ## Add an agent Drop a JSON file into your config folder, named after the agent’s `id`: ``` ~/.termio/config/agents/.json ``` A minimal manifest needs only an `id`, a `name`, and the `command` to launch: ```json { "id": "myagent", "name": "My Agent", "command": "myagent --fancy" } ``` Termio reads the folder at launch and again whenever you bring the app forward with Agents settings open, so a new manifest usually appears without a restart. The agent then shows up wherever you start a session; enable or reorder it from **Settings ▸ Agents** like any other. Where its CLI lives is a fact about a machine, so the command path is one row per machine: open the agent under **Settings ▸ Agents** and its **Launch** section lists every machine you have, each with its own path field. See [Remote hosts](/docs/remote-hosts) for the same rows read from a machine’s side. Use a built-in’s `id` (for example `claudeCode`) as your filename to **override** it — handy for launching Claude Code through a wrapper script while keeping its icon and hooks. ## Manifest fields | Field | Required | Meaning | | --- | --- | --- | | `id` | ✓ | Stable slug and persistence key (letters, digits, `-`, `_`, `.`). | | `name` | ✓ | Display name in menus and the sidebar. | | `command` | | The program and arguments to launch. Omit it to open your login shell. The binary must be on your `PATH`. | | `icon` | | An icon — see below. Defaults to a generic glyph. | | `status` | | Screen-scrape rules for live status — see below. | | `resume` | | How a reopened session continues its exact conversation — an object of launch-argument templates and a session-store descriptor. See the [Agent Terminal Protocol](/docs/atp#resume). Omit it and every launch starts fresh. | | `permissionBypassFlag` | | A flag appended when you toggle “skip permission prompts” for this agent. | | `tint` | | A hex color (e.g. `"#14B8A6"`) used to tint the “working” spinner. | ### Icon Point at an SF Symbol, or an image dropped alongside the manifest: ```json "icon": { "symbol": "sparkles" } ``` ```json "icon": { "path": "myagent.png" } ``` For `path`, put the image file next to the `.json` in the same folder. ### Live status If your agent doesn’t ship a hook system, Termio can still read its status by matching regular expressions against the terminal screen. List patterns that mean “busy” under `working` and patterns that mean “waiting on you” under `attention`: ```json "status": { "working": ["thinking", "Running"], "attention": ["approve\\?", "Continue\\? \\(y/n\\)"] } ``` A line matching `attention` flips the session to **needs you** (the key keeps the protocol’s name; it wins over `working`, since a prompt can sit under a still-spinning header); a line matching `working` marks it **working**. See the [sidebar status reference](/docs/sidebar#status) for what each state means. Agents that ship their own hook system (like the built-ins) get more precise status through hooks instead of screen-scraping. That path is covered under [Session control](/docs/session-control) — for a custom agent, the `status` regex above is the simplest route. --- # Status hooks & session control URL: https://www.termio.sh/docs/session-control Markdown: https://www.termio.sh/docs/session-control.md Two related, opt-in switches sit behind the live statuses you see in the sidebar: **status hooks** that let agents report what they’re doing, and the **session control** skill that teaches them to drive sibling sessions. Neither is required to use Termio; both make a fleet of agents easier to run. The commands they expose are documented in [Command-line tool](/docs/cli). ## Live agent status Termio can install small **status hooks** into your agents’ own config files (Claude Code, Codex, Cursor, and the plugin-based agents). When an agent starts working, finishes, or stops to ask a question, the hook reports that to Termio, so the sidebar’s [status dots](/docs/sidebar#status) reflect exactly what’s happening instead of being guessed from output. Turn it on from **Settings ▸ Agents ▸ Live agent status** (Termio also offers it once on first launch). Toggling it off removes the hooks again, leaving any hooks you added yourself untouched. The switch says whether you want the feature; each machine installs it for its own agents. Open a machine under **Settings ▸ Remote Hosts** and press **Set Up** — or **Reinstall Hooks and Skill** on its **Agents** page — to put the hooks on that box. That is what gives an agent running on a VPS the same status dots as one running here. See [Devices](/docs/devices). ## Session control **Settings ▸ Agents ▸ Session control** turns on the `termio sessions` orchestration API below, and teaches your agents it exists by installing a `termio` **agent skill** into each agent’s skills folder (`~/.claude/skills`, `~/.codex/skills`). The skill loads on demand — an agent carries only its one-line description until a task actually involves driving sibling sessions — and Termio re-asserts it on every launch, so app updates propagate and hand-edits heal automatically. Toggling it off removes the skill again. Running an agent Termio doesn’t auto-configure? The same skill is published at [termio.sh/skill.md](https://termio.sh/skill.md) and installable straight from the repo: ```bash npx skills add termio-sh/termio --skill termio ``` Both toggles only write files on your Mac — hook entries in agent configs and the skill file. Nothing leaves your machine: status reports and session commands travel over a local socket to the Termio app, and nowhere else. --- # Git worktrees URL: https://www.termio.sh/docs/worktrees Markdown: https://www.termio.sh/docs/worktrees.md When you have several agents working a repository at once, you don’t want them stepping on each other’s files. Git worktrees give each line of work its own checkout on its own branch — and Termio surfaces them natively. ## How Termio sees worktrees Termio derives worktrees directly from git (`git worktree list`), so any worktree you create — from the command line or elsewhere — shows up in the sidebar automatically. Each one appears as a nested folder under its project, labeled with its branch, forming a three-level tree: project → worktree → sessions. The list stays in sync: Termio reconciles it when a project loads, when the app regains focus, and when the git directory changes. ## Working in a worktree Open a session inside a worktree and it starts in that worktree’s directory on that worktree’s branch. Point one agent at your `main` checkout and another at a `feat/…` worktree, and their edits never collide — each has its own working tree, its own index, its own branch. An empty worktree still appears in the tree. Open its context menu and choose **New Terminal** or **New Agent Session** to start there. ## Reviewing the work The inspector’s git pane is read-only by design: three tabs — **Changes**, **Compare**, and **History** — let you read the diff, see what the branch would merge, and browse commits per worktree. Committing, pushing, and opening pull requests happen in the terminal, where you (or the agent) already are — see the [inspector guide](/docs/inspector). --- # Devices URL: https://www.termio.sh/docs/devices Markdown: https://www.termio.sh/docs/devices.md Termio runs your agents on machines you already own: this Mac, the Mac mini on the same desk, a VPS you pay for. There is no Termio cloud to provision from and nothing to sign into — a machine is a machine Termio can `ssh` to. Everything in the app files under one hierarchy: ``` Device a machine you can reach, this Mac included └ Workspace a named scope in the sidebar └ Project a checkout on that machine └ Session one real terminal ``` Each level belongs to exactly one level above it. That is what makes the machine question answerable once, at the top, instead of being asked again for every project and every pane. The level that answers it is the [workspace](/docs/workspaces). ## Adding a machine This Mac is a device like any other, and it’s the one you already have. Until you add a second, the level stays out of your way — `New Terminal` and `Open Project…` are plain verbs, because there is only one place they could mean. Termio reads `~/.ssh/config` and never rewrites it behind your back. Anything you can already `ssh` to is a machine Termio can reach: - **File ▸ Connect to…** lists the aliases in your config that Termio hasn’t worked on yet. Connecting opens a terminal there. - **Add Host…** appends a `Host` block for a box that isn’t in your config yet. Lead with the address; the name is what you’ll type after `ssh`. A key is the credential that works everywhere, including for the sessions running on the box. A password is saved to your Keychain, never to `ssh` config, and read only when `ssh` asks for it. ## Setting up a machine Open **Settings ▸ Remote Hosts**, pick the machine, and press **Set Up**. Termio copies the session host — `termiod`, plus the `termio` CLI on Linux — into `~/.local/bin` over SSH. No root, no package manager, nothing to add to your shell profile. It then looks for the agent CLIs installed there and writes Termio’s hooks and its agent skill for each one, so an agent on a VPS reports **working**, **needs you**, and **done** exactly the way a local one does. The same button is the update, and the machine’s page reduces the whole chain to one line — *Ready*, *Set up this host*, or *Update ready*. [Remote hosts](/docs/remote-hosts) is the operating manual: testing the route, why a password isn’t enough, what each rung installs, and how to repair one. Termio never provisions a machine, never runs a control plane, and never routes your traffic. It shells out to the system `ssh`, with your `~/.ssh/config` and your keychain as the only authorities. Your code stays on hardware you control — which is the only trust story that survives giving an agent a shell on a private repo. ## Sessions live on the machine Every session runs inside `termiod` on the machine that owns it — including the sessions on this Mac, which run in the local daemon. The app is a viewer that attaches and detaches. That’s why closing the window leaves everything running, and why a remote session isn’t tied to the SSH connection that opened it: dropping the link detaches you, it doesn’t kill the agent. Reattaching restores the screen. Only **Close Session** (`⌘W`) ends a session on purpose. Quitting Termio detaches instead, and the next launch reattaches. ## What reaches across Once a machine is set up, the panes that read a project read it on the machine the project is on: - **Files** — browse the checkout, open a file, and save it back to the machine it was read from. - **Search** — search a checkout on the machine, follow its `cd`, and open what the search found. - **Changes, Compare, History** — the git pane reads that machine’s repository, and stays as read-only there as it is here. - **Agents** — the same manifests, hooks, and skill, installed on the box. Where an agent’s CLI lives is a fact about one machine, so it’s answered on that machine’s own page under **Remote Hosts** rather than on a page about agents. - **Paste an image** into an agent running on the machine and Termio carries the file across. Sessions started outside Termio show up by themselves — `ssh` in, launch something with `termiod`, and the sidebar files it as an ordinary session. The machine’s own roster is the source, so nothing running on the box goes missing. Adding a host, testing the route, and reading the one line that says ready. The scope that decides which machine a project opens on. The hooks that make status honest, on this Mac and on every machine. Pair the phone with a machine and reach its sessions from anywhere. --- # Remote hosts URL: https://www.termio.sh/docs/remote-hosts Markdown: https://www.termio.sh/docs/remote-hosts.md [Devices](/docs/devices) is the model: a machine is a machine you can `ssh` to, and everything files under it. This page is the operating manual for the pane that does the work — **Settings ▸ Remote Hosts**, one row per machine, each opening onto that machine’s own page. The shape is worth knowing before the details. A machine is two things at once: a **route** (how you reach it) and an **identity** (what’s installed on it). You can’t install anything on a box you can’t reach, so the pane reads in that order, and so does everything below. ## Adding a host Termio reads `~/.ssh/config` and never rewrites it behind your back. Anything you can already `ssh` to is a machine Termio can reach — there is nothing to import. - **File ▸ Connect to…** lists the aliases in your config Termio hasn’t worked on yet. Picking one opens a terminal there. - **Add Host…** — at the end of **File ▸ New SSH Connection**, and on the Remote Hosts pane — appends a `Host` block for a box that isn’t in your config yet. Lead with the address; the name is what you’ll type after `ssh`. If your other hosts already share a user and a key, that pair is offered rather than retyped. A host you add this way is an ordinary `~/.ssh/config` entry afterwards. The machine’s page carries a **Host block ▸ Edit** row that opens the entry it was defined in, so the config stays the one place the answer lives. ## Testing the route The **Reached by** section names the destination and says how `ssh` will sign in — either the key your config pins, or “the keys `ssh` offers on its own”. Beside it is a **Test** button. It reports one of four outcomes, the last of which carries `ssh`’s own reason rather than a fixed label: | Outcome | What it means | | --- | --- | | Reachable | `ssh` connected and authenticated. Nothing to fix. | | Wants a password | The host offered password auth. The one failure with a one-click fix — see below. | | Auth failed | It answered, and refused the credentials. A key problem, not a network one. | | The reason `ssh` gave | Everything else — refused, timed out, unknown host — reported in `ssh`’s words. | Test with the machine’s own alias, the same way the rest of the app reaches it. If `ssh mybox` fails in your terminal, this will fail too — Termio carries no credentials of its own. ## Why a password isn’t enough A host that takes a password will let you open a plain terminal and nothing else. The connections that carry sessions, the file tree, search, and the git panes all run non-interactively (`BatchMode=yes`), which means they can never answer a prompt. A password works exactly where a human is sitting there to type it, and fails everywhere else. So the pane says that plainly and offers the fix in place: **Set Up Key…** runs `ssh-copy-id` in a terminal with the key Termio would use. The host asks for your password once, there, and from then on every path can reach it. If `~/.ssh` has no key `ssh` would offer on its own, the row says so and points at `ssh-keygen` first. The key is yours and the command is the standard one. Termio never stores a password, never embeds an SSH implementation, and never writes a credential anywhere but where `ssh` and your keychain already keep them. ## Setting it up Getting a box ready is a real dependency chain — put the server there, find out which agent CLIs it has, write Termio’s hooks and skill into them. Showing that as four independent states would turn picking a machine into infrastructure triage, so the pane promises **one outcome** and keeps the rungs underneath it: | The line says | What it means | | --- | --- | | **Set up this host** | Nothing asked yet, or a rung is blocking. Press **Set Up**. | | **Checking…** | It’s running, and names the rung it’s on. | | **Ready** | Agents on that machine can run — and, with the integration switches on, report their status back here. | | **Update ready** | A newer server is staged, and the running one is holding it off because it has work in progress. | Pressing **Set Up** walks the ladder: copy `termiod` into `~/.local/bin` over SSH, probe for your agent CLIs, then bring the hooks and the skill into line with what the switches on **Settings ▸ Agents** ask for — installing each half, or removing it, per switch. With both off it deliberately installs neither, and the line then promises only that agents there can run. No root, no package manager, nothing added to your shell profile. When a rung fails, the line names **that rung** rather than “setup failed” — and only the first one. A machine with no `termiod` also has no hooks, and naming both invites you to fix the consequence instead of the cause. An agent CLI that isn’t installed there doesn’t block **Ready**. It’s a fact about that agent, not a fault in the machine — the machine is ready to run the agents it actually has. ## Agents on that box A remote machine’s page carries an **Agents** link — “what does *this box* have” — which is the same question as **Settings ▸ Agents** asked from the other axis. Opening it gives you: - **Command paths.** One row per agent, each with the machine’s own answer underneath: *Installed*, *Not installed*, or *Can’t check*. Leave a path empty to launch the agent the way that machine’s login shell would; fill it in (`/opt/homebrew/bin/codex`) when the binary lives somewhere the profile doesn’t add. Which agents appear at all is still **Settings ▸ Agents**. - **Installed by Termio.** What Termio wrote into each agent’s config on that machine — **Hooks** (so agents report their status) and the **Skill**. One **Reinstall Hooks and Skill** button repairs both; reach for it after hand-editing one, or when a status dot stops moving. The skill a box gets is not the one this Mac gets. Agents here learn `termio sessions`, scoped to their own project; agents on a machine learn `termiod list`, `create`, `send` and `kill`, scoped to that whole machine — because that is the command that exists there. *Can’t check* is a third answer on purpose, and it’s never a defect in the agent: it means Termio couldn’t reach the machine to ask. Reporting that as “not installed” would send you to reinstall a CLI that’s already there; reporting it as “installed” would promise a launch that can’t happen. Both switches themselves — **Live agent status** and **Session control** — live on **Settings ▸ Agents**, because wanting the feature is a preference. Where it gets installed is a machine operation, which is why it happens here, per machine. See [Status hooks & session control](/docs/session-control). ## Keeping a host current The same **Set Up** button is the update. It works from any state — nothing installed, an older build, a build the running daemon hasn’t picked up yet — and it puts the old binary back if the new one doesn’t answer. The machine’s page shows the version it’s actually running. An update that would interrupt work in progress doesn’t take it. The line reads **Update ready**, names the session holding it up, and offers **Update Anyway** beside it. Left alone, the new build takes over the next time the daemon stops on its own. Your idle terminals never hold an update back; a command still running or an agent mid-task does. From the command line the same reconcile is one verb: ```bash termiod deploy --host mybox ``` It ends in the same state whatever state it started in, so running it against a box that looks wrong is safe. See [The Termio server](/docs/server). ## When a host stops answering Work from the outside in — `ssh` first, then the daemon, then Termio: ```bash ssh mybox true # does SSH itself work? termiod list --host mybox # is the server answering there? termiod deploy --host mybox # reinstall and verify it ``` A dropped connection detaches you; it doesn’t kill anything. The sessions are on that box, in its daemon, and reattaching restores the screen — see [What survives](/docs/persistence). If they really did go away after a reboot, the box is missing a service: `termiod service install`. The device switcher reports an unreachable machine in `ssh`’s own words rather than going quiet, so the failure you read is the failure `ssh` had. More symptoms and their checks are in [Troubleshooting](/docs/troubleshooting). The model: what a machine is, and what reaches across to one. `termiod` itself — installing it, keeping it alive, looking at it. The two switches whose files land on every machine you set up. --- # The Termio server URL: https://www.termio.sh/docs/server Markdown: https://www.termio.sh/docs/server.md Every session runs inside `termiod`, the Termio server, on the machine that owns it — including the ones on this Mac. The Mac app, the iOS app and the CLI are all *viewers*: they attach to a session and detach from it. Nothing about a session belongs to the window you were looking at. That is the whole reason quitting Termio does not kill your agents. ## What it is `termiod` is a session host, not a window manager. It owns the PTY, keeps the scrollback, and hands a viewer a snapshot when one attaches. Locally a viewer reaches it over a Unix socket; remotely the app opens an SSH pipe to the same binary on the far box. One protocol, both ways — which is why a session on a VPS behaves like a session here. Panes, splits and layout are the app's business and never the server's. ## Getting it onto a machine Adding a box under **Settings ▸ Remote Hosts** and pressing **Set Up** copies one binary into `~/.local/bin` over SSH and starts it. There is no package to install and no root involved. ```bash termiod deploy # install or update it here, then verify termiod deploy --host mybox # same, over your own SSH config ``` `deploy` is the only install path, and it is a reconcile: run it against a box in any state and it ends in the same one. Termio reads `~/.ssh/config` to find the host, so a machine you can already `ssh` to needs nothing new. See [Devices](/docs/devices). ## Keeping it running ```bash termiod service install # launchd on macOS, systemd --user on Linux ``` Without a service the server lives as long as your login session. With one it comes back after a crash and after a reboot, which is what you want on a box you only ever reach remotely. ## Updating without dropping sessions A new build replaces the running one **in place**: the process keeps its id and its PTYs, and the sessions never notice. ```bash termiod handoff ``` That is also what happens when the app updates itself — the reason a Termio update does not cost you your running agents. See [What survives](/docs/persistence) for every case, including the ones a handoff cannot cover. ## Looking at it Three commands answer nearly every "is it me or is it Termio" question: | Command | What it tells you | | --- | --- | | `termiod status` | What this machine has: the binary, its version, and whether a daemon is answering on the socket. | | `termiod list` | The sessions the host is holding — add `--host ` to ask another machine. | | `termiod logs` | What the server recorded while nobody was attached. `-f` follows it; `--path` prints the file for a bug report. | `termiod` is the server's own command, and separate from the [`termio` command-line tool](/docs/cli) the app installs for you. You rarely need `termiod` by hand — the app runs these for you — but it is there when a box is misbehaving and you want to look rather than guess. ## Stopping it ```bash termiod stop ``` It declines while a command is still running or a viewer is attached, so a stray `stop` cannot take an agent's work with it. Sessions end when their process ends, or when you close them. --- # What survives URL: https://www.termio.sh/docs/persistence Markdown: https://www.termio.sh/docs/persistence.md Termio’s one structural promise is that a session outlives the thing you were looking at it through. Close the window, quit the app, let it update itself, drop the SSH link to a VPS — the agent keeps working. This page is the honest version of that claim: what survives each event, what doesn’t, and what it costs when a process really does go away. ## The rule Every session runs inside `termiod`, the [Termio server](/docs/server), on the machine that owns it — including the ones on this Mac. The app, the CLI, and the iOS app are all *viewers*. A viewer attaches to a session and detaches from it; it never holds the process. So the question “does this kill my agent?” always reduces to one thing: **did the process die, or did a viewer just let go?** Only two things end a session on purpose — **Close Session** (`⌘W`), and the process exiting by itself. ## What survives what | What happens | Agent keeps running | Layout comes back | Screen comes back | Conversation continues | | --- | --- | --- | --- | --- | | Switch sessions, hide the window, use another app | Yes | — | Yes | Yes | | Close the window | Yes | Yes | Yes | Yes | | Quit Termio (`⌘Q`) and relaunch | Yes | Yes | Yes | Yes | | Termio updates itself | Yes | Yes | Yes | Yes | | The SSH link to a remote machine drops | Yes | Yes | Yes | Yes | | `termiod handoff` (server updates in place) | Yes | Yes | Yes | Yes | | The server stops, or the machine reboots | No | Yes | No | On this Mac, where the agent supports [resume](/docs/atp#resume). Never on another machine | | **Close Session** (`⌘W`) | No | — | — | — | The top six rows are one event under six names: a viewer went away and the process didn’t notice. Reattaching repaints the screen from the server’s own scrollback, so you come back to the output you’d have seen if you had never left. “Layout comes back” means the sidebar tree, the split groups, and the inspector tab you left each workspace on. That structure is what Termio writes to disk; your terminals’ contents are not part of it. ## Detaching is the default Nothing casual ends a session. Closing the window, quitting the app, a self-update, a laptop that lost Wi-Fi mid-`ssh` — every one of them detaches. The next launch reattaches to the sessions that are still there rather than starting new ones, which is why a long agent run is safe to walk away from. Ending a session is the deliberate act: `⌘W` **closes the focused session**, the way `⌘W` closes a browser tab, and its pane collapses with it. Once no session is left, `⌘W` closes the window and the app keeps running with everything else alive. See [Keyboard shortcuts](/docs/keyboard#panes). Want to leave a machine but keep the work? Just quit. There is no “detach” command to remember, because detaching is what quitting already does. ## When the process really does go away A server that stops — a reboot, a `termiod stop`, a box that lost power — takes its PTYs with it. There is no way around that; a process is a process. What Termio does is make coming back cheap: - **The tree is restored.** Projects, worktrees, workspaces, split groups, and the session you left each workspace on all come back, because they live in a file rather than in the daemon. - **Sessions restart in their own directory.** A shell comes back as a shell, rooted where it was. - **Agents on this Mac that can resume, do.** Reopening a session relaunches the agent into the *same conversation* rather than a blank one. Resume is **exact-or-nothing**: Termio either continues the precise conversation a session was bound to, or it starts fresh. It never guesses at a nearby one. Which agents can do this is a fact about each agent’s own CLI, declared in its [manifest](/docs/atp#resume) — see the resume column in [Running multiple agents](/docs/agents#what-each-agent-reports). ## Surviving a reboot Without a service, the server lives as long as your login session. That is usually fine on the Mac in front of you and almost never fine on a box you only ever reach over SSH, where nobody logs in interactively at all. ```bash termiod service install # launchd on macOS, systemd --user on Linux termiod service status ``` With a service installed the server is brought back after a crash, and started again at login after a reboot. On Linux, surviving your logout also needs lingering enabled for the user (`loginctl enable-linger`); without it the unit stops with your session. Be clear about what this buys: the *server* comes back, not the processes it was holding. A reboot still ends every PTY on that machine. A service is what stops “nothing is listening on that box” from being a permanent state — see [Troubleshooting](/docs/troubleshooting). ## Sleep A sleeping machine isn’t running anything, so the honest rule for a laptop is: sessions don’t die when the Mac sleeps, they pause with it and pick up when it wakes. Termio does hold the machine out of *idle* sleep while there is work worth protecting. While any session is working or waiting on you, the server renews a short `caffeinate` lease, so a Mac plugged into power stays reachable for the agent that went quiet on a permission prompt — exactly the moment your phone needs to answer it. On battery it takes no lease at all, and closing the lid sleeps the machine regardless: nothing in user space can override clamshell sleep. If you want a session you can always reach, run it on a machine that stays up — a VPS, or the Mac mini on the desk — and pair your [iPhone](/docs/iphone) against that rather than against a laptop. ## Updating without dropping sessions A new server build replaces the running one **in place**: the process keeps its id and its PTYs, and the sessions never notice. ```bash termiod handoff ``` That is also what happens when the app updates itself, which is why a Termio update doesn’t cost you your running agents. A machine being set up or updated from **Settings ▸ Remote Hosts** takes the same path, and an update that *would* interrupt work in progress says so and names the session first. ## What Termio does not keep Being clear about the edges is part of the promise: - **Termio keeps no transcript of your terminals.** Scrollback lives in the server for the session’s lifetime and goes away with it; nothing durable is written. The one exception is a handoff, which passes each session’s replay buffer to the incoming server through a temporary file it unlinks as it goes — in flight for the length of an update, never a stored record. - **An agent’s conversation belongs to the agent.** Resume works by handing the agent back its own session id; the conversation is in the agent’s store, under your own account, exactly as it would be from a plain terminal. - **Nothing syncs anywhere.** State is per machine, on that machine. There is no Termio cloud holding a copy — see [Devices](/docs/devices). What `termiod` is, how it gets onto a machine, and how to look at it. The manifest that declares how an agent launches, reports, and resumes. Sessions missing after a reboot, and the rest of the usual surprises. --- # The iOS app URL: https://www.termio.sh/docs/iphone Markdown: https://www.termio.sh/docs/iphone.md Termio for iPhone is a companion, not a separate service. It connects to machines you own — your Mac, or a box that Mac set up — over your local network or a tunnel you configure. There’s no relay of ours in between. ## Pairing 1. On your Mac, open **Settings ▸ Mobile** and pick the machine your iPhone should connect to. Each one serves its own sessions. 2. Press **Publish** for a machine that isn’t reachable yet, then scan the QR code from the iOS app: tap the machine pill ▸ **Scan QR Code**. The camera reads the code on-device; nothing is stored or transmitted. Once paired, the phone reaches that machine’s sessions whenever it can dial the address it was paired against. **Rotate Token…** signs out every paired iPhone and requires a fresh scan — that’s the revoke. A phone can attach straight to a machine that isn’t this Mac, over the same session protocol the Mac uses. Your laptop doesn’t have to be awake to stand in the middle. ## What you can do from the phone - **See your projects and sessions**, with the same live statuses as on the Mac. Home is one workspace at a time — swipe from the left edge for the workspace rail, the way Slack switches — and each section names the machine its workspace is on. The “Needs You” strip stays outside that scope on purpose, so an agent blocked in another workspace still reaches you. - **Type straight into a session.** The phone drives the real terminal — you’re typing into the agent exactly as on the desktop, with a key-bar for scrollback and jump keys, and hold-to-talk voice dictation if you enable it. - **Preview files** read-only over the same connection. ## Reaching a machine remotely On the same Wi-Fi, pairing is all you need. To reach a machine from anywhere, give it an address: **Settings ▸ Mobile ▸ Tunnel** starts one for you, or **Custom** runs the tunnel command you choose — a named `cloudflared` tunnel, or your own relay — and matches the URL it prints. Termio never routes your traffic through servers of ours; a hosted tunnel provider terminates on theirs, which is why Custom is there. A Mac that’s asleep can’t answer the phone, and nothing in the app can wake it from outside. If you want a session you can always reach, run it on a machine that stays up — a VPS, or the Mac mini on the desk — and pair against that. --- # Command-line tool URL: https://www.termio.sh/docs/cli Markdown: https://www.termio.sh/docs/cli.md `termio` is the shell side of the app: it opens projects, and through `termio sessions` it is the orchestration API one agent uses to see and steer its siblings. The switch that exposes it is [Session control](/docs/session-control). Turn on **Settings ▸ Server ▸ Command line ▸ Command-line tool**. It symlinks a `termio` command onto your `PATH` (at `/usr/local/bin/termio`; macOS asks for permission once), and turning it off removes the link again. ## Open a project ```bash termio # open the current directory as a project termio ~/code/myapp # open a specific folder ``` Run it in any directory and Termio brings that folder into the sidebar — the shell equivalent of **Open Project**. ## The orchestration model The `termio sessions` family is Termio’s orchestration API: it lets one agent — or your own scripts — see and steer sibling sessions in the same project. Before the verb reference, the design rules everything below follows: - **Project-scoped by default.** Every command resolves the caller to its own project (via the session id the PTY carries, or the working directory) and can only see and drive siblings there — never sessions in unrelated projects. - **One address, one target.** Sessions are addressed by `termio://session/` deep links minted at creation and printed by `list` (a bare id or unique id-prefix works too). The link names the pane, never its mutable contents, so a copied address survives the session promoting, demoting, or being renamed — and stays self-describing when pasted anywhere. - **Transcript as truth.** An agent’s *result* is read from its own structured transcript (the path and line range the replies hand back), never scraped off the screen. The screen is the result channel only for plain `run` terminals, which have no transcript. - **Waiting is explicit.** No command blocks unless you pass `--wait`, and `--wait` always means the same thing: wait for the turn’s *outcome*, fail fast when no outcome can come, and split the result across exit codes. - **Signal, never kill.** The supervision plane observes and reports — `watch` events, the `stalled` alarm — but never terminates or auto-answers a session. Acting on a signal is always the supervisor’s decision. ## Drive sessions Add `--json` to any command for machine-readable output. | Command | What it does | | --- | --- | | `termio sessions list` | List the sessions in this project with their live status. | | `termio sessions watch` | Block and stream one line per status change — the push alternative to polling `list`. | | `termio sessions spawn ""` | Start a new agent session on the prompt; replies immediately with its session link. | | `termio sessions run ""` | Start a new plain terminal session typing that shell command — a dev server, a test run — in a visible pane, no LLM. | | `termio sessions send ""` | Type text into an existing session and submit it with a real Return keypress — a prompt to drive it, or a menu choice (`"1"`, `"yes"`) to answer a permission prompt. | | `termio sessions read ` | Print the session’s current screen without focusing it (`--lines N` keeps the tail) — the result channel for `run` sessions. | | `termio sessions close ` | Close one or more session tabs. | | `termio sessions focus ` | Bring a session to the front in the app. | `` is the `termio://session/` address `list` prints (a bare id, unique id-prefix, or session title also works). Commands are scoped to the current project automatically, and `termio sessions --help` prints focused help for any one verb. `answer` is a deprecated alias of `send` (meaningful for agent sessions only), and `send` with no target behaves like `spawn` — both kept so existing scripts keep working. Prefer `spawn` and `send`. ## Spawning without waiting `spawn` returns the moment the session exists: the reply carries the new session’s link, and the prompt itself is typed in once the agent finishes booting. Add `--agent ` (e.g. `codex`, `grok`, `pi`) to choose the agent; it defaults to the caller’s own kind. ## Running plain commands Not everything in a fleet is an agent. `run` starts a *terminal* session on a shell command — a dev server, a test watcher, a build — in a visible pane you can watch and take over, and `read` prints any session’s current screen without focusing it. A plain command has no transcript, so its screen is its result channel; agent results keep flowing through transcripts. ```bash termio sessions run "pnpm test" termio sessions read 1a2b3c4d --lines 40 ``` ## Waiting for the outcome: `--wait` Waiting is always explicit, and it means the same thing everywhere: `--wait` waits for the *outcome* of the turn, never for plumbing. Both `send` and `spawn` accept it (with an optional `--timeout `, default 300000, clamped 1000–600000): ```bash termio sessions send termio://session/ab12cd34-9f2e-4c31-b8d7-3e5a12c90f44 "run the tests" --wait termio sessions spawn "summarize the failing CI job" --wait --timeout 120000 ``` The call blocks until the turn settles — the session was seen `working` and has rested off it — and the reply carries the final `status`, the `transcript` path, and the `cursor`..`cursor_end` line range the response landed in, so the caller reads exactly the new content with its own file tools. Two special cases: - A session that stops to ask something short-circuits the wait immediately: the reply is `status: "needs-you"` with the on-screen question in `prompt` — answer it with another `send`. - A plain terminal with no status signal falls back to screen settling: the screen changed after the send, then went still. Waits also **fail fast instead of burning the timeout** when no outcome can come: - A prompt that shows no effect within 5 seconds — no status move, no screen change — errors as `prompt_stalled`: the input was eaten (an agent still booting, a program that ignores typed text), and no turn is coming. - A session that closes mid-wait errors as `session_closed`; an agent that exits back to its shell mid-wait errors as `agent_gone`. On timeout the reply still comes back (`timed_out: true`) with whatever the current status is; the session keeps running. The exit code splits the three outcomes so scripts can branch without parsing: **0** settled, **1** error (including stalled/vanished), **3** timed out. ## Supervising with `watch` `watch` is built so an agent can supervise a fleet without polling: - **Snapshot on attach.** It first prints one line per session with its *current* status (tagged `"snapshot":true` in `--json`), so a supervisor attaching late still learns a session is already waiting on input. Skip it with `--no-snapshot`. - **Filtered by default.** Live events default to the two states a supervisor acts on, `done` and `needs-you`; widen with `--state working,idle,done,needs-you`. - **Stall alarm, opt-in.** `--state stalled` adds a watch-plane signal for the unattended runaway: a session still `working` that has made **no repo change and next-to-no transcript growth for 20+ minutes**. Sustained output — a long build streaming logs — suppresses it. The event carries the detector’s reasoning in `evidence` (`"working 42m, no repo change, transcript +3 lines"`), fires once per quiet stretch, and re-arms when progress resumes. Termio only signals; it never kills — the session’s real status stays `working` and the sidebar is untouched. Not in the default filter. - **Actionable events.** A `needs-you` event carries the on-screen question in `prompt`; a `done` event carries the session’s `transcript` path and `cursor_end` — enough to answer, or to read the result, without another round-trip. - **Heartbeat.** In `--json` mode the app writes `{"heartbeat":true}` after 30 seconds of silence, so a quiet stream is distinguishable from a dead one. - **Exit codes.** `0` after Ctrl-C (the normal end of supervision), `2` when the stream closed from the app side — “I chose to stop” and “Termio went away” are different outcomes. ## Spawned sessions know their caller When one Termio session spawns another, the delivered prompt opens with a short provenance envelope: it names the caller’s link and allows exactly one back-channel — a mid-task question, or a one-line completion ping, via `termio sessions send …`. Nothing else travels that way: no conversation, no delegating tasks back. Results stay transcript-as-truth — the supervisor reads the worker’s transcript, with `watch` as the backstop. A `spawn` from a plain shell (outside any Termio session) gets no envelope. ## Fail loud The CLI is built for callers that trust exit codes, not prose. A command that runs and fails — an error reply, an empty one — exits `1`; a command it cannot parse exits `2` without contacting the app. Flags take either spelling, `--agent codex` or `--agent=codex`, and `--` ends them, which is how text beginning with a dash reaches a session: ```sh termio sessions send -- --force ``` One-shot commands give up after a 15-second client timeout rather than hanging on a busy app (override with the `TERMIO_CLI_TIMEOUT` environment variable; a `--wait` call widens the client timeout automatically to outlive the server-side wait). ## JSON contract Agents code against `--json` output, so its shapes are pinned, not reverse-engineered. Every JSON reply carries `"schema_version": 1`; fields may be *added* within a version, never renamed or removed. Optional fields are omitted when unknown (never emitted as `""` or `null`). Every error, on any verb, has one shape — and the exit code is nonzero: ```json {"ok": false, "error": "", "message": "", "schema_version": 1} ``` The stable codes: `disabled`, `no_scope`, `bad_op`, `bad_request`, `no_text`, `no_target`, `not_found`, `ambiguous`, `wrong_agent`, `bad_agent`, `no_agent`, `start_failed`, `not_live` — plus, on `--wait` only, `prompt_stalled`, `session_closed`, and `agent_gone`. Success replies, per verb (`schema_version` and `"ok": true` omitted below for brevity): | Verb | Reply fields | | --- | --- | | `list` | `project`, `sessions: [{link, id, title, agent, status, description, transcript?}]` — `link` is the canonical address, `id` its 8-char short form | | `spawn` / `run` | `target`, `title`, `created: true`, `queued: true` — the payload is still being delivered | | `send` | `target`, `title`, `transcript?`, `cursor?` — read the response from `cursor` onward | | `read` | `target`, `title`, `screen` — the current viewport, right-trimmed, trailing blank rows dropped | | `send`/`spawn`/`run` `--wait` | `target`, `title`, `status`, `timed_out`, `created?`, `transcript?`, `cursor?`, `cursor_end?`, `prompt?` | | `close` | `closed`, `title` | | `focus` | `focused` | The two wait verbs share one reply shape (`--wait` means the same thing everywhere): the final `status`, and the transcript line range `cursor`..`cursor_end` holding the response. `prompt` appears only on a `needs-you` outcome — the question the session is showing on screen. `watch` streams newline-delimited JSON events rather than one reply: ```json {"schema_version": 1, "link": "termio://session/ab12cd34-9f2e-4c31-b8d7-3e5a12c90f44", "status": "done", "title": "…", "cwd": "…", "snapshot": true, "prompt": "…", "transcript": "…", "cursor_end": 42} ``` `cwd` is omitted until the shell reports one; `snapshot` marks the on-attach roster lines; `prompt` rides on `needs-you` events, `transcript` + `cursor_end` on `done` events (exactly as in wait replies), and `evidence` on `stalled` events — the stall detector’s reasoning, e.g. `"working 42m, no repo change, transcript +3 lines"`. `stalled` appears only on the watch stream, never as a session’s `status` in `list` or wait replies. After 30 seconds of silence the app writes `{"heartbeat": true}`. This is what lets one agent hand work to another — `spawn` a task in a sibling session, then `watch` (or poll `list`) until it reports **done** and read the result. Watching several at once, `termio sessions watch` prints the link the moment any of them turns **done** or **needs-you** — one agent supervising a fleet, all on your own machine. --- # Agent Terminal Protocol URL: https://www.termio.sh/docs/atp Markdown: https://www.termio.sh/docs/atp.md Editor protocols re-render your agent inside an editor pane. ATP takes the opposite bet: the agent’s own TUI already is the interface, so it stays in a real terminal, and the protocol standardizes only the thin layer around it — **launch**, **live status**, and **exact resume**. Every agent Termio ships is defined by this same manifest; there is no privileged internal API. ## The manifest One JSON file per agent. This is Termio’s bundled Grok manifest, verbatim: ```json { "id": "grok", "order": 95, "name": "Grok", "wire": "grok", "command": "grok", "permissionBypassFlag": "--yolo", "resume": { "create": "--session-id {id}", "resume": "--resume {id}", "storeRoot": "~/.grok/sessions", "storeMatch": "dir:{id}" }, "icon": { "vector": "grok" }, "install": "https://x.ai/cli", "titleStatus": { "attention": ["Action Required"] }, "hooks": { "type": "json", "file": "~/.grok/hooks/termio.json", "dialect": "grok", "conversation": "sessionId", "events": [ { "on": "SessionStart", "state": "idle" }, { "on": "UserPromptSubmit", "state": "working" }, { "on": "PreToolUse", "state": "working" }, { "on": "PostToolUse", "state": "working" }, { "on": "Stop", "state": "done" } ] } } ``` | Field | Meaning | | --- | --- | | `id` | Stable identifier. Sessions persist it, so it never changes once shipped. | | `name` | Display name in the picker and sidebar. | | `command` | The CLI to launch, resolved on your login shell’s `PATH`. | | `permissionBypassFlag` | The vendor’s skip-permissions flag, wired to a one-click toggle. Optional. | | `icon` | `vector` (a built-in brand mark), `path` (your PNG/SVG file), or `symbol` (an SF Symbol), with an optional `tint`. | | `install` | The vendor’s install page, offered when the command isn’t found. Optional. | | `order` | Position in the agent picker. Optional; omitted manifests sort last. | | `skills` | The agent’s user-level skills directory (`{"dir": "~/.claude/skills"}`), which is where Termio writes its session-control skill. Optional; omit it for an agent with no skills ecosystem. | | `configHome` | The environment variable the agent documents for moving its config tree, and the path that variable replaces (`{"env": "CLAUDE_CONFIG_DIR", "path": "~/.claude"}`). Set it and Termio installs where the agent actually reads, rather than at the literal default. Optional. | ## Status A session is always in one of four states — `working`, `attention` (blocked on you), `done`, or `idle` — shown in the sidebar, the menu bar, and on your iPhone. The manifest declares how the agent reports them, through three channels: | Channel | Role | | --- | --- | | `hooks` | The precise channel: Termio installs the agent’s own hook configuration (JSON, TOML, or a plugin, per `dialect`) so the agent itself reports each `on → state` event. When hooks are declared they are the single source of truth. | | `titleStatus` | Regex rules over the agent’s live terminal title (OSC 0/2) — the in-band signal some agents broadcast. Coexists with hooks and corrects a missed event the instant the title flips. | | `status` | Screen-classification regexes for agents with no hook system at all. Ignored when hooks are declared. | `hooks.conversation` names where the agent’s own session id appears in its hook payloads (a stdin JSON field for shell hooks, an event key path for plugins). With it, each status report also carries conversation *identity* — so when the agent rotates to a new conversation mid-session (`/new`, `/clear`), Termio re-binds the tab to the live one the moment the next event fires. ## Resume Reopening a session relaunches the agent into the same conversation. Resume is **exact-or-nothing**: Termio either resumes the precise conversation a tab is bound to, or launches fresh — it never guesses. Two families, inferred from which fields are present: **Pinned id** — the CLI accepts a session id at launch. `create` starts a fresh conversation under a Termio-minted id and `resume` continues it; `storeRoot` + `storeMatch` describe the agent’s on-disk session store so Termio can tell the two apart (creating a duplicate id errors, as does resuming a missing one). **Discovered id** — the agent mints the id itself. `discover` describes the mechanism, never an agent: where session records live, how a record is read, and key paths to the id and working directory. A record is either `jsonl` (the first line of a log that is itself the transcript) or `json` (a standalone metadata file). Termio recovers the id, binds it to the tab, and resumes exactly from then on. ```json "resume": { "resume": "resume {id}", "discover": { "root": "~/.codex/sessions", "format": "jsonl", "id": "payload.id", "cwd": "payload.cwd" } } ``` ## Add your own agent Drop a manifest at `~/.termio/config/agents/.json` and restart Termio — it appears in the picker alongside the built-ins, with the same status dots and resume behavior. A minimal manifest is just an id, a name, and a command; add status and resume as the CLI supports them. The [Custom agents](/docs/custom-agents) guide walks through this end to end — icons, screen-scrape status rules, and overriding a built-in. --- # Troubleshooting URL: https://www.termio.sh/docs/troubleshooting Markdown: https://www.termio.sh/docs/troubleshooting.md Almost everything that looks like a Termio bug is one of the cases below, and each one has a way to check rather than guess. Start here before filing anything. ## Start with these three On any machine, in any state, these answer most of the question: ```bash termiod status # what's installed here, and is a daemon answering? termiod list # what sessions is it holding? termiod logs -n 200 # what happened while nobody was watching ``` `termiod status` in particular ends a surprising share of “it broke” reports: it says which binary is installed, which version, and whether anything is actually listening on the socket. To ask about another machine, `termiod list --host ` queries that box's daemon over your own SSH config. ## A status dot never changes The sidebar’s [status dots](/docs/sidebar#status) come from hooks installed into each agent’s own config, and they are installed **per machine**. An agent on a box you set up before turning the feature on has no hooks, so it reports nothing and Termio falls back to reading the screen. Open **Settings ▸ Remote Hosts**, pick the machine, open its **Agents** page and press **Reinstall Hooks and Skill**. For this Mac the same repair is **Install on This Mac**, on **Settings ▸ Agents**. See [Status hooks & session control](/docs/session-control). ## The agent is sitting on a “trust these hooks?” prompt Some agents verify their hook files and refuse to run hooks whose contents changed — Codex will show **Modified since last trusted** and wait. This one is nasty because the hooks *are* the status channel: until you answer, the agent reports nothing, so the sidebar shows the last status it knew rather than **needs you**. If a session looks idle but nothing is happening, look at the pane itself. Answer the prompt once and status resumes. Termio re-asserts its hooks on launch (that is what keeps other tools from quietly replacing them), so the prompt can come back after an update. ## An agent won’t launch, or exits instantly A pane that dies at once with “failed to launch” is nearly always `PATH`, not the agent. Termio starts agents through your real login shell, so anything your profile sets up is available — but a CLI installed somewhere your profile doesn’t add is still invisible. Open **Settings ▸ Agents** and look at the agent’s row: Termio resolves your login shell’s `PATH` and flags a command it can’t find. Either fix your profile, or paste the absolute path (`/opt/homebrew/bin/codex`) into the command field. The same field is per machine, so do it on the machine that’s failing. ## Status hooks were working and stopped Agent config files are shared ground — other agent managers write to the same `~/.claude/settings.json` and `~/.codex/hooks.json`, and some replace the whole hooks block rather than merging into it. When that happens Termio’s reports stop arriving and status falls back to screen-reading. Termio re-asserts its own hooks when it launches and when you bring it back to the front, so on this Mac it usually heals by itself. That sweep is local only: another machine keeps whatever is on it until you repair it there, with **Reinstall Hooks and Skill** on that machine’s **Agents** page. ## `termio sessions` says `disabled` The orchestration API is opt-in. Turn on **Settings ▸ Agents ▸ Session control**, which also installs the `termio` skill that teaches agents the commands exist. Every CLI error has the same shape and a nonzero exit code, so a script can branch on `error` rather than parse prose — the codes are listed in the [JSON contract](/docs/cli#json-contract). ## `termio: command not found` The command is a symlink the app installs for you: **Settings ▸ Server ▸ Command line ▸ Command-line tool**, which links `termio` into `/usr/local/bin` (macOS asks once). If the toggle is already on, open a new shell — a session started before the link existed has a stale `PATH` hash. `termiod` is a different command that lives on each machine; see [The Termio server](/docs/server). ## Sessions are gone after a reboot Without a service, the server lives as long as your login session. Install one and it survives logouts, crashes and reboots: ```bash termiod service install termiod service status ``` This is the usual surprise on a remote box, where nobody logs in interactively. See [The Termio server](/docs/server) and [What survives](/docs/persistence). ## A machine stopped answering Work from the outside in. If plain `ssh` fails, Termio will too — it uses your own `~/.ssh/config` and never carries its own credentials. ```bash ssh mybox true # does SSH itself work? termiod list --host mybox # is the server answering there? termiod deploy --host mybox # reinstall and verify it ``` `deploy` is a reconcile, so running it against a box in a bad state is safe. A key is the credential that works everywhere, including for sessions already running on the box; a passphrase-protected key needs to be loaded into `ssh-agent` first, the same as it would for any non-interactive `ssh`. The machine’s own page has a **Test** button that names which outcome you have — see [Remote hosts](/docs/remote-hosts#testing-the-route). ## The iPhone app can’t reach a machine Pairing binds the phone to one address. Check them in order: 1. **Is the machine awake?** A sleeping Mac answers nothing, and nothing in the app can wake it from outside. Pair against a machine that stays up if you want this to always work — see [What survives](/docs/persistence#sleep). 2. **Is it published?** **Settings ▸ Mobile** must have the machine published; on the same Wi-Fi that’s all it takes. 3. **From elsewhere, is there a tunnel?** Off your own network the machine needs an address — **Settings ▸ Mobile ▸ Tunnel**, or your own. 4. **Was the token rotated?** **Rotate Token…** signs out every paired phone by design. Scan the QR code again. See [The iOS app](/docs/iphone). ## Notifications don’t appear Task notifications are a system permission like any other: macOS asks once, and if it was declined the app can’t ask again. Check **System Settings ▸ Notifications ▸ Termio**, and check that **Task completion** is on under **Settings ▸ Agents ▸ Notifications**. They’re also deliberately quiet. A turn that finished while Termio was the frontmost app doesn’t notify — you were already looking at it — and neither does a turn that took only a moment. ## Something looks wrong and you want evidence The server keeps a log of what happened while nobody was attached — which is exactly the window a bug tends to fall into. ```bash termiod logs -n 200 # the recent tail termiod logs -f # follow it live termiod logs --path # the file itself, for a bug report ``` Reproducing something intermittent? Leave `termiod logs -f` running in a pane of its own. The log is the one record that spans the moment you weren’t attached. ## Still stuck Open an issue at [github.com/termio-sh/termio](https://github.com/termio-sh/termio/issues) with the output of `termiod status` and the tail of `termiod logs`. Those two answer most of the questions a maintainer would otherwise have to ask. Add the macOS version, the Termio version, and whether the session was local or on another machine.