Fast tab completion for the stock macOS bash 3.2 (/bin/bash), written in C.
macOS still ships bash 3.2 from 2007, and most modern completion tools have dropped support for it. This project was built specifically for that bash. All completion logic runs in a single compiled C binary, so pressing TAB doesn't run any shell code. Completion is instant and new terminals open as fast as before.
$ docker run --network <TAB>
bridge host none my_app_default
$ git checkout fe<TAB>
$ git checkout feature/login
$ make in<TAB>
$ make installIt is a lightweight alternative to bash-completion that completes docker, git, kubectl, make,
mvn, npm and more. You don't need Homebrew bash, the bash-completion package or zsh, and
there are no shell scripts. It shines on parameter monsters like curl and wget. Hundreds of
flags are grouped to fit your screen, and TAB on an empty argument shows ready-made command
snippets.
If you use bash on a Mac, you've probably found that completion tools don't support it:
- bash-completion 2.x requires bash 4.2 or newer.
- bash-completion 1.3, the last version that runs on bash 3.2, is unmaintained. It loads every completion script at shell startup, which makes each new terminal slow to open.
- The usual workaround is to
brew install bashand change your login shell.
autocomplete works with the bash already on your Mac:
- Built for macOS bash 3.2. Tested against Apple's
/bin/bashand Apple clang. - Fast because it's all C. Bash registers the binary with
complete -C, so every TAB calls the binary directly. No bash functions, nosed/awkpipelines, no subshells. - Fast shell startup. The init line registers commands and loads nothing else. Specs are read only when you press TAB.
- No dependencies. One binary, plain C, builds with the Xcode Command Line Tools.
Most completion tools help you spell a flag you already know. Snippets help when you don't remember which flags you need. Press TAB right after a command and you get a list of complete, working commands, each with a one-letter key and a short explanation:
$ curl <TAB>
π @d -- curl -d 'name=ann&age=30' https://httpbin.org/post π POST form fields
π @j -- curl --json '{"id":1}' https://httpbin.org/post π POST JSON
π @l -- curl -L -o page.html https://example.org π follow redirects, save to file
π₯ @x -- curl -X DELETE https://httpbin.org/delete π any other method: PUT, PATCH, DELETE
...
$ curl @x<TAB>
$ curl -X DELETE https://httpbin.org/deleteType @ plus the key and press TAB, and the whole command is inserted for you to edit.
π marks the most used ones, which are listed first. When TAB already completes something
else there (files for tar, hosts for ssh), type @<TAB> to see the snippets.
Bundled snippets cover the commands whose flags are hard to remember:
- curl, wget: POST JSON or form data, uploads, headers only, redirects, resuming
- ps, lsof: top CPU/memory processes, who listens on a port, open files of a process
- find, grep: recently changed files, large files, recursive search with excludes
- tar: create, extract, list, exclude folders, strip the top folder
- zip, unzip, unrar: zip a folder without macOS junk, exclude folders, passwords,
split archives,
.appbundles with symlinks, extract into a folder, multi-part RARs; gzip/gunzip, xz/unxz, bzip2/bunzip2, compress/uncompress: keep the original, read without unpacking, all CPU cores for xz,.tar.*via pipes - ssh: port forwarding, jump hosts, SOCKS proxy, remote commands
- jq: pretty-print, pick and filter fields, CSV, shell variables
- jfr: hot methods, GC pauses, allocations from a Java flight recording
- brew: dependency tree (all installed or one formula), what depends on a formula, leaves, unused dependencies, cleanup, Brewfile backup
- docker
system/image/network/container/volume prune,docker compose config - macOS defaults (hidden Finder, Dock and screenshot settings), sips (resize, convert HEIC), pbcopy, pbpaste, say, duti, mdls
- Shell keywords for, while, until, case, if: practical one-liners
(
if git <TAB>still completes git, and so dothen/do/else/elif)
Add your own with a snippets: list in any spec (format).
Other tools don't do this inside TAB completion. bash-completion, zsh and fish complete flags and values only. Separate cheat-sheet tools like navi or tldr show examples, but in their own window or command, not on TAB at the prompt you're already typing on.
- Bundled completions for docker, git, kubectl, make, mvn, java, jfr,
python, node, npx, bun, bunx, npm, yarn, pnpm, pip, go,
cargo, gradle, brew, gh, ssh, scp, terraform, helm, aws, az, gcloud, doctl, flyctl/fly, vercel, pulumi, minikube, kind,
k9s, kubectx, kubens, ansible, ansible-playbook, psql, pg_dump, mysql,
mysqldump, redis-cli, sqlite3, swift, pod, fastlane, mas, ffmpeg, pngquant, bat, fzf, colima, orbctl, pyenv, rbenv, asdf, nvm,
tmux, ps, kill, pkill, killall, lsof, find, grep, rg, sed, awk, patch, diff, tar, zip, unzip, unrar, gzip, gunzip, xz, unxz, bzip2, bunzip2, compress, uncompress, help (bash builtins), man (page names from
manpath), jq, curl, wget, rsync, openssl, dig, nc, tcpdump, code (VS Code), claude (Claude Code), macOS open, launchctl, defaults, diskutil, say, caffeinate, pbcopy, pbpaste, sips, screencapture, duti, mdls, mdfind, pmset, networksetup, security, codesign, xcrun, plutil, hdiutil, spctl, xattr, softwareupdate, tccutil, scutil, mdutil, osascript, shortcuts, dscl, log, system_profiler, ditto, textutil, afplay, qlmanage, profiles, sw_vers, nettop, fs_usage, andautocompleteitself. - Wrapper commands sudo, env, time, xargs complete the command after them with
that command's own completion (
sudo git chec<TAB>βcheckout). - Live values from your environment:
- docker: running containers, images, networks, volumes
- git: branches, tags, remotes, stashes, changed files
- kubectl: namespaces, contexts, pods, nodes, resource types
- npm:
package.jsonscripts and dependencies - mvn: profiles and modules from
pom.xml, plugin goals for plugins the project uses - make: Makefile targets and variables, with descriptions from
## textor the comment above each target (honorsmake -C dirandmake -f file) - killall, pkill: your running process names; kill,
caffeinate -w: PIDs with process names - say: voices with language and sample text (
say -v Ed<TAB>), audio devices - code: installed extensions for
--uninstall-extension/--disable-extension - sips: property keys with types for
-g/-s; screencapture: app bundle ids for-B - duti: extensions with their UTI and current app for
-x; app bundle ids for-s(also by app name:duti -s Preview<TAB>βcom.apple.Preview), then every UTI known to the system asUTI -- .ext β current app(typing an extension finds its UTIs:duti -s com.apple.Preview md<TAB>; when several match they are listed asts1: β¦,ts2: β¦andts2<TAB>picks the second; the same works for-d/-l:duti -d md<TAB>βduti -d net.daringfireball.markdown) (β = declared by the app, β = currently opens with it), read live from LaunchServices viaosascript(duti -s com.apple.Preview com.ad<TAB>βcom.adobe.pdf), then the role - mdls: Spotlight attribute names with descriptions for
-name - ssh, scp: hosts from
~/.ssh/configandknown_hosts(scp gr<TAB>βgrafana:) - brew: installed formulae and services
- gh: pull requests, issues, workflow runs, gists, labels, releases, repos
- yarn, pnpm:
package.jsonscripts, dependencies and workspace packages - pip: installed packages; go: packages and test names; cargo: bins, examples, tests
- terraform: resources from state,
*.tfvars; helm: releases, repo charts, namespaces - node:
package.jsonscripts for--run, test reporters; npx, bunx: binaries innode_modules/.bin - bun: scripts, dependencies (
remove,why,update),node_modules/.binforbun x - python: module names for
-m(python3 -m http<TAB>βhttp.server) - java: source files to run (
java <TAB>:*.javafiles under the cwd declaringmain), module names (-m,--add-modules), jars and class paths - aws: profiles, regions, S3 buckets; az: subscriptions, resource groups, locations; gcloud: projects, configurations, regions, zones, GCS buckets; tmux: sessions and buffers
- open: app names (
-a) and bundle ids (-b); launchctl: services and LaunchAgent plists
- Descriptions next to subcommands and flags when several match
(
git stash <TAB>βpop -- Apply a stash and remove it). - Snippets:
curl <TAB>lists ready-made commands,curl @x<TAB>inserts one. - Handles
--flag=value,image:tag, quoted words and file names with spaces. - Add a command by writing a small YAML file, with no rebuild (how).
- Generate flag completion for system commands like
ps,lsortarfrom their man pages, for other tools from their--helpoutput, and delegate Go/cobra programs (helm, gh, hugo) to their own live completion (details). - Built-in completion for SSH hosts, users, PIDs, process names and signals;
~user/paths. - Complete shell aliases like the command they wrap (
autocomplete alias kubectl k). - Very large specs (over 100 KB) are compiled to a cache, so TAB doesn't re-parse YAML.
- To build from source: the Xcode Command Line Tools (
xcode-select --install) on macOS, or gcc/clang and make on Linux. - bash 3.2 (the macOS
/bin/bash) or any newer bash.
Prebuilt binaries are published for macOS (Apple silicon and Intel) and Linux (x86_64, arm64).
Homebrew (macOS, Linux):
brew install hza/tap/autocomplete
echo 'eval "$(autocomplete bash)"' >> ~/.bashrcInstall script (no sudo; installs to ~/.local and adds the eval line to ~/.bashrc):
curl -fsSL https://github.com/hza/bash-autocomplete/releases/latest/download/install.sh | sh
# or
wget -qO- https://github.com/hza/bash-autocomplete/releases/latest/download/install.sh | shSet PREFIX=/usr/local, PROFILE=~/.bash_profile or VERSION=v0.1.0 for sh to change the
defaults (e.g. ... | PREFIX=$HOME/bin sh). The script verifies the download's SHA-256
checksum. Uninstall with ... | sh -s -- --uninstall (same PREFIX/PROFILE).
git clone https://github.com/hza/bash-autocomplete.git autocomplete
cd autocomplete
make installmake install:
- installs the binary to
/usr/local/bin/autocomplete, - installs the bundled specs to
/usr/local/share/autocomplete/, - precompiles any spec over 100 KB into the cache,
- adds
eval "$(autocomplete bash)"to~/.bashrc, unless it's already there.
Open a new terminal (or run source ~/.bashrc) and press TAB after docker, git or make.
macOS Terminal starts login shells, which read
~/.bash_profile, not~/.bashrc.make installwarns if your~/.bash_profiledoesn't load~/.bashrc; add[ -f ~/.bashrc ] && . ~/.bashrcto it.
Options:
make install PREFIX=$HOME/.local # install without sudo (put ~/.local/bin on PATH first)
make install PROFILE=$HOME/.bash_profile # use a different startup fileIf /usr/local isn't writable, use sudo make install. In that case the cache isn't built
during install; it's built on your first completion instead.
make
eval "$(./autocomplete bash)" # recommended
source /dev/stdin <<<"$(./autocomplete bash)" # if you prefer `source`
source <(autocomplete bash)does not work in bash 3.2. A long-standing bash bug makes it silently read nothing. Use one of the forms above.
make uninstall # use the same PREFIX / PROFILE you installed withThis removes the binary, the bundled specs, their cache files and the exact eval line
from your profile. Your own specs in ~/.config/autocomplete are left untouched.
| Command | Description |
|---|---|
autocomplete bash |
Print the bash init script (use with eval). |
autocomplete bash --man |
Same, plus every command in $PATH that has a man page and no completion yet: its flags are read from the man page on the first TAB and cached (see Cache). |
autocomplete list |
List commands that have a spec, one per line with its description. Your own specs in ~/.config/autocomplete get a tag: [override] (replaces a shipped spec; , unchanged if identical to it, so you can delete it), [added] (no shipped spec), [alias -> CMD], plus generated: man|help|cobra and update: NEW > OLD when the shipped spec is newer. |
autocomplete list --paths |
Same, with the spec file each command uses (handy to see which copy wins, or where a bundled spec lives). |
autocomplete list --user |
Only your own specs in ~/.config/autocomplete (generated with add, aliases, hand-written); combines with --paths. |
autocomplete manage |
Same as list --user (takes --paths/--tsv). |
autocomplete list --tsv |
NAME<TAB>DESCRIPTION lines (no padding), for scripts and exec completers. |
autocomplete check FILE |
Validate a spec and print how it was understood (as JSON). |
autocomplete add [--force] [--from cobra|man|help] CMD... |
Write ~/.config/autocomplete/CMD.yaml from the first source that works: cobra bridge, man page, CMD --help. |
autocomplete edit NAME |
Open ~/.config/autocomplete/NAME.yaml in $VISUAL (else $EDITOR, else vi). An existing copy is opened as is; without one, the shipped spec is copied there first. Warns when the shipped spec has a newer version; validates the file after the editor exits; if you saved no changes, the copy is removed (the shipped spec stays in use). |
autocomplete rm [-f] NAME... |
Delete your spec or alias in ~/.config/autocomplete (and its cache), asking [y/n] for each unless -f; the shipped spec, if any, is used again. |
autocomplete diff [NAME...] |
diff -u of your copies in ~/.config/autocomplete against the shipped specs, so you can merge what you want (default: every override that differs; see [override, update: β¦] in list). Exit 1 if any differ. |
autocomplete alias [--force] CMD NAME... |
Complete NAME (e.g. a shell alias) like CMD by symlinking ~/.config/autocomplete/NAME.yaml to its spec. |
autocomplete alias --rm NAME... |
Remove aliases made by autocomplete alias (only symlinks are removed). |
autocomplete alias --list |
List aliases as NAME -> CMD. |
Install autocomplete (make install) and open a new terminal. You don't need Homebrew
bash, bash-completion or zsh.
No. autocomplete is designed for the bash 3.2 that macOS ships as /bin/bash.
bash-completion is written in bash and sources its scripts into every shell.
autocomplete keeps all logic in a compiled C binary that runs only when you press TAB.
Shell startup does nothing beyond one complete registration.
Yes. Write ~/.config/autocomplete/<command>.yaml describing subcommands, flags and
values. See Adding your own commands.
- Adding your own commands: a quick start for new commands
- Writing specs: spec format, value types, supported YAML, where specs are looked up
- Generating specs:
autocomplete add - Cache: man page specs,
execoutput and other cached results - Troubleshooting: common problems and fixes
- How it works: what happens when you press TAB
make # builds ./autocomplete
make cleanThe code is plain C with no dependencies, in src/: main.c (subcommands), complete.c
(spec walk), output.c (candidate list and display), builtins.c (files, make, $PATH,
exec completers), spec.c (spec lookup and cache), yaml.c (YAML reader and writer),
manparse.c (man page reader) and util.c. Bundled specs live in specs/, grouped into topic subfolders (vcs/, cloud/, lang/, β¦); specs are found at any depth.
MIT. See LICENSE.