Getting started

From zero to a working CLI in three commands.

Install the zcli binary, scaffold a project with zcli init, then grow it one file at a time with zcli add command. Each step below shows exactly what runs and what lands on disk.

Requires Zig 0.16+ 3 plugins wired in on init MIT licensed
01

Install the zcli binary

One line downloads the latest release for your OS and drops the zcli scaffolding tool into ~/.local/bin. This is the meta-CLI you'll use to create projects and generate commands — it is not a dependency your app ships with.

Run

$ curl -fsSL https://zcli.sh/install.sh | sh

The script detects your platform, pulls the matching binary from the latest GitHub release, verifies the minisign signature and SHA-256 checksum (fail-closed — it aborts rather than install unverified), and marks it executable.

Prefer building from source?

Clone and build with Zig directly — artifacts land in zig-out/bin/.

$ git clone https://github.com/ryanhair/zcli.git
$ cd zcli && zig build

What happens

install — zsh
$ curl -fsSL …/install.sh | sh
==> Detecting platform… macos / aarch64
==> Fetching latest release… zcli v0.25.0
==> Downloading binary…
==> Verifying checksum…
 Installed zcli to ~/.local/bin/zcli

$ zcli --version
zcli 0.25.0

$ zcli --help
Build beautiful CLIs with zcli — scaffold
projects, add commands, and more.

COMMANDS
  init   Initialize a new zcli project
  add    Add commands and plugins
  tree   Show the command tree discovered from src/commands

If zcli isn't found afterward, make sure ~/.local/bin is on your PATH.

02

Initialize a new project

zcli init is a short wizard that scaffolds a complete project: a build.zig.zon that pulls in zcli, a build.zig that wires up zcli.generate with your chosen plugins, an entrypoint, a README, and one working example command. It then initializes a git repository with an initial commit, fetches dependencies, and verifies the scaffold with a real zig build — so the first command you run is your CLI, not the compiler.

Run

$ zcli init myapp --description "My awesome CLI"

Pass . instead of a name to initialize in the current directory (it must be empty). Every prompt has a flag (--template, --plugins, --github, …) and --defaults answers them all — see the CLI reference for the full surface.

What happens

myapp — zsh
$ zcli init myapp
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…
 Build verified

 Project 'myapp' created successfully!

Next steps:
  cd myapp
  ./zig-out/bin/myapp hello World
  ./zig-out/bin/myapp --help

What you get

myapp/scaffold
myapp/
├── build.zig.zon     # project + zcli dependency
├── build.zig         # zcli.generate + builtin plugins
├── README.md         # build/run/test instructions
├── AGENTS.md         # points coding agents at zcli guide
├── .gitignore        # .zig-cache/, zig-out/ — first commit made
└── src/
    ├── main.zig      # generated entrypoint
    └── commands/
        └── hello.zig  myapp hello

build.zig already registers the help, version, and not-found plugins (the wizard's defaults) — you get --help and "did you mean?" for free. Toggle on completions and more in the plugin picker.

Run the example — it's already built

myapp — zsh
$ cd myapp
$ ./zig-out/bin/myapp hello World
Hello, World!
$ ./zig-out/bin/myapp hello Alice --loud
HELLO, Alice!

That output comes from the generated src/commands/hello.zig — a real command with a typed name argument and a --loud flag. Open it to see the shape every command follows.

03

Add a new command

zcli add command writes a ready-to-edit command file at the right path under src/commands/ — with meta, Args, Options, and an execute() stub already filled in. Nested paths like users/create create the folders for you. Run it from your project root (where build.zig lives). In a terminal it opens a short wizard — add typed arguments and options interactively, review, confirm — seeded from any flags you pass; with piped input it writes the skeleton straight away.

Run

$ zcli add command deploy --description "Deploy your application"

Because the path becomes the command, zcli add command users/create produces myapp users create and creates the users/ namespace automatically.

What happens

myapp — zsh
$ zcli add command deploy \
    --description "Deploy your application"

  … wizard: arguments · options · review → Create it …

 Created src/commands/deploy.zig

  Next steps
    1. Implement execute() in
       src/commands/deploy.zig
    2. zig build
    3. ./zig-out/bin/<app> deploy --help

Run it outside a project and it stops with Not in a zcli project directory — no stray files.

The file it generates

src/commands/deploy.zigzig
const std = @import("std");
const zcli = @import("zcli");
const Context = @import("command_registry").Context;

pub const meta = .{
    .description = "Deploy your application",
    .examples = &.{
        "deploy",
    },
    // .aliases = &.{"alias"}, // alternate names this command answers to
    // .hidden = true,          // omit from help and tree listings
};

pub const Args = struct {};
pub const Options = struct {};

pub fn execute(_: Args, _: Options, context: *Context) !void {
    const stdout = context.stdout();
    try stdout.print("TODO: Implement deploy\n", .{});
}

test "deploy" {
    // Scaffolded smoke test — replace with real assertions.
    _ = @This();
}

Fill in execute(), then zig build — routing regenerates and myapp deploy is live with auto-generated --help.

Add args & options to a command

Args and options are their own scaffolders — zcli add arg and zcli add option splice a typed field into an existing command's struct in place, so you don't hand-edit the boilerplate.

$ zcli add arg deploy service --type []const u8
$ zcli add option deploy env --type []const u8 --short e --nullable
+

See your whole CLI at a glance

zcli tree reads src/commands/ and prints the structure it discovered — no build required. Add --show-options to include each command's arguments and options.

Run

$ zcli tree

Discovery reuses the same scan the build runs, so the tree always reflects what's actually on disk — folders show as (group) namespaces.

What happens

myapp — zsh
$ zcli tree
myapp
├── deploy [Deploy your application]
└── hello [Say hello to someone]

$ zcli tree --show-options
myapp
├── deploy [Deploy your application]
└── hello [Say hello to someone]
    args:    <name:[]const u8>
    options: [--loud/-l]
That's the whole loop

Install · init · add. Ship the rest.

You now have a buildable CLI and a one-liner to grow it. Next: wire up options, add plugins, and generate docs from your command metadata.