v0.8 · Released Mar 14, 2026 · Apache-2.0

CLIs that respect your terminal.

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.

install — bash
ready
$ 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

Linear Supabase Vercel Cloudflare Fly.io Railway ngrok Deno Turso HashiCorp

A complete CLI in one file

Define it once. Get the whole experience.

Subcommands, typed flags, colored output and a full --help tree — derived from this struct, no boilerplate.

main.rs
1use bracket::{Command, Subcommand, Color};
2
3#[derive(Command)]
4#[bracket(name = "deploy", about = "Ship a build to an edge region")]
5struct Deploy {
6    /// Skip the pre-flight health check
7    #[flag(short = 'f')]  force: bool,
8    #[option(default = "iad")] region: String,
9    #[subcommand] cmd: Action,
10}
11
12#[derive(Subcommand)]
13enum Action { Push, Rollback, Status }
14
15fn run(cli: Deploy) -> Result {
16    Color::ok(format!("→ pushing to {}", cli.region));
17    Deploy::parse().dispatch(cli.cmd)
18}

$ deploy --help · generates the full usage tree, colored, with completions registered.

Why Bracket exists

The terminal is a product surface.

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

  • No runtime config files. Your command surface is code. No YAML schema to drift out of sync.
  • No interactive wizard mode. Bracket builds flags-and-args CLIs. TUIs are a different tool, on purpose.
  • No telemetry, ever. The framework phones nobody. Your users' invocations are theirs.
  • No bundled HTTP client. Bracket parses and renders. Networking is your dependency, not ours.
  • No magic global state. Parsed input is a value you own and pass — never a hidden singleton.

What you get out of the box

Six things you'd build anyway.

Every feature below ships on by default. None of them needs a config file, and none of them costs you cold-start budget.

Zero-config color output

Semantic helpers — ok, warn, fail — that respect NO_COLOR, detect TTYs and degrade cleanly when piped.

Color::warn("low disk");

Auto-generated man pages

Your command tree compiles straight to roff. Real man yourcli entries, no hand-written nroff.

$ bracket man > cli.1

Completion scripts

Tab-complete for bash, zsh and fish — including dynamic value completion for options that resolve at runtime.

$ bracket completions zsh

Sub-200ms cold start

Parsing is generated at build time, not reflected at runtime. A no-op invocation returns in ~40ms.

# p99 startup 183ms

JSON output mode

Every command gets a free --json flag. Human-readable by default, machine-parseable on demand.

$ deploy status --json

Plugin system

Third-party crates and packages register subcommands at build time — sandboxed, versioned, opt-in.

$ bracket plugin add db

How it compares

One model, three languages.

Each tool below is excellent in its language. Bracket's claim is the row no single-language framework can fill.

Capability Bracket Clap · Rust Cobra · Go Commander.js
Cold start under 200ms ~210ms ~340ms
Plugin system partial
3-language consistency Rust only Go only JS only
Auto-generated man pages via crate
Shell completions (bash/zsh/fish) bash only
JSON output mode manual manual manual

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

Hello world in under 60 seconds.

Three steps from an empty directory to a colored, completion-aware binary you can ship.

1

Install the CLI

shell
$ cargo install bracket
# or: go install
  github.com/bracket/cli@latest
# or: npm i -g @bracket/cli
2

Scaffold a command

shell
$ bracket new greet
 created greet/main.rs
 added Greet command
$ cd greet
3

Run it

shell
$ 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 examples

Community

Built in the open, reviewed by 148.

Every RFC, every breaking change and every plugin API lands through public discussion. Come argue about flag naming with us.

Core maintainers

  • SK

    Soren Kvist

    @sorenkv · parser & codegen

  • PR

    Priya Rao

    @priyacli · Go runtime & plugins

  • TA

    Tomás Aguilar

    @tomasagl · TypeScript & npm dist

  • NW

    Naomi Webb

    @nwebb · docs, completions, DX

Become a sponsor

Backed by

GitHub Sponsors keep Bracket independent.

No VC, no paid tier, no asterisks. Three companies and 38 individuals fund the maintenance budget.

ngrok
Fly.io
Railway

+ 38 individual sponsors

Sponsor Bracket

From $5/mo · funds CI, security audits and a part-time maintainer.

Frequently asked

Questions, answered straight.

Is Bracket production-ready?

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.

Why three languages, not one?

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.

How does the plugin model handle security?

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.

What are the cold-start guarantees?

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.

Best practices for testing a CLI built with Bracket?

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

Get started in 60 seconds.

One command installs the CLI. The next scaffolds a colored, completion-aware tool you can ship today.

install
$ cargo install bracket

Apache-2.0 · no account · no telemetry