Building Custom Zsh Completion Functions for Internal CLI Tools
Learn how to implement custom Zsh completion functions using _arguments and _describe to provide intelligent Tab-completion for internal CLI tools.
08 Sept 2026, 02:09 UTC

The Problem: Manual Argument Recall
When developing internal CLI tools, engineers often rely on --help flags or documentation to remember specific argument names and valid values. This context-switching slows down development and increases the likelihood of syntax errors. Zsh provides a programmable completion system that allows you to map your tool's specific flags and values to the Tab key, turning a manual lookup into a discovery process.
Prerequisites
- Zsh installed as the active shell (version 5.0+ recommended).
- A CLI tool already installed and available in your
$PATH. - A dedicated directory for your custom shell scripts (e.g.,
~/.zsh/completions).
Defining the Completion Function
Zsh completions are shell functions that start with an underscore. These functions use the _arguments utility to define the expected flags and the _describe utility to provide a list of valid options with descriptions.
Create a file named _mytool (replacing mytool with your actual command name) in your completions directory. Use the following structure:
# ~/.zsh/completions/_mytool
#compdef mytool
_mytool() {
local -a environments
environments=(
'staging:Deploy to the staging environment'
'production:Deploy to the production environment'
'dev:Deploy to the local development environment'
)
_arguments \
'(-h --help)' {-h,--help}'Show this help message'
'(-v --version)' {-v,--version}'Print version information'
'--env[Specify target environment]:environment:(_describe "environments" environments)' \
'--timeout[Set request timeout in seconds]:numeric'
'*:file:_files'
}
Breakdown of the Configuration
#compdef mytool: This header tells Zsh that this function is intended to handle completions for themytoolcommand._arguments: This utility handles the logic of parsing flags. The syntax'(-h --help)'ensures that if-his used,--helpis not suggested, and vice versa._describe: This is used for the--envflag. It takes a label and an array ofvalue:descriptionpairs, allowing the user to see what each environment represents while tabbing._files: A built-in Zsh helper that suggests existing files in the current directory for the final positional argument.
Integrating the Function into the Shell
Zsh does not automatically scan every directory for completions. You must add your directory to the fpath (function path) array before initializing the completion system.
Add these lines to your .zshrc file:
# Add custom completions directory to fpath
fpath=(~/.zsh/completions $fpath)
# Initialize the completion system
autoload -Uz compinit
compinit
Permission Requirement: Ensure the _mytool file is readable by your user. It does not need to be executable, as it is sourced by the shell.
Verification and Diagnostics
To verify the installation, restart your shell or source your configuration (source ~/.zshrc). Perform the following checks:
- Function Loading: Run
whence -v _mytool. The output should indicate that_mytoolis a shell function. - Trigger Test: Type
mytool --env [TAB]. You should see the list of environments (staging, production, dev) with their descriptions. - Flag Test: Type
mytool -[TAB]to see the available short-form flags.
Troubleshooting Common Issues
| Symptom | Cause | Resolution |
|---|---|---|
Changes to _mytool aren't appearing |
Zsh caches completions in .zcompdump |
Run rm ~/.zcompdump; compinit |
compinit throws security warnings |
Insecure permissions on the completions directory | Run chmod 755 ~/.zsh/completions |
| Tab completion hangs the shell | Circular reference or syntax error in _arguments |
Check for unmatched quotes or missing backslashes in the function definition |
Rollback Procedure
If the custom completions cause shell instability, remove the fpath modification from your .zshrc and delete the cached dump file:
# Remove the fpath line from .zshrc, then run:
rm ~/.zcompdump
exec zsh0 replies
A thoughtful contribution can make all the difference. Be the first to share one.