Winix.Files 0.3.0

Prefix Reserved
dotnet tool install --global Winix.Files --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.Files --version 0.3.0
                    
This package contains a .NET tool you can call from the shell/command line.
#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
  • --color forces colour on (overrides NO_COLOR)
  • --no-color forces colour off
  • Respects the NO_COLOR environment variable (no-color.org)

Part of Winix

files 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 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