Back to skills
extension
Category: Development & EngineeringNo API key required

tmux-cli-test

Test CLI applications interactively using tmux sessions. Use when testing TUI apps (ratatui dashboard, interactive prompts), verifying CLI command output, testing keyboard navigation, or validating terminal rendering. Launches commands in tmux, waits on conditions (never sleeps), captures frames, sends keypresses, and asserts on output. Specifically designed for gpu-cli but works with any CLI. Use this skill when asked to test, verify, or QA any terminal-based UI or CLI command flow.

personAuthor: jakexiaohubgithub

tmux CLI Testing

Test a CLI or TUI by running it in a tmux session: wait for a condition, send input, assert on the captured frame, kill the session.

Never sleep — always wait on a condition. A sleep is a guess about timing; it makes tests both slower and flakier than polling for the thing you actually need.

Helpers

source .claude/skills/tmux-cli-test/scripts/tmux_helpers.sh

| Function | Purpose | |----------|---------| | tmux_start <session> <cmd> | Launch command in a detached tmux session | | tmux_kill <session> | Kill session | | tmux_is_alive <session> | Check if session is running | | tmux_capture <session> | Get pane text | | tmux_capture_ansi <session> | Get pane text with ANSI codes | | tmux_capture_to_file <session> <path> | Save pane text to a file | | tmux_wait_for <session> <text> [timeout] | Poll until text appears | | tmux_wait_for_regex <session> <pattern> [timeout] | Poll until regex matches | | tmux_wait_gone <session> <text> [timeout] | Poll until text disappears | | tmux_wait_exit <session> [timeout] | Poll until the process exits | | tmux_send <session> <keys...> | Send keys (tmux key names) | | tmux_type <session> <text> | Type literal text | | tmux_assert_contains <session> <text> [label] | Assert text present | | tmux_assert_not_contains <session> <text> [label] | Assert text absent | | tmux_assert_matches <session> <pattern> [label] | Assert regex matches | | tmux_send_and_wait <session> <keys> <text> [timeout] | Send then wait | | tmux_test <session> <cmd> <ready_text> <fn> | Full lifecycle test |

Screenshot helpers (tmux_screenshot, tmux_screenshot_sizes) and color assertions (tmux_assert_not_monochrome, tmux_assert_min_colors, tmux_assert_has_color, tmux_assert_text_color) are documented in references/color-capture.md, together with the tmux true-color override that RGB-painting TUIs require.

Override the defaults before calling anything:

TMUX_TEST_POLL_INTERVAL=0.3  # seconds between polls
TMUX_TEST_TIMEOUT=30         # max wait seconds
TMUX_TEST_WIDTH=120          # terminal columns
TMUX_TEST_HEIGHT=30          # terminal rows

Workflow

Every test is the same five steps: start the session, wait for a ready signal, interact, assert on the captured output, kill the session.

source .claude/skills/tmux-cli-test/scripts/tmux_helpers.sh

tmux_start "test-help" "./crates/target/debug/gpu --help"
tmux_wait_for "test-help" "Usage:" 10
tmux_assert_contains "test-help" "run"
tmux_assert_contains "test-help" "dashboard"
tmux_kill "test-help"

Prefer tmux_test when a test has more than a couple of assertions — it kills the session even when one fails.

More Patterns

| Need | Read | |---|---| | Dashboards, interactive prompts, error paths, C-c interrupts, driving tmux without the helpers, debugging a failed test, session naming | references/examples.md | | Running the CLI inside a container | references/docker.md | | True color, screenshots, color assertions | references/color-capture.md |

Anti-Patterns

| Bad | Good | Why | |-----|------|-----| | sleep 3 | tmux_wait_for s "Ready" | Sleeps are flaky and slow | | sleep 5 && tmux capture-pane | tmux_wait_for s "expected" && tmux_capture s | Wait on a condition, not a duration | | Hardcoded binary path | GPU_BIN=./crates/target/debug/gpu | Easy to switch debug/release | | No cleanup on failure | tmux_test or an explicit tmux_kill | Leftover sessions break the next run | | grep -q with no timeout loop | tmux_wait_for | The text may not be rendered yet | | Checking .len() of TUI text | Check displayed content only | Unicode width is not byte length |