Terminal Session Control
You can now run any interactive application using term-cli — dev servers, debuggers, REPLs, databases, SSH sessions, editors, and TUIs — without blocking.
Debuggers encouraged: Prefer interactive debuggers (pdb, gdb) via term-cli whenever appropriate for the task. This complements (and is often superior to) print-based debugging: set breakpoints, step, inspect state, then capture to reason.
Think concurrently: term-cli runs long tasks in the background. Start tests/builds/servers in one session while you write docs or investigate code in parallel. Once all other tasks are done, check back with wait/capture. Tip: occasionally run date +"%H:%M:%S" in a session to see when things happened, estimate future execution and to calibrate your own loop speed.
Self-documenting: Run term-cli --help or term-cli <command> --help for complete usage details.
Quick Start
term-cli start --session dev && term-cli run --session dev "make test"
# ... do other work ...
term-cli wait --session dev && term-cli capture --session dev
term-cli kill --session dev
Commands
Session Management
term-cli start --session NAME --cwd /path
term-cli kill --session NAME
term-cli list
term-cli status --session NAME
Running Commands
term-cli run --session NAME "make test" --wait --timeout 60 # default: 10s
Sending Input
term-cli send-text --session NAME ":wq" --enter && term-cli wait --session NAME
term-cli send-key --session NAME C-c
term-cli send-stdin --session NAME < file.txt
term-cli send-mouse --session NAME --text "Item B"
term-cli send-mouse --session NAME --text "Item B" --scroll-down 3
Keys: C-c C-d C-z C-u C-l (ctrl), Enter Escape Tab Space BSpace, Up Down Left Right, Home End PPage NPage, F1-F12
send-mouse works only in alternate screen mode (active TUI). Prefer --text targets over coordinates when possible.
Capturing Output
# Visible screen only (default: physical rows, trimmed, no ANSI)
# Prefer this — it's almost always enough
term-cli capture --session NAME
# Last N physical rows from the bottom of the visible screen
term-cli capture --session NAME --tail 10
# Last N logical lines from scrollback+visible history (joins wrapped lines)
# Use only when you need output that scrolled off-screen
# In alternate screen (TUIs / nested tmux), this is blocked by default
term-cli capture --session NAME --scrollback 20
# Override alternate-screen protection (may return stale/misleading history)
term-cli capture --session NAME --scrollback 20 --force
# Include ANSI escape codes (colors) — works with any mode
term-cli capture --session NAME --scrollback 10 --raw
# Annotated capture — pane metadata + highlight annotations
# Shows screen mode, bell, cursor position, and highlighted TUI elements
# Use this when operating TUIs where you need to identify selected items
term-cli capture --session NAME --annotate
# Default capture auto-enables annotations for active alternate-screen TUIs.
# Force plain output when needed:
term-cli capture --session NAME --no-annotate
# Optional line numbers (1-based) for visible-screen captures (not scrollback)
term-cli capture --session NAME --annotate --line-numbers
When to use --annotate vs --raw vs plain capture:
- Plain
capture— default for shells, REPLs, command output. Fast, clean text. --annotate— for TUIs (menus, file managers, settings panels) where you need to know what's happening. Returns visible content plus anAnnotations:section with screen mode (normal/alternate), bell alerts, cursor position (Cursor: row,col, 1-based), optional mouse mode (Mouse: ...when enabled), and highlighted rows (1-based). Bell alerts are one-shot: shown once then cleared.- Auto behavior — plain
captureauto-enables annotations only when an active alternate-screen TUI is detected; idle shell prompts stay plain. Use--annotate/--no-annotateto override. --raw— when you need exact ANSI escape codes (e.g., parsing color semantics yourself). Higher token cost.
Waiting
Prefer wait — it detects shell prompts ($, %, #), REPL prompts (>>>, (Pdb), >), and is fastest.
term-cli wait --session NAME
Use wait-idle for TUIs (vim, htop, less) that don't have a detectable prompt — waits for screen to settle:
term-cli wait-idle --session NAME # defaults: 2s idle, 10s timeout
Use wait-for sparingly — waits for pattern in screen output:
term-cli wait-for --session NAME "error" "success" --ignore-case --print-match
term-cli wait-for --session NAME "error" --print-match-context 3 # match + 3 lines above/below
⚠️ Warning: wait-for matches the entire screen including your echoed command:
# WRONG — pattern appears in echoed command, triggers immediately - WRONG
term-cli send-text --session NAME "./build.sh && echo DONE" --enter
term-cli wait-for --session NAME "DONE"
# ↑ Problem: The screen now shows "./build.sh && echo DONE" so "DONE" matches instantly
# RIGHT — wait for program output (not your own marker)
term-cli run --session NAME "npm run dev"
term-cli wait-for --session NAME "Listening on port"
# RIGHT — assemble pattern during print so it doesn't appear in command
term-cli send-text --session NAME "./build.sh && printf 'DON' && printf 'E\n'" --enter
term-cli wait-for --session NAME "DONE"
# BEST — use wait or run --wait when possible
term-cli run --session NAME "./build.sh" --wait
Human Assistance
When you need human help (passwords, CAPTCHAs, manual intervention):
term-cli request --session NAME --message "Please enter SSH password"
term-cli request-wait --session NAME # default timeout: 300s (5 min)
term-cli request-status --session NAME
term-cli request-cancel --session NAME
How it works: Your human runs term-assist list to see pending requests, then term-assist attach --session NAME to join. A status bar shows your message. When finished, they press Ctrl+B Enter to complete — they can optionally type a response message for you. request-wait then returns and you continue.
If they detach with Ctrl+B d while a request is still pending, request-wait fails with exit code 4.
File Transfer (over SSH / any shell)
Transfer files in-band through the terminal session — works over SSH, in containers, or any shell. No scp/sftp needed.
# Upload a local file to the remote session
term-cli upload --session NAME local_file.txt remote_file.txt
# Download a remote file to local
term-cli download --session NAME remote_file.txt local_file.txt
# Overwrite existing files (both commands refuse by default)
term-cli upload --session NAME file.txt file.txt --force --timeout 30
# Pipe support: upload from stdin (REMOTE_PATH required)
cat config.json | term-cli upload --session NAME - /remote/config.json
# Pipe support: download to stdout (status message goes to stderr)
term-cli download --session NAME /remote/data.csv - | jq .
Key behaviors:
- Hash verification — SHA-256 integrity check
- Gzip compression — transfers are always gzip-compressed
- Pipe support — use
-, status/verbose output goes to stderr - Requires Python 3 on the remote — transfer deploys a Python helper; fails if
python3is not available - Prompt required — session must be at a clean shell prompt; transfers fail (exit 2) if a command is running, a TUI is active, or there is partial input on the line
- Overwrite protection — refuses without
--force - Fast Uploads
- Optimized Downloads — starts with pipe-pane only in normal screen mode; in alternate screen mode (nested tmux/TUI), it goes directly to chunked mode for reliability.
- Default timeout: 120s — set with
--timeout
Other Commands
term-cli resize --session NAME --cols 120 --rows 40
term-cli scroll --session NAME -50
term-cli pipe-log --session NAME /tmp/out.log
term-cli unpipe --session NAME
Example: Concurrency (Run long tasks while you work)
term-cli start --session tests && term-cli run --session tests 'date +"%H:%M:%S"; pytest -q'
# ... do other work in parallel ...
term-cli wait --session tests --timeout 1800; term-cli capture --session tests
term-cli kill --session tests
Example: Dev Server
term-cli start --session server && term-cli run --session server "npm run dev"
term-cli wait-idle --session server --timeout 15 && term-cli capture --session server
# ... later ...
term-cli send-key --session server C-c && term-cli wait --session server
term-cli kill --session server
Example: Python Debugger (pdb)
term-cli start --session debug && term-cli run --session debug "python3 -m pdb script.py" && term-cli wait --session debug
term-cli send-text -s debug "b 42" --enter && term-cli wait -s debug # breakpoint
term-cli send-text -s debug "c" --enter && term-cli wait -s debug # continue
term-cli send-text -s debug "p some_var" --enter && term-cli wait -s debug && term-cli capture -s debug --tail 5
term-cli send-text -s debug "q" --enter; term-cli wait -s debug # quit (may need 'y' to confirm)
term-cli kill --session debug
Example: SSH with Password (Human Helps)
term-cli start --session remote && term-cli run --session remote "ssh user@host"
term-cli wait --session remote && term-cli capture --session remote
# If password prompt shown, request human help; if shell prompt, key auth succeeded
term-cli request --session remote --message "Please enter SSH password"
term-cli request-wait --session remote && term-cli capture --session remote
Example: File Transfer over SSH
# Assumes passwordless login
term-cli start --session remote && term-cli run --session remote "ssh user@host" --wait
# Upload a config file
term-cli upload --session remote ./nginx.conf /etc/nginx/nginx.conf
# Download a log file
term-cli download --session remote /var/log/app.log ./app.log
Tips
- Defaults are sane. Plain
capture(visible screen),wait(10s timeout), andstart(80x24) work for most cases. Use--tailto focus on just the last few rows. Only add--timeoutor--scrollbackwhen the default isn't enough. - Use long-form flags.
--session,--timeout,--scrollbackover-s,-t,-n. Short forms save almost no tokens and hurt readability. - Chain commands: Fewer tool calls = less overhead, lower token usage, faster wall clock time. Use
&&for dependent operations (send-text --enter && wait),;to always run the next command (wait --timeout 5; capture— see output even on timeout). - Remember: you can run interactive apps through
term-cli(debuggers, TUIs, SSH, installers). - Lost context after memory compaction? Plain
capturefirst (visible screen is usually enough). Use--scrollback 30if relevant output scrolled off in normal screen mode. In alternate screen mode, prefer--tail/--annotate; use--forceonly when you explicitly accept potentially stale history. - Prefer debuggers (
pdb/gdb) viaterm-clifor bug analysis; step + inspect beats print-debugging for many issues. - Think concurrently: start long tasks in one session and keep working; check progress with
status,wait, andcapture. - Use
--helpon any command for details:term-cli run --help - Default timeout is 10s for most wait commands — increase for builds, tests, or slow operations
run --waitalready waits for prompt — don't add anotherwaitafter it- Capture before sending keys to verify screen state
- Unsure if a TUI is running?
term-cli status --session NAMEshowsScreen: alternatewhen a full-screen app (vim, htop, less) is active. Usewait-idlefor alternate screen,waitfor normal. - Navigating TUIs? Use
capture --annotateto see screen mode, bell alerts, cursor position (Cursor: row,col, 1-based), and which items are highlighted. Plaincaptureloses all styling, and--rawcosts many tokens. - Default
request-waittimeout is 5 minutes — usually no need to override - Locked sessions (exit code 5): agent can only
capture,status,wait-*,request*,list,scroll,pipe-log,unpipe. Commandsrun,send-*,resize,kill,upload,downloadare blocked
Exit Codes
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | Runtime error |
| 2 | Invalid input |
| 3 | Timeout |
| 4 | Human detached while request pending |
| 5 | Session locked (agent read-only) |
| 127 | tmux not found |
Feedback
As an agent, you're well-positioned to spot improvements. Found a bug or have an idea? Email the maintainer: Elias Oenal termcli@eliasoenal.com