tess-cli 0.31.0

A less-style terminal pager for files, pipes, and live logs — with structured-log filtering, pretty-printing (JSON/YAML/TOML/XML/HTML/CSV), ANSI passthrough, multi-file navigation, and ctags jumping. Rust, macOS + Linux.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
# Changelog

All notable changes to `tess` are documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

Dates are ISO 8601. Pre-1.0 minor bumps may include small breaking changes; those
are called out where relevant.

## [Unreleased]

## [0.30.0] — 2026-05-24

### Added

- Global config layer at `/etc/tess/formats.toml` and `/etc/tess/keys.toml`
  (override path via `$TESS_GLOBAL_CONFIG_DIR`). The per-user files at
  `~/.config/tess/` are now layered on top with per-section-key replace
  semantics: a local `[format.X]` overrides the global `[format.X]` of
  the same name, but every other global entry survives. Same rule for
  `[group.X]` and individual keys inside `[bindings]`.
- `tess --list-formats` annotates each format with its source:
  `[built-in]`, `[global]`, `[local]`, or
  `[<layer>, overrides <lower-layer>]`.

### Changed

- A malformed global config file prints a warning on stderr and is
  treated as empty; the binary continues with built-ins + local config.
  (Malformed local configs still fail startup as before.)

## [0.29.1] — 2026-05-24

### Fixed

- `Viewport::frame_hex` was missing `word_wrap: false` in its
  `RenderOpts` struct literal — a stray oversight from the 0.28.0
  `--wordwrap` work. **0.29.0 source did not compile from a clean
  checkout** (`cargo install tess-cli@0.29.0` and the Homebrew formula
  build both failed); the local release build only worked because the
  working tree had been patched before commit. 0.29.1 ships the fix.
  Hex mode never word-wraps (chop, fixed columns), so the value is
  always `false`.

### Documentation

- README badge polish: standardized header layout/colors, added logos to the
  license and release badges.
- CLAUDE.md: tagging now requires creating the matching GitHub release in the
  same step.
- `OUT-OF-SCOPE.md` cleanup:
  - Removed the contradictory "remove on ship; note in CHANGELOG"
    rule that fought the cumulative `Picked up:` tracking on the
    long-tail less-flag entry.
  - Reformatted the long-tail entry so each picked-up release is its
    own bullet, with a sentence pointing at the Out-of-scope section
    to disambiguate "we pick up flags" from "we aim for `less` parity".
  - Replaced the brief "Bug-for-bug compatibility with GNU `less`"
    item with an explicit **"`less` parity is not a goal"** section:
    a four-bullet opener (no drop-in replacement, no byte-for-byte
    layout, no undocumented-quirk chasing, no `less` config files /
    env), a 17-row table of specific `less` features `tess` won't
    pursue with rationale + `tess` equivalent, and a "Specific
    intentional divergences" subsection.
  - Moved **Windows support** from Deferred to Waiting. Reframed as
    "not a primary goal" with a clearer policy (macOS + Linux daily
    driver; open to a real Windows use case + someone willing to
    drive integration testing). Added a note on NTFS file-system
    semantics around `--follow`.
- `CHANGELOG.md`: backfilled `0.20.0` through `0.29.0` (ten releases)
  in Keep-a-Changelog form.

## [0.29.0] — 2026-05-24

### Added

- `--follow-name` flag accepted for `tail -F` / `less --follow-name`
  compatibility. tess already follows by path (rotation/truncation
  detected on every poll and re-opened from offset 0, shipped in 0.25.0);
  this flag is a no-op for consistency. Emits a one-line stderr note if
  given without `-f`.
- `--exit-follow-on-close` flag. In follow mode with piped stdin, exit
  when the upstream writer closes the pipe. Default off (today's
  behavior preserved). No-op for file sources.

## [0.28.0] — 2026-05-24

### Added

- `-s` / `--squeeze-blank-lines`. Collapse runs of two or more
  consecutive blank lines into a single blank at display time. Real line
  numbers, search, and tag jumps are unaffected.
- `--header=L[,C]`. Pin top `L` source rows at the top of the viewport.
  The `C` (left columns) field is wired but currently inert — future
  horizontal-scroll work can opt into it without re-plumbing. Runtime
  adjustment via `:header L [C]`.
