Your folder structure is your CLI.
zcli is a batteries-included framework for command-line tools in Zig. Add a .zig file, get a command. Help text, completions, "did you mean?", prompts, progress bars, and docs — wired up at compile time for zero-cost dispatch.
$ myapp deploy api --env staging Deploying api to staging $ myapp dploy api Error: unknown command 'dploy' Did you mean deploy? (run with --help) $ myapp users create --help USAGE myapp users create <name> [options] -a, --admin Grant admin role --output json · table · plain ✔ generated from your command metadata
Everything a polished CLI needs, none of the wiring.
The boring-but-essential parts of a command-line tool, generated for you and type-checked at compile time.
File-based commands
Create files in commands/ to add commands; nest folders for subcommands. The path becomes the command.
Type-safe parsing
Define args & options as Zig structs. Parsing is checked via comptime introspection — wrong types never compile.
Did-you-mean
Auto-generated --help, --version, shell completions, and typo suggestions out of the box.
Interactive prompts
Text, confirm, select, multi-select, password, search, number, and editor — all falling back gracefully without a TTY.
Progress & spinners
Nine spinner styles plus progress bars with ETA. Semantic colors adapt to your terminal's capabilities.
Docs generation
Emit markdown, man pages, and HTML straight from command metadata. Config loads transparently from JSON, TOML, or YAML.
Commands are discovered, not registered.
No registries to maintain, no macros to memorize. Commands are discovered and routing is generated at build time — there's no runtime filesystem scanning or reflection.
1 · Lay out your commands
Each .zig file maps to a command path. Add an index.zig to describe a group.
- Folders become subcommand namespaces
- Variadic args, flags, typed numbers — all from struct fields
- Aliases & examples live in command metadata
src/commands/ ├── init.zig → myapp init ├── deploy.zig → myapp deploy └── users/ ├── index.zig → myapp users ├── create.zig → myapp users create └── list.zig → myapp users list
const Context = @import("command_registry").Context; pub const meta = .{ .description = "Deploy your application", .options = .{ .env = .{ .short = 'e', .description = "Target environment", }, }, }; pub const Args = struct { service: []const u8, }; pub const Options = struct { env: []const u8 = "production", }; pub fn execute(args: Args, options: Options, context: *Context) !void { try context.stdout().print( "Deploying {s} to {s}\n", .{ args.service, options.env }); }
2 · Write the command
Up to four exports: meta, Args, Options, and execute. The context hands you stdout/stderr, an allocator, the active theme, and plugin data.
- Defaults on struct fields become option defaults
?boolfor optional flags,[]const []const u8for variadic- Dispatch is generated — zero-cost at runtime
Seven plugins ship in the box.
Add them in build.zig. Plugins contribute global options, lifecycle hooks, and build-time config — store state in context.plugins.{id}.
| Plugin | Provides | Default |
|---|---|---|
| zcli_help | --help / -h, auto-generated help text | default |
| zcli_version | --version / -V | default |
| zcli_not_found | "Did you mean?" suggestions for typos | default |
| zcli_completions | Generate & install completions for bash, zsh, fish | optional |
| zcli_config | Transparent config loading — JSON, TOML, YAML, per-command scoping | optional |
| zcli_output | --output flag — json, table, plain | optional |
| zcli_github_upgrade | upgrade command via GitHub releases | optional |
Prompts and progress you can use anywhere.
zinput and zprogress work with or without the rest of zcli — no framework dependency required.
zinput — interactive prompts
Eight prompt types. Configurable prefix; line-based fallback when stdin isn't a TTY.
const idx = try zinput.select(writer, reader, .{ .message = "Framework:", .choices = &.{ "express", "fastify", "koa" }, }); const ok = try zinput.confirm(writer, reader, .{ .message = "Continue?", .default = true, });
zprogress — spinners & bars
Nine spinner styles, progress bars with ETA, and semantic status states.
var spinner = zprogress.spinner(.{ .style = .dots }); spinner.start("Loading..."); // ... do work, spinner.tick() in a loop ... spinner.succeed("Done!"); // ✔ Done! spinner.fail("Failed"); // ✖ Failed spinner.warn("Warning"); // ⚠ Warning
Stop wiring up arg parsers.
Add zcli to build.zig.zon, drop a file in commands/, and run zig build. That's the whole onboarding.