- Completed: 47/47 utilities - ALL IMPLEMENTED
- Utilities: basename, cat, chmod, chown, cp, cut, date, dd, df, dirname, du, echo, env, false, find, free, grep, head, id, ln, ls, mkdir, mktemp, mv, nl, printf, pwd, readlink, realpath, rm, rmdir, seq, sleep, sort, stat, tac, tail, tee, test, timeout, touch, tr, true, uniq, wc, whoami, yes
- Flag coverage: 288/288 MUST, 220/220 SHOULD (100%)
- Compatibility: 90-100% GNU feature coverage for completed utilities
- Infrastructure: justfile build system, CI/CD, privileged testing, writer-based I/O, Zig 0.16.0, 7 shared common modules (time, path, glob, prompt, format, file_ops, lib color detection)
- Packaging: Homebrew tap, Nix flake with Cachix binary cache (4 platforms), GitHub release binaries
- Documentation: Claude Code quality check (/qc), man page style guide, testing strategy, CHANGELOG.md
- Enable
scripts/tiger-check.shin CI after the Tiger Style migration is finished (Phases 3-6 indocs/tiger-style-review/README.md). Add a CI job runningscripts/tiger-check.sh --base origin/mainto gate PRs on NEW Tiger Style violations. Deferred deliberately: the pre-commit hook already blocks NEW violations locally, and we want builds green through the migration before enforcing in CI (the tree still carries ~3442 pre-existing violations;--baseonly fails on newly introduced ones, but enable CI once the debt is burned down by the function-length, assertion, and cleanup phases).
- Balance: 80% of GNU's usefulness with 20% of the complexity
- High test coverage: 90%+ with TDD approach
- Modern enhancements: Colors, icons, smart formatting, performance
- OpenBSD-inspired: Clear options, concise man pages with examples
- Practical compatibility: Features people actually use
For each of the 37 POSIX utilities, download the OPTIONS
section from
pubs.opengroup.org/onlinepubs/9699919799/utilities/<util>.html
and save as docs/specs/<util>-posix.txt. Plain text,
flags and descriptions only.
37 POSIX utilities: basename, cat, chmod, chown, cp, cut, date, dd, df, dirname, du, echo, env, false, find, grep, head, id, ln, ls, mkdir, mv, nl, printf, pwd, rm, rmdir, sleep, sort, tail, tee, test, touch, tr, true, uniq, wc
Skip (non-POSIX): free, mktemp, readlink, realpath, seq, stat, tac, timeout, whoami, yes
For each of the 47 utilities, capture four sources:
- macOS man page + help: Parse flags from
MANPATH=/usr/share/man man <util>, then append/bin/<util> --help(or-h) output under a--- --help output ---separator. Save todocs/specs/<util>-macos.txt. - OpenBSD man page: Fetch from
man.openbsd.org/<util>.1and save todocs/specs/<util>-openbsd.txt. - GNU coreutils: Run
g<util> --help(from Nix coreutils package), save todocs/specs/<util>-gnu.txt. - Our flags: Run
./zig-out/bin/<util> --help, save todocs/specs/<util>-vibeutils.txt.
Create docs/specs/<util>-flags.md for each utility with
a flags table:
| Flag | POSIX | macOS | OpenBSD | GNU | Ours | Tier |
|------|-------|-------|---------|-----|------|------|
| -a | yes | yes | yes | yes | yes | MUST |
| -X | no | yes | no | yes | no | SHOULD |
| -Z | no | no | no | yes | no | WONT |
Auto-assign tiers:
- MUST: All POSIX-required flags (target 100%)
- MUST: All flags present in both macOS AND OpenBSD (platform parity baseline)
Prompt user for remaining flags: Use AskUserQuestion menu per utility to decide tier for flags that are GNU-only, macOS-only, or OpenBSD-only. Present the flag, its description, and which sources have it. Let user pick SHOULD or WONT.
Tiers:
- MUST: POSIX-required + macOS/OpenBSD parity
- SHOULD: User-approved GNU/platform flags
- WONT: Rare/legacy flags (document rationale)
Write bash scripts in tests/posix/, one per utility
(e.g., tests/posix/test_cp.sh). Each script:
- Validates every MUST and SHOULD flag is accepted
- Tests correct behavior against expected output
- Does NOT compare against GNU coreutils directly
- Run via
just test-posix
For each utility:
- Red: Write failing tests for basic functionality
- Green: Implement minimal code to pass tests
- Refactor: Improve code quality while keeping tests green
- Repeat: Add more test cases for edge cases and flags
- Test: Basic text output
- Test: No newline flag (-n)
- Test: Escape sequences (-e)
- Test: Multiple arguments
- Test: Empty input
- Test: Combined flags (-en, -ne)
- Test: Octal sequences (\101)
- Test: Hex sequences (\x41)
- Implement: Basic functionality
- Implement: Flag parsing
- Implement: Escape sequence handling
- Implement: --help and --version flags
- Man page: Write concise man page with examples
- Test: -E flag disables escapes even after -e
- Test: -E flag behavior in combined flags
- Implement: -E flag to explicitly disable escape sequences
- Test: Single file reading
- Test: Multiple files concatenation
- Test: STDIN reading
- Test: Line numbering (-n)
- Test: Show ends (-E)
- Test: Show tabs (-T)
- Test: Non-existent file error
- Test: Number non-blank lines (-b)
- Test: Squeeze blank lines (-s)
- Test: Show non-printing (-v)
- Implement: Basic file reading
- Implement: STDIN support
- Implement: Line numbering
- Implement: Special character display
- Man page: Write concise man page with examples
- Test: -A flag combines -vET behavior
- Test: -e flag combines -vE behavior
- Test: -t flag combines -vT behavior
- Test: -u flag is silently ignored
- Test: -A with control characters
- Implement: -A (--show-all) combination flag
- Implement: -e combination flag
- Implement: -t combination flag
- Implement: -u flag (no-op for POSIX)
- Implement: Long option support (--show-all already works)
- Test: Basic directory listing
- Test: Hidden files (-a)
- Test: One file per line (-1)
- Test: Alphabetical sorting
- Test: Empty directory handling
- Test: Mixed files and directories
- Implement: Basic listing
- Implement: Directory iteration
- Implement: Hidden file filtering
- Implement: Alphabetical sorting
- Man page: Write concise man page with examples
- Test: Long format (-l) with permissions, size, date
- Test: stat() wrapper for file attributes
- Test: Permission string formatting (e.g., -rw-r--r--)
- Test: Human readable sizes (-h) with K/M/G/T
- Test: Kilobyte sizes (-k) always in 1K blocks
- Test: Show all files (-a) including . and ..
- Test: Almost all (-A) excluding . and ..
- Implement: stat() wrapper in common library
- Implement: Permission string formatter
- Implement: Size formatters (bytes, human, kilobytes)
- Implement: Date/time formatter (smart: recent vs old)
- Implement: Long format assembly
- Implement: User/group name lookup via C interop
- Implement: Hard link count display
- Implement: Total blocks calculation
- Test: Sort by time (-t) newest first
- Test: Sort by size (-S) largest first
- Test: Reverse sort (-r) for any sorting mode
- Test: File type indicators (-F) /=*@|
- Test: Directory itself (-d) without recursion
- Test: Symlink target display with -l
- Implement: Modular sorting system
- Implement: Time-based comparator
- Implement: Size-based comparator
- Implement: Reverse sort wrapper
- Implement: File type detection and indicators
- Implement: Symlink target reading and display
- Test: Color capability detection (isatty, TERM)
- Test: --color=auto/always/never modes
- Test: Basic color scheme (dirs, executables, symlinks) ✓
- Test: LS_COLORS environment variable parsing ✓
- Test: --group-directories-first option ✓
- Test: Terminal width detection for columns ✓
- Test: Smart column formatting (-C is default) ✓
- Implement: Color system with graceful degradation ✓
- Implement: LS_COLORS parser (simplified) ✓
- Implement: Directory grouping logic ✓
- Implement: Responsive column layout ✓
- Test: Recursive listing (-R) with proper formatting
- Test: Recursive with cycle detection
- Test: Inode display (-i) before filename
- Test: Numeric user/group IDs (-n)
- Test: Comma-separated output (-m)
- Test: Single column force (-1) ✓ already done
- Implement: Recursive directory walker
- Implement: Symlink cycle detection
- Implement: Inode display formatting
- Implement: Comma-separated formatter
- Test: Nerd font icon detection
- Test: Icon mapping for common file types
- Test: Git status integration (modified/new files)
- Test: Smart date formatting ("2 hours ago")
- Implement: Optional icon system
- Implement: Git repository detection
- Implement: Human-friendly date formatting
- SELinux context (-Z, --context) - Too Linux-specific
- Author field (--author) - Nobody uses this
- Emacs dired mode (-D, --dired) - Too niche
- Complex quoting styles - Just escape when needed
- Multiple time formats - One smart format is enough
- Block size gymnastics (--block-size) - Just -h and -k
- All the --indicator-style variants - Just -F
- Explicit --si flag - We use binary (1024) for -h
- Test: Single file copy
- Test: Copy to existing directory
- Test: Error on directory without recursive flag
- Test: Preserve attributes (-p)
- Test: Directory copy (-r)
- Test: Interactive mode (-i)
- Test: Force overwrite (-f)
- Test: Symbolic link handling (-d)
- Test: Error cases (permission denied, disk full)
- Implement: Basic file copying
- Implement: Attribute preservation (mode, timestamps)
- Implement: Copy to directory detection
- Implement: Directory recursion
- Implement: Symlink handling (-d/--no-dereference)
- Man page: Write concise man page with examples
- Test: File rename in same directory
- Test: Move to different directory
- Test: Directory move
- Test: Interactive mode (-i)
- Test: Force mode (-f)
- Test: Cross-filesystem move
- Test: Atomic rename when possible
- Implement: Basic move/rename
- Implement: Cross-filesystem support
- Implement: Directory handling
- Man page: Write concise man page with examples
- Test: Single file removal
- Test: Multiple files
- Test: Directory removal (-r)
- Test: Force mode (-f)
- Test: Interactive mode (-i)
- Test: Write-protected file handling
- Test: Non-existent file behavior
- Implement: Basic removal
- Implement: Recursive removal
- Implement: Safety checks
- Man page: Write concise man page with examples
Phase 1: Basic File Removal
- Test: Basic file removal
- Test: Non-existent file with force
- Test: Multiple file removal
- Test: Directory without recursive flag
- Test: Verbose output
- Implement: Core removal logic
- Implement: Force mode handling
- Implement: Error reporting
Phase 2: Safety and Interaction
- Test: Interactive mode prompts
- Test: Force mode bypasses prompts
- Test: Root directory protection
- Test: Same-file detection (hard links)
- Test: Empty path handling
- Test: Path traversal attack prevention
- Implement: User interaction system
- Implement: Write-protected file prompts
- Implement: Interactive once mode (-I)
- Implement: Critical system path protection
Phase 3: Recursive Directory Operations
- Test: Recursive directory removal
- Test: Deep nested directories
- Test: Symlink handling (don't follow)
- Test: Permission handling with force
- Implement: Depth-first directory traversal
- Implement: Symlink detection
- Implement: Permission modification for force mode
- Implement: Inode tracking for cycles
Phase 4: Advanced Safety Features
- Test: Symlink cycle detection
- Test: Cross-filesystem boundary handling
- Test: Race condition protection
- Implement: Complex symlink cycle detection
- Implement: Device ID tracking for filesystem boundaries
- Implement: Atomic operations using *at() syscalls
- Implement: File descriptor-based removal for TOCTOU protection
- Test: Single directory creation
- Test: Parent creation (-p)
- Test: Mode setting (-m)
- Test: Multiple directories
- Test: Already exists error
- Test: Permission denied
- Implement: Basic mkdir
- Implement: Parent directory creation
- Implement: Permission setting (partial - chmod TODO)
- Man page: Write concise man page with examples
- Test: Empty directory removal
- Test: Non-empty directory error
- Test: Parent removal (-p)
- Test: Multiple directories
- Test: Non-existent directory
- Test: File instead of directory error
- Test: Verbose output (-v)
- Test: Ignore fail on non-empty (--ignore-fail-on-non-empty)
- Test: Parent removal stops on error
- Test: Path traversal protection
- Test: Symbolic link detection
- Test: Unicode path handling
- Test: Long path support
- Test: Memory management (no leaks)
- Test: Progress indicators
- Implement: Basic removal with atomic operations
- Implement: Parent cleanup with ParentIterator (memory-safe)
- Implement: Verbose output with colors
- Implement: --ignore-fail-on-non-empty flag
- Implement: Path validation (traversal, symlinks, system paths)
- Implement: Atomic removal with unlinkat syscall
- Implement: Progress indicators for bulk operations
- Man page: Write concise man page with examples
- Test: Create new file
- Test: Update existing file timestamp
- Test: Specific time (-t)
- Test: Reference file (-r)
- Test: Access time only (-a)
- Test: Modification time only (-m)
- Test: -h/--no-dereference for symlinks
- Test: --time=WORD support
- Test: Multiple files
- Test: -c/--no-create flag
- Test: Timestamp parsing validation
- Test: Error handling
- Test: Pre-1970 date validation
- Implement: File creation
- Implement: Timestamp manipulation
- Implement: Reference file support
- Implement: Atomic operations (no race conditions)
- Implement: Dynamic path allocation
- Implement: Comprehensive error handling
- Man page: Write concise man page with examples
- Test: Basic working directory
- Test: Logical path (-L)
- Test: Physical path (-P)
- Test: Symlink resolution
- Test: PWD environment variable validation
- Test: Flag precedence (last flag wins)
- Test: Security validation with inode comparison
- Test: Output format validation
- Implement: Basic pwd
- Implement: Path resolution options
- Implement: Secure PWD validation using inode comparison
- Implement: Proper error handling with common library
- Implement: GNU/POSIX compliant flag handling
- Man page: Write concise man page with examples
- Test: Basic permission changes (numeric: 755, 644)
- Test: Symbolic mode changes (u+x, g-w, o=r)
- Test: Recursive mode (-R)
- Test: Preserve root (-c, --changes)
- Test: Error handling (permission denied)
- Test: Special bits (setuid, setgid, sticky)
- Implement: Numeric mode parser
- Implement: Symbolic mode parser
- Implement: Recursive directory walker
- Man page: Write concise man page with examples
- Test: Basic ownership change (user:group)
- Test: User only change
- Test: Group only change (:group)
- Test: Recursive mode (-R)
- Test: Dereference/no-dereference (-h, -H, -L, -P)
- Test: From reference file (--reference)
- Implement: User/group parsing
- Implement: Ownership change syscalls
- Implement: Recursive walker with symlink handling
- Man page: Write concise man page with examples
- Test: Create hard link
- Test: Create symbolic link (-s)
- Test: Force overwrite (-f)
- Test: Interactive mode (-i)
- Test: Create links in directory (-t)
- Test: Relative symlinks (--relative)
- Test: Error cases (cross-device hard link)
- Implement: Hard link creation
- Implement: Symbolic link creation
- Implement: Path resolution for relative links
- Implement: Path security validation
- Man page: Write concise man page with examples
- Test: Strip directory from path
- Test: Strip suffix (-s, --suffix)
- Test: Multiple paths (-a, --multiple)
- Test: Zero delimiter (-z, --zero)
- Test: Edge cases (/, //, no slash)
- Implement: Path parsing logic
- Implement: Suffix stripping
- Implement: Multiple file handling
- Man page: Write concise man page with examples
- Test: Extract directory from path
- Test: Multiple paths
- Test: Zero delimiter (-z, --zero)
- Test: Edge cases (/, //, no slash, .)
- Implement: Path parsing logic
- Implement: Multiple path handling
- Man page: Write concise man page with examples
- Test: Sleep for seconds
- Test: Sleep for decimal seconds (0.5)
- Test: Sleep for minutes/hours/days suffix (5m, 2h, 1d)
- Test: Multiple time arguments (sleep 1m 30s)
- Test: Signal handling (interruptible)
- Implement: Time parsing with units
- Implement: High-precision sleep
- Implement: Signal-safe sleep
- Man page: Write concise man page with examples
- Test: Always returns 0 exit code
- Test: Ignores all arguments
- Implement: Minimal implementation
- Man page: Write concise man page
- Test: Always returns 1 exit code
- Test: Ignores all arguments
- Test: Produces no output
- Test: Handles empty arguments array
- Test: Handles many arguments
- Implement: Minimal implementation
- Man page: Write concise man page
- Test: File existence checks (-e, -f, -d, -r, -w, -x)
- Test: String comparisons (=, !=, -z, -n)
- Test: Numeric comparisons (-eq, -ne, -lt, -le, -gt, -ge)
- Test: Logical operators (-a, -o, !)
- Test: Complex expressions with parentheses
- Test: Exit codes (0 for true, 1 for false, 2 for error)
- Test: Both test and [ ] forms
- Test: Terminal tests (-t)
- Test: Special file tests (-p, -S, -b, -c, -L/-h)
- Test: File size test (-s)
- Test: Permission bit tests (-g for setgid)
- Test: Operator precedence and negation
- Test: Error handling for invalid expressions
- Implement: Expression parser with proper precedence
- Implement: File test operations (all POSIX types)
- Implement: String and numeric comparison operations
- Implement: Logical operators with POSIX precedence
- Implement: Parentheses grouping support
- Implement: Both test and [ executable forms
- Man page: Write concise man page with examples
- Test: Display current date/time
- Test: Custom format string (+FORMAT)
- Test: Set date/time (-s, --set)
- Test: Display file's date (-r, --reference)
- Test: UTC mode (-u, --utc)
- Test: RFC formats (--rfc-3339, --rfc-email)
- Test: Relative dates (-d "2 days ago")
- Implement: Format string parser (strftime-like)
- Implement: Date parsing for various formats
- Implement: Relative date calculations
- Man page: Write concise man page with examples
- Test: Print current environment
- Test: Run command with modified env (env VAR=value cmd)
- Test: Clear environment (-i, --ignore-environment)
- Test: Unset variables (-u, --unset)
- Test: Change directory (-C, --chdir)
- Test: Split string arguments (-S)
- Implement: Environment manipulation
- Implement: Command execution with env
- Implement: Argument splitting parser
- Man page: Write concise man page with examples
- Test: Generate sequence (seq 10)
- Test: Start and end (seq 5 10)
- Test: Start, increment, end (seq 1 2 10)
- Test: Floating point sequences (seq 0.1 0.1 1.0)
- Test: Format string (-f "%03g")
- Test: Separator (-s ", ")
- Test: Equal width (-w)
- Implement: Number sequence generation
- Implement: Format string support
- Implement: Width calculation
- Man page: Write concise man page with examples
- Test: Write to stdout and file
- Test: Write to multiple files
- Test: Append mode (-a, --append)
- Test: Ignore interrupts (-i)
- Test: Diagnose write errors (-p)
- Test: Binary data handling
- Implement: Multi-writer system
- Implement: Signal handling
- Implement: Error diagnosis
- Man page: Write concise man page with examples
- Test: Repeat "y" infinitely
- Test: Repeat custom string
- Test: Multiple arguments joined with space
- Test: Performance (must be fast)
- Test: SIGPIPE handling
- Implement: Efficient output loop
- Implement: Buffer optimization
- Implement: Signal handling
- Man page: Write concise man page with examples
- Test: Print effective username
- Test: No options accepted
- Test: Error when can't determine user
- Implement: Get effective user ID
- Implement: User lookup
- Man page: Write concise man page with examples
- Test: Print all IDs (default)
- Test: User ID only (-u, --user)
- Test: Group ID only (-g, --group)
- Test: All group IDs (-G, --groups)
- Test: Names instead of numbers (-n, --name)
- Test: Real instead of effective (-r, --real)
- Test: Different user (id username)
- Implement: ID retrieval syscalls
- Implement: User/group lookups
- Implement: Format selection
- Man page: Write concise man page with examples
- Test: Basic format strings (%s, %d, %f)
- Test: Escape sequences (\n, \t, \x41)
- Test: Width and precision (%.2f, %10s)
- Test: Multiple arguments with reuse
- Test: Octal/hex formats (%o, %x, %X)
- Test: Error handling (type mismatches)
- Implement: Format string parser
- Implement: Type conversions
- Implement: Escape sequence handling
- Man page: Write concise man page with examples
- Test: Basic memory information display (total, used, free, available)
- Test: Human readable format (-h) with K/M/G/T units
- Test: Show swap information (default)
- Test: Hide swap information (-s, --no-swap)
- Test: Continuous monitoring (-c, --count with interval)
- Test: Wide format (-w) for better readability
- Test: Color-coded memory usage levels (green/yellow/red)
- Test: Cross-platform support (Linux /proc/meminfo, macOS vm_stat)
- Implement: Linux memory parsing (/proc/meminfo)
- Implement: macOS memory info via syscalls (host_statistics64)
- Implement: Human-readable size formatting
- Implement: Color-coded output with terminal detection
- Implement: Inline usage bar (parallels df's --bar)
- Implement: Continuous monitoring with refresh
- Man page: Write concise man page with examples
- Test: Basic copy (if=input of=output)
- Test: Block size (bs=1M, ibs=512, obs=4096)
- Test: Count limit (count=100)
- Test: Seek/skip (seek=10, skip=5)
- Test: Conversion (conv=ucase,lcase,notrunc,sync)
- Test: Status output (status=progress)
- Test: Direct I/O (iflag=direct, oflag=direct)
- Implement: Block-based I/O
- Implement: Conversion operations
- Implement: Progress reporting
- Man page: Write concise man page with examples
- Test: Resolve to absolute path
- Test: Canonicalize existing (-e, --canonicalize-existing)
- Test: Canonicalize missing (-m, --canonicalize-missing)
- Test: No symlinks (-s, --strip, --no-symlinks)
- Test: Relative to directory (--relative-to)
- Test: Relative base (--relative-base)
- Implement: Path resolution
- Implement: Symlink following
- Implement: Relative path computation
- Man page: Write concise man page with examples
- Test: Print symlink target
- Test: Canonicalize (-f, --canonicalize)
- Test: Canonicalize existing (-e)
- Test: Canonicalize missing (-m)
- Test: No newline (-n, --no-newline)
- Test: Error on non-symlink
- Implement: Symlink reading
- Implement: Path canonicalization
- Man page: Write concise man page with examples
- Test: Create temporary file
- Test: Create temporary directory (-d, --directory)
- Test: Custom template (mktemp /tmp/test.XXX)
- Test: Dry run (-u, --dry-run)
- Test: Custom tmpdir (--tmpdir)
- Test: Suffix (--suffix=.txt)
- Implement: Secure random name generation
- Implement: Atomic file creation
- Implement: Template parsing
- Man page: Write concise man page with examples
- Test: Basic timeout with seconds (timeout 5 sleep 10)
- Test: Command succeeds before timeout (exit status 0)
- Test: Command killed on timeout (exit status 124)
- Test: Floating point durations (timeout 2.5 sleep 3)
- Test: Time units (5s, 2m, 1h, 0.5d)
- Test: Zero timeout disables (timeout 0 sleep 1)
- Test: Exit status preservation (--preserve-status)
- Test: Kill after timeout (-k 2s kills if TERM ignored)
- Test: Custom signals (-s INT, -s KILL, -s 15)
- Test: Foreground mode (-f) for interactive commands
- Test: Verbose mode (-v) diagnostic output
- Test: Command not found (exit 127)
- Test: Command not executable (exit 126)
- Test: Signal handling (SIGTERM, SIGKILL propagation)
- Test: Child process handling
- Test: Error cases (invalid duration, invalid signal)
- Implement: Duration parser (float + units)
- Implement: Process spawning with exec
- Implement: Timer using setitimer or timerfd
- Implement: Signal management and propagation
- Implement: Foreground TTY handling
- Implement: Exit status handling
- Implement: Verbose diagnostic messages
- Man page: Write concise man page with examples
Why Priority: macOS lacks timeout, causing issues in scripts/CI Key Features: Must support both simple (timeout 5 cmd) and complex (timeout -k 2s -s INT 10s cmd) usage Platform Considerations:
- Linux: Use timerfd_create for precise timing
- macOS/BSD: Use setitimer or kqueue timers
- Signal handling must be robust across platforms
- Test: Reverse file lines
- Test: Multiple files
- Test: Custom separator (-s, --separator)
- Test: Separator before line (-b, --before)
- Test: Regex separator (-r, --regex)
- Test: Large file handling
- Implement: Reverse line reading
- Implement: Memory-efficient algorithm
- Implement: Separator handling
- Man page: Write concise man page with examples
- Test: Number all lines (default)
- Test: Number non-empty lines (-b a, -b t)
- Test: Number format (-n ln, -n rn, -n rz)
- Test: Starting number (-v 100)
- Test: Increment (-i 2)
- Test: Width (-w 4)
- Test: Separator (-s ": ")
- Implement: Line numbering logic
- Implement: Format options
- Implement: Section handling
- Man page: Write concise man page with examples
- Test: Default 10 lines
- Test: Custom line count (-n)
- Test: Byte count (-c)
- Test: Multiple files
- Test: STDIN input
- Test: File headers with multiple files
- Implement: Line-based reading
- Implement: Byte-based reading
- Implement: Multi-file handling
- Man page: Write concise man page with examples
- Test: Default 10 lines
- Test: Custom line count (-n)
- Test: Follow mode (-f)
- Test: Follow mode truncation detection
- Test: Follow retry file rotation (-F)
- Test: Follow retry nonexistent file (-F)
- Test: Follow + reverse mutual exclusion
- Test: Byte count (-c)
- Test: Multiple files
- Test: Reverse line reading
- Test: Zero-terminated lines (-z)
- Test: Files without final newline
- Implement: Efficient line reading from end
- Implement: Follow mode with kqueue (macOS) and inotify (Linux)
- Implement: Follow retry with file rotation detection (-F)
- Implement: Multi-file follow (GNU tail follows all files)
- Implement: CircularLineBuffer for performance
- Implement: Zero-terminated line support
- Implement: Zig 0.15.1 Reader API migration
- Man page: Write concise man page with examples
- Test: Line count (-l)
- Test: Word count (-w)
- Test: Byte count (-c)
- Test: Character count (-m)
- Test: Maximum line length (-L)
- Test: Multiple files
- Test: STDIN input
- Test: Unicode handling
- Test: Default behavior (lines, words, bytes)
- Test: File error handling
- Implement: Efficient counting with streaming
- Implement: Unicode support with proper character counting
- Implement: Performance-optimized byte counting
- Man page: Write concise man page with examples
- Test: Basic alphabetical sort
- Test: Numeric sort (-n)
- Test: Reverse sort (-r)
- Test: Key-based sort (-k)
- Test: Unique sort (-u)
- Test: Case-insensitive (-f)
- Test: Memory limit handling
- Implement: In-memory sorting
- Implement: External merge sort
- Implement: Key extraction
- Man page: Write concise man page with examples
- Test: Remove adjacent duplicates
- Test: Count occurrences (-c)
- Test: Only duplicates (-d)
- Test: Only unique (-u)
- Test: Skip fields (-f)
- Test: Case-insensitive (-i)
- Implement: Line comparison
- Implement: Counting logic
- Implement: Field skipping
- Man page: Write concise man page with examples
- Test: Byte selection (-b)
- Test: Character selection (-c)
- Test: Field selection (-f)
- Test: Delimiter (-d)
- Test: Complement (-c)
- Test: Multiple files
- Implement: Range parsing
- Implement: UTF-8 character handling
- Implement: Field extraction
- Man page: Write concise man page with examples
- Test: Character translation
- Test: Character deletion (-d)
- Test: Squeeze repeats (-s)
- Test: Complement set (-c)
- Test: Character classes [:alpha:]
- Test: Range expansion [a-z]
- Implement: Translation tables
- Implement: Unicode support
- Implement: Character class parsing
- Man page: Write concise man page with examples
- Test: File information display
- Test: Custom format (-c)
- Test: Filesystem info (-f)
- Test: Dereference (-L)
- Test: Terse output (-t)
- Implement: System call wrapper
- Implement: Format string parser
- Implement: Human-readable output
- Man page: Write concise man page with examples
- Test: Directory size calculation
- Test: Human readable (-h)
- Test: Summary only (-s)
- Test: Max depth (-d)
- Test: Exclude patterns
- Test: Hard link handling
- Implement: Directory traversal
- Implement: Size calculation
- Implement: Caching for performance
- Man page: Write concise man page with examples
- Test: Filesystem listing
- Test: Human readable (-h)
- Test: Filesystem type (-t)
- Test: Inode information (-i)
- Test: Mount point resolution
- Implement: Mount point parsing
- Implement: Space calculation
- Implement: Filesystem filtering
- Man page: Write concise man page with examples
- Test: Name matching (-name)
- Test: Type filtering (-type)
- Test: Size filtering (-size)
- Test: Time filtering (-mtime)
- Test: Execution (-exec)
- Test: Logical operators
- Test: Depth control
- Implement: Expression parser
- Implement: Directory walker
- Implement: Action execution
- Man page: Write concise man page with examples
- Test: Basic pattern matching
- Test: Regular expressions (-E)
- Test: Case insensitive (-i)
- Test: Invert match (-v)
- Test: Line numbers (-n)
- Test: Recursive (-r)
- Test: Binary file handling
- Implement: Pattern compilation
- Implement: Line matching
- Implement: Performance optimizations
- Man page: Write concise man page with examples
- Coverage Goals: 90%+ line, 85%+ branch, 100% error paths
- Unit Tests: Individual flags, combinations, edge cases
- Integration Tests: Pipes, signals, GNU compatibility, benchmarks
Goal: Replace zig-clap's 3,000 lines with focused ~400-line library supporting 95% of real usage patterns
Design Philosophy:
- API-first design with type-safe interfaces
- Zero allocations for flag parsing (positionals may allocate)
- Compile-time validation where possible
- Self-documenting through struct field names
- OpenBSD-inspired simplicity with GNU compatibility
- Test: Boolean flag parsing (-h, --help, -v, --verbose)
- Test: Combined short flags (-abc = -a -b -c)
- Test: Unknown flag error handling
- Test: Flag mapping generation from struct reflection
- Test: Memory management (no leaks)
- Implement: Core
ArgParser.parse()function with generic struct support - Implement: Comptime flag mapping using
@typeInfo() - Implement: Boolean flag state management
- Implement: ParseResult with proper cleanup
- Implement: Error types (InvalidArgument, UnknownFlag, MissingValue)
- Test: String option parsing (--color=auto, --output file)
- Test: Both
--option=valueand--option valuesyntax - Test: Missing value error for string options
- Test: Positional argument collection
- Test: GNU
--separator handling - Test: Single
-as positional (stdin convention) - Implement: String option value extraction
- Implement: Two-pass parsing (flags first, then values)
- Implement: Positional argument allocation and management
- Implement: State machine for parsing stages
- Test: Help text parsing from struct
metafield - Test: Automatic help formatting matching GNU style
- Test: Usage line generation with positional indicators
- Test: Option description alignment and formatting
- Test: Integration with existing --help flag patterns
- Implement:
printHelp()function - Implement: Help text parser for embedded descriptions
- Implement: GNU-style help formatting
- Implement: Usage line generation based on struct analysis
- Test: POSIX compliance for argument ordering
- Test: Error message format matching GNU conventions
- Test: Complex combined flags with string options
- Test: Edge cases (empty args, only positionals, only flags)
- Test: Integration with all existing utility patterns
- Implement: Full GNU argument parsing compatibility
- Implement: Comprehensive error reporting
- Implement: Performance optimization (comptime where possible)
- echo: Migrated simplest case (boolean flags only)
- cat: Multiple boolean flags, combination flags (-A, -e, -t)
- ls: Complex case with string options (--color, --time-style)
- cp/mv/rm: Interactive flags and mixed option types
- mkdir/rmdir/touch: Mode settings and timestamp options
- All utilities: Complete migration for all 22 implemented utilities
- Test: Drop-in compatibility with existing utility code
- Test: Performance benchmarks vs zig-clap
- Test: Binary size comparison
- Test: Compile time comparison
- Update: build.zig to remove zig-clap dependency
- Update: build.zig.zon to remove clap entry
- Verify: All existing tests pass with new parser
- Document: API documentation in argparse.zig
Success Criteria:
- Library under 500 lines total (vs 3,000 for zig-clap)
- 95%+ test coverage with embedded tests
- All existing utilities work unchanged
- Argument parsing <1ms for complex cases
- Zero regressions in functionality
- Binary size comparable or smaller than zig-clap
API Design Pattern:
const EchoArgs = struct {
help: bool = false, // -h, --help
version: bool = false, // -V, --version
suppress_newline: bool = false, // -n
positionals: []const []const u8,
pub const help_text =
\\-h, --help Display this help and exit.
\\-V, --version Output version information and exit.
\\-n Do not output the trailing newline.
\\<str>... Text to echo.
;
};
const args = Args.parse(EchoArgs, allocator) catch |err| switch (err) {
error.InvalidArgument => return usage_error(),
else => return err,
};
defer args.deinit(allocator);Implemented idiomatic Zig writer pattern to enable comprehensive testing of stdout/stderr output without hangs or skipped tests.
- Pass writers as parameters (idiomatic Zig pattern) ✓
- Enable full output testing without process-level complexity ✓
- Maintain production behavior while improving testability ✓
- Zero allocation overhead for production code ✓
- Writer parameter pattern implemented across all utilities
- Test infrastructure using buffer writers (std.ArrayList(u8).writer())
- Stdout/stderr isolation in all utilities
- Memory management with proper cleanup patterns
- cat - printVersion, printHelp with writer parameters
- ls - lsMain function accepting writer parameter
- mkdir - runMkdir with stdout/stderr writers
- rmdir - handleError returning !void for proper error propagation
- touch - mainWithWriter accepting both writers
- mv - Complete parameter threading for progress functions
- ln - createSingleLink with writer parameters, test_mode support
- cp - runCp and all sub-modules updated (errors.zig, user_interaction.zig, etc.)
- chmod - printHelp and printVersion updated
- chown - printHelp and printVersion updated
- common/lib.zig - printErrorTo function added
- echo - Already had writer support, updated for consistency
- rm - Already had writer support, maintained
- pwd - Already had writer support, maintained
- Implemented anytype writer compatibility across all utilities
- Verified stdout/stderr isolation in tests
- Removed dead code buffering tests (746 lines of stdlib testing)
- Fixed writer parameter patterns to prevent test stderr pollution
- Consistent runXxx() pattern returning ExitCode
- main() as thin wrapper calling runXxx()
- All output functions accept writer parameters
- Tests use buffer writers for output verification
- Zero test hangs due to stdout buffering
- All utilities use consistent writer pattern
- Full test coverage for output functionality
- No performance regression (verified with timing tests)
- Clear pattern established for future utilities
- Balance OpenBSD clarity with GNU usefulness
- Modern UX improvements (colors, icons, responsive layouts)
- Smart defaults (auto-color, readable dates, parallel I/O)
- Create common library for:
- Error handling (fatal, printError, printWarning, ExitCode)
- Color output (Style with terminal detection)
- Progress indicators (Progress with ETA)
- Version/help support (CommonOpts)
- Advanced argument parsing (using zig-clap)
- File operations helpers (stat wrappers, permission formatting)
- Unified file permissions (file_ops.zig - prevents macOS SIGABRT)
- Size formatters (bytes, -k kilobytes, -h human readable)
- Date/time formatting helpers (smart recent vs old)
- User/group name lookup (getpwuid/getgrgid via C interop)
- CI environment detection (isRunningInCI, shouldSkipMacOSCITest)
- Internal stderr color detection (stderrSupportsColor in lib.zig — removed ~500 hardcoded isTty calls)
- C time bindings (time.zig — c_tm, localtime_r, gmtime_r, strftime, mktime, TimeUnit, parseTimeString)
- Path canonicalization (path.zig — canonicalizeMissing for non-existent path components)
- Glob matching (glob.zig — globMatch/globMatchInsensitive with bracket expressions)
- Interactive prompts (prompt.zig — promptYesNo for cp/mv/rm confirmation)
- Human-readable formatting (format.zig — formatHumanReadable with SI/IEC suffixes, parseBlockSize)
- File content copying (file_ops.zig — copyFileContents, isSameFile)
- Terminal width detection for responsive layouts
- Parallel I/O utilities for performance
- Set up build.zig
- Configure test runner
- Common library module system
- Integrate zig-clap dependency
- Basic Makefile for common tasks (removed in 0.8.0)
- justfile migration: Full parity with Makefile, now sole task runner
- Security fixes: Replace fragile version parsing with safe ZON parser
- Modular architecture: Metadata-driven utility configuration in build/utils.zig
- Memory management: Fix memory leaks and add proper cleanup
- Error handling: Replace @panic() calls with graceful error returns
- Test coverage: Comprehensive unit tests for build system functions
- Code quality: Pre-commit hook for automatic formatting and testing
- Coverage system: Removed non-functional coverage system (Zig 0.15.1 lacks native coverage)
- CI/CD pipeline: GitHub Actions workflows for cross-platform testing
- Multi-platform releases: GitHub Actions matrix build (linux arm64/amd64, darwin arm64/amd64)
- Release automation: release.sh extracts notes from CHANGELOG.md and updates GitHub release
- Cachix binary cache: Explicit push via
nix build --print-out-paths | cachix push - Weekly flake update: CI updates flake.lock and pushes fresh builds to Cachix
- Add install targets for man pages
- Man page style guide (OpenBSD-inspired):
- Concise DESCRIPTION
- Clear OPTIONS section
- 2-3 practical EXAMPLES
- Brief SEE ALSO
- No verbose explanations
- Help text standardization (via --help flag)
- Help text consistency test (automated checks across all utilities)
- Man page standardization (mdoc format, consistent sections across 48 pages)
- Design philosophy document
- Zig patterns reference (ZIG_PATTERNS.md)
- Standard library summary (STD_LIBRARY_SUMMARY.md)
- Terminal capability detection (basic, 256, truecolor)
- NO_COLOR environment variable support
- VIBEUTILS_STYLE environment variable (plain/color/full)
- Graceful fallback for limited terminals
- Colored help output with syntax highlighting
- Nerd Font glyphs in help and ls
- LS_COLORS parsing and theming
Comprehensive cross-platform testing for commands that require elevated privileges (chmod, chown, etc.) across Linux, macOS, OpenBSD, FreeBSD, and NetBSD in GitHub Actions.
- Tools: fakeroot, unshare (user namespaces), podman (rootless containers)
- Strategy: Full privilege simulation without actual root
- Coverage: 100% of privilege-related tests
- Tools: Real sudo (GitHub Actions allows), limited fakeroot
- Strategy: Focus on error paths, use sudo for critical tests
- Coverage: ~70% through error simulation + real sudo tests
- FreeBSD: fakeroot available in ports
- OpenBSD: Use doas for privilege testing
- NetBSD: Basic permission testing
- Strategy: Run in VMs via vmactions/* GitHub Actions
- rm: chmod operations on write-protected files
- mkdir: Setting custom permissions with -m flag
- cp: Preserving permissions/ownership with -p
- ls: Displaying special permission bits
- chmod: Permission modification (setuid/setgid/sticky) - tests migrated to privilege framework ✓
- chown: Ownership changes
- ln: Hard link permission requirements
- stat: Ownership/permission display
- find: Permission-denied scenarios
- du/df: Restricted directory access
- Create src/common/privilege_test.zig module
- Add platform detection (fakeroot, unshare, etc.)
- Implement test skip annotations for unprivileged environments
- Add mock system calls for unit testing
- Linux: Test with fakeroot (automated privilege simulation)
- macOS: Native testing with privilege simulation support
- BSD: Set up VM-based testing with vmactions (not implemented;
no BSD workflow in
.github/workflows/) - Add privileged test matrix to CI pipeline
- Cross-platform CI/CD with Ubuntu and macOS runners
- Coverage reporting with Codecov integration
- Security scanning with Dependabot and CodeQL
- Automated release workflow with multi-platform binaries
- Permission Simulation: Test actual permission changes (infrastructure ready)
- Error Paths: Test permission-denied handling
- Integration Tests: Real operations in permitted locations
(
tests/privilege_integration/file_ops_test.zig,workflow_test.zig) - Mock Tests: Unit tests with injected syscalls (via requiresPrivilege)
- test-privileged: Cross-platform privileged test runner
- test-privileged-local: macOS with Docker fallback
- test-linux-privileged: Linux Docker container
- Test error paths (permission denied scenarios)
- Use dependency injection for mockable syscalls
- Focus on logic testing without privilege operations
- Document privilege requirements
- All privilege-related tests pass on Linux with fakeroot (infrastructure ready)
- Core functionality works without privileges
- Clear test output indicating skipped privileged tests
-
Test Workflow (.github/workflows/test.yml)
- Cross-platform testing (Ubuntu, macOS)
- Unit tests and integration tests
- Privileged test support with fakeroot
-
Integration Workflow (.github/workflows/integration.yml)
- Full integration test suite (48 utilities)
- Cross-platform (Ubuntu, macOS)
-
Documentation Workflow (.github/workflows/docs.yml)
- GitHub Pages deployment
- Man page conversion to HTML
-
Release Workflow (.github/workflows/release.yml)
- Automated release on tag push
- Multi-platform binary generation (4 targets)
- GitHub Release creation with asset upload
- Homebrew tap formula auto-update
- Cachix binary cache push (explicit, not daemon)
-
Update Flake Workflow (.github/workflows/update-flake.yml)
- Weekly flake.lock update (Monday 6am UTC)
- Cachix push across 4 platforms after update
- Manual trigger via workflow_dispatch
- Cachix Binary Cache: Prebuilt binaries for darwin-arm64, darwin-amd64, linux-arm64, linux-amd64
- Release Script: Extracts notes from CHANGELOG.md, updates GitHub release after CI
- Privileged Testing: Smart detection and fallback for privilege simulation
- CI Environment Detection: Helper functions for CI-specific behavior
See docs/plans/2026-03-01-modern-features-design.md for
full design.
- Modify argparse to render colored help on TTY
- Bold utility name and section headers
- Cyan flag names, yellow arguments
- Respect NO_COLOR, plain text when piped
- Nerd-font glyphs for section headers
- Yellow UPPERCASE metavariable highlighting in descriptions
- Handle trailing punctuation and (s) suffixes
- Highlight matched text in bold red
- Filename in magenta, line numbers in green
-
--color=auto/always/neverflags - Match GNU grep color conventions
-
VIBEUTILS_STYLE=full: color, icons, git status (TTY only) -
VIBEUTILS_STYLE=color: color only, no icons (TTY only) -
VIBEUTILS_STYLE=plain: no color, no icons, no glyphs -
VIBEUTILS_STYLE=always: force all features through pipes - Presets respect TTY detection (no ANSI leaking into pipes)
- NO_COLOR still respected
- Integrated in ls, grep, du, and help output
-
--color=autochecks isatty(stdout) in ls -
df: human-readable by default (df.zig:88) -
du: human-readable by default (currentlydu.zig:33defaultshuman_readable = false) -
ls -l: human-readable by default (currentlyls/main.zig:27defaultshuman_readable = false) - Explicit flags always override
- chown: warn when argument looks like octal mode
- chmod: warn when numeric mode contains 8 or 9
- rm: refuse to remove '/' without --no-preserve-root
- cp/mv: hint about -i for interactive overwrite prompts
- ln: warn when creating dangling symlinks
- Auto-enable git status when inside a git repo
-
--git=WHENflag (always/auto/never) - Respects VIBEUTILS_STYLE (plain/color disable git)
- Fix: suppress git status and icons in
-1mode - Fix: alphabetize
--helpflags - Fix: flush stderr in
fatalWithWriterbefore exit
-
df: green/yellow/red by usage percentage -
df: optional inline usage bar -
du: color size relative to largest entry -
du: file-type icons before paths (--icons=WHEN) -
wc: semantic column colors (--color=WHEN) - Icon coverage: 59 extensions, brand colors, dark-bg visibility
- Recursive directory listing with box-drawing lines
- File-type icons via common/icons
- Truecolor/256/basic icon coloring (reuse ls pattern)
-
-Ldepth limit,-ddirectories only -
-Ipattern exclusion - Summary line (N directories, M files)
-
--color=auto/always/never, respect NO_COLOR - Man page
- Progress module in
src/common/ - Show status line on stderr after 2s delay
- Update in place, clear when done
- Only when stderr is a TTY
- Permission denied with actionable hint
- Directory not empty with
rm -rsuggestion
The O_APPEND bug (issue #5) exposed gaps in our testing strategy. These items address the categories of testing that would have caught it — and similar bugs — earlier.
- Generic test harness that runs each binary under different fd configurations
- Test
>> fileappend mode for every utility - Test pipe mode (
| cat) for every utility - Test truncate mode (
> file) for every utility - Test dup'd descriptors (
2>&1 >> file)
-
>>must append, not overwrite - Stdout to a closed pipe must produce SIGPIPE/EPIPE
- Stderr must be unbuffered
- Exit codes conform to POSIX spec
- Utility-agnostic: same I/O contract tests run against every binary
- Replace ad-hoc
testing.tmpDir(.{})usage with sharedcommon.test_dir.TestDirin all utility tests - Ensure all tests use absolute paths (no fchdir)
- Utilities to migrate: cat, chmod, chown, cut, dd, du, find, grep, head, ln, ls, mkdir, mktemp, nl, pwd, readlink, realpath, rm, rmdir, stat, tac, tail, tee, test, touch, tr, uniq, wc
- Consolidate mv.zig's local TestDir into the shared one
- cp overwrite hint test fails under
.use_llvm = truebut passes with self-hosted backend - mv overwrite hint test has the same issue
- Root cause: likely Style/writer generic instantiation differs between backends
- Blocking accurate coverage numbers (2 of 49 binaries fail)
Resolved during the Zig 0.16 migration. build.zig:409, 434
still set use_llvm = true and zig build test is fully
green, including cp.zig:1181-1276 and mv.zig:1033+
overwrite-hint tests.
- Test the writer setup code path in main(), not just runUtil() with test-provided writers
- Integration tests that exercise the compiled binary's actual I/O initialization
Salvaged from closed PR #34. tests/utilities/dd_test.sh
lacks behavioral coverage for several MUST-tier conv=
values; some existing tests also compare against macOS
/usr/bin/dd, which produces empty output and silently
passes.
- Replace macOS
/usr/bin/ddcomparisons with hardcoded GNU-equivalent expected values - Add behavioral tests for
conv=sync(NUL padding + full block),conv=notruncvs. truncate contrast,conv=fsync,conv=osync,conv=ascii,conv=ebcdic,conv=ibm,conv=noerror - Cross-check against the existing rejection tests
added in commit cc57c2a (
conv=sparse/par*/files=)
-
lsdoes not switch to single-column output when stdout is a pipe (POSIX violation, found 2026-04-25, tracked as #113). GNU/BSDlsauto-detect a non-tty stdout and emit one entry per line. vibeutilslskeeps multi-column layout with column-aligned padding even under a pipe, so pipelines likels dir | grep | sort | tailsee lines with embedded trailing whitespace from the alignment. This silently broke the gale upgrade script (gale-project/scripts/bootstrap.sh): the padded line0.11.3.lockslipped pastgrep -v '\.lock$'because the suffix is no longer at end-of-line, thensort -V | tail -1picked it as the "highest" version, and the script wrote that garbage into~/.gale/gale.toml. gale-project worked around it by replacing thelspipeline with a bash glob, but the underlying behavior is non-POSIX and surprises any tool that pipesls. Repro:Expected (matching GNU/BSD): single-column output, no padding, the highest non-ls /Users/tcole/.gale/pkg/gale | grep -v '\.lock$' \ | sort -V | tail -1 | cat -A # 0.11.3.lock $.lockentry on its own line. Spec: POSIXlssays "If the standard output is not a terminal, the default format shall be the same as the -1 option." (pubs.opengroup.org/onlinepubs/9699919799/utilities/ls.html).
- All utilities pass GNU coreutils test suite
- 90%+ test coverage
- Clean static analysis reports
- Privileged operations tested (Linux, macOS)
- CI/CD pipeline operational