﻿---
title: Nushell Command Completion Guide
date: 2025-02-11
excerpt: Configure Carapace and Fish as Nushell external completers, with alias normalization and dispatcher logic for smarter CLI completion.
tags:
  - Terminal
  - Shell
  - Carapace
  - Workflow
  - Nushell
  - CLI
cover: https://assets.vluv.space/cover/ToolChain/carapace.webp
updated: 2026-07-08 08:00:45
lang: en
i18n:
  cn: /nu_completion
  translation: 2
---

[Nushell](https://www.nushell.sh/) is a modern shell built with Rust. Compared with Fish, which is only available on UNIX platforms, Nushell is naturally cross-platform across Windows, Linux, and macOS. Sharing one configuration across systems is very convenient, and the community is active and iterates quickly.

But what is the cost? Fish has a more mature completion ecosystem. For example, it natively supports completion for Claude Code, while Nushell community [support for it](https://github.com/nushell/nu_scripts/tree/main/custom-completions/claude) arrived slightly later.

## I Want It All.webp

The good news is that in Nushell we can implement completion through two approaches, and they coexist perfectly:

1. **Custom Completions**: hand-written completion scripts. See the [Nushell community script repository](https://github.com/nushell/nu_scripts/tree/main/custom-completions).
2. **External Completers**: bridge external completion tools such as Fish, Carapace, and Zoxide.

This post mainly introduces the second approach: reusing Fish's completion capability inside Nushell.

## Overall Architecture

In `completions.nu`, I split the completion system into three modules:

1. **Bridge**: connects external completion systems, such as Fish and Carapace.
2. **Normalization**: expands aliases back to real commands.
3. **Dispatcher**: chooses the most suitable completer according to command type.

### Step 1. Bridge Fish and Carapace

Many mature command-line tools already provide high-quality completions for other shells:

- Fish's `complete --do-complete`
- Carapace's cross-shell completion interface

```sh
$ fish --command "complete --do-complete 'git switch or'"
origin/HEAD    Remote Branch
origin/dev     Remote Branch
origin/main    Remote Branch

$ carapace git nushell git switch ''
[{"value":"switch ","display":"switch","description":"Switch branches","style":{"fg":"blue"}}]
```

With Nushell's **External Completer API**, we can integrate these capabilities into Nushell:

```nu
let fish_completer = {|spans|
    fish --command $"complete '--do-complete=($spans | str replace --all "'" "\\'" | str join ' ')'"
    | from tsv --flexible --noheaders --no-infer
    | rename value description
    | update value {|row|
      let value = $row.value
      let need_quote = ['\' ',' '[' ']' '(' ')' ' ' '\t' "'" '"' "`"] | any {$in in $value}
      if ($need_quote and ($value | path exists)) {
        let expanded_path = if ($value starts-with ~) {$value | path expand --no-symlink} else {$value}
        $'"($expanded_path | str replace --all "\"" "\\\"")"'
      } else {$value}
    }
}

let carapace_completer = {|spans: list<string>|
    CARAPACE_LENIENT=1 carapace $spans.0 nushell ...$spans | from json
}
```

### Step 2. Handle Aliases

Suppose you define:

```nu
alias g = git
```

Most completers do not recognize `g`, so Git completion will not trigger. We can query the current command with `scope aliases`; if it is an alias, expand it back to the real command before passing it to the completer.

For example, `g switch` will be converted to `git switch`, then completion runs. The concrete implementation is in the dispatcher in the next step.

### Step 3. Dispatcher

Completion quality varies by tool and completer:

- Some tools have the most complete completion in Fish, such as `git` and `bun`.
- Other tools are better handled by Carapace.

So I use a **dispatch strategy**: specified commands go through Fish, while the rest go through Carapace.

The dispatcher below implements both Step 2 alias normalization and Step 3 dispatch logic:

```nu
let external_completer = {|spans|
    let expanded_alias = scope aliases
    | where name == $spans.0
    | get -o 0.expansion

    let spans = if $expanded_alias != null {
        $spans
        | skip 1
        | prepend ($expanded_alias | split row ' ' | take 1)
    } else {
        $spans
    }

    match $spans.0 {
        nu | tv | bun | git | rclone => $fish_completer
        _ => $carapace_completer
    } | do $in $spans
}
```

### Step 4. Enable the External Completer

Finally, enable external completion in the Nushell configuration:

```nu
$env.config.completions = {
  case_sensitive: false
  quick: true
  partial: true
  algorithm: "prefix"
  external: {
    enable: true
    completer: $external_completer
  }
  use_ls_colors: true
}
```

You can also further configure `menu` and `keybindings` for a smoother experience. See my configuration repository [Efterklang/dotfiles](https://github.com/Efterklang/dotfiles).

## Demo

After configuration, typing `ssh` and pressing `Tab` automatically lists remote hosts from `~/.ssh/config`, and `git` completion is also fully available:

![nu_completion](https://assets.vluv.space/nu_completion.gif)