- `--rscroll=CHAR`. Character displayed at the right edge of a line
  chopped in `-S` chop mode, signaling "more content right". Default
  `>`. Pass `--rscroll ''` to disable.
- `-z N` / `--window=N`. PageDown / PageUp step size in lines.
  Default: full body height. Half-page commands always advance by half
  the screen regardless.
- `--wordwrap`. In wrap mode, break lines at the last whitespace before
  `cols` instead of mid-character. Falls back to mid-character break
  when no whitespace fits.

## [0.27.0] — 2026-05-24

### Added

- `-X` / `--no-init`. Skip alt-screen entry on startup; content remains
  in terminal scrollback after exit. Crucial for piped use and
  git-pager-style workflows.
- `-F` / `--quit-if-one-screen`. When the entire source fits within one
  screen and is not still being streamed, print verbatim and exit — no
  pager. Pairs naturally with `-X`.
- `-K` / `--quit-on-intr`. Accepted for `less` compatibility; no-op
  since Ctrl-C already quits.
- `-e` / `--quit-at-eof` and `-E` / `--QUIT-AT-EOF`. Auto-exit when
  scrolling past end-of-file. `-e` quits on the second consecutive
  forward-motion at EOF; `-E` quits on the first. Mutually exclusive.
- `+CMD` startup commands. Pre-clap argv pass extracts `+G`, `+NUM`,
  `+/pattern`, `+?pattern` tokens and applies them against the viewport
  before the event loop. Honors `-i` / `-I` for the search forms.

### Changed

- `TerminalGuard::enter` gains a `with_alt_screen: bool` parameter.
  When false, raw mode is still enabled but `EnterAlternateScreen` is
  skipped, and the drop path doesn't emit `LeaveAlternateScreen`.

## [0.26.0] — 2026-05-24

### Added

- `-i` / `--ignore-case`. Smart-case search: case-insensitive unless
  the pattern contains an uppercase character. Matches less / ripgrep /
  vim smartcase. Applies to `/`, `?`, `--grep`, and `--filter ~ / !~`
  regex operators.
- `-I` / `--IGNORE-CASE`. Force case-insensitive search regardless of
  pattern case. Mutually exclusive with `-i`.
- `-G` / `--no-hilite-search`. Disable search-match highlighting at
  startup. Search navigation (`n` / `N`) still works.
- `:case [sensitive|smart|insensitive]` colon command. Cycles when
  given without an argument. Re-compiles any active search so the new
  policy takes effect on the next frame.
- `:hlsearch` / `:nohlsearch` colon commands. Toggle search-match
  highlighting at runtime.

### Changed

- `GrepPredicate::compile` and `CompiledFilter::compile` now accept a
  `case_mode: CaseMode` parameter. Threaded through main.rs from `-i`
  / `-I` resolution. Library API change for downstream callers.

## [0.25.0] — 2026-05-24

### Added

- Follow-mode auto-reopen on rotation or truncation. `FileSource`
  stat's the path on every pump tick; a shrinking size or changed
  inode flips a one-shot rotation flag. The app loop reacts by
  re-opening the source from its path, clearing the line index, and
  snapping to bottom. Status flashes `(F reopened)` for ~1s.
- `(F idle)` status indicator. After ~5s of no new bytes in follow
  mode, the marker changes from `(F)` to `(F idle)` so users can tell
  the source is being watched but quiet.
- `--follow-suspend-on-motion` flag. Opt-in `less +F` semantics — any
  motion command (scroll, page, goto-line) suspends following.
  Re-engage with `Shift-F`. Bare `G` (goto-bottom) intentionally never
  suspends since it's the user re-engaging.

## [0.24.0] — 2026-05-24

### Added

- Tab completion in the `:tag` / `Ctrl-]` prompt. Extends to the
  longest common prefix on first Tab; second consecutive Tab shows the
  match count.
- Auto-reload of the tags file when its mtime changes. Before every
  tag operation (`:tag NAME`, `Ctrl-]`, `:tnext` / `:tprev` /
  `:tselect`, Tab completion), tess re-stats the tags file and
  re-parses if newer. Successful reload surfaces `[tags reloaded]`.
