Skip to Content

hty send

Deliver input to a running session’s PTY, exactly as if a user had typed it. You can send plain text, symbolic key names, raw bytes, or a sequenced combination of all three.

The <session> argument accepts a session name, a full UUID, or a unique UUID prefix. If only one session is running, you can omit it.

Reference

hty send [SESSION] --text "..." | --raw-text "..." | --key NAME | --seq "..." | --bytes-hex HEX
                   | --click ROW COL | --scroll up|down

Send input to a session. Exactly one of --text, --raw-text, --key,
--seq, --bytes-hex, --click, --scroll is required.

Flags:
  --text STRING        UTF-8 text with C-style escapes (\n \t \r \\ \e).
  --raw-text STRING    UTF-8 bytes sent verbatim. No escape processing —
                       `\n` stays as literal backslash-n. Use this when
                       you want to type source code or other content that
                       contains backslashes you don't want interpreted.
                       Prefer over --text when shell quoting gets hairy.
  --key NAME           Named key with optional modifiers.
                       Supports ctrl-, alt-/meta-, shift- prefixes,
                       function keys (f1-f12), and combinations like
                       ctrl-alt-f or shift-up. Run `hty keys` for details.
  --seq STRING         Send a sequence of keys, text, and delays in one call.
                       Quoted strings are text, durations (e.g. 200ms, 1s)
                       are pauses, and bare words are key names.
                       Example: --seq '"hello" 200ms enter 500ms "world"'
  --bytes-hex HEX      Raw bytes encoded as hex.

Mouse input (issue #24). Rows and columns are 1-indexed, matching
snapshot conventions. The target app must have enabled mouse mode
(the usual `CSI ?1000/1002/1003 h` sequences); otherwise the
command fails with "target app has not enabled mouse input". Check
`hty snapshot --json`'s `mouse.enabled` to verify.
  --click ROW COL      Click at ROW/COL. Defaults to left button;
                       combine with --button to change.
  --scroll up|down     Scroll at the cursor (or --at ROW COL); emits
                       --amount N events (default 1).
  --button B           Button for --click: left (default),
                       right, middle.
  --at ROW COL         Row/col for --scroll. Defaults to 1 1.
  --amount N           Repeat count for --scroll. Default 1.

Delay flags (optional, combine with any mode above):
  --delay-before DUR   Sleep before sending (e.g. 200ms, 1s).
  --delay-after DUR    Sleep after sending.
  --delay-char DUR     Send text character-by-character with a delay
                       between each. Only works with --text, --raw-text,
                       or --seq.

Wait + snapshot flags (fuse send + wait + snapshot into one round-trip):
  --snapshot           Include the post-action snapshot in the response.
                       Combines with --json and --ansi like `hty snapshot`.
  --wait-duration DUR  Sleep DUR after sending, then snapshot. Requires
                       --snapshot (otherwise use --delay-after).
  --wait-until-idle [MS]
                       Block until the screen has been quiet for MS
                       milliseconds (default 100). The idle window is
                       measured from the moment this op begins on the
                       server, so a session that was already quiet
                       before the send won't trip the check immediately.
  --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).
  --json               With --snapshot, emit `{ok, matched, elapsed_ms,
                       snapshot, ...}` instead of the plain buffer.
  --ansi               With --snapshot, print the styled ANSI rendering
                       instead of the plain buffer.

Examples

# Type into an insert-mode editor hty send my-session --text "ihello world" # Confirm a yes/no prompt hty send my-session --text "y\n" # Press Escape hty send my-session --key esc # Send Ctrl-C hty send my-session --key ctrl-c # Type text, then press enter after 200ms hty send my-session --seq '"hello" 200ms enter' # Send raw bytes (the up-arrow escape sequence) hty send my-session --bytes-hex "1b5b41"

--text vs. --raw-text

--text processes C-style escapes — \n becomes a newline, \t a tab, \e the Escape byte, \\ a literal backslash. That’s what you usually want for typing commands, pressing Enter, etc.

--raw-text sends the UTF-8 bytes verbatim with no escape processing. Use it when you want to type content that contains backslashes you don’t want interpreted — for example, pasting source code, regex patterns, or shell snippets into an editor:

# With --text, the \n would be turned into a real newline hty send my-session --raw-text "const re = /\\n/;"

Prefer --raw-text whenever shell quoting of backslashes gets painful.

Mouse input

For TUIs that opt into mouse tracking (btop, lazygit, k9s, tmux, many fzf layouts), hty send can deliver click and scroll events. Rows and columns are 1-indexed, matching hty snapshot.

# Left-click at row 5, column 12 hty send my-session --click 5 12 # Right-click hty send my-session --click 5 12 --button right # Scroll down three times at the default position hty send my-session --scroll down --amount 3 # Scroll up at a specific cell hty send my-session --scroll up --at 10 40

hty sniffs the CSI ?1000/1002/1003 h/l (event set) and ?1006 h/l (SGR extended) toggles out of the PTY output stream to track per-session mouse state. send --click uses SGR encoding when the app has enabled ?1006, and legacy X10 otherwise. If an X10-only app receives coordinates past column/row 223 (past the 1-byte X10 range), the send fails with a clear error rather than silently truncating or upgrading the encoding to something the app can’t parse.

If the target app has not enabled any mouse-event mode, the command fails with target app has not enabled mouse input. Check hty snapshot --json’s snapshot.mouse.enabled field to verify before sending.

Fused send + wait + snapshot

For agent workflows, the common pattern is “type something, wait for the program to settle, then read the screen.” Instead of three round-trips (send, wait, snapshot), you can fuse them into one by combining --snapshot with a --wait-* flag:

# Type a command, wait for the screen to go idle for 200ms, read it back hty send my-session --text "git status\n" \ --snapshot --wait-until-idle 200 --json # Press Enter and wait for a specific string to appear hty send my-session --key enter \ --snapshot --wait-until-text "press any key" --timeout 5000 # Submit and wait for a regex match, returning styled ANSI hty send my-session --text "help\n" \ --snapshot --wait-until-regex "^(ERROR|OK):" --ansi

The response format follows hty snapshot — plain buffer by default, JSON with --json, styled ANSI with --ansi. With --json you also get matched, elapsed_ms, and timeout info, so an agent can tell whether the wait succeeded without a second call.

FlagEffect
--snapshotInclude the post-action screen in the response. Required for any of the flags below.
--wait-duration DURSleep DUR after sending, then snapshot. Use for programs that don’t produce observable output.
--wait-until-idle [MS]Wait until the screen has been quiet for MS ms (default 100). Idle is measured from when the op starts on the server, so a session that was already idle won’t trip the check.
--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 process exits.
--timeout DURCap on any --wait-until-* (default 30s; 0 = no cap).
--json / --ansiOutput format for the included snapshot.

Every input sent via hty send is recorded in the session log as an input event, making sessions fully reproducible and auditable.

Last updated on