Skip to content

0.8.2 (part B): help command, did-you-mean, completions, man page, 100-column help, one error channel - #29

Merged
BitmapAsset merged 10 commits into
mainfrom
feat/cli-polish
Oct 14, 2026
Merged

BitmapAsset merged 10 commits into
mainfrom
feat/cli-polish

Conversation

@BitmapAsset

Copy link
Copy Markdown
Owner

Part B of 0.8.2: the CLI's help, discovery and error surface brought to the standard of large-vendor CLIs, with the parser kept hand-rolled (no clap, MSRV 1.78, zero external crates). No new dependencies of any kind.

What changes

  1. One command table (COMMANDS in probbit-cli/src/main.rs): every command, subcommand and flag with its arity, summary, usage, sections and examples. Every --help, probbit help, the completions and the man page render from it (usage.rs), and check_flags reads its flag lists from it, so help, completions and man cannot drift from what the parser accepts.
  2. probbit help [COMMAND [SUBCOMMAND]]: identical text to COMMAND [SUBCOMMAND] --help; probbit help alone = probbit --help. persona and live subcommands now have their own help.
  3. Did-you-mean: probbit: unknown command "persna"; did you mean "persona"? see \probbit --help` (Damerau-Levenshtein ≤ 2, ≤ 1 for words of ≤ 3 characters, or a unique prefix). Subcommands too (persona lnt→lint, live verfy→verify`). Exit code stays 2.
  4. probbit completions bash|zsh|fish: static scripts from the table; unknown shell → exit 2 with the list.
  5. 100-column help: top-level help starts with the version line, groups commands (persona · live · monitor · decide/run · mcp · other), ends with six examples; monitor --help split into usage / modes / flags / demo; the terminal hero screen fits 100 columns. Every flag and sentence of meaning kept.
  6. One error channel (documented in docs/CHEATSHEET.md "Errors"): a human line on stderr always; a bad input's {"error"} object on stdout when stdout is not a terminal (unchanged for Python, MCP, npm, examples, scripts — all read it piped) or with --json; at a terminal without --json only the line, never both. Bad commands/flags stay off stdout unless --json (code: "flag", or "command" with "suggestion"), preserving Python's empty-stdout flag contract. --json is accepted by every command.
  7. probbit man + committed docs/probbit.1 (test: equals the generated output). release.yml attaches probbit.1 (one line); install.sh installs it to ~/.local/share/man/man1/ (PROBBIT_MAN_DIR overrides), never fatally.
  8. README Install, CHEATSHEET, CHANGELOG ## 0.8.2 - unreleased, CONTRIBUTING (regenerate docs/probbit.1 after a help change or version bump).

Before → after (0.8.1 → this branch)

0.8.1 now
probbit help persona unknown command, exit 2 persona help, exit 0
probbit persna init x.yaml unknown command, no hint did you mean "persona"?, exit 2
probbit completions zsh unknown command a zsh script (zsh -n clean)
probbit man unknown command roff page; man -l docs/probbit.1 renders
top-level help lines > 100 cols 10 of 15 (longest 447) 0
monitor --help one 3,259-char paragraph block, longest line 205 sectioned, longest line ≤ 100
error at a terminal line + JSON object line only (--json for the object)

Tests

New: tests/cli_table.rs, cli_help.rs, cli_suggest.rs, cli_completions.rs, cli_layout.rs, cli_errors.rs (pipe, pseudo-terminal via script, --json), cli_man.rs; unit tests for the table and the distance. Existing help assertions updated for the new top-level layout. Locally: the full probbit-cli suite (release), the Python suite (44), npm/test-wrapper.cjs (13), examples/node/decide.mjs, scripts/bench/test_install_sh.sh and test_install_contract.py pass; cargo +1.78.0 build --release --locked --workspace is clean. zsh/fish syntax checks run where those shells exist (bash everywhere on Unix).

Notes

  • Binary size: 2.33 MB → 2.46 MB (release, aarch64-apple-darwin), the help text and the generators.
  • The man page header carries the version: the release bump must run cargo run -p probbit-cli -- man > docs/probbit.1 (the test names the command).
  • At a terminal, probbit --help / probbit help still show the hero screen (as in 0.8.1, test hero_screen_at_a_terminal); the grouped list is what they print piped (probbit --help | less, which the hero now says).

