Nushell Command Completion Guide
Nushell 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 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:
- Custom Completions: hand-written completion scripts. See the Nushell community script repository.
- 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:
- Bridge: connects external completion systems, such as Fish and Carapace.
- Normalization: expands aliases back to real commands.
- 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
$ fish --command "complete --do-complete 'git switch or'"origin/HEAD Remote Branchorigin/dev Remote Branchorigin/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: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: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
gitandbun. - 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: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:$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.
Demo
After configuration, typing ssh and pressing Tab automatically lists remote hosts from ~/.ssh/config, and git completion is also fully available:


