Winix.Man 0.3.0

Prefix Reserved
dotnet tool install --global Winix.Man --version 0.3.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.Man --version 0.3.0
                    
This package contains a .NET tool you can call from the shell/command line.
#tool dotnet:?package=Winix.Man&version=0.3.0
                    
nuke :add-package Winix.Man --version 0.3.0
                    

man

Cross-platform man page viewer with colour, clickable hyperlinks, and pager support. Renders groff man pages natively on any platform.

man replacement (and works on Windows too, where no man command exists).

Install

Scoop (Windows)

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

Winget (Windows, stable releases)

winget install Winix.Man

.NET Tool (cross-platform)

dotnet tool install -g Winix.Man

Direct Download

Download native binaries from GitHub Releases.

Usage

man [options] [section] <name>

Locates and renders a man page for the specified command or topic. Searches bundled pages first, then MANPATH, then system-detected man page locations.

Examples

# View a man page
man timeit

# View a man page from a specific section
man 3 printf

# Show the path to the man page file instead of rendering it
man --path timeit

# Show all search directories in order
man --manpath

# Render without opening a pager
man --no-pager timeit

# Force colour output in a pipe
man --color timeit | cat

# Get machine-readable page metadata as JSON
man --json timeit

# AI agent metadata
man --describe

Options

