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:
- Subcommands - What commands like
git commit,docker rundo - Options - Flags like
-v,--help,--output - Arguments - What values options accept (files, directories, custom lists)
- Dynamic values - Values generated by running shell commands
When you type a command and press Tab, nosh:
- Finds the completion file for that command
- Determines where you are (main command, subcommand, option)
- 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:
| Completer | What it suggests |
|---|---|
files | Files in current directory |
directories | Directories only |
executables | Programs in PATH |
env_vars | Environment variables |
users | System users |
hosts | Known SSH hosts (from ~/.ssh/known_hosts) |
processes | Running process names |
signals | POSIX 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.toml → git command).
Troubleshooting
"Completions not working for X"
-
Check if a completion file exists:
ls ~/.config/nosh/packages/*/completions/x.toml -
If not, create one or install a package that has it
-
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.