Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.
Rust Tool Base (RTB)
A batteries-included application framework for building Rust CLI tools — idiomatic, composable, and AI-ready.
Status: pre-1.0. The crates are implemented and published, but the public API is not frozen — expect breaking changes within 0.x. The authoritative contract for each subsystem lives in
0008-rust-tool-base.
RTB is a Rust sibling of Go Tool Base (GTB).
It is not a line-for-line port. Go Tool Base leans on Go's idioms
(context.Context, functional options, package-level init(),
interface{}-keyed containers); RTB leans on Rust's (? + miette,
typestate builders, linkme distributed slices, strongly-typed serde
config, tokio structured concurrency).
What RTB gives you
- Opinionated application scaffolding —
Application::builder()typestate assembler that wires clap, figment, tracing, miette, tokio, and a signal- boundCancellationTokenin one call. - Strongly-typed layered config — env > CLI flags > user file > embedded
defaults, materialised into your
serde::Deserializestruct. Hot-reload vianotify+arc-swapwith awatch::Receiverfor observers. - Embedded-assets + overlay FS —
rust-embedfor compile-time bundling (with dev-mode disk passthrough) +vfs::OverlayFSfor user-override layering. Structured formats (YAML/JSON/TOML) are deep-merged; binaries follow last-registered-wins. - Diagnostic errors, not error handlers — every crate derives
thiserror::Error + miette::Diagnostic;main()returnsmiette::Result<()>and a framework-installed hook renders them with labels, help, and severity. - Self-update —
self-replace+ minisign-signed releases (prehashedED—ed25519(BLAKE2b-512(archive))), verified withminisign-verify, the same cratecargo-binstalluses, so one signature serves both. The staged binary is self-tested before an atomic swap. - VCS —
gix-first Git ops (fallback togit2for unsupported writes),octocrabfor GitHub (Enterprise-capable, GitHub-App-capable),gitlabfor self-hosted GitLab with nested groups. - AI —
genaifor multi-provider (Claude, OpenAI, Gemini, Ollama), with a direct-reqwestpath for Anthropic-only features (prompt caching, managed agents, extended thinking). Structured output validated withjsonschema. - MCP — official
rmcpSDK; commands opt in viaCommand::mcp_exposed = trueand surface theirclap::Argsschema throughCommand::mcp_input_schema.mcp serve(stdio default) andmcp listship as built-in subcommands. - TUI docs browser —
ratatui+termimad, with streaming AI Q&A. - Credentials —
keyringwrapped in aCredentialStoretrait;secrecy::SecretStringend-to-end, zeroed on drop. - Telemetry — opt-in, salted
machine-uid, pluggable sinks (noop/file/HTTP/OTLP).
Workspace layout
Only the umbrella crate and the rtb binary live here. Every other
crate is a standalone module of the
phpboyscout Rust toolkit, consumed here
as an ordinary dependency:
- Foundations —
rtb-error,rtb-redact,rtb-config,rtb-assets,rtb-app,rtb-tui,rtb-credentials,rtb-telemetry,rtb-test-support,rtb-chat(formerlyrtb-ai),rtb-forge(formerlyrtb-vcs). - CLI family —
rtb-cli,rtb-update,rtb-docs,rtb-mcp, inphpboyscout/rust/cli.
rust-tool-base/
├── Cargo.toml # workspace root
├── crates/
│ ├── rtb/ # umbrella crate (public API)
│ └── rtb-cli-bin/ # the `rtb` scaffolder binary
├── examples/
│ └── minimal/ # smoke-test tool
├── docs/ # user-facing documentation
│ # (specs live in the project wiki)
├── .gitlab-ci.yml # CI + the tagged `dist` release stage
├── deny.toml # cargo-deny policy
├── rustfmt.toml
├── rust-toolchain.toml # pinned stable
├── justfile # common tasks
├── LICENSE # MIT
└── SECURITY.md
Quick start
A working end-to-end example lives at
examples/minimal. Run it with:
$ cargo run -p rtb-example-minimal -- version
$ cargo run -p rtb-example-minimal -- greet
$ cargo run -p rtb-example-minimal -- doctor
$ cargo run -p rtb-example-minimal -- --help
Anatomy
A minimal RTB-based tool has three parts: (1) a custom Command
implementation, (2) a linkme registration that inserts it into the
framework's command slice, and (3) an Application::builder() call
in main.
use async_trait;
use distributed_slice;
use App;
use ;
use *;
// 1. Implement the Command trait.
;
// 2. Register into the framework's command slice at link time.
// 3. Wire the Application.
async
version_info!() has to be a macro: it expands env!("CARGO_PKG_VERSION")
at your call site, so it reports your tool's version. A function
couldn't — its env! would expand inside the framework and report the
framework's version instead.
Downstream tools need rtb, tokio, miette, async-trait, and
linkme as direct dependencies. linkme must be direct because its
#[distributed_slice] attribute expands to ::linkme::… paths.
Development
License
MIT — see LICENSE.