You find a new CLI tool on GitHub. The README looks good. You scroll to "Installation" and see the magic one-liner: curl -sSL https://... | sh. You run it. The script downloads a binary, drops it somewhere sensible, and adds it to your PATH by appending a line to your .bashrc.
Except you use fish. And fish doesn't understand export PATH=. So the binary is on your disk, but your shell can't find it. You open the install script, figure out where it put things, and manually write set -gx PATH ~/.local/bin $PATH into your config.fish. You've done this before. You'll do it again.
This is a small problem. But it's a revealing one. The kind of developer who installs CLI tools from GitHub release pages, who tries new package managers, who runs fish instead of bash because they actually thought about their shell choice, that's your target user. And your installer just told them you didn't think about them.
What Is parm?
parm is a binary package manager for GitHub Releases, written in Go by alxrw. You give it a repo (parm install owner/repo) and it finds the latest release, picks the right binary for your platform, downloads it, and symlinks it onto your PATH. No root access, no system package manager, no registry to maintain. It queries GitHub directly.
It handles updates (parm update), version pinning (parm pin), removal (parm remove), and has a search command that queries GitHub's API. It's pre-release (v0.1.6) but functional, with a clear roadmap toward v0.2.0. About 138 stars and one very active maintainer.
The interesting design choice: there is no curated package registry. Homebrew has formulae. asdf has plugins. parm has GitHub's API and your judgment. The README is upfront about this: "Users are responsible for vetting packages." That's a tradeoff, and it's a deliberate one.
The Snapshot
| Project | parm |
| Stars | ~138 at time of writing |
| Maintainer | Solo developer, actively releasing |
| Code health | Clean Go with standard stack (Cobra, Viper, go-github), 32% test file ratio |
| Docs | Good README with usage table, disclaimers, and package compatibility guide |
| Contributor UX | Merged my PR next-day with "lgtm" |
| Worth using | Yes, for grabbing CLI tools from GitHub without the Homebrew ceremony |
Under the Hood
parm follows the standard Go CLI layout. Commands live in cmd/ (one file per subcommand, Cobra-based). Core business logic lives in internal/core/ with separate installer and updater packages. The GitHub client lives in internal/gh/ using go-github v74. Configuration is TOML-based via Viper.
The dependency list is heavier than you'd expect for a tool this focused. Eight direct dependencies: go-github for the API, cobra and viper for CLI and config, semver for version comparison, mpb for progress bars, gopsutil for platform detection, filetype for binary type detection, oauth2 for GitHub authentication. None of these are unreasonable individually, but it's a lot of moving parts for "download a binary and symlink it." Compare to tools like ubi or eget that do the same thing with fewer dependencies. That said, the extra weight buys real features: progress bars, proper semver handling, and platform detection that works on all three major OSes.
The architecture within internal/ is well-separated. The installer handles asset selection (matching your OS and architecture against release asset names), archive extraction (tar, zip, and raw binaries), and symlink management. The manifest tracks what's installed, where, and at what version. The verification package handles binary validation. Each concern has its own package and its own tests. 19 test files out of 59 Go files is a decent ratio for a project this age.
What the Go code gets right: cross-platform support. The build targets include linux/darwin/windows on both amd64 and arm64. Platform detection via gopsutil picks the correct release asset. The asset name matching is smart enough to handle the inconsistent naming conventions across GitHub repos (linux-amd64, Linux_x86_64, linux-x64, etc.).
What the Go code doesn't cover: the shell. The install script (scripts/install.sh) that handles the curl | sh onboarding path was bash/zsh-only. It wrote export PATH=... into .bashrc, .zshrc, or .profile. Fish, the third most popular interactive shell, was completely unsupported. The binary would install, but the user's shell couldn't find it. For a tool whose entire value proposition is "install any program from your terminal," having the install script fail on a common terminal is a gap.
The Contribution
Issue #49 reported the fish problem in February 2026, and it was on the v0.2.0 roadmap. I picked it up.
The fix was about 70 lines added to scripts/install.sh. Fish uses set -gx PATH instead of export PATH=, and its config file lives at ~/.config/fish/config.fish (or wherever $XDG_CONFIG_HOME points). The implementation detects fish by checking whether the config file exists or whether fish is available on PATH, resolves the config path respecting XDG conventions, creates the directory if needed, and writes the PATH entry in fish syntax. It also handles GITHUB_TOKEN persistence (parm uses this for GitHub API rate limits) with the fish equivalent. If a user has both bash and fish installed, both configs get updated. The deduplication logic (grep for the bin directory before appending) follows the same pattern the script already used for bash/zsh.
Getting into the codebase took about fifteen minutes. The install script was self-contained, and the existing bash/zsh code was a clear template for the fish additions. PR #51 was approved with "lgtm" and merged the next day with "Merged, thank you for your contribution!" No review rounds, no changes requested. Clean in, clean out.
The Verdict
parm is for developers who install CLI tools from GitHub and are tired of the manual download-extract-chmod-symlink dance. If you've ever navigated to a GitHub releases page, scrolled through thirty assets to find the right one for your platform, downloaded it, extracted it, figured out which binary inside the tarball is the one you actually want, chmod'd it, and moved it to somewhere on your PATH, parm automates all of that.
The no-registry approach is either a feature or a concern depending on your threat model. There's no vetting, no review process, no curated list. You point parm at a repo and trust the maintainer's releases. The README is honest about this. For tools you already trust (ripgrep, fd, bat, delta), it's faster than Homebrew. For tools you've never heard of, you're on your own.
The project has momentum. Version pinning just shipped. Fish shell support (that's us) just landed. Windows shim support is on the roadmap. The maintainer is responsive and the codebase is clean enough that contributions land quickly. What would push parm further: a parm doctor command that validates your setup, shell completions for the major shells, and better error messages when a release doesn't have a compatible asset. But the core works today, and it's already replaced a chunk of my manual workflow.
Go Look At This
If you install CLI tools from GitHub, try parm. parm install junegunn/fzf and see how it feels. If you use fish, the installer now works thanks to PR #51.
Star the repo. Check the open issues. The v0.2.0 milestone has clear feature requests if you want to contribute.
This is Review Bomb #10, a series where I find under-the-radar projects on GitHub, read the code, contribute something, and write it up. If you know a project that deserves more eyeballs, drop it in the comments.
This post was originally published at wshoffner.dev/blog. If you liked it, the Review Bomb series lives there too.
Top comments (0)