babashka.cli
Command-line argument parsing for turning Clojure functions into CLIs
What this skill does
# babashka.cli
Command-line argument parsing library for transforming Clojure functions into CLIs with minimal effort.
## Overview
babashka.cli converts command-line arguments into Clojure data structures, supporting both keyword-style (`:opt value`) and Unix-style (`--opt value`) arguments. Designed to minimize friction when creating CLIs from existing Clojure functions.
**Key Features:**
- Automatic type coercion
- Flexible argument syntax (`:foo` or `--foo`)
- Subcommand dispatch
- Validation and error handling
- Boolean flags and negative flags
- Collection handling for repeated options
- Default values
**Artifact:** `org.babashka/cli`
**Latest Version:** 0.8.60
**License:** MIT
**Repository:** https://github.com/babashka/cli
## Installation
Add to `deps.edn`:
```clojure
{:deps {org.babashka/cli {:mvn/version "0.8.60"}}}
```
Or `bb.edn` for Babashka:
```clojure
{:deps {org.babashka/cli {:mvn/version "0.8.60"}}}
```
Since babashka 0.9.160, babashka.cli is built-in.
## Core Concepts
### Parsing vs. Args Separation
- `parse-opts` - Returns flat map of parsed options
- `parse-args` - Separates into `:opts`, `:cmds`, `:rest-args`
### Open World Assumption
Extra arguments don't cause errors by default. Use `:restrict` for strict validation.
### Coercion Strategy
Values are coerced based on specifications, not inferred from values alone. This ensures predictable type handling.
## API Reference
### Parsing Functions
#### parse-opts
Parse command-line arguments into options map.
```clojure
(require '[babashka.cli :as cli])
;; Basic parsing
(cli/parse-opts ["--port" "8080"])
;;=> {:port "8080"}
;; With coercion
(cli/parse-opts ["--port" "8080"] {:coerce {:port :long}})
;;=> {:port 8080}
;; With aliases
(cli/parse-opts ["-p" "8080"]
{:alias {:p :port}
:coerce {:port :long}})
;;=> {:port 8080}
```
**Options:**
- `:coerce` - Type coercion map (`:boolean`, `:int`, `:long`, `:double`, `:symbol`, `:keyword`)
- `:alias` - Short name to long name mappings
- `:spec` - Structured option specifications
- `:restrict` - Restrict to specified options only
- `:require` - Required option keys
- `:validate` - Validation predicates
- `:exec-args` - Default values
- `:args->opts` - Map positional args to option keys
- `:no-keyword-opts` - Only accept `--foo` style (not `:foo`)
- `:error-fn` - Custom error handler
#### parse-args
Parse arguments with separation of options, commands, and rest args.
```clojure
(cli/parse-args ["--verbose" "deploy" "prod" "--force"]
{:coerce {:verbose :boolean :force :boolean}})
;;=> {:cmds ["deploy" "prod"]
;; :opts {:verbose true :force true}
;; :rest-args []}
```
Returns map with:
- `:opts` - Parsed options
- `:cmds` - Subcommands (non-option arguments)
- `:rest-args` - Arguments after `--`
#### parse-cmds
Extract subcommands from arguments.
```clojure
(cli/parse-cmds ["deploy" "prod" "--force"])
;;=> {:cmds ["deploy" "prod"]
;; :args ["--force"]}
;; Without keyword opts
(cli/parse-cmds ["deploy" ":env" "prod"]
{:no-keyword-opts true})
;;=> {:cmds ["deploy" ":env" "prod"]
;; :args []}
```
### Coercion
#### Type Keywords
- `:boolean` - True/false values
- `:int` - Integer
- `:long` - Long integer
- `:double` - Floating point
- `:symbol` - Clojure symbol
- `:keyword` - Clojure keyword
#### Collection Coercion
Use empty vector to collect multiple values:
```clojure
(cli/parse-opts ["--path" "src" "--path" "test"]
{:coerce {:path []}})
;;=> {:path ["src" "test"]}
```
Typed collections:
```clojure
(cli/parse-opts ["--port" "8080" "--port" "8081"]
{:coerce {:port [:long]}})
;;=> {:port [8080 8081]}
```
#### auto-coerce
Automatic coercion for unspecified options (enabled by default):
```clojure
(cli/parse-opts ["--enabled" "true" "--count" "42" "--mode" ":prod"])
;;=> {:enabled true :count 42 :mode :prod}
```
Converts:
- `"true"`/`"false"` → boolean
- Numeric strings → numbers via `edn/read-string`
- Strings starting with `:` → keywords
### Boolean Flags
```clojure
;; Flag present = true
(cli/parse-opts ["--verbose"])
;;=> {:verbose true}
;; Combined short flags
(cli/parse-opts ["-vvv"])
;;=> {:v true}
;; Negative flags
(cli/parse-opts ["--no-colors"])
;;=> {:colors false}
;; Explicit values
(cli/parse-opts ["--force" "false"]
{:coerce {:force :boolean}})
;;=> {:force false}
```
### Positional Arguments
#### Basic args->opts
Map positional arguments to named options:
```clojure
(cli/parse-opts ["deploy" "production"]
{:args->opts [:action :env]})
;;=> {:action "deploy" :env "production"}
```
#### Variable Length Collections
Use `repeat` for collecting remaining args:
```clojure
(cli/parse-opts ["build" "foo.clj" "bar.clj" "baz.clj"]
{:args->opts (cons :cmd (repeat :files))
:coerce {:files []}})
;;=> {:cmd "build" :files ["foo.clj" "bar.clj" "baz.clj"]}
```
#### Mixed Options and Arguments
```clojure
(cli/parse-opts ["--verbose" "deploy" "prod" "--force"]
{:coerce {:verbose :boolean :force :boolean}
:args->opts [:action :env]})
;;=> {:verbose true :action "deploy" :env "prod" :force true}
```
### Validation
#### Required Options
```clojure
(cli/parse-args ["--name" "app"]
{:require [:name :version]})
;; Throws: Required option: :version
```
#### Restricted Options
```clojure
(cli/parse-args ["--verbose" "--debug"]
{:restrict [:verbose]})
;; Throws: Unknown option: :debug
```
#### Custom Validators
```clojure
(cli/parse-args ["--port" "0"]
{:coerce {:port :long}
:validate {:port pos?}})
;; Throws: Invalid value for option :port: 0
;; With custom message
(cli/parse-args ["--port" "-1"]
{:coerce {:port :long}
:validate {:port {:pred pos?
:ex-msg (fn [{:keys [option value]}]
(str option " must be positive, got: " value))}}})
;; Throws: :port must be positive, got: -1
```
### Default Values
Provide defaults via `:exec-args`:
```clojure
(cli/parse-args ["--port" "9000"]
{:coerce {:port :long}
:exec-args {:port 8080 :host "localhost"}})
;;=> {:opts {:port 9000 :host "localhost"}}
```
### Error Handling
Custom error handler:
```clojure
(defn error-handler [{:keys [type cause msg option]}]
(when (= type :org.babashka/cli)
(println "Error:" msg)
(when option
(println "Option:" option))
(System/exit 1)))
(cli/parse-args ["--invalid"]
{:restrict [:valid]
:error-fn error-handler})
```
Error causes:
- `:restrict` - Unknown option
- `:require` - Missing required option
- `:validate` - Validation failed
- `:coerce` - Type coercion failed
### Subcommand Dispatch
#### dispatch
Route execution based on subcommands:
```clojure
(defn deploy [opts]
(println "Deploying to" (:env opts)))
(defn rollback [opts]
(println "Rolling back" (:version opts)))
(def table
[{:cmds ["deploy"] :fn deploy :args->opts [:env]}
{:cmds ["rollback"] :fn rollback :args->opts [:version]}
{:cmds [] :fn (fn [_] (println "No command specified"))}])
(cli/dispatch table ["deploy" "production"])
;; Prints: Deploying to production
(cli/dispatch table ["rollback" "v1.2.3"])
;; Prints: Rolling back v1.2.3
```
#### Nested Subcommands
```clojure
(def table
[{:cmds ["db" "migrate"] :fn db-migrate}
{:cmds ["db" "rollback"] :fn db-rollback}
{:cmds ["db"] :fn (fn [_] (println "db requires subcommand"))}])
(cli/dispatch table ["db" "migrate" "--env" "prod"])
```
#### Dispatch Options
Pass options to parse-args:
```clojure
(cli/dispatch table args
{:coerce {:port :long}
:exec-args {:host "localhost"}})
```
The `:fn` receives enhanced parse-args result:
- `:dispatch` - Matched command path
- `:args` - Remaining unparsed arguments
- `:opts` - Parsed options
- `:cmds` - Subcommands
### Formatting & Help
#### format-opts
Generate help text from spec:
```clojure
(def spec
{:port {:desc "Port to listen on"
:default 8080
:coerce :long}
:host {:desc "Host address"
:default "localhost"
:alias :h}
:verbose {:desc "EnableRelated in General
modeling-omnistudio-epc-catalog
IncludedSalesforce Industries CME EPC product-modeling skill for Product2-based catalog creation. Use when creating EPC products, configuring product attributes, building offer bundles with Product Child Items, or reviewing EPC DataPack JSON metadata for product catalog changes. TRIGGER when: user creates or updates Product2 EPC records, AttributeAssignment payloads, AttributeMetadata/AttributeDefaultValues, Offer bundles, or ProductChildItem relationships. DO NOT TRIGGER when: designing OmniScripts/FlexCards/Integration Procedures (use building-omnistudio-omniscript, building-omnistudio-flexcard, or building-omnistudio-integration-procedure), implementing Apex business logic (use generating-apex), or troubleshooting deployment pipelines (use deploying-metadata).
relationship-science-coach
IncludedUse this skill for direct, practical adult relationship coaching: couples conflict, repair, trust, marriage, dating, flirting, attachment patterns, emotional connection, sex, desire differences, eroticism, kink negotiation, affection, love languages, breakups, and long-term passion. Draw on Gottman, EFT and Hold Me Tight, attachment science, modern sex research, Perel, Nagoski, Kerner, Schnarch, Love and Stosny, and flexible love-language tools. Be concrete and low-hedge. Redirect only for imminent danger, abuse, coercive control, minors, non-consent, self-harm, stalking, or medical/legal/psychiatric decisions.
building-sf-integrations
IncludedSalesforce integration architecture and runtime plumbing with 120-point scoring. Use this skill to set up Named Credentials, External Credentials, External Services, REST/SOAP callout patterns, Platform Events, and Change Data Capture. TRIGGER when: user sets up Named Credentials, External Services, REST/SOAP callouts, Platform Events, CDC, or touches .namedCredential-meta.xml files. DO NOT TRIGGER when: Connected App/OAuth config (use configuring-connected-apps), Apex-only logic (use generating-apex), or data import/export (use handling-sf-data).
venue-templates
IncludedAccess comprehensive LaTeX templates, formatting requirements, and submission guidelines for major scientific publication venues (Nature, Science, PLOS, IEEE, ACM), academic conferences (NeurIPS, ICML, CVPR, CHI), research posters, and grant proposals (NSF, NIH, DOE, DARPA). This skill should be used when preparing manuscripts for journal submission, conference papers, research posters, or grant proposals and need venue-specific formatting requirements and templates.
let-fate-decide
IncludedDraws the 12 Houses of the Zodiac Tarot spread to inject entropy into planning when prompts are vague, ambiguous, or casually delegated. Interprets the spread to guide next steps. Use when the user says 'let fate decide', 'YOLO', 'whatever', 'idk', or other nonchalant phrases, makes Yu-Gi-Oh references, or when you are about to arbitrarily pick between multiple reasonable approaches. Prefer over ask-questions-if-underspecified when the user's tone is casual or playful rather than precision-seeking.
net-ops
IncludedCross-platform network troubleshooting (Windows, macOS, Linux) via local or remote shell. Use for: DNS broken, can't resolve hostnames, nslookup/dig works but apps fail, NRPT, WFP, scutil, /etc/resolver, systemd-resolved, /etc/resolv.conf, NetworkManager, VPN DNS leak residue (ProtonVPN/Mullvad/WireGuard/AnyConnect), AV/firewall blocking DNS or DoH, Tailscale DNS interaction, intermittent connectivity, remote diagnostics over SSH.