Skip to main content

Crate tess

Crate tess 

Source
Expand description

§tess

GitHub License: MIT Rust edition 2021
Latest release crates.io Homebrew

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_start in formats.toml (or pass --record-start REGEX) to group continuation lines under their leading record — search, filter, and --grep then 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. --follow for tail-style appended logs (background reader thread, doesn’t fight your tty); --live for whole-file rewrites (editor saves, AI agents, build outputs).
  • Cheap on huge files. mmap’d file source, lazy line indexing, --tail N reverse-scans only as far as needed.
  • Long-line scrolling that actually works. j walks wrap-rows so the last 1000 chars of a 5000-char line are reachable; J / K jump 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/tess

The formula lives in the codedeviate/homebrew-cli tap and builds from source on first install.

§crates.io

cargo install tess-cli

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

tar -xzf tess-<version>.tar.gz
cd <extracted-dir>
cargo build --release
install -m 755 target/release/tess ~/.local/bin/tess

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

GoalCommand
View a filetess Cargo.toml
View piped outputgit log | tess
Watch a log livetess -f /var/log/syslog
Watch a file get rewrittentess --live src/main.rs
Pretty-print JSON / YAML / etc.tess --prettify config.json
Show line numberstess -N script.sh
Don’t wrap long linestess -S /etc/hosts
Last 1000 lines (cheap on huge files)tess --tail 1000 huge.log
tail -f last 1000tess -f --tail 1000 huge.log
First 50 linestess --head 50 file.txt
Apache 5xx errorstess --format apache-combined --filter status~^5 access.log
Navigate multiple filestess 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 ReturnScroll down one screen line (walks through wrap rows)
k y Ctrl-YScroll up one screen line
J / KJump to next / previous logical line (skips wrap rows of long lines)
Space f Ctrl-F PgDnPage down
b Ctrl-B PgUpPage up
g / GGo to top / bottom
/ pattern EnterForward regex search; Esc cancels
? pattern EnterBackward regex search
n / NRepeat last search forward / backward
-N / -S / Shift-FToggle line numbers / chop / follow mode
Shift-PToggle pretty-print on/off
Shift-RForce reload from disk (with --live)
:n / :p / :e <path>Next file / previous file / open new file
Ctrl-] / Ctrl-TTag jump / pop tag stack
q Q Ctrl-CQuit

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 context

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:

tess photo.png
tess logo.gif           # GIFs render their first frame

Flags:

FlagBehavior
--blocksUnicode half-block () mode — twice the vertical resolution per terminal row.
--image-width NScale to N columns (default: terminal width).
--no-imageView 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.rs and keys.rs don’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. Mirrors render’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). The registry_matches_translate test 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.rs so that app.rs can 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 %s is 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 DisplayTemplate parser from format.rs, validating against a fixed set of prompt-only placeholder names. Rendered against a PromptContext populated by the viewport on every frame.
render
shell
“Drop TUI -> run shell command -> restore TUI” helper. Used by !cmd at runtime AND by lesskey’s !shell command bindings (Task 3).
source
style_spec
Parser for the --status-style / --prompt-style CLI flags and the per-format prompt_style config key. Maps a comma-separated token list onto an ansi::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