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

# Sub-Commands & Command Groups

> Create nested command hierarchies by mounting separate Cli instances

Sub-commands let you group related commands under a namespace. This creates nested hierarchies like `my-cli pr list` or `docker container ls`.

## When to Use Sub-Commands

Use sub-commands when you need:

* **Logical grouping** - Group related operations (e.g., all PR commands under `pr`)
* **Namespace separation** - Keep command names short without conflicts
* **Modular organization** - Split large CLIs into separate modules
* **Deep hierarchies** - Create multi-level command trees

Examples: `git remote add`, `docker container stop`, `gh pr create`

## Creating Command Groups

<Steps>
  ### Create the root CLI

  Start with a router CLI:

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

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

  ### Create a separate group CLI

  Create another `Cli` instance for the group:

  ```ts theme={null}
  const pr = Cli.create('pr', {
    description: 'Pull request commands',
  })
  ```

  ### Add commands to the group

  ```ts theme={null}
  pr.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 }
    },
  })
  ```

  ### Mount the group

  Mount the group on the root CLI:

  ```ts theme={null}
  cli
    .command(pr)  // Mount the entire pr group
    .serve()
  ```
</Steps>

## Complete Example from README

Here's the complete PR group example from the incur README:

```ts theme={null}
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)  // Link the `pr` group.
  .serve()
```

### Usage

```bash theme={null}
my-cli pr list --state closed
# → prs: (empty)
# → state: closed
```

### Help Text

```bash theme={null}
my-cli --help
# my-cli – My CLI
#
# Usage: my-cli <command>
#
# Commands:
#   pr  Pull request commands
#
# Built-in Commands:
#   completions  Generate shell completion script
#   mcp add      Register as an MCP server
#   skills add   Sync skill files to your agent
#
# Global Options:
#   --format <toon|json|yaml|md|jsonl>  Output format
#   --help                              Show help
#   --llms                              Print LLM-readable manifest
#   --mcp                               Start as MCP stdio server
#   --verbose                           Show full output envelope
#   --version                           Show version
```

```bash theme={null}
my-cli pr --help
# my-cli pr – Pull request commands
#
# Usage: my-cli pr <command>
#
# Commands:
#   list  List pull requests
#
# Global Options:
#   --format <toon|json|yaml|md|jsonl>  Output format
#   --help                              Show help
#   --json                              Shorthand for --format json
#   --verbose                           Show full output envelope
```

## Multiple Commands in a Group

Add multiple commands to create a rich command group:

```ts theme={null}
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'),
      limit: z.coerce.number().default(10).describe('Number of PRs to show'),
    }),
    run(c) {
      return {
        prs: [
          { id: 1, title: 'Fix bug', state: 'open' },
          { id: 2, title: 'Add feature', state: 'open' },
        ],
        state: c.options.state,
        limit: c.options.limit,
      }
    },
  })
  .command('create', {
    description: 'Create a pull request',
    args: z.object({
      title: z.string().describe('PR title'),
    }),
    options: z.object({
      draft: z.boolean().optional().describe('Create as draft'),
      base: z.string().default('main').describe('Base branch'),
    }),
    run(c) {
      return {
        id: 123,
        title: c.args.title,
        draft: c.options.draft ?? false,
        base: c.options.base,
      }
    },
  })
  .command('merge', {
    description: 'Merge a pull request',
    args: z.object({
      number: z.coerce.number().describe('PR number'),
    }),
    options: z.object({
      squash: z.boolean().optional().describe('Squash commits'),
      delete: z.boolean().optional().describe('Delete branch after merge'),
    }),
    run(c) {
      return {
        merged: c.args.number,
        squash: c.options.squash ?? false,
        deleted: c.options.delete ?? false,
      }
    },
  })
```

```bash theme={null}
my-cli pr list --state all --limit 5
# → prs[2]{id,title,state}:
# →   1,Fix bug,open
# →   2,Add feature,open
# → state: all
# → limit: 5

my-cli pr create "Fix parsing bug" --draft --base develop
# → id: 123
# → title: Fix parsing bug
# → draft: true
# → base: develop

my-cli pr merge 42 --squash --delete
# → merged: 42
# → squash: true
# → deleted: true
```

## Multiple Groups

Mount multiple command groups on the root CLI:

```ts theme={null}
const cli = Cli.create('gh', { description: 'GitHub CLI' })

// PR commands
const pr = Cli.create('pr', { description: 'Pull request commands' })
  .command('list', { /* ... */ })
  .command('create', { /* ... */ })

// Issue commands
const issue = Cli.create('issue', { description: 'Issue commands' })
  .command('list', { /* ... */ })
  .command('create', { /* ... */ })
  .command('close', { /* ... */ })

// Repository commands
const repo = Cli.create('repo', { description: 'Repository commands' })
  .command('clone', { /* ... */ })
  .command('fork', { /* ... */ })
  .command('view', { /* ... */ })

cli
  .command(pr)
  .command(issue)
  .command(repo)
  .serve()
```

