Loading...
Loading...
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.
npx skill4agent add gpu-cli/skills tmux-cli-testsource .claude/skills/tmux-cli-test/scripts/tmux_helpers.sh| Function | Purpose |
|---|---|
| Launch command in detached tmux session |
| Kill session |
| Check if session is running |
| Get pane text |
| Get pane text with ANSI codes |
| Poll until text appears |
| Poll until regex matches |
| Poll until text disappears |
| Poll until process exits |
| Send keys (tmux key names) |
| Type literal text |
| Assert text present |
| Assert text absent |
| Assert regex matches |
| Send then wait |
| Full lifecycle test |
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 rows1. Start session with command
2. Wait for ready signal (text appearing)
3. Interact (send keys, wait for responses)
4. Assert on captured output
5. Kill sessionsource .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"test_help_page() {
local s="$1"
tmux_assert_contains "$s" "run" "help shows run command"
tmux_assert_contains "$s" "dashboard" "help shows dashboard command"
tmux_assert_not_contains "$s" "ERROR" "no errors in help"
}
tmux_test "help-test" "./crates/target/debug/gpu --help" "Usage:" test_help_pagesource .claude/skills/tmux-cli-test/scripts/tmux_helpers.sh
TMUX_TEST_WIDTH=140
TMUX_TEST_HEIGHT=35
GPU_BIN="./crates/target/debug/gpu"
tmux_start "dash" "$GPU_BIN dashboard"
tmux_wait_for "dash" "Pods" 15
# Verify panels rendered
tmux_assert_contains "dash" "Pods" "pods panel visible"
tmux_assert_contains "dash" "Jobs" "jobs panel visible"
# Navigate with keys
tmux_send "dash" j
tmux_send "dash" j
tmux_wait_for "dash" "▶" 5 # selection cursor
# Switch panel
tmux_send "dash" Tab
tmux_wait_for_regex "dash" "Jobs.*selected" 5
# Open help overlay
tmux_send "dash" '?'
tmux_wait_for "dash" "Help" 5
tmux_assert_contains "dash" "Keybindings" "help shows keybindings"
# Close help
tmux_send "dash" Escape
tmux_wait_gone "dash" "Keybindings" 5
# Quit
tmux_send "dash" q
tmux_wait_exit "dash" 5tmux_start "init" "$GPU_BIN init"
tmux_wait_for "init" "project" 10
# Type project name
tmux_type "init" "my-test-project"
tmux_send "init" Enter
# Wait for next prompt
tmux_wait_for "init" "GPU" 10
# Select GPU with arrow keys
tmux_send "init" j j Enter
tmux_wait_for "init" "provider" 10
tmux_kill "init"tmux_start "status" "$GPU_BIN status"
tmux_wait_for_regex "status" "(No active|Pod)" 10
FRAME=$(tmux_capture "status")
if echo "$FRAME" | grep -q "No active"; then
echo "No pods running - expected for cold test"
elif echo "$FRAME" | grep -q "Pod"; then
tmux_assert_matches "status" "Pod.*READY\|ACTIVE" "pod in valid state"
fi
tmux_wait_exit "status" 15tmux_start "bad-cmd" "$GPU_BIN run --nonexistent-flag"
tmux_wait_for_regex "bad-cmd" "error|Error|unknown" 10
tmux_assert_not_contains "bad-cmd" "panic" "no panics on bad input"
tmux_wait_exit "bad-cmd" 5tmux_start "run-job" "$GPU_BIN run python -c 'import time; time.sleep(3600)'"
tmux_wait_for "run-job" "Running" 30
# Interrupt
tmux_send "run-job" C-c
tmux_wait_for_regex "run-job" "cancel|interrupt|stopped" 10
tmux_wait_exit "run-job" 10source .claude/skills/tmux-cli-test/scripts/tmux_docker_helpers.sh
TMUX_DOCKER_CONTAINER="gpu-ftr-alex-chen-001"
# TMUX_DOCKER_SESSION="test" # default, override if neededTMUX_DOCKER_CONTAINERdocker_tmux_*docker exec| Function | Purpose |
|---|---|
| Send tmux keys |
| Type literal text |
| Get pane text |
| Get pane text with ANSI codes |
| Poll until text appears |
| Poll until regex matches |
| Poll until text disappears |
| Assert text present |
| Assert text absent |
| Assert regex matches |
| Send then wait |
source .claude/skills/tmux-cli-test/scripts/tmux_docker_helpers.sh
TMUX_DOCKER_CONTAINER="gpu-ftr-alex-chen-001"
docker_tmux_send "gpu dashboard" Enter
docker_tmux_wait_for "Pods" 15
docker_tmux_capture
docker_tmux_send j
docker_tmux_send "?"
docker_tmux_wait_for "Help" 5
docker_tmux_assert_contains "Keybindings" "help shows keybindings"
docker_tmux_send Escape
docker_tmux_wait_gone "Help" 5
docker_tmux_send q# Start
tmux new-session -d -s test -x 120 -y 30 "./crates/target/debug/gpu dashboard"
# Poll for ready (inline wait loop)
for i in $(seq 1 100); do
tmux capture-pane -t test -p | grep -q "Pods" && break
sleep 0.3
done
# Assert
FRAME=$(tmux capture-pane -t test -p)
echo "$FRAME" | grep -q "Pods" && echo "PASS" || echo "FAIL"
# Cleanup
tmux send-keys -t test q
tmux kill-session -t test 2>/dev/null| Bad | Good | Why |
|---|---|---|
| | Sleeps are flaky and slow |
| | Condition, not duration |
| Hardcoded binary path | | Easy to switch debug/release |
| No cleanup on failure | | Leftover sessions break next run |
| | Text may not be rendered yet |
Checking | Check display content only | Unicode width != byte length |
# Capture what's actually on screen
tmux_capture "session-name"
# Save to file for comparison
tmux_capture_to_file "session-name" "/tmp/failed-frame.txt"
# Capture with colors to verify styling
tmux_capture_ansi "session-name"gpu-test-dashboard
gpu-test-init
gpu-test-run-basic
gpu-test-status
gpu-test-error-handling