Skip to content
SuperKevinZhouPublic

About

Programmable terminal sessions for AI/LLM tool-use and test automation — spawn named tabs, type, exec, mouse, snapshot, and fetch viewports as text.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Latest commit

 

History

26 Commits

Folders and files

Repository files navigation

tuiless

tuiless is an experimental CLI for driving stateful terminal sessions from stateless commands.

The core idea is:

  • every CLI invocation is short-lived;
  • a per-workspace background runtime owns the actual shell tabs;
  • commands such as exec, press, type, resize, snapshot, and fetch talk to that runtime over local IPC;
  • snapshot returns the current visible terminal viewport as plain text.

This is intentionally similar in spirit to browser automation CLIs, but the target is a terminal tab rather than a DOM page. In v0, the only supported resource is a named shell tab.

Status

The Windows-first v0 runtime is complete and hardened for repeatable CLI automation. The repository ships unit coverage plus cross-process smoke coverage for runtime startup, PTY input, snapshots, fetch, resize, and workspace isolation.

Working core path:

  • auto-start a background runtime for the current working directory;
  • create or reuse a named shell tab;
  • execute shell input with exec;
  • capture the current viewport with snapshot;
  • fetch the full terminal text history with fetch;
  • resize a tab;
  • list open tabs;
  • install the bundled tuiless Codex skill with skill --path.

Platform and validation boundaries:

  • Windows is the only currently implemented IPC target.
  • attach now restores ANSI colors and uses an event-priority refresh loop, but still needs broader real-TUI validation.
  • mouse commands inject terminal mouse escape sequences, but target applications must enable mouse reporting.
  • Unix domain sockets and non-Windows PTY validation are not implemented;
  • attach still needs a real interactive TUI pass in CI or a human terminal.

Install / Build

cargo build

The development binary is:

.\target\debug\tuiless.exe

Quick Start

Open a named tab with an explicit simulated terminal size:

.\target\debug\tuiless.exe open tab_1 --cols 100 --rows 30

Run a shell command inside that tab:

.\target\debug\tuiless.exe exec tab_1 "echo hello"

Snapshot the current visible viewport:

.\target\debug\tuiless.exe snapshot tab_1

Fetch the full text content accumulated in the tab:

.\target\debug\tuiless.exe fetch tab_1

List tabs in the current workspace runtime:

.\target\debug\tuiless.exe list

Resize the simulated terminal:

.\target\debug\tuiless.exe resize tab_1 --cols 120 --rows 40

Clean up the runtime:

.\target\debug\tuiless.exe close --all

Install the bundled tuiless skill to a local skills directory:

.\target\debug\tuiless.exe skill --path "$env:USERPROFILE\.codex\skills"

Runtime Model

tuiless computes a session key from the canonical current working directory. The first CLI command for that workspace starts a background runtime. Later CLI commands connect to the same runtime and operate on the same tab state.

Runtime state is scoped by workspace session key, while registry metadata lives outside the repo by default:

  • registry files live under %LOCALAPPDATA%\tuiless\registry on Windows;
  • set TUILESS_REGISTRY_DIR to override the registry location;
  • tab names are scoped to the current workspace;
  • tab_1 in one workspace is independent from tab_1 in another workspace.

Runtime startup is race-tolerant: registry writes use a temporary-file publish path, malformed or stale registry files are discarded, named-pipe ownership prevents a second server from taking over, and background servers are detached from the invoking command's Windows process handles. IPC frames are limited to 16 MiB. Snapshot stability waits for a quiet period but always returns within five seconds even when a program continuously emits output.

Command Reference

open

tuiless open <tab> [--cols <n>] [--rows <n>]

Ensures a tab exists. If the tab is new, it is created with the requested terminal size, or the default size if no size is provided.

snapshot

tuiless snapshot <tab> [--wait-stable <ms>]

Returns the current visible viewport as plain text. This is not a full scrollback dump.

The default stable wait is currently 150ms.

fetch

tuiless fetch <tab> [--wait-stable <ms>]

Returns the full plain-text contents currently retained for the tab, including scrollback history rather than only the visible viewport. For fullscreen/alternate-screen TUIs, fetch also keeps a bounded history of distinct viewport frames so state remains inspectable after the TUI exits.

This command does not apply any truncation of its own.

The default stable wait is currently 150ms.

exec

tuiless exec <tab> <line>

Types <line> into the tab and presses Enter.