- Chained `;` tag addresses (`/foo/;/bar/`). Each step searches from
  the line matched by the previous one, matching vim behavior. `;`
  inside `/.../` or `?...?` patterns is treated as literal.
- Graceful skip of unsupported tag-address forms (`:s/...`, `:call ...`,
  etc.). Jump goes to line 1 of the target file with a status hint
  rather than silently failing.
- `:tselect [NAME]` colon command. Opens a picker overlay listing every
  match for the tag. ``/`` or `j`/`k` navigate; Enter or 1–9 picks
  directly. Without a name, uses the currently-active multi-match list.

### Changed

- `TagAddress` enum gains `Chained(Vec<TagAddress>)` and
  `Unsupported(String)` variants alongside the existing `Line` and
  `Pattern`.

## [0.23.0] — 2026-05-24

### Added

- `:color [strict|interpret|raw]` colon command. Cycles through the
  three ANSI policies when given without an argument, or sets one
  directly.
- `--truecolor=auto|never|always` flag. `auto` (default) checks
  `$COLORTERM` and downsamples 24-bit RGB to the xterm 256-color
  palette when truecolor isn't advertised; `never` always downsamples;
  `always` passes RGB through.
- `--status-style=SPEC` and `--prompt-style=SPEC` flags. Style the
  status row and prompt row with attribute / fg / bg tokens. Grammar:
  `bold,dim,italic,underline,reverse,fg=COLOR,bg=COLOR`. COLOR is a
  named color (`black`..`white`, optional `bright-` prefix), `#RRGGBB`,
  or 0–255. Empty string disables theming.
- Per-format `prompt_style = '...'` key in `formats.toml`. CLI
  `--prompt-style` wins; format-level wins over `--status-style`.
- Backslash escapes in `--display` and `--prompt` literals: `\e` /
  `\x1b` / `\033` (ESC), `\n`, `\t`, `\r`, `\xHH`, `\NNN`. Lets users
  embed raw SGR sequences directly in templates.
- True `-r` / `--raw-control-chars` passthrough. When `AnsiMode::Raw`
  is active, the writer emits original source bytes for each visible
  row verbatim, so cursor moves and non-SGR CSI sequences flow to the
  terminal. Wrap math is best-effort, matching `less -r`.

## [0.22.0] — 2026-05-22

### Added

- `--hex-group N` flag. Sets hex grouping in `--hex` mode to 2, 4, 8,
  16, or 32 hex characters per group (1 / 2 / 4 / 8 / 16 bytes).
  Default 4 (matches `xxd`). 32 collapses each row to a single
  unspaced group.
- `:hex N` colon command. Changes the group size live without
  restarting.

## [0.21.2] — 2026-05-22

### Documentation

- Lib crate ships with `README.md` embedded as crate docs, so the
  docs.rs front page renders the same content as GitHub.

## [0.21.1] — 2026-05-21

### Changed

- `--help` output is now colorized to match the `--examples` palette
  (yellow headers, cyan literals, bold descriptions).
- Removed brittle `display_order` numbering from clap derives; help
  flag order is now derived from declaration order, which is easier
  to maintain.

## [0.21.0] — 2026-05-21

### Added

- Interactive **help overlay**. `:help` / `:h` / `F1` opens a
  category-grouped, filter-enabled overlay showing every key binding
  and command, including any user remaps from `~/.config/tess/keys.toml`.
  Scrollwheel and `j`/`k` navigate the cursor; type to filter.
- `--mouse` flag and `TerminalGuard` toggles mouse capture. Scrollwheel
  scrolls the body and click-/scroll-events drive the file picker.
- Right-aligned `:help` discoverability hint on the default status
  line. Users get pointed at the help overlay without having to
  read the man page.

## [0.20.0] — 2026-05-21

### Added

- `:b` / `:buffers` colon command. Opens a **full-screen file picker
  overlay** listing the current working set. Type to filter, ``/``
  or `j`/`k` to navigate, Enter to switch, Ctrl-D to drop a file.
  Each row shows the path, an indicator for the currently-open file,
  and the saved top-line offset so re-entry restores scroll position.

