You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Dynamic (runtime) completion now works in all six generated shells. Previously
only bash, zsh, and fish shelled out to the __complete callback for a :complete option; PowerShell, nushell, and elvish offered only a static
candidate pool. PowerShell now inspects the token before the cursor, elvish
branches on the previous word, and nushell attaches a per-flag custom
completer — each invoking app __complete KEY <partial>. The generated
scripts were verified against real pwsh, nu, and elvish.
Terminal-aware help: print-app-help, print-command-help, and run-app
now accept :color :auto and :width :auto. :color :auto honors NO_COLOR
and CLICOLOR_FORCE and otherwise enables ANSI styling only when the target
stream is a terminal (isatty on SBCL; environment-only elsewhere); :width :auto reads $COLUMNS. Explicit t / nil / integer values still force the
decision, and the defaults remain nil so existing behavior is unchanged.
option-value-source reports the provenance of an option value — :command-line, :env, :config, or :default (or nil when unset) — the
analogue of clap's ArgMatches::value_source, so a handler can tell an
explicit choice apart from a fallback. invocation-option-sources exposes the
raw map.
Required options are now spelled out in the one-line usage synopsis (for
example Usage: demo run [options] --config <FILE>) instead of being hidden
inside the [options] catch-all; non-required and hidden options stay in the
catch-all.
run-app accepts :usage-exit-code and :error-exit-code to override the
exit codes returned for usage errors (default 64, EX_USAGE) and other
unhandled errors (default 70, EX_SOFTWARE).
Dynamic (runtime) completion: an option or positional may carry a :complete
function (lambda (partial) => candidates). Add make-complete-command (or make-standard-commands :include-dynamic-p t) and the generated bash, zsh, and
fish completions call back into the program (app __complete KEY PARTIAL) at
completion time to offer runtime candidates — no re-parsing of the command
line. A candidate may be a plain value or a (value . description) cons
(emitted tab-separated; fish shows the description). render-complete-reply
exposes the same lookup directly, and option-complete reads the function.
Bash, zsh, and fish completion now complete nested subcommands at every level
of a command tree (app remote <TAB> offers add / remove), with each
command's options — and its ancestors' — completing once that command is on
the line. All three were verified by exercising the generated scripts under the
real shells, not just syntax-checking them.
The built-in completion and docs commands now carry :completion-candidates, so generated shell completion suggests the shell
names (demo completion <TAB>) and documentation formats
(demo docs <TAB>) while the parsers still accept aliases (pwsh, nu, md, roff).
:value-hint (:file / :dir) on make-option (value-bearing) and make-positional: the generated bash, zsh, and fish completions offer file or
directory path completion at the hinted slot (compgen -f/-d, _files/_files -/,
the fish directory completer). Surfaced in help ("expects a file/directory")
and JSON. Reader option-value-hint.
Positional value completion: make-positional now accepts :completion-candidates (like options), and all six shell completers
(bash, zsh, fish, powershell, nushell, elvish) offer a positional's :choices / :completion-candidates values at its argument slot. Positional
candidates also appear in the JSON schema.
Opt-in help description word-wrapping: print-app-help, print-command-help,
and run-app accept :width N to word-wrap option/command/positional
descriptions to column N, with continuation lines aligned under the
description gutter. No terminal-width detection; off by default.
:auto-help on make-app (default t): pass nil to suppress the built-in -h / --help flag for a CLI that manages its own help or forwards --help
to a wrapped tool. A help command added via make-standard-commands is
unaffected. Reader app-auto-help.
Greedy variadic :value-count on a :value option: :+ (one or more) and :* (zero or more) consume every following token up to the next option-like
token (--files a b c), storing the list. :+ requires at least one value.
Help shows <NAME>... and JSON emits "+" / "*".
:see-also, :authors, and :manual-date on make-app: rendered as the SEE
ALSO / AUTHORS sections and the .TH date of render-manpage, and exposed in render-json (the first two). render-manpage also emits standard EXIT STATUS
(0 / 64 / 70) and ENVIRONMENT (env-backed options) sections, and the generated
page passes mandoc -T lint cleanly. Readers app-see-also, app-authors, app-manual-date.
:required-if / :required-unless on make-option: conditional
requirements. :required-if makes an option mandatory when any listed option
is present; :required-unless makes it mandatory unless any listed option is
present. Both signal cli-missing-option-value, are hidden-target safe, and
render in help. Readers option-required-if, option-required-unless.
:require-command on make-app: parsing fails with cli-unknown-command
(listing the available commands) unless a subcommand is dispatched, expressing
a "subcommand mandatory" contract. Reader app-require-command.
Invalid :choices values (for options and positionals) now append a
nearest-match "Did you mean: ...?" suggestion to the error, reusing the same
suggestion machinery as unknown options and commands.
:value-count N on a :value option: consume exactly N following tokens as a
parsed list (--point 1 2 => (1 2)); too few remaining tokens signal cli-missing-option-value, and with :multiple-p each occurrence contributes
its own N-element list. Reader option-value-count.
:kind :key-value: each occurrence parses key=value (a bare key records
value t) and accumulates the pairs into an alist, so -D a=1 -D b=2 reads as (("a" . "1") ("b" . "2")) — the compiler-define / docker-env shape.
inclusive-group: an all-or-none option group (if any member is supplied, all
must be), the complement of exclusive-group; rendered as "all or none of" in
help and signalling cli-missing-dependent-option on a partial set.
:allow-negative-numbers on make-app: keeps a token that looks like a
negative number (-5, -1.5) from being parsed as a short-option cluster, so
it can serve as a positional or option value. Reader app-allow-negative-numbers.
:group on make-option: a help-section label that groups related options
under their own heading in help output (mirroring a command's :group) and is
exposed in JSON. Reader option-help-group.
Response-file expansion: make-app :expand-response-files t expands a @path
argument into the whitespace-separated arguments read from that file before
parsing (recursively; @@ escapes a literal leading @; a missing file is a
usage error), mirroring the gcc/clap convention. Reader app-expand-response-files.
:min-count / :max-count on a rest make-positional: constrain how many
values it collects (too few signals cli-missing-positional, too many cli-unexpected-argument); shown in help and JSON.
:default-command on make-command: a command with :subcommands can name a
default subcommand dispatched when no subcommand token is present, mirroring
the app-level :default-command.
Opt-in colored help: print-app-help, print-command-help, and run-app
accept :color t to wrap headings and names in ANSI styling. There is no
automatic terminal detection, so callers stay in control; off by default.
Nested subcommands: make-command accepts :subcommands (a list of command
specs), so a command dispatches like a mini-app (git remote add). The next
non-option token selects a subcommand; the parent command's options stay
available to the whole subtree and global counters accumulate across the path.
Parsing, help (path-qualified usage plus a subcommand list), the man/Markdown/
JSON renderers, and run-app dispatch all recurse to arbitrary depth. The new invocation-command-path accessor returns the full root-to-leaf chain.
:choices on make-positional: restricts a positional to a closed set,
validated before the parser runs (cli-invalid-positional-value on mismatch)
and shown in help and JSON output, matching option :choices.
:help-footer on make-command: trailing help/epilog prose for a command,
mirroring the app-level :help-footer and falling back to it; reflected in the
man/Markdown/JSON renderers. Reader command-help-footer.
:allow-abbreviated-options on make-app: opt-in GNU-style unambiguous
long-option prefix matching (--verb for --verbose); an ambiguous prefix
signals cli-unknown-option listing the matches. Off by default to preserve
strict exact-match parsing. Reader app-allow-abbreviated-options.
render-elvish-completion and elvish support in render-completion and the completion command, bringing built-in completion coverage to six shells
(bash, zsh, fish, powershell, nushell, elvish).
:config on parse-argv and run-app: an optional plist of option-key -> value that supplies option defaults, slotting into the
precedence chain below CLI arguments and environment variables but above a
literal :default. Values are coerced like defaults (a string runs through
the option parser, a list is spread, a delimited option splits a string), so a
caller can layer in configuration loaded from a file without forking the
parser.
:deprecated on make-option and make-command (t or a reason string):
the entity stays visible in help and completion but is annotated as deprecated
in help, man, Markdown, and JSON output; dispatching a deprecated command
makes run-app print a warning to stderr. Readers option-deprecated and command-deprecated.
:value-delimiter on make-option: a single-character delimiter that makes a :value option split one occurrence into a list (--tags a,b,c => ("a" "b" "c")), parsing each piece (honoring :type) and accumulating across
occurrences. Empty pieces are dropped, and string/list :defaults and
environment values are split the same way.
render-nushell-completion and nushell/nu support in render-completion
and the completion command: a Nushell module with an export extern
covering subcommands and global option flags.
render-json: emits the app's declared spec (options, positionals, commands,
types, ranges, delimiters, defaults) as a machine-readable JSON object for
external tooling. Also available as the json format of the docs command.
Typed values on make-option and make-positional: :type selects a
built-in value parser (:integer, :number, :float, :boolean, or the
default :string), and :min/:max add inclusive bounds for numeric types.
This replaces most hand-written :parser lambdas for common domain
validation. The chosen type and range appear in help metadata, and both
are validated at make-* time (:type and :parser are mutually exclusive,
bounds require a numeric type, and :min must not exceed :max). Numeric
parsing binds *read-eval* off so a value can never execute reader code.
:kind :count on make-option: a repeatable counter option where each
occurrence increments an integer (-vvv => 3, or a repeated --verbose),
defaulting to 0. Ideal for verbosity flags.
render-manpage: generates a section-1 man page (roff) from an app spec,
covering NAME, SYNOPSIS, DESCRIPTION, OPTIONS, ARGUMENTS, COMMANDS, and
EXAMPLES, and honoring hidden options/commands.
make-docs-command and render-docs: a built-in docs [FORMAT] command
(parallel to completion [SHELL]) that prints the generated man page or
Markdown to stdout; make-standard-commands gains :include-docs-p.
render-powershell-completion and powershell/pwsh support in render-completion and the completion command: a Register-ArgumentCompleter -Native script that offers subcommands and
option tokens, narrowing to a subcommand's own options once it appears.
Option readers option-value-type, option-value-min, and option-value-max
for the new typed-value metadata.
:requires-any-of on make-option: declares that at least one of a set of
alternative options must also be supplied (unlike :requires, which demands
all of them). Signals the new cli-missing-any-of-options condition and
renders as requires one of: ... in help. make-app rejects a :requires-any-of whose every alternative conflicts with the option itself,
since such an option could never be validly supplied.
Contributor, support, and release-process documentation.
Changed
Completion renderers (render-completion, render-bash-completion, render-zsh-completion, render-fish-completion) now return the generated
script as a string when called without a stream, mirroring the format
destination contract; passing a stream still writes to it and returns no
values. Previously the no-stream form wrote to *standard-output* and
returned no values, so there was no way to obtain the script as a string
through the public API.
Expanded package metadata for ASDF consumers and package indexes.
Documented the cl-prolog git-dependency install path, since cl-cli is not
yet resolvable through a bare Quicklisp quickload.
Option :requires/:conflicts-with validation now reuses the Prolog
rulebase built once at make-app time instead of rebuilding and re-querying
it on every parse-argv call.
Option resolution, relation validation, and completion metadata now build the
option and target lookup once as a hash table instead of scanning the option
list linearly on every access; the dynamic-completion index and subcommand
lookup are likewise cached per app, and repeated-option value accumulation is
now O(1) per token rather than O(n²).
Fixed
Fish completion never offered top-level command names: they were emitted with
a __fish_seen_subcommand_from <command> guard (true only after the command
was typed) instead of __fish_use_subcommand, so app <TAB> completed
nothing. Confirmed and fixed via behavioral fish -c 'complete -C ...' testing
(syntax-only checks could not catch it).
Generated bash completion never completed a separated option value
(--mode <TAB>): the case "$prev" scan set expect_value / value_source
but nothing turned them into COMPREPLY. The completer now consumes them and
uses _init_completion -s so an attached value (--mode=<TAB>) completes
through the same path. Verified against the real bash-completion library.
Fish completion offered file paths alongside an option's closed :choices
value set; choice/candidate value options are now -f (exclusive) so only the
declared values are suggested.
An option's :value-hint never drove shell completion (only positionals did):
bash now completes files for a plain / :file-hint value option (via complete -o default) and directories for a :dir hint (compgen -d), fish
completes directories for a :dir-hint option, and zsh completes files/dirs
for a :file / :dir-hint option (_files / _files -/).
Generated bash completion never completed a subcommand's options: the root -* fallback returned the global options and stopped before any command case
ran, and the first-word command completion pre-empted app -<TAB>. Both are
now guarded, so subcommand (and nested-subcommand) option completion works.
Found via behavioral testing (sourcing the script and inspecting COMPREPLY).
Generated bash completion emitted an empty case label (a bash syntax error)
for a :value option that had no :choices / :completion-candidates; such
an option now falls through to the shell's default file completion. The bash,
zsh, and fish generators are now checked with real bash -n / zsh -n / fish --no-execute in the test suite's manual verification.
Bash completion never offered attached-value candidates (--option=value)
for command-scoped options.
A short-option cluster silently dropped characters that followed a
mid-cluster :stop-parsing-p flag or boolean instead of surfacing them as
positional input.
An :optional-value option configured with :consume-optional-value-p t
could not accept a separated bare - (the stdin/stdout idiom) as its value.
make-app no longer accepts a positional sequence with a required positional
after an optional one, since such a spec could never assign the later
positional correctly.
make-app now rejects two options whose keys collide (e.g. case-differing
single-character names like -a/-A) instead of letting them silently share
one storage slot.
make-app now rejects a user-declared option that reuses the reserved :help/:version key, which previously could hijack CLI dispatch into the
help/version action.
extract-application-argv/application-argv no longer strip a literal
application argument that happens to match a runtime marker when it appears
after the -- separator.
make-option/make-positional now validate identifier safety before
interning the option/positional key, instead of after.
A relation rulebase cached on a shared, reused command-spec (per the
library's own "reusable command spec" pattern) could be silently overwritten
by a second, unrelated make-app call that spliced in the same command
object, corrupting validation for the first app. The cache now lives per-app,
keyed by command object, instead of being stored on the shared command
struct.
Corrected broken absolute-path documentation links and gave SECURITY.md a
concrete private vulnerability-reporting path.
Generated shell completion now emits candidates through a more robust
protocol: bash builds COMPREPLY as an array with prefix filtering, dynamic
completion reads tab-delimited value<TAB>description records with while read (instead of cut -f1, which mangled values containing tabs),
and PowerShell matches the current word with an ordinal StartsWith.
Security
Rendered output no longer trusts declared spec metadata verbatim: help,
usage, version, and deprecation text, generated shell completions, and
generated man-page / Markdown documentation now strip terminal control
characters (and, for Markdown, escape markup) from option names,
descriptions, and values, so hostile metadata can no longer inject ANSI
escape or other terminal control sequences into a user's terminal. Spec
construction additionally rejects control characters in option value names.