Clojure DSL for declarative command-line interface construction.
Switch branches/tags
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Failed to load latest commit information.


Build Status Coverage Status

A simple DSL for building command-line interfaces with trees of subcommands. This library wraps the org.clojure/tools.cli library for option parsing.


Library releases are published on Clojars.

To use this version with Leiningen, add the following dependency to your project definition:

[mvxcvi/directive "0.4.2"]


The command macro lets you declare a tree of subcommands, each of which can have its own options, arguments, and initialization function. Leaf commands specify an action to take when invoked.

(require '[mvxcvi.directive :refer [command execute]])

(def commands
  ; Commands start with the command name, followed by usage information.
  (command "my-tool [global opts] <command> [command args]"
    ; Second is a longer description string.
    "Command-line tool for my awesome project."

    ; Next are option specs, passed to
    ["--config" "Set path to tool configuration."
     :default my-config/default-path]
    ["-v" "--verbose" "Show extra debugging messages." :default false]
    ["-h" "--help" "Show usage information."]
    ; NOTE: don't use `:default false` for help flags, because it will break
    ; the implicit 'cmd help <args>' handler.

    ; Functions may be given as symbolic references like this or defined inline
    ; as below.
    (init my-config/initialize)

    ; Subcommands are just nested `command` nodes.
    (command "config <type>"
      "Show configuration information."

      (command "dump"
        "Prints out a raw version of the configuration map."

        [nil "--pretty" "Formats the info over multiple lines for easier viewing."]

        ; Leaf commands have actions, functions which run on the option map and
        ; any non-option arguments.
        (action [opts args]
          (if (:pretty opts)
            (pprint opts args)
            (prn opts args)))))

    (command "foo <action> [args]"
      "Low-level commands dealing with foos."

      (init [opts]
        (assoc opts :foo true))

      (action do-foo-action))))

(defn -main [& args]
  (execute commands args))

Initialization functions are given the current option map and must return an updated version of the map in addition to causing any necessary side-effects. Actions take the option map and a vector of the remaining arguments. Either may be specified as a symbol or as an inline function body.


This is free and unencumbered software released into the public domain. See the UNLICENSE file for more information.