Flags accepted by a command. Long form is derived from the atom name (:base_port becomes --base-port); short aliases and every other behavior are opt-in.

Types

option :port,    type: :integer
option :ratio,   type: :float
option :name,    type: :string     # default
option :verbose, type: :boolean    # also supports --no-verbose
option :v,       type: :count      # -vv -> 2

Typed values are coerced before your handler sees them. Invalid values produce an error and never reach run/2.

Short aliases

option :port, type: :integer, short: :p

Accepts -p 4000 or --port 4000.

Defaults and env var fallback

option :port, type: :integer, default: 4000, env: "PORT"

Priority: CLI flag > PORT env var > default.

Choices

option :format, type: :string, choices: ["json", "csv", "table"]

Rejected values produce an error before run/2 runs.

Repeated flags (:multi)

option :tag, type: :string, multi: true
# --tag a --tag b  ->  args[:tag] == ["a", "b"]

Multi-value flags (:num_args)

:num_args collects several values from a single flag invocation into a list. Pass an integer for an exact count or a range for a variable count:

option :point, type: :integer, num_args: 2
# --point 1 2   ->  args[:point] == [1, 2]

option :tags, type: :string, num_args: 1..3
# --tags a b c  ->  args[:tags] == ["a", "b", "c"]

Both the space-separated form (--point 1 2) and the --flag=value form work. Collection stops at the next flag (a token starting with -) or at --, and each value is coerced to the option's :type. A count outside the declared range is a usage error:

--point 1   ->   error: --point expects 2 value(s), got 1

This is distinct from :multi, which repeats the whole flag (--tag a --tag b) rather than consuming multiple tokens after one flag.

Hyphen-leading values (:allow_hyphen_values)

A value that starts with - normally looks like a flag, so it is rejected. Negative numbers are always accepted (--range -5 5). For other hyphen-leading values, set :allow_hyphen_values:

option :pattern, type: :string, allow_hyphen_values: true
# --pattern -v   ->  args[:pattern] == "-v"

option :range, type: :integer, num_args: 2, allow_hyphen_values: true
# --range -a -b  ->  args[:range] == ["-a", "-b"]

A single-value option consumes exactly the next token as its value, so a following flag is still parsed normally (--pattern x --verbose). With :num_args, collection consumes hyphen-leading tokens up to the declared count.

Delimited values (:value_delimiter)

:value_delimiter splits one value on a string into a list:

option :tags, type: :string, value_delimiter: ","
# --tags a,b,c  ->  args[:tags] == ["a", "b", "c"]

option :ids, type: :integer, value_delimiter: ","
# --ids 1,2,3   ->  args[:ids] == [1, 2, 3]

Each element is coerced to the option's :type and validated against :choices. A string :default is split the same way, and it combines with :multi (each occurrence is split and the results flattened). This differs from :num_args, which reads several separate tokens after one flag.

Optional value (:default_missing_value)

:default_missing_value is the value a flag takes when it is present with no value, distinct from :default (the flag absent):

option :color, type: :string, default: "auto", default_missing_value: "always"
# (absent)      ->  args[:color] == "auto"
# --color       ->  args[:color] == "always"
# --color=never ->  args[:color] == "never"

An explicit value must use the --flag=value form. --color never leaves never as a positional argument, so --color still resolves to the missing value.

Custom value parsers (:parse)

:parse transforms a value into a domain type (an atom, a Date, a struct) after :type coercion. See Validation for the full contract:

option :mode, type: :string, parse: fn
  "r" -> {:ok, :read}
  "w" -> {:ok, :write}
  _ -> {:error, "must be r or w"}
end
# --mode r  ->  args[:mode] == :read

Aliases (long-form)

option :color, type: :string, aliases: [:colour]
# --color red and --colour red both set args[:color]

Global options

Propagate an option to every subcommand:

option :verbose, type: :boolean, global: true

Children inherit the option spec but can override by redeclaring.

Hiding options

option :internal, type: :boolean, hide: true

Still accepted by the parser, not shown in help.

Boolean negation

Every boolean option automatically accepts its --no-<name> inverse:

option :color, type: :boolean, default: true

# --color        -> true
# --no-color     -> false

Putting it together

option :port, type: :integer, short: :p,
  default: 4000, env: "PORT",
  validate: fn p -> if p in 1024..65_535, do: :ok, else: {:error, "invalid port"} end,
  help: "Port to listen on"

See also

  • Validation for :validate and cross-param validators.
  • Constraints for :conflicts_with, :requires, :required_if, :required_unless, and groups.
  • Help and output for :help_heading, :display_order, and other presentation controls.