Skip to content

Repository files navigation

oz

Config-driven CLI wizard framework. Define interactive prompts in YAML, run them with Bubbletea, and execute the resulting shell commands.

Install

Homebrew (macOS)

brew install svyatov/tap/oz

Go

go install github.com/svyatov/oz/cmd/oz@latest

Binary Download

Download pre-built binaries from the Releases page.

Quick Start

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 executing

Or install a wizard from the registry:

oz add rails-new      # download from registry
oz run rails-new      # run it

Available Wizards

Pre-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 version

The 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.

Contributing a Wizard

Have a CLI tool you use often? Wrap it in a wizard and share it:

  1. Create a YAML config: oz create my-tool or oz generate my-tool
  2. Test it: oz validate my-tool && oz run my-tool
  3. Add at least one fixture under wizards/testdata/my-tool/ (required — CI rejects a wizard without one) and generate its golden with oz test my-tool --update --config-dir .
  4. Submit a PR adding your file to the wizards/ directory

See CONTRIBUTING.md for details, including the fixture format.

Example Wizard

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

Commands

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).

Per-Wizard Subcommands

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

Features

Option Types

  • select -- single choice from a list (choices or choices_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/max bounds

Password Fields and Secret Delivery

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.

  - name: token
    type: password
    label: API token
    secret_env: GH_TOKEN   # deliver via env var instead of argv
  • With secret_env: oz sets GH_TOKEN=<value> in the executed command's environment and emits nothing for the option into argv. On Linux this keeps the secret off the world-readable process list (/proc/<pid>/cmdline is world-readable; /proc/<pid>/environ is owner/root-only). macOS/BSD have no /proc and restrict cross-user ps by default, so env delivery is no less private than a flag everywhere and strictly more private on Linux — but "off the process list" is a Linux-specific 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's argv — 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_env and flag are set, env delivery wins (nothing is emitted to argv) and oz prints a warning.

Configuration

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

Version Control

Wizards can detect the installed tool version and filter options by semver range:

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 (||).

Conditional Visibility

Show or hide options based on previous answers:

- 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: pg

Dynamic Choices

Load choices from a shell command at runtime:

- name: branch
  type: select
  label: Branch
  choices_from: git branch --format='%(refname:short)'

Generate Wizards from --help

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 formats

The 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.

Shell Completions

# 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.ps1

Contributing

See CONTRIBUTING.md for development setup, coding standards, and how to contribute wizards.

License

MIT

Releases

Used by

Contributors

Languages