v0.2.0
Install
cargo install mandibleOr download a binary below. Verify with the accompanying .sha256.
Six fixes, three of them found by rendering a deliberately awkward CLI through a
real pseudo-terminal rather than by any test.
Fixed
-
The USAGE line no longer repeats the command name.
docker import
rendered asimport docker import [OPTIONS] file|URL|- [REPOSITORY[:TAG]].
The check asked whether the usage's first word was the node's name, but
cobra and argparse both print the full command path, so the name was stapled
on the front of nearly every subcommand of every such tool. It now scans the
whole leading run of command words. Tools that print no name at all
(Usage: [OPTIONS] FILE) still get one added, which is what the prepending
was for. -
A token wider than the pane is broken across lines instead of discarded.
It was ellipsis-truncated, so a 150-character URL rendered as
https://registry.example.com/v2/org…with everything after it unrecoverable
from the parsed view. Splits are placed by display width, so a double-width
character cannot straddle the boundary and overflow the pane. -
A relative tool path works.
mandible ./scripts/tool.pyfailed with
"No such file or directory" for a file plainly present: the path was checked
against the caller's working directory, then the probe ran with its own
directory redirected into a scratch dir (§6 rule 8). Resolution now yields an
absolute path — viastd::path::absolute, deliberately not
fs::canonicalize; see below. -
argparse subcommands survive a styled section heading.
add_subparsers(title="commands")is the ordinary way to name that block,
and the dedicated scan was gated on the heading readingpositional arguments, so a styled heading collapsed the entire command tree to a single
node. The scan's structural evidence — a{a,b,c}pseudo-entry with deeper
lines beneath it — is stronger than the heading text ever was, and still
refuses a plain positional carryingchoices=[...]. -
A command list at the same indent as its heading is recognized.
dnf4
prints its whole command list flush at column 0 under a flush-left heading;
the engine required content indented more than its heading, somandible dnfshowed one node and no subcommands. Now 30. -
A pending row's spinner no longer touches the name.
dnf's longest
command renderedcheck-update⋯ loading, one mangled word rather than a name
and its status — the same defect fixed for summaries in an earlier release
(apt-get'sdselect-upgradeFollow) and missed in the sibling branch,
because no tool in the suite had a pending row at the column untildnf
gained subcommands.
Notes on two near-misses
Both were caught by the PATH-wide coverage sweep and by nothing else; the unit
suite was green through both.
- Making resolved paths absolute with
fs::canonicalizedefeated §6 rule 0.
is_never_probematches on the file name, andreboot,poweroff,
shutdownandtelinitare symlinks tosystemctl— resolving renamed them
before the refusal ran. It also broke teniptables*tools, which dispatch on
argv[0]. Fixed by usingstd::path::absolute, which does not follow links. - The same-indent command rule initially fabricated 28 subcommands out of
mysqlslap's config-variable table (port 3306,no-drop FALSE), because at
a shared indent every row is a candidate heading for the rows beneath it and
init-commandcontains the word "command". A heading must now not itself look
like a row.
Final sweep is identical to baseline on every aggregate — 89.19% described
across 2266 tools, 1 suspicious, 320 verbatim — with zero status changes, zero
nodes lost, and dnf the only gain.
Internal
scripts/smoke_cli.py: a deliberately awkward argparse CLI for exercising
layout by hand — a twelve-level command chain, four flag-table shapes, tokens
with no whitespace to wrap at, and sixty flags. It found three of the bugs
above within minutes of existing.