# CLI Reference

URL: https://tuios.dev/docs/cli-reference

> Every tuios command and flag, with examples. Sessions, windows, input and output, agents, remote hosts, tapes, environment variables and exit codes.

```bash
tuios [flags]              # start or attach to TUIOS
tuios <command> [flags]    # run a command
```

Every command accepts `--help`, and its output always matches your build. `tuios help <command>` does the same. To search every command, run `tuios help -i`. See [help](https://tuios.dev/docs/cli-reference#help).

## Conventions

Most commands that act on a running session share these flags:

- `-s, --session <name>`: the target session. The default is the most recently active one.
- `-w, --window <target>`: the target window. The default is the focused one.
- `--json`: print JSON instead of a table.

A window target is matched in this order, and the first match wins:

1. The full window ID
2. The index `list-windows` prints
3. The custom name, then the window title
4. A unique ID prefix, such as the 8 characters `list-windows` shows

An index or ID therefore beats a window that happens to be named `2`, and a name beats an ID prefix: a pane named `db` is never mistaken for a pane whose ID starts with `db`. An ambiguous name or prefix is an error, never a guess.

Commands that talk to the daemon exit `3` when no daemon is running. See [Exit codes](https://tuios.dev/docs/cli-reference#exit-codes).

## Global flags

Three flags work on every command:

- `--debug`: enable debug logging.
- `--cpuprofile <file>`: write a CPU profile.
- `--pprof <addr>`: serve `net/http/pprof` on this address, for example `:6060`. With no host, the server listens on 127.0.0.1 only. The profiles have no password. Give `0.0.0.0:6060` only on a network you trust.

These work only on a bare `tuios`:

- `--standalone`: run a session without the daemon, for this run. `TUIOS_NO_DAEMON=1` does the same for a whole shell.
- `--skill`: print the agent skill for driving TUIOS from a pane, then exit.
- `--list-themes`: print every theme id, then exit.
- `--preview-theme <name>`: print a theme's 16 ANSI colours, then exit.
- `-v, --version`: print the version, the VT backend (`pure-Go` or `ghostty`), commit, build date and builder. There is no `version` subcommand.

```bash
tuios --theme $(tuios --list-themes | fzf --preview 'tuios --preview-theme {}')
tuios --skill > ~/.claude/skills/tuios/SKILL.md
```

A bare `tuios` attaches to a daemon session, because `startup.daemon` defaults to `true`. See [The startup table](https://tuios.dev/docs/configuration#the-startup-table).

### Interface flags

These flags change how the TUI looks for one run. They are accepted by `tuios`, `new`, `attach`, `ssh` and `tape play`. Any other command rejects them. Each one overrides a [config option](https://tuios.dev/docs/configuration#command-line-flags).

| Flag                                       | Values                                                                                                               | Default                                    |
| ------------------------------------------ | -------------------------------------------------------------------------------------------------------------------- | ------------------------------------------ |
| `--theme <name>`                           | a theme id                                                                                                           | none: your terminal's colours              |
| `--border-style <style>`                   | `rounded`, `normal`, `thick`, `double`, `hidden`, `block`, `ascii`, `outer-half-block`, `inner-half-block`, `glyphs` | `rounded`                                  |
| `--dockbar-position <pos>`                 | `top`, `bottom`, `hidden`                                                                                            | `top`                                      |
| `--window-title-position <pos>`            | `top`, `bottom`, `hidden`                                                                                            | `top`                                      |
| `--window-button-style <style>`            | `dots`, `pill`                                                                                                       | `dots`                                     |
| `--window-button-position <pos>`           | `left`, `right`                                                                                                      | `left`                                     |
| `--scrollback-lines <n>`                   | 100 to 1000000                                                                                                       | `10000`                                    |
| `--zoom-max-width <cells>`                 | cells, `0` for full screen                                                                                           | `0`                                        |
| `--ascii-only`                             |                                                                                                                      | Use ASCII instead of Nerd Font icons       |
| `--hide-window-buttons`                    |                                                                                                                      | Hide the window buttons                    |
| `--hide-scrollbar`                         |                                                                                                                      | Hide the scrollbar                         |
| `--shared-borders`                         |                                                                                                                      | Share borders between tiled panes          |
| `--no-animations`                          |                                                                                                                      | Turn animations off                        |
| `--confirm-quit`                           |                                                                                                                      | Always ask before quitting                 |
| `--show-keys`                              |                                                                                                                      | Show the keycast overlay                   |
| `--show-clock`, `--show-cpu`, `--show-ram` |                                                                                                                      | Show the clock, CPU graph or RAM use       |
| `--hide-clock`                             |                                                                                                                      | Deprecated. The clock is hidden by default |

## Commands

| Group                                                                                 | Commands                                                                                                                                                                                                                                    |
| ------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [Sessions](https://tuios.dev/docs/cli-reference#sessions)                             | `new`, `attach`, `ls`, `list-clients`, `detach-client`, `ssh-agent-path`, `resurrect`, `rename-session`, `kill-session`, `kill-server`, `daemon`, `logs`                                                                                    |
| [Worktrees](https://tuios.dev/docs/cli-reference#worktrees)                           | `worktree`, `fan`, `ship`                                                                                                                                                                                                                   |
| [Windows and workspaces](https://tuios.dev/docs/cli-reference#windows-and-workspaces) | `new-window`, `split-window`, `popup`, `xpanes`, `close-workspace`, `focus-window`, `pip`, `move-window`, `set-window`, `set-layout`, `select-workspace`, `list-workspaces`, `set-workspace-name`, `set-session-name`, `set-session-accent` |
| [Input and output](https://tuios.dev/docs/cli-reference#input-and-output)             | `send-keys`, `send-text`, `run`, `capture-pane`, `screenshot`, `wait-for`, `subscribe`                                                                                                                                                      |
| [Inspection](https://tuios.dev/docs/cli-reference#inspection)                         | `list-windows`, `get-window`, `session-info`, `list-verbs`                                                                                                                                                                                  |
| [Tape verbs](https://tuios.dev/docs/cli-reference#run-command)                        | `run-command`                                                                                                                                                                                                                               |
| [Configuration](https://tuios.dev/docs/cli-reference#configuration)                   | `config`, `set-config`, `get-config`, `list-options`, `list-themes`, `import-theme`, `list-glyphs`, `keybinds`                                                                                                                              |
| [Dock and hooks](https://tuios.dev/docs/cli-reference#dock-and-hooks)                 | `list-dock-components`, `refresh-dock`, `list-hooks`                                                                                                                                                                                        |
| [Agents](https://tuios.dev/docs/cli-reference#agents)                                 | `list-agents`, `get-agent-state`, `set-agent-state`, `set-agent-meta`, `set-agent-session`, `start-agent`, `send-agent-message`, `read-agent-messages`, `ask-agent`, `ask-human`, `explain-agent-detect`, `explain-agent-screen`, `stash`   |
| [Inbox and review](https://tuios.dev/docs/cli-reference#inbox-and-review)             | `list-attention`, `peek-prompt`, `respond`, `queue`, `review`, `checkpoint`, `agent-log`, `resume-agent`                                                                                                                                    |
| [Agent setup](https://tuios.dev/docs/cli-reference#agent-setup)                       | `integration`, `doctor`, `pane-grants`, `set-pane-grants`, `mcp`, `tmux-shim`, `tmux`                                                                                                                                                       |
| [Remote hosts](https://tuios.dev/docs/cli-reference#remote-hosts)                     | `hosts`                                                                                                                                                                                                                                     |
| [Tapes and layouts](https://tuios.dev/docs/cli-reference#tapes-and-layouts)           | `tape`, `layout`                                                                                                                                                                                                                            |
| [Other](https://tuios.dev/docs/cli-reference#other-commands)                          | `ssh`, `notify`, `update`, `completion`, `help`                                                                                                                                                                                             |

`agent-hook`, `agent-statusline` and `agent-proto` are run by the harness integrations and by `start-agent --protocol`, not by hand. Their `--help` says what they do.

Aliases: `attach` is `a`, `new` is `n`, `ls` is `list-sessions`, `resurrect` is `restore`, `keybinds` is `keys` or `kb`, and `hosts remove` is `hosts rm`.

## Sessions

A session lives in the daemon and keeps running after you detach (Ctrl+B d). The daemon starts on its own when a command needs it. See [Sessions](https://tuios.dev/docs/sessions).

### new

Create a session and attach to it. Alias: `n`.

```bash
tuios new [session-name] [flags]
```

- `-d, --detach`: create the session without attaching.
- `--host <name>`: create the session on a machine from the `[hosts]` table.
- `--ssh`: with `--host`, run ssh to the host and use its own `tuios` instead of attaching here.
- `--global`: create a global session, which can hold panes from more than one machine.
- `--hold`: after a failure, wait for Enter before exiting.
- `--cwd <dir>`: the directory the session's windows start in. The default is the current directory. It does not work with `--host`.
- The [interface flags](https://tuios.dev/docs/cli-reference#interface-flags).

```bash
tuios new                          # auto-generated name
tuios new api --cwd ~/dev/api      # windows start in ~/dev/api
tuios new work --theme dracula
tuios new build --detach           # headless, for scripting
tuios new --host buildbox ci -d    # on another machine
```

A detached session gets one window and works at once with `send-keys`, `run-command` and `capture-pane`. It is 80x24 until a client attaches. Run from a script, a service or CI, with no terminal on stdout, its panes still get `TERM=xterm-256color` and `COLORTERM=truecolor`, not `dumb`. See [Headless Sessions](https://tuios.dev/docs/sessions#headless-sessions).

### attach

Attach to a session. Alias: `a`.

```bash
tuios attach [session-name] [flags]
```

- `-c, --create`: create the session if it does not exist.
- `--host <name>`: attach to a session on a machine from the `[hosts]` table.
- `--ssh`: with `--host`, run ssh to the host and use its own `tuios`. Press the prefix twice to send it to the remote client.
- `--hold`: after a failure, wait for Enter before exiting.
- `--force`: attach from inside a pane of the same session. See [Attach from inside a pane](https://tuios.dev/docs/cli-reference#attach-from-inside-a-pane).
- `-d, --detach-others`: detach every other client of the session, as `tmux attach -d` does.
- The [interface flags](https://tuios.dev/docs/cli-reference#interface-flags).

```bash
tuios attach                  # the session you used last
tuios attach work -c          # attach, or create it
tuios attach -d work          # attach, and detach the other clients of work
tuios attach --host build api # the session api on the machine build
```

With no name, `attach` opens the session you last typed or clicked in. Windows that agents or scripts open, and keys that `send-keys` or a tape sends, do not count. The choice is saved with the session, so it holds across a daemon restart. If the daemon is not running, it starts, restores the sessions saved on disk, and opens one of them. With nothing saved, it opens a new session. A name that matches no session is an error unless you pass `-c`.

`SIGINT` or `SIGTERM` ends a client even when its terminal stopped reading, for example after a killed SSH connection. The client asks for a normal quit first. If the quit does not finish within 3 seconds, the client exits. A second signal makes it exit at once. A forced exit puts the terminal mode back, tries to reset the screen, and exits with `128` plus the signal number. If the screen stays wrong, run `reset`. The session keeps running in the daemon. `tuios new`, `tuios --standalone` and `tape play` do the same.

With `-d`, each other client of the session detaches as this client attaches. The session continues to run. Each detached client exits with status 0 and prints "Another client attached to this session." To do this on every attach, set `single_client = true` in `[daemon]`. See [One client for each session](https://tuios.dev/docs/sessions#one-client-for-each-session).

With `ssh_agent = "follow"` in `[daemon]`, `attach` points the ssh agent link of the session at the `SSH_AUTH_SOCK` of this client. See [SSH Agent](https://tuios.dev/docs/ssh-agent).

#### Attach from inside a pane

Inside a TUIOS pane, `tuios attach` refuses to attach the session that holds the pane. A bare `tuios attach` or `tuios` in a pane also refuses, because it names no session.

- To show a different session in the pane, name that session.
- To attach the same session, open a terminal outside TUIOS.
- To attach anyway, use `--force` or set `TUIOS_ALLOW_NESTED=1`.

The same refusal applies to a chain. For example, session A shows session B in a pane, and a pane of B then attaches A. See [Nested attach](https://tuios.dev/docs/sessions#nested-attach) for how TUIOS finds the pane.

To move the client that shows the pane to a different session, use [`switch-session`](https://tuios.dev/docs/cli-reference#switch-session).

### switch-session

Switch an attached client to a different session, in place. This is what the session switcher does. Nothing is nested.

```bash
tuios switch-session [HOST:]NAME [flags]
```

- `-s, --session <name>`: switch the client that shows this session.
- `--client <id>`: switch the client with this id, from `tuios list-clients`.
- `-c, --create`: create the session if it does not exist.
- `--cwd <dir>`: with `--create`, the directory the new session's windows start in.
- `--json`: print the result as JSON.

The command switches one client: the client that `--client` names, else the client that shows the `-s` session, else, from a pane or a popup, the client that shows the pane's session, else the only client that is attached. With more than one client, name one.

`HOST` is a machine from the `[hosts]` table. A session that `--create` makes has one window. When the session exists, `--create` and `--cwd` do nothing, and the client switches to it. When the session is not arranged yet, `[startup]` applies to it. From a pane, the command needs the `admin` grant.

```bash
tuios switch-session api                           # from a pane: show the session api
tuios switch-session --create --cwd ~/dev/api api  # make api in ~/dev/api if it is missing
tuios switch-session build:api                     # the session api on the host build
tuios switch-session -s work api                   # from outside: switch the client that shows work
```

See [Sessionizer](https://tuios.dev/docs/sessions#sessionizer) for a key that opens a project.

### ls

List sessions. Alias: `list-sessions`.

```bash
tuios ls [flags]
```

- `--json`: print JSON.
- `--all-hosts`: also list every machine in the `[hosts]` table.
- `--host <name>`: list one machine. `local` means this one.

The table shows name, window count, status, creation time and last activity. `--json` prints a bare array:

```json
[
  {
    "name": "work",
    "id": "04623592-051c-49d5-9a29-69a2e2ccdb33",
    "created": 1790101795,
    "last_active": 1790101795,
    "window_count": 1,
    "attached": false,
    "width": 80,
    "height": 24,
    "windows": [
      { "id": "bef3bdc3-2b99-4bf5-9499-b1d3aabb7f9c", "title": "Terminal bef3bdc3", "workspace": 1 }
    ],
    "current_workspace": 1,
    "dir": "tuios",
    "branch": "main"
  }
]
```

- `created` and `last_active` are Unix seconds.
- `dir` is the base name of the focused pane's directory (`~` for home). `branch` is its git branch. Both are omitted when unknown.
- `display_name`, `accent` and `restored` appear only when set.

With no daemon running, `ls` lists the sessions saved on disk, marks each `saved` (`"saved": true` in JSON), and exits `3`.

A session with a label, set by [`set-session-name`](https://tuios.dev/docs/cli-reference#workspaces-and-labels), shows in the table as `name (label)`. The `name` field in JSON stays the real name; `display_name` carries the label.

With `--all-hosts` or `--host`, the JSON is an object with a `hosts` array. Each entry has `host`, `status` and `sessions`. A host that does not answer gets a row saying so and does not fail the command.

### list-clients

List every connection to the daemon: its client id, its process id and its session.

```bash
tuios list-clients [--json]
```

```json
[
  {"client_id": "client-1790941960197517900", "pid": 4242, "session": "work"}
]
```

- `session` is empty while the connection is not attached.
- The list includes the connection of `list-clients` itself.
- `pid` comes from the kernel. It is `0` on Windows and the BSDs.
- For an SSH or web client, `pid` is the local server process, not the remote SSH process or the browser.

To follow changes, run `tuios subscribe --types client-session-changed`. Each event carries `client_id`, `pid`, `session` and `attached`. `attached` is `true` when a client enters a session and `false` when it leaves or disconnects. A session rename sends one event with the new name for each client in the session.

### detach-client

Detach attached clients from their sessions, as `tmux detach-client` does. The sessions continue to run. Each detached client exits with status 0 and prints "The tuios detach-client command detached this client."

```bash
tuios detach-client [--client ID | --session NAME] [--all-other] [--json]
```

- `--client <id>`: detach the client with this id, from `tuios list-clients`.
- `-s, --session <name>`: detach every client of this session.
- `-a, --all-other`: keep one client and detach every other client of its session.
- `--json`: print the ids of the detached clients.

The command detaches the first of these that applies:

1. The client that `--client` names.
2. Every client of the session that `-s` names.
3. From a pane, the client used last in the session of the pane.
4. The client used last in the only session with a client.

With `--all-other`, the command keeps the client that `--client` names, or the client used last in the session. It detaches every other client of that session.

From a pane, the command needs the `admin` grant. See [pane-grants](https://tuios.dev/docs/cli-reference#pane-grants-and-set-pane-grants).

```bash
tuios detach-client --client client-1790941960197517900
tuios detach-client -s work               # detach every client of work
tuios detach-client -s work --all-other   # keep the client used last in work
```

```json
{"detached":["client-1790941960197517900"]}
```

### ssh-agent-path

Print the ssh agent link of a session. With `ssh_agent = "follow"` in `[daemon]`, the link points at the agent socket of the client that attached to the session or used it last. New panes get `SSH_AUTH_SOCK` set to the link.

```bash
tuios ssh-agent-path [-s NAME] [--json]
```

- `-s, --session <name>`: the session whose link to print. In a pane, the default is the session of the pane. Outside a pane, `-s` is required.
- `--json`: print JSON, with `follow` and the socket that the link points at (`target`).

Without `--json`, the command fails when `ssh_agent` is not `"follow"`. A shell that started before you set the option keeps its old `SSH_AUTH_SOCK`. Add this line to your shell rc:

```bash
p=$(tuios ssh-agent-path 2>/dev/null) && export SSH_AUTH_SOCK="$p"
```

See [SSH Agent](https://tuios.dev/docs/ssh-agent).

### resurrect

Restore a session saved on disk. Alias: `restore`.

```bash
tuios resurrect               # list what can be restored
tuios resurrect work          # restore it and attach
```

The daemon restores saved sessions when it starts, so you need this only after `tuios daemon --no-restore`, or to bring back one session on demand. Each window gets a fresh shell in its saved directory. Running programs and scrollback are not restored. See [Session Resurrection](https://tuios.dev/docs/sessions#session-resurrection).

### rename-session

Rename a session.

```bash
tuios rename-session [session] <new-name> [flags]
```

- `-s, --session <name>`: the session to rename. Default: the session of the pane you run it in.

```bash
tuios rename-session test work   # rename another session
tuios rename-session work        # rename the session of this pane
```

The new name is what `tuios ls`, `attach` and `-s` use, and what new panes get as `TUIOS_SESSION`. Panes that already run keep the old name, and TUIOS commands from them still reach the session. `attach` and `kill-session` with the old name fail and name the new one. The rename also clears a label set with `set-session-name`.

A name that another running or saved session has is refused, and so is an empty name or one with a slash or a control character.

> **Older daemon**
>
> A daemon older than this command does not know it, and refuses. Run `tuios kill-server` and start TUIOS again.

### kill-session

Close every window in a session and disconnect its clients.

```bash
tuios kill-session <session-name>
```

> This also deletes the session's saved state, so it cannot be resurrected.

### kill-server

Stop the daemon and every session in it.

```bash
tuios kill-server
```

The command waits until the daemon has saved every session and removed its socket, so you can start a new daemon as soon as it returns. It fails if the daemon has not stopped within 10 seconds. Sessions are restored when the next daemon starts. To start clean, use `tuios daemon --no-restore`. With no daemon running, it removes a stale socket if there is one and exits `0`.

### daemon

Run the daemon in the foreground, for debugging.

```bash
tuios daemon [flags]
```

- `--log-level <level>`: `off` (default), `errors`, `basic`, `messages` (all protocol messages except PTY I/O), `verbose` (including PTY I/O), or `trace` (full hex dumps).
- `--no-restore`: do not restore saved sessions on start.

```bash
tuios daemon --log-level messages
```

`tuios start-server` starts the daemon in the background with no session. It is hidden from `--help` because commands start the daemon on their own.

### logs

Print the daemon's log.

```bash
tuios logs [flags]
```

- `-n, --lines <n>`: entries to show. Default `50`. `0` shows all.
- `--all`: show every stored entry.
- `-f, --follow`: keep printing new entries.
- `--clear`: empty the log after printing it.

The daemon keeps the last 1000 entries in memory. It also appends errors and basic events to `$XDG_STATE_HOME/tuios/daemon.log`, so a crash leaves a record. To log more, run `tuios set-config daemon.log_level messages`. The levels `verbose` and `trace` also record pane content, titles and paths.

## Worktrees

### worktree

Run a session in a git worktree. The rail groups these sessions under their repository and labels each with its branch.

```bash
tuios worktree new <branch> [flags]
tuios worktree ls [flags]
tuios worktree rm <session> [flags]
tuios worktree diff <session> [--stat]
tuios worktree pull <host:session> [flags]
```

`worktree new` creates a worktree and a session in it. A branch that does not exist is created from `--base`, or from `HEAD`. The session is named `<repo>-<branch>`, with slashes turned into hyphens. Worktrees go under `$XDG_DATA_HOME/tuios/worktrees/<repo>/<branch>`.

- `--repo <dir>`: a directory inside the repository. Default: the current directory.
- `--base <ref>`: the ref a new branch starts from. Default: `HEAD`.
- `--name <session>`: the session name.
- `--agent <cli>`: start an agent CLI instead of a shell, such as `claude`, `codex` or `gemini`.
- `-d, --detach`: do not attach.
- `--host <name>`: make the worktree on a machine from the `[hosts]` table, in its checkout of this repository. `--clone` clones it there when the host has none.
- `--json`: print JSON.

`worktree ls` lists worktree sessions with their repository, branch, agent state, uncommitted changes and fan prompt status. A row marked `gone` is a session whose worktree was deleted. Filter with `--repo <name>` or `--group <stem>`.

`worktree rm` runs `git worktree remove` and kills the session. It refuses a worktree with uncommitted changes and removes nothing. `--stash` keeps the changes in git stash, and `--force` discards them. `--keep-session` leaves the session running. The branch is never deleted.

`worktree diff` prints the commits the session made on top of its base, then its uncommitted changes, then its untracked files. `--stat` prints the summary form.

`worktree pull <host:session>` copies what a worktree session on another machine has done into a new worktree session here: its commits as a new branch, and its uncommitted work, untracked files included, applied uncommitted. `--branch` names the branch here, `--name` the session, `--repo` the repository, and `-d` does not attach. A branch that already exists here is refused.

```bash
tuios worktree new feat/retry --base main --agent claude --detach
tuios worktree ls
tuios worktree rm api-feat-retry --stash
```

### fan

Start the same prompt in several agents, each in its own worktree.

```bash
tuios fan <count> <prompt> --agent <cli> [flags]
tuios fan compare <session> [--no-changes]
tuios fan verify <session> -- <command>...
tuios fan diff <session-a> <session-b> [--stat]
tuios fan keep <session> [--stash | --force] [--merge [--squash | --ff-only] [--into <branch>]]
```

- `--agent <cli>`: the agent to run, as you type it, arguments included. Several, comma-separated or repeated, are cycled across the sessions. Required.
- `--prompt <text>`: one session's prompt. Repeat it once per session instead of one prompt for all.
- `--env NAME` or `--env NAME=VALUE`: pass a variable to every agent. `PATH` is always sent.
- `--base <ref>`: the ref every branch starts from. Default: `HEAD`.
- `--name <stem>`: the branch stem. Default: `fan/` and the first words of the prompt.
- `--repo <dir>`: a directory inside the repository.
- `--grants <list>`: what every agent may do through TUIOS. See [pane-grants](https://tuios.dev/docs/cli-reference#pane-grants-and-set-pane-grants).
- `--host <name>`: run the fan-out on a machine from the `[hosts]` table. `--clone` clones the repository there when it has none.
- `--wait`: return only when every prompt is sent or given up on.
- `--json`: print JSON.

The branches are the stem, then `stem-2`, `stem-3` and so on. Each prompt is typed once its agent is ready. `tuios worktree ls --group <stem>` shows whether each one was sent.

An agent that has not shown its prompt after 30 seconds puts a question in the Inbox asking you to look at its pane.

`fan compare` prints one line per attempt: its agent and state, the files and lines it changed against the fan's base, and its last check. `fan verify` runs a command in every attempt, in a window named `verify`, and exits `1` if any check failed. `--timeout` counts a slow check as failed, and `--no-wait` returns once the checks start. `fan diff` shows what the second attempt did differently from the first.

`fan keep` keeps one session and removes the others with the same stem, the way `worktree rm` does. A sibling with uncommitted changes is left in place unless you pass `--stash` or `--force`. `HOST:SESSION` works with `compare`, `verify` and `keep`.

`fan keep --merge` first merges the kept session's branch into its base in the main checkout, as [`ship merge`](https://tuios.dev/docs/cli-reference#ship) does. `--squash`, `--ff-only` and `--into <branch>` choose how. When the merge conflicts or is refused, it is undone and no sibling is removed.

```bash
tuios fan 3 --agent claude 'Add a retry with backoff to the HTTP client.'
tuios fan compare api-fan-add-a-retry-with
tuios fan verify api-fan-add-a-retry-with -- go test ./...
tuios fan keep api-fan-add-a-retry-with-2 --stash
tuios fan keep api-fan-add-a-retry-with-2 --merge --squash
```

See [Worktrees](https://tuios.dev/docs/worktrees#comparing-and-keeping-one).

### ship

Take an agent's work from its worktree to a merged change.

```bash
tuios ship commit [-m <message>] [--force]
tuios ship merge [--into <branch>] [--squash | --ff-only] [-m <message>]
tuios ship push [--remote <name>] [--yes] [--wait <duration>] [--request <id>]
tuios ship pr [--base <branch>] [--title <text> [--body <text>]] [--draft] [--remote <name>] [--yes]
tuios ship status [--refresh]
```

Every subcommand takes `-s, --session`, `-w, --window` and `--json`.

- `commit` stages every change in the pane's work tree, untracked files included, and commits it on its branch. Without `-m`, the message is the pane's last prompt. `--force` commits while the agent is working or waiting on a prompt.
- `merge` merges the branch into its base in the main checkout. A merge that conflicts is undone, and the command lists the files.
- `push` pushes the branch and sets its upstream. A push is never forced.
- `pr` pushes the branch and opens a pull request with your own `gh` login.
- `status` shows the pull request and its checks. `--refresh` asks `gh` now.

git uses your own identity, signing setup and hooks. `push` and `pr` show what they will send and ask `[y/N]` first. `-y, --yes` skips the question. Inside a TUIOS pane, they also need your yes in the Inbox. `--wait` sets how long to wait for it (default `2m`).

The pull request and its checks show on the agent's row in the rail, in `tuios worktree ls`, and as `pr` in `ls --json` and `worktree ls --json`.

```bash
tuios ship commit -s api-feat-retry -m 'Add a retry to the client'
tuios ship merge -s api-feat-retry --squash
tuios ship pr -s api-feat-retry --draft
```

See [Ship](https://tuios.dev/docs/ship).

## Remote control commands

The commands below drive a running session from a script or another pane. They are a thin layer over the daemon's [JSON control protocol](https://tuios.dev/docs/control-protocol). For more than a few calls, or to get structured errors and blocking waits, use the protocol directly. `tuios --skill` prints a guide to this surface for agents.

Most of them work on a detached session. The ones that need an attached client say so.

## Windows and workspaces

### new-window

Open a window and print its ID and name.

```bash
tuios new-window [name] [command...] [flags]
```

- `--workspace <n>`: the workspace to open it on. Default: the current one.
- `--cwd <dir>`: the starting directory. Default: the daemon's.
- `--no-focus`: leave the focus where it is.
- `--host <name>`: run the window's process on a machine from the `[hosts]` table. The window still belongs to this session.
- `--grants <list>`: what the window's process may do through TUIOS: `read`, `write`, `fan`, `respond`, `admin`, or `none`. See [pane-grants](https://tuios.dev/docs/cli-reference#pane-grants-and-set-pane-grants).
- `--print-id`: print only the full window ID, for `id=$(...)`.
- `--json`: print JSON.

Words after the name are a command the window runs instead of a shell. They are passed as they are, with no shell in between. Put `--` before a command that has flags of its own, or `tuios` reads them as its own flags. The window closes when the command exits.

```bash
tuios new-window build                      # prints: e0657732  build
tuios new-window top htop                   # run htop with no shell
tuios new-window logs -- tail -f /var/log/system.log
tuios new-window tests --workspace 2 --cwd /src/api --no-focus
tuios new-window --json | jq -r .window_id
```

### split-window

Split a pane and print the new pane's ID. Needs an attached client with tiling on.

```bash
tuios split-window <horizontal|vertical> [flags]
```

- `-w, --window <target>`: the pane to split.
- `--name <name>`: a name for the new pane.
- `--json`: print JSON.

### popup

Run a command in a floating pane over the layout. The pane closes when the command exits. Needs an attached client.

```bash
tuios popup [flags] -- <command> [args...]
```

- `--width <size>`: cells, or a percentage of the pane area. Default `80%`.
- `--height <size>`: cells or a percentage. Default `60%`.
- `--wait`: stay open until the command exits, and exit with its status. `--timeout <ms>` bounds the wait.
- `--capture-stdout`: print the command's standard output here instead of in the popup. Implies `--wait`. Not on Windows.
- `--name <name>`, `--cwd <dir>`, `--workspace <n>`, `--json`.

The size flags have no short form, because `-w` means window and `-h` means help. A picker such as fzf draws on the terminal and prints only the choice, so with `--capture-stdout` the choice is what this command prints.

```bash
file=$(tuios popup --capture-stdout -- fzf)
tuios popup --wait -- gum confirm "Deploy?" && ./deploy.sh
tuios popup --width 90% --height 80% -- htop
```

### xpanes

Open one pane per item in a new tiled workspace, and turn multifocus on for all of them. Then the keys you type go to every pane. This is like [tmux-xpanes](https://github.com/greymd/tmux-xpanes).

```bash
tuios xpanes [flags] [items...]
command | tuios xpanes [flags]
```

The items are the arguments. With no arguments, TUIOS reads one item from each line of stdin, when stdin is not a terminal. TUIOS ignores empty lines.

- `-c, --command <cmd>`: the command for each pane. TUIOS replaces `{}` with the item, in shell quotes.
- `-s`: speedy mode. The pane runs the command with `sh -c`, with no interactive shell, and stays until you press Enter.
- `-ss`: speedy mode. The pane closes when the command stops.
- `--interval <seconds>`: the time to wait between the panes, for example `0.5`.
- `-I, --replace <text>`: the text that TUIOS replaces with the item. Default `{}`.
- `--ssh`: run `ssh` with the item in each pane. The same as `-s -c 'ssh -- {}'`. The `--` makes sure that ssh does not read an item that starts with `-` as an option.
- `-l, --layout <name>`: `tiled`, `even-horizontal` or `even-vertical`, or `t`, `eh` and `ev`. Default `tiled`.
- `-n, --items-per-pane <n>`: the number of items for each pane. TUIOS joins them with spaces.
- `--no-sync`: do not turn multifocus on.
- `--workspace <n>`: the workspace for the panes. It must be empty. Default: the first empty workspace.
- `--session <name>`: default: the session of this pane, else the most recently active. It has no short form, because `-s` is speedy mode, as in tmux-xpanes.
- `--force`: open more than 64 panes.
- `--json`: print the result, with the window ids, as JSON.

Each pane starts a shell. With `-c`, TUIOS types the command into the shell, and the shell stays when the command stops, as in tmux-xpanes. With `-s`, the pane runs the command with no shell. When the command stops, the pane shows "The command stopped. Press Enter to close the pane." With `-ss`, the pane closes when the command stops. With `--interval`, TUIOS waits between the panes: between opening them with `-s` or `-ss`, and between typing the commands without them. Without `-c`, each pane is a shell. In a session on another machine, the daemon there chooses the shell, and the pane does not get the two variables below. Each pane gets the item in `TUIOS_XPANES_ITEM` and its number, from 1, in `TUIOS_XPANES_INDEX`. The item is the name of the pane. The panes start in the folder where you run `tuios xpanes`.

The layout and multifocus need a client attached to the session. The layout also needs tiling on and the `bsp` layout. When TUIOS cannot do one of them, it opens the panes and tells you what it did not do.

```bash
tuios xpanes --ssh host1 host2 host3
printf 'a\nb\nc\n' | tuios xpanes -c 'echo {}'
tuios xpanes -ss --interval 1 -c 'curl -s https://{}/health' web1 web2 web3
ls /var/log/*.log | tuios xpanes -l even-horizontal --no-sync -c 'tail -f {}'
tuios xpanes -n 2 -c 'diff {}' a.txt b.txt c.txt d.txt
```

### close-workspace

Close every pane on a workspace, like tmux `kill-window`. Without a number, TUIOS closes the panes of the current workspace.

```bash
tuios close-workspace [-s session] [workspace]
```

Scratch panes stay open. To close a scratch group, name its scratch workspace. A pane that runs this command needs the `admin` grant. The `close_workspace` action does the same from the keyboard. See [Close every pane on a workspace](https://tuios.dev/docs/keybindings#close-every-pane-on-a-workspace).

```bash
tuios close-workspace 2
```

### focus-window

Move the focus and print the pane that has it. Pass exactly one of a window, `--relative` or `--direction`.

```bash
tuios focus-window build
tuios focus-window --relative next     # next or prev on this workspace
tuios focus-window --direction left    # left, right, up or down
```

Naming a window switches to its workspace. `--direction` needs an attached client.

### pip

Pin a pane as the [picture-in-picture view](https://tuios.dev/docs/sessions#picture-in-picture) of the attached client, or unpin it.

```bash
tuios pip agent        # pin the pane named agent. Name it again to unpin it
tuios pip              # pin the focused pane
tuios pip --off        # unpin whatever is pinned
```

The view belongs to the client, not to the session, so the command needs an attached client. `--json` prints `pinned` and `window_id`.

### move-window

Move a window to another workspace.

```bash
tuios move-window <workspace> [-w <target>] [--follow] [--json]
```

`--follow` switches to that workspace after the move.

### set-window

Rename, minimize or restore a window. Pass only the flags you want to change.

```bash
tuios set-window --name "api tests"
tuios set-window -w build --name ""      # clear the custom name
tuios set-window -w build --minimize
tuios set-window -w build --restore
```

### set-layout

Turn tiling on or off, reset split ratios, or flip the focused split. Needs an attached client.

```bash
tuios set-layout --tiling true
tuios set-layout --equalize
tuios set-layout --rotate
```

### Workspaces and labels

| Command                               | What it does                                                                                                                                                                                                                  |
| ------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `tuios select-workspace <n>`          | Show a workspace                                                                                                                                                                                                              |
| `tuios list-workspaces`               | List workspaces with name, window count, and which one is showing                                                                                                                                                             |
| `tuios set-workspace-name <n> [name]` | Label a workspace. No name clears it                                                                                                                                                                                          |
| `tuios set-session-name [name]`       | Set the label the rail and dock show. The session keeps its real name for `-s` and `TUIOS_SESSION`. No name clears it. To change the name itself, use [`rename-session`](https://tuios.dev/docs/cli-reference#rename-session) |
| `tuios set-session-accent [accent]`   | Set the session's accent colour, shared by every client. No accent clears it                                                                                                                                                  |

```bash
tuios set-workspace-name 2 review
tuios list-workspaces --json | jq '.workspaces[] | select(.window_count == 0)'
```

## Input and output

### send-keys

Send keystrokes to a session.

```bash
tuios send-keys <keys> [flags]
```

- `-w, --window <target>`: the target window.
- `-l, --literal`: write the text to the pane's PTY with no key parsing.
- `-r, --raw`: send each character as a key. Do not split on spaces or commas.
- `-N, --repeat <n>`: send the whole sequence this many times, 1 to 1000.

With `-w`, the keys go to that window's program, whether or not a client is attached and whichever window has the focus. Without `-w`, they go to the attached client as if you pressed them, so window-mode bindings and the prefix work. With no client attached, they go to the focused window. The command prints where the keys went.

- Single keys: `i`, `Enter`, `Escape`, `Space`, `Tab`, `BTab`, `Backspace`, `Insert`, `Delete`, arrows (`Up` and so on), `Home`, `End`, `PageUp`, `PageDown`, `F1` to `F12`. The arrows, `Home` and `End` go in their SS3 form (`ESC O H`) when the program in the pane has turned on application cursor keys (DECCKM), as a real terminal sends them.
- Chords: `ctrl+c`, `alt+b`, `shift+Up`. Not case sensitive. tmux's `C-c` and `M-x`, and `^C`, work too.
- A named key or a chord goes in the keyboard mode the program in the pane asked for. TUIOS encodes it the same way as a key you press. A program that asked for the kitty keyboard protocol or modifyOtherKeys gets that encoding. For example, Vim with the kitty keyboard protocol gets `ctrl+h` as `ESC [ 104;5u`, not as Backspace. A shell gets `0x08`. Text, `--literal` and `--raw` keys are not changed.
- An escape sequence written as `\e[A`, `\x1b[A` or `\033[A`.
- Sequences: separate keys with a space or a comma: `"ctrl+b q"` or `"ctrl+b,q"`.
- `PREFIX` and `$PREFIX` expand to your leader key, without `-w` and with a client attached. In bash and zsh, use single quotes so the shell does not expand `$PREFIX` first: `tuios send-keys 'PREFIX q'`.

From inside a pane, `send-keys` works differently:

- `send-keys` with `PREFIX` is refused. The message names the TUIOS command to use instead, such as `focus-window`, `new-window` or `split-window`.
- Without `-w`, the keys go to the focused pane. TUIOS does not read them as key bindings.
- The Inbox does not take an answer that `send-keys` typed.

A word that looks like a misspelled key, such as `Dwon` or `F13`, is refused with the list of names, and nothing is sent. To type text, use `send-text`: `send-keys` splits on spaces, so `'echo hello'` types `echohello`.

```bash
tuios send-keys i                          # enter terminal mode
tuios send-keys 'PREFIX d'                 # detach the attached client
tuios send-keys -w docs Down --repeat 10   # scroll a pager
tuios send-keys -w build ctrl+c
tuios send-keys -w build --literal --raw "make test"
tuios send-keys -w build Enter
```

> **--literal does not parse keys**
>
> `tuios send-keys --literal Enter` types the five letters `Enter`. To run a command, send the text with `--literal --raw`, then `Enter` in a second call. Or use `send-text` with a trailing newline.
>
> On a session with no client attached, keys go straight to the pane, so prefix sequences do nothing.

### send-text

Write text to a pane exactly as given. Nothing is parsed. End the text with a newline to run it.

```bash
tuios send-text -w build 'go build ./...
'
tuios send-text -w build 'partial input'         # type without running
printf 'go test ./...\n' | xargs -0 tuios send-text -w build
```

### run

Type one command line at a pane's shell prompt, wait for it to finish, print what it printed, and exit with its exit status.

```bash
tuios run -w build --timeout 600000 -- go test ./... && echo passed
tuios run -w build --lines 40 -- make
tuios run -w build --json -- make lint
```

- `--lines <n>`: keep only the last `n` lines of the output.
- `--timeout <ms>`: how long to wait. Default `30000`. A timeout does not stop the command, and the error names the `wait-for` command that picks it up.
- `--json`: print JSON with the output, the exit code and how long it took.

`run` reads where the command starts and ends from the shell's OSC 133 marks, so the pane's shell needs prompt integration. A pane whose shell sends no marks is refused with `no_shell_integration`, and a pane already running a command with `not_at_prompt`. Nothing is typed in either case. `tuios doctor shell` says which panes have the marks and prints the lines that turn them on for zsh, bash and fish.

The words after `--` are joined with spaces into one line that the shell parses, so quote them as you would when typing.

### capture-pane

Print a pane's content.

```bash
tuios capture-pane [flags]
```

- `-w, --window <target>`: the pane.
- `-S, --scrollback`: include the scrollback, not only the screen.
- `--lines <n>`: keep only the last `n` lines. `0` keeps all.
- `--ansi`: keep colour and style escape codes.
- `--resolved`: rewrite indexed colours to 24-bit RGB, so the capture matches what a themed client draws.
- `--palette <colours>`: 16 comma-separated `#rrggbb` colours for `--resolved`. Default: the xterm colours.
- `--last-command`: only what the last finished command printed. Needs a shell with OSC 133 marks, like `run`.

```bash
tuios capture-pane -w build --scrollback --lines 40   # the tail of a build
tuios capture-pane --ansi > pane.txt
```

`--lines` counts from the last line with content, so blank rows below the cursor do not count.

### screenshot

Render a pane to an image file and print its path. The daemon renders it, so this works on a detached session.

```bash
tuios screenshot [flags]
```

- `-w, --window <target>`: the pane.
- `-f, --format <fmt>`: `png`, `svg`, `ansi`, `html` or `txt`.
- `--frame <style>`: `window`, `plain` or `none`.
- `--theme <name>`: render in this theme instead of the session's.
- `-o, --out <path>`: write here instead of a generated name under `screenshot.directory`.
- `-S, --scrollback`: put the scrollback above the screen.
- `--lines <n>`: limit the scrollback to the last `n` rows.
- `--cursor`: draw the cursor.
- `--copy`, `--no-copy`: try, or do not try, to copy the image to the clipboard.
- `--json`: print JSON with `path`, `host`, `format`, `cols`, `rows`, `bytes`, `copied` and `warnings`.

```bash
tuios screenshot -s work -w build
tuios screenshot --format svg --out demo.svg
tuios screenshot --theme catppuccin_mocha
```

The picture is drawn from the pane's cells, so colours, styles and links are exact. `png` and `svg` include the frame. `ansi` and `txt` do not. The `[screenshot]` [config table](https://tuios.dev/docs/configuration#the-screenshot-table) sets the defaults.

With no theme set, basic colours use the xterm defaults, because only your terminal knows its real palette. The result warns about this. Pass `--theme` to render in a named palette.

Region and full-screen captures need an attached client: press Ctrl+B C in the TUI.

### wait-for

Block until a condition matches, then exit `0`. The daemon watches its own events, so there is no polling.

```bash
tuios wait-for <condition> [flags]
```

| Condition          | Matches when                                                                                                                                  |
| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `session-exists`   | The session named with `-s` exists                                                                                                            |
| `window-output`    | The window's output matches `--pattern`                                                                                                       |
| `window-exit`      | The window's shell exits                                                                                                                      |
| `window-idle`      | The window prints nothing for `--idle` ms                                                                                                     |
| `agent-state`      | An agent reaches one of the `--until` states. Without `-w`, any agent in the session                                                          |
| `agent-message`    | Mail arrives. With `-w`, unread mail in that inbox, including mail sent before the wait. Without `-w`, any message sent after the wait starts |
| `command-finished` | A shell with OSC 133 marks finishes a command. With `-w`, that pane's next command. Prints the exit code                                      |

- `--pattern <regexp>`: required by `window-output`.
- `--until <states>`: comma-separated states, required by `agent-state`.
- `--any-session`: for `agent-state`, watch every session.
- `--select <selector>`: for `agent-state`, watch the agent panes a [selector](https://tuios.dev/docs/agent-inbox#selectors) matches, in every session. `--every` waits until every one of them is in an `--until` state.
- `--command-seq <n>`: for `command-finished`, match the first command after `n` finished ones. It matches at once when that already happened.
- `--idle <ms>`: silence that counts as idle. Default `500`.
- `--thread <id>`: for `agent-message`, only this thread.
- `--timeout <ms>`: give up after this long and exit non-zero. Default `30000`.
- `--json`: print JSON.

```bash
tuios wait-for window-output -w build --pattern 'BUILD_OK' --timeout 120000
tuios wait-for window-idle -w build --idle 2000
tuios wait-for agent-state -s work --until needs_input
tuios wait-for agent-state --select 'group:fan/add-retry' --until idle,done --every --timeout 3600000
tuios wait-for command-finished -w build --timeout 600000
```

> **The pattern also matches old output**
>
> `window-output` searches the scrollback, so text printed before the wait started matches at once. A pane also echoes the command you sent. If the marker appears in the command, the wait matches the echo. Build the marker so it only appears in the output, such as `printf "BUILD_%s\n" OK`, or use `window-exit` in a window opened for one command.

### subscribe

Print the daemon's event stream, one JSON object per line.

```bash
tuios subscribe --types agent-state
tuios subscribe -s work --types window-created,window-closed
tuios subscribe --types bell --count 1
tuios subscribe --hosts --types agent-state,host-changed
```

- `--types <list>`: only these event types.
- `-s, --session <name>`: only this session. Without it, the stream covers every session, unlike most commands.
- `-w, --window <id>`: only events about this window.
- `--count <n>`: exit after this many events.
- `--hosts`: also the agent-state and session events of every linked host.
- `--after-seq <n>` and `--boot-id <id>`: resume after a disconnect. The first line is an ack carrying `seq` and `boot_id`. When the daemon cannot replay everything you missed, a `gap` line says why, and you should read current state again.

See [Control Protocol](https://tuios.dev/docs/control-protocol) for the event types.

## Inspection

These commands read the daemon's state and work with no client attached.

### list-windows

List the windows in a session.

```bash
tuios list-windows [--json]
tuios list-windows --all [--all-hosts] [--text N] [--json]
```

The table shows index, ID prefix, name, workspace, size and agent state. `*` marks the focused window. The index and the ID prefix both work with `-w`.

`--all` lists the panes of every session on this machine, one row each. `--all-hosts` adds every session on each host in `[hosts]`. `--text N` adds the last N lines of each pane's screen, up to 200. These are the rows the [pane navigator](https://tuios.dev/docs/find-a-pane) shows. Each row has `session`, `host` (with `untrusted` for another machine), `workspace`, `workspace_name`, `window_id`, `name`, `title`, `command`, `cwd`, `focused`, `agent_state`, and with `--text`, `text` or `text_error`. The text is read with `capture-pane`, under the same grants. Text from another machine is printed inside the untrusted fence.

```json
{
  "success": true,
  "message": "command executed",
  "windows": [
    {
      "window_id": "15ff15b6-472e-4b22-b5e6-a709d82ded31",
      "index": 1,
      "title": "dev",
      "custom_name": "dev",
      "display_name": "dev",
      "workspace": 1,
      "focused": false,
      "minimized": false,
      "x": 0,
      "y": 0,
      "width": 80,
      "height": 24,
      "pty_id": "3f42acd9-5c9f-4f93-a2dd-c7af358cae33",
      "agent_state": "none"
    }
  ],
  "total": 3,
  "focused_index": 2,
  "focused_window_id": "e0657732-bf81-4636-8b24-6052f7ad76fe",
  "current_workspace": 1,
  "workspace_windows": [3, 0, 0, 0, 0, 0, 0, 0, 0]
}
```

- The ID field is `window_id`, and the focused window is `focused_window_id`.
- `shell` is the base name of the shell that a new pane in the session runs. `foreground_cmd` is not on a pane at its prompt, so `shell` says what that pane runs.
- `custom_name` appears only on renamed windows. `display_name` is always present.
- `cwd` appears when the shell has reported its directory.
- `pid` is the process the pane started, and `tty` is its terminal device. Both appear only for a pane whose process runs on this machine.
- `zoomed: true` marks a zoomed window. `history_rows` counts the scrollback lines above the screen.
- `workspace_windows` counts windows per workspace, starting with workspace 1.

### get-window

Print one window. With no argument, the focused window.

```bash
tuios get-window [id-or-name] [--json]
```

The JSON is the same window object as in `list-windows`, merged into the top level next to `success` and `message`. There is no cursor position. To read the screen, use `capture-pane`.

### session-info

Print the session's state.

```bash
tuios session-info [--json]
```

```json
{
  "success": true,
  "message": "command executed",
  "session_name": "work",
  "session_id": "04623592-051c-49d5-9a29-69a2e2ccdb33",
  "display_name": "",
  "accent": "",
  "mode": "unknown",
  "current_workspace": 1,
  "num_workspaces": 9,
  "workspace_names": {},
  "workspace_order": null,
  "window_count": 3,
  "tiling_mode": "floating",
  "layout_mode": "unknown",
  "master_ratio": 0.5,
  "width": 80,
  "height": 24,
  "tui_attached": false
}
```

- `tiling_mode` is only `tiling` or `floating`. The layout name, such as `bsp`, is `layout_mode`, which is `unknown` when none is set.
- `mode` is always `unknown`. Do not use it.
- The theme and other options are not here. Read them with `get-config`.

### list-verbs

List the control protocol's verbs, with parameters, examples, error codes and the protocol version.

```bash
tuios list-verbs                 # every verb
tuios list-verbs capture-pane    # one verb
tuios list-verbs --json
```

These are kebab-case protocol verbs. They are a different set from the PascalCase `run-command` verbs. See [Control Protocol](https://tuios.dev/docs/control-protocol).

## run-command

Run one tape command in a session, or any keybinding action by its name. Tape commands use PascalCase names. Actions use the snake\_case names that `tuios keybinds list` prints.

```bash
tuios run-command <command> [args...] [flags]
```

- `--list`: print every command.
- `--json`: print the result as JSON.

| Command                                                     | Arguments                                   | What it does                                                                                                                                                                                                            |
| ----------------------------------------------------------- | ------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `NewWindow`                                                 | `[name]`                                    | New window                                                                                                                                                                                                              |
| `CloseWindow`                                               | `[name]`                                    | Close the focused window, or every window with that name                                                                                                                                                                |
| `NextWindow`, `PrevWindow`                                  |                                             | Focus the next or previous window                                                                                                                                                                                       |
| `FocusWindow`                                               | `<name>`                                    | Focus a window                                                                                                                                                                                                          |
| `RenameWindow`                                              | `<name>` or `<old> <new>`                   | Rename the focused window, or a named one                                                                                                                                                                               |
| `MinimizeWindow`, `RestoreWindow`                           | `[name]`                                    | Minimize or restore the focused or named window                                                                                                                                                                         |
| `TerminalMode`, `WindowManagementMode`                      |                                             | Switch mode                                                                                                                                                                                                             |
| `ToggleTiling`, `EnableTiling`, `DisableTiling`             |                                             | Change tiling                                                                                                                                                                                                           |
| `SnapLeft`, `SnapRight`, `SnapFullscreen`                   |                                             | Snap the focused window                                                                                                                                                                                                 |
| `Split`                                                     | `horizontal` or `vertical`                  | Split the focused window                                                                                                                                                                                                |
| `RotateSplit`, `EqualizeSplits`                             |                                             | Flip the split, or reset ratios                                                                                                                                                                                         |
| `ArrangePanes`                                              | `<layout> [workspace [window...]]`          | Lay out the panes of the workspace again as `tiled`, `even-horizontal` or `even-vertical`. The named windows come first. With a workspace, the command fails when that workspace is not showing. Needs the `bsp` layout |
| `SetMultifocus`                                             | `[window...]`                               | Put exactly these windows in multifocus. Each window must be on the showing workspace and not minimized. From a pane, each window must be one that the pane can type into. With no window, empty the set                |
| `Screenshot`                                                |                                             | Save the focused window as an image                                                                                                                                                                                     |
| `SwitchWorkspace`                                           | `1`-`9`                                     | Show a workspace                                                                                                                                                                                                        |
| `MoveToWorkspace`                                           | `1`-`9`                                     | Move the focused window there                                                                                                                                                                                           |
| `EnableAnimations`, `DisableAnimations`, `ToggleAnimations` |                                             | Change animations                                                                                                                                                                                                       |
| `SetDockbarPosition`                                        | `top`, `bottom`, `hidden`                   | Move the dock                                                                                                                                                                                                           |
| `SetBorderStyle`                                            | `<style>`                                   | Change the border style                                                                                                                                                                                                 |
| `SetTheme`                                                  | `<theme>`                                   | Change the theme                                                                                                                                                                                                        |
| `ShowNotification`                                          | `<message> [info\|success\|warning\|error]` | Show a notification                                                                                                                                                                                                     |
| `ListWindows`, `GetWindow`, `GetSessionInfo`                |                                             | Same data as `list-windows`, `get-window` and `session-info`                                                                                                                                                            |
| `<action>`                                                  |                                             | Run a keybinding action, such as `toggle_spotlight` or `command:build`. `Action <action>` is the same                                                                                                                   |
| `Press`                                                     | `<keys>`                                    | Press keys through TUIOS, as you would: the leader, prefixes, copy mode and dialogs get them                                                                                                                            |
| `Run`                                                       | `<command line>`                            | Type the line into the focused window and press Enter                                                                                                                                                                   |

Some commands need an attached client, such as `SetDockbarPosition`, every action and `Press`. On a detached session they fail with a message saying so.

> **CloseWindow with a name closes every match**
>
> Two windows titled `zsh` are both closed. Pass no name to close only the focused window.

`--json` merges the command's result into the top level:

```json
{ "success": true, "message": "command executed", "window_id": "459a7fc9-a1fd-4770-86ac-0bc60b9598b9", "name": "dev2" }
```

On failure the object is `{"success": false, "message": "..."}` and the exit code is `1`. Check `.success`, not the message text.

```bash
tuios run-command --json NewWindow dev | jq -r .window_id
tuios run-command EnableTiling
tuios run-command -s work SwitchWorkspace 2
```

In scripts, prefer `EnableTiling` over `ToggleTiling`, so running the script twice gives the same result.

## Configuration

### config

```bash
tuios config path     # print the config file path
tuios config edit     # open it in $EDITOR
tuios config reset    # overwrite it with the defaults
```

```bash
tuios config apply    # apply config.toml to the running daemon now
tuios config browse   # search the options and set one, in an explorer
```

```bash
tuios config files              # list the files the config is read from, in merge order
tuios config origin [key]       # show which file sets each key
tuios config prune [--dry-run]  # remove the keys of config.toml that have their default value
```

`config edit` uses `$EDITOR`, then `$VISUAL`, then the first of `vim`, `vi`, `nano` and `emacs` it finds. `config reset` asks first. It writes the first-start `config.toml` again: comments, `[startup]` and no other setting. It keeps the `include` list and lists the other files that still apply.

`config files` lists the included files, the `config.d` files and `config.toml`, from lowest to highest precedence. It marks a read-only file. `config origin` names the file that sets each key, and the earlier files that set it too. Give a key to show that key and the keys under it. Both take `--json`.

`config prune` removes the keys of `config.toml` that have their default value. It lists the keys that another file sets, and asks before it changes the file. `--dry-run` changes nothing. `--yes` skips the question, and a run with no terminal needs it. The `[startup]` keys, the comments and the `include` list stay. See [Split the Config](https://tuios.dev/docs/config-files).

`config apply` applies changes to `config.toml` that give panes or other machines more. The daemon applies a change that gives less at once. A change that gives more waits for `config apply` or a daemon restart. This covers `[agents.permissions]`, `[hosts]` and link policies. Run `config apply` from a terminal outside TUIOS. It is refused from inside a pane. It prints each change that it applied. See [Changes that wait](https://tuios.dev/docs/agents#changes-that-wait).

### set-config

Change an option in a running session. The config file is not changed.

```bash
tuios set-config <path> <value>
```

```bash
tuios set-config appearance.border_style thick
tuios set-config appearance.sidebar.enabled true
tuios set-config daemon.log_level messages
```

A path is the option's dotted name, such as `screenshot.format`. An `[appearance]` option also answers to its bare name, so `border_style` works. An unknown path or value is refused, and the error lists what is accepted. Use `list-options` to find paths.

`set-config` cannot change `[keybindings]`, `[hooks]`, `[hosts]`, `[tailscale]`, `[agents]` or the `[dock]` lists. Edit the file for those.

### get-config

Read an option from a running session.

```bash
tuios get-config border_style
tuios get-config appearance.sidebar.position --json
```

An option with no session override reads as its default. `--json` adds `source` (`session` or `default`) and `default`.

### list-options

List every path `set-config` accepts, with type, default, accepted values and a description.

```bash
tuios list-options                        # everything
tuios list-options --section sidebar      # one group
tuios list-options appearance.sidebar.    # a path prefix
tuios list-options --search "pane bg"     # fuzzy search, best match first
tuios list-options --json | jq -r '.options[].path'
```

`--search` ranks every option by its path, its value and its description, the same search the settings page runs.

A session override is shown next to the default. The default of `startup.daemon` and `startup.tiled` is the one that applies on this machine: `false` when your config file has no `[startup]` table. Each JSON row has `path`, `type`, `section`, `description` and `default`, plus `accepted`, `min`, `max`, `deprecated` and `session_value` when they apply.

### list-themes

List themes, or show one theme's colours and their contrast against its background.

```bash
tuios list-themes --filter catppuccin
tuios list-themes catppuccin_mocha
tuios list-themes --json | jq -r .active
```

It reads the themes directory again on every call, so a new theme file shows up at once.

### import-theme

Convert a kitty, Ghostty, Alacritty or WezTerm colour scheme into a TUIOS theme file.

```bash
tuios import-theme ~/.config/kitty/current-theme.conf
tuios import-theme ~/gruvbox.toml --name gruvbox
tuios set-config appearance.theme gruvbox
```

The format is detected from the content. Colours the scheme does not set use the xterm defaults. `--name` sets the theme id. The theme can be selected at once.

### list-glyphs

List glyph sets, or show what one set draws, role by role.

```bash
tuios list-glyphs
tuios list-glyphs heavy
tuios list-glyphs mine --json | jq -r '.problems[]?'
```

`config browse [-s <session>]` opens the options you can set in an explorer. Each option shows its type, default, current value and source. These are the values that `list-options --json` and `get-config --json` print. Press Enter on an option to set it. The explorer sets it the same way as `set-config`, with the same refusals and errors. It needs a running daemon.

See [Glyph sets](https://tuios.dev/docs/configuration#glyph-sets).

### keybinds

Inspect and change keybindings. Aliases: `keys`, `kb`.

| Command                                | What it does                                                          |
| -------------------------------------- | --------------------------------------------------------------------- |
| `tuios keybinds list`                  | Print the main binding tables                                         |
| `tuios keybinds list-custom`           | Print bindings that differ from the defaults, in 9 of the 23 sections |
| `tuios keybinds browse [search]`       | Search every binding in an explorer, with one tab for each scope      |
| `tuios keybinds doctor`                | Report keys claimed twice, and keys TUIOS takes from the pane         |
| `tuios keybinds explain <key>`         | Print every scope a key acts in, and which programs want it           |
| `tuios keybinds unbind <action> [key]` | Take one key, or every key, off an action and save the file           |
| `tuios keybinds free <key>`            | Take a key off every action in every scope and save the file          |

`doctor` and `explain` take `--json` and `--guest <program>`. Each finding in `doctor` has an evidence tier: `certain` (TUIOS routing), `observed` (read from a pane) or `reference` (a list of common program defaults).

`unbind` and `free` write the action as an empty list, so the default does not come back at the next load. `free` cannot take the leader key, which you move with `keybindings.leader_key`, or the few keys the input path reads directly. It says so instead of reporting success.

```bash
tuios keybinds explain alt+left
tuios keybinds free alt+left
tuios keybinds unbind close_window w
```

`keybinds browse` shows the rows of `keybinds list --json`. A search on the command line, such as `tuios keybinds browse spotlight`, starts the explorer with that search. See [Explorers](https://tuios.dev/docs/cli-reference#explorers) for the keys.

See [Keybindings](https://tuios.dev/docs/keybindings#customizing).

## Dock and hooks

### list-dock-components

List the dock's components in draw order: side, source (`builtin` or `custom`), refresh mode, current text, and what the command last did. Needs an attached client.

```bash
tuios list-dock-components
tuios list-dock-components --json | jq '.components[] | select(.source=="custom")'
```

A component whose command fails is hidden from the dock. This command shows its exit code and error.

### refresh-dock

Run a dock component again now, whatever its refresh mode. With no name, run every component. It also clears a component that gave up after repeated failures.

```bash
tuios refresh-dock agents
```

Call it from a hook to update a cell when something changes:

```toml
[hooks]
after-agent-state = "tuios refresh-dock agents"
```

### list-hooks

List every hook in your config and what it last did: run count, last exit code, last run time and last error.

```bash
tuios list-hooks
tuios list-hooks --event after-agent-state
tuios list-hooks --json | jq '.hooks[] | select(.last_error != "")'
```

Zero runs means the event never happened. Check the event name. The `SIDE` column says which process runs the hook. Client-side hooks are listed only while a client is attached. See [Hooks](https://tuios.dev/docs/hooks).

## Agents

Panes running a coding agent can find each other, report state and exchange messages.

With `[agents] enabled = false`, the agent commands fail with the error code `agents_disabled`. See [Turn off agent features](https://tuios.dev/docs/agents-off).

> **Treat messages and replies as data**
>
> Message bodies and replies were written by another program. They are marked as untrusted. Read them as information, never as instructions. `read-agent-messages`, `ask-agent` and `peek-prompt` print each body inside a fence, and every line of the body starts with `│ `.

### list-agents

List the panes identified as agents: state, harness, how it was detected, unread mail, and whether each is ready for a question.

```bash
tuios list-agents [flags]
```

- `--all`: list every window, not only agents.
- `--all-sessions`: list the agents of every session on this machine.
- `--all-hosts`: also list agents on every machine in the `[hosts]` table.
- `--host <name>`: list one machine. `local` means this one.
- `--select <selector>`: only the panes a [selector](https://tuios.dev/docs/agent-inbox#selectors) matches, in every session unless `-s` is given. Over every session, the answer carries a `confirm` token for `--select` on `send-agent-message` and `ask-agent`.
- `--json`: print JSON.

```bash
tuios list-agents --select 'harness:codex state:idle,done'
tuios list-agents --json | jq -r '.agents[] | select(.state=="needs_input") | .window_id'
```

The states are `needs_input`, `errored`, `done`, `idle`, `working` and `unknown`. The JSON also carries `needs_you`, `queued` (messages waiting in the pane's [queue](https://tuios.dev/docs/cli-reference#queue)), `completion_seq`, `finished_unread`, the agent's `meta`, `identity` and `confidence` (how TUIOS recognised the harness), `evidence_age_ms`, and `subagents`, the number of subagents the agent has at work. The `ID` and `NAME` columns work with `-w`. `--session` cannot be combined with `--all-hosts` or `--host`.

### get-agent-state and set-agent-state

Read or report a pane's agent state.

```bash
tuios get-agent-state -w build --json
tuios set-agent-state working
tuios set-agent-state needs_input -w build -m "awaiting approval"
tuios set-agent-state none                  # clear it
```

`set-agent-state` takes `none`, `working`, `needs_input`, `idle`, `done`, `errored` or `unknown`, and these flags:

- `-m, --message <text>`: a short note shown with the state.
- `--source <source>`: `report` (default), `osc`, `screen` or `stall`.
- `--harness <id>`: the harness the state is about, such as `claude-code`.
- `--kind <kind>`: what a `needs_input` state waits for, `approval` or `question`. The Inbox groups it by this.
- `--if-state <states>`: apply only when the pane is in one of these states.
- `--agent-session-id <id>`: the harness's conversation id, stored for a later resume.

Sources are ranked in that order, highest first. A source cannot overwrite a state set by a higher-ranked source. A refused report prints why on stderr and changes nothing. The [harness integrations](https://tuios.dev/docs/cli-reference#integration) report for you.

`get-agent-state --json` carries `identity`, `confidence` and `evidence_age_ms`: the milliseconds since the last evidence about the state arrived, or `null` for a pane that never had a state. It also carries `subagents`, the number of subagents the pane's agent has at work. See [Agents](https://tuios.dev/docs/agents#reading-the-state).

### set-agent-meta and set-agent-session

`set-agent-meta` records short facts about the agent in a pane, such as its model or how full its context is. The rail draws them on the agent's row. They never change the state.

```bash
tuios set-agent-meta -w "$TUIOS_PANE_ID" --source statusline --ttl 60s model=opus context=42%
tuios set-agent-meta summary=               # remove one key
tuios set-agent-meta --source statusline --clear
```

Keys are lower-case letters, digits, `_` and `-`, and values are cut to 80 characters. `--ttl` drops the keys after that long. The metadata clears when the agent leaves the pane. tuios writes the keys `now`, `prompt` and `subagents` from the harness hooks. `set-agent-meta` refuses them, and `--clear` does not remove them.

`set-agent-session <id> --harness <id>` stores a harness's conversation id on a pane without changing its state, so the conversation can be resumed later.

### start-agent

Start an agent in a new pane, and return once it shows it is at its prompt.

```bash
tuios start-agent <agent> [-- args...] [flags]
```

- `--name <name>`: the pane's name, which `list-agents` shows and `-w` takes.
- `--prompt <text>`: a first prompt, typed once the agent is ready.
- `-s, --session <name>`: the session to open the pane in, created if it does not exist. `HOST:SESSION` starts it on another machine.
- `--cwd <dir>` or `--repo <dir>`: where it starts. Default: the focused pane's directory.
- `--grants <list>`: what the agent may do through TUIOS.
- `--protocol acp|codex`: run the agent headless over the Agent Client Protocol or the Codex app-server. The pane shows the conversation as a transcript you type into.
- `--env`, `--focus`, `--workspace <n>`, `--ready-timeout <ms>` (default `120000`), `--clone`, `--json`.

```bash
tuios start-agent claude --name reviewer
tuios ask-agent -w reviewer 'review the diff on this branch'
tuios start-agent --protocol acp 'opencode acp' --name helper --prompt 'Summarise the TODOs.'
```

An agent that stops on a question of its own, such as whether to trust the folder, is not ready: the command says what it waits on and exits non-zero, and the pane is kept for you to answer.

### send-agent-message

Leave a message in another pane's inbox, or post a notice to the whole session. It does not type into the pane.

```bash
tuios send-agent-message <text> [flags]
```

- `-w, --window <target>`: the recipient. Omit it to post a session-wide notice.
- `--from <id>`: the sending window, normally `"$TUIOS_PANE_ID"`.
- `--subject <text>`: a one-line summary, up to 120 characters.
- `--attach <path>`: an absolute path to a file. Repeat up to 8 times.
- `--reply-to <id>`: answer a message. The reply joins its thread.
- `--select <selector>`: send one message to every agent pane a [selector](https://tuios.dev/docs/agent-inbox#selectors) matches, in every session. It lists the panes and sends nothing until you say yes, pass `--yes`, or pass `--confirm` with the token `list-agents --select` printed.
- `--json`: print JSON.

```bash
tuios send-agent-message -w build --from "$TUIOS_PANE_ID" 'rebased onto main, please retest'
tuios send-agent-message -w build --from "$TUIOS_PANE_ID" --reply-to 12 'retested, still green'
```

The recipient has to read its inbox. For an agent that does not, use `ask-agent`. Messages are kept in memory: they are lost when the daemon stops, and the oldest are dropped when the queue is full.

### read-agent-messages

Read messages.

```bash
tuios read-agent-messages -w "$TUIOS_PANE_ID" --unread
tuios read-agent-messages --limit 50
tuios read-agent-messages --thread 12
```

- `-w, --window <target>`: read this pane's inbox and mark what it returns as read.
- `--unread`: only unread messages.
- `--notices`: include session-wide notices in an inbox read.
- `--peek`: do not mark anything read.
- `--thread <id>`: one conversation. Any message ID in the thread works.
- `--limit <n>`: at most this many, newest last. Default `20`.

Without `-w`, it reads every message and marks nothing.

### ask-agent

Wait until an agent is not busy, type a question into its pane, wait for it to finish, and print what the pane showed.

```bash
tuios ask-agent -w review --from "$TUIOS_PANE_ID" 'does the retry path look right to you?'
```

- `--from <id>`: the asking window. Without it there is no loop detection.
- `--ready-timeout <ms>`: how long to wait for the target to be ready. Default `30000`.
- `--settle <ms>`: for a pane that reports no state, silence that counts as done. Default `2000`.
- `--timeout <ms>`: how long to wait for the answer. Default `300000`.
- `--lines <n>`: cap the reply. Default `200`.
- `--force`: send without waiting for the target to be ready.
- `--stall-timeout <ms>`: how long after Enter the target has to show it took the question. Default `5000`. Past it the call fails with `prompt_stalled`.
- `--allow-blocked`: type at a target on `needs_input`. The text answers its prompt, so read the prompt first.
- `--select <selector>`, `--yes`, `--confirm <token>`: ask every agent pane a selector matches, at most 16, after confirmation.

It refuses to type at an agent on `needs_input` (`agent_blocked`), since the question would answer its prompt, and it refuses an ask that closes a loop with one already in flight, so two agents cannot deadlock on each other.

### ask-human

Ask the person a question with fixed answers, wait for them to pick one, and print it.

```bash
answer=$(tuios ask-human 'Deploy to staging?' -o yes -o no) && echo "$answer"
tuios ask-human 'Which region?' -o us -o eu --no-wait
tuios ask-human --request-id 9f86d081884c7d65
```

- `-o, --option <answer>`: an answer, 1 to 9 of them.
- `--timeout <ms>`: how long to wait. Default `120000`, at most an hour.
- `--no-wait`: ask and return at once. The answer arrives as mail.
- `--request-id <id>`: come back for a question already asked.

It exits `0` with the answer, `2` when there is no answer yet (the question stays, and the answer is mailed to the asking pane later), and `1` when the question ended without one. Only a person at an attached client can answer. See [Questions an agent asks you](https://tuios.dev/docs/agent-inbox#questions-an-agent-asks-you).

### Diagnosing detection

```bash
tuios explain-agent-detect -w build          # what the process detector saw
tuios explain-agent-screen --harness codex   # what a harness's screen rules make of the pane
```

`explain-agent-screen --lines <n>` reads that many lines from the bottom.

### stash

Copy a file into the session's store, so another agent can read it after you delete yours.

```bash
path=$(tuios stash put /tmp/flame.png)
tuios send-agent-message -w review --attach "$path" 'the hot path is in decode'
tuios stash list
tuios stash get -s build:api <stored-path> flame.png
```

- `stash put <file>`: the path must be absolute and readable by the daemon's user. Files are stored by content, so the same bytes return the same path. The limits are 16 MB per file and 256 MB per session. When a session is full, the oldest files that no message points at are removed.
- `stash list`: list stored files, oldest first. `USED` marks files a message still points at.
- `stash get <stored-path> [file]`: copy a file from a session on another machine. Up to 8 MB. On this machine, open the stored path directly.

Stored files last until the session is killed or the daemon stops.

## Inbox and review

These commands read and act on the [Inbox](https://tuios.dev/docs/agent-inbox), the queue and the review. Acts that are yours alone, such as dismissing or snoozing an item, are in the TUI, not here.

### list-attention

List the Inbox: approvals and questions agents are blocked on, mail to you, errored agents, conversations to resume, and finished turns nobody has looked at, in every session and on every linked host.

```bash
tuios list-attention
tuios list-attention --kind approval --kind question
tuios list-attention --host build
tuios list-attention --select 'harness:codex group:fan/add-retry'
tuios list-attention --json --kind approval | jq -r '.items[0].window'
```

- `--kind <kind>`: only these kinds: `approval`, `plan`, `ask`, `question`, `mail`, `errored`, `resume`, `finished`, `outbox`.
- `--host <name>`, `-s, --session <name>`: only this machine or session.
- `--select <selector>`: only the items a selector matches.
- `--snoozed`: also list the items you snoozed, with when each opens again.

`tuios subscribe --types attention` streams every change.

### peek-prompt and respond

Read the prompt an agent is blocked on, and answer it, without attaching.

```bash
tuios peek-prompt -w review
tuios respond -w review --prompt-id 75f8b9fadb5b5dfc approve
tuios respond -w review choose 2
tuios peek-prompt -w buildbox:api:review --json
```

`respond` takes `approve`, `approve_always`, `deny`, `choose <n>` or `text <answer>`, whichever the prompt offers. Pass the `--prompt-id` that `peek-prompt` printed, so an answer never lands on a prompt you have not read. The daemon reads the prompt again first and refuses with `prompt_changed` if it moved on. `--timeout <ms>` (default `5000`) is how long it waits for the pane to move on.

Answering a prompt is acting as you. `respond` works from a shell outside every pane only when `[daemon] respond_from_shell = true` is set in the config file. A program in a pane is refused with `not_human`, unless the pane holds the `respond` grant.

### queue

Queue a message for the agent in a pane. It is typed as a prompt once the agent has been at rest for a second, one message per rest, and never over a prompt it is waiting on.

```bash
tuios queue -w build 'make the backoff jitter configurable'
tuios queue ls
tuios queue ls -w build
tuios queue rm q3
tuios queue rm --all -w build
```

A message the agent shows no sign of taking is marked `stalled`, is never typed again, and opens a question in the Inbox. A pane holds at most `[agents.queue] max` messages (8 by default), each at most 16 KiB. The queue lives in the daemon's memory. `queue ls` shows each entry's id, its state (`waiting`, `delivering` or `stalled`), who queued it, its age and the pane. Put `--` before text that starts with `ls` or `rm`.

### review

Show what the agent in a pane changed, leave notes on its lines, and send the notes to it as one message.

```bash
tuios review [SESSION] [flags]
tuios review note FILE:LINE TEXT... [flags]
tuios review notes [--clear]
tuios review send [--id <id>] [--now]
```

- `-w, --window <target>`: the pane. Default: the focused pane.
- `--base <ref>`: diff against this ref instead of the worktree's base.
- `--uncommitted`: only what is not committed.
- `--against <session>`: diff against another attempt of the same fan.
- `--stat`: only the changed files and their counts.
- `--path <path>`: only this path. Repeatable.
- `--context <n>`: lines of context, 0 to 20. Default `3`.

`review note` puts a note on a line of the new side, or on a removed line with `--side old`, or on a whole hunk with `--hunk '<header>'`. `--edit <id>` replaces a note's text and `--remove <id>` drops it. `review send` sends the unsent notes through the queue, typed when the agent is at rest. `--now` sends only if the agent is at rest now.

```bash
tuios review api-fan-retry-2 --stat
tuios review note -s api-fan-retry-2 api/retry.go:42 'log the attempt number here too'
tuios review send -s api-fan-retry-2
```

See [Reviewing an agent's changes](https://tuios.dev/docs/agent-inbox#reviewing-an-agents-changes).

### checkpoint

List, read and restore the checkpoints of an agent's turns. The daemon saves a pane's git work tree each time its agent finishes a turn that changed a file. The index, `HEAD`, the branch and the stash do not change.

```bash
tuios checkpoint list [--json]
tuios checkpoint diff [N] [--stat] [--path <path>]... [--context <n>] [--json]
tuios checkpoint restore N [--force] [--json]
```

Every subcommand takes `-s, --session` and `-w, --window`.

- `list` prints one row per checkpoint: its number, the turn, the agent state, the time, the commit and the label.
- `diff N` shows what turn N changed. Without N it shows the newest.
- `restore N` first saves the work tree as a safety checkpoint and prints its number. Restore that number to undo the restore. `--force` restores while the agent is working or waiting on a prompt.

```bash
tuios checkpoint list -w build
tuios checkpoint diff -w build 3 --stat
tuios checkpoint restore -w build 2
```

See [Checkpoints](https://tuios.dev/docs/checkpoints).

### agent-log

Show what the agent in a pane has been doing: prompts, tool calls and how they ended, finished turns, the subagents it started and stopped, and commands its shell ran, oldest first. It needs the Claude Code or Codex integration.

```bash
tuios agent-log -w build
tuios agent-log -w build --since 30m --recap
tuios agent-log -w build --json
```

- `--recap`: a summary instead: turns, files written, commands, the last test run, what the agent last said, and its state now.
- `--since <duration>`: only what happened in this long.
- `--limit <n>`: at most this many entries, the newest. Default `64`, at most `256`.

### resume-agent

Type a pane's resume command into its shell, such as `claude --resume <id>`, with the conversation id a hook recorded. It types only when the shell is at its prompt.

```bash
tuios resume-agent -w build
tuios resume-agent -w build --dry-run
```

`daemon.resume_agents` decides what a daemon restart does on its own. See [Resuming after a restart](https://tuios.dev/docs/agent-inbox#resuming-after-a-restart).

## Agent setup

### integration

Wire coding-agent harnesses' hooks to TUIOS agent state.

```bash
tuios integration install claude-code
tuios integration install --all
tuios integration install claude-code --mcp
tuios integration install claude-code --statusline --then '~/.claude/statusline.sh'
tuios integration status
tuios integration uninstall codex
```

- `--all`: every supported harness whose configuration directory exists.
- `--mcp`: also register `tuios mcp`, read-only, as an MCP server. `--mcp-write` registers it with the tools that type into panes.
- `--statusline`: also feed Claude Code's model, context use and cost to the rail. A status line of your own is never replaced: `--then` chains to it.
- `--command <path>`: the program the hooks run, when `tuios` is not on the harness's `PATH`.

See [Agents](https://tuios.dev/docs/agents#the-agent-reports-for-itself) for what each harness reports.

### doctor

```bash
tuios doctor agents     # per harness: on PATH, integration installed and current, what it reports
tuios doctor shell      # per pane: whether its shell sends OSC 133 marks, for run and --last-command
```

With a daemon running, `tuios doctor agents` ends with the agent panes whose integration is not installed, then the ones whose integration is out of date. For each harness it prints the command that fixes it, such as `Update it with: tuios integration install claude-code`. Panes on other machines are left out. If TUIOS cannot read which machine the panes of a session run on, it says it did not check that session.

In `--json`:

- `panes_without_integration` lists the panes with no integration and the panes whose integration is out of date. Each entry has an `integration` field, `not installed` or `out of date`.
- `sessions_not_checked` lists the sessions whose panes were not checked.
- Each harness has `program` (the program its hooks run), `other_program` (true for a current install made with `--command`) and `unreadable` (true when a file could not be read or parsed). `tuios integration status --json` has the same fields.

`tuios keybinds doctor` checks the keybindings.

### pane-grants and set-pane-grants

`pane-grants`, run inside a pane, prints what that pane may do through TUIOS. `set-pane-grants` changes it.

```bash
tuios pane-grants
tuios set-pane-grants -w reviewer --grants read
tuios set-pane-grants -w supervisor --grants read,write,fan,respond
tuios set-pane-grants -w reviewer --reset
```

The grants are `read`, `write`, `fan`, `respond` and `admin`, or `none`. A pane may change only its own grants unless it holds `admin`, and never give more than it holds. See [What a pane may do](https://tuios.dev/docs/agents#what-a-pane-may-do).

`pane-grants` also says when a change to `config.toml` waits for `tuios config apply`.

### mcp

Serve the TUIOS control surface as Model Context Protocol tools over stdin and stdout, for a harness to load.

```bash
tuios mcp
tuios mcp --write
claude mcp add tuios -- tuios mcp
```

By default it is read-only and reaches only the session of the pane it runs in, its fan group, and sessions a fan from it started. `--write` adds the tools that type into panes. `--scope all` reaches every session, for a harness that runs outside TUIOS. The daemon enforces the limits, not the server.

### tmux-shim and tmux

`tuios tmux-shim` runs a command with a `tmux` that answers in this TUIOS session, so a tool that drives tmux, such as Claude Code agent teams, opens its panes here.

```bash
tuios tmux-shim -- env CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1 claude
tuios tmux-shim --log-all
```

It is off until you run it, and changes nothing outside the command it starts. `tuios tmux <args>` answers one tmux command line the same way. `--log <file>` records the calls the shim cannot fully answer in another file than `$XDG_STATE_HOME/tuios/tmux-shim.log`. `--log-all` records every call. Not on Windows.

```bash
tuios tmux list-panes -F '#{pane_id} #{=20:pane_title}'
```

See [tmux Shim](https://tuios.dev/docs/tmux-shim) for the commands and formats it answers, and for control mode.

## Remote hosts

### hosts

List the machines in the `[hosts]` table and the state of each link.

```bash
tuios hosts [--json]
tuios hosts add <name> <addr> [flags]
tuios hosts remove <name>
tuios hosts test <name> [--start] [--dry-run] [--json]
tuios hosts tailnet [--json]
tuios hosts sync [host...] [flags]
```

The daemon keeps one ssh link to each host.

| Status         | Meaning                                                                                 |
| -------------- | --------------------------------------------------------------------------------------- |
| `up`           | The link is open and the remote daemon answers                                          |
| `connecting`   | The first attempt has not finished                                                      |
| `reconnecting` | The link dropped and is being dialled again                                             |
| `no_daemon`    | The machine is up and no TUIOS daemon runs on it. Run `tuios hosts test <name> --start` |
| `no_tuios`     | The machine is up and the link cannot find `tuios` on it. Run `tuios hosts test`        |
| `unreachable`  | The last attempt failed. The line under the table says why                              |
| `incompatible` | The remote daemon speaks a protocol this build does not serve. Upgrade one side         |

`hosts add` writes the host to the config file and tests the link. From a terminal outside TUIOS, the daemon opens it at once. From a pane, it waits for `tuios config apply`. Options:

- `--command <path>`: the `tuios` binary on the host. By default the link looks on the `PATH`, then the usual install paths, then a login shell.
- `--connect-timeout <s>`: seconds one dial may take. Default `10`.
- `--ssh-option <arg>`: one extra ssh argument. Repeat for more. Only safe options are accepted. See [Rules for ssh\_options](https://tuios.dev/docs/configuration#rules-for-ssh_options).
- `--tailnet`: take the address from the tailnet machine with that name.
- `--repos-root <dir>`: where the host keeps its checkouts, such as `~/src`, for `fan --host`, `worktree new --host` and `start-agent`.

`hosts remove` (alias `rm`) removes the host and closes its link. `hosts test` dials once by itself, with no daemon, and prints what ssh said or where it looked for `tuios`. `hosts tailnet` lists the machines on your tailnet and which ones `hosts add` would offer.

```bash
tuios hosts add build gaurav@buildbox
tuios hosts add lab lab-01 --ssh-option -J --ssh-option bastion
tuios hosts test build
```

When the host is up and no daemon runs there, `hosts test` prints the command to run on the host, with the `tuios` binary that the link found. Add `--start` to run it from here:

```bash
tuios hosts test build --start            # start the daemon on build if it does not run
tuios hosts test build --start --dry-run  # show what --start does, and change nothing
```

`--start` runs `start-server` over the same ssh that the link uses, then dials again and prints the new state. It does nothing when a daemon runs. On a host with no `tuios`, it starts nothing: run `tuios hosts sync <name> --start`. With `--json`, the result is the host report with `action` (`daemon started`, `would start daemon`, `none` or `start failed`), `before` and `start_command`.

The daemon runs ssh with `BatchMode` on, so it never asks for a password or about a host key. Run ssh to the host once by hand to accept its key.

With a host configured:

- `tuios ls --all-hosts` and `tuios list-agents --host build` list it.
- `tuios attach --host build api` and `tuios new --host build` open sessions on it in this client, with your local theme, config and prefix. If the link drops, the client reconnects on its own for up to three minutes.
- `tuios new-window --host build` opens a pane in a local session whose process runs on the host.

#### hosts sync

Install the TUIOS version of this machine on the hosts in the `[hosts]` table. With no names, sync does every host. Name hosts to sync only those.

```bash
tuios hosts sync --dry-run          # show the plan and change nothing
tuios hosts sync --dev              # install a build of this checkout on every host
tuios hosts sync build lab          # only these hosts
tuios hosts sync build --restart    # install, then restart the daemon on build
tuios hosts sync build --start      # install if needed, then start the daemon if it does not run
tuios hosts sync --restart --yes --json
```

| Flag              | What it does                                                                                                  |
| ----------------- | ------------------------------------------------------------------------------------------------------------- |
| `--dev`           | Build the binary from a TUIOS checkout: `--src`, or else the current folder. The version is `dev+COMMIT`      |
| `--src <dir>`     | The checkout to build. Sets `--dev`                                                                           |
| `--binary <path>` | Send this binary. A host with a different system fails                                                        |
| `--restart`       | Restart each daemon whose version is different                                                                |
| `--start`         | Start the daemon on each host where it does not run. A host with no `tuios` gets it first                     |
| `-y, --yes`       | Restart without a question. `--restart` needs it when there is no terminal                                    |
| `--dry-run`       | Show the plan and change nothing                                                                              |
| `--json`          | Print the result as JSON                                                                                      |
| `--local`         | Sync this machine too. The name `local` does the same                                                         |
| `--ghostty`       | Refused. The ghostty backend cannot be cross-compiled. Build it on the host with `scripts/install.sh ghostty` |

First, sync reads each host over ssh: its system, the `tuios` on it, the version of that `tuios`, and its daemon. Then it installs where the version is different. It prints one row for each host: the system, the version before and after, the daemon, and what it did.

A release build gets the archive of its own release for each host system. It checks the archive against the published checksums, and sends the binary over ssh. The host needs no internet. Without a release, sync builds the checkout in the current folder, or asks for `--src` or `--binary`.

sync installs over the `tuios` that the host has, if you can write its folder. Otherwise it installs to `~/.local/bin/tuios`, and warns if a login shell does not find it there. sync never uses sudo. It writes the binary to a temporary file, checks it, runs it once, and moves it into place.

> **A restart ends the programs in the panes**
>
> sync does not restart a daemon unless you add `--restart`. The old daemon keeps running, and the row gives the command that restarts it. A restart saves each session and restores it with its layout and new shells. The programs in the panes end. Before a restart, sync lists the sessions and the panes with a running program, and asks you. A daemon with no sessions restarts without a question.

With `--start`, sync starts the daemon on each host where it does not run, after the install. The row says `daemon started`, and `daemon.started` is `true` in `--json`. With `--dry-run` the row says `would start daemon`.

One host that fails does not stop the others. The command exits `1` when a host fails. `TUIOS_SSH` sets the ssh program that sync runs.

All ssh calls of one run to a host use one shared connection, through a private socket in `$XDG_RUNTIME_DIR` that sync removes at the end. When Tailscale SSH in check mode holds the login, one approval covers the run. On a terminal, sync lists the link to open for each host, and asks to wait up to 5 minutes. Without a terminal, or with `--json`, the host fails at once. Its row says `needs approval`, and in `--json` it has `"error_kind": "tailscale_check"` and `approval_url`. A login the tailnet policy refuses has `"error_kind": "tailscale_policy"`. See [Tailscale SSH check mode](https://tuios.dev/docs/remote-hosts#tailscale-ssh-check-mode).

See [the hosts table](https://tuios.dev/docs/configuration#the-hosts-table).

## Tapes and layouts

### tape

Run and manage [tape scripts](https://tuios.dev/docs/tape-scripting).

| Command                                                      | What it does                                                                                                                          |
| ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------- |
| `tuios tape play <file.tape>`                                | Start a TUI and play the tape in it. Ctrl+P pauses. Takes the [interface flags](https://tuios.dev/docs/cli-reference#interface-flags) |
| `tuios tape exec <file.tape> [-s <session>] [--timeout <d>]` | Play the tape in a running session, with no new TUI, and wait for it to end                                                           |
| `tuios tape validate <file.tape>`                            | Check the syntax without running it                                                                                                   |
| `tuios tape list`                                            | List saved recordings                                                                                                                 |
| `tuios tape show <name>`                                     | Print a recording                                                                                                                     |
| `tuios tape delete <name>`                                   | Delete a recording                                                                                                                    |
| `tuios tape dir`                                             | Print the recordings directory                                                                                                        |

#### tape exec

`tape exec` sends the tape to a running daemon session, whose attached client plays it. The command returns when the tape ends. It exits non-zero with the file, line and column when a command fails. `--timeout` sets how long it waits, 30 minutes by default. See [tape exec](https://tuios.dev/docs/tape-scripting#tape-exec). For one command, use [`run-command`](https://tuios.dev/docs/cli-reference#run-command).

### layout

Manage saved layout templates. Save a layout in the TUI with Ctrl+B L s.

| Command                      | What it does                           |
| ---------------------------- | -------------------------------------- |
| `tuios layout list`          | List saved layouts                     |
| `tuios layout export <name>` | Print a layout as a tape script        |
| `tuios layout delete <name>` | Delete a layout. This cannot be undone |
| `tuios layout dir`           | Print the layouts directory            |

```bash
tuios layout export dev > dev.tape
tuios tape exec dev.tape
```

## Other commands

### ssh

Serve TUIOS over SSH.

```bash
tuios ssh [flags]
```

- `--host <addr>`: the address to listen on. Default `localhost`.
- `--port <port>`: default `2222`.
- `--key-path <path>`: the host key. Generated if not given.
- `--authorized-keys <path>`: public keys allowed to connect. Default `~/.config/tuios/authorized_keys`. Keys with options are not accepted.
- `--no-auth`: accept every connection without checking keys. Use only on a trusted network.
- `--default-session <name>`: put every connection in this session.
- `--ephemeral`: run without the daemon. Nothing persists.
- The [interface flags](https://tuios.dev/docs/cli-reference#interface-flags).

Every connection gets a shell on this machine, so the server checks keys. It reads only `~/.config/tuios/authorized_keys`. It does not read `~/.ssh/authorized_keys` unless you name that file with `--authorized-keys`.

- With keys, only their holders connect. A key added while the server runs works on the next connection.
- With no keys file, the server does not start. This applies to `localhost` too. The server prints three ways to start.
- With `--no-auth`, every connection is accepted, and the server prints a warning. On `localhost`, every user on this machine can connect.
- An empty `--host` listens on every interface. The server applies the rules for a network address to it.

A keys file that cannot be read, does not parse, or holds no key stops startup. TUIOS does not apply the rules in `sshd_config` to the keys.

TUIOS does not accept a key that has options, such as `command=`, `from=` or `restrict`. TUIOS cannot apply these options, and every session is a full TUIOS. The log names the line of each refused key. A file that holds only keys with options stops startup. To let such a key in, add it again with no options.

A client picks its session in this order: `--default-session`, then the SSH user name unless it is generic (`tuios`, `root`, `anonymous`), then a command argument (`ssh -p 2222 host attach work`), then the first session, or a new one.

```bash
# Add your public key. Use your own public key file.
mkdir -p ~/.config/tuios
cat ~/.ssh/id_ed25519.pub >> ~/.config/tuios/authorized_keys
tuios ssh --host 0.0.0.0 --port 2222
ssh -p 2222 work@server

# Or use the keys that sshd accepts
tuios ssh --host 0.0.0.0 --authorized-keys ~/.ssh/authorized_keys
```

> **Upgrading from v0.8.0**
>
> In v0.8.1, `tuios ssh` does not start without a keys file, on `localhost` too. It also stops reading `~/.ssh/authorized_keys` by itself. If your key is only in that file, do one of these:
>
> - Add your public key to `~/.config/tuios/authorized_keys`, as in the example above.
> - Start with `--authorized-keys ~/.ssh/authorized_keys`.
> - Start with `--no-auth`. Then every user on this machine can connect, or everyone who reaches the port on a network address.
>
> A key with options is not accepted now. Add the key again with no options if it must open a TUIOS session.

The settings page on an SSH client changes only that session. It never writes the server's config file. The web terminal is a separate binary, `tuios-web`. See [Web Terminal](https://tuios.dev/docs/web).

### notify

Send a test notification through each provider in the `[notify]` table, and print the result for each.

```bash
tuios notify test [--json]
```

```
ntfy (ntfy.sh): sent.
webhook (hooks.example.com): failed. The server answered 401 Unauthorized. Check the token.
```

The command sends from its own process, not from the daemon, so it reads a `token_env` variable from your shell. The output shows the host of each provider, never a token or the full address. It exits `1` when a provider fails. `--json` prints `provider`, `host`, `ok` and `error` for each. See [Push notifications](https://tuios.dev/docs/push-notifications).

### update

Replace this binary with the newest release.

```bash
tuios update --check        # show what would be installed
tuios update                # install it
tuios update --check --pre  # include prereleases
```

It only updates a binary installed from a release archive, which is what the install script uses. For Homebrew, a system package, Nix or `go install`, it refuses and prints the command that updates that install. It also updates `tuios-web` when it sits next to `tuios`. Every download is checked against the release checksums.

The running daemon keeps the old version. To switch, detach, run `tuios kill-server`, and start TUIOS again. Set `GITHUB_TOKEN` or `GH_TOKEN` to raise the GitHub API rate limit.

### completion

Print a shell completion script.

```bash
tuios completion bash > /etc/bash_completion.d/tuios
tuios completion zsh > "${fpath[1]}/_tuios"
tuios completion fish > ~/.config/fish/completions/tuios.fish
tuios completion powershell | Out-String | Invoke-Expression
```

`--no-descriptions` leaves out the descriptions.

### help

Print the help of a command, or show all commands.

```bash
tuios help                # general help
tuios help config edit    # help for one command
tuios help --json         # every command as JSON
tuios help -i             # search the commands in an explorer
```

- `--json`: print the command and every command under it as JSON. Each command has its path, usage line, short and long help, examples, aliases and flags.
- `-i, --interactive`: open the same commands in an explorer. The detail pane shows the usage, help and flags of the command.

### Explorers

`tuios help -i`, `tuios config browse` and `tuios keybinds browse` open an explorer. All three use the same keys:

| Key                      | Action                 |
| ------------------------ | ---------------------- |
| /                        | Search                 |
| Arrow keys, j, k         | Move                   |
| Tab, or a click on a tab | Show one group         |
| J, K                     | Scroll the detail pane |
| q, Esc                   | Leave                  |

An explorer opens only with its own flag or command. A plain command prints plain output, on a terminal too.

## Scripting examples

### Open a build pane and wait for it

```bash
#!/bin/bash
set -euo pipefail

tuios new-window build
# printf builds the marker, so the command's echo cannot match it.
tuios send-text -w build 'go build ./... && printf "BUILD_%s\n" OK
'

if tuios wait-for window-output -w build --pattern 'BUILD_OK' --timeout 120000; then
  tuios capture-pane -w build --scrollback --lines 5
else
  tuios capture-pane -w build --scrollback --lines 40
  exit 1
fi
```

### Lay out a workspace

```bash
#!/bin/bash
set -euo pipefail

tuios run-command EnableTiling
tuios new-window editor nvim .
tuios new-window logs -- tail -f /var/log/system.log
tuios new-window shell
tuios focus-window editor
```

### Pick a window with fzf

```bash
#!/bin/bash
id=$(tuios list-windows --json |
  jq -r '.windows[] | "\(.display_name)\t\(.window_id)"' |
  fzf --with-nth=1 | cut -f2)

[ -n "$id" ] && tuios focus-window "$id"
```

Each command opens a new connection to the daemon. For long-running scripts, use the [control protocol](https://tuios.dev/docs/control-protocol) directly. It has structured error codes, blocking waits and an event stream.

## Environment variables

Variables TUIOS reads:

| Variable                   | Effect                                                                                                                                                                                                                    |
| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `TUIOS_NO_DAEMON=1`        | A bare `tuios` runs standalone, like `--standalone`                                                                                                                                                                       |
| `TUIOS_ALLOW_NESTED=1`     | `tuios attach` inside a pane attaches the session that holds the pane, like `--force`                                                                                                                                     |
| `TUIOS_WEB_PASSWORD`       | The password of `tuios-web`. `tuios-web` does not pass it to panes. Prefer `--password-file`                                                                                                                              |
| `XDG_CONFIG_HOME`          | Config directory. Default `~/.config`                                                                                                                                                                                     |
| `XDG_STATE_HOME`           | Where `daemon.log`, saved sessions and debug logs go                                                                                                                                                                      |
| `XDG_DATA_HOME`            | Tape recordings and worktrees                                                                                                                                                                                             |
| `XDG_RUNTIME_DIR`          | Where the daemon socket lives                                                                                                                                                                                             |
| `EDITOR`, `VISUAL`         | Editor for `tuios config edit`                                                                                                                                                                                            |
| `TUIOS_LOG_LEVEL`          | Starting daemon log level, as in `--log-level`                                                                                                                                                                            |
| `TUIOS_DEBUG_INTERNAL=1`   | Internal debug logging, to `/tmp/tuios-debug.log`, and every key pressed in terminal mode to `/tmp/tuios-events.log`. Both are opened with mode `0600`, and a file there that another user owns, or a symlink, is refused |
| `TUIOS_KITTY_GRAPHICS`     | `1` or `0` forces kitty graphics support on or off                                                                                                                                                                        |
| `TUIOS_KITTY_PLACEHOLDERS` | `1` or `0` forces kitty Unicode placeholder images on or off                                                                                                                                                              |
| `TUIOS_SIXEL_GRAPHICS`     | `1` or `0` forces sixel support on or off                                                                                                                                                                                 |
| `TUIOS_DEBUG_CAPS=1`       | Logs graphics capability detection to `$XDG_STATE_HOME/tuios/tuios-caps.<pid>.log`                                                                                                                                        |
| `GITHUB_TOKEN`, `GH_TOKEN` | Used by `tuios update` for the release lookup                                                                                                                                                                             |

Variables TUIOS sets in every pane:

| Variable                           | Value                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `TUIOS_SESSION`                    | The session name                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| `TUIOS_PANE_ID`, `TUIOS_WINDOW_ID` | The window's ID                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `TUIOS_SOCKET`                     | The daemon socket                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
| `TUIOS_HOST`                       | The machine the pane runs on                                                                                                                                                                                                                                                                                                                                                                                                                                                                |
| `TUIOS_ENV`                        | `1`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| `TUIOS_RESTORED`                   | `1` in a shell started by a session restore                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
| `TUIOS_PANE_GRANTS`                | What the pane may do through TUIOS as it starts                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `TUIOS_PANE_TOKEN`                 | Proves the pane's ID where the kernel cannot name the caller's pane                                                                                                                                                                                                                                                                                                                                                                                                                         |
| `TERM`, `COLORTERM`                | Detected from the terminal of the client that created the session. A pane keeps that `TERM` only when the machine that runs it has a terminfo entry for it, and gets `xterm-256color` otherwise. To keep your own `TERM`, install its entry on that machine, for example with `infocmp -x xterm-kitty \| ssh HOST tic -x -`. A session made with no terminal on stdout (a script, a service, CI, an agent running `tuios new --detach`) gets `xterm-256color` and `truecolor`, never `dumb` |
| `TERM_PROGRAM`                     | `TUIOS`, or `ghostty` or `WezTerm` when the host terminal passes kitty or sixel graphics through                                                                                                                                                                                                                                                                                                                                                                                            |

## Exit codes

| Code      | Meaning                                                                                                                                                |
| --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `0`       | Success                                                                                                                                                |
| `1`       | Failure: bad arguments, no such session or window, a refused option, a timeout                                                                         |
| `2`       | `ask-human`: no answer yet                                                                                                                             |
| `3`       | No daemon is running                                                                                                                                   |
| `128` + N | A client that a signal had to force out, such as `130` for `SIGINT` and `143` for `SIGTERM`. See [attach](https://tuios.dev/docs/cli-reference#attach) |

Every command that needs the daemon exits `3` without one. `tuios ls` then lists the sessions saved on disk, so a script can tell a stopped daemon (`3`) from a running daemon with no sessions (`0` and an empty list):

```bash
tuios ls --json > sessions.json
case $? in
  0) echo "daemon up" ;;
  3) echo "daemon down; the list is from disk" ;;
  *) echo "failed" ;;
esac
```

`ls` is the only command that prints JSON when there is no daemon. The others print an explanation on stderr.

A `tuios attach` that ends because the session was killed or the daemon was lost exits `1`. A normal detach exits `0`.

The CLI does not tell failure types apart by exit code. For that, use the control protocol's [error codes](https://tuios.dev/docs/control-protocol#error-codes).

## Related

*[An interactive figure goes here. Open the page to use it.](https://tuios.dev/docs/cli-reference)*
