CLI tool
pls-rs/pls avatar
pls-rs/pls

pls: a prettier ls(1) that trades scriptability for readability

pls is a prettier and powerful ls(1) for the pros.

966 stars35 forksRustGPL-3.0

At a glance

What is it?
pls is a Rust rewrite of ls(1) that prints colored, structured directory listings and is configured through a .pls.yml file, with a single file binary and docs hosted at pls.cli.rs. It is designed for people reading listings rather than for scripts parsing them, and it ships under GPL-3.0-or-later.
Who is it for?
pls fits developers who read directory listings in a terminal several times a day and want color, structure and per project configuration without writing a shell alias. It does not fit shell scripts that parse ls output, and teams that need to ship it as part of a proprietary product should read GPL-3.0-or-later before adopting it.
Can I use it commercially?
Yes, with conditions. GPL-3.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
Is it still maintained?
Yes. The repository last received commits 43 days ago.
What is it written in?
Mainly Rust, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Seven adjectives for one replacement listing tool

pls is presented as a prettier and more capable replacement for ls(1), and the project explains its name by expanding it into seven adjectives that each describe a design property rather than a slogan.

The list is worth reading because it is a compact specification. Pretty means the output is cleaner, friendlier and more colorful than the classic listing. Performant means it is speedy, and the implementation language is given as the reason: it is written in Rust. Practical means sensible defaults and an interface that does not need a manual to be useful. Petite means it is a small, single file, binary executable. Pliable means power users can tweak it extensively. Personable means it prioritises consumption by humans over scripts. Powerful, in the original list, means it provides more features than the competition.

That last pair of adjectives, personable and powerful, is the tension in the whole tool. A listing that is designed to be read by a person is a listing that changes shape, adds color and possibly rearranges or filters what it shows. That is a good trade for an interactive shell and a bad one for a script that parses ls output and expects one path per line.

Who this is for is therefore narrower than the name suggests: developers working interactively in a terminal, on macOS or Linux, who want more from a directory listing than the POSIX default provides and who are willing to keep a YAML file in their project to describe what the listing should show.

A Rust binary with clap arguments and figment YAML configuration

The dependency list in Cargo.toml tells you how the tool is assembled. Command line parsing uses clap with the derive and wrap_help features, so the flags are defined as a Rust struct and help text wraps to the terminal width. Color comes from the colored crate, and terminal control, which is what makes an interactive listing possible, comes from crossterm with default features turned off.

Configuration goes through figment2 with the yaml and test features enabled. That combination explains the presence of a .pls.yml file at the root of the repository and a second .pls.yml in the examples directory: the tool reads layered YAML configuration, and the examples directory shows what such a file looks like in practice.

Two other dependencies hint at what the tool does beyond printing names. git2 is included with the vendored-libgit2 feature and default features disabled, so the project can read Git repository metadata without requiring a Git installation on the host. json5 is present, which suggests that some configuration or output can be written in JSON5, the relaxed JSON dialect, rather than strict JSON.

The crate metadata also shows how the binary is packaged. The Cargo.toml declares a single binary target named pls, and the binstall section notes that the pls binary sits at the archive root rather than in the default subfolder, with pkg-fmt set to zip. The comment above that block explains the intent: assuming cargo-binstall is present globally, the listed packages install using pre-built binaries and the process is quite fast.

Two other choices in the manifest are easy to miss and relevant to anyone embedding the tool. The edition is 2024 and rust-version is 1.88.0, so building from source needs a recent Rust toolchain. The release metadata sets sign-commit and sign-tag to true, so published tags and release commits are signed, while publish and push are false, meaning the release process stops short of pushing on its own.

The justfile is the developer entry point, not an installer

There is no user facing installation command in the README, which is a gap worth stating plainly. The documentation site is the place the project points to for getting started, at pls.cli.rs with a guides entry point, and the README itself is little more than the name expansion and a link to that documentation. The badges indicate macOS and Linux as the platforms, and the packaging section of the documentation is where a download or install method would be described.

What the repository does contain is a justfile, and it is the entry point for working on pls itself. The install recipe pulls the JavaScript dependencies, installs the Rust tooling binaries and then delegates to the docs and examples sub-recipes.

shell
cargo bin --install

The run recipe compiles and runs the program in development mode, with arguments passed straight through.

shell
cargo run -- {{ args }}

The test recipe uses cargo-nextest rather than the built in test runner, and the release recipe produces the optimized binary.

shell
cargo nextest run {{ args }}
shell
cargo build --release

The bench recipe is the one worth knowing about, because it shows how performance claims are checked. It depends on the release build, then runs hyperfine against the release binary with a warmup of 10 and shell mode disabled.

shell
cargo bin hyperfine --warmup 10 --shell=none './target/release/pls {{ args }}'

That recipe is the reason the manifest pins hyperfine, cargo-nextest, cargo-release and cargo-msrv as installable binaries. The maintainers benchmark the release binary the same way a user would run it, which is a better basis for the performant claim than a microbenchmark of an internal function.

A polyglot repository with a pnpm toolchain for the docs

The top level of the repository is unusual for a Rust CLI. Alongside src/, Cargo.toml, Cargo.lock and the LICENSE, it contains package.json, pnpm-lock.yaml, pnpm-workspace.yaml, a justfile, docs/, examples/, pkg/ and a .node-version file.

The package.json explains why. It is a private package with no build script of its own, whose devDependencies are a documentation and linting toolchain: prettier with the Astro plugin for the docs site, oxlint and oxfmt for linting and formatting, eslint with the typescript parser and the prettier config, and typescript. The packageManager field pins pnpm at 11.13.0, and the presence of pnpm-lock.yaml and pnpm-workspace.yaml confirms pnpm is the package manager rather than npm or yarn.

