noshdocs

Completions

Tab completion for commands

Completions

Completions give you tab completion for commands. When you press Tab, nosh suggests subcommands, options, and arguments based on completion definitions.

How Completions Work

Unlike other shells that use scripts for completions, nosh uses TOML files. Each file defines:

  1. Subcommands - What commands like git commit, docker run do
  2. Options - Flags like -v, --help, --output
  3. Arguments - What values options accept (files, directories, custom lists)
  4. Dynamic values - Values generated by running shell commands

When you type a command and press Tab, nosh:

  1. Finds the completion file for that command
  2. Determines where you are (main command, subcommand, option)
  3. Shows appropriate suggestions

Built-in Completions

nosh ships with completions for common commands:

~/.config/nosh/packages/builtins/completions/
├── git.toml
├── cargo.toml
├── npm.toml
└── docker.toml

These load automatically. Try it:

git <Tab>        # Shows: add, commit, push, pull, ...
git commit -<Tab>  # Shows: -m, -a, --amend, ...
cargo <Tab>      # Shows: build, run, test, ...

Creating Your Own Completion

Step 1: Create the file

Create a file named after the command in a package's completions directory:

# For a command called "mycli"
~/.config/nosh/packages/my-completions/completions/mycli.toml

Or add to an existing package you've created.

Step 2: Define the completion

Here's a simple example for a CLI tool with start, stop, and status commands:

[completions.mycli]
description = "My CLI tool"

[completions.mycli.subcommands]
start = "Start the service"
stop = "Stop the service"
status = "Show current status"

[completions.mycli.options]
"-h" = "Show help"
"--help" = "Show help"
"-v" = "Verbose output"
"--config" = { description = "Config file path", takes_value = true, value_completer = "files" }

Now when you type mycli <Tab>, you'll see start, stop, status as options.

Step 3: Test it

mycli <Tab>         # Shows: start, stop, status
mycli --<Tab>       # Shows: --help, --config
mycli --config <Tab>  # Shows files in current directory

Completion Format Reference

Basic Structure

[completions.commandname]
description = "What the command does"

[completions.commandname.subcommands]
# Subcommands go here

[completions.commandname.options]
# Options/flags go here

[completions.commandname.dynamic]
# Dynamic completers go here

Simple Subcommands

Just map subcommand names to descriptions:

[completions.docker.subcommands]
run = "Run a command in a new container"
build = "Build an image from a Dockerfile"
pull = "Pull an image from a registry"
push = "Push an image to a registry"
ps = "List containers"
images = "List images"

Subcommands with Their Own Options

Subcommands can have nested options and arguments:

[completions.git.subcommands.commit]
description = "Record changes to the repository"
positional = "files"   # What <Tab> completes after options
options = [
    { name = "-m", description = "Commit message", takes_value = true },
    { name = "-a", description = "Stage all modified files" },
    { name = "--amend", description = "Amend the previous commit" },
    { name = "--no-edit", description = "Use previous commit message" },
]

Options

Options come in several forms:

[completions.mycommand.options]
# Simple flag (no value)
"-v" = "Verbose output"
"--verbose" = "Verbose output"

# Flag that takes a value
"-o" = { description = "Output file", takes_value = true }

# Flag with specific completer for its value
"--output" = { description = "Output directory", takes_value = true, value_completer = "directories" }

# Flag with custom choices
"--format" = { description = "Output format", takes_value = true, choices = ["json", "yaml", "toml"] }

Positional Arguments

Define what Tab completes for arguments (not options):

[completions.cat]
description = "Concatenate and print files"
positional = "files"   # Tab completes file names

[completions.cd]
description = "Change directory"
positional = "directories"   # Tab completes directory names

Built-in Completers

These are available for value_completer and positional:

CompleterWhat it suggests
filesFiles in current directory
directoriesDirectories only
executablesPrograms in PATH
env_varsEnvironment variables
usersSystem users
hostsKnown SSH hosts (from ~/.ssh/known_hosts)
processesRunning process names
signalsPOSIX signals (SIGTERM, SIGKILL, etc.)

