> ## 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.

# Schemas

> Use Zod schemas for validation and automatic type inference

## Overview

Incur uses [Zod](https://zod.dev) schemas to validate inputs and outputs. Schemas enable:

* **Automatic validation** — inputs are parsed before `run()` executes
* **Type inference** — TypeScript infers types from schemas with zero manual annotations
* **Error reporting** — validation errors include field-level details
* **Schema composition** — reuse schemas across commands

## Importing Zod

Incur re-exports Zod, so you can import `z` directly from `incur`:

```typescript theme={null}
import { Cli, z } from 'incur'
```

## Arguments Schema

The `args` schema defines positional arguments. Keys define the order arguments are parsed from the command line.

```typescript theme={null}
cli.command('copy', {
  args: z.object({
    source: z.string().describe('Source file'),
    dest: z.string().describe('Destination file'),
  }),
  run(c) {
    // c.args.source and c.args.dest are typed as string
    return { copied: `${c.args.source} → ${c.args.dest}` }
  },
})
```

```bash theme={null}
$ my-cli copy file.txt backup.txt
copied: file.txt → backup.txt
```

### Optional Arguments

Use `.optional()` to make an argument optional:

```typescript theme={null}
args: z.object({
  package: z.string().optional().describe('Package name'),
})
```

### Default Values

Use `.default()` to provide a fallback value:

```typescript theme={null}
args: z.object({
  env: z.enum(['staging', 'production']).default('staging'),
})
```

## Options Schema

The `options` schema defines named flags. Options are parsed from `--name value` or `--name` (for booleans).

```typescript theme={null}
cli.command('deploy', {
  args: z.object({
    env: z.enum(['staging', 'production']),
  }),
  options: z.object({
    force: z.boolean().optional().describe('Overwrite existing deployment'),
    dryRun: z.boolean().default(false).describe('Preview changes without deploying'),
  }),
  run(c) {
    // c.options.force: boolean | undefined
    // c.options.dryRun: boolean
    return { deployed: !c.options.dryRun }
  },
})
```

```bash theme={null}
$ my-cli deploy staging --force
deployed: true

$ my-cli deploy production --dry-run
deployed: false
```

### Boolean Options

Boolean options are flags that don't require a value:

```typescript theme={null}
options: z.object({
  verbose: z.boolean().default(false),
})
```

```bash theme={null}
$ my-cli run --verbose
```

Use `--no-` prefix to negate:

```bash theme={null}
$ my-cli run --no-verbose
```

### String Options

String options require a value:

```typescript theme={null}
options: z.object({
  output: z.string().describe('Output file path'),
})
```

```bash theme={null}
$ my-cli build --output dist/bundle.js
```

### Number Options

Number options are automatically coerced:

```typescript theme={null}
options: z.object({
  port: z.number().default(3000).describe('Server port'),
})
```

```bash theme={null}
$ my-cli serve --port 8080
```

### Enum Options

Use `z.enum()` for a fixed set of values:

```typescript theme={null}
options: z.object({
  format: z.enum(['json', 'yaml', 'toml']).default('json'),
})
```

```bash theme={null}
$ my-cli export --format yaml
```

### Array Options

Use `z.array()` to collect multiple values:

```typescript theme={null}
options: z.object({
  include: z.array(z.string()).describe('Files to include'),
})
```

```bash theme={null}
$ my-cli build --include src/a.ts --include src/b.ts
```

### Kebab-Case to camelCase

Incur automatically maps kebab-case flags to camelCase schema keys:

```typescript theme={null}
options: z.object({
  saveDev: z.boolean().optional(),
})
```

```bash theme={null}
$ my-cli install --save-dev
# Parsed as c.options.saveDev = true
```

## Environment Variables Schema

The `env` schema validates environment variables. Keys are the variable names (e.g. `NPM_TOKEN`).

```typescript theme={null}
cli.command('deploy', {
  args: z.object({
    env: z.enum(['staging', 'production']),
  }),
  env: z.object({
    DEPLOY_TOKEN: z.string(),
    API_BASE: z.string().default('https://api.example.com'),
  }),
  run(c) {
    // c.env.DEPLOY_TOKEN: string
    // c.env.API_BASE: string
    return { url: c.env.API_BASE, token: c.env.DEPLOY_TOKEN }
  },
})
```

If `DEPLOY_TOKEN` is not set, incur raises a validation error before `run()` executes.

## Output Schema

The `output` schema validates the return value from `run()`. This ensures the command always returns data in the expected shape.

```typescript theme={null}
cli.command('deploy', {
  args: z.object({
    env: z.enum(['staging', 'production']),
  }),
  output: z.object({
    url: z.string(),
    duration: z.number(),
  }),
  run(c) {
    // Return value must match output schema
    return {
      url: `https://${c.args.env}.example.com`,
      duration: 3.2,
    }
  },
})
```

If the return value doesn't match, incur raises a validation error.

## Type Inference

Incur infers types from schemas automatically. No manual type annotations are required.

```typescript theme={null}
cli.command('greet', {
  args: z.object({
    name: z.string(),
  }),
  options: z.object({
    loud: z.boolean().default(false),
  }),
  output: z.object({
    message: z.string(),
  }),
  run(c) {
    c.args.name      // inferred as: string
    c.options.loud   // inferred as: boolean
    
    return {
      message: `hello ${c.args.name}`,  // inferred as: string
    }
  },
})
```

Type errors are caught at compile time:

```typescript theme={null}
run(c) {
  return { message: 123 }  // ❌ Type error: expected string
}
```

## Schema Composition

Reuse schemas across commands:

```typescript theme={null}
import { Cli, z } from 'incur'

