Completion System
Fast, smart, extensible. No shell scripting required.
Multi-tier architecture
Hash tries completers in priority order and the first one that owns the argument wins:
| Tier | Source | Example |
|---|---|---|
| 1 | Completion plugins | docker rm <TAB> → containers |
| 2 | Tool-native (Cobra) | kubectl get po<TAB> → pods |
| 3 | Aliases & Functions | myal<TAB> → myalias (ƒ) |
| 4 | Environment variables | cd $H<TAB> → $HASH_SRC with value preview |
| 5 | Executables | Commands from PATH |
| 6 | VCS (git / jj) | git checkout <TAB> → branches |
| 7 | Semantic handlers | ssh <TAB> → known hosts, kill <TAB> → processes |
| 8 | Filesystem | ./sr<TAB> → ./src/ |
| 9 | Agent fallback | Complex context-aware completions |
A tier that matches the argument owns it, even while its data is still loading. If a plugin source is slow (a cold docker daemon, say), Hash shows a dim fetching completions... notice and opens the menu by itself when the data lands, instead of offering filenames that would be wrong for that argument.
The menu is searchable: keep typing while it is open and the list narrows with each character (using your configured matching mode, prefix or fuzzy); backspace widens it again. Hash's own builtins complete too: builtin names appear in command position labeled hash builtin, and completions <TAB> offers its subcommands.
Completion plugins
Plugins teach the shell where completion candidates for a command come from. A plugin is a small TOML file: it declares a command to run and how to parse its output into completion items. Hash ships with a built-in plugin for docker:
web-server abc123def456 nginx:latest (Up 2 hours)
postgres 789aaa000bbb postgres:16 (Exited (0) 3 days ago)
The docker plugin knows which subcommand wants which list: docker stop completes running containers, docker start completes stopped ones, docker rmi completes images, and so on. Its sources run with no cache, so completion state always reflects the command you just ran.
Generate one with the agent
The fastest way to write a plugin is to let the shell's agent do it:
Inspecting `kubectl --help`...
Asking the agent to draft a completion plugin for kubectl...
[a]ccept [r]evise <what to change> [q]uit: r also complete namespaces
Revise as many times as you like; each round sends your instruction back to the agent. Accepting writes the spec to ~/.config/hash/completions/kubectl.toml and activates it immediately, no restart.
Or write one by hand
name = "systemctl"
commands = ["systemctl"]
[[rules]]
subcommands = ["start", "stop", "restart", "status"]
[rules.source]
exec = ["systemctl", "list-units", "--all", "--plain", "--no-legend"]
value_column = 1
description_column = 4
cache_ttl = "0s" # lists change when you run the command itself
Rules map subcommands to a candidate-producing command; one output line becomes one completion item. Sources run isolated and off the keystroke path: results are cached per command and working directory, slow sources fill in via the fetching notice, and a user spec that declares docker replaces the built-in one. The full spec reference lives in completion-plugins.md.
completions reload # re-read user specs after editing them by hand
Aliases and functions
Hash completes your user-defined aliases and shell functions. These show with a ƒ icon to distinguish them from PATH executables.
- Aliases defined with
alias name='...' - Functions defined with
name() { ... } - Functions from sourced scripts (
~/.hashrc) - Functions imported during shell migration
Alias/function completions work anywhere in the command line, not just at the start. This is useful for commands like xargs or find -exec that take function names as arguments.
File completion details
- Tilde expansion:
~/→ home directory - Hidden files: shown if prefix starts with
. - Directory indicator:
/appended - Preserves
./prefix (e.g.,./src<TAB>→./src/)
Fuzzy matching
When enabled, completion items are fuzzy-filtered by subsequence matching:
idxmatchesindex.js,index.htmlmntsmatchesmain_test.go
fuzzy = true
Icons
File type icons (requires Nerd Font):
.go→ Go gopher.py→ Python.js/.ts→ JavaScript/TypeScript- Directories → folder icon
file_icons = true # default