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

# Shell Completions

> Dynamic tab completions for bash, zsh, fish, and nushell

Every incur CLI has a built-in `completions` command that generates shell hook scripts for tab completion. The hook calls back into your binary at every tab press, so completions are always in sync with your commands.

## Installation

Generate and install completions for your shell:

### Bash

```bash theme={null}
eval "$(my-cli completions bash)"
```

Add to `~/.bashrc` for persistence:

```bash theme={null}
echo 'eval "$(my-cli completions bash)"' >> ~/.bashrc
```

### Zsh

```bash theme={null}
eval "$(my-cli completions zsh)"
```

Add to `~/.zshrc` for persistence:

```bash theme={null}
echo 'eval "$(my-cli completions zsh)"' >> ~/.zshrc
```

### Fish

```bash theme={null}
my-cli completions fish | source
```

Add to `~/.config/fish/config.fish` for persistence:

```bash theme={null}
echo 'my-cli completions fish | source' >> ~/.config/fish/config.fish
```

### Nushell

```nu theme={null}
let _incur_complete_my_cli = {|spans|
    COMPLETE=nushell my-cli -- ...$spans | from json
}
```

Add to your nushell config and register as an external completer.

## How It Works

Completions are **dynamic**—the hook calls back into your CLI at every tab press:

```
$ my-cli <TAB>
        ↓
    Shell hook invokes: COMPLETE=bash my-cli -- my-cli
        ↓
    CLI returns: "install\vstatus\vdeploy"
        ↓
    Shell displays: install  status  deploy
```

This means:

* Completions are always accurate
* No generated files to maintain
* Subcommands, options, and enum values are suggested automatically
* Works with mounted CLIs and OpenAPI-generated commands

## Command Completions

Tab completion suggests available commands:

```sh theme={null}
$ my-cli <TAB>
install    # Install a package
status     # Show repo status
deploy     # Deploy to an environment
```

## Subcommand Completions

Command groups suppress trailing space so you can keep tabbing:

```sh theme={null}
$ my-cli pr <TAB>
list       # List pull requests
create     # Create a pull request
merge      # Merge a pull request

$ my-cli pr list <TAB>
--state    # Filter by state
```

## Option Completions

Options are suggested when the current word starts with `-`:

```sh theme={null}
$ my-cli install --<TAB>
--saveDev  # Save as dev dependency
--force    # Force install

$ my-cli install -<TAB>
-D         # Save as dev dependency
-f         # Force install
```

Short aliases are automatically suggested.

## Enum Value Completions

Enum options suggest their valid values:

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

Cli.create('my-cli', { description: 'My CLI' })
  .command('deploy', {
    args: z.object({ 
      env: z.enum(['staging', 'production']) 
    }),
    run(c) {
      return { deployed: c.args.env }
    },
  })
  .serve()
```

```sh theme={null}
$ my-cli deploy <TAB>
staging
production
```

## Boolean Options

Boolean options don't expect values, so completion continues:

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

Cli.create('my-cli', { description: 'My CLI' })
  .command('install', {
    options: z.object({ 
      saveDev: z.boolean().optional(),
      force: z.boolean().optional(),
    }),
    run() {
      return { installed: true }
    },
  })
  .serve()
```

```sh theme={null}
$ my-cli install --saveDev <TAB>
--force    # Next option suggestion
```

## Descriptions in Completions

Descriptions from schemas appear in completions:

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

Cli.create('my-cli', { description: 'My CLI' })
  .command('deploy', {
    description: 'Deploy to an environment',
    args: z.object({ 
      env: z.enum(['staging', 'production']).describe('Target environment') 
    }),
    options: z.object({ 
      force: z.boolean().optional().describe('Force deployment') 
    }),
    run(c) {
      return { deployed: c.args.env }
    },
  })
  .serve()
```

**In zsh and fish**, descriptions appear alongside completions:

```sh theme={null}
$ my-cli <TAB>
deploy -- Deploy to an environment

