Zero-config color output
Semantic helpers — ok, warn, fail — that respect NO_COLOR, detect TTYs and degrade cleanly when piped.
Color::warn("low disk");
One authoring model for Rust, Go and TypeScript — colored output, real help text, shell completions and man pages, all generated, all under a sub-200ms cold start.
$
cargo
install
bracket
also available via go install and npm i -g
Works on macOS, Linux & Windows · bash · zsh · fish · PowerShell
Trusted in production at
A complete CLI in one file
Subcommands, typed flags, colored output and a full --help tree — derived from this struct, no boilerplate.
$ deploy --help · generates the full usage tree, colored, with completions registered.
Why Bracket exists
A CLI is the first thing a developer touches and the last thing most teams polish. Help text reads like a stack trace, colors are an afterthought, completions never ship, and the binary takes 900ms to print --version. We think that's backwards. The command line is a UX surface with the same standards as anything you'd put behind a login screen — it just hasn't been treated like one.
The Rust crate clap nailed typed, derive-based parsing. Go's cobra nailed subcommand trees and the docs generator. JavaScript's yargs nailed ergonomics for quick scripts. Each is excellent in its lane — and each leaves you re-learning a different mental model the moment you switch languages. A team shipping a Rust core, a Go agent and a Node wrapper writes the same CLI three times, three ways.
Bracket is one authoring model across all three. The same declarative command definition, the same generated help, the same completion scripts, the same JSON output flag — whether you compile to a Rust binary, a Go binary or an npm-distributed Node entrypoint. On top of that sits a plugin system, so a `deploy` CLI can gain a `deploy db` subcommand from a crate you didn't write. Define the surface once; Bracket makes it feel native everywhere.
Things we don't do
What you get out of the box
Every feature below ships on by default. None of them needs a config file, and none of them costs you cold-start budget.
Semantic helpers — ok, warn, fail — that respect NO_COLOR, detect TTYs and degrade cleanly when piped.
Color::warn("low disk");
Your command tree compiles straight to roff. Real man yourcli entries, no hand-written nroff.
$ bracket man > cli.1
Tab-complete for bash, zsh and fish — including dynamic value completion for options that resolve at runtime.
$ bracket completions zsh
Parsing is generated at build time, not reflected at runtime. A no-op invocation returns in ~40ms.
# p99 startup 183ms
Every command gets a free --json flag. Human-readable by default, machine-parseable on demand.
$ deploy status --json
Third-party crates and packages register subcommands at build time — sandboxed, versioned, opt-in.
$ bracket plugin add db
How it compares
Each tool below is excellent in its language. Bracket's claim is the row no single-language framework can fill.
Cold-start figures: median of 1,000 no-op invocations on an M2 / Linux 6.8, Bracket 0.8 · measured Mar 2026.
Cold start · no-op invocation
lower is better — median ms
GitHub stars · last 12 months
8,408 today — up from 1,120 a year ago
Quick start
Three steps from an empty directory to a colored, completion-aware binary you can ship.
$ cargo install bracket
# or: go install
github.com/bracket/cli@latest
# or: npm i -g @bracket/cli
$ bracket new greet
✓ created greet/main.rs
✓ added Greet command
$ cd greet
$ bracket run -- --name Mai
hello, Mai 👋
$ bracket run -- --help
full usage tree printed
That's a complete CLI — colored output, --help, completions and a man page, all generated.
See full examplesCommunity
Every RFC, every breaking change and every plugin API lands through public discussion. Come argue about flag naming with us.
1,287
open threads — RFCs, help and show-and-tell.
6,940
members — 412 online as you read this.
Core maintainers
Soren Kvist
@sorenkv · parser & codegen
Priya Rao
@priyacli · Go runtime & plugins
Tomás Aguilar
@tomasagl · TypeScript & npm dist
Naomi Webb
@nwebb · docs, completions, DX
Backed by
No VC, no paid tier, no asterisks. Three companies and 38 individuals fund the maintenance budget.
+ 38 individual sponsors
From $5/mo · funds CI, security audits and a part-time maintainer.
Frequently asked
Yes, with the honesty a 0.8 deserves. Bracket ships 4,408 CLIs in production today, including tools at ngrok, Fly.io and Railway, and the parser and codegen layers have been API-stable since 0.6. We're pre-1.0 only because the plugin manifest format may still get one more breaking revision before we freeze it. Everything else follows semver, every release runs against a 1,900-case conformance suite across all three languages, and we publish a migration note for any change that touches user code.
Because real teams already are. A typical infrastructure team ships a Rust core for performance, a Go agent for portability and a Node wrapper because that's what their users npm install. Without Bracket, that's three CLI codebases with three flag conventions, three help styles and three sets of completion bugs. Bracket gives each language a native, idiomatic API — derive macros in Rust, struct tags in Go, decorators in TypeScript — that all compile down to the same command model, the same help renderer and the same completion engine. One spec, three native binaries.
Plugins are resolved and linked at build time, never downloaded or executed at runtime — so there is no auto-update surface to exploit. Each plugin declares its capabilities (filesystem, network, environment) in a manifest, and bracket plugin add shows that capability set before it writes anything to your lockfile. A plugin that never declared network access cannot open a socket; the generated code simply doesn't expose the API. Plugin versions are pinned by content hash, and our advisory feed lets you fail a build if a linked plugin has a known CVE.
All argument parsing is generated at build time as a flat match table — there is no reflection, no schema construction and no allocation on the hot path. On the Rust and Go targets a no-op invocation returns in roughly 40ms median and stays under 200ms at p99 on commodity hardware. The Node target carries V8 startup, so it lands closer to 90ms median; still well inside budget. We treat any regression past the 200ms p99 line as a release blocker, and every CI run prints the cold-start histogram so a slow change can't merge unnoticed.
Test the three layers separately. Parsing is pure — call Cmd::parse_from(&["deploy","--region","ord"]) and assert on the resulting struct, no process needed. For command logic, write your run functions to take parsed input and return a value, so they're ordinary unit tests. For end-to-end behavior, Bracket ships a bracket::test harness that captures stdout, stderr and exit code, and supports golden-file snapshots of help text so a docs regression fails loudly. Always assert against --json output in CI — it's stable across color and terminal-width changes.
Ready when you are
One command installs the CLI. The next scaffolds a colored, completion-aware tool you can ship today.
$ cargo install bracket
Apache-2.0 · no account · no telemetry