tess
A less-style terminal pager for files, pipes, and live logs — with first-class support for structured-log filtering, pretty-printing of JSON/YAML/TOML/XML/HTML/CSV, ANSI color passthrough for piped tools that emit colored output, wrap-aware scrolling of long lines, multi-file navigation with :n/:p/:e colon commands, and ctags/etags tag jumping (-t NAME, Ctrl-]/Ctrl-T). Escape to the shell with !cmd, preprocess inputs ($LESSOPEN / --preprocess), and remap keys via ~/.config/tess/keys.toml. Written in Rust. macOS + Linux.
|
Why?
less is great, but it doesn't know what your data is. tess adds a small amount of structure-awareness without trying to be a log explorer:
- Parse-aware filtering. Declare a log format once (built-in or your own regex), then keep only the lines whose parsed
status,ip,level, etc. match a predicate. Hide them entirely or just dim them so context is preserved. - Multi-line records. Set
record_startin formats.toml (or pass--record-start REGEX) to group continuation lines under their leading record — search, filter, and--grepthen operate on whole records like PHP stack traces or Java exception dumps. - Pretty-printing. Open a JSON / YAML / TOML / XML / HTML / CSV file and see it laid out, not minified — without leaving the pager. Toggle on/off with
Shift-P. - Live + follow modes.
--followfor tail-style appended logs (background reader thread, doesn't fight your tty);--livefor whole-file rewrites (editor saves, AI agents, build outputs). - Cheap on huge files. mmap'd file source, lazy line indexing,
--tail Nreverse-scans only as far as needed. - Long-line scrolling that actually works.
jwalks wrap-rows so the last 1000 chars of a 5000-char line are reachable;J/Kjump by whole logical lines when you don't want to scroll through every wrap.
It is not a log search/correlation tool, structured query engine, or replacement for lnav. It's a single-file pager.
Install
Homebrew (macOS / Linuxbrew)
The formula lives in the codedeviate/homebrew-cli tap and builds from source on first install.
crates.io
The crate is published as tess-cli because the bare tess name was already taken (an unrelated parked crate). The installed binary is still tess.
From source
Requires Rust 1.85+. See INSTALL.md for full instructions, including a macOS 26 (Tahoe) gotcha where cp into /usr/local/bin can get a binary SIGKILLed by the kernel — install avoids it.
Quick start
| Goal | Command |
|---|---|
| View a file | tess Cargo.toml |
| View piped output | git log | tess |
| Watch a log live | tess -f /var/log/syslog |
| Watch a file get rewritten | tess --live src/main.rs |
| Pretty-print JSON / YAML / etc. | tess --prettify config.json |
| Show line numbers | tess -N script.sh |
| Don't wrap long lines | tess -S /etc/hosts |
| Last 1000 lines (cheap on huge files) | tess --tail 1000 huge.log |
tail -f last 1000 |
tess -f --tail 1000 huge.log |
| First 50 lines | tess --head 50 file.txt |
| Apache 5xx errors | tess --format apache-combined --filter status~^5 access.log |
| Navigate multiple files | tess foo.log bar.log baz.log then :n / :p |
Run tess --examples for a curated list, or tess --manual for the full manual paged through tess itself.
Key bindings
Vim-ish + less-ish hybrid. The full table is in MANUAL.md; these are the ones you'll use:
| Key(s) | Action |
|---|---|
↓ j e Ctrl-E Return |
Scroll down one screen line (walks through wrap rows) |
↑ k y Ctrl-Y |
Scroll up one screen line |
J / K |
Jump to next / previous logical line (skips wrap rows of long lines) |
Space f Ctrl-F PgDn |
Page down |
b Ctrl-B PgUp |
Page up |
g / G |
Go to top / bottom |
/ pattern Enter |
Forward regex search; Esc cancels |
? pattern Enter |
Backward regex search |
n / N |
Repeat last search forward / backward |
-N / -S / Shift-F |
Toggle line numbers / chop / follow mode |
Shift-P |
Toggle pretty-print on/off |
Shift-R |
Force reload from disk (with --live) |
:n / :p / :e <path> |
Next file / previous file / open new file |
Ctrl-] / Ctrl-T |
Tag jump / pop tag stack |
q Q Ctrl-C |
Quit |
Pressing /<Enter> (or ?<Enter>) with an empty pattern repeats the last search in the typed direction, the way less does it.
Plain-text grep
If you don't have (or don't need) a parsed format, --grep PATTERN filters
by regex against the raw line. Repeatable, AND-combined.
--grep composes with --filter when both are set (line must match both):
Structured log filtering
Three formats ship built-in: apache-common, apache-combined, nginx-combined. Drop a TOML file at ~/.config/tess/formats.toml to define your own:
[]
= '^(?P<ts>\S+) (?P<level>\w+) (?P<msg>.+)$'
Then:
Operators: = (exact), != (exact ≠), ~ (regex), !~ (regex ≠). Multiple --filter flags AND together. Quote arguments with '…' in interactive shells so ! doesn't trigger history expansion.
Run tess --list-formats to see what's available, including your custom ones.
Status line
<source> <top>-<bottom>/<total> <pct>% +<wrap>/<wraps> [<format>] [filter|dim] [/<search>] [pretty:<type>] (L) (F)
Each segment appears only when relevant. The +12/50 indicator surfaces wrap-row position when scrolled inside a long wrapping line, so j is visibly making progress.
Images
tess auto-detects image files by magic bytes (PNG, JPEG, GIF, BMP, WebP, TIFF, TGA, ICO, PNM) and renders them as colored ASCII art directly in the pager. No flags needed — just open the file:
Flags:
| Flag | Behavior |
|---|---|
--blocks |
Unicode half-block (▀) mode — twice the vertical resolution per terminal row. |
--image-width N |
Scale to N columns (default: terminal width). |
--no-image |
View raw bytes instead of rendering; useful with --hex. |
Color output uses 24-bit truecolor SGR; pass --no-color for a plain character-only render. Export the art with -o FILE or --stdout — ANSI-colored by default, plain text under --no-color.
The image feature is on by default. Build without it (--no-default-features) for a smaller binary that treats all inputs as text.
Command-line flags
The full set, alphabetical by long name — the same order tess --help lists them. Run tess --manual for the long-form descriptions.
| Flag | Description |
|---|---|
--blocks |
Render images with Unicode half-blocks (▀) for ~2× vertical detail. |
-S, --chop-long-lines |
Chop long lines instead of wrapping. |
--content-type TYPE |
Force the --prettify content type (auto/raw/json/yaml/toml/xml/html/csv). |
--dim |
With --filter/--grep, dim non-matching lines instead of hiding them. |
--display TEMPLATE |
Render each parsed line through a <field> template (requires --format). |
--examples |
Print a curated list of usage examples and exit. |
--exit-follow-on-close |
In follow mode with piped stdin, exit when the writer closes the pipe. |
--filter FIELD<op>VALUE |
Filter visible lines by parsed field (repeatable, AND'd; requires --format). |
-f, --follow |
Follow the source for new bytes (like tail -f); toggle with Shift-F. |
--follow-name |
Follow by path (compat no-op — tess already re-opens on rotation/truncation). |
--follow-suspend-on-motion |
Any user motion suspends following (less +F semantics). |
--format NAME |
Apply a named log format (built-in or from formats.toml). |
--grep PATTERN |
Filter visible lines by regex on the raw line (repeatable, AND'd; no --format needed). |
--head N |
Show only the first N lines. Mutually exclusive with --tail. |
--header L[,C] |
Pin the top L lines (and left C columns) at the top of the viewport. |
--hex |
Render the source as an xxd-style hex dump. |
--hex-group N |
Hex bytes per group in --hex mode (2/4/8/16/32; default 4). |
-i, --ignore-case |
Smart-case search (insensitive unless the pattern has an uppercase char). |
-I, --IGNORE-CASE |
Force case-insensitive search regardless of pattern. |
--image-width N |
Target width in columns for image rendering. |
-N, --LINE-NUMBERS |
Show line numbers. |
--list-formats |
Print available log formats and their fields, then exit. |
--live |
Re-read the file when its on-disk content changes. Mutually exclusive with --follow. |
--manual |
Print the full user manual and exit. |
--mouse |
Enable mouse capture (click picker/help rows, scrollwheel scrolls the body). |
--no-color |
Show raw control bytes as ^X; disable SGR/OSC interpretation. |
-G, --no-hilite-search |
Disable search-match highlighting by default (navigation still works). |
--no-image |
Treat a detected image file as raw text instead of rendering it. |
-X, --no-init |
Don't enter the alt-screen; leave content in terminal scrollback. |
--no-preprocess |
Ignore $LESSOPEN for this invocation. |
-o, --output FILE |
Batch mode: write filtered output to FILE (- = stdout) and exit. |
--preprocess CMD |
Pipe the source through CMD before rendering (%s = file path). |
--prettify |
Pretty-print structured content (JSON/YAML/TOML/XML/HTML/CSV). |
--prompt TEMPLATE |
Replace the status line with a templated string. |
--prompt-style SPEC |
Style for --prompt output (e.g. bold,fg=cyan). |
-e, --quit-at-eof |
Quit when scrolling forward past EOF a second time (less -e). |
-E, --QUIT-AT-EOF |
Quit the first time EOF is reached (less -E). |
-F, --quit-if-one-screen |
Exit without paging if the source fits on one screen. |
-K, --quit-on-intr |
less compat no-op (tess always quits on Ctrl-C). |
-r, --raw-control-chars |
Pass every byte to the terminal raw, including escapes (less -r). |
--record-start REGEX |
Treat lines matching REGEX as record boundaries; others join the prior record. |
--rscroll CHAR |
Right-edge marker for chopped lines (default >; empty disables). |
-s, --squeeze-blank-lines |
Collapse runs of blank lines into one (less -s). |
--status-style SPEC |
Style for the status row (e.g. reverse, fg=COLOR). |
--stdout |
Synonym for --output -: write batch output to stdout. |
--tab-width N |
Tab stop width (default 8). |
-t, --tag NAME |
Jump to tag NAME at startup (requires a tags file). |
-T, --tag-file PATH |
Path to the tags file (default: walk up from CWD for tags). |
--tail N |
Show only the last N lines (cheap on huge files). Mutually exclusive with --head. |
--truecolor MODE |
24-bit RGB handling (auto/never/always). |
-z, --window N |
PageDown / PageUp step size in lines (default: full screen). |
--wordwrap |
In wrap mode, break on whitespace boundaries instead of mid-character. |
Project status
Pre-1.0; the CLI is settling but small breakage at minor-version boundaries is permitted (and called out in commit messages). Daily-driver-quality on macOS and Linux. No Windows support — the stdin / /dev/tty plumbing is Unix-specific.
Architecture notes and module layout: CLAUDE.md. Deferred features and intentional non-goals: OUT-OF-SCOPE.md.
Building from source
|
License
MIT — see LICENSE.