CLI Principles
CLI Principles Section 3a Draft

3a. Parameter rules

Source 03a-parameters.md

Back to the spec index

These apply to every CLI and to the router. They keep calls readable in logs and examples.

  1. No single-character aliases. -i for -id is not allowed. Every parameter has exactly one name, and it is a word. A reader of a log line should not need a lookup table.
  2. One canonical spelling per parameter, across all CLIs. Pick one word for a concept and use it everywhere. -output, not -format. -id, not -identifier in one CLI and -key in another. The shared names live in the registry below. A CLI may not accept a synonym alongside the canonical name.
  3. Abbreviations only where obvious. -env is fine. A name that a new reader would have to guess is spelled out. The CLI author judges what is obvious. Once a name is used by more than one CLI it goes in the registry, and the registry spelling is the only one accepted (an abbreviation is not accepted next to its long form, see rule 2).
  4. No implicit behaviour where it affects security. A small set of parameters is sensitive (see below). A sensitive parameter must be passed explicitly on every call: no default, no “current context”, no env var, no config. If it is missing the command fails with exit code 3 and names the parameter. Other parameters, including the ones that choose the target (environment, tenant, organization, project, namespace), follow the normal precedence of section 9: a setting, overridden by an env var, overridden by a parameter.
  5. Prefer the single dash prefix. -name value is the encouraged form, and all docs, examples, -help text, and logs use it. The traditional --name value is discouraged but not forbidden. A CLI may accept it, and the spec does not require it to. This is a strong convention, not a hard rule.
  6. Bare -- marks the boundary between parameters and a body. Everything after it is passed on untouched and is never read as a parameter. Use it for a command to pass through (for example a future run verb: tracker run -id T-12 -- git status). Accepted by every CLI.
  7. Standard input is -stdin. A boolean parameter that says “read the input body from stdin”. A bare - as a value never means stdin. -stdin and -file cannot be given together (input error). A CLI that takes a body documents it in describe.
  8. Names are singular. Commands and parameters use singular nouns: task, not tasks. -field, not -fields. -id, not -ids. To give several values, repeat the parameter (-id A -id B), where describe allows it. This covers command and parameter names. It does not cover keys or text inside output (changes, next, and items are data). Folder and file names are singular too (section 9).

Syntax details

  • Value form: a space between name and value (-name value) is the standard, and all docs, examples, and logs use it. The spec makes no rule about -name=value. A CLI may accept it or not.
  • Names are lowercase, with hyphens between words (-dry-run, -no-input).
  • Boolean parameters are given by name alone (-quiet). There is no -quiet=false. To turn one off, leave it out.
  • Repeated parameters (-id A -id B) are allowed only where describe says so. Otherwise it is an input error.

Sensitive parameters (rule 4)

  • -confirm is always sensitive. It is never taken from config, env, or a default.
  • Account or identity is sensitive on any command that queries or changes security elements: access, policy, roles, permissions, and the like. The caller must say explicitly which account or identity the command acts as or on. [The exact parameter name goes in the registry when the first such CLI is designed.]
  • Nothing else is sensitive by default. A CLI that wants to mark another parameter sensitive says so in describe and explains why.
  • describe marks sensitivity per command, so a permission system and an agent can see it without running the command.

Scope parameters are not sensitive. -env, tenant, organization, project, and namespace are often needed for a command to work at all. They resolve in the usual order (section 9): a setting in config, overridden by an env var, overridden by a parameter. If none gives a value, the command fails with invalid_input, names the parameter, and the fix says how to set it. describe marks these required.

  • Because the value may come from a setting or an env var the caller did not type, a mutation shows the resolved value of every scope parameter in its changes output (section 6), so the human sees where the change will land.

Parameter registry. It starts with the list below and grows as CLIs are designed. A CLI that needs a shared name adds it here in the same change.

Name Meaning Notes
-id exact ID of one resource never a name or wildcard
-output result format text, json, yaml, md. -format is not accepted
-field limit the returned fields repeat for several: -field id -field status
-verbose extra human-readable diagnostics on stderr boolean, no alias, see section 12
-env target environment a scope parameter (setting, env var, or parameter). Canonical spelling is -env, not -environment
-confirm confirm a mutation sensitive, always explicit. Boolean, given by name alone
-dry-run preview a mutation
-if-version precondition on version
-lock-timeout seconds to wait for a lock whole seconds, 0 fails at once
-file read input from a file not with -stdin
-stdin read input body from standard input boolean

Consequences

  • No separate -json shortcut. Use -output json. This follows rule 2.
  • Standard single-letter flags from clig.dev (-h, -q, -v, -f, -n) are not used.
  • Many argument parsers assume --name for long names and do not support single-dash long names. That is accepted. It is a cost to the implementer, who handles it in whatever way suits their language. It is not a reason to drop the single-dash convention.