Option Description
--no-pager Print output directly to stdout instead of opening a pager
--color Force coloured output (overrides NO_COLOR)
--no-color Disable coloured output
--width N Override output width in columns (must be ≥ 10). Without --width, the value comes from $MANWIDTH if set, otherwise terminal width capped at 80 columns (matches GNU man-db's effective behaviour via groff).
-w, --path Print the path to the man page file and exit (do not render)
--where Alias for --path
--manpath Print the list of man page search directories and exit
--json Write a JSON summary of the page metadata to stdout and exit
--describe Print machine-readable metadata (flags, examples, composability) and exit
--version Show version
-h, --help Show help

Page Discovery

Man pages are located in the following order:

  1. Bundled — pages shipped with Winix tools (always available, cross-platform)
  2. MANPATH — directories listed in the MANPATH environment variable
  3. Auto-detected — standard system locations (/usr/share/man, /usr/local/share/man, etc. on Linux/macOS; no system locations on Windows)

Section numbers work as on POSIX: man 3 printf searches section 3 directories (man3/) within each search root.

Use man --manpath to inspect the effective search path.

Exit Codes

Code Meaning
0 Man page found and rendered (or --path/--manpath output printed)
1 Man page not found
2 Usage error (bad arguments)
125 Internal error

Environment Variables

Variable Effect
MANPATH Extra search directories for man pages, prepended to auto-detected locations. Separator: : on Linux/macOS, ; on Windows.
MANWIDTH Render width when --width is not given (must be ≥ 10). Otherwise the default is terminal width capped at 80 columns.
MANPAGER Pager command to invoke. May be a bare executable (less) or a shell command line with arguments (less -R); value is passed to sh -c (Unix) or cmd /c (Windows).
PAGER Fallback pager command consulted when MANPAGER is unset. Same parsing rules as MANPAGER.
NO_COLOR Disables coloured output and clickable hyperlinks (no-color.org). Overridden by --color.

Pager resolution order: $MANPAGER$PAGER → sibling less/less.exe in the same directory as manless found on PATH → built-in minimal pager.

Colour

  • Automatic: colour when outputting to a terminal, plain when piped
  • --color forces colour on (overrides NO_COLOR)
  • --no-color forces colour off
  • Respects the NO_COLOR environment variable (no-color.org)
  • Section headings, bold, and underline formatting from the man page source are rendered using ANSI escape codes when colour is active
  • Clickable hyperlinks (OSC 8) are emitted for URLs found in the page when colour is active

Part of Winix

man 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.3.0 91 5/26/2026
0.3.0-rc2 103 5/10/2026
0.2.0 108 4/16/2026
0.2.0-test4 106 4/15/2026

## [0.3.0] - 2026-05-10

### Changed
- `--json` output now goes to **stdout** (was stderr) per suite convention. This is a pipeline contract change — `man --json ls | jq` now works without `2>&1` redirection. Matches `winix`, `whoholds`, `treex`, `files`, and the rest of the suite's structured-output tools.
- `--path` / `--where` now reports paths that pass a structural validity check, slightly stronger contract than "first matching path on MANPATH". Truncated installs, AV quarantine stubs, and OneDrive placeholder files no longer surface as the canonical location for a page.
- Exit codes now follow the documented contract: usage errors exit **2**, the tool's own internal failures exit **125**. Pre-fix several paths returned generic non-zero codes that conflated user error with internal error.

### Fixed
- `--json` output now escapes ASCII control characters (`0x00`–`0x1F`) per RFC 8259 §7. Pre-fix a page containing a literal control character in its name or rendered content produced invalid JSON that broke downstream parsers.
- Corrupt or truncated gzip-compressed pages no longer crash with a stack trace. The decompression and read pipeline catches `IOException` and the gzip framing exceptions, surfacing a friendly `man: corrupt or unreadable page: <path>` diagnostic and exit 125.
- External pager dispatch now honours compound `MANPAGER` / `PAGER` values like `less -R` or `less -FX`. Pre-fix the value was passed as the literal program name and the spawn failed silently; now compound values dispatch via `cmd /c $PAGER` on Windows and `sh -c $PAGER` on POSIX.
- External pager dispatch now checks the child process's exit code, not just whether `Process.Start` succeeded. After the shell-dispatch fix above, a missing pager binary let the shell start cleanly and exit with 9009 (cmd) or 127 (sh) — `man` returned exit 0 with the user seeing only the shell's "not recognized" diagnostic and an empty page. Now any non-zero exit other than 130 (SIGINT user-quit) triggers fallback to the built-in pager with a stderr warning.
- Bundled-pages discovery now also looks at `<exeDir>/share/man` (was just `<exeDir>/man`). The published layout uses the POSIX `share/man/man1/` convention, so the bundled fallback was unreachable for AOT installs and Scoop-deployed binaries.
- Permission-denied page no longer leaks a stack trace and a framework SR resource-key message. `UnauthorizedAccessException` is now caught alongside `IOException` in the read pipeline and surfaced as `man: permission denied: <path>`.
- `PageDiscovery.FindInSection` no longer silently shadows valid pages with corrupt or non-groff files in higher-priority MANPATH roots. A truncated bundled install, AV quarantine stub, or OneDrive placeholder ahead of the real page would render as garbage with exit 0. Added a cheap structural peek (`LooksLikeManPage`) that requires at least one groff macro line in the first 64 lines; falls through to the next candidate when the structural check fails.
- When every candidate file for a page fails the structural check (a corrupt `.gz` that decompresses to non-groff text is the canonical reproducer), `man` now emits `found N candidate file(s) for X but none appear to be valid groff man pages` plus the rejected paths and exits 125. Pre-fix this was indistinguishable from "no manual entry" exit 1, leaving users to guess whether the page was missing or broken.
- `--version` output no longer carries the `+gitsha` SourceLink suffix. Users see plain `man 0.3.0`, matching the suite-wide convention.

### Internal
- `Cli.Run` library seam extracted, enabling orchestration testing without process spawning.
- Pure helpers (`ResolveWidth`, `EscapeJsonString`, `ResolveExternalPager`) lifted into `Winix.Man` for direct unit-testing.
- `PageDiscovery.LastRejectedPaths` rejection-tracking field added so the orchestration layer can distinguish "no candidate" from "every candidate rejected".

### Documentation
- `MANPAGER`, `PAGER`, and `MANWIDTH` environment variables are now documented in `man.1` and the README.
- MANPATH separator description corrected — it is platform-dependent (`;` on Windows, `:` on POSIX), not "colon-separated".
- `--width` default documented accurately as "terminal width capped at 80" (was vaguely "terminal width").
- `man.1` regenerated from `man.1.md` to reflect all of the above.
- AI agent guide JSON-routing fields and MANPATH section corrected to match the actual emitter.

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