Command Line
Documentation in progress
Some sections are still incomplete.
thinkterm is the command you use from a shell day to day.
thinkterm --helpIt is not the only binary installed. Desktop and server installations both carry thinkterm-mux-server, which is what actually serves remote clients and browsers — see Remote, Web and The mux server below. A compatibility wezterm name is installed alongside, and the shell completions cover both. Desktop and server installations also carry thinkterm-plugin-server, the program plugins run under, the built-in Snippets plugin among them; it is started when something first needs it and is never run by hand — see Plugins.
Subcommands
| Command | What it does |
|---|---|
start | Start the GUI, optionally running an alternative program. Aliased as -e |
tui [DOMAIN…] | Open the ThinkTerm interface in the current terminal |
connect <name> | Connect to a ThinkTerm multiplexer |
ssh | Establish an SSH session |
serial | Open a serial port |
cli <subcommand> | See and control panes, tabs and windows, and check on coding agents, through a running multiplexer server |
update | Check for a newer release and install it. See Updates |
plugin | List plugins, turn them on and off, reload them and send one a call. See Plugins |
skill setup|status|remove|show | Install ThinkTerm's skill and a session-start hook into coding agents. See Letting a coding agent use ThinkTerm |
config check [FILE] | Load a configuration file as the app would and report its errors and warnings, without applying it; the running app is left alone. Without a file it checks the one the app would load. Exits with status 1 when the file does not load |
imgcat <file> | Output an image to the terminal |
ls-fonts | Display information about the fonts in use |
show-keys | Show the current key assignments |
set-working-directory | Tell the terminal the current working directory, for shell integration |
record | Record a terminal session as an asciicast |
replay | Replay an asciicast |
shell-completion | Emit a completion script for bash, zsh, fish, elvish, PowerShell or Fig |
One more subcommand is left out of --help. list-files writes a Project's file listing, and it is what the desktop runs on a remote host to search that host's files — see Files and Notes. It is not meant to be run by hand, but it has to be there: a host whose ThinkTerm predates it cannot be searched.
Talking to a running multiplexer
thinkterm cli addresses a server that is already running — the one behind your GUI window, or a background mux server. Because the multiplexer can address any pane across windows, Threads and connected clients, these commands reach sessions you are not currently looking at.
Which server answers is otherwise implicit, and it is the usual reason a command appears to do nothing: by default thinkterm cli prefers a running GUI, and starts a server if it finds none. --prefer-mux sends the command to the background mux server instead, and --no-auto-start makes the command fail rather than start a server just to answer it.
--class <NAME> picks out a GUI started under a window class of its own, with --class on thinkterm start. Without it, the command finds the GUI you started normally; you only need --class to reach one you started with a class of its own. thinkterm tui takes the same flag and works the same way.
A command that acts on a pane takes --pane-id with its number, and otherwise acts on the pane it runs in, which WEZTERM_PANE names. --pane names one instead, by a label you gave it — see Naming panes.
Finding things
| Command | What it does |
|---|---|
list | List windows, tabs and panes, each with its label if it has one |
list-clients | List connected clients |
get-pane-direction | Report the pane in a given direction |
Creating and destroying
| Command | What it does |
|---|---|
spawn | Start a new tab or window. Prints the new pane's id. --label names the pane; --wait waits for its program and exits with its status |
split-pane | Split an existing pane. Prints the new pane's id, and takes --label and --wait as spawn does |
move-pane-to-new-tab | Move a pane out into a tab of its own |
kill-pane | Close a pane |
Moving around and resizing
| Command | What it does |
|---|---|
activate-pane | Focus a pane |
activate-pane-direction | Focus the pane in a direction |
activate-tab | Focus a tab |
adjust-pane-size | Resize a pane |
zoom-pane | Zoom a pane to fill its tab |
Text in and out
| Command | What it does |
|---|---|
send-text | Write text into a pane as a paste, bracketed if the program in it asked for that. No Enter is added: --enter presses it a moment after the text, and --no-paste sends the text as plain input |
send-key | Press keys by name, one after another, encoded the way the program in the pane asked for keys, as if typed. --wait then waits for the command they start. See Pressing keys |
get-text | Read the text on a pane's screen. --pane-id or --pane picks the pane, by default the one the command runs in. --start-line and --end-line pick the rows: 0 is the top of the screen, and a negative number reaches back into the scrollback. --format is text (the default), vt, which keeps the colour and style sequences as --escapes does, or html, with its colours. --trim drops the spaces that end lines and the empty lines at the end, and --unwrap joins the lines the terminal wrapped back into the lines they were |
Waiting and watching
| Command | What it does |
|---|---|
wait | Wait for a pane's program to end, or with --command for the command running in its shell, and exit with its status. See Waiting for a program or a command |
events | Print what happens as it happens until interrupted. See Watching what happens |
Naming and infrastructure
| Command | What it does |
|---|---|
set-tab-title | Rename a tab |
set-pane-label | Label a pane, so that --pane finds it. An empty label takes it away |
set-window-title | Rename a window |
rename-workspace | Rename a workspace |
agent list | List the panes running a detected coding agent, and each one's state. Read-only |
agent context | Print where the pane it runs in is and what shares its tab, for a coding agent starting there. See Letting a coding agent use ThinkTerm |
tlscreds | Obtain TLS credentials |
proxy | Start an RPC proxy pipe |
color-schemes | List the built-in colour schemes. Answered from the binary, with no server involved; --json prints their colours too |
stop-server | Stop the background session server. Every terminal inside it ends, so it asks first when run at a terminal; --force does not ask |
Browser access
| Command | What it does |
|---|---|
web-server status|on|off | Whether the server is accepting browsers, and where. on takes --bind-address |
web-token mint|list|revoke | Mint, list and revoke the links a browser gets in with |
Both address the mux server rather than a GUI's own multiplexer, and a minted link is a login as your user on that machine. With reaching other machines on, it is also a way into every machine that account can SSH to. Web covers what that means.
list, list-clients, agent list and web-token list print a table by default, and JSON with --format json. The first three, and events, take --json for short.
The commands that print a list or text — those four, get-text and color-schemes among them — end quietly, with a success status, when whatever they are piped into stops reading, as in thinkterm cli list | head.
Stopping the session server
thinkterm cli stop-server stops the background session server at the default socket, and every terminal inside it ends with it. It never starts a server: with none running, it says so and changes nothing.
Run at a terminal, it names the server's process and socket and asks first, and anything but y or yes leaves the server running. It asks only when its standard input is a terminal, so a script or a pipe stops the server without a question; --force skips the question at a terminal too. It then waits up to ten seconds for the server to exit, and says so if it is still shutting down after that.
The next launch, connect or cli command that needs a server starts a fresh one. Remote describes the other ways to stop it.
Naming panes
A pane number changes from one run to the next. A label does not: thinkterm cli set-pane-label --pane-id 12 build names pane 12, spawn and split-pane take --label build to name the pane they create, and from then on --pane build finds it.
thinkterm cli set-pane-label build
thinkterm cli spawn --label tests -- npm test
thinkterm cli get-text --pane tests --trimA label is the name a pane tab carries in the window, so renaming the tab there sets the same label. It is kept by the server, which means every window, the CLI, a browser and a restored Thread see it. A label has no control characters and at most 128 characters, and is trimmed. One of only digits is refused, since --pane reads digits as a pane number. An empty label takes the label away. list and agent list show the label in a LABEL column and a label field, and events reports a pane being labelled.
--pane accepts:
- A pane number. Digits and nothing else. If another pane's label is those same digits, the command stops and lists both rather than guess.
- A label, with case ignored.
- A Thread's name, alone or after its Project's as in the sidebar —
my-project · fix login— when that Thread has one pane. thread/label, for a label that several Threads use.
When a label is held by several panes and exactly one of them is in the Thread of the pane running the command, that one is meant. Otherwise the command stops, says how many panes the name could be, and lists each with its number, label, title and Thread; name one by number. --pane cannot be combined with --pane-id. Every command that takes a pane takes --pane, spawn, split-pane and activate-tab among them.
Pressing keys
send-key takes one or more keys, each written as modifiers and a key joined with +, in any case:
thinkterm cli send-key --pane build ctrl+c
thinkterm cli send-key escape up up enterThe modifiers are ctrl, alt (or opt), shift and cmd (or super). A key is a single character, or enter, tab, escape, backspace, delete, space, up, down, left, right, home, end, pageup, pagedown, insert, or f1 to f24. Each is encoded the way the program in the pane asked for keys, as if typed. A key it does not know stops the command before anything is sent.
Waiting for a program or a command
wait blocks until something in a pane ends, and exits with how it ended:
thinkterm cli spawn --label tests --wait -- npm test
thinkterm cli wait --pane tests --for 10m
thinkterm cli send-key --pane build --wait enterwaitwaits for the program the pane started to end, and exits with its status.wait --commandwaits for the command running in the pane's shell, and exits with that command's status. A command typed a moment ago has a second to start; when none has, it exits with the status of the last command that finished, or 0 if none ever did.send-key --wait KEY…presses the keys and then waits for the command they start. It counts the shell's finished commands first, so a command that ends at once is not missed. To type a command and wait for it,send-textand thensend-key --wait enteris surer thanwait --commandafterwards.spawn --waitandsplit-pane --waitprint the new pane's number first, then wait for its program.
--timeout, or --for for short, gives up after a number of seconds or a number with s, m or h: 90, 30s, 10m, 1.5h. A wait that gives up exits with status 124 and says timed out. A program or command that ended without a known status exits with 125, and says so: a command the shell ended without an exit status, and a program in a pane a new server took over with --takeover, which can no longer say how its program ended. Neither reads as a success. A pane that closes without its program's status arriving exits with 1, and a program ended by a signal names the signal on standard error.
wait --command and send-key --wait need a shell that reports where its commands start and end. That is ThinkTerm's shell integration; see Install. Without it they stop with an error that says so, and spawn --wait still works.
Watching what happens
thinkterm cli events prints one line for each thing that happens on the server until interrupted:
14:02:11 command_finished pane 12 status 0
14:02:11 agent_status pane 12 claude idle--pane or --pane-id keeps only what happens to one pane; tab, window and client events drop out. --format json, or --json, prints one object per line instead, with a type and the event's own fields. These are kept stable: events and fields may be added, never removed or retyped. If more than a few thousand events pile up unprinted, the extra ones are dropped. The command reconnects by itself when the connection to the server is replaced, and stops with an error when it is lost.
type | When | Fields besides type |
|---|---|---|
pane_added, pane_removed | A pane opens or closes | pane_id |
program_exited | The program the pane started ended | pane_id, exit_code (null when unknown), signal |
command_finished | The pane's shell reported a command finished | pane_id, exit_code (null when it gave none) |
bell | The pane rang the bell | pane_id |
notification | A program raised a desktop notification | pane_id, title, body |
title_changed | The pane's title changed | pane_id, title |
cwd_changed | The pane's working directory changed | pane_id |
progress | A progress report | pane_id, state (none, percentage, error or indeterminate), percent |
user_var | A program set a user variable | pane_id, name, value |
clipboard_written | A program wrote the clipboard. What it wrote is not repeated | pane_id |
agent_status | A coding agent was found in the pane, changed state or went | pane_id, agent, state |
foreground_program | The program in front in the pane changed | pane_id, executable, runs |
pane_focused | A pane took focus | pane_id |
pane_label_changed | A pane was labelled, or lost its label | pane_id, label (null when taken away) |
tab_added | A tab was added to a window | tab_id, window_id |
tab_title_changed | A tab was renamed | tab_id, title |
window_title_changed | A window was renamed | window_id, title |
workspace_changed | A window moved to another workspace | window_id, workspace |
workspace_renamed | A workspace was renamed | old_workspace, new_workspace |
client_joined, client_left | A window, browser or phone began or stopped showing panes | hostname, username, pid, epoch, id |
Exit statuses
A cli command that fails exits with 1, except these:
| Status | Meaning |
|---|---|
| 2 | The command line asks for something that cannot be: arguments that do not parse, an unknown key, a label of only digits |
| 3 | A pane that is not there, or a --pane name that matches none or several |
| 4 | The server cannot be reached, or will not talk to this version of the program |
A command that waits exits with what it waited for instead, as above.
Agent status from a script
thinkterm cli agent list prints one row for each pane in which ThinkTerm has found a coding agent:
| Column | What it holds |
|---|---|
PANEID | The pane, by the id the other cli commands take |
AGENT | The agent's id, such as claude |
STATE | working, blocked, idle, error or unknown |
EVIDENCE | What decided the state: report when the program sent its own status report, contract when the program reported it through THINKTERM_AGENT, screen when a screen rule matched, fallback when nothing matched and a known agent is taken to be idle |
AGE | How long ago the state last changed |
WORKSPACE | The multiplexer workspace the pane is in. For a pane in a Thread this is an internal name, not the Thread's |
TITLE | The pane's title |
LABEL | The pane's label, empty when it has none |
--format json prints an array of objects instead, with the fields pane_id, label (null when there is none), agent, state, evidence, session_id (null when there is none), since_unix (when the state last changed, in Unix seconds by the clock of the machine that detected it), workspace and title. These fields are meant for scripts and are kept stable. Agent Status explains where the states come from.
Letting a coding agent use ThinkTerm
A coding agent running in a ThinkTerm pane can use thinkterm cli to see the other panes, read them, type into them and open new ones. thinkterm skill setup teaches Claude Code and Codex to, and tells each one where it is when it starts. It is separate from recognising agents, which installs nothing, and it changes nothing until you run it.
thinkterm skill setup --dry-run
thinkterm skill setup| Command | What it does |
|---|---|
skill setup | Installs or updates the skill and the hook in the agents found on this machine. Safe to run again. --dry-run says what would change without changing it |
skill status | Shows, for each agent, whether the skill and the hook are current, outdated, missing, someone else's or unreadable |
skill remove | Takes the skill and the hook out again |
skill show | Prints the skill, for an agent setup does not know. npx skills add RoversX/ThinkTerm --skill thinkterm -g installs the same skill from the repository for other agents, such as Cursor |
setup, status and remove take --agent claude or --agent codex to name one agent whether or not it was found. Without it they go through the agents whose own folder exists here, and say so when there is none.
What setup writes
| Agent | The skill | The hook |
|---|---|---|
| Claude Code | ~/.claude/skills/thinkterm/SKILL.md | A SessionStart entry in ~/.claude/settings.json |
| Codex | ~/.agents/skills/thinkterm/SKILL.md | A SessionStart entry in ~/.codex/hooks.json |
CLAUDE_CONFIG_DIR and CODEX_HOME move an agent's own folder, and with it Claude Code's skill and both settings files. The skill is a short note: that the agent is inside ThinkTerm, that thinkterm cli --help lists the commands, where thinkterm is if it is not on PATH, and that typing into a pane it did not open, closing panes or stopping the server may deserve asking you first. A pane has THINKTERM=1 set, which the skill's description keys on.
The hook runs when the agent starts. In a ThinkTerm pane it prints the pane's number, label and title and its tab's title, then the other panes in that tab — up to twelve, then how many more — how many panes other tabs hold, and how to read a pane. This is thinkterm cli agent context. Outside a ThinkTerm pane, or when ThinkTerm does not answer, it prints nothing and succeeds, and it never starts a server, so the agent starts as it would without it. Waiting for it is capped at ten seconds. The hook is not installed on Windows yet; the skill is.
setup edits only the hooks entry of the settings file and leaves the rest of its text as it was. It leaves alone a settings file that is not a JSON object, and a skill file called thinkterm that is not ThinkTerm's, and says so. Claude Code runs hooks only in folders you have trusted, and Codex runs a new hook once you approve it with /hooks. Run setup again after an update to bring an outdated skill or hook up to date.
Undoing it. thinkterm skill remove deletes the skill file, and its folder when nothing else is in it, and takes the hook out of the settings file together with any SessionStart list or hooks object that leaves empty. Other hooks and other skills stay. By hand, delete the file and remove the entry.
Colour schemes as data
thinkterm cli color-schemes prints the names of the built-in colour schemes, one per line. --json prints a JSON array instead, one object per scheme, with name, foreground, background, cursor_bg, cursor_fg, cursor_border, selection_bg and selection_fg as #rrggbb strings, and ansi and brights as lists of eight. A scheme that names no cursor or selection colours gets the defaults filled in.
The list is the one compiled into the binary, so it needs no server and reads no config. Schemes defined in your config, or kept in a colours directory, are not in it.
The mux server
thinkterm-mux-server is the multiplexer on its own, with no window. When the desktop keeps its terminals in a session server, that server is one of these, started for you; on a host with no desktop you start it yourself, usually with --daemonize.
| Flag | What it does |
|---|---|
--daemonize | Detach from the terminal and run in the background |
--takeover | Take over from the mux server already running on the same socket: its panes, windows and clients carry on under the new binary, and the old server exits. The running server must support it. Not available on Windows |
--no-initial-pane | Start with no pane, instead of one running the default program. The desktop's session server starts this way, since every pane in it is one the desktop asked for |
--cwd <DIR> | The working directory of the first pane's program |
-- <PROG>… | Run this program in the first pane instead of your shell |
-n, --config-file, --config name=value | Skip the config, pick a config file, or override one option — the same global flags thinkterm takes |
Two programs in two panes can talk to each other
send-text and get-text are enough to let one program drive another, with no changes to either of them:
Program A ──send-text──► Program B's terminal input
B does the work and prints a response
Program A ◄──get-text─── reads the result backAnything interactive can take part — a coding assistant, a shell script, a REPL. The pane is the interface, so nothing has to be built for this specifically.
Today the exchange happens by injecting terminal input, which means there is no message identity and no structured response. send-text --enter and send-key do the typing, and get-text --trim reads the screen back. Completion need not be polled for: when B is a shell command, send-key --wait enter returns when it ends, with its status, and wait does the same for a program. When B is a coding agent, events --pane reports its agent_status going back to idle. A more structured protocol is under design.
Related
- Agent Status — what
cli agentandcli eventsreport on - Web — what
web-serverandweb-tokenturn on - TUI — the workspace in a plain terminal
- Plugins — what
thinkterm pluginmanages - Keyboard and Commands — the keymap
show-keysprints
