Winix.Less 0.4.0

Prefix Reserved
dotnet tool install --global Winix.Less --version 0.4.0
                    
This package contains a .NET tool you can call from the shell/command line.
dotnet new tool-manifest
                    
if you are setting up this repo
dotnet tool install --local Winix.Less --version 0.4.0
                    
This package contains a .NET tool you can call from the shell/command line.
#tool dotnet:?package=Winix.Less&version=0.4.0
                    
nuke :add-package Winix.Less --version 0.4.0
                    

less

Native terminal pager with ANSI colour passthrough, search, follow mode, and modern defaults.

less replacement with sane defaults on every platform. Passes ANSI escape codes through unchanged, so coloured output from tools like man, files, and treex renders correctly.

Install

Scoop (Windows)

scoop bucket add winix https://github.com/Yortw/winix
scoop install winix/less

Winget (Windows, stable releases)

winget install Winix.Less

.NET Tool (cross-platform)

dotnet tool install -g Winix.Less

Direct Download

Download native binaries from GitHub Releases.

Usage

less [options] [+command] [file]

Displays file content (or stdin) one screen at a time. When no file is given, reads from stdin. Currently accepts at most one file argument — for concatenated paging of multiple files, use cat file1 file2 | less. Multi-file paging with :n / :p navigation is tracked for a future release.

Examples

# Page a file
less somefile.txt

# Page piped input (colour passes through by default)
man timeit | less

# Show line numbers
less -N somefile.txt

# Chop long lines instead of wrapping
less -S somefile.txt

# Follow a growing file (like tail -f)
less +F logfile.log

# Jump to end of file on open
less +G somefile.txt

# Open with an initial search
less +/error logfile.log

# Force exit if output fits on one screen
less -F somefile.txt

Options