Example usage:

[completions.kill.options]
"-s" = { description = "Signal to send", takes_value = true, value_completer = "signals" }

[completions.kill]
positional = "processes"

Dynamic Completers

For values that come from running commands, use dynamic completers:

[completions.git.dynamic]
branches = { command = "git branch --format='%(refname:short)' 2>/dev/null" }
remotes = { command = "git remote 2>/dev/null" }
tags = { command = "git tag 2>/dev/null" }

[completions.git.subcommands.checkout]
description = "Switch branches"
positional = "branches"   # Uses the dynamic completer

[completions.git.subcommands.push]
description = "Push to remote"
positional = "remotes"

Caching

Dynamic completers cache results for 5 seconds by default. For slow commands, increase the cache time:

[completions.kubectl.dynamic]
pods = { command = "kubectl get pods -o name 2>/dev/null", cache_seconds = 30 }
namespaces = { command = "kubectl get namespaces -o name 2>/dev/null", cache_seconds = 60 }

Complete Example

Here's a full completion for a hypothetical myapp CLI:

[completions.myapp]
description = "My application CLI"

# Top-level subcommands
[completions.myapp.subcommands]
serve = "Start the server"
deploy = "Deploy to production"
config = "Manage configuration"
db = "Database operations"

# Top-level options (apply to all subcommands)
[completions.myapp.options]
"-h" = "Show help"
"--help" = "Show help"
"--version" = "Show version"
"-v" = "Verbose output"

# Dynamic completers
[completions.myapp.dynamic]
environments = { command = "myapp config list-envs 2>/dev/null" }
db_tables = { command = "myapp db tables 2>/dev/null", cache_seconds = 30 }

# Subcommand: serve
[completions.myapp.subcommands.serve]
description = "Start the development server"
options = [
    { name = "-p", description = "Port number", takes_value = true },
    { name = "--port", description = "Port number", takes_value = true },
    { name = "--host", description = "Host to bind", takes_value = true },
]

# Subcommand: deploy
[completions.myapp.subcommands.deploy]
description = "Deploy to an environment"
positional = "environments"   # Uses dynamic completer
options = [
    { name = "--dry-run", description = "Show what would be deployed" },
    { name = "-f", description = "Skip confirmation" },
]

# Subcommand: db
[completions.myapp.subcommands.db]
description = "Database operations"
subcommands = { migrate = "Run migrations", seed = "Seed the database", reset = "Reset database" }

# Nested subcommand: db migrate
[completions.myapp.subcommands.db.subcommands.migrate]
description = "Run database migrations"
options = [
    { name = "--step", description = "Number of migrations", takes_value = true },
]

Converting ZSH Completions

If you have ZSH completion files, convert them to TOML:

/convert-zsh /usr/share/zsh/functions/Completion/Unix/_git

This creates a TOML completion from the ZSH definition. Review and edit the result—automatic conversion isn't perfect but gives you a starting point.

Installing Completion Packages

Get completions made by others:

/install user/completions-pack

After installation, completions work automatically for any commands defined in the package.

Updating Completions

/sync

This updates both built-in completions and any packages you've installed.

Where Completions Live

~/.config/nosh/packages/
├── builtins/                # Built-in completions
│   └── completions/
│       ├── git.toml
│       ├── cargo.toml
│       └── ...
├── my-completions/          # Your own package
│   └── completions/
│       └── mycli.toml
└── awesome-completions/     # Installed package
    └── completions/
        └── kubectl.toml

Completions are discovered automatically from all packages. The file name determines which command it provides completions for (git.tomlgit command).

Troubleshooting

"Completions not working for X"

  1. Check if a completion file exists:

    ls ~/.config/nosh/packages/*/completions/x.toml
  2. If not, create one or install a package that has it

  3. Make sure the file name matches the command name exactly

"Dynamic completer is slow"

Increase the cache time:

[completions.mycommand.dynamic]
slow_thing = { command = "slow-command", cache_seconds = 60 }

"Completions are outdated"

Run /sync to get the latest built-in completions.