Arguments & Options
Arguments and options are declared on the command, and the type of run(args) is inferred from that
declaration. Nothing else needs to be written down.
Arguments
Section titled “Arguments”Arguments are positional, matched in the order they are declared. name and description are
required; type defaults to string.
import { cli, z } from 'clibuilder'
cli({ name: 'app', version: '1.0.0' }).default({ arguments: [ { name: 'name', description: 'your name' } ], run(args) { // args.name: string this.ui.info(`hello, ${args.name}`) }})An argument typed as an array is variadic — it collects every remaining positional value:
cli({ name: 'app', version: '1.0.0' }).command({ name: 'cat', description: 'print files', arguments: [ { name: 'files', description: 'files to print', type: z.array(z.string()) } ], run(args) { // args.files: string[] for (const file of args.files) this.ui.info(file) }})$ app cat a.txt b.txt c.txtOptions
Section titled “Options”Options are declared as a record keyed by option name. description is required; type defaults to
boolean | undefined, which is what you want for a flag.
cli({ name: 'app', version: '1.0.0' }).default({ options: { 'no-progress': { description: 'disable progress bar' } }, run(args) { // args['no-progress']: boolean | undefined if (args['no-progress']) this.ui.info('progress bar disabled') }})Option values are coerced to the declared type, and a value that doesn’t fit is a usage error.
options: { port: { description: 'port to listen on', type: z.number() }}$ app --port=3000 # args.port === 3000$ app --port=abc # usage error: expected to be numberAliases
Section titled “Aliases”options: { project: { description: 'project directory', alias: ['p'] }}$ app -p ./packages/core$ app --project ./packages/core$ app --project=./packages/coreSingle-character options bundle: -abc sets a and b to true and gives c the following value.
An alias can be hidden from the help message:
options: { project: { description: 'project directory', alias: ['p', { alias: 'proj', hidden: true }] }}Defaults
Section titled “Defaults”default supplies the value when the option is absent.
options: { port: { description: 'port to listen on', type: z.number(), default: 3000 }}Conflicting options
Section titled “Conflicting options”conflicts names the options this one cannot be used with.
options: { full: { description: 'print the whole file', conflicts: ['lines'] }, lines: { description: 'lines to print', type: z.number(), alias: ['n'], default: 10 }}$ app --full -n 20option --full cannot be used with option -nPassing both is a usage error (exit code 2), reported with the other parse errors.
A default value does not count as passed, so app --full is valid here.
The conflict applies in both directions, and the help message lists it on both options.
Repeating an option
Section titled “Repeating an option”An array-typed option accumulates every occurrence:
options: { tag: { description: 'a tag', type: z.array(z.string()) }}$ app --tag=a --tag=b # args.tag === ['a', 'b']A non-array option given more than once is a usage error, rather than silently keeping the last one.
Optional values
Section titled “Optional values”Wrap the type in z.optional() to make an argument or option optional. Without it, a declared
argument is required, and omitting it is a usage error that prints the help message.
cli({ name: 'app', version: '1.0.0' }).default({ arguments: [ { name: 'a', description: 'an optional name', type: z.optional(z.string()) } ], options: { y: { description: 'an optional number', type: z.optional(z.number()) } }, run(args) { // args.a: string | undefined // args.y: number | undefined }})Supported types
Section titled “Supported types”| Declaration | args type |
Argument | Option |
|---|---|---|---|
| (omitted) | string / boolean | undefined |
The default — a string | The default — a flag |
z.string() |
string |
✓ | ✓ |
z.number() |
number |
✓ coerced | ✓ coerced |
z.boolean() |
boolean |
✓ coerced | ✓ --flag, --flag=true, --flag false |
z.array(z.string()) |
string[] |
✓ variadic | ✓ repeatable |
z.array(z.number()) |
number[] |
✓ variadic, coerced | ✓ repeatable, coerced |
z.optional(...) |
T | undefined |
✓ makes it optional | ✓ |
Option validation happens before run() is called. A value that fails its schema, an unknown flag,
a missing required argument, or an extra positional argument all produce the same outcome: the CLI
prints the help message and does not run the command.
Built-in options
Section titled “Built-in options”Every command inherits these; you don’t declare them, and you shouldn’t shadow them.
| Flag | Alias | Effect |
|---|---|---|
--help |
-h |
Print the resolved command’s help message |
--version |
-v |
Print the CLI version |
--verbose |
-V |
Display level debug |
--silent |
Display level none |
|
--debug-cli |
Display level trace, including clibuilder’s own internal messages |
args.help is always present in the inferred type for that reason.
One more is added only when the CLI declares config, since it would have nothing to report
otherwise:
| Flag | Alias | Effect |
|---|---|---|
--show-config |
Print the resolved config and the file it came from — see Configuration |