Commands compose. Any command can register child modules as subcommands, producing a tree of arbitrary depth.
Basic nesting
defmodule Devtool.CLI do
use Cheer.Command
command "devtool" do
about "Developer toolkit"
subcommand Devtool.Server
subcommand Devtool.Db
end
end
defmodule Devtool.Server do
use Cheer.Command
command "server" do
about "Server management"
subcommand Devtool.Server.Start
subcommand Devtool.Server.Stop
end
endEach subcommand is a full command module with its own options, arguments, help, and optional children.
Require a subcommand
By default, invoking a parent command with no child shows help. To treat that as an error instead:
command "devtool" do
subcommand_required true
# ...
endAliases
defmodule MyApp.CLI.Checkout do
use Cheer.Command
command "checkout" do
aliases ["co", "ck"]
# ...
end
endmy-app co, my-app ck, and my-app checkout all resolve to the same
module.
Prefix inference
Allow any unambiguous prefix to resolve to a declared subcommand:
command "git" do
infer_subcommands true
subcommand MyApp.CLI.Checkout
subcommand MyApp.CLI.Status
endgit sta -> status
git che -> error: 'che' is ambiguous; candidates: check, checkoutExact matches always win over prefix inference. Aliases are not prefix-matched.
"Did you mean?"
Unknown subcommands produce a typo suggestion:
$ my-app chekout
error: unknown command 'chekout'
Did you mean 'checkout'?
Suggestions are ranked by Jaro distance and gated at 0.7 similarity.
External subcommands (plugins)
Let a command accept unknown subcommand tokens and surface them to
run/2. Enables git-style plugin dispatchers:
command "my-tool" do
external_subcommands true
subcommand MyApp.CLI.Status # declared subs still take precedence
end
@impl Cheer.Command
def run(args, _raw) do
case args[:external_subcommand] do
{name, rest} -> System.cmd("my-tool-#{name}", rest)
nil -> :ok
end
endBehavior:
- Declared subcommands match first.
- First non-option token not matching a declared sub becomes the external subcommand name. Everything after it passes through verbatim, including flags the parent does not know about.
args[:external_subcommand]isnilwhen no external sub was invoked,{name, rest}when one was. Pattern matches are total.
Propagating version
By default only the root's -V / --version prints its version. To share
with children:
command "my-tool" do
version "1.0.0"
propagate_version true
endHelp for a specific subcommand
All of these show the same help:
my-tool server start --help
my-tool server start -h
my-tool help server startSee also
- Lifecycle hooks for
persistent_before_run, which applies to every descendant. - Help and output for
display_orderon subcommands.