Winix.TreeX
0.3.0
Prefix Reserved
dotnet tool install --global Winix.TreeX --version 0.3.0
dotnet new tool-manifest
dotnet tool install --local Winix.TreeX --version 0.3.0
#tool dotnet:?package=Winix.TreeX&version=0.3.0
nuke :add-package Winix.TreeX --version 0.3.0
treex
Enhanced directory tree with colour, filtering, size rollups, gitignore, and clickable hyperlinks. Cross-platform tree replacement.
tree replacement (and works on Linux/macOS too).
Install
Scoop (Windows)
scoop bucket add winix https://github.com/Yortw/winix
scoop install winix/treex
Winget (Windows, stable releases)
winget install Winix.TreeX
.NET Tool (cross-platform)
dotnet tool install -g Winix.TreeX
Direct Download
Download native binaries from GitHub Releases.
Usage
treex [options] [paths...]
Walks one or more directories (default: .) and renders a tree-formatted listing to stdout. Filters, sizes, and sort order are all configurable.
Examples
# Basic tree of current directory
treex
# Show only C# files with their ancestor directories
treex src --ext cs
# Clean tree with sizes, skipping hidden and gitignored
treex --size --gitignore --no-hidden
# Sort by size to find the largest files
treex --size --sort size
# Limit to 2 levels deep
treex -d 2
# Multiple roots
treex src tests
# Structured output (NDJSON to stdout)
treex --ndjson | jq '.name'
# AI agent metadata
treex --describe
Options
| Option | Description |
|---|---|
-g, --glob PATTERN |
Match filenames against glob pattern (repeatable) |
-e, --regex PATTERN |
Match filenames against regex (repeatable) |
--ext EXT |
Match file extension, e.g. cs, log (repeatable) |
-t, --type TYPE |
Filter by type: f (file), d (directory), l (symlink) |
--min-size SIZE |
Minimum file size (e.g. 100k, 10M, 1G) |
--max-size SIZE |
Maximum file size (e.g. 100k, 10M) |
--newer DURATION |
Modified within duration (e.g. 1h, 30m, 7d) |
--older DURATION |
Not modified within duration (e.g. 1h, 7d) |
-d, --max-depth N |
Maximum directory depth (0-based; 0 = root only, 1 = root + immediate children, N = root + N levels of children) |
--no-hidden |
Skip hidden files and directories |
--gitignore |
Respect .gitignore rules |
-i, --ignore-case |
Case-insensitive matching |
--case-sensitive |
Case-sensitive matching |
-s, --size |
Show file sizes |
--date |
Show last-modified dates |
--sort MODE |
Sort: name (default), size, modified |
-D, --dirs-only |
Show only directories |
--no-links |
Disable clickable terminal hyperlinks |
--ndjson |
Streaming NDJSON to stdout (one JSON object per node) |
--json |
JSON envelope to stdout on exit (suite convention; pipe-friendly for jq) |
--describe |
Print machine-readable metadata (flags, examples, composability) and exit |
--no-color |
Disable colored output |
--color |
Force colored output |
--version |
Show version |
-h, --help |
Show help |
Size Units
--min-size and --max-size accept values with optional unit suffix: b (bytes), k (kilobytes, 1024), M (megabytes), G (gigabytes). No suffix = bytes. Examples: 500, 10k, 2M, 1G.
Duration Units
--newer and --older accept a duration: a number followed by s (seconds), m (minutes), h (hours), d (days), w (weeks). Examples: 30m, 1h, 7d.
Differences from tree
| Behaviour | tree | treex |
|---|---|---|
| Windows availability | Yes (DOS-era, limited) | Yes |
| Linux/macOS | Varies by distro | Yes |
| Colour | No (Windows) / Basic (Linux) | Full ANSI |
| Clickable hyperlinks | No | Yes (when terminal supports) |
| Filter by name/ext | No | --glob, --ext, --regex |
| Filter by size | No | --min-size / --max-size |
| Filter by date | No | --newer / --older |
| Respect .gitignore | No | --gitignore |
| Skip hidden | No | --no-hidden |
| Size rollups | No | --size |
| JSON output | No | --ndjson / --json |
| Sort order | name only by default (most builds) |
name, size, modified |
Exit Codes
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | Runtime error (permission denied, invalid path, or partial walk where one or more directories could not be enumerated) |
| 125 | Usage error (bad arguments) |
When a walk encounters unreadable directories (e.g. a chmod-denied subdirectory), each unreadable path is reported on stderr (treex: <path>: <reason>), the rendered tree marks the directory with [error opening dir], and the process exits 1 with exit_reason: walk_error_partial in the --json envelope. This matches tree(1) behaviour.
Structured Output
treex supports two machine-readable output modes, both on stdout per suite convention:
NDJSON (--ndjson) — one JSON object per tree node, suitable for streaming consumers and jq:
| Field | Type | Description |
|---|---|---|
path |
string | File path relative to the search root (forward-slash separated) |
name |
string | Filename only |
type |
string | file, dir, or link |
size_bytes |
int | null | File size in bytes; null for directories without --size rollup |
modified |
string | ISO 8601 timestamp |
depth |
int | Depth relative to root (0 = root, 1 = immediate child) |
JSON envelope (--json) — single summary object emitted after the walk completes:
| Field | Type | Description |
|---|---|---|
tool |
string | "treex" |
version |
string | Tool version |
exit_code |
int | Process exit code |
exit_reason |
string | Machine-readable reason (success, walk_error_partial, path_not_found, not_a_directory, usage_error, runtime_error) |
directories |
int | Number of directories walked (excluding root) |
files |
int | Number of files walked |
total_size_bytes |
int | Sum of file sizes (only present when --size is on) |
walk_errors |
array | Paths that could not be read during the walk; each entry has path and reason. Always present (empty array on success). |
error |
string | Human-readable failure reason (only present on pre-walk error envelopes — path_not_found, not_a_directory) |
Colour
- Automatic: colour when outputting to a terminal, plain when piped
--colorforces colour on (overridesNO_COLOR)--no-colorforces colour off- Respects the
NO_COLORenvironment variable (no-color.org) - Clickable hyperlinks are disabled when colour is off (e.g.
--no-color, piped output) or when--no-linksis passed
Part of Winix
treex is part of the Winix CLI toolkit.
| Product | Versions 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. |
This package has no dependencies.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.3.0 | 90 | 5/26/2026 |
| 0.3.0-rc2 | 92 | 5/10/2026 |
| 0.2.0 | 112 | 4/16/2026 |
| 0.2.0-test4 | 98 | 4/15/2026 |
| 0.1.0 | 118 | 4/2/2026 |
## [0.3.0] - 2026-05-09
### Changed (BREAKING)
- `--max-depth N` semantics: now `depth ≤ N` with the search root at depth 0. `--max-depth 0` shows only the root; `--max-depth 1` shows root + immediate children. Previously `--max-depth 0` showed root + immediate children, contradicting the documented contract. Migration: subtract 1 from any existing `--max-depth` values.
### Changed
- `--ndjson` records no longer include envelope fields (`tool`, `version`, `exit_code`, `exit_reason`). Stream-level metadata is now emitted only via `--json`. Each NDJSON line carries only per-record fields (`path`, `name`, `type`, `size_bytes`, `modified`, `depth`) — ~35% bandwidth reduction on small trees, line-record convention compliance.
- `size_bytes` field now emits JSON `null` for directories without `--size` rollup (was the sentinel `-1`).
- `--json` output now goes to **stdout** (was stderr) per suite convention. Pipe-friendly for `jq` and matches `man`, `winix`, `whoholds`.
- `--older DURATION` description updated from "Modified before duration" to "Not modified within duration" — same behaviour, clearer phrasing.
### Fixed
- `--size --ndjson` now reports correct `total_size_bytes` in the summary envelope. Previously the NDJSON branch never accumulated sizes, so the summary always reported `0` regardless of actual file sizes.
- Walk errors (permission denied, vanishing directories, I/O failures) are now surfaced to stderr and the process exits 1 with `exit_reason: walk_error_partial`. Previously these were silently swallowed and the partial tree shipped with exit 0, contradicting the documented exit-1 contract.
- Directories that cannot be enumerated are now annotated with `[error opening dir]` in the rendered tree, matching `tree(1)` precedent.
- `--describe` JSON field schema for `size_bytes` updated from `int` to `int|null` to match the post-fix emitter.
- Path that exists but is a regular file (not a directory) now reports "not a directory" instead of the misleading "path not found", and surfaces `exit_reason: not_a_directory` in the `--json` envelope.
- `--version` output no longer carries the `+gitsha` SourceLink suffix. Users see plain `treex 0.3.0`, matching the suite-wide convention.
### Added
- Library seam `Winix.TreeX.Cli.Run` for orchestration testing without process spawning.
- `--json` pre-walk error envelopes (path_not_found, not_a_directory) now carry an `error` field with the human-readable failure detail (in addition to the machine-readable `exit_reason`).
- `--json` summary envelope now includes a `walk_errors` array enumerating directories and files that could not be read during the walk. Each entry is `{"path": "...", "reason": "..."}`. Always present (empty array on success); non-empty when `exit_reason: "walk_error_partial"`. Closes the JSON-consumer blind spot where the prior envelope reported `exit_reason: walk_error_partial` but didn't enumerate which paths failed.
See full changelog at https://github.com/Yortw/winix/blob/main/src/treex/CHANGELOG.md