The justfile reinforces this. Its install recipe runs pnpm i before installing the Rust binaries, and it installs a pre-commit and pre-push hook through pnpm prek install with the hook-type flags set for both events. The lint recipe runs those hooks through pnpm prek run, optionally scoped to specific files or across all files.

So pls is not only a Rust project. The documentation site is part of the repository, built with Node tooling, and the CI story involves three languages at once: Rust for the binary, TypeScript and Astro for the docs, and a pre-commit hook layer that runs the same checks locally and in CI. That is a meaningful cost for a contributor, and it is the reason the repository ships a justfile with aliases such as i for install and l for lint, so the common tasks are one word.

Human first output means pls is the wrong tool for scripts

The README states the design position directly: pls prioritises consumption by humans over scripts. Everything else follows from that sentence.

A listing optimized for reading tends to include extra information, which is exactly what breaks parsers. It may present entries in groups, mark directories, show sizes or types in a decorated way, or apply the YAML configuration that the project maintains. Any of those changes the shape of the output relative to the classic ls(1) that a script was written against.

There is a second, subtler cost. A tool that adds color usually detects whether stdout is a terminal, which means the same command produces different output when redirected to a file or piped into another program. That is good behaviour in general, but it means a script that works in an interactive shell and fails in CI is a plausible outcome, and the fix is not always obvious from the failure message.

For the same reason, pls is not a replacement for ls inside tooling, shell completion, file managers, or any program that shells out to ls expecting one path per line. It is a replacement for the ls a human types.

The trade-off is honest and worth stating to anyone on a team that mixes the two uses. If a directory is read by a person and also parsed by a script, the script should keep calling the system ls, and pls should be left to the interactive prompt.

GPL-3.0-or-later is the licence to check before you redistribute

The licence is GPL-3.0-or-later, as stated in the Cargo.toml and in the LICENSE file at the root of the repository. The README badge points at that same file.

For personal use, for installing the binary on your own machines, and for use inside a company that does not distribute the tool, a copyleft licence on a command line utility is rarely the deciding factor. Where it does become relevant is redistribution. If you bundle the pls binary into an internal developer image, ship it inside a product installer, or hand it to customers, the copyleft obligations attach to what you distribute and to how you convey it. This is a note about what the licence file contains, not legal advice, and it is the kind of question to raise with whoever owns licence compliance before the binary enters a distribution channel.

The licence also interacts with the packaging metadata. The manifest sets publish to false and push to false in the release metadata, and the crate is distributed through the project's own channels with the homepage pointing at pls.cli.rs for both the project and its documentation. The source is published, the licence is the copyleft variant, and the documented install path is the documentation site rather than a package registry entry in the README.

One more practical detail for anyone mirroring the tool internally: the binstall configuration expects the binary at the archive root, so any internal packaging that assumes a versioned subdirectory will need to match that layout.

A 7.0.0 beta line and an unusual version history

The crate version in Cargo.toml is 7.0.0-beta.1, matching the newest listed GitHub release, v7.0.0-beta.1, dated 2026-06-30. The repository is not archived and the last push was on 2026-09-10, so work has continued on the tree since that tag.

The release list also shows v0.0.1-beta.13 and v0.0.1-beta.12, both dated 2026-06-03. That version numbering is worth a moment of attention, because a project moving from 0.0.1-beta.13 to 7.0.0-beta.1 within a month implies a deliberate version scheme change rather than a slow count, and it means that if you find older references to the 0.0.1 beta series in blog posts or issue threads, they describe a much earlier state of the tool.

The version jump is consistent with the other signs of a project in its current phase. The README is a stub that points to a documentation site rather than a manual, the repository has picked up a full JavaScript documentation toolchain, and the manifest now carries release metadata for signed commits and tags.

For an adopter, the practical consequence is that pls is a moving target with an explicit beta line. Pinning to a specific release tag rather than tracking main is the safer choice, and reading the changelog in the docs site before an upgrade is worth the few minutes it takes.

Editorial conclusion

pls fits developers who read directory listings in a terminal several times a day and want color, structure and per project configuration without writing a shell alias. It does not fit shell scripts that parse ls output, and teams that need to ship it as part of a proprietary product should read GPL-3.0-or-later before adopting it. Verify first that your target platforms match the documented macOS and Linux support, that the version you pin corresponds to the 7.0.0-beta.1 line or the newer stable release, and that the .pls.yml options in the documentation cover the metadata you need your listing to show.

Frequently asked questions

What is the pls command?

pls is a Rust rewrite of ls(1) that produces cleaner, colorful output and is meant for people reading listings rather than for scripts parsing them. It is a single file binary and is configured through a .pls.yml file.

How do I install pls on macOS or Linux?

The README does not include an install command and points to the documentation site at pls.cli.rs for the getting started guide. The repository badges list macOS and Linux as the supported platforms.

How do I build and run pls from source?

The justfile drives the development workflow. The install recipe runs pnpm i and cargo bin --install, the run recipe uses cargo run with your arguments, and the test recipe uses cargo nextest run.

Can I use pls instead of ls in shell scripts?

Not safely. The project states that pls prioritises consumption by humans over scripts, so its output shape and terminal detection differ from the classic ls(1). Scripts that parse listings should keep using the system ls.

What licence does pls use?

pls is released under GPL-3.0-or-later. The licence is stated in Cargo.toml and the LICENSE file at the root of the repository.

Official sources

  1. License: GPL-3.0
  2. pls-rs/pls on GitHub
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/pls-rs-pls.svg)](https://hysenlabs.com/projects/pls-rs-pls)

Was this useful?