Skip to content



When contributing pull requests, it's a good idea to run basic checks locally:

# install development dependencies
shtab (main)$ pip install pre-commit -e .[dev]
shtab (main)$ pre-commit install  # install pre-commit checks
shtab (main)$ pytest              # run all tests


Most of the magic lives in shtab/

  • shtab/
    • complete() - primary API, calls shell-specific versions
    • complete_bash()
    • complete_zsh()
    • complete_tcsh()
    • ...
    • add_argument_to() - convenience function for library integration
    • Optional(), Required(), Choice() - legacy helpers for advanced completion (e.g. dirs, files, *.txt)
    • get_main_parser() - returns shtab's own parser object
    • main() - shtab's own CLI application

Given that the number of completions a program may need would likely be less than a million, the focus is on readability rather than premature speed optimisations. The generated code itself, on the other had, should be fast.

Helper functions such as replace_format allows use of curly braces {} in string snippets without clashing between Python's str.format and shell parameter expansion.

The generated shell code itself is also meant to be readable.


Tests and deployment are handled automatically by continuous integration. Simply tag a commit v{major}.{minor}.{patch} and wait for a draft release to appear at Tidy up the draft's description before publishing it.

Note that tagging a release is possible by commenting /tag vM.m.p HASH in an issue or PR.