v0.1 · darwin & linux · zero dependencies

Install things.
Undo things.
Instantly.

HopCLI is a package manager for the terminal. Every install runs as one transaction — downloaded, verified and linked in parallel — and every change is one command away from undone, with no re-download.

zsh
$ hop install ripgrep fd bat jq
install ripgrep 15.2.0 1.7 MB
install fd 10.5.0 1.3 MB
install bat 0.26.1 3.1 MB
install jq 1.8.2 822 KB
4 to install · 6.8 MB to download
==> Downloading 4 packages
jq
fd
ripgrep
bat
✓ installed 4 in 0.82s · 6.8 MB downloaded
$ hop rollback
✓ rolled back to generation 2 (0.012s, no download)
0.82s4 tools, parallel
12msfull rollback
0deps to build hop
The pitch

Homebrew taught everyone what a friendly package manager feels like. HopCLI keeps the feeling and fixes the mechanics.

Nothing here is a knock on Homebrew — it's the reason a whole generation of developers expects installing a CLI tool to be one line. HopCLI is what that idea looks like designed around a transaction log instead of a mutable prefix.

Homebrewhop
Refreshing the index clones or pulls a large git repository of formula files one conditional HTTP request — usually a 304, done in milliseconds
Installing downloads and links serially, mutating the prefix as it goes every artifact downloads in parallel, verified before anything touches disk
A failed install can leave a half-linked formula behind changes nothing — the previous generation stays live until the very last step
Undoing a change not really a supported operation hop rollback — a symlink swap, ~10ms, no network
What a recipe can do arbitrary Ruby, executed on your machine at install time a JSON document. hop never executes a recipe — only downloads and unpacks what it names
Checksums present on many formulae, absent on others every built-in recipe is SHA-256 pinned against bytes hop actually downloaded and tested
Project reproducibility a Brewfile, with no lockfile hopfile.toml + hop.lock — the second machine gets the same bytes as the first
Startup cost a Ruby interpreter boots on every invocation a static binary — single-digit milliseconds
Leftover dependencies accumulate; brew autoremove is a separate step you have to remember swept automatically the moment nothing needs them anymore
How it works

Nothing is ever edited in place

hop never modifies an installed package. It builds a new, complete snapshot — a generation — and switches to it with one filesystem rename.

Every package hop installs lands in a content-addressed store, named by its own hash: ripgrep-15.2.0-3750b2e9…. That directory is never touched again — not by an upgrade, not by a removal.

An install, upgrade or removal computes an entirely new generation: the full list of what should be on your PATH. Once every artifact is downloaded and verified, hop does the only thing that actually changes your environment — it repoints one symlink, current → profiles/7.

That rename is atomic at the filesystem level. It can't half-happen. Every earlier generation is still sitting on disk, which is the entire mechanism behind hop rollback: pointing the same symlink at an older number.

generation 5 rg 14.1 · fd 9.0 generation 6 rg 15.2 · fd 9.0 generation 7 rg 15.2 · fd 10.5 · jq 1.8 STORE (by hash) ripgrep-15.2.0-3750… fd-10.5.0-b67e18… fd-9.0.0-a12f90… jq-1.8.2-2d7534… rg-14.1.0-9c0021… current → hop rollback

generation 7 is a symlink target — generations 5 and 6 sit untouched, one rename away

The index

Every recipe, checksum-verified by inspection

The built-in index isn't hand-typed. A generator resolves each package's latest release, downloads the real artifact, and derives its checksum by opening it — nothing here is guessed. Close to 200 entries come straight from Homebrew's own bottle infrastructure, with their real dependency graphs relocated into hop's own store rather than Homebrew's. Images span more than Linux cloud builds: freebsd-vm, openbsd-vm and netbsd-iso are genuinely non-Linux, fedora-workstation and archlinux-iso are real installer ISOs rather than cloud images, security-focused distros like kali-linux and tails sit alongside everyday ones, and eleven separate macos-* recipes span Lion through Tahoe from Apple's own infrastructure. raspios-lite honestly has no amd64 build at all — Pi hardware is arm64-only, so hop says so. Windows isn't here on purpose: Microsoft's ISOs sit behind an interactive, EULA-gated web flow with no stable checksum manifest, which every other entry in this index deliberately has — that's the line hop stays behind.

284 recipes · 1,048 verified artifacts · darwin-arm64, darwin-amd64, linux-amd64, linux-arm64 hop search <name>
Coming from Homebrew

Bring what you already have

hop migrate reads Homebrew's Cellar directly — brew doesn't need to be running, and nothing about your Homebrew installation is touched.

zsh
$ hop migrate --dry-run ==> Homebrew at /opt/homebrew 284 formulae installed · 68 you asked for yourself 11 casks (GUI apps — hop manages command-line tools, so these stay with brew) hop can take these over + flyctl 0.4.101 → 0.4.102 + gh 2.100.0 + grpcurl 1.9.4 + mkcert 1.4.4 + shellcheck 0.11.0 61 formulae have no hop recipe yet — these stay with brew; the two coexist fine.

Nothing gets uninstalled without you typing it

hop installs the overlap under its own store and tells you exactly what to run to retire the Homebrew copies — it never calls brew uninstall for you. Removing software installed by another tool is your decision, not a package manager's.

Get it

Ships as one static binary

Built entirely on the Go standard library — no dependency to audit, no toolchain to install beyond Go itself. Works offline the moment it's built: the recipe index is compiled into the binary.

clone and build

$ git clone https://github.com/samuelbanapour/hopcli && cd hop && make build

or, with Go installed

$ go install github.com/samuelbanapour/hopcli/cmd/hop@latest

then put it on PATH

$ eval "$(hop shellenv)" # add to ~/.zshrc