Spotlight-style launcher for two things:
- Makefile targets β executed locally via
make <target> - SSH hosts from
~/.ssh/configβ opens a plainssh <host>interactive session
Press Esc, type to filter, hit Enter.
cargo install --path .Add to your shell rc:
# ~/.zshrc
eval "$(runic init zsh)"
# ~/.bashrc (Esc is a readline prefix, so use double-Esc)
eval "$(runic init bash)"Requires fzf on PATH (brew install fzf).
On an empty prompt, press Esc. A fuzzy picker opens, showing:
- Targets from
runic.mkin the current directory (or any parent) - Targets from the project's
Makefile(if it exists) - Targets from
~/.runic.mk - Hosts from
~/.ssh/config
Pick one, it runs. The command lands in shell history so you can re-run with β.
Three files, merged in this priority (earlier wins on name collision):
Drop alongside your project's Makefile. Descriptions come from ## ... comments above each target.
## Spin up the dev stack
up:
docker compose up -d
## Tail the API logs
logs:
docker compose logs -f api
## Deploy to $(ENV)
deploy:
./scripts/deploy.sh $(ENV)
.PHONY: up logs deployIf your project already has a Makefile, runic reads it too. No duplication needed.
## Build release binary
build:
cargo build --release
## Run tests
test:
cargo testIf a target name exists in both files, runic.mk wins β useful for personal overrides without touching the shared Makefile.
Shortcuts you want from any directory:
## Open today's journal
journal:
$(EDITOR) ~/notes/$(shell date +%Y-%m-%d).md
## Sync dotfiles
dotsync:
cd ~/dotfiles && git pull && ./install.shIf a target references a variable that isn't defined anywhere (Makefile, environment, CLI args), runic asks for it on /dev/tty before running:
deploy:
ssh $(HOST) "cd /srv && ./deploy.sh"Pressing Enter on deploy β runic asks HOST: , you type prod.example.com, it runs make deploy HOST=prod.example.com.
Skip the prompt by passing directly:
runic run deploy HOST=prod.example.comOr set a default with ?=:
HOST ?= staging.example.comOptional. All fields have sensible defaults.
[ssh]
# Hostnames matching these patterns are hidden from the picker.
# `*` is the only glob metacharacter (one per pattern).
exclude = ["github.com", "git.*", "gitlab.*", "bitbucket.*"]
# If non-empty, ONLY hosts matching a pattern here appear.
include = []
[picker]
height = "50%"
[shell]
# How long the shell waits for a follow-up byte after Esc, in milliseconds.
# 10ms is near-instant and eliminates the ~400ms default Esc delay in zsh.
# Set to 0 to leave the shell's existing timeout untouched.
key_timeout_ms = 10Tip: If you're in tmux, add
set -sg escape-time 0to~/.tmux.confβ tmux adds its own 500ms Esc delay on top of the shell's.
| Command | What it does |
|---|---|
runic pick |
Open the picker (normally invoked via the Esc widget). |
runic run TARGET [VAR=VAL ...] |
Run a target directly, forwarding args to make. |
runic list |
List all discovered targets with their source file. |
runic edit |
Open the nearest runic.mk / Makefile / ~/.runic.mk in $EDITOR. |
runic init zsh|bash |
Print shell integration for eval. |
Runic doesn't do directory-entry hooks β use direnv for that. Runic is purely a launcher.
MIT β see LICENSE.