The zcli CLI
The meta-CLI you install with curl | sh scaffolds projects, restructures commands, watches builds, and cuts releases. It is itself a zcli app — every command below is a file in its own commands/ directory, running on the framework’s own help, completions, “did you mean?”, and self-upgrade plugins. It’s a build-time tool: your finished CLI never depends on it.
zcli
├── init scaffold a new project
├── add command · group · arg · option · plugin
├── rm command · arg · option
├── mv move/rename a command
├── tree show the command tree (no build needed)
├── dev watch, rebuild, optionally re-run
├── guide version-matched reference, by topic
├── release version-bump, tag, and push a release
├── gh add workflow ci · release — GitHub Actions scaffolds
└── upgrade self-update from GitHub releases
zcli init
zcli init myapp --description "My awesome CLI"
zcli init . # initialize the current directory
Scaffolds a complete project — build.zig.zon, a build.zig wired with zcli.generate, src/main.zig, a README, an example command — then verifies its own output: it fetches dependencies and runs zig build, so the first command you type runs your CLI, not the compiler. It also initializes a git repository with a Zig .gitignore and an initial commit (skipped when git is absent or you’re already inside a work tree).
On a TTY, init is a short wizard — every step has a sensible default:
- Description — one line that anchors every
--helprender. - CLI shape —
multi(subcommands, git style — the default) orsingle(the app itself is the command, rg style; see single-command CLIs). Asked up front because restructuring between shapes later is annoying. - Plugins — multi-select of the built-ins (help, version, and not-found are preselected). Selecting
github_upgradegets a follow-up prompt for theOWNER/REPOit pulls releases from — defaulted from your git remote when runningzcli init .in an existing repo — noTODO:placeholders in generated code. - GitHub Actions — optionally scaffold the
ciand/orreleaseworkflows (release is preselected whengithub_upgradewas chosen — it completes the self-update loop). - Summary + confirm — one block showing every decision before anything touches disk.
Every prompt has a flag, so agents and scripts get the identical surface:
zcli init <name|.>
--description <text> --app-version <semver>
--template <multi|single> --plugins <list|none>
--upgrade-repo <owner/repo> (github_upgrade config)
--no-git --github <ci,release|none>
--no-build --yes / --defaults
--dry-run (print the plan, write nothing)
Any flag answers its prompt; --defaults answers every remaining prompt with its default (implied when stdin isn’t a TTY); --yes additionally skips the confirm; --dry-run prints the summary and file list and exits without writing.
Every scaffold also includes an AGENTS.md with a marker-delimited zcli section pointing coding agents at zcli guide — re-running init refreshes just that section, never clobbering the rest of the file. See AI agents.
zcli add / rm / mv
Structure changes are mechanical edits — these commands splice command files via an AST engine that keeps struct fields, meta entries, and argument ordering in sync, and preserves your hand-written execute body.
zcli add command users/create -d "Create a user" # wizard when run bare on a TTY
zcli add group users -d "Manage users" # a directory + index.zig
zcli add arg users/create name --type []const u8 # positional, spliced into Args
zcli add option users/create admin --type bool --default false --short a
zcli add plugin timing # skeleton in src/plugins/
zcli rm option users/create admin # one or more names, atomic
zcli rm arg users/create name
zcli rm command users/create # removes file, tidies empty dirs
zcli mv users/create people/new # move/rename, creates/tidies groups
Details worth knowing:
add arg—--multiplemakes a variadic (last position only),--nullablean optional,--before/--aftercontrol placement; ordering rules (required → optional → variadic) are validated on every edit.add option— omit--defaulton a non-nullable scalar to create a required option;--multiplebuilds an array option;--shortclaims a one-letter flag.rm arg/rm option— take several names and apply all-or-nothing; a typo rejects the whole batch rather than half-editing a file.
zcli tree
zcli tree # the discovered hierarchy, with descriptions
zcli tree --show-options # plus each command's args & options signature
Reads command structure straight from source (std.zig.Ast) — no build required — using the same discovery rules as the framework itself. The output is ANSI-free on purpose: it’s the read-back format agents and scripts consume.
zcli dev
zcli dev # watch src/, rebuild on change
zcli dev -- ./zig-out/bin/myapp serve # …and (re)run this after each build
Native file watching (FSEvents/kqueue/inotify) with an ~80 ms debounce to coalesce editor bursts. With -- <cmd>, the spawned process is killed and restarted after each successful rebuild; status lines go to stderr so redirecting stdout stays clean.
zcli guide
zcli guide # list topics
zcli guide structure # a worked example for one topic
A version-matched reference: the content ships inside the binary and matches the zcli you’re compiling against, so it can’t drift the way training data or old blog posts do. Topics: structure, sharing, storage, arena, output, prompts, ui, http, secrets, plugins, testing.
zcli release & zcli gh
The release workflow lives in the meta-CLI too — versioning, tagging, CI scaffolding:
zcli gh add workflow ci # once: build + test on every push/PR
zcli gh add workflow release # once: scaffold .github/workflows/release.yml
zcli release patch # bump, test, tag, push — CI builds & publishes
(zcli init can scaffold both workflows up front — the extras step, or --github ci,release.)
Full walkthrough, including binary signing and self-upgrade for your users: Ship & distribute.
zcli upgrade
The meta-CLI updates itself from GitHub releases — zcli upgrade (or --check), with minisign-verified artifacts. Your own CLI can offer the same command by enabling the github_upgrade plugin.