Config-driven CLI wizard framework for people who keep re-reading --help: describe a tool's flags once in YAML, then answer prompts instead of retyping the command.
- Scaffold from
--help.oz generate <tool>reads a tool's own help output and writes the wizard for you, tested against 59 real CLIs. - Runs where you do. macOS, Linux, and Windows, on amd64 and arm64.
- One binary, no runtime. Nine direct dependencies, nothing to install alongside it.
- Six option types, four ready-made wizards. select, multi-select, confirm, input, password, and number; rails-new, bundle-gem, docker-run, and git-switch.
- Wizards are regression tested.
oz testreplays pinned fixtures and diffs the built command, with the wrapped tool never installed or run.
brew install svyatov/tap/ozgo install github.com/svyatov/oz/cmd/oz@latestDownload pre-built binaries from the Releases page.
oz create mywizard # scaffold a new wizard YAML and open in $EDITOR
oz run mywizard # run the interactive wizard
oz run mywizard -n # dry-run: print command without executingOr install a wizard from the registry:
oz add rails-new # download from registry
oz run rails-new # run itPre-built wizards are available in the wizards/ directory:
| Wizard | Tool | Description |
|---|---|---|
| rails-new | Ruby on Rails | Scaffold a new Rails application |
| bundle-gem | Bundler | Generate a new rubygem skeleton |
| docker-run | Docker | Run a container with selected options |
| git-switch | Git | Switch to a branch from the local list |
Browse and install wizards:
oz list --remote # browse registry
oz add <name> # install a wizard
oz update <name> # update to latest versionThe registry is this repository's wizards/ directory (indexed by index.yml) on the main branch, so a wizard is installable as soon as it's merged here. Point OZ_REGISTRY_URL at another base URL to use a different source.
Have a CLI tool you use often? Wrap it in a wizard and share it:
- Create a YAML config:
oz create my-tooloroz generate my-tool - Test it:
oz validate my-tool && oz run my-tool - Add at least one fixture under
wizards/testdata/my-tool/(required: CI rejects a wizard without one) and generate its golden withoz test my-tool --update --config-dir . - Submit a PR adding your file to the
wizards/directory
See CONTRIBUTING.md for details, including the fixture format.
A wizard is one YAML file in ~/.config/oz/wizards/. Save this as
~/.config/oz/wizards/docker-run.yml to wrap docker run:
name: docker-run
description: Run a Docker container
command: docker run
flag_style: space
options:
- name: image
type: input
label: Image name
flag: ""
positional: true
required: true
- name: detach
type: confirm
label: Run detached?
flag: -d
- name: port
type: input
label: Port mapping
flag: -p
validate:
pattern: '^\d+:\d+$'
message: "Use host:container format (e.g. 8080:80)"
- name: env
type: multi_select
label: Environment
flag: -e
choices:
- value: NODE_ENV=production
label: Production
- value: NODE_ENV=development
label: Development| Command | Description |
|---|---|
oz run <wizard> |
Run a wizard interactively |
oz run <wizard> -n |
Dry-run (print command only) |
oz run <wizard> -p <preset> |
Run with a saved preset |
oz run <wizard> -- <args> |
Pass extra args to the built command |
oz list |
List available wizards |
oz list --remote |
Browse wizards in the registry |
oz add <name> |
Install a wizard from registry or local file |
oz update <wizard> |
Re-fetch a wizard from the registry |
oz create <name> |
Create a new wizard from template |
oz generate <tool> [subcmd...] |
Generate wizard YAML from --help output |
oz edit <wizard> |
Open wizard config in $EDITOR |
oz remove <wizard> |
Remove a wizard config |
oz validate <path> |
Validate a wizard YAML file |
oz test [wizard] |
Run wizard fixtures and check built commands |
oz test [wizard] --update |
Regenerate fixture goldens |
Aliases: r (run), a (add), c/new (create), g/gen (generate), e (edit), rm (remove), l/ls (list), u (update).
| Command | Description |
|---|---|
oz run <wizard> doctor |
Check tool installation and version |
oz run <wizard> show |
Show all options with descriptions |
oz run <wizard> pins |
Interactive pin manager |
oz run <wizard> pins list |
Display current pins |
oz run <wizard> pins clear |
Remove all pins |
oz run <wizard> presets list |
List saved presets |
oz run <wizard> presets show <name> |
Show preset values and command |
oz run <wizard> presets show <name> -v |
Annotated view with labels and descriptions |
oz run <wizard> presets save <name> |
Save last-used values as preset |
oz run <wizard> presets remove <name> |
Remove a preset |
- select: single choice from a list (
choicesorchoices_from) - multi_select: multiple choices with optional
separator - confirm: yes/no toggle (
flag,flag_true,flag_false) - input: free-text entry with optional
validate(pattern, min/max length) - password: masked entry for secrets, redacted in oz's output and never persisted (see below)
- number: integer/float entry with optional inclusive
min/maxbounds
A password option masks its input, shows **** everywhere oz prints a command or answer
(dry-run, confirmation prompt, values editor, oz run show), and is never written to
last-used state, presets, or pins. Every run re-prompts for it.
Under options: in the wizard's YAML file:
- name: token
type: password
label: API token
secret_env: GH_TOKEN # deliver via env var instead of argv- With
secret_env: oz setsGH_TOKEN=<value>in the executed command's environment and emits nothing for the option intoargv. On Linux this keeps the secret off the world-readable process list (/proc/<pid>/cmdlineis world-readable;/proc/<pid>/environis owner/root-only). That guarantee is Linux-specific. macOS and the BSDs have no/proc, and oz makes no claim about what their process tools expose. Treat env delivery there as a smallerargvfootprint, not a privacy guarantee. - Without
secret_env: the value is passed as the option's normal flag (--token <value>). oz still masks it in its own output, but a flag value is inherent to CLI argument passing and remains visible in the executed process'sargv. That is a documented limitation, not a defect. - Tradeoff: env delivery hands the secret to the wrapped process and every child it spawns
(env is inherited; argv is per-process), so a descendant that logs or crash-dumps its environment
can re-expose it. If both
secret_envandflagare set, env delivery wins (nothing is emitted toargv) and oz prints a warning.
Wizards live in ~/.config/oz/wizards/ (override with OZ_CONFIG_DIR or --config-dir).
| Field | Description |
|---|---|
flag |
CLI flag (e.g. --output) |
flag_style |
equals (default) or space |
positional |
Emit as bare argument, not flag |
default |
Pre-selected value |
required |
Prevent empty input submission |
allow_none |
Add "(none)" choice to select |
show_when / hide_when |
Conditional visibility based on other answers |
choices_from |
Shell command for dynamic choices |
version_control |
Auto-detect tool version and filter options |
versions |
Semver constraint to show option only for matching versions |
Wizards can detect the installed tool version and filter options by semver range. At the top level of the wizard's YAML file:
version_control:
command: ruby --version
pattern: '(\d+\.\d+\.\d+)'
label: Ruby
custom_version_command: rbenv versions --bare
available_versions: "3.2,3.1,3.0"
custom_version_verify: rbenv versions --bare | grep -q {{version}}
options:
- name: yjit
type: confirm
label: Enable YJIT?
flag: --yjit
versions: ">= 3.1"Supports all semver constraint syntax: >=, <=, >, <, !=, tilde (~1.2), caret (^2.0), wildcards (1.2.x), hyphen ranges (1.2 - 1.4), and OR (||).
Show or hide options based on previous answers. Under options: in the wizard's YAML file:
- name: db
type: select
label: Database
choices:
- { value: pg, label: PostgreSQL }
- { value: sqlite, label: SQLite }
- name: pool_size
type: input
label: Connection pool size
flag: --pool
show_when:
db: pgLoad choices from a shell command at runtime. Under options: in the wizard's YAML file:
- name: branch
type: select
label: Branch
choices_from: git branch --format='%(refname:short)'Instead of writing YAML from scratch, scaffold a wizard from any CLI tool's help output:
oz generate docker run # parse docker run --help, save to config dir
oz generate kubectl apply # subcommand support
oz generate ffmpeg # works with most help formatsThe parser auto-detects help format and handles GNU, Cobra, kubectl/pflag, Clap (Rust), argparse (Python), Thor (Ruby), dry-cli (Hanami), npm, man pages, Homebrew, and headerless formats. Tested against 59 real-world CLI tools.
# Bash
oz completion bash > /etc/bash_completion.d/oz
# Zsh
oz completion zsh > "${fpath[1]}/_oz"
# Fish
oz completion fish > ~/.config/fish/completions/oz.fish
# PowerShell
oz completion powershell > oz.ps1Questions and bug reports both go to GitHub Issues. For a suspected vulnerability, use the private channel in SECURITY.md instead.
oz is maintained by one person in their spare time. Issues and pull requests get a reply, fixes ship when they are ready, and there is no support commitment beyond that. See Governance for what happens if that stops.
See CONTRIBUTING.md for development setup, coding standards, and how to contribute wizards.
Released versions and what changed in each are in CHANGELOG.md.