Skip to main content

Overview

Incur CLIs are created using Cli.create(), which can produce either a single-command CLI (one action) or a multi-command CLI (router with subcommands).

Single-Command CLI

Pass a run function directly to Cli.create() to create a CLI that performs one action.
import { Cli, z } from 'incur'

Cli.create('greet', {
  description: 'A greeting CLI',
  args: z.object({
    name: z.string().describe('Name to greet'),
  }),
  run(c) {
    return { message: `hello ${c.args.name}` }
  },
}).serve()
$ greet world
message: hello world
Even single-command CLIs can register subcommands later using .command(). The root handler runs when no subcommand matches.

Multi-Command CLI

Omit the run function to create a router CLI that delegates to subcommands.
import { Cli, z } from 'incur'

Cli.create('my-cli', {
  description: 'My CLI',
})
  .command('status', {
    description: 'Show repo status',
    run() {
      return { clean: true }
    },
  })
  .command('install', {
    description: 'Install a package',
    args: z.object({
      package: z.string().optional().describe('Package name'),
    }),
    options: z.object({
      saveDev: z.boolean().optional().describe('Save as dev dependency'),
    }),
    alias: { saveDev: 'D' },
    run(c) {
      return { added: 1, packages: 451 }
    },
  })
  .serve()
$ my-cli status
clean: true

$ my-cli install express -D
added: 1
packages: 451

Configuration Options

Required Options

name
string
required
The CLI binary name. Used in help text, error messages, and CTAs.

Common Options

description
string
A short description of what the CLI does. Shown in help output.
version
string
The CLI version string. Displayed with --version.
run
function
The root command handler. When provided, creates a single-command CLI. Accepts a context object with args, options, env, and helper functions like ok() and error().

Advanced Options

args
z.ZodObject<any>
Zod schema for positional arguments. Keys define the argument order.
options
z.ZodObject<any>
Zod schema for named options/flags.
env
z.ZodObject<any>
Zod schema for environment variables. Keys are variable names (e.g. NPM_TOKEN).
vars
z.ZodObject<any>
Zod schema for middleware variables. Enables typed dependency injection.
output
z.ZodType
Zod schema for the return value. Enforces output shape validation.
format
'toon' | 'json' | 'yaml' | 'md' | 'jsonl'
Default output format. Overridden by --format or --json flags.
outputPolicy
'all' | 'agent-only'
Controls when output data is displayed.
  • 'all' — displays to both humans and agents (default)
  • 'agent-only' — suppresses data in TTY mode, only returns to agents
alias
Record<string, string>
Map of option names to single-character aliases.
aliases
string[]
Alternative binary names for this CLI (e.g. shorter aliases in package.json bin).

Command Groups

Create separate CLI instances and mount them as command groups using .command(cli).
import { Cli, z } from 'incur'

const cli = Cli.create('my-cli', { description: 'My CLI' })

// Create a `pr` group
const pr = Cli.create('pr', { description: 'Pull request commands' })
  .command('list', {
    description: 'List pull requests',
    options: z.object({
      state: z.enum(['open', 'closed', 'all']).default('open'),
    }),
    run(c) {
      return { prs: [], state: c.options.state }
    },
  })

cli
  .command(pr)  // Mount the `pr` group
  .serve()
$ my-cli pr list --state closed
prs: (empty)
state: closed
Command groups inherit outputPolicy and middleware from their parent CLI unless explicitly overridden.

CLI Name in Context

The CLI name is available in the run context as c.name. Useful for composing help text and error messages.
const cli = Cli.create('deploy-cli', { description: 'Deploy tools' })

cli.command('check', {
  output: z.string(),
  run(c) {
    if (!authenticated()) {
      return `Not logged in. Run \`${c.name} auth login\` to log in.`
    }
    return 'OK'
  },
})

Single-Object Syntax

You can pass all options, including name, as a single object:
Cli.create({
  name: 'greet',
  description: 'A greeting CLI',
  args: z.object({
    name: z.string().describe('Name to greet'),
  }),
  run(c) {
    return { message: `hello ${c.args.name}` }
  },
}).serve()
This is useful when loading configuration from package.json or other sources.

Next Steps

Commands

Learn how to register and define commands

Schemas

Explore Zod schema validation and type inference

Build docs developers (and LLMs) love