Documentation Index
Fetch the complete documentation index at: https://mintlify.com/wevm/incur/llms.txt
Use this file to discover all available pages before exploring further.
Overview
Incur normalizes all command output into a structured envelope with consistent shape. The envelope includes:
- Data — the command’s return value
- Metadata — command name, duration, and CTAs
- Error details — when the command fails
By default, incur uses TOON format for output, which is up to 60% more token-efficient than JSON for agents.
Output Envelope Structure
Every command produces an output envelope with a consistent structure.
Success Envelope
{
ok: true,
data: <command return value>,
meta: {
command: "my-cli deploy",
duration: "42ms",
cta?: { ... } // optional
}
}
Error Envelope
{
ok: false,
error: {
code: "NOT_AUTHENTICATED",
message: "Token not found",
retryable: false, // optional
fieldErrors: [...] // optional, for validation errors
},
meta: {
command: "my-cli deploy",
duration: "12ms",
cta?: { ... } // optional
}
}
Return Values
By default, commands return data directly:
cli.command('status', {
run() {
return { clean: true, branch: 'main' }
},
})
$ my-cli status
clean: true
branch: main
Use --verbose to see the full envelope:
$ my-cli status --verbose
ok: true
data:
clean: true
branch: main
meta:
command: status
duration: 8ms
Using c.ok() and c.error()
Use c.ok() and c.error() to attach CTAs or signal structured errors.
c.ok()
Return success with optional metadata:
cli.command('deploy', {
run(c) {
return c.ok(
{ deployed: true, url: 'https://staging.example.com' },
{
cta: {
commands: [
{ command: 'logs', description: 'View deployment logs' },
{ command: 'status', description: 'Check status' },
],
},
}
)
},
})
$ my-cli deploy
deployed: true
url: https://staging.example.com
Suggested commands:
my-cli logs # View deployment logs
my-cli status # Check status
c.error()
Return a structured error:
cli.command('deploy', {
run(c) {
if (!authenticated()) {
return c.error({
code: 'NOT_AUTHENTICATED',
message: 'Token not found',
retryable: false,
cta: {
commands: [
{ command: 'login', description: 'Log in to continue' },
],
},
})
}
return { deployed: true }
},
})
$ my-cli deploy
Error (NOT_AUTHENTICATED): Token not found
Suggested commands:
my-cli login # Log in to continue
Call-to-Actions (CTAs)
CTAs tell users (human or agent) what commands to run next. They appear after successful or failed commands.
String CTAs
Simplest form — just the command name:
return c.ok(
{ items: [...] },
{
cta: {
commands: ['get 1', 'list closed'],
},
}
)
Suggested commands:
my-cli get 1
my-cli list closed
Object CTAs
Provide structured arguments and options with descriptions:
return c.ok(
{ items: [...] },
{
cta: {
commands: [
{
command: 'get',
args: { id: 1 },
description: 'View item details',
},
{
command: 'list',
args: { state: 'closed' },
description: 'View closed items',
},
],
},
}
)
Suggested commands:
my-cli get 1 # View item details
my-cli list closed # View closed items
Type-Safe CTAs
CTAs are fully type-inferred. If your CLI has registered commands, the command field is typed as a union of command names:
cli
.command('get', { ... })
.command('list', { ... })
.command('deploy', {
run(c) {
return c.ok(
{ deployed: true },
{
cta: {
commands: [
{ command: 'get' }, // ✅ typed as 'get' | 'list' | 'deploy'
{ command: 'status' }, // ❌ type error if 'status' isn't registered
],
},
}
)
},
})
Incur supports multiple output formats. Use --format <fmt> or --json to override the default.
TOON (default)
TOON is a token-efficient format designed for agents. It strips braces, quotes, and redundant keys.
$ my-cli status
clean: true
branch: main
files[2]: a.txt,b.txt
JSON
Standard JSON with indentation:
$ my-cli status --json
{
"clean": true,
"branch": "main",
"files": ["a.txt", "b.txt"]
}
YAML
YAML format:
$ my-cli status --format yaml
clean: true
branch: main
files:
- a.txt
- b.txt
Markdown
Tables for flat objects and arrays:
$ my-cli users --format md
| id | name | email |
|----|-------|------------------|
| 1 | Alice | alice@example.com|
| 2 | Bob | bob@example.com |
JSONL
Newline-delimited JSON, useful for streaming:
$ my-cli logs --format jsonl
{"type":"chunk","data":"connecting..."}
{"type":"chunk","data":"streaming logs"}
{"type":"done","ok":true}
Formats are resolved in this order:
- Explicit
--format or --json flag
- Command-level
format option
- CLI-level
format option
- Default (
toon)
Cli.create('my-cli', {
format: 'json', // CLI-level default
})
.command('deploy', {
format: 'yaml', // Overrides CLI default
run() {
return { deployed: true }
},
})
$ my-cli deploy
# Uses YAML (command-level override)
$ my-cli deploy --json
# Uses JSON (flag override)
Output Policy
Control when output data is displayed with outputPolicy.
'all' (default)
Displays to both humans and agents:
cli.command('deploy', {
outputPolicy: 'all', // default
run() {
return { deployed: true }
},
})
'agent-only'
Suppresses data in TTY mode, but still returns it to agents:
cli.command('deploy', {
outputPolicy: 'agent-only',
run() {
// Agents get the data; humans see nothing (unless --verbose or --json)
return { id: 'deploy-123', url: 'https://staging.example.com' }
},
})
$ my-cli deploy
# (no output in terminal)
$ my-cli deploy --json
{"id":"deploy-123","url":"https://staging.example.com"}
Inheritance:
- CLI-level
outputPolicy applies to all commands
- Group-level
outputPolicy overrides CLI-level
- Command-level
outputPolicy overrides group-level
Streaming Output
Use async *run to stream chunks incrementally:
cli.command('logs', {
async *run() {
yield 'connecting...'
yield 'streaming logs'
yield { progress: 50 }
yield { progress: 100 }
return c.ok(undefined, {
cta: {
commands: [{ command: 'status', description: 'Check status' }],
},
})
},
})
With default TOON format, each chunk is printed as a line:
$ my-cli logs
connecting...
streaming logs
progress: 50
progress: 100
Suggested commands:
my-cli status # Check status
With --format jsonl, each chunk becomes a structured event:
$ my-cli logs --format jsonl
{"type":"chunk","data":"connecting..."}
{"type":"chunk","data":"streaming logs"}
{"type":"chunk","data":{"progress":50}}
{"type":"chunk","data":{"progress":100}}
{"type":"done","ok":true,"meta":{"command":"logs","duration":"42ms"}}
Error Handling
Errors are automatically captured and normalized:
Thrown Errors
run() {
throw new Error('Something went wrong')
}
$ my-cli deploy
Error: Something went wrong
Structured Errors
Use c.error() for custom error codes:
run(c) {
return c.error({
code: 'DEPLOY_FAILED',
message: 'Could not connect to server',
retryable: true,
})
}
$ my-cli deploy --verbose
ok: false
error:
code: DEPLOY_FAILED
message: Could not connect to server
retryable: true
meta:
command: deploy
duration: 12ms
Validation Errors
Validation errors include field-level details:
$ my-cli deploy
Error: missing required argument <env>
With --verbose:
$ my-cli deploy --verbose
ok: false
error:
code: VALIDATION_ERROR
message: Required
fieldErrors:
- path: env
expected: string
received: undefined
message: Required
meta:
command: deploy
duration: 3ms
Next Steps
CLI Creation
Learn how to create and configure CLIs
Middleware
Explore middleware for composable hooks