Documentation
A batteries-included framework for building command-line interfaces in Zig. Drop .zig files in a directory and get a fully-featured CLI — with command discovery and routing wired up at compile time for zero-cost dispatch.
Quick start3 commands
Install the meta-CLI, scaffold a project, add a command. That's the whole loop.
Install the zcli CLI
See the install page for every option, including shell completions.
$ curl -fsSL https://zcli.sh/install.sh | sh
Scaffold a project
$ zcli init myapp --description "My awesome CLI" Creating new zcli project: myapp Creating build.zig.zon... Creating build.zig... Creating src/main.zig... Creating example command (hello)... Creating README.md... Creating AGENTS.md... Initializing git repository... Fetching dependencies (this may take a moment)... ✔ Build verified ✓ Project 'myapp' created successfully!
Add a command & build
$ cd myapp $ zcli add command deploy --description "Deploy your application" ✔ Created src/commands/deploy.zig $ zig build $ ./zig-out/bin/myapp --help USAGE myapp <command> [options] deploy Deploy your application hello Say hello
Add more commands the same way — start with Commands & structure for the file-path rules, or AI agents if a coding agent is driving.
›Manual setup — wiring build.zig by hand — skip if zcli init worked
Add zcli to build.zig.zon
$ zig fetch --save https://github.com/ryanhair/zcli/archive/refs/tags/v0.25.0.tar.gz
Wire up build.zig
const zcli = @import("zcli"); const zcli_dep = b.dependency("zcli", .{ .target = target, .optimize = optimize }); exe.root_module.addImport("zcli", zcli_dep.module("zcli")); const cmd_registry = try zcli.generate(b, exe, zcli_dep, .{ .commands_dir = "src/commands", .plugins = &.{ zcli.builtin(.help, .{}), zcli.builtin(.version, .{}), zcli.builtin(.not_found, .{}), }, .app_name = "myapp", .app_description = "My awesome CLI", }); exe.root_module.addImport("command_registry", cmd_registry);
Write your first command
const Context = @import("command_registry").Context; pub const meta = .{ .description = "Say hello" }; pub const Args = struct { name: []const u8 }; pub const Options = struct {}; pub fn execute(args: Args, _: Options, context: *Context) !void { try context.stdout().print("Hello, {s}!\n", .{args.name}); }
The full config — plugins_dir, shared modules, docs generation, per-command tests — is in the build integration guide.
Core
Defining a CLI: how files become commands, how types become parsers, and what a command has to work with.
Terminal UX
Talking to the user: prompts, progress, live layouts, full-screen TUIs — all themed from one declaration, all degrading cleanly when piped.
Services
The batteries a real CLI ends up needing — config, credentials, and the network — with safe behavior as the only behavior.