## [0.19.0] — 2026-05-20

### Changed

- Frame rendering uses **synchronized output** (DEC private mode 2026):
  every frame is wrapped in `\x1b[?2026h``\x1b[?2026l` so terminals
  that support it (iTerm2, Kitty, WezTerm, Alacritty, Ghostty, foot,
  recent VTE, Windows Terminal) buffer the whole frame and present it
  atomically. Terminals that don't recognize the sequence ignore it.
- The previous full-screen `Clear(All)` before each redraw is gone.
  Each row now does its own `Clear(UntilNewLine)` after `MoveTo(0, i)`
  immediately before painting, which also covers the shrink-on-resize
  case (old cells past the new edge are wiped).
- Together these eliminate the visible flicker that used to appear on
  every `j` / `k` / arrow keystroke, every poll tick in follow mode,
  and during resizes.

## [0.18.5] — 2026-05-20

### Documentation

- CLAUDE.md: the post-commit build chore skips the debug profile by
  default; only `cargo build --release` runs. Debug is built only when
  actually needed or on explicit request.

## [0.18.4] — 2026-05-20

### Changed

- Records-mode `--filter` now evaluates the format regex against the full
  multi-line record bytes with dotall + multi-line flags enabled, instead
  of just the record's header line. Greedy captures such as
  `(?P<message>.*)$` consume the entire record body across newlines, so
  `--filter message~foo` matches when `foo` appears anywhere in the
  record (header *or* continuation lines), which is how a user thinks
  about a multi-line record. The 0.18.2 header-only behavior was a too
  conservative first cut — fields that are bounded by line-end patterns
  (`[^\]]+`, `\w+`, etc.) keep their old semantics because the bound is
  honored regardless of dotall.

### Fixed

- `--stdout` / `--output` no longer drops records where the filter
  predicate only matches text in the body, mirroring the same change in
  the interactive viewport.

## [0.18.3] — 2026-05-20

### Fixed

- Records-mode status line no longer produces inverted record ranges like
  `R290-8/538631`. In hide mode (filter / grep without `--dim`) the
  status-line `bottom` is a position in `visible_lines`, not a logical
  line index. The R-block was passing that position into `line_to_record`,
  which resolved to an early record in the file (`8`) instead of the
  record actually visible at the bottom of the viewport. A new
  `bottom_visible_line()` helper resolves the real logical line at the
  bottom of the body — `visible_lines[cur + body_rows - 1]` in hide mode,
  `top_line + body_rows - 1` otherwise — and the R-block is derived from
  that. A defensive clamp keeps `rec_bottom >= rec_top` against future
  regressions.

## [0.18.2] — 2026-05-20

### Fixed

- `--filter` in records mode now keeps the entire matching record visible,
  not just the header line. The filter is evaluated against the record's
  header line (where the format regex was designed to anchor with `$`) and,
  on a match, all of the record's physical lines are kept. Previously the
  format regex was applied to the full multi-line record bytes; the `$`
  anchor never matched, the predicate returned `NotParsed`, and every
  record was hidden — or, in batch mode, only the header line was emitted.
- Batch mode (`--stdout` / `--output`) is now records-aware. It walks
  records (not lines), evaluates the filter against the header and grep
  against the full record bytes, and emits every physical line of each
  matching record.

## [0.18.1] — 2026-05-20

### Fixed

- `--dim` actually dims non-matching rows again. The frame writer was queuing
  a row-level `SetAttribute(Dim)` and then immediately clearing it on the
  first cell because each `Cell::Char` carried `Style::default()` (dim=false)
  and the per-cell style diff emitted `NormalIntensity`. The row-level dim
  is now OR'd into each cell's effective style (bold cells still win, since
  bold and dim share the SGR intensity slot), and `Cell::Empty` padding
  inherits the row's dim instead of resetting to default.

## [0.18.0] — 2026-05-19

### Added

- ANSI color support. SGR escapes (colors, bold, underline, italic, inverse,
  strike-through, 8/16/256/truecolor) and OSC 8 hyperlinks are interpreted by
  default instead of being shown as literal escape sequences.
