pathlint
Verify that each command on
PATHresolves from the installer you expect.
⚠ Pre-alpha (0.0.x). Schema and CLI surface are still moving; not ready for production wiring. Skeleton only — no working binary yet.
Why
Most "PATH problems" come from one place: the wrong copy of an executable resolves first. Examples:
- I
cargo install runexon this machine, but the binary that runs is the older one fromwinget— same name, different file. pythonshould come frommise, not from the Microsoft StoreWindowsAppsstub.nodeshould come fromvolta, not from the systemaptinstall.- macOS
gccshould come from Homebrew, not from/usr/bin/gcc.
which python will tell you what wins, but won't tell you whether
that's what should win in a form you can commit to a dotfiles repo
and check on every machine.
pathlint makes that intent explicit: write down "runex should
come from cargo, not from winget" once, and the tool checks it
on every machine you own.
How it works
Two TOML concepts:
[[expect]]— per-command expectations. "command X should be resolved from source S." This is what users actually write.[source.<name>]— how to recognize an installer on disk ("cargolives at~/.cargo/bin"). pathlint ships built-in defaults forcargo,mise,volta,aqua,winget,choco,scoop,brew_arm,brew_intel,apt,pacman,dnf,pkg,flatpak,snap,WindowsApps, and more — users only override when their layout is non-standard.
For each [[expect]], pathlint resolves the command against the real
PATH, looks at where the winning binary lives, and matches that
location to the source labels.
Status
The 0.0.x line ships a working pathlint / pathlint init /
pathlint catalog list. The TOML schema and CLI surface are still
moving, but the resolve / match / report pipeline is in place and
covered by tests. See docs/PRD.md for the full design.
What pathlint won't tell you
pathlint is path-prefix based: it resolves the command, looks at
the resolved binary's full path, and asks "does any defined source's
per-OS path appear in it as a substring?". That makes it fast (no
package-manager calls, no network), but it leaves blind spots you
should know about:
- AUR / Homebrew tap /
make install/ any custom prefix. If a binary lands somewhere not listed in your[source.<name>]entries,pathlintreportsNG (unknown source)even when the install is legitimate. Add a[source.my_prefix]for it, or accept that pathlint can't tell that case apart from a real misordering. - Symlinked system dirs. On Arch / openSUSE TW / Solus,
/usr/sbin → /usr/bin.which lsreports/usr/sbin/ls, so the built-inapt/pacman/dnfsource (/usr/bin) doesn't match. Add[source.usr_sbin] linux = "/usr/sbin"to yourpathlint.tomlif you hit this. - Which package owns this binary.
pathlintdoes not calldpkg -S/rpm -qf/pacman -Qo/brew which-formula. That's intentional in 0.0.x for speed and offline correctness; revisiting is on the 0.2 list.
The full set of known limitations and future trade-offs lives in docs/PRD.md §14, §16.
Usage
# Check the current process PATH against ./pathlint.toml
# Check the User-only or Machine-only PATH (Windows registry)
# Verbose: also show n/a expectations and the resolved PATH
# Drop a starter pathlint.toml in the current directory
# Inspect every known source (built-in + user-defined)
pathlint.toml (minimal example)
[[]]
= "runex"
= ["cargo"]
= ["winget"]
[[]]
= "python"
= ["mise"]
= ["WindowsApps", "choco"]
[[]]
= "node"
= ["mise", "volta"]
[[]]
= "gcc"
= ["mingw", "msys"]
= ["strawberry"]
= ["windows"]
No [source.*] section is needed for any of the names above —
they're all in the built-in catalog. The whole file is the user's
intent.
To override a built-in (mise installed in a non-standard location):
[]
= "D:/tools/mise"
To add a new source:
[]
= "$HOME/dotfiles/bin"
os = [...] accepts windows | macos | linux | termux | unix.
Match is substring + case-insensitive, after env-var expansion (both
%VAR% and $VAR work everywhere) and slash normalization.
Installation
# From crates.io (once published)
# From source (latest main)
Documentation
- 日本語 README
- PRD (English) — the full design, including the built-in source catalog
- PRD (日本語)
- Release process — how to cut a new version
- リリース手順 (日本語)
- Changelog
License
Licensed under either of:
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.
Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.