Skip to main content

babashkacli

Command-line argument parsing for turning Clojure functions into CLIs

Source facts

Repository
hugoduncan/library-skills
Last source activity
November 14, 2025 at 12:29
Detected SKILL.md language
English
Stars
3
Forks
0

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

File Explorer
4 files

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
babashka.cli
description
Command-line argument parsing for turning Clojure functions into CLIs
# 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 "Enable verbose output" :alias :v}}) (println (cli/format-opts {:spec spec})) ;; Output: ;; --port Port to listen on (default: 8080) ;; --host, -h Host address (default: localhost)
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub