Skip to the content.

Bash Completion Generator

Generate a bash completion script for any CLI command by discovering its full subcommand tree and options, then building and validating a completion script.

Input

The user provides a command name. The agent must be able to locate and execute the command.

Step 1 — Locate the Command

Find the command binary:

which <command> || find /usr /home -name '<command>' -type f 2>/dev/null | head -10

If the command is not in PATH, use the full path for all subsequent steps. If the command cannot be found at all, stop and inform the user.

Step 2 — Check for Built-in Completion Generator

Many modern CLI tools can generate their own completion scripts. Try these variants in order — if any succeeds, follow its output/instructions and skip to Step 6 (Install):

<command> completion bash
<command> completions bash
<command> --completion bash
<command> --completions bash
<command> generate-completion bash

If none of these work, proceed to manual discovery.

Step 3 — Discover Subcommands and Options

This is a recursive process. Start with the top-level command and work down the subcommand tree.

3a — Top-level Help

Run the command’s help:

<command> --help 2>&1
<command> -h 2>&1

From the output, extract:

  1. Global options — flags and options that apply to the command itself (e.g., --verbose, --version, -h)
  2. Subcommands — named sub-commands listed in the help output
  3. Option arguments — which options take values vs. are boolean flags
  4. Enum values — options with a fixed set of allowed values (e.g., --format table|json|yaml)

3b — Recurse into Each Subcommand

For each discovered subcommand, run:

<command> <subcommand> --help 2>&1
<command> <subcommand> -h 2>&1

Extract the same information: sub-subcommands, options, option arguments, and enum values.

Continue recursing until there are no deeper subcommands. Most CLIs are 2–3 levels deep.

Efficiency tip: Batch multiple subcommand help calls into a single shell invocation to reduce round-trips:

for cmd in sub1 sub2 sub3; do
    echo "========== $cmd =========="
    <command> $cmd --help 2>&1
    echo
done

3c — Record the Full Command Tree

Build a mental model of the complete tree:

<command>
├── global options: --opt1, --opt2 <value>, --flag
├── subcommand1
│   ├── options: --foo, --bar <value>
│   └── sub-subcommand1a
│       └── options: --baz
├── subcommand2
│   └── options: --qux <enum: a|b|c>
└── ...

Step 4 — Build the Completion Script

Create the bash completion script following these conventions:

Structure Template

# bash completion for <command>                -*- shell-script -*-
# Auto-generated from '<command> --help' and subcommand discovery (<command> <version>)

_<command>() {
    local cur prev words cword
    _init_completion || return

    # 1. Find the subcommand position (skip global options and their args)
    local subcmd="" subcmd_idx=0
    local i
    for (( i=1; i < cword; i++ )); do
        case "${words[i]}" in
            <options-that-take-arguments>)
                (( i++ ))  # skip the argument value
                ;;
            <boolean-flags>)
                ;;
            -*)
                ;;
            *)
                subcmd="${words[i]}"
                subcmd_idx=$i
                break
                ;;
        esac
    done

    # 2. If no subcommand yet, complete top-level
    if [[ -z "$subcmd" ]]; then
        # Handle options that take specific values
        case "$prev" in
            <option-with-enum>)
                COMPREPLY=( $(compgen -W '<enum-values>' -- "$cur") )
                return
                ;;
            <option-with-path>)
                _filedir       # or _filedir -d for directories
                return
                ;;
        esac

        if [[ "$cur" == -* ]]; then
            COMPREPLY=( $(compgen -W '<all-global-options>' -- "$cur") )
        else
            COMPREPLY=( $(compgen -W '<all-subcommands>' -- "$cur") )
        fi
        return
    fi

    # 3. Find sub-subcommand if the subcommand has its own subcommands
    local subsub="" subsub_idx=0
    # ... (same pattern as above, starting from subcmd_idx+1)

    # 4. Dispatch per subcommand
    case "$subcmd" in
        <subcommand1>)
            # Handle sub-subcommands if any, else options
            ;;
        <subcommand2>)
            ;;
    esac
} &&
complete -F _<command> <command>

# vim: ft=bash

Key Patterns

Pattern When to Use
compgen -W 'opt1 opt2' Fixed set of completions (subcommands, enum values)
_filedir Option expects a file path
_filedir -d Option expects a directory path
_filedir yaml Option expects files with a specific extension
return (no COMPREPLY) Option expects a free-form value (number, name, ID)
[[ "$cur" == -* ]] Distinguish between completing options vs. positional args

Rules

Step 5 — Validate

Validation has two parts. Both MUST pass before the skill is complete.

5a — Syntax Validation

Source the script and verify the function loads:

bash -c 'source <completion-file> && type _<command> >/dev/null 2>&1 && echo "✓ OK" || echo "✗ FAIL"'

5b — Content Validation

Validate every subcommand and option against actual command output:

# Validate each top-level subcommand exists
for cmd in <all-subcommands>; do
    <command> $cmd --help >/dev/null 2>&1 || <command> $cmd -h >/dev/null 2>&1
    echo "  $cmd: exit $?"
done

# Validate each sub-subcommand exists
for sub in <sub-subcommands>; do
    result=$(<command> <subcommand> $sub 2>&1 | head -1)
    echo "  <subcommand> $sub: $result"
done

Check the validation output for:

If any discrepancies are found, edit the completion script and re-run validation until clean.

Step 6 — Install

Install the completion script to the user’s bash-completion directory:

mkdir -p ~/.local/share/bash-completion/completions
cp <generated-file> ~/.local/share/bash-completion/completions/<command>

Or if the script was written directly to that location, confirm it exists.

The completion will auto-load in new bash sessions (bash-completion lazy-loads from ~/.local/share/bash-completion/completions/).

Step 7 — Report

Summarize to the user:

  1. Where the completion script was installed
  2. How many subcommands and sub-subcommands are covered
  3. Notable completions (enum values, path completions, etc.)
  4. Any subcommands or options that couldn’t be discovered (e.g., dynamic values like IDs that can’t be completed statically)

Notes

Verification

Changelog

See CHANGELOG.md for version history.