A small command router so your CLI reads like the commands themselves — public
functions become commands, their parameters become arguments, and @desc
becomes help. It's the Elixir analog of Ruby's Thor desc/def.
defmodule MyApp.CLI do
use Terminalwire.CLI, name: "my-app"
@desc "Greet someone by name"
def hello(name) do
puts("Hello, #{name}!")
end
@desc "Deploy to an environment"
def deploy(env) do
confirm = gets("Deploy to #{env}? [y/N] ")
if String.trim(confirm) == "y", do: puts("Deploying…"), else: puts("Aborted")
end
endMount it like any handler — use generates run/1:
WebSockAdapter.upgrade(conn, Terminalwire.WebSock, [handler: &MyApp.CLI.run/1], [])Then my-app hello Ada calls hello("Ada"), my-app deploy staging calls
deploy("staging"), and my-app (or my-app help) prints a generated command
list. An unknown command or wrong argument count exits non-zero with a usage hint.
How it dispatches
- The first argument is the command; it's matched to a
@desc-annotated public function with the same name and the same number of remaining arguments. - Functions without a
@descare ordinary helpers, not commands. - A command's return value sets the exit code when it's an integer; otherwise 0.
Talking to the terminal
Inside a command, use Terminalwire.CLI imports terminal helpers bound to the
current session — puts/1, print/1, warn/1, gets/1, read_secret/1,
env/1 — so you write puts("hi") instead of threading a context around. Bare
IO.puts and any standard-IO library (like Owl) also stream to the user, because
the handler's group leader is a Terminalwire IO device. For everything else on the
context (files, directories, the browser, the raw terminal), context/0 returns
the Terminalwire.Server.Context:
@desc "Import a CSV from the user's machine"
def import(path) do
data = Terminalwire.Server.Context.file_read(context(), path)
puts("imported #{byte_size(data)} bytes")
endScope
This is a router, not a full option parser — it handles commands and positional
arguments. For flags, options, and richer parsing, write a plain run/1 handler
and reach for a library like Optimus; the two
approaches use the exact same Context.
Summary
Functions
The current command's Terminalwire.Server.Context (for files, env, browser, the raw terminal).
Read an environment variable from the user's machine (entitlement-gated).
Prompt (optional) and read a line from the user's stdin.
Write to the user's stdout without a trailing newline.
Write a line to the user's stdout.
Prompt (optional) and read a line without echo (passwords).
Write a line to the user's stderr.
Functions
The current command's Terminalwire.Server.Context (for files, env, browser, the raw terminal).
Read an environment variable from the user's machine (entitlement-gated).
Prompt (optional) and read a line from the user's stdin.
Write to the user's stdout without a trailing newline.
Write a line to the user's stdout.
Prompt (optional) and read a line without echo (passwords).
Write a line to the user's stderr.