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 send command 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 start creates isolated terminal sessions with configurable geometry and stabilization delays
  • tmux-cli.sh send transmits input with bracketed paste protection for reliable command delivery
  • tmux-cli.sh capture serializes screen state to labeled YAML files for inspection or assertion
  • tmux-viewer/index.tsx provides 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →