Introduction
shuck makes any interactive CLI non-interactive.
It wraps any program in a pseudo-terminal so the child process believes it’s running in a real terminal, captures all output, strips TUI artifacts (ANSI escape sequences, spinners, progress bars, cursor movement), and returns clean, pipeable UTF-8 text.
Think of it as expect for the AI agent era — zero config, zero scripting.
The Problem
Interactive CLIs are everywhere. Tools like python3, node, mysql, psql, and hundreds of others detect whether they’re running in a terminal and behave differently:
- They render TUI interfaces with colors, spinners, and progress bars
- They refuse to accept piped input
- They emit raw ANSI escape sequences that corrupt your data
- They can’t be easily composed with
|,>,$(), orxargs
This creates a wall between interactive tools and any kind of automation — CI/CD, AI agents, scripts.
The Solution
# Before: interactive CLIs can't be piped
python3 # launches REPL, blocks
mysql -u root # interactive prompt, requires TTY
# After: clean, pipeable output
echo "import sys; print(sys.version)" | shuck python3
shuck mysql -u root -- -e "SHOW DATABASES" > dbs.txt
shuck solves this by:
- Allocating a PTY — the child process sees
isatty(stdout) == true - Forwarding stdin — you can pipe input in, or provide it with
-- - Capturing all output — everything written to the PTY master
- Stripping ANSI — single-pass VTE state machine, zero allocations
- Returning clean text — valid UTF-8, suitable for any downstream tool
Design Principles
Safety First
shuck never silently modifies, truncates, or reinterprets output. It propagates real exit codes from the child process. stdout is sacred — only child output appears there. Diagnostics go to stderr.
Speed by Design
ANSI stripping uses a single-pass state machine (not regex). The bottleneck is always the child process, never shuck. Written in Rust with zero unnecessary allocations.
Composability
Output is valid UTF-8, suitable for |, >, $(), xargs, and everything else in your shell. Deterministic: same input → same output (modulo child process).
Agent Ready
--jsonmode returns{stdout, stderr, exit_code, duration_ms, timed_out}- Exit codes follow conventions agents already know (124=timeout, 127=not-found)
- MCP server available for direct LLM integration
Next Steps
- Installation — get shuck on your machine
- Quick Start — your first shuck command
- CLI Reference — full option documentation