| name | pty |
|---|---|
| description | Run and manage long-lived or background processes — dev servers, test suites, builds, interactive CLIs, agents — in persistent, detachable terminal sessions. Reach for pty INSTEAD of `&` / nohup / raw background shell whenever you need to start work, go do something else, then come back to read its output, send it input, or restart it; and for any interactive tool that needs a real TTY (auth/keychain prompts, TUIs, REPLs). |
| when_to_use | An agent needs to start a process and check on it later; run a dev server / test suite / build and wait for a readiness or result line; drive an interactive CLI that needs a real terminal; or keep a process alive across disconnects. NOT for one-shot commands whose output you read immediately — run those directly. |
pty runs a command in a managed terminal session you can detach from and
reconnect to later, from anywhere (including over SSH). It's the terminal /
session layer: run, list, peek, send, restart, kill, up.
- A long-lived / background process: dev server, test suite, build, watcher, an agent.
- An interactive CLI that needs a real TTY: keychain/auth prompts, a TUI, a REPL.
- Any "start it, go do something else, come back to read / send / restart" task.
Prefer pty over & / nohup / pipes for these — you get lifecycle control,
readable replayed output, and the ability to wait for specific text. For a
one-shot command whose output you read right now, just run it directly.
pty run -d --name <name> --tag owner=<you> -- <command> # start detached, tagged
pty peek --wait "<ready text>" --plain <name> -t 30 # block until ready
pty peek --full --plain <name> # read full output
pty send <name> --seq "<text>" --seq key:return # send input + Enter
pty kill <name> # clean up when doneTag the sessions you create; only touch sessions you created.
Key modifiers accept +, -, or _ separators and ignore case. For example,
key:ctrl+u, key:ctrl-u, key:ctrl_u, and readline-style key:C-u are
equivalent.
- A broken global
ptyon$PATHsilently breaks the whole message bus.st/ smalltalk delivery shells out topty sendfound on$PATH. If a global-install symlink points at a stale or brokenpty, every agent's message delivery fails network-wide — silently. Runptyfrom the intended install; if you do global-install, confirmpty --versionworks before trusting delivery. - Isolation is
PTY_ROOT, notPTY_SESSION_DIR. To keep scratch/test sessions out of the production registry, setPTY_ROOT=<dir>.PTY_SESSION_DIRis a deprecated alias and is ignored whenPTY_ROOTis already set (as it is inside a supervised session tree) — so setting onlyPTY_SESSION_DIRthere leaks your sessions into the ambient registry. pty now warns when both are set. - Sending text + Enter: mind the timing (top cause of "I sent it but nothing
happened").
pty send <ref> "text"sends NO newline — to submit, usepty send <ref> --seq "text" --seq key:return. The why it can silently fail: a terminal program processes a burst of bytes differently from spaced-out input. With zero spacing, the trailingkey:returnroutinely arrives before the program's readline / PTY event loop has parsed and rendered the typed text (and before bracketed-paste framing closes), so the Enter submits an empty or partial line.pty sendnow inserts a 0.3s gap between--seqitems by default so each chunk is consumed before the next — you usually don't need to think about it. Overrides:--with-delay <sec>to tune (some slow TUIs want 0.5s+), and--with-delay 0for a raw back-to-back stream (fast/bulk sends where you know the receiver can take it). - Don't nest. Inside a session, a bare
pty runruns the command directly (nesting guard); usepty run -dto explicitly background a new session from inside one.
Run pty --help for the full subcommand list, and pty <subcommand> --help for
that command's flags and examples. pty --version prints <semver>+<short-sha>.