CLI Dispatch
A command line is an instruction. A conforming tool does exactly what it was told, refuses what it does not understand, and never guesses its way into fetching, building or running something the user did not name.
The key words MUST, MUST NOT, SHOULD, SHOULD NOT and MAY are to be interpreted as described in BCP 14unchecked (RFC 2119, RFC 8174) when, and only when, they appear in all capitals.
In this page, a conforming tool is a CLI that claims conformance with this standard, and a command token is the word in the position where the tool expects a command or subcommand name.
Requirement ids (CD-n) are stable so tools, reviews and certification can cite them.
An id is never reused or renumbered; a withdrawn rule keeps its id and is marked withdrawn.
Where and how errors are printed is governed by CLI Output; this page governs which input produces an error at all.
Companion pages:
-
CLI Dispatch: rationale explains why each rule exists (non-normative).
-
CLI Dispatch: implementer guidelines shows how to build a conforming dispatcher (non-normative).
-
CLI Dispatch conformance defines levels, the self-test and how to claim conformance.
-
CLI Help defines which help invocations a tool answers, where help comes from (prohelp) and how help uses stdout, stderr and exit codes (CH-1 to CH-24).
-
CLI Styles (draft) defines the strict style this page describes and the contract any other style must meet.
This page is about how a single tool dispatches its own arguments.
It is unrelated to Entrypoint Dispatch in shell-architecture, which covers how a shell refuses or re-executes entrypoints.
|
Unknown commands
- CD-1
-
An unknown command token MUST be an error. A conforming tool MUST:
-
print
unknown command <X>, naming the token it received; -
offer a did-you-mean suggestion when a known command is close (for example by edit distance or prefix);
-
list the valid commands, or point to the exact help command that lists them (CH-24);
-
write all of this to stderr (CO-20);
-
exit with the usage error code
2(CO-18).
-
- CD-2
-
A tool MUST NOT perform any action (network access, file writes, builds, process launches) before it has determined that the command token is known.
No implicit catch-all
- CD-3
-
A bare word MUST NOT be silently treated as a package, file or target to fetch, build or run. Running a package or target MUST require an explicit command (for example
run <pkg>). A command token that does not match a known command is an unknown command (CD-1), never a fallback target. - CD-4
-
Aliases and abbreviations of commands MAY exist only when they are documented and fixed. A tool MUST NOT resolve a token by searching packages, files, plugins or the network for something that happens to share its name.
Bare invocation
- CD-5
-
In the strict style (see CLI Styles), a bare invocation (the tool name with no command) SHOULD print help (or a short usage summary pointing to help) and perform no action (see CH-5).
- CD-6
-
If a tool does have a default action for a bare invocation, that default MUST be documented in its help and reference documentation, and every bare run MUST state which action it ran (for example
no command given; running default: build).--helpMUST still print help without running the default (CH-5). The default action MUST NOT be a catch-all that interprets later words as targets (CD-3 still applies).
Unexpected input
- CD-7
-
Unexpected input MUST be rejected, including by tools that take no arguments. A tool or GUI launcher that accepts no positional arguments or flags MUST exit with an error (usage code
2, message on stderr) when it receives any, instead of silently ignoring them, so users know there is no hidden surface. - CD-8
-
Unknown flags, surplus positional arguments, and missing required arguments MUST be errors under the same rules as CD-1: name the offending input, point to help (CH-18, CH-24), write to stderr, exit
2.
Wrappers and drop-in replacements
- CD-9
-
A wrapper or drop-in replacement MUST forward only the commands it knows the underlying tool implements.
- CD-10
-
For any other command, a wrapper or drop-in replacement MUST NOT forward, guess or fall back. It MUST fail with an error that says this tool does not implement the command and names the tool that does (for example
redub does not implement 'search'; use dub search), on stderr, with exit code2.
Migration for tools with implicit behaviour
- CD-11
-
An existing tool that has implicit behaviour (a catch-all, a silent default, ignored arguments) MUST, in its current major version:
-
warn on every implicit use, on stderr, with the explicit replacement spelled out in full (for example
use: redub run foo); -
offer an opt-in strict mode, by flag, config setting and environment variable, that turns every implicit use into the CD-1 / CD-7 error.
-
- CD-12
-
In the next major version, strict errors MUST become the default. The full changeover is required, not optional: a tool that only warns, or that keeps implicit behaviour as the default past the next major version, does not conform. There is no opt-out: a tool MUST NOT keep implicit behaviour behind a legacy flag, setting or environment variable.
- CD-13
-
The migration warning (CD-11) MUST be a single block on stderr that contains, in this order:
-
the line
warning: '<X>' is not a <tool> command; <what the tool is doing implicitly> is deprecated; -
the line ` use: <the exact explicit command that does the same thing>`, copy-pasteable;
-
the line ` this will be an error in <tool> <next major version>; enable now with <flag> or <ENV>=1`. The warning MUST appear before any action output, MUST NOT be suppressed by
--quiet(CO-10), and MUST appear in the machine-readable result as an item needing attention (CO-12).
-
- CD-14
-
The strict opt-in (CD-11) MUST be spelled consistently: the flag
--strict(or--strict-dispatchif--strictis taken), the environment variable<TOOL>_STRICT=1, and a config key documented next to them. The flag takes precedence over the environment variable, and the environment variable over config. Once strict is the default (CD-12), the same flag MUST remain accepted as a no-op so scripts written during migration keep working. - CD-15
-
The migration MUST follow this timeline:
-
Release W (the first warning release, a minor or patch release of the current major version N): CD-11 warnings and the CD-14 strict opt-in ship together. Release notes MUST announce the change and name the major version in which strict becomes the default.
-
At least one further release in major N SHOULD ship before N+1, so users have a release in which to react to the warnings.
-
Release N+1.0: strict is the default (CD-12). Release notes MUST list every implicit form that is now an error, with its replacement. A tool MUST NOT announce a strict default for a major version and then ship that version without it.
-
Help and documentation
- CD-16
-
<tool> helpand<tool> --helpMUST list every command the dispatcher accepts, including aliases (CD-4) and the bare-invocation default if any (CD-6). A command that dispatches but is missing from help is hidden surface and violates CD-7. Which help invocations MUST work (including<tool> helpas a command), what help contains and where it comes from are governed by CLI Help (CH-1, CH-2, CH-8, CH-15). - CD-17
-
A wrapper or drop-in replacement (CD-9) MUST document which commands it implements itself, which it forwards and to what, and which it rejects, and MUST keep that list in step with the underlying tool versions it supports.
Examples
Unknown command
Non-conforming: the tool treats the typo as a target and acts on it.
$ pkg isntall webapp
Fetching package 'isntall'...
error: package 'isntall' not found in registry
$ echo $?
1
Conforming:
$ pkg isntall webapp
error: unknown command 'isntall'
did you mean: install
valid commands: install, remove, update, list, run, help
see: pkg help
$ echo $?
2
A no-argument tool given an argument
Non-conforming: the argument is silently ignored, and the user cannot tell whether it did anything.
$ settings-gui --reset
(window opens; nothing is reset)
Conforming:
$ settings-gui --reset
error: settings-gui takes no arguments or flags; received '--reset'
see: settings-gui --help
$ echo $?
2
Worked migration: redub
redub is a drop-in replacement for part of dub, the D package manager.
Any first word that is not one of its own commands falls through to its run path, which reads the word as a package name: redub foo behaves as redub run foo, fetching the package foo if it is not the local project.
Its own commands are build, build-universal, update, setup, clean, describe, deps, test, init, install, configure, use and run, plus watch in some builds.
search is not among them.
That list comes from redub’s dispatch table in source/app.d, not from its help: today redub --help lists init, run, build, test, describe and clean, then update, build-universal, watch, install and use as additions, and omits setup, deps and configure (CD-16).
Two more gaps in the same release (redub main at 7cf2af7):
-
redub helpis not a command.helpfalls through to the run path like any other word and is read as a package namedhelp; only--helpprints help. CD-16 requires<tool> helpto list the commands. -
The help text says that with no command redub
will default to "run", but a bareredubruns without saying so. CD-6 requires every bare run to state which action it ran.
This is how dubx search came to fetch a package named search: the word reached redub, whose catch-all read it as a package to run, so a request to search became a fetch of an unrelated package.
No tool in the chain reported an unknown command.
Wrapper: search under redub.
Conforming (CD-10):
$ redub search json
error: redub does not implement 'search'
'search' is implemented by dub: dub search json
redub implements: build, build-universal, update, setup, clean, describe, deps, test, init, install, configure, use, run
see: redub --help
$ echo $?
2
Implicit catch-all: redub foo running a package.
Today (non-conforming):
$ redub foo
(fetches package foo, builds it, runs it)
Current major version, migration release (CD-11, CD-13, CD-14): behaviour unchanged, warning on every use, strict opt-in available.
$ redub foo
warning: 'foo' is not a redub command; running package 'foo' implicitly is deprecated
use: redub run foo
this will be an error in redub 2.0; enable now with --strict or REDUB_STRICT=1
(fetches package foo, builds it, runs it)
$ redub --strict foo
error: unknown command 'foo'
to run a package named foo: redub run foo
see: redub --help
$ echo $?
2
Next major version (CD-12, CD-15): the strict error above is the default for redub foo; redub run foo is the only way to run the package.
(The version number 2.0, the --strict flag and the REDUB_STRICT variable are illustrative; each project names its own.)
For comparison, current dub rejects an unknown first word with Unknown command and exit code 1.
Its one remaining word-level implicit form is a file shortcut: if ./foo or ./foo.d exists, dub foo runs that file as a single-file package (dub run --single), so a local file can shadow the command line.
That is a minor implicit form under CD-11, not a fetch.
Conformance checklist
The full self-test and the conformance levels are on CLI Dispatch conformance.
-
An unknown command prints
unknown command <X>, a did-you-mean suggestion when one is close, and the valid commands or the help command (CD-1). -
Unknown commands go to stderr and exit
2, and nothing is fetched, written, built or run first (CD-1, CD-2). -
No bare word is ever treated as a package, file or target; running one needs an explicit command such as
run <pkg>(CD-3, CD-4). -
A bare invocation shows help, or runs a documented default and says which action it ran (CD-5, CD-6).
-
A tool or GUI launcher that takes no arguments errors on any argument or flag it receives (CD-7).
-
Unknown flags, extra positionals and missing arguments are usage errors with exit
2(CD-8). -
A wrapper or drop-in replacement forwards only commands the underlying tool implements, and names the tool that implements anything else (CD-9, CD-10, CD-17).
-
A tool with implicit behaviour warns on every implicit use in the CD-13 format, and offers
--strict/<TOOL>_STRICT(CD-11, CD-13, CD-14). -
Strict errors are the default by the next major version, on the announced timeline; warnings alone do not conform (CD-12, CD-15).
-
Help lists every accepted command, alias and default (CD-16).
See also CLI Output for where errors are printed and what exit codes mean.