- `--no-color` flag and `-r` / `--raw-control-chars` to opt back into the
  pre-0.18 byte-faithful rendering.
- `ansi` parser module with `strip_sgr` helper; `Cell::Char` now carries
  `Style` and optional hyperlink target.
- Cross-line SGR state: when scrolling into the middle of a styled region,
  tess reconstructs the active style by replaying up to 256 prior lines so
  colors don't visually reset on scroll-back.
- Frame writer now diff-emits crossterm color/attribute commands and wraps
  OSC 8 hyperlinks across the active body.

### Changed

- Non-SGR CSI sequences (cursor moves, screen clears) are silently stripped
  to protect the layout; search/filter/grep operate on the SGR-stripped text.

## [0.17.0] — 2026-05-19

### Added

- `man/tess.1` generated via `clap_mangen` from the CLI definition; a
  `gen-manpage` binary regenerates it.
- `--examples` output is now colorized (cyan command lines, yellow section
  headers).

## [0.16.0] — 2026-05-19

### Added

- ctags / etags tag jumping.
  - `-t NAME` jumps to a tag at startup; `-T PATH` selects an explicit
    tags file; without `-T`, tess walks up from the current file looking
    for `tags` / `TAGS`.
  - `:tag NAME` runtime prompt, `Ctrl-]` jumps to the tag under the cursor.
  - `Ctrl-T` pops the tag stack; `:tnext` / `:tprev` cycle multiple matches.
  - `<tag-tag>` prompt template placeholder reports the current tag.
- `tags` module: ctags + etags parsing, lookup table, and walk-up discovery.

## [0.15.0] — 2026-05-18

### Added

- Multi-file navigation. A `FileSet` working set owns paths, the active
  cursor, and append/delete/next/prev semantics.
  - Colon-command mode: `:n` / `:p` next/previous file, `:e` open,
    `:f` show filename, `:q` quit, `:d` drop current, `:x` remove from
    set, `:t` list set.
  - Marks now carry a `file_index`, and the previous-position slot is
    session-wide across files.
- README gets a badge header (GitHub / release / Rust / crates / Homebrew /
  MIT).

### Changed

- `main` extracts an `open_source_for_path` helper used by file switching.

## [0.14.0] — 2026-05-18

### Added

- Shell integration.
  - `!cmd` shell escape: drops the alt-screen, runs the command via the
    user's `$SHELL`, and resumes on keypress.
  - `--preprocess '|cmd %s'` flag and `$LESSOPEN` env-var fallback to pipe
    files through an external preprocessor before display.
  - User-remappable keybindings via `~/.config/tess/keys.toml`, including
    inline `!cmd` bindings.

### Fixed

- `Ctrl-J` no longer falsely adds the Shift modifier.
- Shell escape re-enables raw mode before reading the resume key.
- `--preprocess` is now in the mutex set with `--live`; the pdftotext
  example is correct.

## [0.13.0] — 2026-05-18

### Added

- `--hex` flag: xxd-style rendering for binary inputs, with byte offsets
  in the status line.
- `--prompt TEMPLATE` and per-format `prompt = '...'` to customize the
  status line; template placeholders include `<tag-tag>` and the active
  format name.
- `--grep`/`--filter` `[hide]` token in formats.toml `grep` field for group
  presets (renamed from the earlier hide-mode token).

### Fixed

- Hex status line shows byte offsets instead of row indices.
- Closed a `RESERVED_LONG_FLAGS` gap that allowed flag/template collisions.

## [0.12.0] — 2026-05-17

### Added

- Session-local marks: `m<x>` sets a mark, `'<x>` jumps to it.
- `Ctrl-X Ctrl-X` jumps to the previous position (round-trip).

## [0.11.0] — 2026-05-15

### Added

- Multi-line records. A `record_start` regex (in format definitions or via
  `--record-start`) groups continuation lines into a single logical record.
  - `line_index` tracks `record_starts`; viewport reports a dual L/R
    line/record readout in the status line when records are active.
  - Search, filter, and grep evaluate against whole records.
- Numeric prefix on motions: `Ng` / `NG` / `N%` go-to wiring.

### Fixed

