v0.1.0 — now available on crates.io, PyPI & npm

Playwright for terminals

Control any terminal emulator programmatically. Send keystrokes, read the screen, take screenshots, and run commands — all over WebSocket JSON-RPC. Built for AI coding agents.

Get started View on GitHub
your script / agent
 
target terminal
Up and running in seconds
One command. No GUI needed. Works with any terminal you already use.

One-liner (Linux / macOS)

curl -fsSL https://raw.githubusercontent.com/moejay/wrightty/main/install.sh | sh

Cargo — CLI + Rust SDK

cargo install wrightty

Python — SDK, CLI & MCP server

pip install wrightty[cli,mcp]

Node.js — SDK

npm install @moejay/wrightty
Everything you need for terminal automation

Run commands

Execute shell commands and capture output. Wait for prompts, handle timeouts, parse results.

Read the screen

Read terminal text including TUI apps, colors, and cursor position. Full cell-level access.

Send keystrokes

Type text, send Ctrl+C, navigate vim, interact with any TUI. Supports all modifiers and special keys.

Screenshots

Capture the terminal as SVG, PNG, or structured JSON. Perfect for visual testing and documentation.

Wait for text

Block until a pattern appears on screen. Supports regex, timeouts, and polling intervals.

Record sessions

Record as asciicast (asciinema-compatible), replayable scripts (Python/JSON/CLI), or video.

Your language, your way
Python, Node.js, Rust, or raw WebSocket — wrightty speaks them all.
from wrightty import Terminal

term = Terminal.connect()  # auto-discovers running server

output = term.run("cargo test")
print(output)

term.wait_for("$")
term.send_keys("Ctrl+c")

svg = term.screenshot("svg")

# Record a session (asciinema-compatible)
rec = term.start_session_recording()
term.run("make build")
result = term.stop_session_recording(rec)

term.close()
import { Terminal } from "@moejay/wrightty";

const term = await Terminal.connect();

const output = await term.run("cargo test");
console.log(output);

await term.waitFor("$");
await term.sendKeys("Ctrl+c");

const svg = await term.screenshot("svg");

// Record a session
const rec = await term.startSessionRecording();
await term.run("make build");
const result = await term.stopSessionRecording(rec);

term.close();
# Start a headless server
$ wrightty term --headless

# Run commands
$ wrightty run "ls -la"
$ wrightty run "cargo test" --timeout 120

# Read screen
$ wrightty read

# Send input
$ wrightty send-keys Ctrl+c
$ wrightty send-keys Escape : w q Enter

# Screenshots
$ wrightty screenshot --format svg -o terminal.svg

# Wait for output
$ wrightty wait-for "BUILD SUCCESS"
// Connect with any WebSocket client
$ wscat -c ws://127.0.0.1:9420

// Read the screen
 {"jsonrpc":"2.0","id":1,"method":"Screen.getText",
   "params":{"sessionId":"0"}}
 {"result":{"text":"$ ls\nCargo.toml  src  tests\n$"}}

// Send keystrokes
 {"jsonrpc":"2.0","id":2,"method":"Input.sendKeys",
   "params":{"sessionId":"0","keys":["Ctrl+c"]}}
 {"result":{}}
Works with what you already use
One unified CLI. Pick your terminal — or go headless.

Headless built-in

Virtual PTY with no window. Uses Alacritty's terminal engine. Perfect for CI, testing, and headless automation.

wrightty term --headless

Alacritty native

Fork with wrightty built in. Zero overhead — reads directly from Alacritty's terminal state.

wrightty term --alacritty

tmux bridge

Works with any terminal running inside tmux. The universal escape hatch.

wrightty term --bridge-tmux

WezTerm bridge

Translates wrightty calls into wezterm cli commands. Full session support.

wrightty term --bridge-wezterm

Kitty bridge

Uses Kitty's remote control protocol. Requires allow_remote_control yes.

wrightty term --bridge-kitty

Ghostty bridge

Connects via Ghostty's Unix IPC socket. Also has a native fork available.

wrightty term --bridge-ghostty

Zellij bridge

Uses Zellij's CLI actions from within a running session.

wrightty term --bridge-zellij

Any terminal + tmux

foot, GNOME Terminal, Rio, or anything else — just run it inside tmux and use the tmux bridge.

tmux && wrightty term --bridge-tmux
7 domains. 28 methods. WebSocket JSON-RPC 2.0.
DomainMethodsPurpose
WrighttygetInfoCapability negotiation
Sessioncreate destroy list getInfoLifecycle management
InputsendKeys sendText sendMouseKeystrokes & text
ScreengetText getContents screenshot waitForText ...Read & capture
Terminalresize getSize setColorPalette ...Configuration
RecordingstartSession startActions captureScreen startVideo ...Session recording
Eventssubscribe unsubscribeScreen updates, bell, title

Read the full protocol spec →

Optional password protection
Name your server instances and protect them with a password. Auth is optional — skip it for local dev.
Server
# Start with a name and password
$ wrightty term --headless --name my-server --password secret123

# Discover shows name and auth status
$ wrightty discover
  ws://127.0.0.1:9420  wrightty-server v0.1.2 (my-server) [password]
Clients
# Python
term = Terminal.connect(password="secret123")

# Node.js
const term = await Terminal.connect({ password: "secret123" })

# CLI
wrightty --password secret123 run "ls -la"
wrightty-py --password secret123 read
wrightty-js --password secret123 read
Give Claude terminal superpowers
Wrightty ships an MCP server. Add it to Claude, Cursor, or any MCP-compatible agent.
claude_desktop_config.json
{
  "mcpServers": {
    "wrightty": {
      "command": "python3",
      "args": ["-m", "wrightty.mcp_server"],
      "env": { "WRIGHTTY_SOCKET": "ws://127.0.0.1:9420" }
    }
  }
}