ogham/exa
A modern replacement for ‘ls’.
Stale — last commit 2y ago
Permissive license, no critical CVEs, actively maintained — safe to depend on.
Has a license, tests, and CI — clean foundation to fork and modify.
Documented and popular — useful reference codebase to read through.
last commit was 2y ago; Scorecard "Branch-Protection" is 0/10…
- ⚠Stale — last commit 2y ago
- ⚠Scorecard: marked unmaintained (0/10)
- ⚠Scorecard: default branch unprotected (0/10)
- ✓27+ active contributors
- ✓Distributed ownership (top contributor 41% of recent commits)
- ✓MIT licensed
- ✓CI configured
- ✓Tests present
What would improve this?
- →Deploy as-is Mixed → Healthy if: 1 commit in the last 180 days; bring "Branch-Protection" to ≥3/10 (see scorecard report)
Computed from maintenance signals — commit recency, contributor breadth, bus factor, license, CI, tests, cross-checked against OpenSSF Scorecard
Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.
Embed the "Safe to depend on" badge
Paste into your README — live-updates from the latest cached analysis.
[](https://repopilot.app/r/ogham/exa)Paste at the top of your README.md — renders inline like a shields.io badge.
▸Preview social card
This card auto-renders when someone shares https://repopilot.app/r/ogham/exa on X, Slack, or LinkedIn.
Ask AI about ogham/exa
Grounded in the actual source code. Pick a starter question or write your own.
Onboarding doc
Onboarding: ogham/exa
Generated by RepoPilot · 2026-06-24 · Source
🎯Verdict
WAIT — Stale — last commit 2y ago
- 27+ active contributors
- Distributed ownership (top contributor 41% of recent commits)
- MIT licensed
- CI configured
- Tests present
- ⚠ Stale — last commit 2y ago
- ⚠ Scorecard: marked unmaintained (0/10)
- ⚠ Scorecard: default branch unprotected (0/10)
<sub>Computed from maintenance signals — commit recency, contributor breadth, bus factor, license, CI, tests, cross-checked against OpenSSF Scorecard</sub>
⚡TL;DR
exa is a Rust-written command-line file listing tool that replaces Unix/Linux ls with modern defaults: colored output for file types, Git integration (via git2 0.13), extended attributes support, and smart sorting (natural order via natord crate). It outputs a single binary with no runtime dependencies and supports grid/tree/long-format display modes with extensive filtering options. Single-binary Rust crate: src/main.rs is the CLI entry point; src/fs/ contains filesystem operations (dir.rs, file.rs, filter.rs, fields.rs) with optional git.rs and xattr.rs features; src/info/ handles file metadata sources and type detection. build.rs processes man pages. Shell completions in completions/bash|fish|zsh and man pages in man/ are pre-built artifacts.
👥Who it's for
Unix/Linux power users and systems administrators who want faster, more readable directory listings with Git-aware filtering and better visual hierarchy. Developers packaging command-line utilities can use exa as a reference for cross-platform Rust CLI tooling with optional feature compilation.
🌱Maturity & risk
The project is effectively abandoned — the README explicitly states 'exa is unmaintained, use the fork eza instead' and the repository is not archived only because the maintainer is unreachable. However, v0.10.1 is stable with passing CI (unit-tests.yml workflow), comprehensive man pages, and shell completions, making it production-ready as-is but unlikely to receive bug fixes or new features.
Critical: single-maintainer project with no active development (unmaintained status). Dependencies are dated (git2 0.13 from ~2020, ansi_term 0.12, lazy_static 1.3) and unlikely to receive security updates. The fork eza-community/eza is the recommended alternative for ongoing maintenance. No visible breaking changes are documented, but relying on this for new projects introduces abandoned-software risk.
Active areas of work
Project is in maintenance-mode stasis. The GitHub Actions workflow (unit-tests.yml) shows passing tests, but there are no recent commits, open PRs, or active milestones visible. The maintainer has directed users to the eza fork (eza-community/eza on GitHub) for ongoing development.
🚀Get running
git clone https://github.com/ogham/exa.git
cd exa
cargo build --release
./target/release/exa --help
The Rust toolchain is pinned to 1.66.1 in rust-toolchain.toml. For development, run source devtools/dev-set-up-environment.sh to configure the environment.
Daily commands:
cargo build --release
./target/release/exa /path/to/dir
# or in dev mode (no debug symbols per profile.dev)
cargo build
./target/debug/exa /path/to/dir
For testing: cargo test. For man page generation (requires datetime crate with 'format' feature): see man/exa.1.md (markdown source).
🗺️Map of the codebase
src/main.rs— Entry point for the entire application; all command-line invocations flow through here to initialize the program and coordinate the display pipeline.src/options/mod.rs— Parses and validates all command-line flags and arguments; understanding this is essential for adding new features or modifying behavior.src/fs/mod.rs— Core filesystem abstraction that wraps file metadata collection; central to understanding how exa gathers and represents file information.src/output/mod.rs— Orchestrates all rendering backends (grid, details, tree); deciding which output format to use and how to format results.src/theme/mod.rs— Manages color theming and styling; critical for understanding how exa applies ANSI color codes and visual presentation.Cargo.toml— Project manifest defining all dependencies and build settings; required reading to understand external crate integration and Rust version constraints.
🛠️How to make changes
Add a new output format (layout view)
- Create a new renderer module in src/output/ (e.g., src/output/myformat.rs) implementing the same interface as grid.rs or details.rs. (
src/output/myformat.rs) - Add a new variant to the view enum in src/options/view.rs (e.g., MyFormat) and parse it in the parser. (
src/options/view.rs) - In src/output/mod.rs, match on the new view variant and call your renderer module. (
src/output/mod.rs) - Add command-line flag parsing in src/options/flags.rs to enable the new view. (
src/options/flags.rs)
Add a new file column or attribute in details view
- Create a new render module in src/output/render/ (e.g., src/output/render/custom_attr.rs) that formats a file attribute. (
src/output/render/custom_attr.rs) - Add the new column definition to src/output/details.rs in the table building logic. (
src/output/details.rs) - Create or modify a flag in src/options/view.rs to control whether this column is shown. (
src/options/view.rs) - Parse the new flag in src/options/parser.rs or src/options/flags.rs. (
src/options/flags.rs)
Add a new file filtering option
- Add a new filter variant or configuration field to src/options/filter.rs. (
src/options/filter.rs) - Parse the new filter flag in src/options/parser.rs. (
src/options/parser.rs) - Implement the filter logic in src/fs/filter.rs in the appropriate filter function. (
src/fs/filter.rs) - Apply the filter when reading directories in src/fs/dir.rs. (
src/fs/dir.rs)
🔧Why these technologies
- Rust — Systems language providing memory safety without GC, enabling a fast single-binary replacement for 'ls' with minimal dependencies.
- ansi_term crate — Provides safe, ergonomic ANSI color code generation for terminal output across platforms.
- term_grid crate — Handles multi-column grid layout calculation with proper column width optimization.
- users crate (Unix only) — Safe FFI bindings to libc for user/group name resolution without spawning external processes.
- zoneinfo_compiled crate — Timezone-aware datetime formatting without depending on system timezone data at runtime.
⚖️Trade-offs already made
-
Single-pass rendering: collect all files before displaying, no streaming output
- Why: Enables accurate column width calculation in grid/details views; supports sorting across entire result set.
- Consequence: Higher memory usage for large directories; latency before any output appears. Trade-off acceptable for typical use cases.
-
Git status via system calls (spawning 'git' process) rather than libgit2
- Why: Reduces compile-time dependencies and binary size; leverages existing git installation.
- Consequence: Slower git integration for large repos; subprocess overhead. Mitigated by optional feature flag.
-
Separate render modules for each attribute rather than template system
- Why: Clear separation of concerns; each render module is independently testable.
- Consequence: More boilerplate for adding new columns; easier to extend than a monolithic table builder.
-
Multiple output format implementations (grid, details, tree) as separate modules
- Why: Each format has distinct layout logic; cleaner than unified polymorphic renderer.
- Consequence: Code duplication in column width calculation; justified by format-specific optimization opportunities.
🚫Non-goals (don't propose these)
- Real-time filesystem monitoring or watch mode
- Remote filesystem support (SSH, NFS, SMB)
- Windows native support (Rust POSIX abstractions are Unix-focused)
- Configuration file parsing (relies on command-line flags and environment variables only)
- Pluggable custom renderers or themes via external scripts
- Recursive deletion or modification of files (display-only tool)
🪤Traps & gotchas
Feature-gated compilation: building without the 'git' feature (--no-default-features) removes Git status display entirely — git.rs is conditionally compiled. Manual argument parsing: no argument framework; adding a new flag requires hand-coded parsing in main.rs with potential for getopt conflicts. Unix-only user lookup: users crate is only available on Unix targets (cfg(unix) in Cargo.toml); --group and --user options will not compile on Windows. Datetime formatting: zoneinfo_compiled dependency requires compiled timezone data; locale-aware date formatting depends on system locale. No vendored Git by default: git2 requires system OpenSSL unless 'vendored-openssl' feature is enabled. git2 0.13 is old: this version of libgit2 may fail to parse some modern .gitignore patterns or new Git repository formats.
🏗️Architecture
💡Concepts to learn
- Extended Attributes (xattr) — exa's --extended flag lists POSIX extended attributes (metadata beyond standard Unix permissions); src/fs/feature/xattr.rs implements this platform-specific feature
- Natural Order Sorting — exa uses the natord crate to sort filenames like humans expect ('file2.txt' after 'file1.txt', not '10.txt' after '2.txt'); critical for usable directory listings
- Feature-Gated Compilation — exa uses Cargo features (git, vendored-openssl) to optionally compile Git support and embed OpenSSL; understanding this pattern is essential for maintaining cross-platform Rust CLI tools
- ANSI Escape Codes — exa uses ansi_term crate to render colored and styled terminal output (colors for file types, bold for headers); critical for visual distinction in a TUI tool
- Unix File Type Modes — exa distinguishes files via st_mode bits (S_ISREG, S_ISDIR, S_ISLNK, S_ISBLK) retrieved via libc; src/info/filetype.rs decodes these to assign icons and colors
- Git Index and Status — exa's --git flag uses git2 crate to query file status (staged, modified, ignored) without shelling out; requires understanding Git's internal index and working tree state
- Symlink Resolution — exa handles symlinks specially: --long shows target, some flags recurse through symlinks; libc's readlink and lstat syscalls are used to detect and resolve links without following them
🔗Related repos
eza-community/eza— The actively maintained fork of exa recommended in the README; includes bug fixes, new features, and ongoing dependency updates since ogham/exa went unmaintainedBurntSushi/ripgrep— Sibling modern Rust CLI tool (grep replacement) with similar design philosophy: single binary, zero runtime deps, colored output, .gitignore integrationsharkdp/bat— Modern Rust replacement forcatwith syntax highlighting and Git integration; complementary to exa for file inspection in Unix workflowsuutils/coreutils— Rust reimplementation of Unix coreutils (including ls); overlaps with exa but aims for GNU compatibility rather than ergonomic replacementlsd-rs/lsd— Alternative Rust ls replacement with icon support and colors; direct competitor with similar feature set and active maintenance
🪄PR ideas
To work on one of these in Claude Code or Cursor, paste:
Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.
Add integration tests for git feature flag in src/fs/feature/git.rs
The repo has a conditional 'git' feature (enabled by default in Cargo.toml) that integrates git status into file listing, but there are no visible integration tests validating this feature works correctly. The unit-tests.yml workflow only shows unit test execution. Adding integration tests would ensure git integration doesn't regress and help contributors understand the feature's behavior.
- [ ] Create tests/git_integration_tests.rs with test cases for git repository detection
- [ ] Add test cases for git status display (untracked, modified, staged files)
- [ ] Test the feature flag behavior by adding tests that run with/without 'git' feature enabled
- [ ] Update .github/workflows/unit-tests.yml to run integration tests alongside unit tests
- [ ] Document test setup in devtools/README.md for contributors
Add cross-platform CI workflow for macOS and Windows in .github/workflows/
The current unit-tests.yml likely runs on Linux only (standard GitHub Actions default). exa targets Unix/Linux systems but the Cargo.toml shows platform-specific dependencies (users crate for Unix). Adding macOS CI would catch platform-specific issues early. Windows support could be validated at least for compilation.
- [ ] Create .github/workflows/cross-platform-tests.yml with matrix strategy for ubuntu-latest, macos-latest, and windows-latest
- [ ] Add Rust toolchain setup (rust-version 1.66.1 from Cargo.toml) for each platform
- [ ] Skip Unix-only tests on Windows or mark them appropriately
- [ ] Run cargo build and cargo test for each platform matrix
- [ ] Test man page generation on macOS (devtools/local-package-for-macos.sh exists)
Add comprehensive output format tests for src/output/ module
The src/output/ directory has 6 modules (cell.rs, details.rs, escape.rs, file_name.rs, grid.rs, grid_details.rs) that handle terminal rendering and formatting. These are critical for user-facing output but likely lack unit test coverage visible in the repo structure. Testing output formatting prevents regressions in display logic.
- [ ] Create tests/ directory if it doesn't exist (not shown in file structure)
- [ ] Add tests/output_formatting_tests.rs with tests for grid layout edge cases (empty dirs, very long filenames, unicode characters)
- [ ] Add tests for ANSI escape sequence handling in src/output/escape.rs (verify color codes don't break on different terminals)
- [ ] Add tests for details view column alignment in src/output/details.rs (permissions, sizes, timestamps)
- [ ] Test file name truncation and icon rendering in src/output/file_name.rs
- [ ] Run tests with EXA_COLORS environment variable variations (documented in man/exa_colors.5.md)
🌿Good first issues
- Add tests for src/fs/filter.rs: The filter module (glob patterns, .gitignore, depth limiting) lacks unit test coverage visible in the repo. Adding test cases for edge cases like nested .gitignore files or symlink loops would improve reliability.
- Document the feature matrix in README.md: The README doesn't explain which exa features require which compile-time flags (e.g., '--git' requires
gitfeature, '--xattr' requires xattr support). Add a 'Feature Support' table clarifying Unix-only vs. universal features. - Verify compatibility with Rust 1.70+: The MSRV is 1.66.1 but dependencies like git2 0.13 (2020) and lazy_static 1.3 may have newer compatible versions. Test against current Rust stable in CI and bump MSRV if safe; document the result.
⭐Top contributors
Click to expand
Top contributors
- @ariasuni — 41 commits
- @Tyrubias — 18 commits
- @TygrisIQ — 6 commits
- @cab-1729 — 4 commits
- @skyline75489 — 4 commits
📝Recent commits
Click to expand
Recent commits
3d1edbb— Remove Thank You button on README since it didn’t work anyway (ariasuni)753150d— Merge pull request #1249 from capak07/test#1010 (ariasuni)d7c9f4e— Removed Say Thanks link in README (capak07)fb05c42— exa is unmaintained, please use the active fork eza instead (ariasuni)f039202— Merge pull request #1188 from 1stDimension/ci-migrate (ariasuni)17893b5— Upgrade Rust version to fix CI (ariasuni)08cc56d— Merge pull request #1219 from MartinNowak/patch-1 (ariasuni)352d32b— Merge pull request #1178 from mtoohey31/fix/obsolete-icons (ariasuni)e79f736— Merge pull request #1175 from dcervenkov/patch-1 (ariasuni)a4cee84— fix fish completion for -i/--inode option (MartinNowak)
🔒Security observations
The exa project presents significant security concerns due to being unmaintained with no plans for updates. Critical issues include severely outdated dependencies (particularly git2 v0.13 from 2020), an outdated Rust MSRV, and lack of active maintenance. The project explicitly directs users to the maintained fork 'eza' instead. The codebase involves system-level operations through libc and Unix APIs, which require careful unsafe code management. For production use, immediate migration to the
- High · Unmaintained Project —
README.md. The README clearly states 'exa is unmaintained, use the fork eza instead.' This means the project no longer receives security updates, bug fixes, or maintenance. Any vulnerabilities discovered will not be patched. Fix: Consider migrating to the maintained fork 'eza' (https://github.com/eza-community/eza) instead. If continuing to use exa, implement additional security monitoring and controls. - Medium · Outdated Rust Edition and MSRV —
Cargo.toml (rust-version = "1.66.1", edition = "2021"). The project uses Rust edition 2021 with MSRV 1.66.1, released in January 2023. This is now significantly outdated. Newer Rust versions contain security fixes and improvements in the standard library and compiler. Fix: Update to the latest stable Rust version (1.75+). Regularly update MSRV to remain within 2-3 releases of the current stable version to ensure access to security patches. - Medium · Outdated Git2 Dependency —
Cargo.toml ([dependencies.git2] version = "0.13"). The git2 dependency is pinned to version 0.13, released in 2020. This is severely outdated and likely contains known security vulnerabilities. Current versions are 0.58+. Fix: Update git2 to the latest version (0.58 or newer). Review the git2 changelog for security fixes between 0.13 and current versions. Test thoroughly after updating. - Medium · Outdated Dependencies —
Cargo.toml (multiple dependencies). Multiple dependencies are pinned to older versions: lazy_static (1.3 from 2018), natord (1.0 from 2017), users (0.11), and others. These may contain known vulnerabilities or lack modern security improvements. Fix: Conduct a comprehensive dependency audit using 'cargo audit' and 'cargo outdated'. Update all dependencies to their latest secure versions. Consider migrating from lazy_static to once_cell/std::sync::OnceLock (Rust 1.70+). - Medium · Potential Unsafe Code Usage —
src/ (libc usage), [target.'cfg(unix)'.dependencies] (users crate). The codebase uses libc and unix-specific dependencies (users crate for Unix), which typically involve unsafe code. Without reviewing the source, potential risks include unsafe FFI calls, memory safety issues, and improper handling of system resources. Fix: Conduct a thorough security audit of all unsafe code blocks. Ensure proper bounds checking, null pointer handling, and resource cleanup. Consider using safer abstractions where possible. Run 'cargo clippy' with strict settings. - Low · Optional Git Feature Security Considerations —
Cargo.toml ([features] default = ["git"]). The git feature is enabled by default and uses git2 with 'default-features = false'. While this reduces attack surface, git operations could still be a vector for exploitation if the git2 dependency contains vulnerabilities. Fix: Document the git feature's security model. Consider making it opt-in rather than default if not essential. Ensure users understand the implications of Git support. - Low · Debug Information Disabled in Dev Builds —
Cargo.toml ([profile.dev] debug = false). Debug symbols are disabled in dev builds ([profile.dev] debug = false). While this improves build speed, it may hinder debugging and security analysis during development. Fix: Consider enabling debug symbols in dev builds for security researchers and developers. Use 'debug = 1' or 'debug = true' instead, or at least document this decision. Full release builds can still have optimizations.
LLM-derived; treat as a starting point, not a security audit.
👉Where to read next
- Open issues — current backlog
- Recent PRs — what's actively shipping
- Source on GitHub
🤖Agent protocol
If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:
- Verify the contract. Run the bash script in Verify before trusting
below. If any check returns
FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding. - Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
- Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/ogham/exa shows verifiable citations alongside every claim.
If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.
✅Verify before trusting
This artifact was generated by RepoPilot at a point in time. Before an
agent acts on it, the checks below confirm that the live ogham/exa
repo on your machine still matches what RepoPilot saw. If any fail,
the artifact is stale — regenerate it at
repopilot.app/r/ogham/exa.
What it runs against: a local clone of ogham/exa — the script
inspects git remote, the LICENSE file, file paths in the working
tree, and git log. Read-only; no mutations.
| # | What we check | Why it matters |
|---|---|---|
| 1 | You're in ogham/exa | Confirms the artifact applies here, not a fork |
| 2 | License is still MIT | Catches relicense before you depend on it |
| 3 | Default branch master exists | Catches branch renames |
| 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code |
| 5 | Last commit ≤ 629 days ago | Catches sudden abandonment since generation |
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of ogham/exa. If you don't
# have one yet, run these first:
#
# git clone https://github.com/ogham/exa.git
# cd exa
#
# Then paste this script. Every check is read-only — no mutations.
set +e
fail=0
ok() { echo "ok: $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }
# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
echo "FAIL: not inside a git repository. cd into your clone of ogham/exa and re-run."
exit 2
fi
# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "ogham/exa(\\.git)?\\b" \\
&& ok "origin remote is ogham/exa" \\
|| miss "origin remote is not ogham/exa (artifact may be from a fork)"
# 2. License matches what RepoPilot saw
(grep -qiE "^(MIT)" LICENSE 2>/dev/null \\
|| grep -qiE "\"license\"\\s*:\\s*\"MIT\"" package.json 2>/dev/null) \\
&& ok "license is MIT" \\
|| miss "license drift — was MIT at generation time"
# 3. Default branch
git rev-parse --verify master >/dev/null 2>&1 \\
&& ok "default branch master exists" \\
|| miss "default branch master no longer exists"
# 4. Critical files exist
test -f "src/main.rs" \\
&& ok "src/main.rs" \\
|| miss "missing critical file: src/main.rs"
test -f "src/options/mod.rs" \\
&& ok "src/options/mod.rs" \\
|| miss "missing critical file: src/options/mod.rs"
test -f "src/fs/mod.rs" \\
&& ok "src/fs/mod.rs" \\
|| miss "missing critical file: src/fs/mod.rs"
test -f "src/output/mod.rs" \\
&& ok "src/output/mod.rs" \\
|| miss "missing critical file: src/output/mod.rs"
test -f "src/theme/mod.rs" \\
&& ok "src/theme/mod.rs" \\
|| miss "missing critical file: src/theme/mod.rs"
# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 629 ]; then
ok "last commit was $days_since_last days ago (artifact saw ~599d)"
else
miss "last commit was $days_since_last days ago — artifact may be stale"
fi
echo
if [ "$fail" -eq 0 ]; then
echo "artifact verified (0 failures) — safe to trust"
else
echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/ogham/exa"
exit 1
fi
Each check prints ok: or FAIL:. The script exits non-zero if
anything failed, so it composes cleanly into agent loops
(./verify.sh || regenerate-and-retry).
Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.
Embed this chat in your README →
Drop this iframe anywhere — the widget runs against the same live analysis cache as the main app.
<iframe src="https://repopilot.app/embed/ogham/exa" width="100%" height="500" style="border:1px solid #d0d7de; border-radius:8px;" allow="microphone" loading="lazy" ></iframe>