// Shared schemas
const envArg = z.object({
  env: z.enum(['staging', 'production']).describe('Target environment'),
})

const deployEnv = z.object({
  DEPLOY_TOKEN: z.string(),
})

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

cli
  .command('deploy', {
    args: envArg,
    env: deployEnv,
    run(c) {
      return { deployed: c.args.env }
    },
  })
  .command('rollback', {
    args: envArg,
    env: deployEnv,
    run(c) {
      return { rolledBack: c.args.env }
    },
  })
  .serve()
```

## Schema Extensions

Extend schemas with `.merge()` or `.extend()`:

```typescript theme={null}
const baseOptions = z.object({
  verbose: z.boolean().default(false),
})

const deployOptions = baseOptions.extend({
  force: z.boolean().optional(),
})

cli.command('deploy', {
  options: deployOptions,
  run(c) {
    // c.options.verbose: boolean
    // c.options.force: boolean | undefined
    return { deployed: true }
  },
})
```

## Validation Errors

When validation fails, incur provides detailed field-level errors:

```bash theme={null}
$ my-cli deploy
Error: missing required argument <env>
See below for usage.

my-cli deploy — Deploy to an environment

Usage: my-cli deploy <env>

Arguments:
  env  Target environment
```

In non-TTY mode (agents), errors include machine-readable field details:

```json theme={null}
{
  "ok": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Required",
    "fieldErrors": [
      {
        "path": "env",
        "expected": "string",
        "received": "undefined",
        "message": "Required"
      }
    ]
  }
}
```

## Descriptions

Use `.describe()` to add human-readable descriptions shown in help output:

```typescript theme={null}
args: z.object({
  name: z.string().describe('Name to greet'),
}),
options: z.object({
  loud: z.boolean().default(false).describe('Use uppercase'),
})
```

```bash theme={null}
$ my-cli greet --help

Usage: my-cli greet <name>

Arguments:
  name  Name to greet

Options:
  --loud  Use uppercase
```

## Deprecated Options

Mark options as deprecated with `.meta({ deprecated: true })`:

```typescript theme={null}
options: z.object({
  zone: z.string().optional().describe('Availability zone').meta({ deprecated: true }),
  region: z.string().optional().describe('Target region'),
})
```

Deprecated flags show `[deprecated]` in `--help` and emit a warning when used:

```bash theme={null}
$ my-cli deploy --zone us-east-1
Warning: --zone is deprecated
```

## Next Steps

<CardGroup cols={2}>
  <Card title="Output" icon="terminal" href="/core-concepts/output">
    Learn about output formats and CTAs
  </Card>

  <Card title="Zod Documentation" icon="book" href="https://zod.dev">
    Explore Zod's full schema API
  </Card>
</CardGroup>
