Expand description
§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.
tess /var/log/syslog # like `less`, but newer keys
git log | tess # pipe-friendly
tess -f --tail 1000 huge.log # tail -f the last 1000 lines
tess --format apache-combined --filter status~^5 access.log
tess --prettify config.json # auto-detected JSON layout§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)
brew install codedeviate/cli/tessThe formula lives in the codedeviate/homebrew-cli tap and builds from source on first install.
§crates.io
cargo install tess-cliThe 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
tar -xzf tess-<version>.tar.gz
cd <extracted-dir>
cargo build --release
install -m 755 target/release/tess ~/.local/bin/tessRequires 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.
tess --grep error app.log
tess --grep error --grep '^\[' app.log # both patterns must match
tess --grep error --dim app.log # show all, dim non-matches--grep composes with --filter when both are set (line must match both):
tess --format apache-combined --filter status=500 --grep timeout access.log§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:
[format.app]
pattern = '^(?P<ts>\S+) (?P<level>\w+) (?P<msg>.+)$'Then:
tess --format app --filter 'level!=DEBUG' app.log # hide DEBUG lines
tess --format app --filter 'level=ERROR' --dim app.log # keep DEBUG dimmed for contextOperators: = (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:
tess photo.png
tess logo.gif # GIFs render their first frameFlags:
| 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.
§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
cargo build --release # binary at target/release/tess
cargo test -- --test-threads=1 # unit + integration tests (PTY tests need serial)
cargo bench # criterion baselines (HTML in target/criterion/)
cargo run -- Cargo.toml # quick interactive run
ls -la | cargo run --release # piped stdin§License
MIT — see LICENSE.
Modules§
- ansi
- ANSI SGR (Select Graphic Rendition) + OSC 8 hyperlink parser.
- app
- batch
- Non-interactive write mode (
--output FILE/--stdout). - cli
- config_
path - Discovery of the global and local config directories. Owned here so
format.rsandkeys.rsdon’t drift on path logic. - error
- file_
set - Owns the multi-file working set: a list of paths, a current-index
cursor, and the navigation primitives that the colon-prompt dispatch
consumes (
:n,:p,:e,:d,:x,:t). - filter
- format
- grep
- hex
- xxd-style hex dump rendering. One row = 16 bytes, with offset prefix and ASCII gutter.
- image_
export - Serialize a rendered ASCII-art cell grid to a writer: ANSI-colored rows by default, or plain text when color is off.
- image_
render - Pure image → ASCII-art kernel. Decodes nothing and touches no terminal;
callers pass an already-decoded
RgbaImage. Mirrorsrender’s discipline: plain inputs, plain cell outputs, exhaustively unit-tested. - input
- keymap
- Static registry of every default keybinding, with category and human-
readable description. Used by the help overlay; not the runtime dispatcher
(that’s
input::translate). Theregistry_matches_translatetest in this module enforces that the two don’t drift. - keys
- Custom keybindings loaded from
~/.config/tess/keys.toml. - line_
index - marks
- Pure helpers for marks (
m<x>/'<x>) and previous-position swap (^X^X). - open
- Helper to open a file source, applying the live-mode wrapper and/or
preprocessor as configured. Factored out of
main.rsso thatapp.rscan also call it when switching files via colon-prompt commands. - overlay
- Overlay subsystem: full-screen popups that take over the body+status
area (file picker, help). App holds an
Option<Box<dyn Overlay>>; events route through it first when present. - preprocess
- Input preprocessor: pipe the source file through a user-defined command
before tess reads it. Supports
--preprocess CMD(CLI) and$LESSOPEN(env var). Pipe-mode only — the command must start with|, and%sis substituted with the source file path (shell-quoted). - prettify
- Content-type detection and pretty-printing for structured data.
- prompt
- Status-line prompt customization. Wraps the existing
DisplayTemplateparser fromformat.rs, validating against a fixed set of prompt-only placeholder names. Rendered against aPromptContextpopulated by the viewport on every frame. - render
- shell
- “Drop TUI -> run shell command -> restore TUI” helper. Used by
!cmdat runtime AND by lesskey’s!shell commandbindings (Task 3). - source
- style_
spec - Parser for the
--status-style/--prompt-styleCLI flags and the per-formatprompt_styleconfig key. Maps a comma-separated token list onto anansi::Style. - tags
- Tag-file parsing and lookup. Supports ctags (traditional + exuberant
suffix) and etags formats. Public API:
TagFile::load,TagFile::lookup,TagFile::find_walking_up,TagFile::reload_if_changed. - terminal
- viewport