Metadata-Version: 2.4
Name: rune-cheatsheet
Version: 0.1.0
Summary: A unified keybinding cheatsheet generated from your configs — which-key for your whole machine
Author: adames
License: MIT
Project-URL: Homepage, https://github.com/adames/rune
Project-URL: Source, https://github.com/adames/rune
Project-URL: Issues, https://github.com/adames/rune/issues
Project-URL: Changelog, https://github.com/adames/rune/blob/main/CHANGELOG.md
Keywords: cheatsheet,keybindings,dotfiles,tmux,neovim,which-key,hud
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Topic :: Utilities
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Dynamic: license-file

# rune

[![ci](https://github.com/adames/rune/actions/workflows/ci.yml/badge.svg)](https://github.com/adames/rune/actions/workflows/ci.yml)

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).

```sh
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
```

![rune demo: show, doctor, which](docs/demo.gif)

## Install

```sh
pipx install rune-cheatsheet
```

From a clone:

```sh
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.

```sh
# 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

```sh
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:

```toml
[[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:

```sh
rune extractors --check
```

### Inline Annotations

Use annotations when you want the description to be yours:

```toml
# @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

```toml
[[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`](examples/dotfiles.rune.toml).

## Output

- `rune show` opens a terminal cheatsheet with search and a keyboard view.
- `rune export --html keys.html` writes one portable HTML page.
- `rune export --md keys.md` writes Markdown for docs or a wiki.
- `rune build -o keys.json` writes the stable JSON contract.
- `rune list` writes a flat TSV, one row per chord: key, action, section, source.

`rune list` is the fzf/grep/awk surface — no nested JSON to unwrap:

```sh
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

```sh
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:

```sh
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](https://github.com/adames/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`](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`](LICENSE).
