What's here
v1.0.0 is the stable API freeze. The framework is feature-complete — the
output layer, command layer, help engine, and auth seam are all in place, and the
public surface is guaranteed under SemVer:
- Plain output —
out/err: one call, no parsing, no allocation for a string literal. The hot path stays cheap. - Three styling paths — a chainable
stylebuilder, inlineparsetags, and a nameddefine_tag/tagregistry. All three render to identical bytes for the same intent. - Full color — the eight standard names plus any 24-bit color via hex or RGB,
with graceful downgrade to 256- or 16-color terminals and clean fall-back to
plain text on pipes,
NO_COLOR, or the Windows console without ANSI. - Command tree — a recursive
Commandtree with a full argument model (flags, counting flags-vvv, options, repeatable options, positionals, variadic positionals) and aliases, registered into anAppfrom anywhere (not justmain), with.hidden()/.requires_auth()flags and structured, non-panicking errors. - Help & version — auto-generated
--help/-h(top-level and per-command) and--version/-V, rendered through the output layer with injectable header/footer. - Auth seam (feature
auth) — gate a command behind a consumer-supplied authorization hook. cli-forge holds the seam; the login state lives in your code. Fails closed, and hides unauthorized commands from help.
Everything above is stable. Future 1.x releases add only strictly-additive API,
bug fixes, and internal optimization; nothing existing changes before 2.0. See
the ROADMAP.
Installation
[]
= "1.0"
Color is on by default. For a build that never emits escape sequences (the API stays complete; every styled value renders as its plain text):
[]
= { = "1.0", = false, = ["std"] }
Quick Start
use ;
// Plain output — the common case.
out;
err;
// Styling, three ways, all rendering to the same bytes for the same intent:
out; // builder
parse; // inline tags
define_tag; // named registry
out;
The three styling paths
The same styled line, produced three ways. The choice is ergonomic, not visual — the bytes are identical.
use ;
// 1. Builder — chain methods; the result is `Display`. Best for computed/one-off.
out;
// 2. Tags — one string with inline markup. Best when text and style live together.
parse;
// 3. Named registry — define the look once, reuse by name across the program.
define_tag;
out;
Tag grammar: <b>…</b> (bold), <u>…</u> (underline),
<c=VALUE>…</c> (color, where VALUE is a named color, #rrggbb, or r,g,b),
and </> to close the innermost tag. Tags nest; unrecognized tags print
literally, so parse never rejects input.
Colors and terminals
Colors are the eight standard names, plus any 24-bit value:
use ;
out;
out;
out;
The terminal's capability is detected once. A 24-bit color renders exactly on a
true-color terminal, downgrades to the nearest 256- or 16-color value where that
is all the terminal supports, and falls away to plain text when output is a pipe,
NO_COLOR is set, or the crate is built without color. CLICOLOR_FORCE
overrides detection and forces color on. The Windows console is handled behind the
same API — virtual-terminal mode is enabled automatically, with a plain-text
fall-back if it cannot be.
Commands
Build a recursive command tree, register commands into an App from anywhere, and
let parse resolve the invocation, parse arguments, and run the selected
command's handler:
use ;
let mut app = new;
app.register;
app.register;
let _ = app.parse;
Commands register from anywhere — a command built in a non-main module is
reachable and behaves identically, the limitation that made the predecessor
unusable. Give a command extra names with .alias("rm") / .aliases(["rm", "del"]);
aliases resolve to the canonical command. Arguments cover flags, counting flags
(Arg::count, read with count()), options, repeatable options and variadic
positionals (.multiple(true), read with values()), and positionals — parsed
from all the standard forms (--long, --long=value, -s, -svalue, bundled
-abc, -vvv, --). Malformed input becomes a structured ParseError: parse
prints it and exits 2, never panicking; try_parse_from returns it instead.
.hidden() keeps a command invokable but out of help; .requires_auth() gates it
behind the auth hook (feature auth).
Help and version come for free. -h / --help renders styled help for the
app or any command (with your help_header / help_footer); -V / --version
prints App::version(...). App::help() renders the top-level help as a string
whenever you want it. Both exit 0 under parse; try_parse_from returns them
as ParseError::HelpRequested / VersionRequested control signals.
Feature flags
| Feature | Default | Description |
|---|---|---|
std |
yes | Standard library: terminal detection, the stdout/stderr writers, and the command layer. |
color |
yes | ANSI / styled output. Implies std. Disable for plain output (still complete). |
auth |
no | The auth seam: App::auth, AuthRequest, and enforcement of requires_auth. Implies std; adds no dependencies. |
Examples
Runnable examples live in examples/:
Force color when output is captured, or disable it, to see both paths:
CLICOLOR_FORCE=1
NO_COLOR=1
Performance
The plain path is allocation-free for a string literal — proven by a
counting-allocator test (tests/allocation.rs), not asserted. Local Criterion
means (Windows x86_64, release build):
| Operation | ns/op |
|---|---|
out plain write (&str) |
~10 |
| builder render, named color + bold | ~50 |
| builder render, 24-bit color | ~75 |
| named-registry render | ~43 |
Styling costs more than the plain path because it builds an owned String and
encodes escape sequences — a cost paid only when you opt into color. Reproduce
with cargo bench --bench bench.
Status
v1.0.0 is the stable API freeze: the public surface is guaranteed under
Semantic Versioning — no breaking changes before 2.0. 1.x releases add only
strictly-additive API, fixes, and internal optimization. See the SemVer promise in
docs/API.md and the ROADMAP.
Contributing
See dev/DIRECTIVES.md for engineering standards and the definition of done. Before a PR: cargo fmt --all, cargo clippy --all-targets --all-features -- -D warnings, and cargo test --all-features must be clean.