```bash theme={null}
gh pr list
gh issue create "Bug report"
gh repo clone owner/repo
```

## Nested Command Hierarchies

You can nest groups multiple levels deep:

```ts theme={null}
const cli = Cli.create('docker', { description: 'Docker CLI' })

// Container commands
const container = Cli.create('container', { description: 'Container operations' })
  .command('ls', {
    description: 'List containers',
    run: () => ({ containers: [] }),
  })
  .command('stop', {
    description: 'Stop a container',
    args: z.object({ id: z.string() }),
    run: (c) => ({ stopped: c.args.id }),
  })

// Image commands
const image = Cli.create('image', { description: 'Image operations' })
  .command('ls', {
    description: 'List images',
    run: () => ({ images: [] }),
  })
  .command('build', {
    description: 'Build an image',
    args: z.object({ path: z.string().default('.') }),
    run: (c) => ({ built: c.args.path }),
  })

cli
  .command(container)
  .command(image)
  .serve()
```

```bash theme={null}
docker container ls
docker container stop abc123
docker image build .
```

## Output Policy Inheritance

Groups can set output policies that inherit to all child commands:

```ts theme={null}
const internal = Cli.create('internal', {
  description: 'Internal commands',
  outputPolicy: 'agent-only',  // Suppress output in human/TTY mode
})
  .command('sync', {
    run: () => ({ synced: true }),
  })
  .command('cache-clear', {
    run: () => ({ cleared: true }),
  })

cli.command(internal).serve()
```

When users run `my-cli internal sync` in a terminal, they won't see the output data (but agents will receive it via `--json` or `--format`).

### Override per Command

```ts theme={null}
const internal = Cli.create('internal', {
  description: 'Internal commands',
  outputPolicy: 'agent-only',
})
  .command('sync', {
    run: () => ({ synced: true }),
    // Inherits agent-only
  })
  .command('status', {
    outputPolicy: 'all',  // Override to show output
    run: () => ({ ok: true }),
  })
```

## Middleware Inheritance

Groups can register middleware that runs for all child commands:

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

const cli = Cli.create('my-cli', {
  description: 'My CLI',
  vars: z.object({ user: z.custom<User>() }),
})

// Auth middleware
const requireAuth = middleware((c, next) => {
  if (!c.var.user) {
    return c.error({ code: 'AUTH', message: 'must be logged in' })
  }
  return next()
})

// Protected commands
const admin = Cli.create('admin', { description: 'Admin commands' })
  .use(requireAuth)  // Applies to all admin commands
  .command('deploy', {
    run: () => ({ deployed: true }),
  })
  .command('backup', {
    run: () => ({ backed_up: true }),
  })

cli.command(admin).serve()
```

<Note>
  Middleware runs in order: root CLI → groups (in traversal order) → per-command. Each level's middleware wraps the next.
</Note>

## Best Practices

### Group by Domain

Organize groups around logical domains:

```ts theme={null}
// Good: domain-based groups
const db = Cli.create('db', { description: 'Database commands' })
const cache = Cli.create('cache', { description: 'Cache commands' })
const api = Cli.create('api', { description: 'API commands' })
```

### Keep Groups Focused

Each group should have a clear purpose:

```ts theme={null}
// Good: focused group
const pr = Cli.create('pr', { description: 'Pull request commands' })
  .command('list', { /* ... */ })
  .command('create', { /* ... */ })
  .command('merge', { /* ... */ })

// Avoid: mixing unrelated commands
const misc = Cli.create('misc', { description: 'Miscellaneous' })
  .command('pr-list', { /* ... */ })
  .command('cache-clear', { /* ... */ })
  .command('user-info', { /* ... */ })
```

### Use Consistent Naming

Use consistent verb patterns within groups:

```ts theme={null}
// Good: consistent verbs
const repo = Cli.create('repo', {})
  .command('create', { /* ... */ })
  .command('delete', { /* ... */ })
  .command('list', { /* ... */ })

// Avoid: inconsistent verbs
const repo = Cli.create('repo', {})
  .command('create', { /* ... */ })
  .command('remove', { /* ... */ })  // Use 'delete' instead
  .command('show-all', { /* ... */ }) // Use 'list' instead
```

<Warning>
  Sub-command names must be unique within their parent. Don't create two commands with the same name in the same group.
</Warning>
