mirror of
https://github.com/l0ng-ai/tty7.git
synced 2026-09-21 16:02:20 +00:00
Made the config directory read-only and asked doctor about it:
CHECK RESULT
TTY7_CONFIG_DIR set (/tmp/…)
config none yet — the defaults are the config
server ok (build 26.8.3)
…
rc=0
Meanwhile, on the same directory:
$ tty7 new
tty7: could not write the machine tree at /tmp/…/machine.json:
Permission denied (os error 13)
Every `new`, every `tab new`, and every settings save fails, and the verb
whose entire job is "check this install" called it healthy. The config row is
about *reading* — it is answering "does the file parse", and there is no file
— so nothing in the table was wrong, and nothing in it was the answer either.
`tty7 doctor || alert` is exactly the thing that should have fired.
Now: a row naming the directory and what stops working, a headline on stderr
so `-q` still says it, `config.dir_writable` in the JSON, and exit 1 alongside
the unparseable-config case it sits next to.
The check writes a probe file and removes it, rather than reading the mode
bits. A read-only mount, an ACL, an immutable flag or another user's directory
all leave `0700` on something that refuses every write, and what is being
diagnosed is whether the write succeeds. A directory that cannot be reached at
all counts as unwritable — an install nobody can open is not a healthy one.
Verified against a live daemon in all three states: healthy exits 0, read-only
exits 1 with the row and the headline, and fixing the permissions goes back to
0. The unit test covers the probe both ways and that it cleans up after
itself; replacing it with an `exists()` check fails it.
474 lines
24 KiB
Plaintext
474 lines
24 KiB
Plaintext
---
|
||
title: "Command reference"
|
||
description: "The verbs an agent drives, their flags, and the JSON they emit under --json."
|
||
---
|
||
|
||
The verbs below are the ones a script or an agent reaches for. tty7 also groups
|
||
its management verbs under `ws`, `tab`, `pane`, `machine` and `server`; two of
|
||
the verbs documented here are shortcuts into them — `ls` is `ws ls` and `split`
|
||
is `pane split`. (`new` is not: it creates a workspace *and* its first tab,
|
||
where `ws new` leaves it empty.) Those groups are not repeated on this page.
|
||
`tty7 ws --help` and its siblings list them, and being generated from the same
|
||
definitions the CLI runs on, they cannot drift from it.
|
||
|
||
## Global flags
|
||
|
||
Accepted anywhere on the line, before or after the subcommand.
|
||
|
||
| Flag | Effect |
|
||
|---|---|
|
||
| `-m, --machine <MACHINE>` | Route to a linked machine over the local server's existing link. Matches the full link key (`me@devbox:22`) or the bare host (`devbox`). SSH links only; a down link, or a jump/proxy chain, is refused with a reason rather than dialled fresh. |
|
||
| `--json` | One JSON object on stdout instead of the human table. A verb that *fails* prints its message on stderr and no JSON at all, so a reader parsing stdout must check the exit code first. Three verbs are exceptions, because there the bad news is the answer: `pane close` still prints `{"closed":[…],"failed":[…]}`, `wait` still prints the state it gave up in, and `doctor` still prints its whole report — an unreachable server is the finding, and `tty7 doctor || alert` needs both the exit code and the rows. (`run` is not an exception — a child exiting nonzero is the verb succeeding, and its JSON is printed as usual.) |
|
||
| `-q, --quiet` | No output on success. Errors still go to stderr. |
|
||
|
||
## Environment
|
||
|
||
Set inside every tty7 pane, inherited by anything launched from one.
|
||
|
||
| Variable | Meaning |
|
||
|---|---|
|
||
| `TTY7_PANE` | This pane's id, e.g. `71` or `%71` (both accepted). Default target of `split`, `send`, `capture`, `procs`, `wait`, `pane close`. |
|
||
| `TTY7_WS` | This pane's workspace id. Default for `run --keep`, `tab new`, `tab ls`, `ws tree`. |
|
||
| `TTY7_CONFIG_DIR` | The server's config dir — how the CLI finds the right server. You never pass a socket path. |
|
||
|
||
Outside a tty7 shell, address-taking verbs fail with
|
||
`not inside a tty7 shell — pass an explicit %pane/@tab/workspace`.
|
||
|
||
## Exit codes
|
||
|
||
| Code | Meaning |
|
||
|---|---|
|
||
| `0` | Success |
|
||
| `1` | The command failed; one line on stderr, prefixed `tty7:` |
|
||
| `2` | Usage error — unknown verb, missing argument, bad type |
|
||
| `124` | `tty7 wait` timed out (the `timeout(1)` convention) |
|
||
| `141` | Unix only: the reader hung up — piping into `head -1`, say — and SIGPIPE ended it, exactly as it ends `cat`. Not a failure. Windows reports 0 for the same thing, having no signal to imitate. |
|
||
| *other* | Only from `tty7 run`, which passes the child's exit code through |
|
||
|
||
If `run` cannot learn the child's code it prints a note to stderr and exits 1
|
||
with `"exit_code_known": false` in the JSON — that is how you tell a real 1 from
|
||
a stand-in.
|
||
|
||
## Top-level verbs
|
||
|
||
### `tty7 [PATH]`
|
||
|
||
No subcommand means the GUI. A running window is asked to come forward and open
|
||
a tab at `PATH`; if none is registered, the app is launched instead.
|
||
JSON: `{"path","delivered","launched"}` — `delivered` says an existing window
|
||
took it, `launched` that a new process was started.
|
||
|
||
Without `PATH` it just activates the app. `-m` is refused: this verb drives the
|
||
GUI on *this* machine.
|
||
|
||
### `tty7 ls`
|
||
|
||
Same as `ws ls`. Table: `WORKSPACE NAME TABS PANES ATTACHED`.
|
||
JSON: `{"workspaces":[{"id","name","tabs","panes","attached"}]}`.
|
||
|
||
`ATTACHED` names the client that has *claimed* the workspace over a control
|
||
link, and a claim lasts exactly as long as that client's link. A GUI claims a
|
||
workspace it is showing as a **remote** one; a GUI window on a workspace of its
|
||
own machine does not claim anything, because there is no second client to
|
||
arbitrate against. So `-` for a local workspace means "no remote client holds
|
||
this", not "nothing has it open" — with a window sitting on it, `tty7 ls` still
|
||
prints `-`.
|
||
|
||
### `tty7 run [--keep] [--cwd DIR] [--ws WORKSPACE] -- CMD...`
|
||
|
||
Spawns a pane running `CMD`, streams its output to stdout, waits, and exits with
|
||
its code. The command must come after `--`.
|
||
|
||
- `--keep` leaves the pane alive as a new tab afterwards. Needs a workspace, so
|
||
it requires `--ws` or `$TTY7_WS` — without one it is an error, not a silent
|
||
fallback.
|
||
- `--cwd` sets the working directory. `--ws` also sets the pane's `TTY7_WS`.
|
||
- Interrupting `run` can leave the pane behind as an orphan — see
|
||
`pane ls --all`.
|
||
|
||
JSON: `{"pane","exit","exit_code_known","kept"}`, printed **after** the streamed
|
||
output. The combined stream is not valid JSON — read the last line.
|
||
|
||
`exit_code_known` is false when the pane ended without leaving a status behind
|
||
for the server to read in the short window it waits. `run` then exits 1 as a
|
||
stand-in and says so on stderr, and this flag is how a caller tells that 1 from
|
||
a command that really did exit 1.
|
||
|
||
A command **killed by a signal** reports `exit: 1` — not the `128+N` a shell
|
||
would give — with `exit_code_known: false` and the same stderr note as any
|
||
other unknown code, because a status that says "terminated by a signal" is not
|
||
an exit code the daemon will invent one from. That flag is exactly how a
|
||
caller tells a command the OOM killer took from one that exited 1 on its own,
|
||
so an orchestrator does not have to read the pane to find out.
|
||
|
||
### `tty7 new [PATH] [--open]`
|
||
|
||
Creates a workspace plus its first tab and shell, at `PATH` if given. Prints the
|
||
workspace id. JSON: `{"id","pane","opened"}`.
|
||
|
||
`--open` also puts a window on it, if a GUI is running on this machine. Without
|
||
it the workspace still appears in the switcher; it just waits to be opened.
|
||
|
||
### `tty7 split [%PANE] (--v|--h) [--ratio R]`
|
||
|
||
Alias of `pane split`. Splits `%PANE` (default `$TTY7_PANE`), spawning a shell
|
||
in the same cwd. Exactly one axis is required — `--v`/`--vertical` puts the new
|
||
pane below, `--h`/`--horizontal` to the right. `--ratio` (default `0.5`) is the
|
||
share kept by the *existing* pane, clamped to `0.05`–`0.95` — a `--ratio 70`
|
||
silently becomes `0.95`, not an error. Prints `%NN`. JSON: `{"pane"}`.
|
||
|
||
### `tty7 send [%PANE] [TEXT] [--enter] [--key KEY]…`
|
||
|
||
Types `TEXT` into the pane as keystrokes; `--enter` is shorthand for `--key
|
||
enter` — it appends CR to the text, or presses Enter on its own when there is
|
||
none, so `tty7 send %42 --enter` runs whatever pane 42 already has typed. With
|
||
one argument the text is the argument and the pane comes from `$TTY7_PANE` —
|
||
but a lone `%42` (or bare `42`, the shape `pane ls --json` prints) is rejected
|
||
as a missing-text error rather than typed, unless a `--key` gives it something
|
||
to do. `--enter` is that key only for the `%`-marked spelling: `tty7 send 83
|
||
--enter` is refused, because it reads as much like typing `83` into your own
|
||
pane as like pressing Enter in pane 83, and the error names both ways to say
|
||
which (`send %83 --enter`, `send %PANE 83 --enter`). A `%` followed by a digit
|
||
that still doesn't parse (`%3x`) is an address error, never text for your own
|
||
pane — while text that merely starts with `%` (`%s/foo/bar/`, `%!sort`) types
|
||
as given, as does anything unmarked that is not a plain number (`3x`, `+5`). To
|
||
type an address-shaped string, name the pane as well: `tty7 send %42 %3x`.
|
||
|
||
A newline inside `TEXT` is Enter, because typing one is. `tty7 send %1
|
||
$'deploy\nyes'` runs `deploy` and leaves `yes` typed at the prompt it asked
|
||
for — so text that came from somewhere else runs a line at a time, and `--enter`
|
||
is the way to submit on purpose.
|
||
|
||
`--key` presses a key instead of typing characters, which is what a pane wants
|
||
once something is already running in it: answering a prompt that only takes
|
||
arrow keys, closing a TUI with `escape`, stopping a build with `C-c`. Repeat it
|
||
for a sequence, and it composes with `TEXT` — the text goes first.
|
||
|
||
| | |
|
||
|---|---|
|
||
| Named | `enter` `escape` `tab` `backtab` `space` `backspace` `delete` `up` `down` `right` `left` `home` `end` `pageup` `pagedown` |
|
||
| Chords | `C-<char>` (Ctrl, e.g. `C-c`), `M-<char>` (Alt) |
|
||
| Aliases | `return` `cr` `esc` `del` `bs` `shift-tab` `pgup` `pgdn` `pgdown` |
|
||
|
||
Names are case-insensitive, and an unknown one is a usage error (exit `2`)
|
||
raised before anything is sent — half a key sequence in a live pane is worse
|
||
than none. One case exception: Alt is a prefixed ESC, so its character goes
|
||
out exactly as written and `M-X` is not `M-x` (Ctrl is unaffected — `C-c`
|
||
and `C-C` are the same byte). Each keystroke is delivered as its own event,
|
||
200 ms apart, so a
|
||
raw-mode TUI reads a sequence as a sequence rather than as a paste.
|
||
|
||
JSON: `{"pane","sent","enter","keys"}`.
|
||
|
||
### `tty7 capture [%PANE] [--plain] [--scrollback]`
|
||
|
||
The pane's replay. Two independent choices:
|
||
|
||
**How much** — the newest scrollback segment by default, the whole ring with
|
||
`--scrollback`. The ring splits into segments on resize, so for a pane that was
|
||
never resized the two are identical.
|
||
|
||
A pane restored after its daemon died counts as resized. The screen it was
|
||
seeded with was recorded at whatever size it had then, so unless the new pane
|
||
comes back that same shape, plain `capture` answers with the restore banner and
|
||
the new prompt alone — everything above the banner needs `--scrollback`.
|
||
|
||
**In what form** — without `--plain`, the stored bytes with ANSI escapes intact,
|
||
decoded as UTF-8 (invalid bytes become U+FFFD). With `--plain`, those bytes
|
||
replayed through a terminal grid and printed as the text they produced.
|
||
|
||
Both forms cover the same span: the grid `--plain` replays through is sized to
|
||
hold whatever the segment can fill, so it never answers with less of the pane
|
||
than the raw form does. Rendering a full ring costs proportionate memory for
|
||
the moment it takes — a few hundred MB for the 8 MiB maximum, and nothing worth
|
||
measuring for an ordinary capture.
|
||
|
||
Either way it is a snapshot, not a stream: it collects the replay, settles for
|
||
~300 ms, and returns. Call it again for a newer one.
|
||
JSON: `{"pane","text"}`.
|
||
|
||
### `tty7 procs [%PANE]`
|
||
|
||
The process tree inside the pane, indented by depth, `*` on the foreground
|
||
process — then a second table of ports those processes are listening on. Prints
|
||
`nothing running in this pane` when both are empty.
|
||
|
||
A pane the server is not running is an error, not an empty answer, so "nothing
|
||
is running in it" and "there is no such pane" stay apart. A pane no workspace
|
||
holds is still one the server runs, and still reports — that is what
|
||
`pane ls --all` surfaces it for.
|
||
|
||
JSON: `{"procs":[{"pid","name","depth","foreground"}],"ports":[{"port","pid","name","addr"}]}` —
|
||
`addr` is the address the socket is bound to (`*`, `0.0.0.0`, `127.0.0.1`,
|
||
`[::1]`, or a specific interface).
|
||
|
||
### `tty7 agents`
|
||
|
||
Every pane running a recognised coding agent. Table:
|
||
`PANE AGENT STATUS MESSAGE`, status one of `idle` / `working` / `waiting` /
|
||
`done`. JSON: `{"agents":[...]}`, plus a `"diagnostics"` array when an agent's
|
||
status hook is missing or out of date — that is why an agent can be listed with
|
||
a status that never moves.
|
||
|
||
### `tty7 wait [%PANE] [--until STATE,…] [--changed] [--timeout SECS] [--interval MS]`
|
||
|
||
Blocks until the pane reaches one of the named states.
|
||
|
||
| Flag | Default | |
|
||
|---|---|---|
|
||
| `--until` | `waiting,done,exit` | See the state table below |
|
||
| `--changed` | off | Only wake on a state the pane moved into *after* the wait began |
|
||
| `--timeout` | none | Give up after N seconds, exiting `124` |
|
||
| `--interval` | `500` | Poll interval in ms (50–3,600,000) |
|
||
|
||
The states come from two places. Four are the agent's own status, as reported
|
||
by its [hooks](/agents/status); the last three are facts about the pane:
|
||
|
||
| State | Means |
|
||
|---|---|
|
||
| `idle` `working` `waiting` `done` | The agent's status |
|
||
| `no-agent` | Nothing is reporting status here — a plain shell, or an agent whose hooks are not installed |
|
||
| `free` | The foreground command has exited; the pane is back to its bare shell |
|
||
| `exit` | The pane itself is gone. Ends every wait whether it was asked for or not |
|
||
|
||
`free` is how you wait for a **command** rather than an agent, and it is the
|
||
one state that costs a second request per poll — so it is only checked when you
|
||
name it, and only when none of the agent states you asked for already matched.
|
||
With `--changed` it means "something ran and then finished", which is what you
|
||
want directly after a `send`; a command quick enough to finish inside one
|
||
`--interval` is never seen running, and the timeout says so.
|
||
|
||
The reply carries the agent's message and native session id. The JSON's `stale`
|
||
flag says whether the answer might belong to the previous turn.
|
||
|
||
`wait` is the one address-taking verb that does **not** refuse a pane the
|
||
server has no record of — it answers `exit`, `matched: true`, `stale: true` and
|
||
exits `0`, where `capture` and `send` would exit `1`. It cannot do otherwise:
|
||
the server forgets a pane once it is reaped, so a pane that finished and was
|
||
cleaned up and a pane id that never existed are the same question to it, and
|
||
refusing would break the first — which is the ordinary end of an
|
||
orchestration. `stale: false` is the only thing that says a pane moved into its
|
||
state while you were watching; a stale `exit` means it was already gone,
|
||
whatever the reason. Check the id itself with `pane ls --all` if you need to
|
||
tell a typo from a finished worker.
|
||
|
||
JSON: `{"pane","status","matched","stale","activity","message","session_id"}`.
|
||
A timeout exits `124` with the same object plus `"timed_out": true` —
|
||
`matched` is `false` there, and `stale` still says whether the pane moved
|
||
while you watched.
|
||
[Orchestration →](/agents/orchestration)
|
||
|
||
### `tty7 events`
|
||
|
||
Streams server events until interrupted, one per line — layout changes and
|
||
workspace preemption. `--json` makes it NDJSON. Blocks forever; run it with a
|
||
timeout or in the background.
|
||
|
||
Without `--json` each line is a sentence meant to be read, and its wording is
|
||
not a format to parse — `--json` below is. The kinds and payloads are stable;
|
||
the prose is not.
|
||
|
||
Because it blocks forever, returning at all means the server closed the stream:
|
||
that exits 1 and says so on stderr, so a loop consuming the lines does not read
|
||
the end of its input as the end of the work.
|
||
|
||
Each line is one externally tagged object, `{"<kind>": {…}}`:
|
||
|
||
| Kind | Payload |
|
||
|---|---|
|
||
| `layout` | `{"workspace","delta"}` — the bulk of the stream |
|
||
| `preempted` | `{"workspace","by"}` — another client took the workspace |
|
||
| `layout_resync` | no payload; the tree moved in a way no delta describes, so re-read it |
|
||
|
||
`delta` is externally tagged the same way. The kinds are `workspace_created`,
|
||
`workspace_renamed`, `workspace_deleted`, `workspace_touched`,
|
||
`active_tab_changed`, `tab_created`, `tab_closed`, `tab_renamed`, `tab_moved`,
|
||
`tab_regrouped`, `tab_restructured`, `ratio_changed` and `pane_facts`.
|
||
|
||
<Note>
|
||
**Two things that sound like their own events are not.** A pane exiting and an
|
||
agent changing status both arrive as a `layout` delta — `pane_facts` carrying
|
||
that pane, with `"live": false` for the exit and `agent.status` for the agent.
|
||
There is no pane-exit line and no agent-status line, whatever the shape of the
|
||
wire protocol suggests, so watch `pane_facts` for both.
|
||
|
||
For agent status specifically, `tty7 wait` and `tty7 agents` are the
|
||
supported way to ask; neither is built on this stream.
|
||
</Note>
|
||
|
||
### `tty7 status`
|
||
|
||
Same as `server status`: pid, uptime, pane count, dialect versions, build,
|
||
socket path. JSON is the `ServerStatus` object itself (`pid`, `uptime_secs`,
|
||
`panes`, `control_version`, `protocol_version`, `build`, `socket`).
|
||
|
||
### `tty7 doctor`
|
||
|
||
The install check: the three environment variables, whether the server answers,
|
||
whether its control and protocol versions match this binary, pid/uptime/panes,
|
||
how many machine links exist, and where each agent's
|
||
[status hooks](/agents/status) stand. Adds a note when you are not inside a
|
||
tty7 shell.
|
||
|
||
A `$TTY7_WS` or `$TTY7_PANE` naming something this server does not have is
|
||
marked `GONE` rather than reported as merely set — a shell outlives the
|
||
workspace it was opened in, and that is why the address-taking verbs suddenly
|
||
fail on an id you have no reason to doubt.
|
||
|
||
The hooks row is the one that explains a mystery: without them an agent reports
|
||
nothing, so `tty7 agents` shows it standing still and `tty7 wait` sits there
|
||
until it times out. Outdated hooks fail the same quiet way. Hooks are a local
|
||
install, so under `-m` the row reads `unknown`.
|
||
|
||
JSON: `{"context":{"config_dir","workspace","pane"},"server":{"reachable","dialect_ok","build","status","routes"},"config":{"ok","state","dir_writable"},"hooks":{"installed","outdated","not_installed"}}`
|
||
— the context fields are booleans, not values, and each `hooks` field is a list
|
||
of agent slugs. `context` also carries `workspace_gone` and `pane_gone` when a
|
||
server answered and could be asked; both are absent when none did, so that "it
|
||
names nothing here" stays distinct from "nobody could check". `config.ok` is
|
||
false only when the file failed to parse and tty7 is running on defaults;
|
||
`config.state` is the same sentence the table prints. `config.dir_writable` is
|
||
probed by writing, not read off the mode bits, because a read-only mount or an
|
||
ACL leaves a `0700` directory that refuses every write — false means settings
|
||
cannot be saved and `tty7 new` cannot file a workspace. Either of those false
|
||
exits `1`, so `tty7 doctor || alert` fires. The extra rows the table can show —
|
||
unread keys, unusable `custom_shells` — are prose, not JSON fields.
|
||
|
||
## `ws` — workspaces
|
||
|
||
Address a workspace by name, by full id, or by a unique id prefix (the 8-char
|
||
prefix `tty7 ls` prints). An ambiguous name or prefix is an error that lists the
|
||
candidates.
|
||
|
||
| Command | Effect | JSON |
|
||
|---|---|---|
|
||
| `ws ls` | Every workspace | `{"workspaces":[...]}` |
|
||
| `ws tree [WORKSPACE]` | One workspace as a tree: tabs, split axes and ratios, panes with cwds | The whole workspace object: `{"id","name","last_active","active_tab","tabs":[{"id","name","sidebar_group","root",…}]}` |
|
||
| `ws new [NAME]` | An empty workspace (no tab, no pane) | `{"id","name"}` |
|
||
| `ws rename WORKSPACE NAME` | Name or rename | `{"id","name"}` |
|
||
| `ws rm WORKSPACE` | Delete the workspace and hang up its panes | `{"removed"}` |
|
||
| `ws attach WORKSPACE` | Take it back from whoever holds it | `{"attached","took_over_from"}` |
|
||
| `ws detach WORKSPACE` | Let go without interrupting anything | `{"detached"}` |
|
||
|
||
`ws attach` is a takeover, not a subscription: the claim belongs to the
|
||
connection that made it, and the CLI's connection ends when the command does.
|
||
So it prints `took over from HOST` when it displaced someone and nothing when
|
||
it did not, and either way the workspace reads as unattached again immediately
|
||
afterwards. Displacing the holder is the whole of what it does from here —
|
||
staying attached is a thing only a client that keeps its link open can do.
|
||
|
||
For the same reason `ws detach` reports `{"detached"}` whether or not there was
|
||
a claim to release: it says "you do not hold this", which was already true.
|
||
|
||
<Warning>
|
||
`ws rm` hangs up the panes the workspace held. If the command reports that
|
||
some panes could not be hung up, they keep running as orphans with no
|
||
workspace — find them with `pane ls --all` and close them one by one.
|
||
</Warning>
|
||
|
||
Prefer `tty7 new <path>` over `ws new` when you want something usable: `ws new`
|
||
leaves an empty workspace you then have to populate, while `tty7 new --json`
|
||
hands back both ids at once.
|
||
|
||
The `root` node in `ws tree --json` is externally tagged, so a leaf is
|
||
`{"Leaf":{"pane":31}}` and a split is `{"Split":{"axis","ratio","a","b"}}` with
|
||
`a`/`b` nested the same way.
|
||
|
||
## `tab` — tabs
|
||
|
||
`@N` numbers tabs across the **whole machine** in tree order, densely from `@1`.
|
||
The numbering shifts whenever any workspace or tab is created or removed, so
|
||
resolve it immediately before use. A full tab UUID also works: `@<uuid>`.
|
||
|
||
| Command | Effect | JSON |
|
||
|---|---|---|
|
||
| `tab ls [WORKSPACE]` | Tabs of a workspace | `{"workspace","tabs":[{"ordinal","id","name","label","agent","group","panes":[…]}]}` |
|
||
| `tab new [WORKSPACE] [--cwd DIR]` | Add a tab with a fresh shell | `{"tab","pane"}` |
|
||
| `tab close @TAB` | Close the tab and every pane in it | `{"closed"}` |
|
||
| `tab rename @TAB NAME` | Name or rename | `{"tab","name"}` |
|
||
| `tab move @TAB INDEX` | Reposition within its workspace | `{"tab","to"}` |
|
||
|
||
`INDEX` counts from **0**, unlike the `@N` address beside it — `tab move @1 0`
|
||
leaves the first tab where it is, and `tab move @1 1` puts it second. It is a
|
||
position in the workspace's own tab list, not an `@N`, so it does not move a tab
|
||
between workspaces. Past the end it clamps to last rather than failing, the way
|
||
`split --ratio` does; `to` in the JSON echoes the number you asked for, so read
|
||
the position back from `tab ls` rather than from the reply.
|
||
|
||
`GROUP` is the heading the GUI's sidebar files the tab under, shown by its last
|
||
segment. Read-only from here: with the default repo grouping the GUI recomputes
|
||
it from the tab's working directory.
|
||
|
||
`label` falls back through the best evidence available — the name if someone set
|
||
one, else the agent running there, else the last segment of the cwd, else the
|
||
foreground process. `name` stays literal, so a script can tell a real name from
|
||
a stand-in.
|
||
|
||
## `pane` — panes
|
||
|
||
| Command | Effect | JSON |
|
||
|---|---|---|
|
||
| `pane ls [WORKSPACE]` | Panes with their workspace, tab, cwd, live flag | `{"panes":[…]}` |
|
||
| `pane ls --all` | The server's whole pane registry, including orphans | `{"panes":[…],"orphans":N}` |
|
||
| `pane split …` | Identical to top-level `split` | `{"pane"}` |
|
||
| `pane close [%PANE…]` | Close panes; their shells are hung up | `{"closed":[…]}` |
|
||
| `pane close --orphans` | Close every pane no workspace holds | `{"closed":[…]}` |
|
||
|
||
`--all` is the one that shows leaks. Each entry is
|
||
`{"pane","workspace","orphan","owner","title","cwd","live"}`: `owner` is the id
|
||
of the workspace that may attach to the pane (absent when none may), and
|
||
`orphan: true` means no workspace holds it. An interrupted `run` leaves orphans
|
||
here, as does a `ws rm` that reported panes it could not hang up.
|
||
|
||
`--orphans` is the reaper for exactly those. It closes what `pane ls --all`
|
||
lists as orphaned and nothing else — panes a workspace holds are untouched —
|
||
and reports an empty list rather than an error when there is nothing to clean
|
||
up, so a script does not have to guard it. A pane that cannot be closed does
|
||
not abandon the rest of the batch: the rest are still attempted, the complaint
|
||
goes to stderr, and the verb exits 1 with `{"closed":[…],"failed":[…]}` — the
|
||
list a retry needs.
|
||
|
||
<Warning>
|
||
`--orphans` closes every orphan on the machine, and an orphan can still be
|
||
doing real work. Not only an interrupted `run`: a `run` that is **in progress
|
||
right now** is an orphan too. `run --ws W` stamps its pane with `W` as the
|
||
owner and files it into no tab until `--keep` does, so while the command
|
||
executes the pane reads exactly like a leftover — same `orphan: true`, same
|
||
`owner`. Nothing in the listing tells the two apart, so `pane ls --all` cannot
|
||
answer this one for you.
|
||
|
||
In practice that means `--orphans` from one shell, or one agent, will kill a
|
||
`tty7 run` started from another. Close orphans by id when anything might be
|
||
running, and keep `--orphans` for a machine you know is idle.
|
||
</Warning>
|
||
|
||
`title` is usually the running command — `claude`, `nvim`, `cargo` — which makes
|
||
`pane ls --all --json` a quick way to find "the pane running X".
|
||
|
||
## `machine` — remotes
|
||
|
||
`machine ls` lists the local machine plus every link the server holds:
|
||
`MACHINE KIND CONNECTED`. JSON: `{"machines":[{"key","kind","connected"}]}`.
|
||
|
||
## `server` — the daemon
|
||
|
||
| Command | Effect |
|
||
|---|---|
|
||
| `server status` | Same as `tty7 status` |
|
||
| `server logs` | Tail the server log; prints the path, and says so when logging was never enabled (`TTY7_LOG=info` before the server starts) |
|
||
| `server start` | Bring up a server on this machine |
|
||
| `server stop` | Stop it — **every pane on the machine dies** |
|
||
| `server restart` | Hand the panes to a new server of this build; **sessions keep running** — the shells, and what they are running, carry across. A pane using tty7's own SSH client is the exception: that connection drops and has to be reopened |
|
||
| `server restart --hard` | Stop, then start — **every pane on the machine dies**, exactly as `stop` |
|
||
|
||
<Warning>
|
||
Do not run `start`, `stop`, or `restart` on someone else's behalf. They change
|
||
or destroy what the user's GUI is attached to.
|
||
</Warning>
|
||
|
||
## Not implemented yet
|
||
|
||
These parse and then exit 1 with an explanation:
|
||
|
||
- `ws stop` — the control dialect has no workspace-stop request yet
|
||
- `machine connect` / `machine disconnect` — use the GUI's connection manager
|