0Z4C0
// navigation
// @ozaco/cli

Command

Declarative command trees with schema-validated flags and a dispatching registry.

import from@ozaco/cli/command

@ozaco/cli/command turns a tree of plain descriptors into a working CLI. You describe leaf subcommands with defineAction and group them with defineCommand; both are pure metadata — no plugin is built at definition time. The DefaultRegistry compiles a registered tree into a path-identified plugin tree and installs each level lazily as dispatch descends into it.

An action carries an input schema (any Standard Schema — zod, valibot, arktype), which both types the handler’s ctx (StandardSchemaV1.InferOutput) and drives parsing. The short map assigns short flags (e.g. { message: 'm' }) and args lists the fields fillable positionally. The runner tokenizes argv, validates it against the schema, and only then calls the handler — parse or validation problems surface as CommandErrors failures rather than exceptions.

Because compiled commands are keyed by their tree path (e.g. kube.config), distinct commands never collide in the shared scope even when they share a human-facing name. --help/-h and --version/-V are handled by the runner.

01

Defining commands

Pure descriptors for leaf actions and the command tree.

fn

defineCommand

Describe a command and its members (nested commands + leaf actions).

signature
function defineCommand<TContext = unknown, TArgs extends unknown[] = []>(
  options: CommandDef.Options<TContext, TArgs>,
): CommandDef.Spec<TContext, TArgs>

Returns a pure Spec, not a plugin. options.actions mixes leaf actions (from defineAction, collected into leaf) and nested command specs (collected into subs, keyed by the token you type). An optional setup builds a context installed when dispatch enters the command.

// parameters
options: CommandDef.Options<TContext, TArgs>
Command `name`, optional `version`/`description`, the `actions` map, and optional `setup`.
// returns A `CommandDef.Spec` to hand to `Registry.actions.register`.
example
import { defineAction, defineCommand } from '@ozaco/cli/command'
import { z } from 'zod'

const greet = defineAction(
  { description: 'Greet someone', input: z.object({ name: z.string() }), args: ['name'] },
  function* (ctx) {
    console.log(`Hello, ${ctx.name}`)
  },
)

const app = defineCommand({
  name: 'app',
  actions: { greet },
})
fn

defineAction

Define a leaf subcommand: a handler carrying its parse metadata.

signature
function defineAction<S extends StandardSchemaV1, R>(
  config: CommandDef.ActionConfig<S> & { input: S },
  handler: (ctx: StandardSchemaV1.InferOutput<S>) => Operation<R>,
): CommandDef.Action<S, R>
function defineAction<R>(
  config: Omit<CommandDef.ActionConfig, 'input'>,
  handler: (ctx: EmptyType) => Operation<R>,
): CommandDef.Action<unknown, R>

The handler is a generator operation; its ctx is the parsed, validated options+args object inferred from input. With no input, ctx is empty. config.short maps schema fields to short flags and config.args lists fields fillable positionally, in order.

// parameters
config: CommandDef.ActionConfig<S>
`description?`, `input?` schema, `short?` flag map, and `args?` positional list.
handler: (ctx) => Operation<R>
Runs after argv is parsed and validated; `ctx` is typed from `input`.
// returns A `CommandDef.Action` — the handler augmented with parse metadata.
example
import { defineAction } from '@ozaco/cli/command'
import { z } from 'zod'

const build = defineAction(
  {
    input: z.object({ watch: z.boolean().default(false) }),
    short: { watch: 'w' },
  },
  function* (ctx) {
    if (ctx.watch) startWatcher()
  },
)
02

Registry

Compiles and dispatches command trees (mirrors the backend `Broker`).

const

DefaultRegistry

The command registry: register command trees, then run argv against them.

signature
const DefaultRegistry: Plugin<
  RegistryDef.Context,
  [options?: RegistryDef.Options],
  RegistryDef.Actions
>

Install it (optionally with { name, version, description }), register each top-level command — which compiles it and runs its own setup — then run(argv) dispatches argv[0] to the matching command tree. get(name) looks a registered command up.

example
import { install } from '@ozaco/std/plugin'
import { Registry } from '@ozaco/cli/core'
import { DefaultRegistry } from '@ozaco/cli/command'

yield* install(DefaultRegistry, { name: 'app', version: '1.0.0' })
yield* Registry.actions.register(app)
yield* Registry.actions.run(process.argv.slice(2))
03

Errors

const

CommandErrors

The `cli:command.*` failure tags raised during parse and validation.

signature
const CommandErrors: {
  Parse: 'cli:command.parse'
  Required: 'cli:command.required'
  Invalid: 'cli:command.invalid'
  Unknown: 'cli:command.unknown'
}

The runner fails with Parse on malformed argv, Required for a missing required field, Invalid when the schema rejects a value, and Unknown for an unrecognized command or flag.

04

Types

ns

CommandDef

The command descriptor types: config, action, spec and resolved option metadata.

signature
namespace CommandDef {
  type Infer<S> // parsed ctx type inferred from an action's input schema
  interface ActionConfig<S extends StandardSchemaV1> {
    description?: string
    input?: S
    short?: Record<string, string>
    args?: readonly string[]
  }
  type Action<S, R> = ActionMeta & ((ctx: Infer<S>) => Operation<R>)
  type Member = Action | Spec
  interface Options<TContext, TArgs> {
    name: string
    version?: string
    description?: string
    actions: Record<string, Member>
    setup?: (...args: TArgs) => Operation<TContext>
  }
  interface Spec<TContext, TArgs> {
    name: string
    leaf: Record<string, Action>
    subs: Record<string, Spec>
    setup?: (...args: TArgs) => Operation<TContext>
  }
}

The CommandDef namespace also exposes:

  • ActionMeta — the metadata defineAction attaches to a handler (_t, input, short, args, description).
  • Built — a Spec compiled into a path-identified plugin (identity like kube.config).
  • OptionInfo — resolved option metadata derived from an input schema (name, type, array, enum?, required, hasDefault) used for tokenizing and help.