Overview
Commands are registered with .command(), which accepts a name and a definition object containing schemas, metadata, and a run handler.
Registering Commands
Chain .command() calls to register multiple commands:
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 (). describe ( 'Package name' ),
}),
run ( c ) {
return { installed: c . args . package }
},
})
. serve ()
$ my-cli status
clean: true
$ my-cli install express
installed: express
Command Definition Structure
A command definition includes schemas for inputs, output, and the handler function.
Required Fields
The command handler. Receives a context object with parsed args, options, env, and helper functions. Can return a value, a Promise, or an AsyncGenerator for streaming.
Zod schema for positional arguments. Keys define the order arguments are parsed. args : z . object ({
source: z . string (). describe ( 'Source file' ),
dest: z . string (). describe ( 'Destination file' ),
})
Zod schema for named options/flags. options : z . object ({
force: z . boolean (). optional (). describe ( 'Overwrite existing files' ),
verbose: z . boolean (). default ( false ). describe ( 'Show detailed output' ),
})
Zod schema for environment variables. Keys are the variable names. env : z . object ({
DEPLOY_TOKEN: z . string (),
API_BASE: z . string (). default ( 'https://api.example.com' ),
})
Zod schema for the return value. Validates the data returned from run(). output : z . object ({
url: z . string (),
duration: z . number (),
})
A short description of what the command does. Shown in help output.
Map of option names to single-character aliases. alias : { saveDev : 'D' , force : 'f' }
Enables -D as shorthand for --saveDev.
Usage examples shown in help output. examples : [
{ args: { env: 'staging' }, description: 'Deploy to staging' },
{ args: { env: 'production' }, options: { force: true }, description: 'Force deploy to production' },
]
Plain text hint displayed in help after examples and before global options.
Behavior
format
'toon' | 'json' | 'yaml' | 'md' | 'jsonl'
Default output format for this command. Overrides CLI-level format.
Controls when output data is displayed. Overrides CLI-level or group-level policy.
Middleware that runs only for this command, after root and group middleware.
Mounting Sub-CLIs
Mount another CLI instance as a command group by passing it directly to .command():
import { Cli , z } from 'incur'
const cli = Cli . create ( 'my-cli' , { description: 'My CLI' })
// Create a sub-group for database commands
const db = Cli . create ( 'db' , { description: 'Database commands' })
. command ( 'migrate' , {
description: 'Run migrations' ,
run () {
return { migrated: true }
},
})
. command ( 'seed' , {
description: 'Seed the database' ,
run () {
return { seeded: true }
},
})
cli
. command ( 'run' , {
description: 'Run a task' ,
run () {
return { ok: true }
},
})
. command ( db ) // Mount the db group
. serve ()
$ my-cli db migrate
migrated: true
$ my-cli db seed
seeded: true
Run Handler Context
The run function receives a context object with parsed inputs and helpers:
run ( c ) {
c . agent // boolean: true if stdout is not a TTY (consumed by an agent)
c . args // Parsed positional arguments
c . options // Parsed named options
c . env // Parsed environment variables
c . var // Variables set by middleware
c . name // CLI name
c . version // CLI version string
// Helpers
c . ok ( data , { cta }) // Return success with optional CTAs
c . error ({ code , message , cta }) // Return error with optional CTAs
}
Returning Success
Return a value directly or use c.ok() to attach CTAs:
run ( c ) {
return { deployed: true }
}
// With CTAs
run ( c ) {
return c . ok (
{ deployed: true },
{
cta: {
commands: [
{ command: 'logs' , description: 'View logs' },
{ command: 'status' , description: 'Check status' },
],
},
}
)
}
Returning Errors
Throw an error or use c.error() for structured errors:
run ( c ) {
throw new Error ( 'Something went wrong' )
}
// Structured error with code
run ( c ) {
return c . error ({
code: 'NOT_AUTHENTICATED' ,
message: 'Token not found' ,
retryable: false ,
})
}
Async Handlers
Handlers can be async:
run ( async c ) {
const response = await fetch ( 'https://api.example.com/data' )
const data = await response . json ()
return data
}
Streaming Handlers
Use async *run to stream chunks incrementally:
cli . command ( 'logs' , {
description: 'Tail logs' ,
async * run () {
yield 'connecting...'
yield 'streaming logs'
yield 'done'
},
})
$ my-cli logs
connecting...
streaming logs
done
With --format jsonl, each chunk becomes {"type":"chunk","data":"..."}. You can also yield objects:
async * run () {
yield { progress: 50 }
yield { progress: 100 }
}
Return c.ok() or c.error() from a streaming handler to attach CTAs:
async * run ( c ) {
yield { step: 1 }
yield { step: 2 }
return c . ok ( undefined , {
cta: {
commands: [{ command: 'status' , description: 'Check status' }],
},
})
}
Command Aliases
Use the alias field to define single-character shortcuts for options:
cli . command ( 'deploy' , {
options: z . object ({
force: z . boolean (). optional (),
dryRun: z . boolean (). optional (),
}),
alias: { force: 'f' , dryRun: 'n' },
run ( c ) {
return { deployed: ! c . options . dryRun }
},
})
$ my-cli deploy -f
deployed: true
$ my-cli deploy -fn
deployed: false
Short aliases can be stacked (e.g. -fn) when all but the last are boolean flags.
Agent Detection
The c.agent boolean is true when stdout is not a TTY (piped or consumed by an agent). Use it to tailor behavior:
cli . command ( 'deploy' , {
args: z . object ({
env: z . enum ([ 'staging' , 'production' ]),
}),
run ( c ) {
if ( ! c . agent ) {
console . log ( `Deploying to ${ c . args . env } ...` )
}
return { url: `https:// ${ c . args . env } .example.com` }
},
})
Next Steps
Schemas Explore Zod schema validation and type inference
Output Learn about output formats and CTAs