Skip to content

What this family does not do

Written down so you do not have to discover it. Each entry says whether the limitation is deliberate, not built yet, or a consequence of a design choice made elsewhere.

The global --output flag does not reach every command

--output is declared once at the clap root with global(true), which propagates it to ordinary subcommands. It does not propagate into a passthrough subtree, because those capture their post-name tokens as a raw trailing var-arg before clap sees them.

Command --output
version, doctor, init Accepted, ignored — nothing tabular to render
config, credentials, telemetry Honoured, but only after the subcommand name
docs, update, mcp Rejected as an unknown argument, in any position

mytool --output json credentials list fails with unrecognized subcommand 'json', because the subtree re-slices the process arguments positionally and the flag displaces the subcommand name.

Consequence of a design choice; see argument parsing.

A passthrough command is not driveable from run_with_args

parse_passthrough exists so a command reads its arguments from App::trailing_args() — the tokens the outer parser already captured — which makes it testable through Application::run_with_args.

None of the built-in passthrough commands use it. config, credentials, telemetry, docs, update and mcp all re-read std::env::args_os() and drop the first two entries. In a test process those first two entries are the test harness's own, so driving them through run_with_args does not work. Migrating them is tracked work, not a design position.

docs serve is not a production server

  • --bind is not restricted to loopback. 127.0.0.1:0 is the default, not a constraint. --bind 0.0.0.0:8080 will happily expose the server.
  • No authentication, no TLS, no rate limiting. It is a per-user local tool.
  • No route serves non-markdown assets. Only *.md files are loaded from the asset tree; there is no /assets/ route, so an <img> in a page 404s.

The first is a gap between the stated intent and the code. The last two are deliberate.

docs browse shows one screen of a page

The content pane wraps text but does not scroll, and has no key bindings of its own. A page longer than the terminal is cut off at the bottom, and docs show is the way to read it. Search mode also does not filter the visible list — Enter jumps to the best fuzzy title match. Not built yet.

docs ask supports one provider, one model, one credential

The bundled implementation hard-codes Anthropic, the model claude-opus-4-7, and ANTHROPIC_API_KEY. There is no setting that changes any of the three. Asking whether you can point docs ask at OpenAI, Gemini, Ollama or a self-hosted endpoint has a short answer: not with the built-in implementation.

That is the seam's design rather than an oversight. rtb-docs exports the AiAnswerStream trait so a tool with different requirements supplies its own implementation; the bundled one is a default, not a configuration surface.

It also sends the entire doc tree as context on every question, with no retrieval or ranking. A large corpus exhausts the model's context window rather than degrading gracefully.

The MCP server does not deliver arguments to commands

mcp list publishes each tool's input_schema, and tools/call then ignores whatever the client sent. The command runs with an App whose trailing_args() is empty, and the reply is the fixed string <name> ok rather than the command's output. Only commands that need no input and produce no stdout are usefully exposed today. Not built yet.

--transport sse and --transport http parse and then fail. Only stdio is implemented.

update cannot install a prerelease by "latest"

--include-prereleases is accepted, stored, and never read. "Latest" is whatever the release provider returns. Name the version explicitly with --target instead.

--target also builds the tag as v<VERSION> with no alternative spelling, so a project tagging releases as 1.2.3 or mytool-v1.2.3 cannot use it.

update has no offline flag

Updater::run_from_file verifies and swaps from a locally staged archive, which is the airgap path — but no CLI subcommand calls it. Reaching it means writing a command in your own tool. run_from_file also skips checksum verification and the downgrade check that run performs.

Checksums are verified only when a checksums asset is named

Signature verification is unconditional. SHA-256 verification happens only when ToolMetadata::update_checksums_asset is set; with it unset, the signature is the sole integrity gate. That is a supported configuration — the signature covers the bytes — but it is not the "both are verified" story you might assume.

The staged-binary self-test requires a root --version

Before swapping, update run executes <staged-binary> --version and requires exit 0 with the release tag in stdout. rtb-cli does not declare a root --version flag, so a tool built straight on Application::builder fails its own self-test and cannot self-update until it adds one. There is also no timeout: a staged binary that hangs on --version hangs the update.

Automatic update checks cannot be turned off by a user

ToolMetadata::update_policy is compiled in. There is no environment variable, config key or flag that overrides it, and the runtime Features set does not gate the pre-run hook. Disabled is the default; if you ship Enabled, you are making that call on every user's behalf.

credentials add cannot set an environment variable

For an env-layer credential the wizard prompts for the secret, discards it, and prints the export line for you to run. A process cannot write to its parent shell's environment. Deliberate — and the prompt still appearing makes it look otherwise.

config without typed config barely validates anything

config validate on a tool that has not called Application::builder().config::<C>(...) checks that the file parses and stops. Unknown keys, wrong types and missing required fields all pass. config schema fails outright, and config set writes without validation.

config set also cannot address array elements, and replaces the whole document if the existing file's top level is not a map.

Everything here is single-tool, single-user

No multi-tenancy, no shared state, no daemon. Configuration, consent and update state live under the invoking user's home directory. Two tools with the same ToolMetadata::name share all three.