Cheer

CIHex.pmHex DocsLicense

A clap-inspired CLI framework for Elixir. Define your command tree once and get parsing, validation, help, shell completion, REPL mode, and testing for free.

Features

Quick Start

defmodule MyApp.CLI.Greet do
  use Cheer.Command

  command "greet" do
    about "Greet someone"

    argument :name, type: :string, required: true, help: "Who to greet"
    option :loud, type: :boolean, short: :l, help: "SHOUT"
  end

  @impl Cheer.Command
  def run(%{name: name} = args, _raw) do
    greeting = "Hello, #{name}!"
    if args[:loud], do: String.upcase(greeting), else: greeting
  end
end

# Run it
Cheer.run(MyApp.CLI.Greet, ["world", "--loud"], prog: "greet")

Validation

# Per-param: inline function
option :port, type: :integer,
  validate: fn p -> if p in 1024..65535, do: :ok, else: {:error, "invalid port"} end

# Per-param: choices
option :format, type: :string, choices: ["json", "csv", "table"]

# Cross-param: runs after all params are parsed
validate fn args ->
  if args[:tls] && !args[:cert], do: {:error, "--tls requires --cert"}, else: :ok
end

Environment Variable Fallback

option :port, type: :integer, default: 4000, env: "PORT"
# Priority: CLI flag > env var > default

Param Groups

group :format, mutually_exclusive: true do
  option :json, type: :boolean
  option :csv, type: :boolean
end

group :auth, co_occurring: true do
  option :username, type: :string
  option :password, type: :string
end

Lifecycle Hooks

before_run fn args -> Map.put(args, :debug, true) end
after_run fn result -> log(result); result end

# Inherited by ALL child subcommands
persistent_before_run fn args -> Map.put(args, :logger, init_logger()) end

Shell Completion

Cheer.Completion.generate(MyApp.CLI.Root, :bash, prog: "my-app")
# Also :zsh and :fish

REPL Mode

Cheer.Repl.start(MyApp.CLI.Root, prog: "my-app")
# my-app> greet world
# my-app> exit

Testing

result = Cheer.Test.run(MyApp.CLI.Greet, ["world"])
assert result.return == "Hello, world!"
assert result.output == ""

Introspection

Cheer.tree(MyApp.CLI.Root)
# %{name: "my-app", subcommands: [%{name: "greet", ...}, ...]}

Examples

The examples/ directory contains standalone Mix projects you can run and experiment with:

Installation

def deps do
  [{:cheer, "~> 0.1.0"}]
end

License

MIT