Building a Custom Autocomplete Function in ZSH

Custom command-line tooling is one of the most effective ways to improve your workflow. Whether you’re automating repetitive tasks, organizing project scripts, or creating a small internal CLI, adding autocomplete makes your tool feel polished, fast, and user-friendly.

In this tutorial, we’ll walk step-by-step through how to build a custom Zsh autocomplete function, using a simple file-lookup command as the foundation. While the example maps logical keys to file paths, the technique is fully generic and can be applied to anything—API commands, Kubernetes actions, internal scripts, or even entire CLI frameworks.

⁉️Why Create a Custom Autocomplete?

Autocomplete dramatically improves usability:

  • Reduces typing effort
  • Avoids memorizing long or cryptic options
  • Prevents mistakes (typos, invalid arguments)
  • Makes simple shell functions feel like full CLI tools

With a small amount of Zsh scripting, you can provide intelligent suggestions for your custom commands.


1. The Base Command

Let’s start with a simple shell function that accepts a keyword as argument and performs an action. In our example, each keyword corresponds to a file path stored in an associative array.

mytool() {
    local -A FILES=(
      [config]="/path/to/project/config.yaml"
      [build]="/path/to/scripts/build.sh"
      [deploy]="/path/to/scripts/deploy.sh"
      [test]="/path/to/scripts/test-suite.sh"
    )

    if [[ -z "$1" ]]; then
        echo "❗ You must specify a parameter. Available keys:"
        for key in "${(@k)FILES}"; do
            echo " - $key${FILES[$key]}"
        done
        return 1
    fi

    local KEY="$1"

    if [[ -z "${FILES[$KEY]}" ]]; then
        echo "❗ Unknown parameter: $KEY"
        return 1
    fi

    local FILE="${FILES[$KEY]}"

    if [[ ! -f "$FILE" ]]; then
        echo "❗ The file '${FILE}' does not exist!"
        return 1
    fi

    pbcopy < "$FILE"
    echo "✅ Success!"
}

What this function does:

  • Defines a mapping of keys → paths
  • Validates the user input
  • Performs a final action (pbcopy is just an example)

The missing piece is autocomplete.

2. Creating the Autocomplete Function

To enable autocomplete, Zsh expects a helper function following a specific naming pattern:

_<commandname>()

So if your command is mytool, the autocomplete handler should be:

_mytool() {
    local -a keys
    keys=(config build deploy test)
    _arguments -C '1:select an action:('"${keys[*]}"')'
}

Key parts explained:

local -a keys

Declares an array of valid suggestions.

keys=(...)

Populates the array with valid autocomplete values.
These must match the keys defined inside mytool().

_arguments -C '1:...:()'

Tells Zsh that:

  • This autocomplete applies to the first argument (1:)
  • The description text appears in tab-completion menus
  • The list inside parentheses defines the allowed values

3. Registering the Autocomplete

Finally, tell Zsh that autocomplete for mytool should be handled by _mytool:

compdef _mytool mytool

Once this line is sourced, pressing TAB after typing:

mytool <TAB>

will display the list of keys.

4. Installing the Script

Save the entire script in a file, for example:

~/.zsh/mytool-autocomplete.zsh

Then source it from your .zshrc:

source ~/.zsh/mytool-autocomplete.zsh

Refresh your shell:

source ~/.zshrc

🎉 Now your autocomplete is live!


5. Making It Generic

This autocomplete pattern can be adapted to any workflow:

✅ Script runners

Map keywords to CI scripts, build scripts, or test scripts.

✅ Kubernetes helpers

Autocomplete namespace names, deployments, or contexts.

✅ API clients

Offer subcommands like listcreateupdate, etc.

✅ File shortcuts

Provide quick access to templates, configuration files, or documentation.

You can even generate the list dynamically—reading from directories, Git branches, or JSON files.

6. Tips for Improving Your CLI

Here are some enhancements you might want to add:

  • Dynamic key generation using filesystem scanning
  • Multi-argument autocomplete
  • Option flags (--force--verbose, …)
  • Command help using --help
  • Colorized output for clarity
  • Logging for debugging

Zsh’s completion system is extremely powerful, and this tutorial only scratches the surface.


Conclusion

By combining a simple shell function with a custom Zsh completion handler, you can create lightweight but highly ergonomic CLI utilities. These tools help streamline your workflow, reduce errors, and feel much more professional.

Adding autocomplete is not only easy—it’s one of the highest-impact improvements you can make for any internal script or tooling.

If you’re building your own developer utilities, this is a great feature to start with.

 

Alberto Pasca

Software engineer @ Pirelli & C. S.p.A. with a strong passion for mobile  development, security, and connected things.