Filter Pipeline
The filter pipeline processes lines in streaming fashion. Each line is normalized at read time, then passes through the pipeline stages — diff-context collapse, line collapse, whitespace squeeze — before reaching the head/tail buffer. The stages below are in execution order.
Stages
1. Read-time Line Normalization (filtered mode only)
Applied while reading each line, before anything else, and skipped entirely in --raw mode:
- Progress-bar squashing — a line containing interior carriage returns (a command redrawing the same line) keeps only the part after the last
\r, emulating what a terminal would display. - Backspace / bell resolution —
\band DEL erase the previous character (UTF-8 aware); BEL bytes are dropped. - Giant JSON line truncation — a line over 2000 bytes whose first non-whitespace byte is
{or[is cut at 2000 bytes with an explicit... [Large JSON Payload Truncated for LLM] ...marker instead of being forwarded whole.
2. ANSI Stripping
Removes all ANSI escape sequences (colors, cursor movement, bold, etc.) using the strip-ansi-escapes crate. This is always applied, even in raw mode.
Input:
\x1b[32mPASSED\x1b[0m test_somethingOutput:
PASSED test_something3. Diff Context Collapsing
Inside a unified diff, long runs of unchanged context lines are collapsed to a … (N unchanged diff lines) … marker. The stage activates only after a real hunk header (@@ … @@), so non-diff output — even indented text whose lines start with a space — is never touched. File and hunk headers and every added/removed line are kept verbatim; only context runs longer than 8 lines are collapsed, keeping 3 lines at each edge. The context buffer is bounded by both a line cap (5000) and a byte cap (1 MB), so a pathological hunk cannot grow memory unbounded. A non-diff line deactivates the stage.
4. Line Collapsing (Identical & Prefix-based)
To handle repetitive noise, the collapser runs in two modes:
Exact Identical Collapsing
When consecutive lines are exactly identical, they are collapsed into a single line with a count suffix (×N).
Input:
Downloading crate...
Downloading crate...
Downloading crate...
Downloading crate...Output:
Downloading crate... (×4)Prefix-based Collapsing
When consecutive lines share the same first word (the "prefix"), they are collapsed into a prefix summary showing the prefix, an ellipsis, and the count suffix ... (×N). This is highly effective for compiler progress and package downloaders (e.g., Compiling, Downloading). Prefix-based collapsing requires the prefix to be at least 2 characters long (to avoid collapsing on bullets like - or *) and preserves any leading indentation.
Input:
Compiling serde v1.0.1
Compiling clap v4.0.0
Compiling l0-compressor v0.3.0Output:
Compiling ... (×3)The collapsed output is emitted when the next non-matching line arrives (or at EOF), ensuring streaming correctness.
5. Whitespace Squeezing
Consecutive blank lines are reduced to a single blank line. Lines containing only whitespace are treated as blank.
Input:
section 1
section 2
section 3Output:
section 1
section 2
section 36. Head/Tail Buffer
The core data structure. Maintains two fixed-size buffers:
- head: first N lines (default 30)
- tail: circular buffer of last M lines (default 30)
When the total line count exceeds the threshold (default 100), the middle is discarded and replaced with a banner:
... [370 lines omitted for LLM] ...The buffer retains max(--tail, --tail-error) lines while streaming (the tail cannot be expanded retroactively once lines have been evicted). At render time, on a non-zero exit the larger error tail (120 lines, configurable) is shown so error messages and stack traces are preserved; on success the smaller tail (30 lines) is shown.
Clean-success squelch
One more render-time gate applies to the success tail. If the exit is zero and no error/warning signal was seen anywhere in the stream, the tail is trimmed further — squelched_tail(cap) halves it with a floor of 5 (30 → 15, 8 → 5, ≤5 unchanged, never expanded). The error-signal tracking is sticky across the whole stream (a signal in an evicted middle line still counts), so a single warning: anywhere backs the squelch off to the full tail, and any non-zero exit does the same. The head and the final summary line always survive. The behavior is on by default and shares the --only-errors keyword set; see Clean-success squelch for the user-facing controls. The runner returns the rendered tail count so the truncation banner reports the truth (30 head + 15 tail), not the configured cap.
Memory Layout
+--------+---------------------------+--------+
| head | (discarded, not stored) | tail |
| 30 ln | | 30 ln |
+--------+---------------------------+--------+
^ ^ ^
| | |
Vec<String> not in memory VecDeque<String>
(fixed) (counter only) (circular, capped)Total memory: O(head_cap + tail_cap) strings, regardless of whether the command produces 100 lines or 10 million.
Binary Detection
The first ~8 KB of output are checked for null bytes or invalid UTF-8. If either is found, the output is classified as binary. Rather than forward a useless, token-expensive blob, l0-compressor emits the sniffed first ~8 KB (lossy UTF-8) and, when the stream was larger, an explicit banner:
... [l0-compressor: binary output detected — showing first 8192 of 1048576 bytes] ...The metric records strategy: "binary_skip" and truncated: true when bytes were dropped, so binary output is never silently presented as if complete.