Run code before or after run/2. Handy for setup, teardown, logging, and sharing state across a command tree.

before_run

Runs after all parsing and validation, just before run/2. Receives the args map, must return an args map.

command "migrate" do
  option :target, type: :string

  before_run fn args ->
    IO.puts("Connecting to database...")
    args
  end
end

Use it to inject derived state into args, open connections, or log start events.

after_run

Runs after run/2 returns. Receives the return value, must return a value (typically the same one).

after_run fn result ->
  IO.puts("Done.")
  result
end

Useful for cleanup, timing, or unconditional logging.

persistent_before_run (inherited)

Like before_run, but inherited by every descendant command. Declared on the root; runs for every subcommand invocation, before each command's own before_run hooks.

command "my-tool" do
  persistent_before_run fn args ->
    Map.put(args, :start_time, System.monotonic_time(:millisecond))
  end

  subcommand MyTool.Server
  subcommand MyTool.Db
end

Every leaf-command handler under my-tool sees args[:start_time].

Multiple hooks

Each hook macro can be called more than once. Hooks run in declaration order.

before_run &add_tracing/1
before_run &add_logger/1
before_run &add_timing/1

Ordering

For a subcommand invocation, the full ordering is:

  1. Every ancestor's persistent_before_run hooks, root-first.
  2. This command's before_run hooks, declaration order.
  3. run/2.
  4. This command's after_run hooks, declaration order.

There is no persistent_after_run -- parent cleanup typically belongs in the root handler or in explicit supervision logic.

See also

  • Subcommands for how persistent hooks flow through nested trees.