mandible
A TUI manual for every command-line tool you have
Install • How it works • Coverage • Keys
man tells you about one command. --help tells you about one invocation. Neither explores a tool you don't already know.
$ mandible docker
[!TIP] Try running
mandible mandible
Install
Linux and macOS, on x86_64 and arm64. Windows is not supported: the containment above
relies on POSIX process groups, and Windows tools use conventions (/?, PowerShell's
own help system) this project does not speak.
Cargo
# fetch prebuilt binary
cargo binstall mandible
# build from source
cargo install mandible
Fedora / EPEL (COPR repo)
sudo dnf copr enable as-foss/mandible
sudo dnf install mandible
Nix
nix run github:AS-FOSS/mandible
Homebrew (macOS or Linux)
brew install as-foss/mandible/mandible
Debian / Ubuntu (signed apt repo)
sudo curl -fsSL https://as-foss.github.io/mandible-apt/mandible-archive-keyring.gpg \
-o /usr/share/keyrings/mandible-archive-keyring.gpg
sudo tee /etc/apt/sources.list.d/mandible.sources >/dev/null <<'EOF'
Types: deb
URIs: https://as-foss.github.io/mandible-apt
Suites: stable
Components: main
Architectures: amd64 arm64
Signed-By: /usr/share/keyrings/mandible-archive-keyring.gpg
EOF
sudo apt-get update && sudo apt-get install mandible
Standalone binaries, .deb and .rpm packages, each with a .sha256, are attached
to every release.
How it works
There is no per-tool logic anywhere in this project. No if tool == "docker", no
vendored catalogue of hand-written definitions. That approach is convenient for a
week and unmaintainable ever after. It is always slightly out of date, and it is
wrong in ways you cannot see from the outside.
The insight it runs on instead: help text isn't written by hand, it's generated, and only a small closed set of generators exists. mandible works out which framework produced a tool's output, then applies that framework's grammar. For example:
[!TIP] Open straight at a subcommand:
mandible cargo clippy. Subcommands that live as separate<tool>-<sub>binaries (cargo-clippy,git-lfs) show up too, markedunverified: they were found onPATH, not in the parent's own help.
Is it actually universal?
That claim is measured, not asserted. cargo xtask coverage runs the pipeline against
every executable on your PATH and scores each one: sources used, framework detected,
nodes, flags, percentage described.
It also carries a structure-sanity column, which exists because a coverage number
alone can be gamed by the very failure it should catch. %described once reported a
tool as fine at 100% while 39 of its 40 subcommands had been fabricated out of wrapped
prose. A metric that improves when the tool gets worse is worse than no metric.
CI gates every change against a fixed tool list, and sweeps the whole PATH
separately for the broad picture.
Coverage is not accuracy, though, and the honest number is lower than a green badge
suggests. Accuracy comes from a human-reviewed audit, where a person checks a randomly
drawn sample of real tools against each tool's own --help text. The sample is
committed to the repo before any verdict is recorded, so it cannot be quietly redrawn
once the results look bad. In the most recent audit, 25 of 43 tools parsed fully
correctly, about 58%. A sample that small leaves real statistical slack, so the true
rate across all tools could sit anywhere between about 43% and 72%. Tools that
mandible itself marks ok do better, at 80% correct with a plausible range of 61%
to 91%.
mandible is useful today and wrong often enough that you should check anything
surprising against the tool's own --help.
[!TIP] Something parses wrong? Run
mandible --report <tool>and paste the output into an issue. That takes two minutes and is a complete contribution on its own.
Settings live in ~/.config/mandible/config.toml. mandible never creates the
file; the settings tab of the ? pop-up writes it, one key at a time, and
leaves your comments and other keys alone. A key that is unknown or has a bad
value is ignored and the footer says which line until you press a key.
# ~/.config/mandible/config.toml
[]
= false
= false
= true
= false
[]
= "tool"
= "tool"
[]
= "default"
theme is default, light, high-contrast, gruvbox-style, nord-style,
dracula-style, catppuccin-style, custom or none. With custom, seven
more keys give the colours, each a name (black to bright-white) or a number
from 16 to 255:
[]
= "custom"
= "bright-cyan" # flag spellings, focused border
= 220 # low confidence, notices
= "bright-black" # secondary text
= 246 # section headings
= 240 # group headings
= 237 # the selection bar
= 252 # text on the bar
| Key | Values | What it does |
|---|---|---|
raw_view |
true, false |
Start in the tool's own --help view, as if t were pressed |
horizontal_scroll |
true, false |
false folds long lines to the pane. true keeps one row per line and scrolls sideways, in the parsed and the raw view. w flips it for the view on screen |
description_preview |
true, false |
false draws a long description in full, in place |
ascii |
true, false |
Force the ASCII glyph set |
flags |
"tool", "az-sections", "az" |
Where s starts in the detail pane |
commands |
"tool", "az" |
Where s starts in the commands list |
theme |
a theme name, "custom" or "none" |
none draws no colour. custom uses the seven colour keys; a missing one keeps the default theme's value |
An environment variable beats the file, and the file beats the default.
Getting a tool's documentation right is mandible's job, not yours. If
something parses wrong, mandible --report <tool> plus an issue fixes it for
everyone. (Local per-tool corrections do exist for the impatient:
~/.config/mandible/overrides/<tool>.toml.)
Environment variables
| Variable | Effect |
|---|---|
NO_COLOR |
Same as theme = "none", and beats the file. TERM=dumb and piped output do the same |
MANDIBLE_ASCII=1 |
Force the ASCII glyph set, for terminals that mangle Unicode. Beats ascii |
MANDIBLE_CONFIG_DIR |
Read config and overrides from a different directory |
MANDIBLE_LOG |
Tracing filter, written to stderr |
Keys
? inside mandible lists every binding, and its second tab, reached with Tab,
changes the settings above. The footer keeps the important keys on screen: arrows or hjkl to move, / to search, Tab between panes, ←/→
to scroll wide lines sideways, t for the tool's own --help, s to sort the
focused pane A–Z, y to copy the selected flag, q to quit.
Search has two modes. names matches a command name or any spelling of a flag,
positional, modifier or environment variable; everything also searches
descriptions. In both, a row matches when it contains every word you type, in any
case, and a dash counts like any other character: size finds --blocksize,
no verify finds --no-verify. The best match is where the cursor jumps; every
other match stays in the tree. While you type, a dim suggestion shows where
Enter will land, or the closest name when nothing matches, and Tab takes it. / opens the first mode, pressing it again switches to the second.
Completions
The packages install shell completions for you. For a hand-built binary,
mandible --completions <shell> prints the script (bash, zsh, fish, and
more); drop it wherever your shell looks for completions. For zsh:
$ mkdir -p ~/.zfunc && mandible --completions zsh > ~/.zfunc/_mandible
then add fpath=(~/.zfunc $fpath) to ~/.zshrc, above its compinit line.
zsh silently skips completion directories it considers insecure (anything
under /tmp), so keep the file under your home.
The tool argument completes too, to the command names on your PATH:
mandible gi<TAB> suggests git rather than whatever files sit in the
current directory. zsh and fish do this today; bash's script format can't
express it yet.
Onto the prompt
y gets a spelling as far as the clipboard. To land it on the command line
instead, add the shell integration:
$ eval "$(mandible --shell-init bash)" # or: zsh — in your ~/.bashrc, ~/.zshrc
Now type a tool name, press Ctrl-X m, browse, and press Enter: the command
you selected (git commit --amend, say) replaces the line, ready to edit.
Quit with q and the line stays exactly as it was.
The binding is a few lines of shell around mandible --print-selection <tool>,
which browses as usual but makes Enter print the selection instead of
expanding the row (the UI draws on stderr, so stdout carries just that one
line). Bind it to a different key, or wrap it in your own widget, by reading
what --shell-init prints.
Diagnostics
$ mandible --doctor gh
framework: cobra (from artifact)
nodes: 29
flags: 2 (100.0% described)
--doctor reports which framework mandible identified, which sources
contributed, and how much of the tool it understood. It turns "mandible is
wrong about tool X" into "the cobra grammar mishandles Y", which is a bug
someone can actually fix.
A probe is stopped after 30 seconds and reported as a failure. --no-timeout
with --doctor or --report lets every probe run as long as it needs.
Running as root
mandible refuses to start as root, printing the flag that lifts the refusal.
A probe like fail2ban-client start --help reaches a daemon socket, and
under root that is an action, not a question. Pass --allow-root to proceed
anyway; mandible never asks for or gains privileges on its own.
Documentation
docs/design.md |
Design authority: the source model, the safety policy, and the measurement behind every non-obvious decision |
AGENTS.md |
The invariants table. Every entry names the failure it prevents |
Contributing
If a tool renders wrong, an issue is the fastest way to tell us. Run
mandible --report <tool> and paste the output into the issue form; it asks
for the rest (your OS, what you expected, a screenshot if you have one). Your
tool's version and its exact help text vanish when you upgrade, and nobody
else can recover them, so the report is a complete contribution on its own.
If you want to go further, audit mandible against the tools on your own machine. Every accuracy figure here comes from one Ubuntu machine on ARM, so whatever you have installed is probably software this project has never seen.
$ git clone https://github.com/AS-FOSS/mandible && cd mandible
$ cargo xtask audit contribute
The command asks for your GitHub login, scans your PATH, draws twenty tools
and opens each one in the normal interface next to its real help text. Press
t to see the raw text, then c, i, w or s for correct, incomplete,
wrong or skip. It saves after every verdict, so you can Ctrl-C and come back
later. When you finish, it writes your verdicts under
audit/submissions/<your-login>/ and prints the commands to commit them and
open the pull request. Merged audits sit under your name in the tree and are
credited in the release notes.
[!NOTE] When auditing, verify by hand and by eye only, in the interface itself:
cargo xtask audit contributeopens it for you, andmandible --review <seed>reopens an unfinished draw. mandible does have automated checks (pty renders of the TUI, andcargo xtask audit emitandreview, which print a tool's raw help text beside the tree as plain text for machines with no terminal), however none of them can tell you what was missed or which flags are wrong. That judgement is the whole point of an audit. So: no AI, no CI, nothing non-manual, and no verdicts formed from the text dump.
CONTRIBUTING.md has the details, plus writing tests and
changing the parser.