cmdlore 0.3.0

A command library that lives in your shell
Documentation

lore

CI crates.io license

A command library that lives in your shell. Press ctrl+g, type a few letters of what you are trying to do, and the command lands in your prompt ready to run.

lore in a terminal

The problem

The commands worth keeping are the ones you write once every few months: the kubectl invocation that finds the pod that keeps restarting, the openssl line that prints a certificate's expiry, the git incantation that finds which commit deleted a function. Shell history keeps them for a while and then loses them between machines, between sessions, or behind four hundred cd .. entries.

History is a log. What you want is a library: curated, described in words you would actually search for, versioned in git, and open one keystroke away.

Install

Nothing here needs administrator rights. Everything installs under your home directory. Every route that installs for you checks the archive against the sha256 published with the release and refuses anything that does not match.

Shell installer (macOS and Linux)

curl -fsSL https://raw.githubusercontent.com/alpcakin/lore/main/packaging/install.sh | sh

Shell installer (Windows, PowerShell)

irm https://raw.githubusercontent.com/alpcakin/lore/main/packaging/install.ps1 | iex

Both put the binary under your home directory and tell you if that directory is not on your PATH yet.

macOS and Linux (Homebrew)

brew install alpcakin/tap/lore

Windows (Scoop)

scoop bucket add alpcakin https://github.com/alpcakin/scoop-bucket
scoop install lore

Cargo

cargo install cmdlore

Prebuilt binaries

Every release carries an archive for Windows, macOS on Intel and Apple silicon, and Linux on x86_64 and arm64, plus a statically linked build that runs on any Linux whatever its glibc. Download one from the releases page, check it against the SHA256SUMS published beside it, unpack it, and put lore somewhere on your PATH.

Upgrading

To see which version you have, whether there is a newer one, and what to run:

lore version

Whichever way you installed lore is the way to upgrade it:

Installed with Upgrade with
Homebrew brew upgrade lore
Scoop scoop update lore
Cargo cargo install cmdlore --force
Shell installer Run the same one line command again
Prebuilt archive Download the new one and replace the file

Only the binary changes. The line in your profile asks the binary for its integration on every shell start, so there is nothing to set up again.

lore never replaces its own binary. Once a day, when you open the picker, it looks up the newest release in the background and mentions it the next time you open the picker, naming the command above that fits your install. It says so only for a new first or second number, never for a patch release. To hear nothing at all, set LORE_NO_UPDATE_CHECK=1.

Set up

lore setup

That detects your shell, shows you the single line it wants to add to your profile, and asks before writing it. The original file is backed up first. Open a new shell and press ctrl+g.

To use a different key:

lore setup --key alt-r

Write the key as ctrl-<letter> or alt-<letter>. Keys the terminal owns, such as ctrl-c and ctrl-m, are refused with the reason.

To take the keybinding out again, see Uninstall.

Using it

Inside the picker:

Key What it does
type Filter by command, description or tags
enter Put the command in your prompt
esc Close and leave the prompt alone
ctrl+s Save the command on your prompt, or one you ran
ctrl+e Edit the selected entry
ctrl+p Pin an entry to the top
ctrl+x Remove an entry, confirmed by pressing it again

Saving asks two things, one line at a time:

save: docker compose logs -f api
 for: Follow the api logs #debug

The command starts as whatever you had typed at the prompt, or the last command you ran, and up and down walk back through your history the way they do in the shell. Then say what it is for, in the words you would search with. Any word written as #tag becomes a tag, and the program and its subcommands, here docker, compose and logs, are added as tags on their own.

On the edit screen, ctrl+s saves from whichever field you are in.

lore never runs anything. It puts the command in your prompt and pressing enter stays your decision.

Entries frequently used rise to the top on their own. A command with one placeholder arrives with the gap already open and the cursor in it, so you can finish it with your shell's own completion. Commands with more placeholders ask for the values and remember what you typed last time.

Nothing here needs the picker:

lore save "kubectl logs -f <pod>" --desc "Follow a pod's logs #k8s"
lore save "terraform plan"          # asks what it is for
lore list --shell bash
lore edit k8s.logs.follow --desc "Tail a pod"
lore rm k8s.logs.follow

How the shell integration works

A keybinding has to be registered inside the shell process, so there is no way to install one without touching a profile file. Every tool in this category works the same way. lore setup appends one line between two markers:

# >>> lore >>>
eval "$(lore init bash)"
# <<< lore <<<

The snippet itself is fetched from the binary on each shell start rather than copied into the profile, so an upgraded binary cannot disagree with a stale copy on disk. It reads no files and does nothing but print. lore uninstall removes exactly the lines between the markers and leaves everything else alone.

Your recent commands go in, and the chosen one comes back, through two temporary files the snippet creates and deletes around each use. Neither travels on stdout: the picker draws under your prompt, which means asking the terminal where the cursor is, and that question goes out on stdout. A shell that captured stdout to read the result would swallow the question, and the panel would never open.

The answer comes back on the terminal's input, so the picker needs that on stdin too. zsh runs a widget's commands with stdin closed off, so its snippet hands the picker the terminal by name, and on macOS the binary can find its own terminal when it has to.

Upgrading the binary is enough for most changes, since the snippet is fetched from it on every shell start. When the release notes say to run lore setup again, the one line in the profile itself changed.

Using lore on more than one machine

