Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Validates Terminal User Interface (TUI) output using freeze for screenshot capture and LLM-as-judge for semantic validation. Supports both visual (PNG/SVG) and text-based validation modes.
.claude/skills/mikeyobrien-tui-validate/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-12 | ✗→✓ | ▲ Improved | 119% | 0% |
| case-04 | ✗→✓ | ▲ Improved | 93% | 0% |
| case-05 | ✗→✓ | ▲ Improved | 163% | 0% |
| case-06 | ✗→✓ | ▲ Improved | 143% | 0% |
| case-07 | ✗→✓ | ▲ Improved | 95% | 0% |
This skill validates Terminal User Interface (TUI) applications by capturing their output and using LLM-as-judge for semantic validation. It leverages freeze from Charmbracelet for high-fidelity terminal screenshots and provides structured validation criteria.
Philosophy: Rather than brittle string matching, this skill uses semantic understanding to validate that TUI output "looks right" - checking layout, content presence, and visual hierarchy without breaking on minor formatting changes.
Required:
freeze CLI tool installed (brew install charmbracelet/tap/freeze)tmux for interactive TUI capture (optional, for live applications)Verification:
bash# Check freeze is installed freeze --version # Check tmux is installed (for interactive capture) tmux -V
file:<path> - ANSI output file to validatecommand:<cmd> - Command to execute and capturetmux:<session> - Live tmux session to capturebuffer:<text> - Raw text/ANSI to validatesvg - Vector format, best for documentationpng - Raster format, best for visual difftext - Text-only extraction, fastest{target_name}.{format} in current directorysemantic - LLM judges based on meaning and layoutstrict - Also checks exact content presencevisual - Requires PNG, checks visual appearanceralph-headerValidates Ralph TUI header component:
[iter N] or [iter N/M] formatMM:SS format▶ auto or ⏸ paused)[SCROLL]idle: Nsralph-footerValidates Ralph TUI footer component:
◉ active, ◯ idle, or ■ done)ralph-fullValidates complete Ralph TUI layout:
tui-basicGeneric TUI validation:
Capture TUI output based on target type:
For file targets:
bashfreeze {file_path} -o /tmp/tui-capture.{format}
For command targets:
bashfreeze --execute "{command}" -o /tmp/tui-capture.{format}
For tmux targets:
bashtmux capture-pane -pet {session} | freeze -o /tmp/tui-capture.{format}
For buffer targets:
bashecho "{buffer}" | freeze -o /tmp/tui-capture.{format}
Constraints:
--theme base16 for consistent rendering--width and --heightExtract content for LLM analysis:
For text/semantic validation:
text, use the captured text directlysvg or png, also capture text version for content analysisFor visual validation:
Constraints:
visualApply LLM-as-judge with the appropriate criteria:
Semantic Validation Prompt Template:
Analyze this terminal UI output and determine if it meets the following criteria:
CRITERIA:
{criteria_description}
TERMINAL OUTPUT:
{captured_text}
Evaluate each criterion and provide:
1. PASS or FAIL for each requirement
2. Brief explanation for any failures
3. Overall verdict: PASS or FAIL
Be lenient on exact formatting but strict on:
- Required content presence
- Logical layout and hierarchy
- No rendering errors or artifactsVisual Validation Prompt Template (with image):
Examine this terminal screenshot and validate:
CRITERIA:
{criteria_description}
Check for:
1. Visual hierarchy and layout
2. Color coding correctness
3. No rendering artifacts or broken characters
4. Proper alignment and spacing
Verdict: PASS or FAIL with explanationConstraints:
Report validation results:
On PASS:
✅ TUI Validation PASSED
Criteria: {criteria_name}
Target: {target}
Mode: {judge_mode}
All requirements satisfied.
{optional_notes}On FAIL:
❌ TUI Validation FAILED
Criteria: {criteria_name}
Target: {target}
Mode: {judge_mode}
Issues found:
- {issue_1}
- {issue_2}
Screenshot saved: {path_if_saved}Constraints:
Input:
/tui-validate file:test_output.txt criteria:ralph-headerProcess:
test_output.txt containing ANSI outputfreeze test_output.txt -o /tmp/capture.svgralph-header criteria via LLM judgeInput:
/tui-validate tmux:ralph-session criteria:ralph-full save_screenshot:trueProcess:
tmux capture-pane -pet ralph-session | freeze -o ralph-session.svgtmux capture-pane -pet ralph-session > /tmp/text.txtralph-full criteria checking header, content, and footerralph-session.svgInput:
/tui-validate command:"cargo run --example tui_demo" criteria:"Shows a bordered box with 'Hello World' text centered inside" output_format:png judge_mode:visualProcess:
freeze --execute "cargo run --example tui_demo" -o /tmp/capture.pngInput:
/tui-validate buffer:"[iter 3/10] 04:32 | 🔨 Builder | ▶ auto" criteria:ralph-header output_format:textProcess:
yamlname: ralph-header description: Ralph TUI header component validation requirements: - name: iteration_counter description: Shows iteration in [iter N] or [iter N/M] format required: true pattern: '\[iter \d+(/\d+)?\]' - name: elapsed_time description: Shows elapsed time in MM:SS format required: true pattern: '\d{2}:\d{2}' - name: hat_indicator description: Shows current hat with emoji prefix required: true examples: ["🔨 Builder", "📋 Planner", "🎯 Executor"] - name: mode_indicator description: Shows loop mode status required: true values: ["▶ auto", "⏸ paused"] - name: scroll_indicator description: Shows [SCROLL] when in scroll mode required: false pattern: '\[SCROLL\]' - name: idle_countdown description: Shows idle timeout when present required: false pattern: 'idle: \d+s'
yamlname: ralph-footer description: Ralph TUI footer component validation requirements: - name: activity_indicator description: Shows current activity state required: true values: ["◉ active", "◯ idle", "■ done"] - name: event_topic description: Shows last event topic required: false examples: ["task.start", "build.done", "loop.terminate"] - name: search_display description: Shows search query and match count when searching required: false pattern: 'Search: .+ \d+/\d+'
yamlname: ralph-full description: Complete Ralph TUI layout validation requirements: - name: header_section description: Header at top with iteration, time, hat, and mode required: true references: ralph-header - name: content_section description: Main terminal content area required: true checks: - Has visible content or is ready for content - Properly bounded between header and footer - name: footer_section description: Footer at bottom with activity status required: true references: ralph-footer - name: visual_hierarchy description: Clear visual separation between sections required: true checks: - Borders or spacing between sections - Consistent width across sections
bash# macOS brew install charmbracelet/tap/freeze # Linux (via Go) go install github.com/charmbracelet/freeze@latest # Verify installation freeze --version
tmux list-sessionstmux list-panes -t {session}tmux capture-pane -pet {session}:{pane}--theme flag for consistent colorsstrict mode for exact matching requirementssemantic mode for layout/presence checkingThis skill can be integrated into test suites:
rust// In tests/tui_validation.rs #[test] #[ignore] // Run with: cargo test -- --ignored fn validate_header_rendering() { // 1. Render header to buffer let output = render_header_to_string(&test_state); // 2. Save to temp file std::fs::write("/tmp/header_test.txt", &output).unwrap(); // 3. Run tui-validate skill (via CLI or programmatic) // /tui-validate file:/tmp/header_test.txt criteria:ralph-header // 4. Assert validation passed }
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-12 | fail→pass | 10,015 | 5,234 | -48% | 1 | 1 | 0% | 1,842 | 4,033 | +119% | 0 | 0 | — |
case-01 | fail→fail | 10,414 | 6,976 | -33% | 1 | 1 | 0% | 1,740 | 3,224 | +85% | 0 | 0 | — |
case-02 | fail→fail | 14,458 | 4,734 | -67% | 1 | 1 | 0% | 2,691 | 3,255 | +21% | 0 | 0 | — |
case-03 | fail→fail | 5,789 | 5,779 | -0% | 1 | 1 | 0% | 1,007 | 3,302 | +228% | 0 | 0 | — |
case-04 | fail→pass | 10,274 | 3,662 | -64% | 1 | 1 | 0% | 1,980 | 3,813 | +93% | 0 | 0 | — |
case-05 | fail→pass | 25,174 | 2,787 | -89% | 1 | 1 | 0% | 1,349 | 3,545 | +163% | 0 | 0 | — |
case-06 | fail→pass | 8,123 | 2,324 | -71% | 1 | 1 | 0% | 1,428 | 3,474 | +143% | 0 | 0 | — |
case-07 | fail→pass | 9,275 | 1,432 | -85% | 1 | 1 | 0% | 1,645 | 3,213 | +95% | 0 | 0 | — |
case-08 | fail→pass | 7,641 | 2,665 | -65% | 1 | 1 | 0% | 1,302 | 3,503 | +169% | 0 | 0 | — |
case-09 | fail→pass | 9,231 | 4,438 | -52% | 1 | 1 | 0% | 1,493 | 3,683 | +147% | 0 | 0 | — |
case-10 | pass→pass | 9,957 | 4,613 | -54% | 1 | 1 | 0% | 1,663 | 3,784 | +128% | 0 | 0 | — |
case-11 | fail→pass | 9,737 | 1,576 | -84% | 1 | 1 | 0% | 1,573 | 3,200 | +103% | 0 | 0 | — |
case-13 | pass→pass | 11,731 | 2,562 | -78% | 1 | 1 | 0% | 1,937 | 3,400 | +76% | 0 | 0 | — |
case-14 | fail→pass | 14,367 | 10,673 | -26% | 1 | 1 | 0% | 2,903 | 5,264 | +81% | 0 | 0 | — |
case-15 | pass→pass | 9,790 | 4,410 | -55% | 1 | 1 | 0% | 1,775 | 3,681 | +107% | 0 | 0 | — |
case-16 | pass→pass | 10,668 | 2,982 | -72% | 1 | 1 | 0% | 1,842 | 3,543 | +92% | 0 | 0 | — |
case-17 | pass→pass | 13,505 | 5,641 | -58% | 1 | 1 | 0% | 2,277 | 4,004 | +76% | 0 | 0 | — |
case-18 | fail→pass | 15,008 | 3,685 | -75% | 1 | 1 | 0% | 2,544 | 3,666 | +44% | 0 | 0 | — |
case-19 | fail→pass | 11,054 | 1,651 | -85% | 1 | 1 | 0% | 2,043 | 3,249 | +59% | 0 | 0 | — |
case-20 | pass→pass | 6,085 | 8,545 | +40% | 1 | 1 | 0% | 1,310 | 4,869 | +272% | 0 | 0 | — |
case-21 | pass→pass | 13,308 | 10,078 | -24% | 1 | 1 | 0% | 2,537 | 4,875 | +92% | 0 | 0 | — |
case-22 | pass→pass | 5,728 | 3,701 | -35% | 1 | 1 | 0% | 1,209 | 3,692 | +205% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted, and 18 counted toward the lift figure. The other 4 produced results that are not comparable between the two arms, so they are excluded from the headline rather than averaged into it. The headline lift of +50 percentage points is the difference between those two pass rates over the 18 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.