A PTY broker for driving interactive, prompt-based programs from an agent (LLM) or any caller that cannot hold a persistent connection.

Tools like git add -p, git rebase -i, REPLs, or installer wizards expect a real terminal on the other end and talk in a back-and-forth of prompt → answer → new prompt. An LLM tool-call, however, is a one-shot process invocation: it starts, does a thing, and exits. There is nothing to keep the interactive program — or its pseudo-terminal — alive between calls.

pty-prompt solves this by splitting into two parts:

So the agent just shells out to pty-prompt step ... repeatedly; the daemon holds the session open and remembers the latest output in between.

# Start a session. Everything after `--` is the command to run.
# Prints the program's initial output once it settles.
pty-prompt start -- git add -p

# Iterate: write TEXT + Enter, wait for the output to settle, and print
# everything produced since the input — i.e. the next prompt.
pty-prompt step y

# With no TEXT, step just re-polls (e.g. while a program is still working).
pty-prompt step

# Block until the child exits, then print everything since the call + exit
# status. For non-interactive commands where you just want the result.
pty-prompt wait

# Graceful shutdown (SIGTERM, then SIGKILL after a grace period).
pty-prompt stop

# Force shutdown (SIGKILL now) — for a wedged child that ignores SIGTERM.
pty-prompt kill-session

step is the one verb you iterate with. It always appends Enter, so you never have to think about line submission. The reply contains everything the child produced since your input (including the echo of what you typed), so you see the freshly-rendered prompt in the same call — no separate "read" step.

A prompt-based program never tells you "I'm finished, your turn". So after sending input, step waits until the child has produced no new bytes for --timeout milliseconds (default 500), then returns whatever accumulated. This is a heuristic — bump --timeout for slow programs.

When you don't want a heuristic at all — e.g. a non-interactive build where the only thing you care about is the final result — use wait instead. It blocks until the child actually exits, then prints everything produced since the call plus the exit status. One call, no polling, no guessing a quiet window. --max-wait (default 60m) caps the block so a wedged child can't hang you:

pty-prompt start --session build -- nix build .#thing -L
pty-prompt wait  --session build          # blocks until the build finishes

When the child exits, step reports it in the status line (see below). The daemon stays up after the child exits so a final step can still retrieve the last output; stop/kill-session then tear it down.

Every invocation prints a single status line to stderr (program output stays on stdout, so the two never mix). It is always printed — including on an empty poll while a program is still working — because that is exactly when the timing is most useful:

[pty-prompt: 13:58:42 | running for 16s, quiet for 1.5s, still running]
[pty-prompt: 14:00:56 | running for 2m14s, quiet for 0.3s, exited: exit status 1]
[pty-prompt: 14:00:56 | ran for 2m14s, exited, session "build" stopped]

Errors use the same bracketed form, e.g. [pty-prompt: session "build" already running for 16s (stop it first)].

Output is ANSI-stripped by default (escape sequences removed, CR dropped), so you get plain, line-oriented text ready to read. Pass --raw to keep escape codes verbatim — useful for full-screen TUIs where cursor movement matters.

Multiple independent programs can run at once via --session NAME (default: default):

pty-prompt start --session rebase -- git rebase -i HEAD~5
pty-prompt step  --session rebase x       # mark a commit for edit
pty-prompt stop  --session rebase

Flag Applies to Meaning
--session NAME all Session name (default default).
--timeout MS start / step Quiet window that counts as "settled" (default 500 ms).
--max-wait MS wait Cap on how long to block for exit (default 60 min).
--raw start / step / wait Keep ANSI escape codes instead of stripping them.

pty-prompt start -- git add -p          # shows the first hunk + "Stage this hunk [y,n,...]?"
pty-prompt step y                       # stage it; shows the next hunk
pty-prompt step n                       # skip this one
pty-prompt step s                       # split a hunk
pty-prompt step q                       # quit
# git exits on its own; the last step's status line ends with "exited".
pty-prompt stop                         # clean up

nix build .#pty-prompt
nix profile install .#pty-prompt
# or for local dev:
go run ./users/Profpatsch/pty-prompt -- bash

Full reference documentation is in the manpage:

man pty-prompt
# or, without installing:
man ./users/Profpatsch/pty-prompt/pty-prompt.1