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:

TierSourceExample
1Completion pluginsdocker rm <TAB> → containers
2Tool-native (Cobra)kubectl get po<TAB> → pods
3Aliases & Functionsmyal<TAB>myalias (ƒ)
4Environment variablescd $H<TAB>$HASH_SRC with value preview
5ExecutablesCommands from PATH
6VCS (git / jj)git checkout <TAB> → branches
7Semantic handlersssh <TAB> → known hosts, kill <TAB> → processes
8Filesystem./sr<TAB>./src/
9Agent fallbackComplex 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:

docker rm <TAB>
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:

$ completions generate kubectl
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

~/.config/hash/completions/systemctl.toml
[plugin]
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 list # show registered plugin handlers (built-in and user)
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:

  • idx matches index.js, index.html
  • mnts matches main_test.go
config.toml
[completions]
fuzzy = true

Icons

File type icons (requires Nerd Font):

  • .go → Go gopher
  • .py → Python
  • .js/.ts → JavaScript/TypeScript
  • Directories → folder icon
config.toml
[completions]
file_icons = true # default