Skip to Content

hty run

Start a program in a new detached PTY session. The session runs in the background — use watch, snapshot, or attach to interact with it.

Reference

hty run [--name NAME] [--rows N] [--cols N] [--cwd PATH] [--scrollback N] [--attach] -- program [args...]

Create a new session and start `program` inside a fresh PTY. The session
is detached from your terminal; observe it with `hty watch` and drive it
with `hty send`/`hty snapshot`/`hty wait`.

Flags:
  --name NAME       Human-friendly alias for the session. Must be unique.
  --rows N          Initial row count (default 24)
  --cols N          Initial column count (default 80)
  --cwd PATH        Child's working directory
  --scrollback N    Scrollback buffer size (default 10000)
  --attach          Spawn + attach in one invocation. Streams PTY
                    output to stdout and forwards stdin into the
                    session, just like `hty attach`. Ctrl-A d
                    detaches (session persists unless --remove is
                    also set). Mutually exclusive with --snapshot
                    and any --wait-until-* flag.
  --remove          Automatically remove the session from the registry
                    once the child process exits (success, failure, or
                    signal). Tied to child lifetime; off by default so
                    sessions persist for `hty list` / `hty logs` /
                    `hty replay` until `hty delete`. Pairs naturally
                    with --attach: `hty run --attach --remove -- ...`
                    is a one-shot "foreground a command in a PTY".

Wait + snapshot flags (let `run` block until the program is ready and
return the initial render in one round-trip):
  --snapshot                Include the post-spawn snapshot in the response.
                            Requires one of --wait-until-* or --wait-duration.
  --wait-duration DUR       Sleep DUR after spawn, then snapshot.
  --wait-until-idle [MS]    Block until the screen has been quiet for MS
                            milliseconds (default 100).
  --wait-until-text STR     Block until STR appears in the rendered buffer.
  --wait-until-regex RE     Block until RE (POSIX extended) matches.
  --wait-until-exit         Block until the child process exits.
  --timeout DUR             Cap on any --wait-until-* (default 30s; 0 = none).
  --ansi                    With --snapshot, print styled ANSI rendering.

`--detach` is accepted as a no-op — every `hty run` session is
detached by default. Use `hty attach` afterwards, or `--attach`
on this invocation, for an interactive view.

Example:
  hty run --name debug-vim -- vim /tmp/foo.txt
  hty run --name app --snapshot --wait-until-idle -- create-next-app my-app
  hty run --attach --remove -- bash -c 'echo hi'

Examples

# Simple session hty run -- vim /tmp/notes.txt # Named session with custom terminal size hty run --name my-vim --rows 40 --cols 120 -- vim /tmp/notes.txt # With working directory hty run --name build --cwd /my/project -- npm test

Fused run + wait + snapshot

For agent workflows that want “start the program, wait until it’s ready, give me the first screen” in a single call, combine --snapshot with a --wait-* flag:

# Launch create-next-app, wait for it to settle, and return the first prompt hty run --name app --snapshot --wait-until-idle -- \ npx create-next-app my-app # Launch a script and wait for a specific line before reading hty run --name install --snapshot --wait-until-text "Finished" --timeout 60000 -- \ ./install.sh # Launch a quick command and wait for it to exit hty run --snapshot --wait-until-exit -- echo hello
FlagEffect
--snapshotInclude the post-spawn screen in the response. Requires one of the --wait-* flags below.
--wait-duration DURSleep DUR after spawn, then snapshot.
--wait-until-idle [MS]Wait until the screen has been quiet for MS ms (default 100).
--wait-until-text STRWait until STR appears in the rendered buffer.
--wait-until-regex REWait until RE (POSIX extended) matches.
--wait-until-exitWait until the child exits.
--timeout DURCap on any --wait-until-* (default 30s; 0 = no cap).
--ansiWith --snapshot, return the styled ANSI rendering instead of plain text.

The -- separator is required when passing arguments to the program. Without it, hty cannot distinguish its own flags from the program’s arguments.

--detach is accepted as a no-op — every hty run session is detached by default. The old -d short form was removed; use the long --detach, or pass --attach to spawn and attach in one go.

Foreground with --attach

--attach spawns the session and immediately attaches to it, just like hty attach would — your terminal goes into raw mode, stdin is forwarded into the PTY, output streams back to stdout, and Ctrl-A d detaches. On clean exit, the child’s exit status becomes the hty run exit status.

# One-shot: edit a file interactively. Ctrl-A d detaches, session persists. hty run --attach -- vim /tmp/notes.txt # One-shot with auto-cleanup: run, print output, remove the session when done. hty run --attach --remove -- bash -c 'echo hello' # Handy for agents with background-process support (Claude Code, etc.): # background the invocation and read the harness's streaming stdout — no # polling, no `hty snapshot` loop, and `--remove` ties the session's # lifetime to the backgrounded shell. hty run --attach --remove -- npm test &

--attach is mutually exclusive with --snapshot and every --wait-until-* flag (including --wait-duration) — the fused wait/snapshot path and the interactive attach loop have no meaningful combination. Pass one or the other.

See hty attach for the detach keybinds and multi-client behavior. All of that applies to run --attach too.

Auto-cleanup with --remove

By default, an exited hty run session lingers in the registry as a “zombie” record so hty list, hty logs, and hty replay keep working on it until you run hty delete. That’s the right behavior for sessions you want to inspect after the fact — but it’s a leak for one-shot invocations (migrations, test scripts, throwaway agent calls) where you will never look at the record again.

Pass --remove to tie the session’s lifetime to the child process. The moment the child exits — whether it succeeded, failed, was signalled, or was terminated by hty kill — the session is automatically deleted from the registry and its log file is removed from disk. No zombies, no follow-up hty delete.

# One-shot migration: session disappears from `hty list` as soon as the script ends. hty run --remove -- ./migrate.sh # Combine with --wait-until-exit to block until the child exits; on return the # session is already gone. Exit code propagates from the child. hty run --remove --wait-until-exit -- sh -c 'exit 7' echo $? # 7 # While the child is still running, `hty list` shows the session normally — # auto-removal only fires after the child exits. hty run --remove --name long -- sleep 60 hty list # shows "long" while sleep is running # …sleep exits after 60s, then a subsequent `hty list` is empty.

Running hty kill on a --remove session still works as expected: the session terminates, the auto-remove sweep reaps the record shortly after, and the two paths don’t race.

--remove is especially useful in AI agent workflows. Agents routinely forget to hty kill sessions once they’ve finished reading the last screen; over time the registry fills with stale entries. Adding --remove to every hty run the agent issues makes the leak structurally impossible.

Last updated on