Your library can follow you to a second laptop, a work machine or a server, through a private git repository that you own. Nothing is sent anywhere else, and this is optional: lore works fully on one machine without it.

With the GitHub CLI installed and logged in, run this on each machine:

lore sync init

The first time, it creates a private repository called lore-library on your GitHub account. On every other machine it finds that repository and connects to it. Without the GitHub CLI, create an empty private repository on any git host and give its address instead:

lore sync init git@github.com:you/lore-library.git

After that there is nothing to remember. Every save, edit and removal is synced in the background, and the picker fetches other machines' changes when it has not done so for a while. To sync right away, or to see why a sync failed:

lore sync
lore sync status

lore runs your own git, so it uses whatever login git push already uses, and never sees a password or token. On a server, that means the server needs access to the repository, for example through an SSH key.

Two machines can save at the same time without a merge conflict. lore merges by entry rather than by line, so new commands from both are kept. If the same entry was edited differently on two machines, the version synced first keeps its id and the other is kept as a copy, such as user.deploy-2, and the sync says so.

Only the library is synced. Which commands you use most is kept per machine.

Sync commits are made by lore, not by you, so saving a command does not file a contribution against your GitHub profile. To put your own name on them instead, set an identity on lore's clone of the repository:

git -C ~/.local/share/lore/sync config user.name "Your Name"
git -C ~/.local/share/lore/sync config user.email "you@example.com"

On macOS that directory is ~/Library/Application Support/lore/sync, and on Windows %APPDATA%\lore\data\sync.

Keep the repository private. Saved commands often contain server names, user names and internal addresses.

To stop syncing on a machine, run lore sync disconnect. Your library stays, and so does the repository. Set LORE_NO_AUTO_SYNC=1 to sync only when you run lore sync yourself.

If you would rather not use git at all, point LORE_CONFIG_DIR at a folder that Dropbox, iCloud Drive or OneDrive already syncs.

Your library

Your own commands live in one YAML file, meant to be read, edited and committed:

Platform Library Usage statistics
Linux ~/.config/lore/commands.yaml ~/.local/share/lore/stats.db
macOS ~/Library/Application Support/lore/commands.yaml ~/Library/Application Support/lore/stats.db
Windows %APPDATA%\lore\config\commands.yaml %APPDATA%\lore\data\stats.db
version: 1
commands:
  - id: k8s.logs.follow
    cmd: kubectl logs -f <pod> -n <namespace:default>
    desc: Watch what a pod is printing right now
    tags: [kubernetes, logs, debug]
    params:
      pod:
        desc: Pod name

Saving, editing and removing all splice single entries in and out of this file rather than rewriting it, so your comments and your ordering survive.

Usage statistics are kept in a separate file, and on most platforms a separate directory, because syncing your definitions through git should never produce churn or a merge conflict. They are local to the machine and never synced.

Set LORE_CONFIG_DIR and LORE_DATA_DIR to put either somewhere else, which is what a portable install on a stick wants.

The schema, including per shell command variants, placeholder syntax and how to hide builtins you do not want, is documented in docs/schema.md.

Around two hundred and forty commands ship compiled into the binary: the everyday git, docker, kubernetes, ssh, networking, file system, process, text, archive, node, python and rust commands, each described by what it is for rather than by its flags. Entries that only make sense on a posix shell are hidden on Windows rather than offered and failing. Anything you do not want, you can hide:

disabled:
  - node.*

Build from source

cargo build --release

Rust 1.88 or newer. cargo fmt and cargo clippy --all-targets -- -D warnings both have to pass, and CI runs them on Linux, macOS and Windows.

Uninstall

Up to four things were put on your machine: a sync connection if you made one, a line in your shell profile, the binary, and your library. Each is removed on its own.

1. Sync, if you set it up. This leaves the repository and your library alone:

lore sync disconnect

2. The shell integration

lore uninstall

This removes exactly the lines between the # >>> lore >>> and # <<< lore <<< markers from your profile and leaves everything else alone. The profile is backed up first, beside the original with .lore-backup added to its name, such as .zshrc.lore-backup. Delete the backup once you are happy.

If you set up more than one shell, run it once per shell with --shell bash, --shell zsh, --shell fish or --shell powershell. Do this before removing the binary, since it is the binary that knows where the profile is.

3. The binary, depending on how you installed it:

Installed with Remove with
Homebrew brew uninstall lore, then optionally brew untap alpcakin/tap
Scoop scoop uninstall lore, then optionally scoop bucket rm alpcakin
Cargo cargo uninstall cmdlore
Shell installer, macOS and Linux rm ~/.local/bin/lore
Shell installer, Windows Remove-Item -Recurse $env:LOCALAPPDATA\Programs\lore, then take that folder out of your user PATH in Settings if you want it gone too
Prebuilt archive Delete lore from wherever you put it

Removing the tap or the bucket only makes Homebrew or Scoop forget where lore came from. Leave it if you might install again.

4. Your library and statistics, only if you want them gone. They are the files listed under Your library. commands.yaml is the library you built; keep it if you might come back.

Platform Remove
Linux rm -r ~/.config/lore ~/.local/share/lore
macOS rm -r ~/Library/Application\ Support/lore
Windows Remove-Item -Recurse $env:APPDATA\lore

Contributing

Bug reports, suggested commands and pull requests are welcome. See CONTRIBUTING.md for how to build and test, and what makes a good builtin command.

Licence

Licensed under either of Apache License 2.0 or MIT License, at your option.