A unified keybinding cheatsheet generated from your configs — which-key for your whole machine
Project description
rune
One keybinding cheatsheet for your whole machine, generated from your configs.
which-key is great inside one program. rune is for the stack around it: tmux,
Neovim, your shell, your terminal, your window manager, Git aliases, and the
little binds you forget until you need them.
A hand-kept cheatsheet (a gist, a wiki page, a sticky note) drifts the moment
you change a config. rune regenerates from the configs themselves, so it's
never out of date. It also does two things a static sheet can't: doctor
finds conflicts across tools — a window-manager chord silently eating a
Neovim mapping — and the chord normalizer makes wildly different spellings
comparable (cmd-alt-ctrl-shift-h, hyper+h, ^A, <C-w> are all the same
kind of thing to rune).
rune show # interactive terminal cheatsheet
rune doctor # find duplicate and shadowed chords
rune which cmd-h # where is this bound?
rune export --html k.html # self-contained cheatsheet + keyboard page
rune build -o keys.json # JSON for another renderer, such as sigil
Install
pipx install rune-cheatsheet
From a clone:
pip install -e .
rune is pure Python 3.11+ and uses the standard library only.
Shell completions
rune completions {bash,zsh,fish} prints a completion script — subcommands,
flags, and even extract's tool names — generated from rune's own parser, so
it can't drift out of sync.
# bash — add to .bashrc
source <(rune completions bash)
# zsh — either eval it directly in .zshrc...
source <(rune completions zsh)
# ...or write it to a file on fpath (needs autoload -Uz compinit && compinit)
rune completions zsh > ~/.zfunc/_rune
# fish — add to config.fish
rune completions fish | source
Platforms
macOS and Linux are first-class — most extractors target tools native to one
or the other (AeroSpace/Hammerspoon/skhd vs. Sway/Hyprland), and each fails
soft when its tool isn't there. Run rune extractors --check to see what's
live on your machine. On Windows, build, export, and doctor work; rune show falls back to plain text instead of the curses UI.
Quick Start
rune init
rune show
rune init writes a small rune.toml. rune show reads it, extracts bindings,
and opens a terminal UI. Press k in the UI for the keyboard view.
No config yet? rune show still tries common tools so you can see whether it is
useful before wiring anything up.
What It Reads
rune can read bindings in two ways. Mix them freely.
Native Extractors
These read real bindings with no annotation:
[[extract]]
tool = "tmux"
[[extract]]
tool = "git"
[[extract]]
tool = "nvim"
path = "~/.config/nvim/lua/keymaps.lua"
Current extractors include tmux, Git aliases, zsh, bash, fish, Neovim, WezTerm, Ghostty, Kitty, Alacritty, Helix, VS Code, AeroSpace, Hammerspoon, skhd, Vim, Sway, Hyprland, readline, and Emacs.
Check what works on your machine:
rune extractors --check
Inline Annotations
Use annotations when you want the description to be yours:
# @rune section Windows
# @rune family system
# @rune row caps + h :: focus left
# @rune row caps + l :: focus right
# @rune end
cmd-alt-ctrl-shift-h = "focus left"
cmd-alt-ctrl-shift-l = "focus right"
@rune is the current marker. The old @cs marker still works.
If an extractor and an annotation produce the same section, the annotation wins. Extraction gives coverage; annotations give taste and authority.
A path containing *, ?, or [ is treated as a glob and expands to one
annotation source per match (path = "~/.config/nvim/lua/keymaps/*.lua").
Example Config
[[extract]]
tool = "tmux"
[[extract]]
tool = "git"
[[annotate]]
path = "~/.config/aerospace/aerospace.toml"
[[view]]
id = "term"
label = "Terminal"
key = "1"
columns = [["tmux-prefix"], ["git-aliases"], []]
There is a fuller example at
examples/dotfiles.rune.toml.
Output
rune showopens a terminal cheatsheet with search and a keyboard view.rune export --html keys.htmlwrites one portable HTML page.rune export --md keys.mdwrites Markdown for docs or a wiki.rune build -o keys.jsonwrites the stable JSON contract.rune listwrites a flat TSV, one row per chord: key, action, section, source.
rune list is the fzf/grep/awk surface — no nested JSON to unwrap:
rune list | fzf
Filtering (the TUI's / search and every --filter flag) is substring
or subsequence — "wndwfcs" finds "window focus" even with the spaces
dropped and half the letters skipped.
The HTML and TUI keyboard views show physical keys by modifier layer. That makes used chords and free chords visible at a glance. The layout assumes an ANSI US-QWERTY physical keyboard for now.
Conflicts
rune doctor
doctor looks for:
- duplicates: the same chord bound twice in the same context
- shadows: an outer layer catches a chord before an inner layer can see it
For example, a window-manager binding can shadow a terminal, tmux, shell, or editor binding. Modal bindings such as tmux prefix maps and Vim leader maps are handled separately so intentional layers do not look like bugs.
Sample output, from a skhd config with a repeated binding and a chord that also lives in zsh:
analyzed 4 chord(s) across 2 context(s): skhd, zsh
duplicate (one silently wins):
⚠ cmd+h bound 2× in skhd — one silently wins
skhd yabai -m window --focus west ← extractor:skhd
skhd yabai -m window --focus west ← extractor:skhd
shadow (outer layer eats the key):
⚠ ctrl+h: skhd grabs it before zsh ever sees it
skhd yabai -m window --focus west ← extractor:skhd
zsh backward delete char ← extractor:zsh
2 conflict(s).
doctor's exit code is the conflict count, so it wires straight into CI or a
pre-commit hook.
Before binding a new chord, check whether it's already taken:
rune which cmd-h
which looks up a chord across every tool rune knows about and prints every
binding it finds, using the same normalizer as doctor — so cmd-h and ⌘h
find the same match. It exits 0 if the chord is bound and 1 if it's free, so
it also works as a script check.
sigil
rune owns the data and its own renderers (TUI, HTML, Markdown); rune build
emits a stable JSON contract that other tools can consume too.
sigil is one such consumer — a native macOS
HUD that reads that JSON and renders it as an overlay. Nothing about the
contract is macOS- or sigil-specific; any renderer of the JSON works the same
way.
(rune's keyboard-cheatsheet feature started life inside sigil and was later pulled out to stand on its own.)
Status
v0.1. The JSON shape is intended to be stable. Extractors are best effort. Annotate anything you want to be exact, polished, or personal.
For internals and maintenance notes, read docs/GUIDE.md.
Authorship
This project and its docs were written with AI assistance. Care was taken to keep the code and explanations readable by both humans and AI agents: short sections, direct examples, stable names, and comments where they earn their place.
License
MIT. See LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file rune_cheatsheet-0.1.0.tar.gz.
File metadata
- Download URL: rune_cheatsheet-0.1.0.tar.gz
- Upload date:
- Size: 55.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2ad337016e3b14dd0d6565179ef1f5105a2f5a20e394ecb154398d7eb4adcd90
|
|
| MD5 |
e060fade665c9dfcae0d6904e54b829f
|
|
| BLAKE2b-256 |
d33e0cad0b87c5906cb9bfe42e827fcaa8abe3110c117ac0955f2ba3071745fc
|
Provenance
The following attestation bundles were made for rune_cheatsheet-0.1.0.tar.gz:
Publisher:
release.yml on adames/rune
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
rune_cheatsheet-0.1.0.tar.gz -
Subject digest:
2ad337016e3b14dd0d6565179ef1f5105a2f5a20e394ecb154398d7eb4adcd90 - Sigstore transparency entry: 2084235343
- Sigstore integration time:
-
Permalink:
adames/rune@fc5494890efaa9bd7daaaccd30513fd4baea64da -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/adames
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@fc5494890efaa9bd7daaaccd30513fd4baea64da -
Trigger Event:
push
-
Statement type:
File details
Details for the file rune_cheatsheet-0.1.0-py3-none-any.whl.
File metadata
- Download URL: rune_cheatsheet-0.1.0-py3-none-any.whl
- Upload date:
- Size: 53.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7501bf3300479cdc3c43f978330753b21376f8a39261e3a7ae4a69e1c95fc7e7
|
|
| MD5 |
f908ea404457a998e556b4c25e8fb64f
|
|
| BLAKE2b-256 |
68b73190676ab194852bbbc2964cf8023214874268db32c6a9aed6562fab9ee0
|
Provenance
The following attestation bundles were made for rune_cheatsheet-0.1.0-py3-none-any.whl:
Publisher:
release.yml on adames/rune
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
rune_cheatsheet-0.1.0-py3-none-any.whl -
Subject digest:
7501bf3300479cdc3c43f978330753b21376f8a39261e3a7ae4a69e1c95fc7e7 - Sigstore transparency entry: 2084235359
- Sigstore integration time:
-
Permalink:
adames/rune@fc5494890efaa9bd7daaaccd30513fd4baea64da -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/adames
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@fc5494890efaa9bd7daaaccd30513fd4baea64da -
Trigger Event:
push
-
Statement type: