ANSI Stripping

shuck’s ANSI stripper is its core innovation. It processes raw terminal output in a single pass, removing all TUI artifacts and returning clean, readable text.

Why Agents Can’t Parse ANSI

When an agent reads output from an interactive CLI tool, it receives raw terminal data full of ANSI escape sequences:

\x1b[1;32m✓\x1b[0m All packages are healthy
\x1b[31m✗\x1b[0m 3 broken dependencies found
\x1b[?25l\x1b[2J\x1b[H  Loading...  \x1b[?25h

Agents can’t reliably parse this. The sequences aren’t just color codes — they include cursor movement, screen clearing, progress bar redraws, and window title changes. Regex-based stripping misses edge cases. Agents need clean text:

✓ All packages are healthy
✗ 3 broken dependencies found
Loading...

shuck handles this correctly because it uses the same VTE state machine as real terminal emulators, not regex patterns.

What Gets Stripped

SGR Color Sequences

The most common sequences — Select Graphic Rendition (SGR) — control text color and style:

\x1b[31m      → set foreground to red
\x1b[1;32m    → bold green
\x1b[0m       → reset all attributes

These are stripped completely. The text they surround is preserved.

Cursor Movement

\x1b[A        → cursor up
\x1b[2;5H     → move to row 2, column 5
\x1b[K        → erase to end of line
\x1b[s        → save cursor position
\x1b[u        → restore cursor position

These are tracked internally to simulate the terminal state. The output is the text that would be visible on a real terminal at the end of execution.

Screen Control

\x1b[2J       → clear entire screen
\x1b[H        → move cursor to home
\x1b[3J       → clear scrollback

Discarded entirely. Progress bars and spinners that clear and redraw the screen are collapsed into their final state.

\x1b]0;My Terminal\x07    → set window title
\x1b]8;;https://example.com\x07link\x1b]8;;\x07  → hyperlink

Discarded entirely.

Carriage Returns

\r\n    → normalized to \n
\r      → handled as cursor-to-column-zero

Backspaces

hello\x08\x08done   →  "hedone" (two chars deleted)

Backspaces are applied faithfully, simulating real terminal behavior.

How the State Machine Works

The stripper is based on Paul Flo Williams’ VT500 state machine diagram, the same reference used by Alacritty, wezterm, and other serious terminal emulators.

States:

Ground → Escape → Intermediate → Csi Entry → Csi Param → Csi Ignore
                              → Osc String → ...
                              → ...

For each byte in the input:

  1. Transition to a new state based on current state + byte value
  2. Perform the action for the transition (emit, collect, execute, etc.)
  3. Only emit bytes in the Ground state (those are literal text)

This gives us:

  • O(n) time complexity — each byte is processed exactly once
  • Zero allocations — no intermediate strings or vectors created
  • Correct handling — even complex sequences like DEC private modes, APC, PM

What is NOT Stripped

shuck strips control sequences, not content. The following are preserved:

  • Regular text characters (ASCII, UTF-8)
  • Newlines \n (after CR+LF normalization)
  • Tab characters \t
  • Unicode — full multi-byte sequences are passed through intact

Testing the Stripper

You can verify stripping behavior directly:

# Output with color codes
printf '\x1b[31mred text\x1b[0m' | shuck cat

# Output: "red text" (no escape codes)
# A progress bar sequence
printf 'Loading...\r' | shuck cat

# Output: "Loading..." (carriage return handled)

Raw Mode

If you need the raw PTY output (ANSI sequences included), use --raw:

shuck --raw some-command > raw-output.txt

This bypasses the VTE stripper entirely. Useful for debugging or when you want to pipe to another tool that handles ANSI.

Performance

On modern hardware, the ANSI stripper processes approximately 2–4 GB/s of input. This means shuck’s stripping overhead is negligible for any real-world workload — the bottleneck will always be the child process or I/O, never the stripper.