$ my-cli deploy --<TAB>
--force -- Force deployment
```

## Multi-Command CLI

Completions work with complex command structures:

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

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: [] }
    },
  })

const cli = Cli.create('my-cli', { description: 'My CLI' })
  .command(pr)
  .command('deploy', {
    description: 'Deploy to an environment',
    run() {
      return { deployed: true }
    },
  })

cli.serve()
```

```sh theme={null}
$ my-cli <TAB>
pr         # Pull request commands
deploy     # Deploy to an environment

$ my-cli pr <TAB>
list       # List pull requests

$ my-cli pr list --state <TAB>
open
closed
all
```

## Completion Help

Run `completions --help` for setup instructions:

```sh theme={null}
$ my-cli completions --help
```

**Output**

```
my-cli completions – Generate shell completion script

Usage: my-cli completions <shell>

Arguments:
  shell  Shell to generate completions for (bash, zsh, fish, nushell)

Examples:
  # Bash
  eval "$(my-cli completions bash)"
  echo 'eval "$(my-cli completions bash)"' >> ~/.bashrc

  # Zsh
  eval "$(my-cli completions zsh)"
  echo 'eval "$(my-cli completions zsh)"' >> ~/.zshrc

  # Fish
  my-cli completions fish | source
  echo 'my-cli completions fish | source' >> ~/.config/fish/config.fish
```

## Implementation

Completions are handled automatically. No additional code needed:

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

Cli.create('my-cli', { description: 'My CLI' })
  .command('status', {
    description: 'Show repo status',
    run() {
      return { clean: true }
    },
  })
  .serve()
```

This CLI automatically supports:

* `my-cli completions bash`
* `my-cli completions zsh`
* `my-cli completions fish`
* `my-cli completions nushell`

## Source Code

The completions implementation is in `src/Completions.ts`:

### Register Hook

```ts theme={null}
import { register } from 'incur/Completions'

const hook = register('bash', 'my-cli')
// Returns shell-specific hook script
```

### Complete Function

```ts theme={null}
import { complete, format } from 'incur/Completions'

const candidates = complete(
  commands,      // Command map
  rootCommand,   // Root command definition
  argv,          // Current command line tokens
  cursorIndex,   // Index of word being completed
)

const output = format('bash', candidates)
// Returns shell-specific formatted output
```

## Environment Variables

Completions use these environment variables:

| Variable          | Description                                  |
| ----------------- | -------------------------------------------- |
| `COMPLETE`        | Shell type: `bash`, `zsh`, `fish`, `nushell` |
| `_COMPLETE_INDEX` | Index of the word being completed            |

The shell hook sets these when invoking the CLI.

## Debugging Completions

Test completions manually:

```bash theme={null}
# Bash
COMPLETE=bash _COMPLETE_INDEX=1 my-cli -- my-cli

# Zsh
COMPLETE=zsh _COMPLETE_INDEX=1 my-cli -- my-cli

# Fish
COMPLETE=fish my-cli -- my-cli ''

# Nushell
COMPLETE=nushell my-cli -- my-cli | from json
```

This simulates what the shell does when you press tab.

## Multiple Binaries

If your CLI has multiple binary names (aliases in `package.json`), completions work for all of them:

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

Cli.create('my-cli', {
  description: 'My CLI',
  aliases: ['mc', 'mycli'],
})
  .command('status', {
    run() {
      return { clean: true }
    },
  })
  .serve()
```

```bash theme={null}
# Install completions for all aliases
eval "$(my-cli completions bash)"
eval "$(mc completions bash)"
eval "$(mycli completions bash)"
```

## Advanced: Custom Completions

For advanced use cases, you can use the completions API directly:

```ts theme={null}
import * as Completions from 'incur/Completions'

const candidates: Completions.Candidate[] = [
  { value: 'install', description: 'Install a package' },
  { value: 'status', description: 'Show status', noSpace: true },
]

const output = Completions.format('bash', candidates)
console.log(output)
```

<Tip>Completions are dynamic and always in sync with your commands. Just install once per shell and they'll work forever.</Tip>
