         (cli/parse-opts arguments option-defs)
         errors (if action
                  errors
-                 (cons (format "No such action: %s" (name action-key))
+                 (cons (if action-key
+                         (format "No such action: %s" (name action-key)))
                        errors))]
     (binding [*parse-context* {:action-context action-context
                                :actions actions

                         (s/join "|")
                         (format " <%s>"))))))))

(defn multi-action-context
  "Create a multi-action context with map **action**.  These don't include the
  name of the action on the command line and are typical UNIX like command
  lines (ex: `ls`, `grep`, etc).

  The parameter is **action** is a list of list of symbols takes the form:
```
'((:action-name1 package1 action1)
  ...)
```
  where **action-name** is a key, **package** is the package and **action** is
  the action definition.

Keys
----
* **help-option:** the help action command, which defaults to [[help-option]]
* **version-option:** usually created with [[version-option]]
* **global-actions:** addition global actions in addition to the help and
  version options listed above
* **action-print-order:** an optional sequence of action name keys indicating
  what order to print action help
* **print-help-fn:** an optional function that takes a string argument of the
  generated option help to provide a way to customize the help message
* **usage-format-fn:** generate the usage portion of the help message that
  takes the names of all action if generating multi-action usage or nil for
  single action usage messages"
  [actions &
   {:keys [global-actions help-option version-option
           action-print-order print-help-fn usage-format-fn]
    :or {help-option (help-option)}}]
  (merge (if action-print-order {:action-print-order action-print-order})
         (if print-help-fn {:print-help-fn print-help-fn})
         {:usage-format-fn (or usage-format-fn (create-default-usage-format))}
         {:action-definitions actions
          :global-actions (concat global-actions
                                  (if help-option [help-option])
                                  (if version-option [version-option]))
          :action-mode 'multi}))

(defn single-action-context
  "Create a single action context with map **action**.  These don't include the
  name of the action on the command line and are typical UNIX like command
  lines (ex: `ls`, `grep`, etc).

  The parameter is **action** is a list of symbols that takes the form:
```
'(package action)
```
  where **package** is the package and **action** is the action definition.

  See [[multi-action-context]] for the description of **options**."
  [action & options]
  (-> (cons :single-action action)
      list
      (#(apply multi-action-context % options))
      (merge {:action-mode 'single})))

(defn create-actions [action-context]
  (->> (:action-definitions action-context)
       (map (fn [[key package action-def]]
              (eval (list 'require `(quote [~package])))
              {key (-> (format "%s/%s" package action-def)
                       symbol
                       eval
                       (assoc :name (name key)))}))
       (apply merge)))

(defn- execute-action [key opts args]
  (let [action (:app (get (create-actions) key))]
    (apply action opts args)))

(defn handle-exception
  "Handle exceptions thrown from CLI actions."
  [e]
  (log/debugf "handle exception: <%s>" e)
  (let [msg (.getMessage e)]
    (if *log-error* (log/error e "action line parse error"))
    (if (instance? java.io.FileNotFoundException e)
      (binding [*out* *err*]
        (println (format "%sio error: %s" (program-fmt) msg)))
      (binding [*out* *err*]
        (println (format "%serror: %s"
                         (program-fmt)
                         (if ex-data
                           (.getMessage e)
                           (.toString e)))))))
  (log/debugf "dumping JVM: %s, rethrow: %s"
              *dump-jvm-on-error* *rethrow-error*)
  (if *dump-jvm-on-error*
    (System/exit 1)
    (if *rethrow-error* (throw e))))

(defmacro with-exception
  "Wrap a CLI forms in a `try` and call [[handle-exception]] if an exception
  occurs."
  {:style/indent 0}
  [& forms]
  (let [ex (gensym)]
    `(try
       ~@forms
       (catch Exception ~ex
         (handle-exception ~ex)))))

(defn error-msg
  "Print every element of the sequence **errors**.  If there **errors** is a
  singleton then only print one in the common usage format."
  [errors]
  (if (= (count errors) 1)
    (str (program-fmt) (first errors))
    (str "The following errors occurred while parsing your action:\n\n"
         (s/join \newline errors))))

(defn- action-help
  ([{:keys [name] :as action} skip-action-name?]

(defn- parse-multi [action-context actions arguments]
  (let [action-keys (action-keys action-context)
        action-key (keyword (first arguments))
        action (get actions action-key)
        {:keys [no-action-fn]} action-context]
    (if (and (nil? action) no-action-fn)
      (binding [*parse-context* {:action-context action-context
                                 :actions actions
                                 :argument arguments
                                 :single-action-mode? false}]
        (no-action-fn))
      (parse-single action-context actions action action-key (rest arguments) false))))

(defn- parse-global [action-context actions arguments]
  (let [{:keys [global-actions]} action-context]