- `goto_percent(100)` lands at the last line, not the top.
- `record_count(head_cap=0)` no longer panics; dropped dead
  `pending_record_start` field.
- Viewport tests renamed to silence `non_snake_case` warnings.

### Tests

- Property-based tests (`proptest`) covering the render kernel invariants.
- PTY smoke tests for spawn / quit / SIGTERM / resize.
- Criterion benchmarks for `line_index`, scroll math, search, and render.
- Integration / property / PTY / bench coverage wired for records mode.

## [0.10.5] — 2026-05-15

### Documentation

- README documents `cargo bench` and `cargo test -- --test-threads=1`.
- Out-of-scope: dropped the already-resolved `Read` import entry.

## [0.10.1] — 2026-05-15

### Changed

- `Cargo.lock` is now committed (binary-crate convention).

## [0.10.0] — 2026-05-13

### Added

- `--grep PATTERN` raw-line regex filtering. Repeatable, AND'd, composable
  with `--filter`. `GrepPredicate` (regex AND on raw lines) hides or dims
  non-matching lines and surfaces grep state in the status line. Threads
  through interactive mode and `--output`/`--stdout` batch mode.
- `--dim` is now permitted alongside `--grep` (keeps non-matches visible
  but faded).

### Fixed

- `expand_argv` handles `--grep` values; `--grep` is in the reserved-flag
  set so user-defined groups can't collide with it.

## [0.9.1] — 2026-05-08

### Changed

- Published on crates.io as `tess-cli` (the `tess` name was unavailable);
  binary is still `tess`. Out-of-scope adopts
  Waiting / Deferred / Not-yet / Out-of-scope buckets.

## [0.9.0] — 2026-05-07

### Added

- `--display TEMPLATE` and per-format `display` key: templated rendering of
  parsed log fields (e.g. compact, colorless, custom field order).

## [0.8.0] — 2026-05-07

### Added

- Non-interactive batch mode: `--output FILE` and `--stdout` write the
  resolved view (with filters / grep / display template applied) without
  entering the alt-screen — useful in pipelines and CI.

## [0.7.0] — 2026-05-07

### Added

- Comparison operators in `--filter`: `<`, `<=`, `>`, `>=` (in addition to
  `=` / `!=` / regex match).

### Documentation

- MANUAL documents the nested-capture-group pattern for log formats.
- Out-of-scope: multi-line log records (`record_start`) deferred (later
  landed in 0.11.0).

## [0.6.6] — 2026-05-05

First crates.io / Homebrew-ready release.

### Changed

- Full crates.io metadata: expanded description, homepage, documentation
  URL, keywords, categories, `exclude` list to drop local artifacts/notes.
- MSRV pinned at `rust-version = "1.85"` (clap_lex 1.1.0 → edition 2024
  → Rust 1.85).
- Release profile tuned.

## [0.6.5] — 2026-05-05

### Added

- MIT license; Cargo metadata for publishing.

### Documentation

- `README.md`.

## [0.6.x development] — 2026-04-27 → 2026-05-05

The initial run from project scaffold to publishable crate. Notable
milestones, in chronological order:

### Added — kernel and core

- `error` enum and exit-code mapping (0 clean / 1 startup / 2 runtime).
- `render` kernel: cell types and ASCII layout, tab expansion to next tab
  stop, control-byte `^X` and invalid-byte `<HH>` rendering, UTF-8
  grapheme cluster decoding with width-2 support, correct wrap and chop at
  width-2 boundaries, `count_rows` fast path for scroll math.
- `source`: `Source` trait, `FileSource` (mmap + fallback), `MockSource`
  for tests, `StdinSource` (synchronous and threaded streaming modes).
- `line_index`: lazy + incremental newline scan.
- `viewport`: state, frame composition, line scroll, paging / half-paging
  / goto / resize, toggles.
- `input`: full key-map event-to-command translation.
- `terminal`: `TerminalGuard` (alt-screen RAII), panic hook, signal flag.
- `app`: main event loop with frame writing.
- `cli`: clap-based argv parsing.
- `main`: CLI wiring, source resolution, terminal guard, app loop.

### Added — features