Option Description
-N Show line numbers
-S Chop long lines (don't wrap)
-F Quit immediately if output fits on one screen
-R Pass raw ANSI colour codes through (default: on)
-X Don't clear the screen on exit
-i Case-insensitive search (ignored if pattern has uppercase)
-I Case-insensitive search always
+F Start in follow mode (like tail -f)
+G Jump to end of file on open
+/pattern Start with an initial forward search for pattern
--help Show help and exit
--version Show version and exit
--color[=auto\|always\|never] Coloured output: auto (default when omitted), always, or never.
--no-color Disable coloured output

Key Bindings

Key Action
q Quit
j / Down Scroll down one line
k / Up Scroll up one line
Space / PgDn Scroll down one screen
PgUp Scroll up one screen
Home / g Jump to beginning
End / G Jump to end
/ Forward search
? Backward search
n Next search match
N Previous search match
F Enter follow mode (press q to exit)
-N Toggle line numbers
-S Toggle line chopping

LESS Environment Variable

The LESS environment variable controls default options. Three states are distinguished:

  • Unset — Winix less uses modern defaults: FRX (quit-if-one-screen, raw colour, no-init). This gives sensible out-of-the-box behaviour without configuring anything.
  • Set to a non-empty value — replaces the defaults entirely. The value is parsed as a list of options (e.g. LESS=-NiR). Unknown flags in the value are ignored.
  • Set to an empty string (LESS=) — disables all defaults. No FRX, no built-ins. Useful when you want every option to come from CLI flags.

This matches the behaviour of traditional less implementations. (Pre-v0.3.0, "unset" and "empty" were conflated and both gave defaults; the empty-disables semantics is now correctly honoured.)

Wildcards on Windows

cmd.exe and PowerShell don't expand */? wildcards before starting programs, so less expands them itself on Windows — less *.log works the same as in bash. * and ? work in any path segment. [...] is matched literally (brackets are legal Windows filename characters), and ** is rejected with an error — less has no recursive mode; use files to find files recursively. A pattern that matches nothing is passed through unchanged, so you get the normal "not found" error. In cmd, quoting a pattern ("*.log") suppresses expansion; PowerShell removes quotes before less sees them, so use --% there if you need a literal. On Linux/macOS your shell expands wildcards as usual and less does nothing extra. Note less accepts a single file, so a pattern must resolve to exactly one match — two or more matches exit 2 with a usage error (the same thing happens in bash when the shell expands).

Exit Codes

Code Meaning
0 Normal exit
1 Error (file not found, read error)
2 Usage error (bad arguments)

Colour

  • ANSI escape codes are passed through by default (-R is on unless NO_COLOR is set)
  • --color forces colour on (overrides NO_COLOR)
  • --no-color disables colour passthrough; raw escape sequences are shown as text
  • Respects the NO_COLOR environment variable (no-color.org)

Part of Winix

less is part of the Winix CLI toolkit.

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

This package has no dependencies.

Version Downloads Last Updated
0.4.0 143 6/13/2026
0.3.0 126 5/26/2026
0.3.0-rc2 108 5/10/2026
0.2.0 121 4/16/2026
0.2.0-test4 113 4/15/2026

## [0.3.0] - 2026-05-09

### Changed (BREAKING)
- Multi-file paging removed. Pre-fix the binary silently overwrote `filePath` with each subsequent positional, opening only the LAST one. README and man both claimed "Multiple files are paged in sequence." Now `less file1 file2` exits with usage error 2 and a clear message. For concatenated paging, use `cat file1 file2 | less`. True multi-file paging with `:n` / `:p` navigation is tracked for v0.5+.
- Usage errors now exit with **2** (POSIX-traditional, matches GNU `less`) instead of ShellKit's suite-default 125. Deliberate suite divergence per `feedback_match_established_tool_conventions.md` since less is a POSIX-replacement.

### Fixed
- `LESS=NiR less file.txt` (or any `LESS` value without `F`) no longer crashes with `IOException: The handle is invalid` on redirected stdout. Pager now detects non-tty up front via `Pager.SelectDumpStrategy` and dumps content directly to stdout when not a terminal — matches GNU `less` behaviour. Plus belt-and-braces try/catch around the pager loop catches mid-loop terminal failures and falls back to a viewport-preserving direct dump.
- `NO_COLOR` env var, `--color`, and `--no-color` flags are now honoured. Pre-fix all three were silently ignored — ANSI passthrough was unconditional. Now `result.ResolveColor()` drives `LessOptions.StripAnsi`, which strips ANSI escapes from rendered output and the dump path. Matches Winix suite-wide `NO_COLOR` policy and aligns with ripgrep / fd / bat (diverges from GNU less which doesn't honour `NO_COLOR`).
- Bare `-` argument is now accepted as the POSIX explicit-stdin marker. Pre-fix ShellKit consumed `-` as an unknown short option and failed with exit 125. `less -` and `less - file.txt` both work; explicit `-` wins over the file argument per POSIX precedence.
- Directory path argument now reports "Is a directory" via `IOException` instead of the misleading "File not found".
- File-load now catches `IOException` and `UnauthorizedAccessException` in addition to the previously-only-caught `FileNotFoundException`. Pre-fix a path-too-long, locked-for-exclusive-write file, or permission-denied target crashed with a stack trace. `UnauthorizedAccessException` emits a tool-supplied English message to avoid leaking SR resource keys under InvariantGlobalization.
- `LESS=` (empty) now correctly disables all defaults. Pre-fix code conflated null and empty via `string.IsNullOrEmpty`, so the documented "set LESS= explicitly to empty to disable defaults" contract was unreachable.
- Console-handle lifecycle cleanup hardened across `Pager`, `Screen`, and `ConsoleInput.ReattachUnix`. `Pager.Run` now catches both `IOException` AND `InvalidOperationException` (the latter fires when `Console.ReadKey` runs after a failed reattach — common on `git diff | less +F`). `Screen.Dispose` guards each terminal write against `IOException` so unwind doesn't compound the original failure. `ConsoleInput.ReattachUnix` narrows from a bare `catch { }` to typed catches with a one-shot stderr diagnostic. The crash-fallback `DumpFromViewport` preserves the user's current viewport position rather than re-emitting content scrolled past.
- `--version` output no longer carries the `+gitsha` SourceLink suffix. Users see plain `less 0.3.0`, matching the suite-wide convention.

### Added
- Library seam `Winix.Less.Cli.Run` for orchestration testing without process spawning or entering the interactive Pager.Run loop.
- `Pager.DumpFromViewport(lines, startIndex)` internal helper for the crash-fallback path; `DumpAllLines` delegates to `DumpFromViewport(lines, 0)` for the small-content path.

See full changelog at https://github.com/Yortw/winix/blob/main/src/less/CHANGELOG.md