sema-cli
0.1.0CLI argument parsing with flags, options, and subcommands
sema-cli
CLI argument parsing with flags, options, and subcommands.
Install
sema pkg add sema-cli
Quick start
(import "sema-cli")
(define spec
(list {:name "host" :short "H" :type :string :default "localhost" :help "Hostname"}
{:name "port" :short "p" :type :int :default 3000 :help "Port number"}
{:name "verbose" :short "v" :type :bool :help "Verbose output"}))
(cli/parse spec (list "--port" "8080" "-v" "server.log"))
; => {:args ("server.log") :host "localhost" :port 8080 :verbose #t}
A small CLI with subcommands
A complete todo script — save as todo.sema and run
sema todo.sema -- add "buy milk" --priority 2 (Sema passes script arguments
after --; cli/args strips the interpreter noise):
(import "sema-cli")
(define commands
(list {:name "add"
:help "Add a todo item"
:spec (list {:name "priority" :short "p" :type :int :default 1 :help "Priority (1-3)"})
:run (fn (opts)
(for-each
(fn (item)
(println (format "added [p~a] ~a" (get opts :priority) item)))
(get opts :args)))}
{:name "list"
:help "List todo items"
:spec (list {:name "all" :short "a" :type :bool :help "Include completed items"})
:run (fn (opts)
(println (if (get opts :all) "listing all items" "listing open items")))}))
(cli/dispatch commands (cli/args)
{:program "todo" :description "A tiny todo manager"})
$ sema todo.sema -- add "buy milk" --priority 2
added [p2] buy milk
$ sema todo.sema -- help
todo — A tiny todo manager
Usage: todo <command> [options]
Commands:
add Add a todo item
list List todo items
Run 'todo help <command>' for command options.
API
| Function | Description |
|---|---|
(cli/args) |
The script's own arguments, without the interpreter/script/-- prefix |
(cli/parse spec args) |
Parse args against a spec; returns a map, positionals under :args |
(cli/help spec program-name description) |
Generate help text from a spec |
(cli/parse! spec args program-name description) |
Parse with auto --help/-h and exit-on-error |
(cli/dispatch commands args opts?) |
Route args to a subcommand and run its handler |
cli/args
(cli/args)
; sema todo.sema -- add x => ("add" "x")
Returns (sys/args) minus the interpreter name, the script path, and the
leading -- separator — i.e. exactly the arguments your script was given.
Use it as the args for cli/parse, cli/parse!, or cli/dispatch.
cli/parse
(cli/parse spec (list "--out" "a.txt" "-v" "--" "-x"))
; => {:args ("-x") :out "a.txt" :verbose #t}
Parses a list of argument strings against a spec (see Spec format).
Returns a map keyed by each option's long :name (as a keyword), with
positional arguments under :args. Raises an error for unknown options,
missing values, bad :int/:float input, and missing {:required #t} options.
cli/help
(cli/help spec "myapp" "A demo application")
Returns the formatted help text: a header, a usage line, and one line per
option showing short/long forms, the value type (<str>, <int>, <float>),
the :help text, and any (required) / (default: ...) markers.
cli/parse!
(define opts (cli/parse! spec (cli/args) "myapp" "A demo application"))
Like cli/parse, but made for script entrypoints: prints help and exits 0
when args contain --help or -h, prints the parse error to stderr and exits
1 on invalid input. Note that -h is reserved — don't give an option the
short name "h" when using cli/parse!.
cli/dispatch
(cli/dispatch commands args)
(cli/dispatch commands args {:program "todo" :description "A tiny todo manager"})
commands is a list of {:name "add" :help "..." :spec [...] :run (fn (opts) ...)}.
The first arg selects the command; the rest are parsed against that command's
:spec with cli/parse and passed to :run. Returns the handler's result.
- No args,
help,--help, or-h→ prints an overview of all commands, returnsnil. help <command>(or--helpafter the command name) → prints that command's option help, returnsnil.- Unknown command → raises an error.
Spec format
Each option is a map:
| Key | Required | Description |
|---|---|---|
:name |
yes | Long option name (e.g. "port"); also the result key |
:short |
no | Single-letter short flag (e.g. "p") |
:type |
no | :string (default), :int, :float, or :bool |
:default |
no | Value used when the option is not given |
:required |
no | #t to error when the option is missing |
:help |
no | Description shown in help text |
A non-bool option without :default and without :required is simply absent
from the result map when not given. Bool options always appear, defaulting
to #f.
Parsing rules
--name valueor--name=valuefor long options-x valuefor short options--flag/-ffor boolean flags (presence =#t)-abcsets the boolean flags-a -b -c; it errors if any letter is not a boolean short flag- Negative numbers (
-5,-2.5) that match no option are positional --stops option parsing; everything after it is positional- Unknown options, missing values, non-numeric
:int/:floatvalues, and missing required options raise errors (cli/parse: ...)
Testing
sema pkg add sema-test # once
sema tests.sema
License
MIT
| Version | Size | Published |
|---|---|---|
| 0.1.0 | 7 KB | 2026-07-07 21:27:55 |