- Follow mode (`-f` / `--follow`, interactive `Shift-F`).
- `--head N` and `--tail N` (reverse byte-offset scan for `--tail`).
- Log-format parsing with named regex captures and field-based filtering
  (`--filter FIELD<op>VALUE`, repeatable, AND'd).
  Built-in formats: apache-common, apache-combined, nginx-combined.
  User-defined formats in `~/.config/tess/formats.toml`.
- User-defined CLI groups (`[group.NAME]` in `formats.toml`):
  `--<groupname>` expands to a fixed flag bundle and turns positionals
  into filters.
- Interactive regex search (`/`, `?`, `n`, `N`) with row highlight.
- Alphabetical `--help`, `--manual`, and `--examples` (auto-page on TTY);
  `INSTALL.md`.
- `--live` flag for in-place file rewrites, plus the `R` reload key.
- `--prettify` and `--content-type` for JSON / YAML / TOML / XML / HTML /
  CSV.
- `J` / `K` jump to next/prev logical line; status shows wrap row.

### Fixed

- Eliminated flicker and restored keyboard input on piped stdin.
- Switched `crossterm` to `use-dev-tty` and scoped the stdin redirect to
  pipe mode (the default mio source failed on macOS with piped stdin).
- `line_index::extend_to_line` breaks when `head_cap` is hit.
- Search `/<Enter>` repeats; scroll walks wrap rows of the last line.
- Per-substring search highlight + attribute-bleed fix.

### Documentation

- `CLAUDE.md`, `OUT-OF-SCOPE.md`, `MANUAL.md` (with extensive examples,
  including the bash history-expansion gotcha for `!`).
- `INSTALL.md` documents the macOS 26 SIGKILL gotcha (codesign on
  recovery).

### Renames

- Crate renamed from `rustless` to `tess`.
- Project directory `Test``tess` in `CLAUDE.md` paths.

### Tests

- Golden-frame integration test exercising
  `FileSource → LineIndex → Viewport → render`.

[Unreleased]: https://github.com/codedeviate/tess/compare/v0.19.0...HEAD
[0.19.0]: https://github.com/codedeviate/tess/compare/v0.18.5...v0.19.0
[0.18.5]: https://github.com/codedeviate/tess/compare/v0.18.4...v0.18.5
[0.18.4]: https://github.com/codedeviate/tess/compare/v0.18.3...v0.18.4
[0.18.3]: https://github.com/codedeviate/tess/compare/v0.18.2...v0.18.3
[0.18.2]: https://github.com/codedeviate/tess/compare/v0.18.1...v0.18.2
[0.18.1]: https://github.com/codedeviate/tess/compare/v0.18.0...v0.18.1
[0.18.0]: https://github.com/codedeviate/tess/releases/tag/v0.18.0
[0.17.0]: https://github.com/codedeviate/tess/releases/tag/v0.17.0
[0.16.0]: https://github.com/codedeviate/tess/releases/tag/v0.16.0
[0.15.0]: https://github.com/codedeviate/tess/releases/tag/v0.15.0
[0.14.0]: https://github.com/codedeviate/tess/releases/tag/v0.14.0
[0.13.0]: https://github.com/codedeviate/tess/releases/tag/v0.13.0
[0.12.0]: https://github.com/codedeviate/tess/releases/tag/v0.12.0
[0.11.0]: https://github.com/codedeviate/tess/releases/tag/v0.11.0
[0.10.5]: https://github.com/codedeviate/tess/compare/v0.10.1...v0.11.0
[0.10.1]: https://github.com/codedeviate/tess/compare/v0.10.0...v0.10.5
[0.10.0]: https://github.com/codedeviate/tess/compare/v0.9.1...v0.10.0
[0.9.1]: https://github.com/codedeviate/tess/releases/tag/v0.9.1
[0.9.0]: https://github.com/codedeviate/tess/releases/tag/v0.9.0
[0.8.0]: https://github.com/codedeviate/tess/compare/v0.6.6...v0.9.0
[0.7.0]: https://github.com/codedeviate/tess/compare/v0.6.6...v0.9.0
[0.6.6]: https://github.com/codedeviate/tess/releases/tag/v0.6.6
[0.6.5]: https://github.com/codedeviate/tess/compare/v0.6.6...v0.6.6