COMMANDS (main.rs) lists every command, subcommand and flag with its arity,
summary, usage, sections and examples. --help renders from it (usage.rs) and
check_flags enforces it (flags_of, sub_flags); the persona, live and monitor
flag lists, USAGE, FLAG_HELP, PERSONA_HELP, LIVE_HELP and monitor's HELP are
gone. Tests: names unique, synopses name exactly the parsed flags, the
dispatcher is the table, every listed command/subcommand has help and every
listed flag parses (tests/cli_table.rs).
The same text as COMMAND [SUBCOMMAND] --help, from the table; alone, the
same as probbit --help (the hero screen at a terminal, the usage piped).
Subcommands of persona and live now have their own --help (usage, flags,
exit codes). Unknown names exit 2. Tests: tests/cli_help.rs.
probbit: unknown command "persna"; did you mean "persona"? see `probbit --help`
Damerau-Levenshtein (OSA) distance <= 2 (<= 1 for words of 3 characters or
fewer), or the one name the word is a prefix of; exit code stays 2 and
stdout stays empty. Covers persona subcommands, live verify/control typos
(when no such persona file exists) and probbit help. Tests: hit, miss,
subcommand typo (tests/cli_suggest.rs) and the distance unit test.
A static script generated from the command table: commands, subcommands and
flags (zsh and fish with each flag's help and what its value is). An unknown
or missing shell exits 2 with the list. Tests: every script names every
command, subcommand and flag the helps list; bash -n and zsh -n (fish -n
where installed); the bash script completes real words
(tests/cli_completions.rs).
… columns

Top-level --help: the version line first, commands grouped (persona, live,
monitor, decide/run, mcp, other) with one summary line each, the flags every
command takes, the exit codes and six examples. Every help wraps at 100
columns without splitting an option group. monitor --help: usage, modes,
flags, demo, exit. The terminal hero puts the rate and the machine on two
lines and points at probbit help and completions. Every flag and sentence of
meaning is kept. Tests: no help line over 100 columns, top-level order and
examples, monitor sections and facts (tests/cli_layout.rs); the hero test
checks 100 columns too.
stderr: one human line, always. stdout: a bad input document's {"error"}
object when stdout is not a terminal (every wrapper: python/, mcp.rs, npm,
examples, scripts read it piped, so their contract is byte-identical) or with
--json; at a terminal without --json the line only, never both. Bad commands
and flags stay off stdout unless --json (code "flag", or "command" with a
"suggestion"), keeping Python's empty-stdout flag contract. --json is
accepted by every command. Documented in docs/CHEATSHEET.md "Errors"; README,
agents.md and probbit-ir-json.md say when the object is printed. Tests: pipe,
pseudo-terminal, --json, usage errors (tests/cli_errors.rs); the Python (44),
npm (13) and node-example suites pass unchanged.
…by install.sh

probbit man prints roff (man(7)) from the command table: NAME, SYNOPSIS,
DESCRIPTION, COMMANDS (every command, subcommand and flag), EXIT STATUS,
ENVIRONMENT, EXAMPLES, SEE ALSO; ASCII, non-ASCII as \[uXXXX]. docs/probbit.1
is its committed output. release.yml copies it next to the archives (it is
inside them under docs/ too). install.sh installs it to
~/.local/share/man/man1 (PROBBIT_MAN_DIR overrides), never fatally.
Tests: the committed page equals the generated one; every section, command
and listed flag is in it; it renders under man -l or mandoc
(tests/cli_man.rs); test_install_sh.sh checks the page lands, under a
scratch HOME too, and that an unwritable man directory is not fatal.
….8.2

README Install: the manual page install and the completion one-liners; how
help and did-you-mean work. CHEATSHEET: a help/completions/man table.
CHANGELOG: 0.8.2 - unreleased, Added and Changed for part B.
# Conflicts:
#	CHANGELOG.md
#	probbit-cli/src/main.rs
@BitmapAsset
BitmapAsset merged commit 1202e22 into main Oct 14, 2026
11 checks passed
@BitmapAsset
BitmapAsset deleted the feat/cli-polish branch October 14, 2026 10:02
@BitmapAsset BitmapAsset mentioned this pull request Oct 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant