Repository navigation
How shelf works
Six steps run whenever a lock gets built, first install, --update, --reinstall, or a shelf source run that finds the lock stale.
- The config parses and validates. Unknown TOML keys are ignored, so leftovers from another tool do not break the file.
- Sources install concurrently, eight at a time by default. Git sources clone or check out, remote sources download with a conditional GET, local paths verify, inline plugins skip this step entirely.
-
buildcommands run throughsh -cin the plugin's source root. - Files select from
use,match, or the shell defaults, minusignore. - Shelf writes the runtime lock and the revision manifest.
- The script renders.
[env]first with names sorted, the defer scheduler when zsh needs it, then oneeval '...'line per plugin.
Each plugin renders into its own eval, so one eval "$(...)" over the whole output still parses each plugin as a unit. Aliases and functions survive. Inline plugins get special handling in zsh, their text feeds source /dev/stdin <<'SHELF_0', because zsh parses an eval'd string whole and would drop aliases defined on earlier lines.
shelf source with a valid lock skips steps 1 to 5. It verifies the fingerprint, restores anything missing, and renders straight from the lock. That is the warm path every shell startup hits.
~/.config/shelf/
config.toml # your configuration
plugins.lock # revision manifest, commit this
plugins.work.lock # one manifest per profile
~/.local/share/shelf/
plugins.lock # runtime lock, do not commit
plugins.work.lock # one runtime lock per profile
repos/ # git sources
github.com/owner/repo/
gist.github.com/user/id/
downloads/ # remote sources
example.com/path/to/plugin.zsh
plugins/ # leftovers from older versions, pruned automatically
Flags and the XDG variables move these directories, shelf path prints the resolved set. Two plugins can point at the same repository with different dir values and share one clone.
The runtime lock records everything it was built from. A rebuild happens when any of it changes:
- the config file contents, byte for byte
- the value of
SHELF_SHELL - the revision manifest contents
- the active profile and the resolved shell
- the data directory
- the presence of every selected file on disk
Edit the config, switch profiles, delete a plugin file, or drop a manifest, and the next shell start relocks once. shelf status and shelf doctor report the same condition as lockfile is stale or selected plugin files are missing instead of rebuilding it.
[env] renders first, sorted by variable name, so every plugin sees the same environment. Plugins follow the order they appear in the config, shelf add appends new ones at the end. Inside a plugin the apply templates run in the order you list them, and the default template wraps hooks.pre around the files with hooks.post after them.
Installs run eight at a time by default, --concurrency N changes the limit per command. Two plugins sharing one clone serialize on a per-directory lock, so a shared repository never sees two writers.
Shelf locks the config directory while it works. Readers share the lock, writers take it exclusively, and a second process prints Blocking waiting for file lock on <dir> before it waits. shelf status reads the checked-out revision of every git plugin in parallel and reports in config order.