carat.cr
carat
All-in-one package manager for Crystal.
Extends shard.yml with a binaries: section — fully compatible with standard Crystal installs: shards install / shards build / shard.lock keep working untouched (shards simply ignores the extra key).
- Build and share your Crystal shards like Ruby gems — a package can be a tool you use right now, not just a library you require.
- System-wide install as a system binary —
carat install owner/repoputs a ready-to-run release binary into~/.carat/bin(on your PATH). One command, immediately useful. No manual installs, no PATH pain. - Compile flags per target system — Windows, Linux and Darwin often need different (link) flags;
binaries:in shard.yml declares them per OS, so the same shard builds everywhere. - Real-world builds — custom
main:files, nativeprebuild:steps (rake/cmake/make), explicit archives and link flags: GUI and app shards install as easily as hello-world CLIs. - Project mode too —
carat installin a project delegates toshards installand builds binaries into./.carat/bin;carat execruns them likebundle exec. - Self-hosting — carat installs and updates itself through carat.
- Shell autocompletion out of the box — bash, fish and zsh:
carat exec <TAB>completes your project binaries (see Shell autocompletion).
In Ruby, a package can be a tool: gem install gives you a working binary in one command, immediately useful. Crystal shards never had that — a CLI shard meant git clone, crystal build, and hand-editing your PATH. carat fixes it:
$ carat install OrelSokolov/hello_binary
$ hello_binary
hello_binary 1.1.0: Hello from a carat-installed shard!
One command: the shard is fetched from GitHub, its binaries: are built with crystal build --release, and self-contained binaries land in ~/.carat/bin — on your PATH, ready to use. No manual installs, no PATH pain, no "first clone five repos and read the README".
The idea
A package should bring value the moment it's downloaded. If a shard ships a binary — a CLI tool, a code generator, a formatter — installing it should be as simple as installing a gem:
| Ruby | Crystal with carat |
|---|---|
gem install rubocop |
carat install crystal-tools/linter |
rubocop just works |
linter just works |
carat treats binaries: in shard.yml as a first-class delivery format: the binary is the package. Libraries keep working the usual way — carat install inside a project delegates to shards install.
Quick start
carat install owner/repo # from GitHub: owner/repo shorthand
carat install name # bare name → github.com/<name>/<name>
carat install https://github.com/owner/repo.git
carat install ~/code/my-tool # local checkout
carat install . # the shard in the current directory — global
# install while developing it locally
Sources are resolved in that order; the built binaries are the same in every case.
Commands
| Command | Behavior |
|---|---|
carat install <src> |
Global install: clone → release build → binaries in ~/.carat/bin |
carat install . |
Global install of the shard in the current dir (local development) |
carat install |
In a project: delegates to shards install, builds binaries: into ./.carat/bin |
carat update <src> |
Compares the installed version with the remote; installs if newer |
carat update |
In a project: delegates to shards update, rebuilds ./.carat/bin |
carat exec <cmd> [args] |
Like bundle exec: runs <cmd> with ./.carat/bin prepended to PATH |
carat completion install |
Install shell autocompletion (bash / fish / zsh) |
Both install and update accept --with-examples to also build the optional examples: section (see below).
Make your shard instantly installable
Add a binaries: section to shard.yml — carat's delivery contract. The simplest form is a list (entrypoints default to src/<name>.cr):
binaries:
- hello_binary
The full form covers real-world builds — custom main files, native prebuild steps, compiler and linker flags that differ per OS:
binaries:
widgets_gallery:
main: examples/widgets_gallery.cr # any file, not just src/
prebuild: rake build:native # run in the shard root first
flags: # crystal build flags
"*": [--something]
darwin: [-DUSE_METAL=0]
link_flags: # linker flags, per-OS selectors
"*": [-Llib]
linux: [lib/libfoo.a] # explicit archive — ld order safe
windows: [/LIBPATH:lib]
Selector resolution follows crosspack's hosts: rules: exact OS family (linux / darwin / windows), then * — first hit wins. A plain list instead of a mapping applies to every OS. Relative paths in link_flags are resolved against the shard root (mold/ld do not open relative --link-flags paths).
Bump the version in shard.yml, push — every user's carat update picks it up. No releases page, no tarballs, no install scripts.
shards ignores the extra binaries: key, so the shard stays a perfectly normal shard — shards install / shards build keep working (verified with shards 0.20).
Optional examples: examples:
examples: is the same contract as binaries: (list or full mapping form), but for demo/sample binaries. It is never built by default — recursive installs would balloon quickly — only on demand:
carat install owner/repo --with-examples # global: binaries in ~/.carat/bin,
# examples in ~/.carat/examples
carat install --with-examples # project: examples in ./.carat/examples
examples:
widgets_gallery:
main: examples/widgets_gallery.cr
Example binaries land in a separate dir (~/.carat/examples / ./.carat/examples), so they never pollute your PATH.
What happens on install
- Fetch —
git clone --depth 1into a staging dir (local directories are copied as-is). - Store — sources are archived at
~/.carat/shards/<name>/<version>/with acurrentversion pointer, so installed versions are inspectable and switchable later. - Build — every
executables:entry is compiled withcrystal build --releasefrom the stored copy and validated by magic bytes (ELF / Mach-O / PE) before it's allowed into the bin dir. - Link — the binary is renamed into
~/.carat/bin/. Overwriting an existing binary always printsWARNING: OVERWRITING WITH NEW BINARYand is recorded in the audit log (~/.carat/logs/carat.log), so silent hijacking of your PATH is impossible.
Layout
~/.carat/bin/ global binaries (entry points only)
~/.carat/examples/ global example binaries (--with-examples)
~/.carat/shards/<name>/<ver>/ source store, per version
~/.carat/shards/<name>/current installed version pointer
~/.carat/logs/carat.log install/overwrite/update audit log
~/.carat/tmp/ staging for clones
./.carat/bin/ project binaries (carat install / carat exec)
./.carat/examples/ project example binaries (--with-examples)
Only self-contained binaries ever go into the bin dir — no assets, no wrappers, no PATH pollution.
Shell autocompletion
carat completion install
- bash — registers
complete -C carat caratin the XDG bash-completion dir (~/.local/share/bash-completion/completions/): no rc edits, live on the first TAB in a new shell. - fish — writes
~/.config/fish/completions/carat.fish(when fish is installed). - zsh — prints a two-line
bashcompinitsnippet for~/.zshrc.
What completes:
carat <TAB>— subcommands (install,update,exec,completion,--help,--version)carat exec <TAB>— the binaries of the current project (./.carat/bin):caratitself is excluded, and only the first argument is completed — the words after it belong to the launched program, and a fully typed word is never re-offered (no duplicate inserts on repeated TABs)carat update <TAB>— globally installed shards
Installing carat
No packages, no sudo — carat is a plain console binary:
git clone https://github.com/OrelSokolov/carat.cr.git && cd carat.cr
rake install
rake install builds the release binary, copies it to ~/.carat/bin (the first binary there is carat itself) and appends ~/.carat/bin to your PATH — ~/.bashrc / ~/.zshrc on Unix, the user PATH on Windows. Idempotent: run it as many times as you like.
On a fresh host without a toolchain, crosspack sets it up first: gem install crosspack && crosspack deps.
After that, carat updates itself like any other shard:
carat update OrelSokolov/carat.cr
Rebuilding over the running binary is safe (build-to-temp + rename).
Status and roadmap
v0.1 focuses on the core promise: gems-style install of binary shards from GitHub and local checkouts.
Next:
- Shardbox lookup for bare names,
carat use <name>@<ver>version switching carat list/carat uninstall- Full dependency resolution on a fork of shards — carat as the single package manager replacing it
License
MIT — see LICENSE.
carat.cr
- 0
- 0
- 0
- 0
- 0
- 20 minutes ago
- October 27, 2026
MIT License
Tue, 27 Oct 2026 17:06:14 GMT