Winix.Files
0.3.0
Prefix Reserved
dotnet tool install --global Winix.Files --version 0.3.0
dotnet new tool-manifest
dotnet tool install --local Winix.Files --version 0.3.0
#tool dotnet:?package=Winix.Files&version=0.3.0
nuke :add-package Winix.Files --version 0.3.0
files
Find files by name, size, date, type, and content. Cross-platform find replacement with glob patterns, text/binary detection, JSON output, and AI discoverability.
find replacement (and works on Linux/macOS too).
Install
Scoop (Windows)
scoop bucket add winix https://github.com/Yortw/winix
scoop install winix/files
Winget (Windows, stable releases)
winget install Winix.Files
.NET Tool (cross-platform)
dotnet tool install -g Winix.Files
Direct Download
Download native binaries from GitHub Releases.
Usage
files [options] [paths...]
Walks one or more directories (default: .) and prints matching file paths to stdout — one per line by default.
Examples
# Find C# files
files src --ext cs
# Find text files only (skip binaries)
files . --text --type f
# Recently modified files
files . --newer 1h --type f
# Detailed listing (path, size, date, type)
files . --long --ext cs
# fd-style: skip hidden and gitignored, source files only
files . --gitignore --no-hidden --ext cs
# Compose with wargs to act on results
files . --glob '*.log' | wargs rm
# Structured output (NDJSON to stdout)
files . --ndjson | jq '.name'
# JSON envelope to stdout (suite convention)
files . --ext cs --json | jq .count
# AI agent metadata
files --describe
# Files between 1 KB and 10 MB
files . --min-size 1k --max-size 10M
# Find files modified more than 7 days ago
files . --older 7d --type f
# Absolute paths (useful in scripts)
files src --absolute --ext cs
# Null-delimited output (safe for filenames with spaces)
files . --glob '*.log' --print0 | xargs -0 rm
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) |
--text |
Only text files |
--binary |
Only binary files |
--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 = search root only, 1 = root + immediate children, N = root + N levels of children) |
-L, --follow |
Follow symlinks |
--absolute |
Output absolute paths |
--no-hidden |
Skip hidden files and directories |
--gitignore |
Respect .gitignore rules |
-i, --ignore-case |
Case-insensitive matching |
--case-sensitive |
Case-sensitive matching |
-l, --long |
Tab-delimited detail output (path, size, date, type) |
-0, --print0 |
Null-delimited output (for xargs -0) |
--ndjson |
Streaming NDJSON to stdout (one JSON object per file) |
--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: 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 find
| Behaviour | find | files |
|---|---|---|
| Default path | Required | . (current directory) |
| Name matching | -name '*.cs' |
--glob '*.cs' or --ext cs |
| Regex | -regex (anchored, varies by OS) |
--regex (filename only) |
| Type filter | -type f/d/l |
--type f/d/l (same) |
| Max depth | -maxdepth N (root + N levels of children) |
--max-depth N (same; 0-based, matches find post-v0.3.0) |
| Newer than | -newer <file> |
--newer 1h (duration-based) |
| Size filter | -size +1M |
--min-size 1M / --max-size 1M |
| Skip hidden | No built-in | --no-hidden |
| Respect .gitignore | No | --gitignore |
| Text/binary filter | No | --text / --binary |
| JSON output | No | --ndjson / --json (both stdout, pipe-friendly) |
| Windows | Not available | Yes |
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 (files: <path>: <reason>) and the process exits 1 with exit_reason: walk_error_partial in the --json envelope, plus a populated walk_errors[] array.
Structured Output
files supports two machine-readable output modes, both on stdout per suite convention:
NDJSON (--ndjson) — one JSON object per matching entry:
| Field | Type | Description |
|---|---|---|
path |
string | File path (relative or absolute per --absolute) |
name |
string | Filename only |
type |
string | file, directory, or symlink |
size_bytes |
int | null | File size in bytes; null for directory entries |
modified |
string | null | ISO 8601 timestamp; null when not populated |
depth |
int | Depth relative to search root (0 = root) |
is_text |
bool? | true/false; only present when --text or --binary is used |
JSON envelope (--json) — single summary object emitted after the walk completes:
| Field | Type | Description |
|---|---|---|
tool |
string | "files" |
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) |
count |
int | Number of entries emitted |
searched_roots |
array | Root paths that were walked (empty array on pre-walk error envelopes) |
walk_errors |
array | Paths that could not be read; 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)
Part of Winix
files 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 | 96 | 5/26/2026 |
| 0.3.0-rc2 | 99 | 5/10/2026 |
| 0.2.0 | 116 | 4/16/2026 |
| 0.2.0-test4 | 102 | 4/15/2026 |
| 0.1.0 | 112 | 4/2/2026 |
## [0.3.0] - 2026-05-09
### Changed (BREAKING)
- `--max-depth N` semantics: now matches GNU `find -maxdepth` (search root yielded as depth 0; `--max-depth 0` emits only the root path; `--max-depth N` includes up to N levels of children below the root). Pre-fix the search root was never yielded and `--max-depth 0` showed immediate children. 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`, optional `is_text`).
- `size_bytes` field now emits JSON `null` for directory entries (was the sentinel `-1`).
- `modified` field now emits JSON `null` when not populated (was the zero-value timestamp `"0001-01-01T00:00:00.0000000+00:00"`).
- `--json` output now goes to **stdout** (was stderr) per suite convention. Pipe-friendly for `jq`; matches `man`, `winix`, `whoholds`, `treex`.
- `--older DURATION` description updated from "Modified before duration" to "Not modified within duration" — same behaviour, clearer phrasing.
### Fixed
- 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` plus a populated `walk_errors[]` array on the `--json` envelope. Previously these were silently swallowed and the partial result list shipped with exit 0, contradicting the documented exit-1 contract.
- 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.
- Top-level `catch (Exception ex)` narrowed to specific types (`RegexParseException` → 125, `UnauthorizedAccessException` / `IOException` → 1). Pre-fix the bare catch piped framework `ex.Message` to user output, which under `InvariantGlobalization=true` could leak SR resource keys per `feedback_invariant_globalization_resource_keys.md`.
- Symlink cycle detection no longer falls back to the unresolved `Path.GetFullPath` on `ResolveLinkTarget` failure — the bare-catch fallback defeated cycle detection on the very directory whose target couldn't be resolved. Now records the failure as a walk error and skips recursion.
- `--version` output no longer carries the `+gitsha` SourceLink suffix the .NET SDK appends by default. Users see plain `files 0.3.0`, matching the suite-wide convention.
### Added
- Library seam `Winix.Files.Cli.Run` for orchestration testing without process spawning.
- `--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"`.
- `--json` pre-walk error envelopes (`path_not_found`, `not_a_directory`) now carry an `error` field with the human-readable failure detail, plus empty `searched_roots` and `walk_errors` arrays for shape parity with success envelopes.
- `peep` added to the man page SEE ALSO list (composes with `files` for periodic file-system polling).
See full changelog at https://github.com/Yortw/winix/blob/main/src/files/CHANGELOG.md