zig-cli is a comprehensive CLI library for Zig 0.16+, inspired by the clapp TypeScript framework. It provides a type-safe CLI framework, an interactive prompt system, and a configuration loader - all using modern Zig APIs.
-
CLI Framework (
src/cli/)- Type-safe command building via
Command(T)(compile-time validated) - Low-level
BaseCommandfor manual control - Argument parsing with type validation
- Option handling (short/long flags)
- Automatic help generation
- Middleware system
- Type-safe command building via
-
Prompt System (
src/prompt/)- Interactive terminal UI
- State machine (5 states: initial -> active <-> error -> submit/cancel)
- Event-driven architecture
- 10 prompt types + UI components
- Terminal feature detection
-
Configuration (
src/config/)- Type-safe config loading via
cli.config.load(T, ...) - TOML, JSONC, JSON5 parsers
- Auto-discovery of config files
- Untyped access via
Configtype
- Type-safe config loading via
zig-cli/
├── build.zig # Build configuration (tests, examples, library)
├── build.zig.zon # Package metadata
├── src/
│ ├── root.zig # Main entry - exports Command, Context, Action, etc.
│ ├── cli/
│ │ ├── Command.zig # Base command definition & ParseContext
│ │ ├── CommandBuilder.zig # TypedCommand(T), TypedContext(T), TypedAction(T)
│ │ ├── Option.zig # Option (flag) handling
│ │ ├── Argument.zig # Positional argument handling
│ │ ├── Parser.zig # Argument parser with validation
│ │ ├── Help.zig # Help text generation
│ │ └── Middleware.zig # Middleware system (chain, context, built-ins)
│ ├── prompt/
│ │ ├── root.zig # Prompt module entry
│ │ ├── Terminal.zig # Low-level terminal I/O
│ │ ├── Ansi.zig # ANSI colors and symbols
│ │ ├── PromptCore.zig # Core prompt logic (buffer, cursor, events)
│ │ ├── PromptState.zig # State machine and events
│ │ ├── TextPrompt.zig # Text input prompt
│ │ ├── ConfirmPrompt.zig # Yes/No confirmation
│ │ ├── SelectPrompt.zig # Single selection list
│ │ ├── MultiSelectPrompt.zig # Multiple selection list
│ │ ├── PasswordPrompt.zig # Masked password input
│ │ ├── NumberPrompt.zig # Number input (integer/float, range)
│ │ ├── PathPrompt.zig # Path input with Tab autocomplete
│ │ ├── GroupPrompt.zig # Sequential prompt groups
│ │ ├── SpinnerPrompt.zig # Spinner/loading animation
│ │ ├── ProgressBar.zig # Progress bar (4 styles)
│ │ ├── Message.zig # Message prompts (intro/outro/note/log)
│ │ ├── Box.zig # Box rendering (4 styles)
│ │ ├── Table.zig # Table rendering (4 styles, alignment)
│ │ └── Style.zig # Style chaining API
│ └── config/
│ ├── root.zig # Config exports: load(), discover(), loadFromString()
│ ├── Config.zig # Config manager (untyped)
│ ├── ConfigLoader.zig # TypedConfig(T) - compile-time schema validation
│ ├── TomlParser.zig # TOML parser
│ ├── JsoncParser.zig # JSONC parser
│ └── Json5Parser.zig # JSON5 parser
├── examples/
│ ├── simple.zig # Minimal typed CLI example
│ ├── basic.zig # Basic CLI with subcommands
│ ├── typed.zig # Type-safe API demo
│ ├── advanced.zig # Complex CLI with multiple commands
│ ├── prompts.zig # All prompt types
│ ├── showcase.zig # Comprehensive feature demo
│ ├── config.zig # Config file examples
│ └── configs/
│ ├── example.toml
│ ├── example.jsonc
│ └── example.json5
└── docs/
├── README.md
├── FEATURE_PARITY.md
├── CONFIG_FEATURES.md
├── IMPLEMENTATION.md
└── FINAL_SUMMARY.md
-
Type-Safe Commands (Primary API)
const GreetOptions = struct { name: []const u8, verbose: bool = false, }; fn greet(ctx: *cli.Context(GreetOptions)) !void { const name = ctx.get(.name); // Compile-time validated! std.debug.print("Hello, {s}!\n", .{name}); } var cmd = try cli.Command(GreetOptions).init(allocator, "greet", "Greet someone"); _ = cmd.setAction(greet);
-
Low-Level API (BaseCommand)
const cmd = try cli.BaseCommand.init(allocator, "myapp", "Description"); _ = try cmd.addOption(cli.Option.init("name", "name", "Your name", .string)); _ = cmd.setAction(myAction);
-
Type-safe Options
- String, int, float, boolean types
- Short and long flags (-n, --name)
- Required vs optional
- Default values
-
Argument Parsing
- Positional arguments
- Variadic arguments
- Type validation
- Error handling with helpful messages
-
Subcommand Support
- Nested commands with aliases
- Per-command options and arguments
- Command-specific actions
-
Auto-generated Help
- Usage instructions
- Option descriptions
- Command listings
- Formatting with proper alignment
-
Middleware System
- Pre/post command hooks
- Chainable middleware
- Built-in: logging, timing, validation, environment checks
-
State Machine
- Clean 5-state design
- Validated state transitions
- Error recovery
-
Prompt Types
- TextPrompt: Free text input with validation
- ConfirmPrompt: Yes/No questions
- SelectPrompt: Single choice from list
- MultiSelectPrompt: Multiple choices from list
- PasswordPrompt: Masked input
- NumberPrompt: Integer/float with min/max range
- PathPrompt: File/directory with Tab autocomplete
- GroupPrompt: Sequential prompts with shared state
- SpinnerPrompt: Animated loading indicator
-
UI Components
- ProgressBar: 4 styles (bar, blocks, dots, ASCII)
- Box: 4 border styles
- Table: 4 styles, column alignment, auto-width
- Messages: intro, outro, note, log, cancel
-
Terminal Features
- Raw mode for key capture
- ANSI color support
- Unicode/ASCII fallback
- Cursor control
- Style chaining API
-
Type-Safe Loading
const MyConfig = struct { host: []const u8, port: u16 = 8080, }; var config = try cli.config.load(MyConfig, allocator, "config.toml"); defer config.deinit(); // config.value.host, config.value.port - direct access
-
Three Format Parsers
- TOML, JSONC, JSON5
-
Auto-Discovery
- Searches standard config locations
- Format auto-detection by extension
This codebase uses modern Zig 0.16.0-dev APIs:
- I/O:
std.Io,std.Io.File.stdout(),.writerStreaming(io, &buf),.interface.print()/.flush() - ArrayList: Unmanaged - all methods take allocator parameter (
.append(alloc, val),.deinit(alloc)) - Sleep:
std.c.nanosleep(&.{ .sec = N, .nsec = N }, null)(nostd.time.sleep) - Process Args:
pub fn main(init: std.process.Init) !voidwithstd.process.Args.Iterator - Env Vars:
std.c.getenv(name)(nostd.posix.getenv) - Build System:
b.createModule(...),b.addTest(.{ .root_module = module }) - Terminal:
winsize.col/.row(not.ws_col/.ws_row)
-
Builder Pattern
- Fluent API for CLI construction
- Method chaining for configuration
-
State Machine
- Explicit state transitions
- Terminal states (submit/cancel)
- Error state with recovery
-
Comptime Generics
Command(T)generates options from struct fieldsContext(T)provides typed field accessConfigLoader(T)validates config against struct schema
-
Strategy Pattern
- Pluggable validators
- Custom action handlers
-
Full-Frame Rendering
- Simple clear-and-redraw approach for prompts
- Works across all terminals
- simple.zig - Minimal typed CLI
- basic.zig - CLI with options and subcommands
- typed.zig - Type-safe API with config integration
- advanced.zig - Complex CLI with multiple commands
- prompts.zig - All prompt types with validation
- showcase.zig - Comprehensive feature demonstration
- config.zig - Configuration file loading
zig build # Build the library
zig build test # Run all tests
zig build examples # Build all 7 examples
zig build run-simple # Run a specific examplezig-cli successfully replicates the core features and developer experience of clapp while leveraging Zig's strengths:
- Memory safety with explicit allocators
- Compile-time type checking via generics
- Zero-cost abstractions
- Cross-platform support
- No runtime dependencies
The library is ready for building sophisticated CLI applications with type-safe argument parsing, interactive prompts, and configuration management.