This is a shell-friendly convenience command. It does not mean "spawn a managed subprocess and wait for completion".

type

tuiless type <tab> <text>

Injects printable text into the tab without pressing Enter.

press

tuiless press <tab> <key> [--ctrl] [--alt] [--shift] [--meta]

Sends a key event. Both chord syntax and explicit modifier flags are supported.

Examples:

tuiless press tab_1 Enter
tuiless press tab_1 Ctrl+A
tuiless press tab_1 A --ctrl
tuiless press tab_1 Esc
tuiless press tab_1 Up

Mouse Commands

Mouse commands use terminal cell coordinates. The origin is the top-left cell, with zero-based x and y.

tuiless click <tab> --x <col> --y <row> [--button left]
tuiless drag <tab> --from-x <c1> --from-y <r1> --to-x <c2> --to-y <r2> [--button left]
tuiless wheel <tab> --delta-y <n> [--x <col> --y <row>]
tuiless mouse-down <tab> --x <col> --y <row> [--button left]
tuiless mouse-up <tab> --x <col> --y <row> [--button left]
tuiless mouse-move <tab> --x <col> --y <row>

These commands guarantee event injection only. Whether the target application reacts depends on terminal mouse reporting support. For negative wheel values, both --delta-y -3 and --delta-y=-3 are accepted.

resize

tuiless resize <tab> --cols <n> --rows <n>

Updates the tab's simulated terminal size.

attach

tuiless attach <tab> [--wait-stable <ms>]

Starts a minimal interactive terminal view for a tab. Detach with Ctrl+].

On start, attach syncs the tab to the current terminal size, fetches an ANSI-formatted frame, enters an alternate-screen raw terminal view, enables mouse capture, and forwards keyboard, mouse, and resize events to the tab. attach requires an interactive TTY for both stdin and stdout; in non-interactive environments it now fails fast without mutating tab state. --wait-stable applies to the initial frame only (default 1ms), so live input stays responsive.

Runtime updates use ANSI snapshots and skip unchanged frames to reduce flicker, while keeping snapshot/fetch as plain-text outputs for automation.

list

tuiless list

Lists known tabs for the current workspace runtime.

close

tuiless close <tab>
tuiless close --all

Closes one tab or shuts down the current workspace runtime.

skill

tuiless skill --path <path-to-skills-directory>

Copies the bundled skills/tuiless package into <path-to-skills-directory>\tuiless. This command is local-only and does not start or use the tab runtime.

Internal Runtime Command

serve is an internal background entrypoint used by regular commands to bootstrap the runtime. It is intentionally hidden from top-level CLI help.

Design Notes

The public model is deliberately simple:

  • a tab is a shell-backed PTY session;
  • terminal size is part of tab state;
  • snapshots are viewport-oriented;
  • fetch returns the retained full text history for a tab;
  • raw input events and convenience actions both exist;
  • higher-level locator APIs such as click text=Submit are intentionally out of scope for v0.

Internally, the current implementation uses:

  • clap for CLI parsing;
  • tokio named pipes for Windows IPC with length-prefixed byte-stream framing;
  • portable-pty for PTY management;
  • vt100 for terminal screen parsing;
  • crossterm for the minimal attach surface.

Development Notes

When validating runtime behavior, run dependent commands serially. Do not run open, exec, snapshot, and process cleanup in parallel, because they target the same background runtime and can create misleading races.

Useful smoke sequence:

Get-Process tuiless -ErrorAction SilentlyContinue | Stop-Process -Force -ErrorAction SilentlyContinue
Remove-Item .tuiless -Recurse -Force -ErrorAction SilentlyContinue
cargo build
.\target\debug\tuiless.exe open smoke --cols 100 --rows 30
.\target\debug\tuiless.exe exec smoke "echo smoke-ok"
Start-Sleep -Milliseconds 800
.\target\debug\tuiless.exe snapshot smoke

Run tests:

cargo test

The integration suite launches real background runtimes, so its commands must remain serial. Run it directly when changing IPC or PTY behavior:

cargo test --test runtime_smoke -- --test-threads=1
cargo clippy --all-targets --all-features -- -D warnings
cargo build --release

For a stale runtime, run tuiless list once from the affected workspace. If the registry points to a dead process it is removed and a fresh runtime is started. close --all is the deterministic cleanup operation used by the smoke tests.

About

Programmable terminal sessions for AI/LLM tool-use and test automation — spawn named tabs, type, exec, mouse, snapshot, and fetch viewports as text.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages