Shell Completion¶
Static completion scripts¶
Generate a static completion script directly from the app definition:
Shell-specific renderers are also available:
(cl-cli:render-bash-completion *app*)
(cl-cli:render-zsh-completion *app*)
(cl-cli:render-fish-completion *app*)
Like format, these renderers take an optional stream: called with no stream
they return the script as a string, and called with a stream they write to it
and return no values. Pass an explicit stream to avoid building an
intermediate string:
If you want a standard built-in completion subcommand, splice
cl-cli:make-standard-commands into the app's commands with
:include-completion-p t (it also returns help and version by default;
version prints just the app name when the app has no version string):
(setf *app*
(cl-cli:make-app
:name "demo"
:commands (append
(cl-cli:make-standard-commands
:include-completion-p t)
(list ...))))
Then users can install completion with:
For Zsh and Fish:
autoload -U compinit && compinit
source <($(command -v demo) completion zsh)
source <($(command -v demo) completion fish)
Current built-in support is bash, zsh, fish, powershell (alias
pwsh), nushell (alias nu), and elvish. Generated completion offers
command names, their options, and positional values from a positional's
:choices or :completion-candidates. The bash, zsh, and fish completers
descend the full nested-subcommand tree (app remote add, with each level's
accumulated option scope); the remaining shells complete the top command level
(PowerShell additionally narrows options to a top-level subcommand, as below). Hidden commands and hidden options are omitted from the generated
script. Aliases are included in generated completion candidates. Options
declared with :choices also feed shell value completion candidates.
The PowerShell renderer emits a Register-ArgumentCompleter -Native script
block that suggests subcommands and option tokens, switching to a
subcommand's own options once that subcommand appears on the line; the
Nushell renderer emits an export extern module covering subcommands and
global option flags:
Curated candidates independent of validation¶
When a CLI accepts free-form values but shell completion should still suggest
a curated set, keep validation open and attach :completion-candidates
separately. Candidates may be plain values or (value . description) pairs:
(cl-cli:make-option :name "profile"
:kind :value
:completion-candidates '(("dev" . "Local development")
("prod" . "Production release"))
:description "Runtime profile.")
Use :completion-candidates when shell suggestions should be broader or
differently documented than parser validation — :choices instead enforces
that the same set is the only thing the parser accepts (see Option Values
and Kinds).
Dynamic completion¶
For candidates that are only known at runtime (branch names, hostnames,
records in a database), attach a :complete function to an option and add the
hidden callback command with make-standard-commands :include-dynamic-p t (or
make-complete-command):
(cl-cli:make-app
:name "demo"
:global-options (list (cl-cli:make-option
:name "branch"
:kind :value
:complete (lambda (partial)
(remove-if-not
(lambda (b) (eql 0 (search partial b)))
(list-git-branches)))))
:commands (cl-cli:make-standard-commands :include-dynamic-p t))
All six generated completions — bash, zsh, fish, PowerShell, nushell, and
elvish — then call demo __complete branch <partial> at completion time and
offer whatever the function prints:
- bash, zsh, PowerShell, and elvish match the token before the cursor against the app's dynamic options, then shell out
- fish attaches the callback per option, as a
complete -a '(command ...)'expression - nushell attaches a per-flag custom completer
Because the shell already knows which slot it is completing, the callback only receives the option key and the partial word — the full command line is never re-parsed.
nushell passes no partial word
The nushell completer invokes demo __complete branch without the
partial and filters the returned list itself, which is how nushell's
custom completers are meant to work. Your :complete function therefore
receives an empty prefix under nushell. That is fine for a bounded list,
but a callback that uses the prefix to limit an expensive lookup should
be able to cope with being asked for everything. A candidate may be a plain string or a (value . description)
cons; descriptions are emitted tab-separated and shown by shells that support them.
A positional may also declare :complete, and __complete resolves positional
keys, but no generated script wires a positional slot to the callback — only
options are.
cl-cli:render-complete-reply performs the same lookup directly if you wire
the callback yourself instead of using make-complete-command.