How to Set Up tmux‑Based CLI Testing for Terminal Scenarios
Use Freebuff's scripts/tmux/ harness to run CLI commands in isolated tmux sessions, capture screen output as YAML, and inspect results with a React viewer—all without flakiness from direct stdin/stdout piping.
The Freebuff project provides a production‑ready tmux testing framework for validating terminal applications in realistic conditions. Unlike conventional subprocess testing that struggles with TTY detection, color codes, and timing issues, Freebuff's scripts/tmux/tmux-cli.sh wrapper creates genuine terminal sessions with full control over dimensions, input handling, and session recording.
Core Architecture of the tmux Testing Harness
Freebuff's testing infrastructure resides in scripts/tmux/ and addresses three critical challenges in CLI automation:
- Bracketed paste mode – The
sendcommand wraps input to prevent character dropping and ensure atomic delivery - Deterministic session lifecycle – Created, captured, and destroyed through a single wrapper interface
- Inspectable artifacts – Every screen state is serialized to YAML in
debug/tmux-sessions/for programmatic or visual verification
The implementation delegates to standard tmux commands but abstracts away the orchestration complexity. Session logs use a structured YAML format that preserves cursor position, cell contents, and metadata—enabling diff‑based assertions or manual review.
Running Your First CLI Test Session
The canonical workflow involves five shell commands. These examples assume execution from the repository root with the tmux-cli.sh wrapper.
1. Start a Detached Session
SESSION=$(./scripts/tmux/tmux-cli.sh start \
--name cli-check -w 160 -h 40 --wait 6)
Parameters control terminal geometry (-w, -h) and initial stabilization time (--wait). The command returns a session identifier stored in $SESSION for subsequent operations.
2. Capture Baseline Screen State
./scripts/tmux/tmux-cli.sh capture "$SESSION" --label initial
This creates a timestamped YAML snapshot under debug/tmux-sessions/$SESSION/ tagged with the provided label.
3. Send Input with Bracketed Paste Protection
./scripts/tmux/tmux-cli.sh send "$SESSION" \
"Search for getAgentBaseName and report what you find" --wait-idle 4
The send subcommand automatically enables bracketed paste mode, ensuring the entire string arrives as one unit. The --wait-idle flag pauses execution until the terminal reports no further output activity.
4. Capture Post‑Action State
./scripts/tmux/tmux-cli.sh capture "$SESSION" --label after-search --wait 2
Multiple captures with descriptive labels create a breadcrumb trail through complex multi‑step scenarios.
5. Terminate the Session
./scripts/tmux/tmux-cli.sh stop "$SESSION"
Sessions left running consume tmux server resources; always include explicit cleanup in test scripts or trap handlers.
Testing Custom Commands and Scripts
The tmux harness supports arbitrary executables, not just the Freebuff CLI. Launch a TypeScript harness or any shell pipeline:
SESSION=$(./scripts/tmux/tmux-cli.sh start \
--name render-check \
-w 160 -h 20 \
--wait 1 \
--command "bun .context/my-render-check.tsx")
./scripts/tmux/tmux-cli.sh capture "$SESSION" --label rendered
./scripts/tmux/tmux-cli.sh stop "$SESSION"
The --command argument overrides the default CLI invocation, making the framework reusable across projects.
Inspecting Results with the Viewer UI
Captured sessions are human‑readable YAML files:
cat debug/tmux-sessions/$SESSION/*.yaml
For visual inspection across multiple captures, Freebuff includes a React viewer in scripts/tmux/tmux-viewer/index.tsx:
bun scripts/tmux/tmux-viewer/index.tsx
The viewer renders terminal state with preserved colors and layout, supports navigation between capture labels, and highlights cursor positions. This is particularly valuable for debugging rendering bugs or verifying ANSI sequence handling.
Key Implementation Files
Reference these paths when extending or debugging the tmux testing setup:
| Path | Purpose |
|---|---|
scripts/tmux/tmux-cli.sh |
Core wrapper implementing start, send, capture, and stop operations |
scripts/tmux/README.md |
Maintainer documentation for the harness internals |
docs/testing.md (lines 79‑101) |
Usage patterns and capture layout specification |
scripts/tmux/tmux-viewer/index.tsx |
React application for browsing YAML session logs |
debug/tmux-sessions/ |
Runtime output directory for session artifacts (git‑ignored) |
According to the Freebuff source code, the testing guide in docs/testing.md documents additional edge cases including multi‑byte character handling and resize event capture.
Summary
tmux-cli.sh startcreates isolated terminal sessions with configurable geometry and stabilization delaystmux-cli.sh sendtransmits input with bracketed paste protection for reliable command deliverytmux-cli.sh captureserializes screen state to labeled YAML files for inspection or assertiontmux-viewer/index.tsxprovides a React interface for visual debugging of captured sessions- All artifacts route through
debug/tmux-sessions/with structured metadata enabling CI integration
Frequently Asked Questions
What problem does tmux-based testing solve that subprocess testing cannot?
Subprocess testing fails when applications probe for TTY availability, rely on terminal dimensions for layout, or use interactive features like password prompts. Freebuff's tmux harness provides a genuine PTY environment while remaining scriptable—eliminating the flakiness of stubbing isatty() or wrestling with pexpect timing heuristics.
Can I assert against captured screen content programmatically?
Yes. The YAML files in debug/tmux-sessions/$SESSION/ contain structured cell data including text content, foreground/background colors, and cursor coordinates. Test frameworks can parse these directly or generate image snapshots for pixel‑perfect visual regression testing.
How do I customize terminal dimensions for responsive layout testing?
Pass -w (width in columns) and -h (height in rows) to the start subcommand. The example scripts/tmux/tmux-cli.sh start --name mobile -w 80 -h 24 simulates a narrow viewport without modifying application code.
Where does session output persist after tmux-cli.sh stop?
Capture files written before stopping remain in debug/tmux-sessions/$SESSION/ indefinitely. The directory name matches the session identifier returned by start, allowing correlation between test logs and CI artifacts. Clean these directories aggressively in CI environments to prevent disk exhaustion.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →