@babashka/cli 0.12.75

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.html ADDED
@@ -0,0 +1,1229 @@
1
+ <?xml version="1.0" encoding="UTF-8" ?>
2
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
3
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
4
+
5
+ <html xmlns="http://www.w3.org/1999/xhtml">
6
+
7
+ <head>
8
+ <title>README.html</title>
9
+ <meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
10
+
11
+ </head>
12
+
13
+ <body>
14
+
15
+ <h1 id="babashka.cli">babashka.cli</h1>
16
+ <p><a href="https://clojars.org/org.babashka/cli"><img
17
+ src="https://img.shields.io/clojars/v/org.babashka/cli.svg"
18
+ alt="Clojars Project" /></a> <a
19
+ href="https://book.babashka.org#badges"><img
20
+ src="https://raw.githubusercontent.com/babashka/babashka/master/logo/built-in-badge.svg"
21
+ alt="bb built-in" /></a></p>
22
+ <p>Turn Clojure functions into CLIs! This library can be used from: - <a
23
+ href="https://github.com/babashka/babashka">babashka</a> - included as a
24
+ built-in library - <a
25
+ href="https://www.clojure.org/guides/install_clojure">Clojure on the
26
+ JVM</a> - we support Clojure 1.10.3 and above on Java 11 and above - <a
27
+ href="https://clojurescript.org">ClojureScript</a> - we test against the
28
+ current release</p>
29
+ <h2 id="api"><a href="API.md">API</a></h2>
30
+ <h2 id="status">Status</h2>
31
+ <p>This library is still in design phase and may still undergo breaking
32
+ changes. Check <a href="CHANGELOG.md#breaking-changes">breaking
33
+ changes</a> before upgrading!</p>
34
+ <h2 id="installation">Installation</h2>
35
+ <p>Add to your <code>deps.edn</code> or <code>bb.edn</code>
36
+ <code>:deps</code> entry:</p>
37
+ <div class="sourceCode" id="cb1"><pre
38
+ class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a>org.babashka/cli {<span class="at">:mvn/version</span> <span class="st">&quot;&lt;latest-version&gt;&quot;</span>}</span></code></pre></div>
39
+ <h2 id="intro">Intro</h2>
40
+ <p>Turn a Clojure function into a CLI that takes Unix-style command line
41
+ arguments:</p>
42
+ <div class="sourceCode" id="cb2"><pre
43
+ class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a>$ cli command --long-opt1 v1 -o v2</span></code></pre></div>
44
+ <p>The main ideas:</p>
45
+ <ul>
46
+ <li>Put as little effort as possible into turning a clojure function
47
+ into a CLI, similar to <code>-X</code> style invocations. For lazy
48
+ people like me! If you are not familiar with <code>clj -X</code>, read
49
+ the docs <a
50
+ href="https://clojure.org/reference/clojure_cli#use_fn">here</a>.</li>
51
+ <li>But with a better UX by not having to use quotes on the command line
52
+ as a result of having to pass EDN directly: <code>--dir foo</code>
53
+ instead of <code>:dir '"foo"'</code> (or who knows how to write the
54
+ latter in <code>cmd.exe</code> or Powershell?).</li>
55
+ <li>By default, employ an open world assumption: passing extra arguments
56
+ does not break and arguments can be re-used in multiple contexts.</li>
57
+ <li>But also support incremental restrictions and validations as a form
58
+ of polishing a CLI for production use.</li>
59
+ </ul>
60
+ <p>See <a href="#clojure-cli">clojure CLI</a> for how to turn your exec
61
+ functions into CLIs.</p>
62
+ <h2 id="projects-using-babashka-cli">Projects using babashka CLI</h2>
63
+ <ul>
64
+ <li><a href="https://github.com/borkdude/jet">jet</a></li>
65
+ <li><a
66
+ href="https://github.com/babashka/http-server">http-server</a></li>
67
+ <li><a href="https://github.com/babashka/neil">neil</a></li>
68
+ <li><a
69
+ href="https://github.com/borkdude/quickdoc#clojure-cli">quickdoc</a></li>
70
+ <li><a
71
+ href="https://github.com/seancorfield/clj-new#babashka-cli">clj-new</a></li>
72
+ <li><a
73
+ href="https://github.com/seancorfield/deps-new#babashka-cli">deps-new</a></li>
74
+ </ul>
75
+ <h2 id="toc">TOC</h2>
76
+ <ul>
77
+ <li><a href="#simple-example">Simple example</a></li>
78
+ <li><a href="#options">Options</a></li>
79
+ <li><a href="#arguments">Arguments</a></li>
80
+ <li><a href="#subcommands">Subcommands</a></li>
81
+ <li><a href="#completions">Completions</a></li>
82
+ <li><a href="#adding-production-polish">Adding Production
83
+ Polish</a></li>
84
+ <li><a href="#babashka-tasks">Babashka tasks</a></li>
85
+ <li><a href="#clojure-cli">Clojure CLI</a></li>
86
+ <li><a href="#leiningen">Leiningen</a></li>
87
+ </ul>
88
+ <h2 id="simple-example">Simple example</h2>
89
+ <p>Babashka CLI works in Clojure, ClojureScript and <a
90
+ href="https://book.babashka.org/">babashka</a>. Here is an example
91
+ babashka script to get you started!</p>
92
+ <div class="sourceCode" id="cb3"><pre
93
+ class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a>#!/usr/bin/env bb</span>
94
+ <span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a>(<span class="kw">require</span> &#39;[babashka.cli <span class="at">:as</span> cli]</span>
95
+ <span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a> &#39;[babashka.fs <span class="at">:as</span> fs])</span>
96
+ <span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a></span>
97
+ <span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a>(<span class="bu">defn</span><span class="fu"> dir-exists? </span>[<span class="kw">path</span>]</span>
98
+ <span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a> (fs/directory? <span class="kw">path</span>))</span>
99
+ <span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a></span>
100
+ <span id="cb3-8"><a href="#cb3-8" aria-hidden="true" tabindex="-1"></a>(<span class="bu">def</span><span class="fu"> spec</span></span>
101
+ <span id="cb3-9"><a href="#cb3-9" aria-hidden="true" tabindex="-1"></a> {<span class="at">:num</span> {<span class="at">:coerce</span> <span class="at">:long</span></span>
102
+ <span id="cb3-10"><a href="#cb3-10" aria-hidden="true" tabindex="-1"></a> <span class="at">:alias</span> <span class="at">:n</span> <span class="co">; adds -n alias for --num</span></span>
103
+ <span id="cb3-11"><a href="#cb3-11" aria-hidden="true" tabindex="-1"></a> <span class="at">:desc</span> <span class="st">&quot;Number of some items&quot;</span></span>
104
+ <span id="cb3-12"><a href="#cb3-12" aria-hidden="true" tabindex="-1"></a> <span class="at">:validate</span> <span class="kw">pos?</span> <span class="co">; tests if supplied --num &gt; 0</span></span>
105
+ <span id="cb3-13"><a href="#cb3-13" aria-hidden="true" tabindex="-1"></a> <span class="at">:require</span> <span class="va">true</span>} <span class="co">; --num,-n is required</span></span>
106
+ <span id="cb3-14"><a href="#cb3-14" aria-hidden="true" tabindex="-1"></a> <span class="at">:dir</span> {<span class="at">:alias</span> <span class="at">:d</span></span>
107
+ <span id="cb3-15"><a href="#cb3-15" aria-hidden="true" tabindex="-1"></a> <span class="at">:desc</span> <span class="st">&quot;Directory name to do stuff&quot;</span></span>
108
+ <span id="cb3-16"><a href="#cb3-16" aria-hidden="true" tabindex="-1"></a> <span class="at">:validate</span> <span class="co">; tests if --dir exists,</span></span>
109
+ <span id="cb3-17"><a href="#cb3-17" aria-hidden="true" tabindex="-1"></a> {<span class="at">:pred</span> dir-exists? <span class="co">; with a custom error message</span></span>
110
+ <span id="cb3-18"><a href="#cb3-18" aria-hidden="true" tabindex="-1"></a> <span class="at">:ex-msg</span> (<span class="kw">fn</span> [{<span class="at">:keys</span> [value]}]</span>
111
+ <span id="cb3-19"><a href="#cb3-19" aria-hidden="true" tabindex="-1"></a> (<span class="kw">str</span> <span class="st">&quot;Directory does not exist: &quot;</span> value))}}</span>
112
+ <span id="cb3-20"><a href="#cb3-20" aria-hidden="true" tabindex="-1"></a> <span class="at">:flag</span> {<span class="at">:coerce</span> <span class="at">:boolean</span> <span class="co">; defines a boolean flag</span></span>
113
+ <span id="cb3-21"><a href="#cb3-21" aria-hidden="true" tabindex="-1"></a> <span class="at">:desc</span> <span class="st">&quot;I am just a flag&quot;</span>}})</span>
114
+ <span id="cb3-22"><a href="#cb3-22" aria-hidden="true" tabindex="-1"></a></span>
115
+ <span id="cb3-23"><a href="#cb3-23" aria-hidden="true" tabindex="-1"></a>(<span class="bu">defn</span><span class="fu"> run </span>[{<span class="at">:keys</span> [opts]}]</span>
116
+ <span id="cb3-24"><a href="#cb3-24" aria-hidden="true" tabindex="-1"></a> (<span class="kw">println</span> <span class="st">&quot;Here are your cli args!:&quot;</span> opts))</span>
117
+ <span id="cb3-25"><a href="#cb3-25" aria-hidden="true" tabindex="-1"></a></span>
118
+ <span id="cb3-26"><a href="#cb3-26" aria-hidden="true" tabindex="-1"></a>(<span class="bu">defn</span><span class="fu"> -main </span>[&amp; args]</span>
119
+ <span id="cb3-27"><a href="#cb3-27" aria-hidden="true" tabindex="-1"></a> (cli/dispatch {<span class="at">:fn</span> run <span class="at">:spec</span> spec} args {<span class="at">:prog</span> <span class="st">&quot;try-me&quot;</span> <span class="at">:help</span> <span class="va">true</span>}))</span>
120
+ <span id="cb3-28"><a href="#cb3-28" aria-hidden="true" tabindex="-1"></a></span>
121
+ <span id="cb3-29"><a href="#cb3-29" aria-hidden="true" tabindex="-1"></a>(<span class="kw">apply</span> -main <span class="va">*command-line-args*</span>)</span></code></pre></div>
122
+ <p>In the above example, <code>:help true</code> wires up automatic
123
+ <code>--help</code>/<code>-h</code> support and terse error messages for
124
+ you. See <a href="#help">Subcommands &gt; Help</a> for customizing
125
+ it.</p>
126
+ <p>The first argument to <code>dispatch</code> is a <a
127
+ href="#tree-format">tree</a> of subcommands. Since this CLI has no
128
+ subcommands, this is all you need to write. See <a
129
+ href="#subcommands">Subcommands</a> for more info.</p>
130
+ <p>And this is how you run it:</p>
131
+ <pre><code>$ bb try-me.clj --num 1 --dir my_dir --flag
132
+ Error: Directory does not exist: my_dir
133
+
134
+ Usage: try-me [options]
135
+
136
+ Run &quot;try-me --help&quot; for more information.</code></pre>
137
+ <p>The directory is validated with <code>dir-exists?</code>. A custom
138
+ error message is produced by the <code>:ex-msg</code> function. Since
139
+ the dir does not exist, let’s create it and try again.</p>
140
+ <pre><code>$ mkdir my_dir
141
+ $ bb try-me.clj --num 1 --dir my_dir --flag
142
+ Here are your cli args!: {:num 1, :dir my_dir, :flag true}
143
+
144
+ $ bb try-me.clj --help
145
+ Usage: try-me [options]
146
+
147
+ Options:
148
+ -n, --num Number of some items (required)
149
+ -d, --dir Directory name to do stuff
150
+ --flag I am just a flag
151
+ -h, --help Show this help
152
+
153
+ $ bb try-me.clj
154
+ Error: Required option: --num
155
+
156
+ Usage: try-me [options]
157
+
158
+ Run &quot;try-me --help&quot; for more information.</code></pre>
159
+ <h3 id="adding-subcommands">Adding subcommands</h3>
160
+ <p>To add subcommands to this CLI, we need to specify a subcommand
161
+ structure. We’ll just give an example here. See <a
162
+ href="#subcommands">Subcommands</a> for more info.</p>
163
+ <div class="sourceCode" id="cb6"><pre
164
+ class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a>(<span class="bu">defn</span><span class="fu"> run </span>[{<span class="at">:keys</span> [opts]}]</span>
165
+ <span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a> (<span class="kw">println</span> <span class="st">&quot;Here are your cli args!:&quot;</span> opts))</span>
166
+ <span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a></span>
167
+ <span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a>(<span class="bu">defn</span><span class="fu"> version </span>[_]</span>
168
+ <span id="cb6-5"><a href="#cb6-5" aria-hidden="true" tabindex="-1"></a> (<span class="kw">println</span> <span class="st">&quot;try-me 1.0&quot;</span>))</span>
169
+ <span id="cb6-6"><a href="#cb6-6" aria-hidden="true" tabindex="-1"></a></span>
170
+ <span id="cb6-7"><a href="#cb6-7" aria-hidden="true" tabindex="-1"></a>(<span class="bu">def</span><span class="fu"> tree</span></span>
171
+ <span id="cb6-8"><a href="#cb6-8" aria-hidden="true" tabindex="-1"></a> {<span class="at">:cmd</span> {<span class="st">&quot;run&quot;</span> {<span class="at">:fn</span> run <span class="at">:doc</span> <span class="st">&quot;Run the thing&quot;</span> <span class="at">:spec</span> spec}</span>
172
+ <span id="cb6-9"><a href="#cb6-9" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;version&quot;</span> {<span class="at">:fn</span> version <span class="at">:doc</span> <span class="st">&quot;Print version&quot;</span>}}})</span>
173
+ <span id="cb6-10"><a href="#cb6-10" aria-hidden="true" tabindex="-1"></a></span>
174
+ <span id="cb6-11"><a href="#cb6-11" aria-hidden="true" tabindex="-1"></a>(<span class="bu">defn</span><span class="fu"> -main </span>[&amp; args]</span>
175
+ <span id="cb6-12"><a href="#cb6-12" aria-hidden="true" tabindex="-1"></a> (cli/dispatch tree args {<span class="at">:prog</span> <span class="st">&quot;try-me&quot;</span> <span class="at">:help</span> <span class="va">true</span>}))</span></code></pre></div>
176
+ <p><code>--help</code> now lists the commands:</p>
177
+ <pre><code>$ bb try-me.clj --help
178
+ Usage: try-me [options] &lt;command&gt;
179
+
180
+ Commands:
181
+ run Run the thing
182
+ version Print version
183
+
184
+ Options:
185
+ -h, --help Show this help
186
+
187
+ Run &quot;try-me &lt;command&gt; --help&quot; for more information on a command.</code></pre>
188
+ <p><code>bb try-me.clj run --num 1 --flag</code> calls <code>run</code>;
189
+ <code>bb try-me.clj version</code> calls <code>version</code>. See <a
190
+ href="#subcommands">Subcommands</a> for shared options, inheritance and
191
+ help customization.</p>
192
+ <h2 id="options">Options</h2>
193
+ <p>For parsing options, use either <a
194
+ href="/API.md#parse-opts"><code>parse-opts</code></a> or <a
195
+ href="/API.md#parse-args"><code>parse-args</code></a>.</p>
196
+ <p>On the command line, a named option is written
197
+ <code>--opt val</code>, or <code>-o val</code> using an <a
198
+ href="#aliases">alias</a>. Babashka CLI also accepts a
199
+ <code>:</code>-prefixed form, <code>:opt val</code>, to match the
200
+ Clojure CLI <code>-X</code> invocation style. The two cannot be mixed in
201
+ a single invocation. Use <code>--</code>/<code>-</code> or
202
+ <code>:</code>, not both.</p>
203
+ <p>Options are configured with a <a href="#spec">spec</a> (short for
204
+ “options spec”, not <code>clojure.spec</code>): a map keyed by option
205
+ name, each value a map of <code>:coerce</code>, <code>:alias</code>,
206
+ <code>:validate</code>, <code>:require</code>, <code>:desc</code>, etc.,
207
+ passed under <code>:spec</code>:</p>
208
+ <div class="sourceCode" id="cb8"><pre
209
+ class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a>{<span class="at">:spec</span> {<span class="at">:port</span> {<span class="at">:coerce</span> <span class="at">:long</span> <span class="at">:alias</span> <span class="at">:p</span>}}}</span></code></pre></div>
210
+ <p>A terser shape is also supported, where each key is lifted to the top
211
+ level and keyed by option name:
212
+ <code>{:coerce {:port :long} :alias {:p :port}}</code>. It is handy for
213
+ quick scripts and partial parsing, but only a spec can carry
214
+ <code>:desc</code>/<code>:ref</code>, so generated help and option
215
+ printing need a spec. The two are otherwise equivalent. The examples
216
+ below use the spec shape.</p>
217
+ <p>Examples:</p>
218
+ <p>Parse <code>{:port 1339}</code> from command line arguments:</p>
219
+ <div class="sourceCode" id="cb9"><pre
220
+ class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a>(<span class="kw">require</span> &#39;[babashka.cli <span class="at">:as</span> cli])</span>
221
+ <span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a></span>
222
+ <span id="cb9-3"><a href="#cb9-3" aria-hidden="true" tabindex="-1"></a>(cli/parse-opts [<span class="st">&quot;--port&quot;</span> <span class="st">&quot;1339&quot;</span>] {<span class="at">:spec</span> {<span class="at">:port</span> {<span class="at">:coerce</span> <span class="at">:long</span>}}})</span>
223
+ <span id="cb9-4"><a href="#cb9-4" aria-hidden="true" tabindex="-1"></a><span class="co">;;=&gt; {:port 1339}</span></span></code></pre></div>
224
+ <p>Use an alias (short option):</p>
225
+ <div class="sourceCode" id="cb10"><pre
226
+ class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a>(cli/parse-opts [<span class="st">&quot;-p&quot;</span> <span class="st">&quot;1339&quot;</span>] {<span class="at">:spec</span> {<span class="at">:port</span> {<span class="at">:coerce</span> <span class="at">:long</span> <span class="at">:alias</span> <span class="at">:p</span>}}})</span>
227
+ <span id="cb10-2"><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a><span class="co">;; {:port 1339}</span></span></code></pre></div>
228
+ <p>Coerce values into a collection:</p>
229
+ <div class="sourceCode" id="cb11"><pre
230
+ class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true" tabindex="-1"></a>(cli/parse-opts [<span class="st">&quot;--paths&quot;</span> <span class="st">&quot;src&quot;</span> <span class="st">&quot;--paths&quot;</span> <span class="st">&quot;test&quot;</span>] {<span class="at">:spec</span> {<span class="at">:paths</span> {<span class="at">:coerce</span> []}}})</span>
231
+ <span id="cb11-2"><a href="#cb11-2" aria-hidden="true" tabindex="-1"></a><span class="co">;;=&gt; {:paths [&quot;src&quot; &quot;test&quot;]}</span></span>
232
+ <span id="cb11-3"><a href="#cb11-3" aria-hidden="true" tabindex="-1"></a></span>
233
+ <span id="cb11-4"><a href="#cb11-4" aria-hidden="true" tabindex="-1"></a>(cli/parse-opts [<span class="st">&quot;--paths&quot;</span> <span class="st">&quot;src&quot;</span> <span class="st">&quot;test&quot;</span>] {<span class="at">:spec</span> {<span class="at">:paths</span> {<span class="at">:coerce</span> []}}})</span>
234
+ <span id="cb11-5"><a href="#cb11-5" aria-hidden="true" tabindex="-1"></a><span class="co">;;=&gt; {:paths [&quot;src&quot; &quot;test&quot;]}</span></span></code></pre></div>
235
+ <p>Transforming to a collection of a certain type:</p>
236
+ <div class="sourceCode" id="cb12"><pre
237
+ class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true" tabindex="-1"></a>(cli/parse-opts [<span class="st">&quot;--foo&quot;</span> <span class="st">&quot;bar&quot;</span> <span class="st">&quot;--foo&quot;</span> <span class="st">&quot;baz&quot;</span>] {<span class="at">:spec</span> {<span class="at">:foo</span> {<span class="at">:coerce</span> [<span class="at">:keyword</span>]}}})</span>
238
+ <span id="cb12-2"><a href="#cb12-2" aria-hidden="true" tabindex="-1"></a><span class="co">;; =&gt; {:foo [:bar :baz]}</span></span></code></pre></div>
239
+ <p>Besides the built-in coercion keywords, <code>:coerce</code> accepts
240
+ any function (called with the string value):</p>
241
+ <div class="sourceCode" id="cb13"><pre
242
+ class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true" tabindex="-1"></a>(cli/parse-opts [<span class="st">&quot;--letter&quot;</span> <span class="st">&quot;alpha&quot;</span>] {<span class="at">:spec</span> {<span class="at">:letter</span> {<span class="at">:coerce</span> (<span class="kw">fn</span> [s] (<span class="kw">subs</span> s <span class="dv">0</span> <span class="dv">1</span>))}}})</span>
243
+ <span id="cb13-2"><a href="#cb13-2" aria-hidden="true" tabindex="-1"></a><span class="co">;;=&gt; {:letter &quot;a&quot;}</span></span></code></pre></div>
244
+ <p>Booleans need no explicit <code>true</code> value and
245
+ <code>:coerce</code> option:</p>
246
+ <div class="sourceCode" id="cb14"><pre
247
+ class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb14-1"><a href="#cb14-1" aria-hidden="true" tabindex="-1"></a>(cli/parse-opts [<span class="st">&quot;--verbose&quot;</span>])</span>
248
+ <span id="cb14-2"><a href="#cb14-2" aria-hidden="true" tabindex="-1"></a><span class="co">;;=&gt; {:verbose true}</span></span>
249
+ <span id="cb14-3"><a href="#cb14-3" aria-hidden="true" tabindex="-1"></a></span>
250
+ <span id="cb14-4"><a href="#cb14-4" aria-hidden="true" tabindex="-1"></a>(cli/parse-opts [<span class="st">&quot;-v&quot;</span> <span class="st">&quot;-v&quot;</span> <span class="st">&quot;-v&quot;</span>] {<span class="at">:spec</span> {<span class="at">:verbose</span> {<span class="at">:alias</span> <span class="at">:v</span> <span class="at">:coerce</span> []}}})</span>
251
+ <span id="cb14-5"><a href="#cb14-5" aria-hidden="true" tabindex="-1"></a><span class="co">;;=&gt; {:verbose [true true true]}</span></span></code></pre></div>
252
+ <p>Long options also support the syntax <code>--foo=bar</code>:</p>
253
+ <div class="sourceCode" id="cb15"><pre
254
+ class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb15-1"><a href="#cb15-1" aria-hidden="true" tabindex="-1"></a>(cli/parse-opts [<span class="st">&quot;--foo=bar&quot;</span>])</span>
255
+ <span id="cb15-2"><a href="#cb15-2" aria-hidden="true" tabindex="-1"></a><span class="co">;;=&gt; {:foo &quot;bar&quot;}</span></span></code></pre></div>
256
+ <p>Flags may be combined into a single short option:</p>
257
+ <div class="sourceCode" id="cb16"><pre
258
+ class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb16-1"><a href="#cb16-1" aria-hidden="true" tabindex="-1"></a>(cli/parse-opts [<span class="st">&quot;-abc&quot;</span>])</span>
259
+ <span id="cb16-2"><a href="#cb16-2" aria-hidden="true" tabindex="-1"></a><span class="co">;;=&gt; {:a true :b true :c true}</span></span></code></pre></div>
260
+ <p>Arguments that start with <code>--no-</code> arg parsed as negative
261
+ flags:</p>
262
+ <div class="sourceCode" id="cb17"><pre
263
+ class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb17-1"><a href="#cb17-1" aria-hidden="true" tabindex="-1"></a>(cli/parse-opts [<span class="st">&quot;--no-colors&quot;</span>])</span>
264
+ <span id="cb17-2"><a href="#cb17-2" aria-hidden="true" tabindex="-1"></a><span class="co">;;=&gt; {:colors false}</span></span></code></pre></div>
265
+ <p>This works for any option. For a boolean option where the negation is
266
+ meaningful, set <code>:negatable true</code> in its spec to advertise it
267
+ in help as <code>--[no-]colors</code>.</p>
268
+ <h2 id="spec">Spec</h2>
269
+ <p>A spec (short for “options spec”, not <code>clojure.spec</code>) is a
270
+ map keyed by option name; each value configures one option. Alongside
271
+ the parsing keys (<code>:coerce</code>, <code>:alias</code>,
272
+ <code>:validate</code>, …) it carries <code>:desc</code> and
273
+ <code>:ref</code>, used when printing options (see <a
274
+ href="#printing-options">Printing options</a>). For example:</p>
275
+ <div class="sourceCode" id="cb18"><pre
276
+ class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb18-1"><a href="#cb18-1" aria-hidden="true" tabindex="-1"></a>(<span class="bu">def</span><span class="fu"> spec </span>{<span class="at">:from</span> {<span class="at">:ref</span> <span class="st">&quot;&lt;format&gt;&quot;</span></span>
277
+ <span id="cb18-2"><a href="#cb18-2" aria-hidden="true" tabindex="-1"></a> <span class="at">:desc</span> <span class="st">&quot;The input format. &lt;format&gt; can be edn, json or transit.&quot;</span></span>
278
+ <span id="cb18-3"><a href="#cb18-3" aria-hidden="true" tabindex="-1"></a> <span class="at">:coerce</span> <span class="at">:keyword</span></span>
279
+ <span id="cb18-4"><a href="#cb18-4" aria-hidden="true" tabindex="-1"></a> <span class="at">:alias</span> <span class="at">:i</span></span>
280
+ <span id="cb18-5"><a href="#cb18-5" aria-hidden="true" tabindex="-1"></a> <span class="at">:default-desc</span> <span class="st">&quot;edn&quot;</span></span>
281
+ <span id="cb18-6"><a href="#cb18-6" aria-hidden="true" tabindex="-1"></a> <span class="at">:default</span> <span class="at">:edn</span>}</span>
282
+ <span id="cb18-7"><a href="#cb18-7" aria-hidden="true" tabindex="-1"></a> <span class="at">:to</span> {<span class="at">:ref</span> <span class="st">&quot;&lt;format&gt;&quot;</span></span>
283
+ <span id="cb18-8"><a href="#cb18-8" aria-hidden="true" tabindex="-1"></a> <span class="at">:desc</span> <span class="st">&quot;The output format. &lt;format&gt; can be edn, json or transit.&quot;</span></span>
284
+ <span id="cb18-9"><a href="#cb18-9" aria-hidden="true" tabindex="-1"></a> <span class="at">:coerce</span> <span class="at">:keyword</span></span>
285
+ <span id="cb18-10"><a href="#cb18-10" aria-hidden="true" tabindex="-1"></a> <span class="at">:alias</span> <span class="at">:o</span></span>
286
+ <span id="cb18-11"><a href="#cb18-11" aria-hidden="true" tabindex="-1"></a> <span class="at">:default-desc</span> <span class="st">&quot;json&quot;</span></span>
287
+ <span id="cb18-12"><a href="#cb18-12" aria-hidden="true" tabindex="-1"></a> <span class="at">:default</span> <span class="at">:json</span>}</span>
288
+ <span id="cb18-13"><a href="#cb18-13" aria-hidden="true" tabindex="-1"></a> <span class="at">:pretty</span> {<span class="at">:desc</span> <span class="st">&quot;Pretty-print output.&quot;</span></span>
289
+ <span id="cb18-14"><a href="#cb18-14" aria-hidden="true" tabindex="-1"></a> <span class="at">:alias</span> <span class="at">:p</span>}</span>
290
+ <span id="cb18-15"><a href="#cb18-15" aria-hidden="true" tabindex="-1"></a> <span class="at">:paths</span> {<span class="at">:desc</span> <span class="st">&quot;Paths of files to transform.&quot;</span></span>
291
+ <span id="cb18-16"><a href="#cb18-16" aria-hidden="true" tabindex="-1"></a> <span class="at">:coerce</span> []</span>
292
+ <span id="cb18-17"><a href="#cb18-17" aria-hidden="true" tabindex="-1"></a> <span class="at">:default</span> [<span class="st">&quot;src&quot;</span> <span class="st">&quot;test&quot;</span>]</span>
293
+ <span id="cb18-18"><a href="#cb18-18" aria-hidden="true" tabindex="-1"></a> <span class="at">:default-desc</span> <span class="st">&quot;src test&quot;</span>}})</span></code></pre></div>
294
+ <p>You can pass the spec to <code>parse-opts</code> under the
295
+ <code>:spec</code> key: <code>(parse-opts args {:spec spec})</code>. An
296
+ explanation of each key:</p>
297
+ <ul>
298
+ <li><code>:ref</code>: a name which can be used as a reference in the
299
+ description (<code>:desc</code>)</li>
300
+ <li><code>:desc</code>: a description of the option.</li>
301
+ <li><code>:coerce</code>: coerce a string value to a type. Built-in
302
+ keywords: <code>:boolean</code> (<code>:bool</code>), <code>:int</code>
303
+ (<code>:long</code>), <code>:double</code>, <code>:number</code>,
304
+ <code>:symbol</code>, <code>:keyword</code>, <code>:string</code>,
305
+ <code>:edn</code>, <code>:auto</code>. A collection collects repeated
306
+ values: <code>[]</code> (vector), <code>#{}</code> (set) or
307
+ <code>()</code> (list); put a coercion keyword inside it to coerce each
308
+ element (e.g. <code>[:keyword]</code>, <code>#{:int}</code>). A function
309
+ is also accepted: it is called with the string and returns the
310
+ value.</li>
311
+ <li><code>:alias</code>: mapping of short name to long name.</li>
312
+ <li><code>:default</code>: default value.</li>
313
+ <li><code>:default-desc</code>: a string representation of the default
314
+ value.</li>
315
+ <li><code>:require</code>: <code>true</code> make this opt
316
+ required.</li>
317
+ <li><code>:validate</code>: a function used to validate the value of
318
+ this opt (as described in the <a href="#validate">Validate</a>
319
+ section).</li>
320
+ <li><code>:collect</code>: collect repeated values into a collection
321
+ (<code>[]</code> vector, <code>#{}</code> set or <code>()</code> list),
322
+ or a function <code>(fn [coll arg-value] ...)</code> for custom
323
+ collection</li>
324
+ <li><code>:negatable</code>: <code>true</code> shows a boolean option as
325
+ <code>--[no-]name</code> in help (the <code>--no-name</code> form parses
326
+ regardless)</li>
327
+ </ul>
328
+ <h3 id="custom-collection-handling">Custom collection handling</h3>
329
+ <p>Usually the above will suffice, but for custom transformation to a
330
+ collection, you can use <code>:collect</code>. Here’s an example of
331
+ parsing out <code>,</code> separated multi-arg-values:</p>
332
+ <div class="sourceCode" id="cb19"><pre
333
+ class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb19-1"><a href="#cb19-1" aria-hidden="true" tabindex="-1"></a>(cli/parse-opts [<span class="st">&quot;--foo&quot;</span> <span class="st">&quot;a,b&quot;</span> <span class="st">&quot;--foo=c,d,e&quot;</span> <span class="st">&quot;--foo&quot;</span> <span class="st">&quot;f&quot;</span>]</span>
334
+ <span id="cb19-2"><a href="#cb19-2" aria-hidden="true" tabindex="-1"></a> {<span class="at">:spec</span> {<span class="at">:foo</span> {<span class="at">:collect</span> (<span class="kw">fn</span> [coll arg-value]</span>
335
+ <span id="cb19-3"><a href="#cb19-3" aria-hidden="true" tabindex="-1"></a> (<span class="kw">into</span> (<span class="kw">or</span> coll [])</span>
336
+ <span id="cb19-4"><a href="#cb19-4" aria-hidden="true" tabindex="-1"></a> (str/split arg-value <span class="ss">#&quot;,&quot;</span>)))}}})</span>
337
+ <span id="cb19-5"><a href="#cb19-5" aria-hidden="true" tabindex="-1"></a><span class="co">;; =&gt; {:foo [&quot;a&quot; &quot;b&quot; &quot;c&quot; &quot;d&quot; &quot;e&quot; &quot;f&quot;]}</span></span></code></pre></div>
338
+ <h3 id="auto-coercion">Auto-coercion</h3>
339
+ <p>Babashka CLI auto-coerces values that have no explicit coercion with
340
+ <a href="/API.md#auto-coerce"><code>auto-coerce</code></a>: it
341
+ automatically tries to convert booleans, numbers and keywords.</p>
342
+ <h3 id="aliases">Aliases</h3>
343
+ <p>An <code>:alias</code> specifies a mapping from short to long
344
+ name.</p>
345
+ <p>The library can distinguish aliases with characters in common, so a
346
+ way to implement the common <code>-v</code>/<code>-vv</code> unix
347
+ pattern is:</p>
348
+ <div class="sourceCode" id="cb20"><pre
349
+ class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb20-1"><a href="#cb20-1" aria-hidden="true" tabindex="-1"></a>(<span class="bu">def</span><span class="fu"> spec </span>{<span class="at">:verbose</span> {<span class="at">:alias</span> <span class="at">:v</span></span>
350
+ <span id="cb20-2"><a href="#cb20-2" aria-hidden="true" tabindex="-1"></a> <span class="at">:desc</span> <span class="st">&quot;Enable verbose output.&quot;</span>}</span>
351
+ <span id="cb20-3"><a href="#cb20-3" aria-hidden="true" tabindex="-1"></a> <span class="at">:very-verbose</span> {<span class="at">:alias</span> <span class="at">:vv</span></span>
352
+ <span id="cb20-4"><a href="#cb20-4" aria-hidden="true" tabindex="-1"></a> <span class="at">:desc</span> <span class="st">&quot;Enable very verbose output.&quot;</span>}})</span></code></pre></div>
353
+ <p>You get:</p>
354
+ <div class="sourceCode" id="cb21"><pre
355
+ class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb21-1"><a href="#cb21-1" aria-hidden="true" tabindex="-1"></a>(cli/parse-opts [<span class="st">&quot;-v&quot;</span>] {<span class="at">:spec</span> spec})</span>
356
+ <span id="cb21-2"><a href="#cb21-2" aria-hidden="true" tabindex="-1"></a><span class="co">;;=&gt; {:verbose true}</span></span>
357
+ <span id="cb21-3"><a href="#cb21-3" aria-hidden="true" tabindex="-1"></a></span>
358
+ <span id="cb21-4"><a href="#cb21-4" aria-hidden="true" tabindex="-1"></a>(cli/parse-opts [<span class="st">&quot;-vv&quot;</span>] {<span class="at">:spec</span> spec})</span>
359
+ <span id="cb21-5"><a href="#cb21-5" aria-hidden="true" tabindex="-1"></a><span class="co">;;=&gt; {:very-verbose true}</span></span></code></pre></div>
360
+ <p>Another way would be to collect the flags in a vector with
361
+ <code>:coerce</code> (and base verbosity on the size of that
362
+ vector):</p>
363
+ <div class="sourceCode" id="cb22"><pre
364
+ class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb22-1"><a href="#cb22-1" aria-hidden="true" tabindex="-1"></a>(<span class="bu">def</span><span class="fu"> spec </span>{<span class="at">:verbose</span> {<span class="at">:alias</span> <span class="at">:v</span></span>
365
+ <span id="cb22-2"><a href="#cb22-2" aria-hidden="true" tabindex="-1"></a> <span class="at">:desc</span> <span class="st">&quot;Enable verbose output.&quot;</span></span>
366
+ <span id="cb22-3"><a href="#cb22-3" aria-hidden="true" tabindex="-1"></a> <span class="at">:coerce</span> []}})</span>
367
+ <span id="cb22-4"><a href="#cb22-4" aria-hidden="true" tabindex="-1"></a></span>
368
+ <span id="cb22-5"><a href="#cb22-5" aria-hidden="true" tabindex="-1"></a>user=&gt; (cli/parse-opts [<span class="st">&quot;-vvv&quot;</span>] {<span class="at">:spec</span> spec})</span>
369
+ <span id="cb22-6"><a href="#cb22-6" aria-hidden="true" tabindex="-1"></a>{<span class="at">:verbose</span> [<span class="va">true</span> <span class="va">true</span> <span class="va">true</span>]}</span></code></pre></div>
370
+ <h2 id="arguments">Arguments</h2>
371
+ <p>To parse positional arguments, you can use <code>parse-args</code>
372
+ and/or the <code>:args-&gt;opts</code> option. E.g. to parse arguments
373
+ for the <code>git push</code> command:</p>
374
+ <div class="sourceCode" id="cb23"><pre
375
+ class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb23-1"><a href="#cb23-1" aria-hidden="true" tabindex="-1"></a>(cli/parse-args [<span class="st">&quot;--force&quot;</span> <span class="st">&quot;ssh://foo&quot;</span>] {<span class="at">:spec</span> {<span class="at">:force</span> {<span class="at">:coerce</span> <span class="at">:boolean</span>}}})</span>
376
+ <span id="cb23-2"><a href="#cb23-2" aria-hidden="true" tabindex="-1"></a><span class="co">;;=&gt; {:args [&quot;ssh://foo&quot;], :opts {:force true}}</span></span>
377
+ <span id="cb23-3"><a href="#cb23-3" aria-hidden="true" tabindex="-1"></a></span>
378
+ <span id="cb23-4"><a href="#cb23-4" aria-hidden="true" tabindex="-1"></a>(cli/parse-args [<span class="st">&quot;ssh://foo&quot;</span> <span class="st">&quot;--force&quot;</span>] {<span class="at">:spec</span> {<span class="at">:force</span> {<span class="at">:coerce</span> <span class="at">:boolean</span>}}})</span>
379
+ <span id="cb23-5"><a href="#cb23-5" aria-hidden="true" tabindex="-1"></a><span class="co">;;=&gt; {:args [&quot;ssh://foo&quot;], :opts {:force true}}</span></span></code></pre></div>
380
+ <p>Note that this library can only disambiguate correctly between values
381
+ for options and trailing arguments with enough <code>:coerce</code>
382
+ information available. Without the <code>:coerce :boolean</code> info,
383
+ we get:</p>
384
+ <div class="sourceCode" id="cb24"><pre
385
+ class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb24-1"><a href="#cb24-1" aria-hidden="true" tabindex="-1"></a>(cli/parse-args [<span class="st">&quot;--force&quot;</span> <span class="st">&quot;ssh://foo&quot;</span>])</span>
386
+ <span id="cb24-2"><a href="#cb24-2" aria-hidden="true" tabindex="-1"></a>{<span class="at">:opts</span> {<span class="at">:force</span> <span class="st">&quot;ssh://foo&quot;</span>}}</span></code></pre></div>
387
+ <p>In case of ambiguity <code>--</code> may also be used to communicate
388
+ the boundary between options and arguments:</p>
389
+ <div class="sourceCode" id="cb25"><pre
390
+ class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb25-1"><a href="#cb25-1" aria-hidden="true" tabindex="-1"></a>(cli/parse-args [<span class="st">&quot;--paths&quot;</span> <span class="st">&quot;src&quot;</span> <span class="st">&quot;test&quot;</span> <span class="st">&quot;--&quot;</span> <span class="st">&quot;ssh://foo&quot;</span>] {<span class="at">:spec</span> {<span class="at">:paths</span> {<span class="at">:coerce</span> []}}})</span>
391
+ <span id="cb25-2"><a href="#cb25-2" aria-hidden="true" tabindex="-1"></a>{<span class="at">:args</span> [<span class="st">&quot;ssh://foo&quot;</span>], <span class="at">:opts</span> {<span class="at">:paths</span> [<span class="st">&quot;src&quot;</span> <span class="st">&quot;test&quot;</span>]}}</span></code></pre></div>
392
+ <h3 id="args-opts">:args-&gt;opts</h3>
393
+ <p>To fold positional arguments into the parsed options, you can use
394
+ <code>:args-&gt;opts</code>:</p>
395
+ <div class="sourceCode" id="cb26"><pre
396
+ class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb26-1"><a href="#cb26-1" aria-hidden="true" tabindex="-1"></a>(<span class="bu">def</span><span class="fu"> cli-opts </span>{<span class="at">:spec</span> {<span class="at">:force</span> {<span class="at">:coerce</span> <span class="at">:boolean</span>}} <span class="at">:args-</span>&gt;opts [<span class="at">:url</span>]})</span>
397
+ <span id="cb26-2"><a href="#cb26-2" aria-hidden="true" tabindex="-1"></a></span>
398
+ <span id="cb26-3"><a href="#cb26-3" aria-hidden="true" tabindex="-1"></a>(cli/parse-opts [<span class="st">&quot;--force&quot;</span> <span class="st">&quot;ssh://foo&quot;</span>] cli-opts)</span>
399
+ <span id="cb26-4"><a href="#cb26-4" aria-hidden="true" tabindex="-1"></a><span class="co">;;=&gt; {:force true, :url &quot;ssh://foo&quot;}</span></span></code></pre></div>
400
+ <div class="sourceCode" id="cb27"><pre
401
+ class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb27-1"><a href="#cb27-1" aria-hidden="true" tabindex="-1"></a>(cli/parse-opts [<span class="st">&quot;ssh://foo&quot;</span> <span class="st">&quot;--force&quot;</span>] cli-opts)</span>
402
+ <span id="cb27-2"><a href="#cb27-2" aria-hidden="true" tabindex="-1"></a><span class="co">;;=&gt; {:url &quot;ssh://foo&quot;, :force true}</span></span></code></pre></div>
403
+ <p>If you want to fold a variable amount of arguments, you can coerce
404
+ into a vector and specify the variable number of arguments with
405
+ <code>repeat</code>:</p>
406
+ <div class="sourceCode" id="cb28"><pre
407
+ class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb28-1"><a href="#cb28-1" aria-hidden="true" tabindex="-1"></a>(<span class="bu">def</span><span class="fu"> cli-opts </span>{<span class="at">:spec</span> {<span class="at">:bar</span> {<span class="at">:coerce</span> []}} <span class="at">:args-</span>&gt;opts (<span class="kw">cons</span> <span class="at">:foo</span> (<span class="kw">repeat</span> <span class="at">:bar</span>))})</span>
408
+ <span id="cb28-2"><a href="#cb28-2" aria-hidden="true" tabindex="-1"></a>(cli/parse-opts [<span class="st">&quot;arg1&quot;</span> <span class="st">&quot;arg2&quot;</span> <span class="st">&quot;arg3&quot;</span> <span class="st">&quot;arg4&quot;</span>] cli-opts)</span>
409
+ <span id="cb28-3"><a href="#cb28-3" aria-hidden="true" tabindex="-1"></a><span class="co">;;=&gt; {:foo &quot;arg1&quot;, :bar [&quot;arg2&quot; &quot;arg3&quot; &quot;arg4&quot;]}</span></span></code></pre></div>
410
+ <p>Options may be interspersed with the positional arguments:</p>
411
+ <div class="sourceCode" id="cb29"><pre
412
+ class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb29-1"><a href="#cb29-1" aria-hidden="true" tabindex="-1"></a>(<span class="bu">def</span><span class="fu"> cli-opts </span>{<span class="at">:spec</span> {<span class="at">:bar</span> {<span class="at">:coerce</span> []} <span class="at">:force</span> {<span class="at">:coerce</span> <span class="at">:boolean</span>}}</span>
413
+ <span id="cb29-2"><a href="#cb29-2" aria-hidden="true" tabindex="-1"></a> <span class="at">:args-</span>&gt;opts (<span class="kw">cons</span> <span class="at">:foo</span> (<span class="kw">repeat</span> <span class="at">:bar</span>))})</span>
414
+ <span id="cb29-3"><a href="#cb29-3" aria-hidden="true" tabindex="-1"></a>(cli/parse-opts [<span class="st">&quot;arg1&quot;</span> <span class="st">&quot;arg2&quot;</span> <span class="st">&quot;--force&quot;</span> <span class="st">&quot;arg3&quot;</span>] cli-opts)</span>
415
+ <span id="cb29-4"><a href="#cb29-4" aria-hidden="true" tabindex="-1"></a><span class="co">;;=&gt; {:foo &quot;arg1&quot;, :bar [&quot;arg2&quot; &quot;arg3&quot;], :force true}</span></span></code></pre></div>
416
+ <p>This also holds for a subcommand leaf in <a
417
+ href="#subcommands">dispatch</a>: a command with variadic
418
+ <code>:args-&gt;opts</code> parses options before, among or after its
419
+ positional arguments. Without <code>:args-&gt;opts</code>,
420
+ <code>dispatch</code> stops at the first positional (to route
421
+ subcommands), so trailing options would not be parsed.</p>
422
+ <h2 id="subcommands">Subcommands</h2>
423
+ <p>To handle subcommands, use <a
424
+ href="/API.md#dispatch">dispatch</a>.</p>
425
+ <p>Say we want a CLI called as:</p>
426
+ <pre><code>$ example copy &lt;file&gt; --dry-run
427
+ $ example delete &lt;file&gt; --recursive --depth 3</code></pre>
428
+ <div class="sourceCode" id="cb31"><pre
429
+ class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb31-1"><a href="#cb31-1" aria-hidden="true" tabindex="-1"></a>(<span class="kw">ns</span> example</span>
430
+ <span id="cb31-2"><a href="#cb31-2" aria-hidden="true" tabindex="-1"></a> (<span class="at">:require</span> [babashka.cli <span class="at">:as</span> cli]))</span>
431
+ <span id="cb31-3"><a href="#cb31-3" aria-hidden="true" tabindex="-1"></a></span>
432
+ <span id="cb31-4"><a href="#cb31-4" aria-hidden="true" tabindex="-1"></a>(<span class="bu">defn</span><span class="fu"> copy </span>[{<span class="at">:keys</span> [opts]}]</span>
433
+ <span id="cb31-5"><a href="#cb31-5" aria-hidden="true" tabindex="-1"></a> (<span class="kw">prn</span> <span class="at">:copy</span> opts))</span>
434
+ <span id="cb31-6"><a href="#cb31-6" aria-hidden="true" tabindex="-1"></a></span>
435
+ <span id="cb31-7"><a href="#cb31-7" aria-hidden="true" tabindex="-1"></a>(<span class="bu">defn</span><span class="fu"> delete </span>[{<span class="at">:keys</span> [opts]}]</span>
436
+ <span id="cb31-8"><a href="#cb31-8" aria-hidden="true" tabindex="-1"></a> (<span class="kw">prn</span> <span class="at">:delete</span> opts))</span>
437
+ <span id="cb31-9"><a href="#cb31-9" aria-hidden="true" tabindex="-1"></a></span>
438
+ <span id="cb31-10"><a href="#cb31-10" aria-hidden="true" tabindex="-1"></a>(<span class="bu">def</span><span class="fu"> table</span></span>
439
+ <span id="cb31-11"><a href="#cb31-11" aria-hidden="true" tabindex="-1"></a> [{<span class="at">:cmds</span> [<span class="st">&quot;copy&quot;</span>] <span class="at">:fn</span> copy <span class="at">:doc</span> <span class="st">&quot;Copy a file&quot;</span> <span class="at">:args-</span>&gt;opts [<span class="at">:file</span>]</span>
440
+ <span id="cb31-12"><a href="#cb31-12" aria-hidden="true" tabindex="-1"></a> <span class="at">:spec</span> {<span class="at">:dry-run</span> {<span class="at">:coerce</span> <span class="at">:boolean</span> <span class="at">:desc</span> <span class="st">&quot;Do a dry run&quot;</span>}}}</span>
441
+ <span id="cb31-13"><a href="#cb31-13" aria-hidden="true" tabindex="-1"></a> {<span class="at">:cmds</span> [<span class="st">&quot;delete&quot;</span>] <span class="at">:fn</span> delete <span class="at">:doc</span> <span class="st">&quot;Delete a file&quot;</span> <span class="at">:args-</span>&gt;opts [<span class="at">:file</span>]</span>
442
+ <span id="cb31-14"><a href="#cb31-14" aria-hidden="true" tabindex="-1"></a> <span class="at">:spec</span> {<span class="at">:recursive</span> {<span class="at">:coerce</span> <span class="at">:boolean</span> <span class="at">:desc</span> <span class="st">&quot;Recurse&quot;</span>}</span>
443
+ <span id="cb31-15"><a href="#cb31-15" aria-hidden="true" tabindex="-1"></a> <span class="at">:depth</span> {<span class="at">:coerce</span> <span class="at">:long</span> <span class="at">:desc</span> <span class="st">&quot;Max depth&quot;</span>}}}</span>
444
+ <span id="cb31-16"><a href="#cb31-16" aria-hidden="true" tabindex="-1"></a> {<span class="at">:cmds</span> [<span class="st">&quot;debug&quot;</span>] <span class="at">:fn</span> <span class="kw">prn</span> <span class="at">:doc</span> <span class="st">&quot;Dump internal state&quot;</span> <span class="at">:no-doc</span> <span class="va">true</span>}])</span>
445
+ <span id="cb31-17"><a href="#cb31-17" aria-hidden="true" tabindex="-1"></a></span>
446
+ <span id="cb31-18"><a href="#cb31-18" aria-hidden="true" tabindex="-1"></a>(<span class="bu">defn</span><span class="fu"> -main </span>[&amp; args]</span>
447
+ <span id="cb31-19"><a href="#cb31-19" aria-hidden="true" tabindex="-1"></a> (cli/dispatch table args {<span class="at">:prog</span> <span class="st">&quot;example&quot;</span> <span class="at">:help</span> <span class="va">true</span>}))</span></code></pre></div>
448
+ <p>Run it with <code>clojure -M -m example ...</code> or
449
+ <code>bb -m example ...</code>. <code>dispatch</code> matches the
450
+ longest <code>:cmds</code> path in the args and calls that entry’s
451
+ <code>:fn</code> with the parsed result. <code>:help true</code> wires
452
+ up <code>--help</code>/<code>-h</code> and terse errors (see <a
453
+ href="#help">Help</a>):</p>
454
+ <pre><code>$ example --help
455
+ Usage: example [options] &lt;command&gt;
456
+
457
+ Commands:
458
+ copy Copy a file
459
+ delete Delete a file
460
+
461
+ Options:
462
+ -h, --help Show this help
463
+
464
+ Run &quot;example &lt;command&gt; --help&quot; for more information on a command.</code></pre>
465
+ <p>The <code>Commands:</code> summaries above come from each entry’s
466
+ <code>:doc</code>, a string documenting that (sub)command. Its first
467
+ line is shown in the parent’s command list. The <code>debug</code>
468
+ command is absent from that list because <code>:no-doc true</code> hides
469
+ a command from <code>--help</code> and from completions. The same
470
+ <code>:no-doc true</code> on a spec option hides that option the same
471
+ way. The option still parses, so it works for deprecated or internal
472
+ flags. The full text of <code>:doc</code> is shown as the description on
473
+ the command’s own <code>--help</code> output, between the usage line and
474
+ <code>Options:</code>:</p>
475
+ <pre><code>$ example copy --help
476
+ Usage: example copy [options] &lt;file&gt;
477
+
478
+ Copy a file
479
+
480
+ Options:
481
+ --dry-run Do a dry run
482
+ -h, --help Show this help</code></pre>
483
+ <p>Running <code>example copy the-file --dry-run</code> calls
484
+ <code>copy</code>, which prints:</p>
485
+ <div class="sourceCode" id="cb34"><pre
486
+ class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb34-1"><a href="#cb34-1" aria-hidden="true" tabindex="-1"></a><span class="at">:copy</span> {<span class="at">:file</span> <span class="st">&quot;the-file&quot;</span>, <span class="at">:dry-run</span> <span class="va">true</span>}</span></code></pre></div>
487
+ <p>The <code>:fn</code> is called with a map of the parsed result:</p>
488
+ <ul>
489
+ <li><code>:opts</code>: the parsed options
490
+ (<code>{:file "the-file" :dry-run true}</code>; <code>:file</code> comes
491
+ from <code>:args-&gt;opts</code>)</li>
492
+ <li><code>:dispatch</code>: the matched command path
493
+ (<code>["copy"]</code>)</li>
494
+ <li><code>:args</code>: any leftover positional args (<code>nil</code>
495
+ here)</li>
496
+ </ul>
497
+ <p>An unknown or missing subcommand prints a terse message and exits
498
+ 1:</p>
499
+ <pre><code>$ example bogus
500
+ Unknown command: bogus
501
+
502
+ Commands:
503
+ copy
504
+ delete
505
+
506
+ Run &quot;example --help&quot; for more information.</code></pre>
507
+ <p>See <a href="https://github.com/babashka/neil">neil</a> for a
508
+ real-world CLI using subcommands.</p>
509
+ <p>Each table entry accepts any <a href="#options">parse-args</a> option
510
+ (<code>:spec</code>, <code>:args-&gt;opts</code>, <code>:alias</code>,
511
+ <code>:restrict</code>, …). The order of entries in the table doesn’t
512
+ matter for invocation on the command line, but is used for
513
+ <code>--help</code> output and completions.</p>
514
+ <h3 id="tree-format">Tree format</h3>
515
+ <p>Subcommands can also be specified in a tree format. The root accepts
516
+ a <code>:spec</code> for top-level options. The first level of
517
+ subcommands is specified under <code>:cmd</code> in a map of strings to
518
+ subcommand options, which are the same as in the table above, minus the
519
+ <code>:cmds</code> entry. You can nest arbitrarily deep.</p>
520
+ <div class="sourceCode" id="cb36"><pre
521
+ class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb36-1"><a href="#cb36-1" aria-hidden="true" tabindex="-1"></a>(<span class="bu">def</span><span class="fu"> tree</span></span>
522
+ <span id="cb36-2"><a href="#cb36-2" aria-hidden="true" tabindex="-1"></a> {<span class="at">:spec</span> {<span class="at">:verbose</span> {<span class="at">:coerce</span> <span class="at">:boolean</span> <span class="at">:inherit</span> <span class="va">true</span> <span class="at">:desc</span> <span class="st">&quot;Verbose output&quot;</span>}}</span>
523
+ <span id="cb36-3"><a href="#cb36-3" aria-hidden="true" tabindex="-1"></a> <span class="at">:cmd</span> {<span class="st">&quot;copy&quot;</span> {<span class="at">:fn</span> copy <span class="at">:doc</span> <span class="st">&quot;Copy a file&quot;</span> <span class="at">:args-</span>&gt;opts [<span class="at">:file</span>]</span>
524
+ <span id="cb36-4"><a href="#cb36-4" aria-hidden="true" tabindex="-1"></a> <span class="at">:spec</span> {<span class="at">:dry-run</span> {<span class="at">:coerce</span> <span class="at">:boolean</span> <span class="at">:desc</span> <span class="st">&quot;Do a dry run&quot;</span>}}}</span>
525
+ <span id="cb36-5"><a href="#cb36-5" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;cache&quot;</span> {<span class="at">:doc</span> <span class="st">&quot;Manage the cache&quot;</span></span>
526
+ <span id="cb36-6"><a href="#cb36-6" aria-hidden="true" tabindex="-1"></a> <span class="at">:cmd</span> {<span class="st">&quot;clean&quot;</span> {<span class="at">:fn</span> clean <span class="at">:doc</span> <span class="st">&quot;Clean the cache&quot;</span>}}}}})</span>
527
+ <span id="cb36-7"><a href="#cb36-7" aria-hidden="true" tabindex="-1"></a></span>
528
+ <span id="cb36-8"><a href="#cb36-8" aria-hidden="true" tabindex="-1"></a>(cli/dispatch tree args {<span class="at">:prog</span> <span class="st">&quot;example&quot;</span> <span class="at">:help</span> <span class="va">true</span>})</span></code></pre></div>
529
+ <p>The table or tree format can be used interchangeably in
530
+ <code>dispatch</code>, <code>format-command-help</code> and the
531
+ like.</p>
532
+ <p>The map’s order is used for help output. This can be problematic with
533
+ more than 8 entries since those maps turn into unordered hash-maps. In
534
+ that case you can use <code>:cmd-order</code> to specify the order for
535
+ printing. Commands not mentioned in <code>:cmd-order</code> are left out
536
+ of printed output, but are still callable on the command line.</p>
537
+ <div class="sourceCode" id="cb37"><pre
538
+ class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb37-1"><a href="#cb37-1" aria-hidden="true" tabindex="-1"></a>{<span class="at">:cmd-order</span> [<span class="st">&quot;copy&quot;</span> <span class="st">&quot;cache&quot;</span>]</span>
539
+ <span id="cb37-2"><a href="#cb37-2" aria-hidden="true" tabindex="-1"></a> <span class="at">:cmd</span> {<span class="st">&quot;copy&quot;</span> {...}</span>
540
+ <span id="cb37-3"><a href="#cb37-3" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;cache&quot;</span> {...}}}</span></code></pre></div>
541
+ <p>Options can be supplied at each level, before and between the
542
+ subcommands. The root level (<code>:cmds []</code>) holds options
543
+ available to every command. For example:</p>
544
+ <div class="sourceCode" id="cb38"><pre
545
+ class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb38-1"><a href="#cb38-1" aria-hidden="true" tabindex="-1"></a>(<span class="bu">def</span><span class="fu"> global-spec </span>{<span class="at">:foo</span> {<span class="at">:coerce</span> <span class="at">:keyword</span>}})</span>
546
+ <span id="cb38-2"><a href="#cb38-2" aria-hidden="true" tabindex="-1"></a>(<span class="bu">def</span><span class="fu"> sub1-spec </span>{<span class="at">:bar</span> {<span class="at">:coerce</span> <span class="at">:keyword</span>}})</span>
547
+ <span id="cb38-3"><a href="#cb38-3" aria-hidden="true" tabindex="-1"></a>(<span class="bu">def</span><span class="fu"> sub2-spec </span>{<span class="at">:baz</span> {<span class="at">:coerce</span> <span class="at">:keyword</span>}})</span>
548
+ <span id="cb38-4"><a href="#cb38-4" aria-hidden="true" tabindex="-1"></a></span>
549
+ <span id="cb38-5"><a href="#cb38-5" aria-hidden="true" tabindex="-1"></a>(<span class="bu">def</span><span class="fu"> table</span></span>
550
+ <span id="cb38-6"><a href="#cb38-6" aria-hidden="true" tabindex="-1"></a> [{<span class="at">:cmds</span> [] <span class="at">:spec</span> global-spec}</span>
551
+ <span id="cb38-7"><a href="#cb38-7" aria-hidden="true" tabindex="-1"></a> {<span class="at">:cmds</span> [<span class="st">&quot;sub1&quot;</span>] <span class="at">:fn</span> <span class="kw">identity</span> <span class="at">:spec</span> sub1-spec}</span>
552
+ <span id="cb38-8"><a href="#cb38-8" aria-hidden="true" tabindex="-1"></a> {<span class="at">:cmds</span> [<span class="st">&quot;sub1&quot;</span> <span class="st">&quot;sub2&quot;</span>] <span class="at">:fn</span> <span class="kw">identity</span> <span class="at">:spec</span> sub2-spec}])</span>
553
+ <span id="cb38-9"><a href="#cb38-9" aria-hidden="true" tabindex="-1"></a></span>
554
+ <span id="cb38-10"><a href="#cb38-10" aria-hidden="true" tabindex="-1"></a>(cli/dispatch table [<span class="st">&quot;--foo&quot;</span> <span class="st">&quot;a&quot;</span> <span class="st">&quot;sub1&quot;</span> <span class="st">&quot;--bar&quot;</span> <span class="st">&quot;b&quot;</span> <span class="st">&quot;sub2&quot;</span> <span class="st">&quot;--baz&quot;</span> <span class="st">&quot;c&quot;</span> <span class="st">&quot;arg&quot;</span>])</span>
555
+ <span id="cb38-11"><a href="#cb38-11" aria-hidden="true" tabindex="-1"></a></span>
556
+ <span id="cb38-12"><a href="#cb38-12" aria-hidden="true" tabindex="-1"></a><span class="co">;;=&gt;</span></span>
557
+ <span id="cb38-13"><a href="#cb38-13" aria-hidden="true" tabindex="-1"></a></span>
558
+ <span id="cb38-14"><a href="#cb38-14" aria-hidden="true" tabindex="-1"></a>{<span class="at">:dispatch</span> [<span class="st">&quot;sub1&quot;</span> <span class="st">&quot;sub2&quot;</span>],</span>
559
+ <span id="cb38-15"><a href="#cb38-15" aria-hidden="true" tabindex="-1"></a> <span class="at">:opts</span> {<span class="at">:foo</span> <span class="at">:a</span>, <span class="at">:bar</span> <span class="at">:b</span>, <span class="at">:baz</span> <span class="at">:c</span>},</span>
560
+ <span id="cb38-16"><a href="#cb38-16" aria-hidden="true" tabindex="-1"></a> <span class="at">:args</span> [<span class="st">&quot;arg&quot;</span>]}</span></code></pre></div>
561
+ <p>It is possible to use <code>:args-&gt;opts</code>, but subcommands
562
+ are always prioritized over arguments:</p>
563
+ <div class="sourceCode" id="cb39"><pre
564
+ class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb39-1"><a href="#cb39-1" aria-hidden="true" tabindex="-1"></a>(<span class="bu">def</span><span class="fu"> table</span></span>
565
+ <span id="cb39-2"><a href="#cb39-2" aria-hidden="true" tabindex="-1"></a> [{<span class="at">:cmds</span> [<span class="st">&quot;sub1&quot;</span>] <span class="at">:fn</span> <span class="kw">identity</span> <span class="at">:spec</span> sub1-spec <span class="at">:args-</span>&gt;opts [<span class="at">:some-opt</span>]}</span>
566
+ <span id="cb39-3"><a href="#cb39-3" aria-hidden="true" tabindex="-1"></a> {<span class="at">:cmds</span> [<span class="st">&quot;sub1&quot;</span> <span class="st">&quot;sub2&quot;</span>] <span class="at">:fn</span> <span class="kw">identity</span> <span class="at">:spec</span> sub2-spec}])</span>
567
+ <span id="cb39-4"><a href="#cb39-4" aria-hidden="true" tabindex="-1"></a></span>
568
+ <span id="cb39-5"><a href="#cb39-5" aria-hidden="true" tabindex="-1"></a>(cli/dispatch table [<span class="st">&quot;sub1&quot;</span> <span class="st">&quot;dude&quot;</span>]) <span class="co">;;=&gt; {:dispatch [&quot;sub1&quot;], :opts {:some-opt &quot;dude&quot;}}</span></span>
569
+ <span id="cb39-6"><a href="#cb39-6" aria-hidden="true" tabindex="-1"></a>(cli/dispatch table [<span class="st">&quot;sub1&quot;</span> <span class="st">&quot;sub2&quot;</span>]) <span class="co">;;=&gt; {:dispatch [&quot;sub1&quot; &quot;sub2&quot;], :opts {}}</span></span></code></pre></div>
570
+ <p>Specs are not merged across levels: an option is parsed with the spec
571
+ of the level it appears at, so it must be supplied before its
572
+ subcommand. With <code>:restrict</code>, supplying it after the
573
+ subcommand is an error:</p>
574
+ <div class="sourceCode" id="cb40"><pre
575
+ class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb40-1"><a href="#cb40-1" aria-hidden="true" tabindex="-1"></a>(<span class="bu">def</span><span class="fu"> table</span></span>
576
+ <span id="cb40-2"><a href="#cb40-2" aria-hidden="true" tabindex="-1"></a> [{<span class="at">:cmds</span> [<span class="st">&quot;group&quot;</span>] <span class="at">:spec</span> {<span class="at">:registry</span> {}}}</span>
577
+ <span id="cb40-3"><a href="#cb40-3" aria-hidden="true" tabindex="-1"></a> {<span class="at">:cmds</span> [<span class="st">&quot;group&quot;</span> <span class="st">&quot;sub&quot;</span>] <span class="at">:fn</span> <span class="kw">identity</span> <span class="at">:spec</span> {<span class="at">:format</span> {}}}])</span>
578
+ <span id="cb40-4"><a href="#cb40-4" aria-hidden="true" tabindex="-1"></a></span>
579
+ <span id="cb40-5"><a href="#cb40-5" aria-hidden="true" tabindex="-1"></a>(cli/dispatch table [<span class="st">&quot;group&quot;</span> <span class="st">&quot;--registry&quot;</span> <span class="st">&quot;X&quot;</span> <span class="st">&quot;sub&quot;</span>] {<span class="at">:restrict</span> <span class="va">true</span>})</span>
580
+ <span id="cb40-6"><a href="#cb40-6" aria-hidden="true" tabindex="-1"></a><span class="co">;;=&gt; {:dispatch [&quot;group&quot; &quot;sub&quot;], :opts {:registry &quot;X&quot;}}</span></span>
581
+ <span id="cb40-7"><a href="#cb40-7" aria-hidden="true" tabindex="-1"></a></span>
582
+ <span id="cb40-8"><a href="#cb40-8" aria-hidden="true" tabindex="-1"></a>(cli/dispatch table [<span class="st">&quot;group&quot;</span> <span class="st">&quot;sub&quot;</span> <span class="st">&quot;--registry&quot;</span> <span class="st">&quot;X&quot;</span>] {<span class="at">:restrict</span> <span class="va">true</span>})</span>
583
+ <span id="cb40-9"><a href="#cb40-9" aria-hidden="true" tabindex="-1"></a><span class="co">;; throws: Unknown option: --registry</span></span></code></pre></div>
584
+ <p>Mark an option with <code>:inherit true</code> to also accept it at
585
+ any descendant level (after the subcommand). It is coerced and
586
+ restrict-checked wherever it appears:</p>
587
+ <div class="sourceCode" id="cb41"><pre
588
+ class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb41-1"><a href="#cb41-1" aria-hidden="true" tabindex="-1"></a>(<span class="bu">def</span><span class="fu"> table</span></span>
589
+ <span id="cb41-2"><a href="#cb41-2" aria-hidden="true" tabindex="-1"></a> [{<span class="at">:cmds</span> [<span class="st">&quot;group&quot;</span>] <span class="at">:spec</span> {<span class="at">:registry</span> {<span class="at">:inherit</span> <span class="va">true</span>}}}</span>
590
+ <span id="cb41-3"><a href="#cb41-3" aria-hidden="true" tabindex="-1"></a> {<span class="at">:cmds</span> [<span class="st">&quot;group&quot;</span> <span class="st">&quot;sub&quot;</span>] <span class="at">:fn</span> <span class="kw">identity</span> <span class="at">:spec</span> {<span class="at">:format</span> {}}}])</span>
591
+ <span id="cb41-4"><a href="#cb41-4" aria-hidden="true" tabindex="-1"></a></span>
592
+ <span id="cb41-5"><a href="#cb41-5" aria-hidden="true" tabindex="-1"></a>(cli/dispatch table [<span class="st">&quot;group&quot;</span> <span class="st">&quot;sub&quot;</span> <span class="st">&quot;--registry&quot;</span> <span class="st">&quot;X&quot;</span>] {<span class="at">:restrict</span> <span class="va">true</span>})</span>
593
+ <span id="cb41-6"><a href="#cb41-6" aria-hidden="true" tabindex="-1"></a><span class="co">;;=&gt; {:dispatch [&quot;group&quot; &quot;sub&quot;], :opts {:registry &quot;X&quot;}}</span></span></code></pre></div>
594
+ <p>A descendant may redefine it in its own spec, in which case the
595
+ descendant’s definition wins.</p>
596
+ <p>Instead of marking individual options, pass <code>:inherit</code> to
597
+ <code>dispatch</code>: <code>true</code> to inherit all options, or a
598
+ set of keys to inherit only those:</p>
599
+ <div class="sourceCode" id="cb42"><pre
600
+ class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb42-1"><a href="#cb42-1" aria-hidden="true" tabindex="-1"></a>(cli/dispatch table [<span class="st">&quot;group&quot;</span> <span class="st">&quot;sub&quot;</span> <span class="st">&quot;--registry&quot;</span> <span class="st">&quot;X&quot;</span>] {<span class="at">:inherit</span> <span class="va">true</span>})</span>
601
+ <span id="cb42-2"><a href="#cb42-2" aria-hidden="true" tabindex="-1"></a>(cli/dispatch table [<span class="st">&quot;group&quot;</span> <span class="st">&quot;sub&quot;</span> <span class="st">&quot;--registry&quot;</span> <span class="st">&quot;X&quot;</span>] {<span class="at">:inherit</span> #{<span class="at">:registry</span>}})</span></code></pre></div>
602
+ <h3 id="help">Help</h3>
603
+ <p>Pass <code>:help true</code> to <code>dispatch</code> (and
604
+ <code>:prog</code>, the program name) to add help to a CLI, no
605
+ <code>:restrict</code> needed:</p>
606
+ <div class="sourceCode" id="cb43"><pre
607
+ class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb43-1"><a href="#cb43-1" aria-hidden="true" tabindex="-1"></a>(cli/dispatch table args {<span class="at">:prog</span> <span class="st">&quot;example&quot;</span> <span class="at">:help</span> <span class="va">true</span>})</span></code></pre></div>
608
+ <ul>
609
+ <li><p><code>--help</code>/<code>-h</code> print help for the command in
610
+ front of them and return (the process ends with status 0). So
611
+ <code>example deps outdated --help</code> shows help for
612
+ <code>deps outdated</code>.</p></li>
613
+ <li><p>A mistyped or missing subcommand prints a terse message and exits
614
+ with 1.</p></li>
615
+ <li><p><code>-h, --help</code> is listed in each command’s options,
616
+ appended last. To control the order, give the command entry an
617
+ <code>:order</code> (a vector of option keys); it is used verbatim, so
618
+ you decide the order, which options to list, and whether to list
619
+ <code>--help</code> at all (omit <code>:help</code> to hide it; it still
620
+ works):</p>
621
+ <div class="sourceCode" id="cb44"><pre
622
+ class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb44-1"><a href="#cb44-1" aria-hidden="true" tabindex="-1"></a>{<span class="at">:cmds</span> [...] <span class="at">:spec</span> {...} <span class="at">:order</span> [<span class="at">:help</span> <span class="at">:port</span> <span class="at">:verbose</span>]} <span class="co">; --help first</span></span></code></pre></div>
623
+ <p>Without <code>:order</code>, the order is taken from the spec (a
624
+ vec-of-pairs spec keeps its order; a map follows its key order, which
625
+ Clojure does not guarantee), and <code>--help</code> is
626
+ appended.</p></li>
627
+ <li><p><code>--help</code>/<code>-h</code> are reserved while
628
+ <code>:help</code> is on (a command may still define its own
629
+ <code>:help</code>).</p></li>
630
+ <li><p>An entry’s <code>:epilog</code> (a string) is rendered verbatim
631
+ after that command’s options, for examples, notes or links. Put it on
632
+ the root entry (<code>:cmds []</code>) for the top-level help.</p></li>
633
+ </ul>
634
+ <p>It works for a single-command CLI too: a one-entry table whose
635
+ <code>:cmds</code> is <code>[]</code>. <code>example --help</code> then
636
+ shows Usage + Options:</p>
637
+ <div class="sourceCode" id="cb45"><pre
638
+ class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb45-1"><a href="#cb45-1" aria-hidden="true" tabindex="-1"></a>(cli/dispatch [{<span class="at">:cmds</span> [] <span class="at">:fn</span> run <span class="at">:spec</span> {<span class="at">:port</span> {<span class="at">:coerce</span> <span class="at">:long</span> <span class="at">:desc</span> <span class="st">&quot;Port&quot;</span>}}}]</span>
639
+ <span id="cb45-2"><a href="#cb45-2" aria-hidden="true" tabindex="-1"></a> args</span>
640
+ <span id="cb45-3"><a href="#cb45-3" aria-hidden="true" tabindex="-1"></a> {<span class="at">:prog</span> <span class="st">&quot;example&quot;</span> <span class="at">:help</span> <span class="va">true</span>})</span></code></pre></div>
641
+ <p><code>--help</code>/<code>-h</code> are a success path: they print
642
+ help and return (no exit call), so your <code>-main</code> ends and the
643
+ process exits 0, like a normal command. Errors go through the dynamic
644
+ <code>*exit-fn*</code>, which exits non-zero:</p>
645
+ <table>
646
+ <colgroup>
647
+ <col style="width: 50%" />
648
+ <col style="width: 50%" />
649
+ </colgroup>
650
+ <thead>
651
+ <tr class="header">
652
+ <th>invocation</th>
653
+ <th>outcome</th>
654
+ </tr>
655
+ </thead>
656
+ <tbody>
657
+ <tr class="odd">
658
+ <td><code>--help</code> / <code>-h</code></td>
659
+ <td>print help, return (status 0), no <code>*exit-fn*</code></td>
660
+ </tr>
661
+ <tr class="even">
662
+ <td>group, no subcommand</td>
663
+ <td>terse message, <code>*exit-fn*</code> exit 1,
664
+ <code>:cause :input-exhausted</code></td>
665
+ </tr>
666
+ <tr class="odd">
667
+ <td>unknown subcommand</td>
668
+ <td>terse message, <code>*exit-fn*</code> exit 1,
669
+ <code>:cause :no-match</code></td>
670
+ </tr>
671
+ <tr class="even">
672
+ <td>flag error</td>
673
+ <td>terse message, <code>*exit-fn*</code> exit 1, <code>:cause</code> =
674
+ the babashka.cli cause</td>
675
+ </tr>
676
+ </tbody>
677
+ </table>
678
+ <p>A bare group is a usage error (exit 1), like <code>git bisect</code>
679
+ with no subcommand; its full help is one keystroke away via
680
+ <code>--help</code>. <code>*exit-fn*</code> is called only on errors,
681
+ with <code>{:exit :cause :dispatch :data}</code> (<code>:cause</code> is
682
+ the dispatch cause: <code>:no-match</code>,
683
+ <code>:input-exhausted</code>, or a flag cause; <code>:data</code> holds
684
+ the raw <code>dispatch</code> error data). The default exits the process
685
+ (<code>System/exit</code> on JVM, <code>js/process.exit</code> on Node);
686
+ rebind it to not exit (tests, REPL) or to remap codes by
687
+ <code>:cause</code>:</p>
688
+ <div class="sourceCode" id="cb46"><pre
689
+ class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb46-1"><a href="#cb46-1" aria-hidden="true" tabindex="-1"></a><span class="co">;; treat a bare group as success (exit 0) instead of a usage error</span></span>
690
+ <span id="cb46-2"><a href="#cb46-2" aria-hidden="true" tabindex="-1"></a>(<span class="kw">binding</span> [cli/*exit-fn* (<span class="kw">fn</span> [{<span class="at">:keys</span> [exit cause]}]</span>
691
+ <span id="cb46-3"><a href="#cb46-3" aria-hidden="true" tabindex="-1"></a> (System/exit (<span class="kw">if</span> (<span class="kw">=</span> <span class="at">:input-exhausted</span> cause) <span class="dv">0</span> exit)))]</span>
692
+ <span id="cb46-4"><a href="#cb46-4" aria-hidden="true" tabindex="-1"></a> (cli/dispatch table args {<span class="at">:prog</span> <span class="st">&quot;example&quot;</span> <span class="at">:help</span> <span class="va">true</span>}))</span></code></pre></div>
693
+ <p>Both handlers are overridable: pass your own <code>:help-fn</code>
694
+ (called with <code>{:tree :dispatch :prog :inherit}</code>) and/or
695
+ <code>:error-fn</code> to <code>dispatch</code>. To render the standard
696
+ help and add to it, call <code>format-command-help</code>, the same
697
+ renderer the default uses:</p>
698
+ <div class="sourceCode" id="cb47"><pre
699
+ class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb47-1"><a href="#cb47-1" aria-hidden="true" tabindex="-1"></a>(cli/dispatch table args</span>
700
+ <span id="cb47-2"><a href="#cb47-2" aria-hidden="true" tabindex="-1"></a> {<span class="at">:prog</span> <span class="st">&quot;example&quot;</span> <span class="at">:help</span> <span class="va">true</span></span>
701
+ <span id="cb47-3"><a href="#cb47-3" aria-hidden="true" tabindex="-1"></a> <span class="at">:help-fn</span> (<span class="kw">fn</span> [{<span class="at">:keys</span> [tree dispatch prog inherit]}]</span>
702
+ <span id="cb47-4"><a href="#cb47-4" aria-hidden="true" tabindex="-1"></a> (<span class="kw">println</span> <span class="st">&quot;my-tool v1.2.3&quot;</span>)</span>
703
+ <span id="cb47-5"><a href="#cb47-5" aria-hidden="true" tabindex="-1"></a> (<span class="kw">println</span> (cli/format-command-help</span>
704
+ <span id="cb47-6"><a href="#cb47-6" aria-hidden="true" tabindex="-1"></a> {<span class="at">:table</span> tree <span class="at">:cmds</span> dispatch <span class="at">:prog</span> prog <span class="at">:inherit</span> inherit})))})</span></code></pre></div>
705
+ <p>The function <code>format-command-help</code> is also usable on its
706
+ own (without <code>dispatch</code>): pass <code>:table</code> (a
707
+ <code>dispatch</code> <a href="#tree-format">table or tree</a>),
708
+ <code>:cmds</code> (the command path, default <code>[]</code>),
709
+ <code>:prog</code>, and optional <code>:inherit</code>. It returns the
710
+ help string.</p>
711
+ <p>A custom <code>:error-fn</code> receives the dispatch error data
712
+ (<code>{:cause :dispatch :prog :inherit :tree :msg ...}</code>) and is
713
+ responsible for exiting (call <code>*exit-fn*</code> or exit yourself).
714
+ To keep the standard terse message and add to it, call
715
+ <code>format-command-error</code> (the same renderer the default uses)
716
+ and exit afterwards:</p>
717
+ <div class="sourceCode" id="cb48"><pre
718
+ class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb48-1"><a href="#cb48-1" aria-hidden="true" tabindex="-1"></a>(cli/dispatch table args</span>
719
+ <span id="cb48-2"><a href="#cb48-2" aria-hidden="true" tabindex="-1"></a> {<span class="at">:prog</span> <span class="st">&quot;example&quot;</span> <span class="at">:help</span> <span class="va">true</span></span>
720
+ <span id="cb48-3"><a href="#cb48-3" aria-hidden="true" tabindex="-1"></a> <span class="at">:error-fn</span> (<span class="kw">fn</span> [data]</span>
721
+ <span id="cb48-4"><a href="#cb48-4" aria-hidden="true" tabindex="-1"></a> (<span class="kw">println</span> (cli/format-command-error data))</span>
722
+ <span id="cb48-5"><a href="#cb48-5" aria-hidden="true" tabindex="-1"></a> (<span class="kw">println</span> <span class="st">&quot;See https://example.com/docs&quot;</span>)</span>
723
+ <span id="cb48-6"><a href="#cb48-6" aria-hidden="true" tabindex="-1"></a> (cli/*exit-fn* {<span class="at">:exit</span> <span class="dv">1</span> <span class="at">:cause</span> (<span class="at">:cause</span> data)}))})</span></code></pre></div>
724
+ <h2 id="completions">Completions</h2>
725
+ <p>The <code>dispatch</code> function can generate dynamic shell
726
+ completions for <code>bash</code>, <code>zsh</code>, <code>fish</code>,
727
+ <code>powershell</code> and <code>nushell</code>. Shells call back into
728
+ your program on each TAB to generate completions. The <code>:prog</code>
729
+ (program name) value is essential in the <code>dispatch</code> call. The
730
+ generated snippet registers completion for that name, so it must match
731
+ the command you type, and it must be a plain command name consisting of
732
+ only alphanumeric characters, <code>.</code>, <code>_</code> or
733
+ <code>-</code>.</p>
734
+ <div class="sourceCode" id="cb49"><pre
735
+ class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb49-1"><a href="#cb49-1" aria-hidden="true" tabindex="-1"></a>(cli/dispatch table args {<span class="at">:prog</span> <span class="st">&quot;mycli&quot;</span> <span class="at">:help</span> <span class="va">true</span>})</span></code></pre></div>
736
+ <p>If the installed command has a different name, e.g. when a distro
737
+ renames it, pass <code>--prog &lt;name&gt;</code> when generating the
738
+ snippet to register that name instead:</p>
739
+ <div class="sourceCode" id="cb50"><pre
740
+ class="sourceCode bash"><code class="sourceCode bash"><span id="cb50-1"><a href="#cb50-1" aria-hidden="true" tabindex="-1"></a><span class="ex">mycli</span> org.babashka.cli/completions snippet <span class="at">--shell</span> zsh <span class="at">--prog</span> sq</span></code></pre></div>
741
+ <p>The completions call goes through a hidden
742
+ <code>org.babashka.cli/completions</code> subcommand group that
743
+ <code>dispatch</code> adds for you. Running
744
+ <code>mycli org.babashka.cli/completions snippet --shell &lt;shell&gt;</code>
745
+ prints the install snippet for that specific shell to stdout. It does
746
+ not write files or edit your shell config for you.</p>
747
+ <p>Subcommands and options come with completion support out of the box.
748
+ Descriptions come from the same <code>:desc</code> (options) and
749
+ <code>:doc</code> (subcommands) you already write for
750
+ <code>--help</code>. A <code>:no-doc</code> subcommand or option is
751
+ hidden. Options that already appeared are filtered out of later
752
+ suggestions, except repeatable options
753
+ (e.g. <code>:coerce [:string]</code>).</p>
754
+ <p>Here follow the instructions to enable auto-completions in your
755
+ shell.</p>
756
+ <h3 id="bash">Bash</h3>
757
+ <p>Add this code to your bash init file:</p>
758
+ <div class="sourceCode" id="cb51"><pre
759
+ class="sourceCode bash"><code class="sourceCode bash"><span id="cb51-1"><a href="#cb51-1" aria-hidden="true" tabindex="-1"></a><span class="bu">source</span> <span class="op">&lt;(</span><span class="ex">mycli</span> org.babashka.cli/completions snippet <span class="at">--shell</span> bash<span class="op">)</span></span></code></pre></div>
760
+ <p>Bash completes values only and does not show descriptions. For
761
+ correct handling of <code>=</code> and <code>:</code> inside values,
762
+ install the bash-completion package, which needs bash 4.1 or newer. The
763
+ macOS system bash 3.2 still works for the common cases.</p>
764
+ <h3 id="zsh">Zsh</h3>
765
+ <p>Add this to your zsh init file, after <code>compinit</code>:</p>
766
+ <div class="sourceCode" id="cb52"><pre
767
+ class="sourceCode bash"><code class="sourceCode bash"><span id="cb52-1"><a href="#cb52-1" aria-hidden="true" tabindex="-1"></a><span class="bu">source</span> <span class="op">&lt;(</span><span class="ex">mycli</span> org.babashka.cli/completions snippet <span class="at">--shell</span> zsh<span class="op">)</span></span></code></pre></div>
768
+ <p>or save the output as <code>_mycli</code> on your
769
+ <code>$fpath</code>. Option and subcommand descriptions show inline.
770
+ Completions also fire when the program is invoked by path, such as
771
+ <code>./mycli</code>.</p>
772
+ <h3 id="fish">Fish</h3>
773
+ <pre class="fish"><code>mycli org.babashka.cli/completions snippet --shell fish | source</code></pre>
774
+ <p>Option and subcommand descriptions show inline. Completion also fires
775
+ on a path invocation.</p>
776
+ <h3 id="powershell">Powershell</h3>
777
+ <p>Add this to your <code>$PROFILE</code>:</p>
778
+ <div class="sourceCode" id="cb54"><pre
779
+ class="sourceCode powershell"><code class="sourceCode powershell"><span id="cb54-1"><a href="#cb54-1" aria-hidden="true" tabindex="-1"></a>mycli org<span class="op">.</span><span class="fu">babashka</span><span class="op">.</span><span class="fu">cli</span><span class="op">/</span>completions snippet <span class="op">--</span>shell powershell <span class="op">|</span> <span class="fu">Out-String</span> <span class="op">|</span> <span class="fu">Invoke-Expression</span></span></code></pre></div>
780
+ <p>Descriptions show in menu-completion mode, which you can enable with
781
+ <code>Set-PSReadLineKeyHandler -Key Tab -Function MenuComplete</code>.</p>
782
+ <h3 id="nushell">Nushell</h3>
783
+ <p>Nushell cannot <code>source</code> from a pipe, so save the snippet
784
+ to a file in the autoload directory and restart <code>nu</code>:</p>
785
+ <pre class="nu"><code>mkdir ($nu.user-autoload-dirs | first)
786
+ mycli org.babashka.cli/completions snippet --shell nushell | save -f (($nu.user-autoload-dirs | first) | path join &quot;mycli.nu&quot;)</code></pre>
787
+ <p>On nushell versions without autoload dirs, save it anywhere and add
788
+ <code>source &lt;literal path&gt;</code> to your <code>config.nu</code>.
789
+ Descriptions show in the completion menu.</p>
790
+ <p>Unlike the other shells, nushell has no per-command completion
791
+ registration. Instead, one global hook
792
+ (<code>$env.config.completions.external.completer</code>) handles TAB
793
+ for all external commands. The snippet does not overwrite a completer
794
+ you already have there: it saves the previous one and falls back to it
795
+ for every command other than <code>mycli</code>, so several tools can
796
+ install side by side.</p>
797
+ <h3 id="developing-completions">Developing completions</h3>
798
+ <p>Completions are registered for the command name <code>:prog</code>,
799
+ so the command you type must match it. During development you usually
800
+ invoke the build directly, e.g. <code>./run.clj</code>, under a
801
+ different name. Make the dev build callable under your
802
+ <code>:prog</code> name on <code>PATH</code>. On unix shells, symlink it
803
+ and prepend its directory:</p>
804
+ <div class="sourceCode" id="cb56"><pre
805
+ class="sourceCode bash"><code class="sourceCode bash"><span id="cb56-1"><a href="#cb56-1" aria-hidden="true" tabindex="-1"></a><span class="fu">ln</span> <span class="at">-sf</span> <span class="st">&quot;</span><span class="va">$PWD</span><span class="st">/run.clj&quot;</span> /tmp/mycli <span class="co"># name the link :prog</span></span>
806
+ <span id="cb56-2"><a href="#cb56-2" aria-hidden="true" tabindex="-1"></a><span class="bu">export</span> <span class="va">PATH</span><span class="op">=</span><span class="st">&quot;/tmp:</span><span class="va">$PATH</span><span class="st">&quot;</span> <span class="co"># bash and zsh</span></span></code></pre></div>
807
+ <p>In fish use <code>set -gx PATH /tmp $PATH</code>, in nushell
808
+ <code>$env.PATH = ($env.PATH | prepend /tmp)</code>. On Windows, put a
809
+ <code>mycli</code> wrapper script on your <code>PATH</code> instead of a
810
+ symlink.</p>
811
+ <p>Then source the snippet in the shell you are testing, using the
812
+ install command from its section above, and re-source it after each
813
+ change to your CLI so new commands and options show up.</p>
814
+ <p>Now <code>mycli &lt;TAB&gt;</code> completes commands and
815
+ <code>mycli sub --&lt;TAB&gt;</code> its options. To see the completer’s
816
+ raw output directly, without a shell, call the hidden subcommand
817
+ yourself. The tokens after <code>--</code> are what the shell would pass
818
+ on TAB, here the subcommand <code>sub</code> and a <code>--</code> to
819
+ complete its options. It prints one candidate per line, as the value, a
820
+ tab, then the description:</p>
821
+ <div class="sourceCode" id="cb57"><pre
822
+ class="sourceCode bash"><code class="sourceCode bash"><span id="cb57-1"><a href="#cb57-1" aria-hidden="true" tabindex="-1"></a><span class="ex">mycli</span> org.babashka.cli/completions complete <span class="at">--shell</span> zsh <span class="at">--</span> sub <span class="at">--</span></span></code></pre></div>
823
+ <h3 id="completing-option-values">Completing option values</h3>
824
+ <p>To complete an option’s value, give it one of:</p>
825
+ <ul>
826
+ <li><code>:complete</code> - a static collection of values (or
827
+ <code>{:value .. :description ..}</code> maps)</li>
828
+ <li>A set-valued <code>:validate</code>, whose members double as
829
+ completions</li>
830
+ <li><code>:complete-fn</code> - a function for dynamic completion</li>
831
+ </ul>
832
+ <div class="sourceCode" id="cb58"><pre
833
+ class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb58-1"><a href="#cb58-1" aria-hidden="true" tabindex="-1"></a>{<span class="at">:env</span> {<span class="at">:coerce</span> <span class="at">:string</span></span>
834
+ <span id="cb58-2"><a href="#cb58-2" aria-hidden="true" tabindex="-1"></a> <span class="at">:complete</span> [<span class="st">&quot;dev&quot;</span> <span class="st">&quot;staging&quot;</span> <span class="st">&quot;prod&quot;</span>]} <span class="co">; static list</span></span>
835
+ <span id="cb58-3"><a href="#cb58-3" aria-hidden="true" tabindex="-1"></a> <span class="at">:level</span> {<span class="at">:coerce</span> <span class="at">:keyword</span></span>
836
+ <span id="cb58-4"><a href="#cb58-4" aria-hidden="true" tabindex="-1"></a> <span class="at">:validate</span> #{<span class="at">:local</span> <span class="at">:global</span> <span class="at">:system</span>}} <span class="co">; reused as completions</span></span>
837
+ <span id="cb58-5"><a href="#cb58-5" aria-hidden="true" tabindex="-1"></a> <span class="at">:branch</span> {<span class="at">:coerce</span> <span class="at">:string</span></span>
838
+ <span id="cb58-6"><a href="#cb58-6" aria-hidden="true" tabindex="-1"></a> <span class="at">:complete-fn</span> (<span class="kw">fn</span> [{<span class="at">:keys</span> [to-complete opts]}] <span class="co">; dynamic</span></span>
839
+ <span id="cb58-7"><a href="#cb58-7" aria-hidden="true" tabindex="-1"></a> (git-branches to-complete))}}</span></code></pre></div>
840
+ <p>The <code>:complete-fn</code> is called with
841
+ <code>{:to-complete &lt;partial&gt; :opts &lt;opts parsed so far&gt; :option &lt;key&gt;}</code>
842
+ and returns values (strings) (or
843
+ <code>{:value .. :description ..}</code> maps). All three sources are
844
+ prefix-filtered against the partial value for you.</p>
845
+ <p>An option value with none of these defaults to the shell’s own file
846
+ completion. For a value where file suggestions are not appropriate, you
847
+ can opt out with <code>:complete false</code>.</p>
848
+ <p>Positional arguments mapped with <a
849
+ href="#args-opts"><code>:args-&gt;opts</code></a> complete in the same
850
+ way. A positional resolves to its spec key by position, so the same
851
+ <code>:complete</code>, <code>:complete-fn</code> or set
852
+ <code>:validate</code> on that key completes the positional too. With
853
+ <code>:args-&gt;opts [:env]</code> and
854
+ <code>:env {:complete ["dev" "prod"]}</code>,
855
+ <code>mycli deploy &lt;TAB&gt;</code> completes
856
+ <code>dev</code>/<code>prod</code>.</p>
857
+ <p>A positional declared in <code>:args-&gt;opts</code> with no value
858
+ completion defaults to the shell’s own file completion the same way. So
859
+ <code>:args-&gt;opts [:file]</code> with a bare <code>:file</code> makes
860
+ <code>mycli cat &lt;TAB&gt;</code> complete filenames and
861
+ <code>:complete false</code> opts out here too.</p>
862
+ <h2 id="adding-production-polish">Adding Production Polish</h2>
863
+ <p>Babashka cli lets you get up and running quickly. As you move toward
864
+ production quality, it’s helpful to let users know when their inputs are
865
+ invalid. Strict validation can be introduced with <a
866
+ href="#restrict">:restrict</a>, <a href="#require">:require</a>, and <a
867
+ href="#validate">:validate</a>.</p>
868
+ <p>As you add polish, you’ll likely make use of a <a
869
+ href="#spec">:spec</a>, a custom <a
870
+ href="#error-handling">:error_fn</a>, and maybe <a
871
+ href="#subcommands">subcommand dispatching</a>.</p>
872
+ <h2 id="restrict">Restrict</h2>
873
+ <p>Use the <code>:restrict</code> option to restrict options to only
874
+ those explicitly mentioned in configuration:</p>
875
+ <div class="sourceCode" id="cb59"><pre
876
+ class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb59-1"><a href="#cb59-1" aria-hidden="true" tabindex="-1"></a>(cli/parse-args [<span class="st">&quot;--foo&quot;</span>] {<span class="at">:spec</span> {<span class="at">:bar</span> {}} <span class="at">:restrict</span> <span class="va">true</span>})</span>
877
+ <span id="cb59-2"><a href="#cb59-2" aria-hidden="true" tabindex="-1"></a><span class="co">;;=&gt;</span></span>
878
+ <span id="cb59-3"><a href="#cb59-3" aria-hidden="true" tabindex="-1"></a>Execution error (ExceptionInfo) at babashka.cli/parse-opts (cli.cljc<span class="at">:357</span>).</span>
879
+ <span id="cb59-4"><a href="#cb59-4" aria-hidden="true" tabindex="-1"></a>Unknown option: <span class="at">:foo</span></span></code></pre></div>
880
+ <h2 id="require">Require</h2>
881
+ <p>Mark an option required in its <a href="#spec">spec</a> with
882
+ <code>:require true</code>; parsing throws when it is not present:</p>
883
+ <div class="sourceCode" id="cb60"><pre
884
+ class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb60-1"><a href="#cb60-1" aria-hidden="true" tabindex="-1"></a>(cli/parse-args [<span class="st">&quot;--foo&quot;</span>] {<span class="at">:spec</span> {<span class="at">:bar</span> {<span class="at">:require</span> <span class="va">true</span>}}})</span>
885
+ <span id="cb60-2"><a href="#cb60-2" aria-hidden="true" tabindex="-1"></a><span class="co">;;=&gt;</span></span>
886
+ <span id="cb60-3"><a href="#cb60-3" aria-hidden="true" tabindex="-1"></a>Execution error (ExceptionInfo) at babashka.cli/parse-opts (cli.cljc<span class="at">:363</span>).</span>
887
+ <span id="cb60-4"><a href="#cb60-4" aria-hidden="true" tabindex="-1"></a>Required option: <span class="at">:bar</span></span></code></pre></div>
888
+ <p>Required options are shown as <code>(required)</code> in
889
+ <code>--help</code>, in the slot a default would otherwise occupy.</p>
890
+ <h2 id="validate">Validate</h2>
891
+ <div class="sourceCode" id="cb61"><pre
892
+ class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb61-1"><a href="#cb61-1" aria-hidden="true" tabindex="-1"></a>(cli/parse-args [<span class="st">&quot;--foo&quot;</span> <span class="st">&quot;0&quot;</span>] {<span class="at">:spec</span> {<span class="at">:foo</span> {<span class="at">:validate</span> <span class="kw">pos?</span>}}})</span>
893
+ <span id="cb61-2"><a href="#cb61-2" aria-hidden="true" tabindex="-1"></a>Execution error (ExceptionInfo) at babashka.cli/parse-opts (cli.cljc<span class="at">:378</span>).</span>
894
+ <span id="cb61-3"><a href="#cb61-3" aria-hidden="true" tabindex="-1"></a>Invalid value <span class="kw">for</span> option <span class="at">:foo</span>: <span class="dv">0</span></span></code></pre></div>
895
+ <p>To gain more control over the error message, use <code>:pred</code>
896
+ and <code>:ex-msg</code>:</p>
897
+ <div class="sourceCode" id="cb62"><pre
898
+ class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb62-1"><a href="#cb62-1" aria-hidden="true" tabindex="-1"></a>(cli/parse-args [<span class="st">&quot;--foo&quot;</span> <span class="st">&quot;0&quot;</span>] {<span class="at">:spec</span> {<span class="at">:foo</span> {<span class="at">:validate</span> {<span class="at">:pred</span> <span class="kw">pos?</span> <span class="at">:ex-msg</span> (<span class="kw">fn</span> [m] (<span class="kw">str</span> <span class="st">&quot;Not a positive number: &quot;</span> (<span class="at">:value</span> m)))}}}})</span>
899
+ <span id="cb62-2"><a href="#cb62-2" aria-hidden="true" tabindex="-1"></a><span class="co">;;=&gt;</span></span>
900
+ <span id="cb62-3"><a href="#cb62-3" aria-hidden="true" tabindex="-1"></a>Execution error (ExceptionInfo) at babashka.cli/parse-opts (cli.cljc<span class="at">:378</span>).</span>
901
+ <span id="cb62-4"><a href="#cb62-4" aria-hidden="true" tabindex="-1"></a>Not a positive number: <span class="dv">0</span></span></code></pre></div>
902
+ <h2 id="error-handling">Error handling</h2>
903
+ <p>By default, an exception will be thrown in the following situations:
904
+ - A restricted option is encountered - A required option is missing -
905
+ Validation fails for an option - Coercion fails for an option</p>
906
+ <p>You may supply a custom error handler function with
907
+ <code>:error-fn</code>. The function will be called with a map
908
+ containing the following keys: - <code>:type</code> -
909
+ <code>:org.babashka/cli</code> (for filtering out other types of
910
+ errors). - <code>:cause</code> - one of: - <code>:restrict</code> - a
911
+ restricted option was encountered. - <code>:require</code> - a required
912
+ option was missing. - <code>:validate</code> - validation failed for an
913
+ option. - <code>:coerce</code> - coercion failed for an option. -
914
+ <code>:msg</code> - default error message. - <code>:option</code> - the
915
+ option being parsed when the error occurred. - <code>:spec</code> - the
916
+ spec passed into <code>parse-opts</code> (see the <a
917
+ href="#spec">Spec</a> section).</p>
918
+ <p>The following keys are present depending on <code>:cause</code>: -
919
+ <code>:cause :restrict</code> - <code>:restrict</code> - the value of
920
+ the <code>:restrict</code> opt to <code>parse-args</code> (see the <a
921
+ href="#restrict">Restrict</a> section). - <code>:cause :require</code> -
922
+ <code>:require</code> - the value of the <code>:require</code> opt to
923
+ <code>parse-args</code> (see the <a href="#require">Require</a>
924
+ section). - <code>:cause :validate</code> - <code>:value</code> - the
925
+ value of the option that failed validation. - <code>:validate</code> -
926
+ the value of the <code>:validate</code> opt to <code>parse-args</code>
927
+ (see the <a href="#validate">Validate</a> section). -
928
+ <code>:cause :coerce</code> - <code>:value</code> - the value of the
929
+ option that failed coercion.</p>
930
+ <p>By default, babashka cli will throw exception on errors it detects.
931
+ You can do the same from your custom error handler.</p>
932
+ <p>For a more polished user experience, you might choose to have your
933
+ custom error handler print the error and exit. For example:</p>
934
+ <div class="sourceCode" id="cb63"><pre
935
+ class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb63-1"><a href="#cb63-1" aria-hidden="true" tabindex="-1"></a>(cli/parse-opts</span>
936
+ <span id="cb63-2"><a href="#cb63-2" aria-hidden="true" tabindex="-1"></a> []</span>
937
+ <span id="cb63-3"><a href="#cb63-3" aria-hidden="true" tabindex="-1"></a> {<span class="at">:spec</span> {<span class="at">:foo</span> {<span class="at">:desc</span> <span class="st">&quot;You know what this is.&quot;</span></span>
938
+ <span id="cb63-4"><a href="#cb63-4" aria-hidden="true" tabindex="-1"></a> <span class="at">:ref</span> <span class="st">&quot;&lt;val&gt;&quot;</span></span>
939
+ <span id="cb63-5"><a href="#cb63-5" aria-hidden="true" tabindex="-1"></a> <span class="at">:require</span> <span class="va">true</span>}}</span>
940
+ <span id="cb63-6"><a href="#cb63-6" aria-hidden="true" tabindex="-1"></a> <span class="at">:error-fn</span></span>
941
+ <span id="cb63-7"><a href="#cb63-7" aria-hidden="true" tabindex="-1"></a> (<span class="kw">fn</span> [{<span class="at">:keys</span> [spec <span class="kw">type</span> cause msg option] <span class="at">:as</span> data}]</span>
942
+ <span id="cb63-8"><a href="#cb63-8" aria-hidden="true" tabindex="-1"></a> (<span class="kw">if</span> (<span class="kw">=</span> <span class="at">:org.babashka/cli</span> <span class="kw">type</span>)</span>
943
+ <span id="cb63-9"><a href="#cb63-9" aria-hidden="true" tabindex="-1"></a> (<span class="kw">case</span> cause</span>
944
+ <span id="cb63-10"><a href="#cb63-10" aria-hidden="true" tabindex="-1"></a> <span class="at">:require</span></span>
945
+ <span id="cb63-11"><a href="#cb63-11" aria-hidden="true" tabindex="-1"></a> (<span class="kw">println</span></span>
946
+ <span id="cb63-12"><a href="#cb63-12" aria-hidden="true" tabindex="-1"></a> (<span class="kw">format</span> <span class="st">&quot;Missing required argument:</span><span class="sc">\n</span><span class="st">%s&quot;</span></span>
947
+ <span id="cb63-13"><a href="#cb63-13" aria-hidden="true" tabindex="-1"></a> (cli/format-opts {<span class="at">:spec</span> (<span class="kw">select-keys</span> spec [option])})))</span>
948
+ <span id="cb63-14"><a href="#cb63-14" aria-hidden="true" tabindex="-1"></a> (<span class="kw">println</span> msg))</span>
949
+ <span id="cb63-15"><a href="#cb63-15" aria-hidden="true" tabindex="-1"></a> (<span class="kw">throw</span> (ex-info msg data)))</span>
950
+ <span id="cb63-16"><a href="#cb63-16" aria-hidden="true" tabindex="-1"></a> (System/exit <span class="dv">1</span>))})</span></code></pre></div>
951
+ <p>would print:</p>
952
+ <pre><code>Missing required argument:
953
+ --foo &lt;val&gt; You know what this is.</code></pre>
954
+ <p>You can also choose collect and then report all detected errors (see
955
+ <code>babashka.cli-test/error-fn-test</code> for an example of
956
+ this).</p>
957
+ <h2 id="adding-default-args">Adding default args</h2>
958
+ <p>You can supply default args with <code>:exec-args</code>:</p>
959
+ <div class="sourceCode" id="cb65"><pre
960
+ class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb65-1"><a href="#cb65-1" aria-hidden="true" tabindex="-1"></a>(cli/parse-args [<span class="st">&quot;--foo&quot;</span> <span class="st">&quot;0&quot;</span>] {<span class="at">:exec-args</span> {<span class="at">:bar</span> <span class="dv">1</span>}})</span>
961
+ <span id="cb65-2"><a href="#cb65-2" aria-hidden="true" tabindex="-1"></a><span class="co">;;=&gt; {:foo 0, :bar 1}</span></span></code></pre></div>
962
+ <p>Note that args specified in <code>args</code> will override defaults
963
+ in <code>:exec-args</code>:</p>
964
+ <div class="sourceCode" id="cb66"><pre
965
+ class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb66-1"><a href="#cb66-1" aria-hidden="true" tabindex="-1"></a>(cli/parse-args [<span class="st">&quot;--foo&quot;</span> <span class="st">&quot;0&quot;</span> <span class="st">&quot;--bar&quot;</span> <span class="st">&quot;42&quot;</span>] {<span class="at">:exec-args</span> {<span class="at">:bar</span> <span class="dv">1</span>}})</span>
966
+ <span id="cb66-2"><a href="#cb66-2" aria-hidden="true" tabindex="-1"></a><span class="co">;;=&gt; {:foo 0, :bar 42}</span></span></code></pre></div>
967
+ <h2 id="printing-options">Printing options</h2>
968
+ <p>Given a <a href="#spec">spec</a> (like the
969
+ <code>from</code>/<code>to</code>/<code>paths</code>/<code>pretty</code>
970
+ one above), print its options with <code>format-opts</code>:</p>
971
+ <div class="sourceCode" id="cb67"><pre
972
+ class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb67-1"><a href="#cb67-1" aria-hidden="true" tabindex="-1"></a>(<span class="kw">println</span> (cli/format-opts {<span class="at">:spec</span> spec <span class="at">:order</span> [<span class="at">:from</span> <span class="at">:to</span> <span class="at">:paths</span> <span class="at">:pretty</span>]}))</span></code></pre></div>
973
+ <p>This will print:</p>
974
+ <pre><code> -i, --from &lt;format&gt; The input format. &lt;format&gt; can be edn, json or transit. (default: edn)
975
+ -o, --to &lt;format&gt; The output format. &lt;format&gt; can be edn, json or transit. (default: json)
976
+ --paths Paths of files to transform. (default: src test)
977
+ -p, --pretty Pretty-print output.</code></pre>
978
+ <p>As options can often be re-used in multiple subcommands, you can
979
+ determine the order <em>and</em> selection of printed options with
980
+ <code>:order</code>. If you don’t want to use <code>:order</code> and
981
+ simply want to present the options as written, you can also use a vector
982
+ of vectors for the spec:</p>
983
+ <div class="sourceCode" id="cb69"><pre
984
+ class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb69-1"><a href="#cb69-1" aria-hidden="true" tabindex="-1"></a>[[<span class="at">:pretty</span> {<span class="at">:desc</span> <span class="st">&quot;Pretty-print output.&quot;</span></span>
985
+ <span id="cb69-2"><a href="#cb69-2" aria-hidden="true" tabindex="-1"></a> <span class="at">:alias</span> <span class="at">:p</span>}]</span>
986
+ <span id="cb69-3"><a href="#cb69-3" aria-hidden="true" tabindex="-1"></a> [<span class="at">:paths</span> {<span class="at">:desc</span> <span class="st">&quot;Paths of files to transform.&quot;</span></span>
987
+ <span id="cb69-4"><a href="#cb69-4" aria-hidden="true" tabindex="-1"></a> <span class="at">:coerce</span> []</span>
988
+ <span id="cb69-5"><a href="#cb69-5" aria-hidden="true" tabindex="-1"></a> <span class="at">:default</span> [<span class="st">&quot;src&quot;</span> <span class="st">&quot;test&quot;</span>]</span>
989
+ <span id="cb69-6"><a href="#cb69-6" aria-hidden="true" tabindex="-1"></a> <span class="at">:default-desc</span> <span class="st">&quot;src test&quot;</span>}]]</span></code></pre></div>
990
+ <p>If you need more flexibility, you can also use
991
+ <code>opts-&gt;table</code>, which turns a spec into a vector of
992
+ vectors, representing rows of a table. You can then
993
+ use<code>format-table</code> to produce a table as returned by
994
+ <code>format-opts</code>. For example to add a header row with labels
995
+ for each column, you could do something like:</p>
996
+ <div class="sourceCode" id="cb70"><pre
997
+ class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb70-1"><a href="#cb70-1" aria-hidden="true" tabindex="-1"></a>(cli/format-table</span>
998
+ <span id="cb70-2"><a href="#cb70-2" aria-hidden="true" tabindex="-1"></a> {<span class="at">:rows</span> (<span class="kw">concat</span> [[<span class="st">&quot;alias&quot;</span> <span class="st">&quot;option&quot;</span> <span class="st">&quot;ref&quot;</span> <span class="st">&quot;default&quot;</span> <span class="st">&quot;description&quot;</span>]]</span>
999
+ <span id="cb70-3"><a href="#cb70-3" aria-hidden="true" tabindex="-1"></a> (cli/opts-&gt;table</span>
1000
+ <span id="cb70-4"><a href="#cb70-4" aria-hidden="true" tabindex="-1"></a> {<span class="at">:spec</span> {<span class="at">:foo</span> {<span class="at">:alias</span> <span class="at">:f</span>, <span class="at">:default</span> <span class="st">&quot;yupyupyupyup&quot;</span>, <span class="at">:ref</span> <span class="st">&quot;&lt;foo&gt;&quot;</span></span>
1001
+ <span id="cb70-5"><a href="#cb70-5" aria-hidden="true" tabindex="-1"></a> <span class="at">:desc</span> <span class="st">&quot;Thingy&quot;</span>}</span>
1002
+ <span id="cb70-6"><a href="#cb70-6" aria-hidden="true" tabindex="-1"></a> <span class="at">:bar</span> {<span class="at">:alias</span> <span class="at">:b</span>, <span class="at">:default</span> <span class="st">&quot;sure&quot;</span>, <span class="at">:ref</span> <span class="st">&quot;&lt;bar&gt;&quot;</span></span>
1003
+ <span id="cb70-7"><a href="#cb70-7" aria-hidden="true" tabindex="-1"></a> <span class="at">:desc</span> <span class="st">&quot;Barbarbar&quot;</span> <span class="at">:default-desc</span> <span class="st">&quot;Mos def&quot;</span>}}}))</span>
1004
+ <span id="cb70-8"><a href="#cb70-8" aria-hidden="true" tabindex="-1"></a> <span class="at">:indent</span> <span class="dv">2</span>})</span></code></pre></div>
1005
+ <h3 id="terminal-width">Terminal width</h3>
1006
+ <p><code>format-opts</code> and <code>format-table</code> wrap long
1007
+ descriptions to the terminal width, aligning continuation lines under
1008
+ the description column:</p>
1009
+ <pre><code> --copy-resources &lt;resource&gt; Copy non cljs/cljc files from --paths as
1010
+ resources; a keyword matches by extension,
1011
+ otherwise by regex</code></pre>
1012
+ <p>On by default; <code>:wrap false</code> disables it.</p>
1013
+ <p>The width comes from <code>:max-width-fn</code>, a
1014
+ <code>(fn [cfg] -&gt; width)</code> defaulting to
1015
+ <code>cli/default-width-fn</code>: on node it reads
1016
+ <code>process.stdout.columns</code>; on the JVM it reads
1017
+ <code>$COLUMNS</code> then probes JLine (when on the classpath). Falls
1018
+ back to 80. Override per call:</p>
1019
+ <div class="sourceCode" id="cb72"><pre
1020
+ class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb72-1"><a href="#cb72-1" aria-hidden="true" tabindex="-1"></a>(cli/format-opts {<span class="at">:spec</span> spec <span class="at">:max-width-fn</span> (<span class="kw">constantly</span> <span class="dv">80</span>)})</span></code></pre></div>
1021
+ <p>On the JVM, <code>default-width-fn</code> reads the real width via
1022
+ JLine when it is on the classpath. babashka bundles it, so bb scripts
1023
+ get it for free; without JLine the width falls back to
1024
+ <code>$COLUMNS</code>/80. If you want real-width detection on another
1025
+ JVM, you can add a JLine provider (FFM is the lightest):</p>
1026
+ <div class="sourceCode" id="cb73"><pre
1027
+ class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb73-1"><a href="#cb73-1" aria-hidden="true" tabindex="-1"></a><span class="co">;; deps.edn</span></span>
1028
+ <span id="cb73-2"><a href="#cb73-2" aria-hidden="true" tabindex="-1"></a>org.jline/jline-terminal {<span class="at">:mvn/version</span> <span class="st">&quot;3.30.4&quot;</span>}</span>
1029
+ <span id="cb73-3"><a href="#cb73-3" aria-hidden="true" tabindex="-1"></a>org.jline/jline-terminal-ffm {<span class="at">:mvn/version</span> <span class="st">&quot;3.30.4&quot;</span>}</span></code></pre></div>
1030
+ <h2 id="babashka-tasks">Babashka tasks</h2>
1031
+ <p>For documentation on babashka tasks, go <a
1032
+ href="https://book.babashka.org/#tasks">here</a>.</p>
1033
+ <p>Since babashka <code>0.9.160</code>, <code>babashka.cli</code> has
1034
+ become a built-in and has better integration through <code>-x</code> and
1035
+ <code>exec</code>. Read about that in the <a
1036
+ href="https://book.babashka.org/#cli">babashka book</a>.</p>
1037
+ <h2 id="clojure-cli">Clojure CLI</h2>
1038
+ <p>You can control parsing behavior by adding
1039
+ <code>:org.babashka/cli</code> metadata to Clojure functions. It does
1040
+ not introduce a dependency on <code>babashka.cli</code> itself. Not
1041
+ adding any metadata will result in string values, which in many cases
1042
+ may already be a reasonable default.</p>
1043
+ <p>Adding support for this library will cause less friction with shell
1044
+ usage, especially on Windows since you need less quoting. You can
1045
+ support the same function for both <code>clojure -X</code> and
1046
+ <code>clojure -M</code> style invocations without writing extra
1047
+ boilerplate.</p>
1048
+ <p>In your <code>deps.edn</code> <code>:aliases</code> entry, add:</p>
1049
+ <div class="sourceCode" id="cb74"><pre
1050
+ class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb74-1"><a href="#cb74-1" aria-hidden="true" tabindex="-1"></a><span class="at">:exec</span> {<span class="at">:extra-deps</span> {org.babashka/cli {<span class="at">:mvn/version</span> <span class="st">&quot;&lt;latest-version&gt;&quot;</span>}}</span>
1051
+ <span id="cb74-2"><a href="#cb74-2" aria-hidden="true" tabindex="-1"></a> <span class="at">:main-opts</span> [<span class="st">&quot;-m&quot;</span> <span class="st">&quot;babashka.cli.exec&quot;</span>]}</span></code></pre></div>
1052
+ <p>Now you can call any function that accepts a map argument. E.g.:</p>
1053
+ <div class="sourceCode" id="cb75"><pre
1054
+ class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb75-1"><a href="#cb75-1" aria-hidden="true" tabindex="-1"></a>$ clojure -M<span class="at">:exec</span> clojure.core <span class="kw">prn</span> <span class="at">:a</span> <span class="dv">1</span> <span class="at">:b</span> <span class="dv">2</span></span>
1055
+ <span id="cb75-2"><a href="#cb75-2" aria-hidden="true" tabindex="-1"></a>{<span class="at">:a</span> <span class="st">&quot;1&quot;</span>, <span class="at">:b</span> <span class="st">&quot;2&quot;</span>}</span></code></pre></div>
1056
+ <p>Use <code>:org.babashka/cli</code> metadata for coercions:</p>
1057
+ <div class="sourceCode" id="cb76"><pre
1058
+ class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb76-1"><a href="#cb76-1" aria-hidden="true" tabindex="-1"></a>(<span class="kw">ns</span> my-ns)</span>
1059
+ <span id="cb76-2"><a href="#cb76-2" aria-hidden="true" tabindex="-1"></a></span>
1060
+ <span id="cb76-3"><a href="#cb76-3" aria-hidden="true" tabindex="-1"></a>(<span class="bu">defn</span><span class="fu"> foo</span></span>
1061
+ <span id="cb76-4"><a href="#cb76-4" aria-hidden="true" tabindex="-1"></a> {<span class="at">:org.babashka/cli</span> {<span class="at">:coerce</span> {<span class="at">:a</span> <span class="at">:symbol</span></span>
1062
+ <span id="cb76-5"><a href="#cb76-5" aria-hidden="true" tabindex="-1"></a> <span class="at">:b</span> <span class="at">:long</span>}}}</span>
1063
+ <span id="cb76-6"><a href="#cb76-6" aria-hidden="true" tabindex="-1"></a> <span class="co">;; map argument:</span></span>
1064
+ <span id="cb76-7"><a href="#cb76-7" aria-hidden="true" tabindex="-1"></a> [m]</span>
1065
+ <span id="cb76-8"><a href="#cb76-8" aria-hidden="true" tabindex="-1"></a> <span class="co">;; print map argument:</span></span>
1066
+ <span id="cb76-9"><a href="#cb76-9" aria-hidden="true" tabindex="-1"></a> (<span class="kw">prn</span> m))</span></code></pre></div>
1067
+ <div class="sourceCode" id="cb77"><pre
1068
+ class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb77-1"><a href="#cb77-1" aria-hidden="true" tabindex="-1"></a>$ clojure -M<span class="at">:exec</span> my-ns foo <span class="at">:a</span> foo/bar <span class="at">:b</span> <span class="dv">2</span> <span class="at">:c</span> vanilla</span>
1069
+ <span id="cb77-2"><a href="#cb77-2" aria-hidden="true" tabindex="-1"></a>{<span class="at">:a</span> foo/bar, <span class="at">:b</span> <span class="dv">2</span>, <span class="at">:c</span> <span class="st">&quot;vanilla&quot;</span>}</span></code></pre></div>
1070
+ <p>Note that any library can add support for babashka CLI without
1071
+ depending on babashka CLI.</p>
1072
+ <p>An example that specializes <code>babashka.cli</code> usage to a
1073
+ function:</p>
1074
+ <div class="sourceCode" id="cb78"><pre
1075
+ class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb78-1"><a href="#cb78-1" aria-hidden="true" tabindex="-1"></a><span class="at">:prn</span> {<span class="at">:extra-deps</span> {org.babashka/cli {<span class="at">:mvn/version</span> <span class="st">&quot;&lt;latest-version&gt;&quot;</span>}}</span>
1076
+ <span id="cb78-2"><a href="#cb78-2" aria-hidden="true" tabindex="-1"></a> <span class="at">:main-opts</span> [<span class="st">&quot;-m&quot;</span> <span class="st">&quot;babashka.cli.exec&quot;</span> <span class="st">&quot;clojure.core&quot;</span> <span class="st">&quot;prn&quot;</span>]}</span></code></pre></div>
1077
+ <div class="sourceCode" id="cb79"><pre
1078
+ class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb79-1"><a href="#cb79-1" aria-hidden="true" tabindex="-1"></a>$ clojure -M<span class="at">:prn</span> --foo=bar --baz</span>
1079
+ <span id="cb79-2"><a href="#cb79-2" aria-hidden="true" tabindex="-1"></a>{<span class="at">:foo</span> <span class="st">&quot;bar&quot;</span> <span class="at">:baz</span> <span class="va">true</span>}</span></code></pre></div>
1080
+ <p>You can also pre-define the exec function in
1081
+ <code>:exec-fn</code>:</p>
1082
+ <div class="sourceCode" id="cb80"><pre
1083
+ class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb80-1"><a href="#cb80-1" aria-hidden="true" tabindex="-1"></a><span class="at">:prn</span> {<span class="at">:extra-deps</span> {org.babashka/cli {<span class="at">:mvn/version</span> <span class="st">&quot;&lt;latest-version&gt;&quot;</span>}}</span>
1084
+ <span id="cb80-2"><a href="#cb80-2" aria-hidden="true" tabindex="-1"></a> <span class="at">:exec-fn</span> clojure.core/prn</span>
1085
+ <span id="cb80-3"><a href="#cb80-3" aria-hidden="true" tabindex="-1"></a> <span class="at">:main-opts</span> [<span class="st">&quot;-m&quot;</span> <span class="st">&quot;babashka.cli.exec&quot;</span>]}</span></code></pre></div>
1086
+ <p>To alter the parsing behavior of functions you don’t control, you can
1087
+ add <code>:org.babashka/cli</code> data in the <code>deps.edn</code>
1088
+ alias:</p>
1089
+ <div class="sourceCode" id="cb81"><pre
1090
+ class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb81-1"><a href="#cb81-1" aria-hidden="true" tabindex="-1"></a><span class="at">:prn</span> {<span class="at">:deps</span> {org.babashka/cli {<span class="at">:mvn/version</span> <span class="st">&quot;&lt;latest-version&gt;&quot;</span>}}</span>
1091
+ <span id="cb81-2"><a href="#cb81-2" aria-hidden="true" tabindex="-1"></a> <span class="at">:exec-fn</span> clojure.core/prn</span>
1092
+ <span id="cb81-3"><a href="#cb81-3" aria-hidden="true" tabindex="-1"></a> <span class="at">:main-opts</span> [<span class="st">&quot;-m&quot;</span> <span class="st">&quot;babashka.cli.exec&quot;</span>]</span>
1093
+ <span id="cb81-4"><a href="#cb81-4" aria-hidden="true" tabindex="-1"></a> <span class="at">:org.babashka/cli</span> {<span class="at">:coerce</span> {<span class="at">:foo</span> <span class="at">:long</span>}}}</span></code></pre></div>
1094
+ <div class="sourceCode" id="cb82"><pre
1095
+ class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb82-1"><a href="#cb82-1" aria-hidden="true" tabindex="-1"></a>$ clojure -M<span class="at">:prn</span> --foo=<span class="dv">1</span></span>
1096
+ <span id="cb82-2"><a href="#cb82-2" aria-hidden="true" tabindex="-1"></a>{<span class="at">:foo</span> <span class="dv">1</span>}</span></code></pre></div>
1097
+ <h3 id="antq"><a href="https://github.com/liquidz/antq">antq</a></h3>
1098
+ <p><code>.clojure/deps.edn</code> alias:</p>
1099
+ <div class="sourceCode" id="cb83"><pre
1100
+ class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb83-1"><a href="#cb83-1" aria-hidden="true" tabindex="-1"></a><span class="at">:antq</span> {<span class="at">:deps</span> {org.babashka/cli {<span class="at">:mvn/version</span> <span class="st">&quot;&lt;latest-version&gt;&quot;</span>}</span>
1101
+ <span id="cb83-2"><a href="#cb83-2" aria-hidden="true" tabindex="-1"></a> com.github.liquidz/antq {<span class="at">:mvn/version</span> <span class="st">&quot;1.7.798&quot;</span>}}</span>
1102
+ <span id="cb83-3"><a href="#cb83-3" aria-hidden="true" tabindex="-1"></a> <span class="at">:paths</span> []</span>
1103
+ <span id="cb83-4"><a href="#cb83-4" aria-hidden="true" tabindex="-1"></a> <span class="at">:main-opts</span> [<span class="st">&quot;-m&quot;</span> <span class="st">&quot;babashka.cli.exec&quot;</span> <span class="st">&quot;antq.tool&quot;</span> <span class="st">&quot;outdated&quot;</span>]</span>
1104
+ <span id="cb83-5"><a href="#cb83-5" aria-hidden="true" tabindex="-1"></a> <span class="at">:org.babashka/cli</span> {<span class="at">:coerce</span> {<span class="at">:skip</span> []}}}</span></code></pre></div>
1105
+ <p>On the command line you can now run it with:</p>
1106
+ <div class="sourceCode" id="cb84"><pre
1107
+ class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb84-1"><a href="#cb84-1" aria-hidden="true" tabindex="-1"></a>$ clj -M<span class="at">:antq</span> --upgrade</span></code></pre></div>
1108
+ <p>Note that we are calling the same <code>outdated</code> function that
1109
+ you normally call with <code>-T</code>:</p>
1110
+ <div class="sourceCode" id="cb85"><pre
1111
+ class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb85-1"><a href="#cb85-1" aria-hidden="true" tabindex="-1"></a>$ clj -Tantq outdated <span class="at">:upgrade</span> <span class="va">true</span></span></code></pre></div>
1112
+ <p>even though antq has its own <code>-main</code> function.</p>
1113
+ <p>Note that we added the
1114
+ <code>:org.babashka/cli {:coerce {:skip []}}</code> data in the alias to
1115
+ make sure that <code>--skip</code> options get collected into a
1116
+ vector:</p>
1117
+ <div class="sourceCode" id="cb86"><pre
1118
+ class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb86-1"><a href="#cb86-1" aria-hidden="true" tabindex="-1"></a>clj -M<span class="at">:antq</span> --upgrade --skip github-action</span></code></pre></div>
1119
+ <p>vs.</p>
1120
+ <div class="sourceCode" id="cb87"><pre
1121
+ class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb87-1"><a href="#cb87-1" aria-hidden="true" tabindex="-1"></a>clj -Tantq outdated <span class="at">:upgrade</span> <span class="va">true</span> <span class="at">:skip</span> &#39;[<span class="st">&quot;github-action&quot;</span>]&#39;</span></code></pre></div>
1122
+ <p>The following projects have added support for babashka CLI. Feel free
1123
+ to add a PR to list your project as well!</p>
1124
+ <h3 id="clj-new"><a
1125
+ href="https://github.com/seancorfield/clj-new#babashka-cli">clj-new</a></h3>
1126
+ <h3 id="codox"><a
1127
+ href="https://github.com/weavejester/codox">codox</a></h3>
1128
+ <p>In <code>deps.edn</code> create an alias:</p>
1129
+ <div class="sourceCode" id="cb88"><pre
1130
+ class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb88-1"><a href="#cb88-1" aria-hidden="true" tabindex="-1"></a><span class="at">:codox</span> {<span class="at">:extra-deps</span> {org.babashka/cli {<span class="at">:mvn/version</span> <span class="st">&quot;&lt;latest-version&gt;&quot;</span>}</span>
1131
+ <span id="cb88-2"><a href="#cb88-2" aria-hidden="true" tabindex="-1"></a> codox/codox {<span class="at">:mvn/version</span> <span class="st">&quot;0.10.8&quot;</span>}}</span>
1132
+ <span id="cb88-3"><a href="#cb88-3" aria-hidden="true" tabindex="-1"></a> <span class="at">:exec-fn</span> codox.main/generate-docs</span>
1133
+ <span id="cb88-4"><a href="#cb88-4" aria-hidden="true" tabindex="-1"></a> <span class="co">;; default arguments:</span></span>
1134
+ <span id="cb88-5"><a href="#cb88-5" aria-hidden="true" tabindex="-1"></a> <span class="at">:exec-args</span> {<span class="at">:source-paths</span> [<span class="st">&quot;src&quot;</span>]}</span>
1135
+ <span id="cb88-6"><a href="#cb88-6" aria-hidden="true" tabindex="-1"></a> <span class="at">:org.babashka/cli</span> {<span class="at">:coerce</span> {<span class="at">:source-paths</span> []</span>
1136
+ <span id="cb88-7"><a href="#cb88-7" aria-hidden="true" tabindex="-1"></a> <span class="at">:doc-paths</span> []</span>
1137
+ <span id="cb88-8"><a href="#cb88-8" aria-hidden="true" tabindex="-1"></a> <span class="at">:themes</span> [<span class="at">:keyword</span>]}}</span>
1138
+ <span id="cb88-9"><a href="#cb88-9" aria-hidden="true" tabindex="-1"></a> <span class="at">:main-opts</span> [<span class="st">&quot;-m&quot;</span> <span class="st">&quot;babashka.cli.exec&quot;</span>]}</span></code></pre></div>
1139
+ <p>CLI invocation:</p>
1140
+ <div class="sourceCode" id="cb89"><pre
1141
+ class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb89-1"><a href="#cb89-1" aria-hidden="true" tabindex="-1"></a>$ clojure -M<span class="at">:codox</span> --output-path /tmp/out</span></code></pre></div>
1142
+ <h3 id="deps-new"><a
1143
+ href="https://github.com/seancorfield/deps-new#babashka-cli">deps-new</a></h3>
1144
+ <h3 id="kaocha"><a
1145
+ href="https://github.com/lambdaisland/kaocha">kaocha</a></h3>
1146
+ <p>In <code>deps.edn</code> create an alias:</p>
1147
+ <div class="sourceCode" id="cb90"><pre
1148
+ class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb90-1"><a href="#cb90-1" aria-hidden="true" tabindex="-1"></a><span class="at">:kaocha</span> {<span class="at">:extra-deps</span> {org.babashka/cli {<span class="at">:mvn/version</span> <span class="st">&quot;&lt;latest-version&gt;&quot;</span>}</span>
1149
+ <span id="cb90-2"><a href="#cb90-2" aria-hidden="true" tabindex="-1"></a> lambdaisland/kaocha {<span class="at">:mvn/version</span> <span class="st">&quot;1.66.1034&quot;</span>}}</span>
1150
+ <span id="cb90-3"><a href="#cb90-3" aria-hidden="true" tabindex="-1"></a> <span class="at">:exec-fn</span> kaocha.runner/exec-fn</span>
1151
+ <span id="cb90-4"><a href="#cb90-4" aria-hidden="true" tabindex="-1"></a> <span class="at">:exec-args</span> {} <span class="co">;; insert default arguments here</span></span>
1152
+ <span id="cb90-5"><a href="#cb90-5" aria-hidden="true" tabindex="-1"></a> <span class="at">:org.babashka/cli</span> {<span class="at">:alias</span> {<span class="at">:watch</span> <span class="at">:watch</span>?</span>
1153
+ <span id="cb90-6"><a href="#cb90-6" aria-hidden="true" tabindex="-1"></a> <span class="at">:fail-fast</span> <span class="at">:fail-fast</span>?}</span>
1154
+ <span id="cb90-7"><a href="#cb90-7" aria-hidden="true" tabindex="-1"></a> <span class="at">:coerce</span> {<span class="at">:skip-meta</span> <span class="at">:keyword</span></span>
1155
+ <span id="cb90-8"><a href="#cb90-8" aria-hidden="true" tabindex="-1"></a> <span class="at">:kaocha/reporter</span> [<span class="at">:symbol</span>]}}</span>
1156
+ <span id="cb90-9"><a href="#cb90-9" aria-hidden="true" tabindex="-1"></a> <span class="at">:main-opts</span> [<span class="st">&quot;-m&quot;</span> <span class="st">&quot;babashka.cli.exec&quot;</span>]}</span></code></pre></div>
1157
+ <p>Now you are able to use kaocha’s exec-fn to be used as a CLI:</p>
1158
+ <div class="sourceCode" id="cb91"><pre
1159
+ class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb91-1"><a href="#cb91-1" aria-hidden="true" tabindex="-1"></a>$ clj -M<span class="at">:kaocha</span> --watch --fail-fast --kaocha/reporter kaocha.report/documentation</span></code></pre></div>
1160
+ <h3 id="quickdoc"><a
1161
+ href="https://github.com/borkdude/quickdoc#clojure-cli">quickdoc</a></h3>
1162
+ <h3 id="tools.build"><a
1163
+ href="https://github.com/clojure/tools.build">tools.build</a></h3>
1164
+ <p>In <code>deps.edn</code> create an alias:</p>
1165
+ <div class="sourceCode" id="cb92"><pre
1166
+ class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb92-1"><a href="#cb92-1" aria-hidden="true" tabindex="-1"></a><span class="at">:build</span> {<span class="at">:deps</span> {org.babashka/cli {<span class="at">:mvn/version</span> <span class="st">&quot;&lt;latest-version&gt;&quot;</span>}</span>
1167
+ <span id="cb92-2"><a href="#cb92-2" aria-hidden="true" tabindex="-1"></a> io.github.clojure/tools.build {<span class="at">:git/tag</span> <span class="st">&quot;v0.8.2&quot;</span> <span class="at">:git/sha</span> <span class="st">&quot;ba1a2bf&quot;</span>}}</span>
1168
+ <span id="cb92-3"><a href="#cb92-3" aria-hidden="true" tabindex="-1"></a> <span class="at">:paths</span> [<span class="st">&quot;.&quot;</span>]</span>
1169
+ <span id="cb92-4"><a href="#cb92-4" aria-hidden="true" tabindex="-1"></a> <span class="at">:ns-default</span> build</span>
1170
+ <span id="cb92-5"><a href="#cb92-5" aria-hidden="true" tabindex="-1"></a> <span class="at">:main-opts</span> [<span class="st">&quot;-m&quot;</span> <span class="st">&quot;babashka.cli.exec&quot;</span>]}</span></code></pre></div>
1171
+ <p>Now you can call your build functions as CLIs:</p>
1172
+ <div class="sourceCode" id="cb93"><pre
1173
+ class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb93-1"><a href="#cb93-1" aria-hidden="true" tabindex="-1"></a>clj -M<span class="at">:build</span> jar --verbose</span></code></pre></div>
1174
+ <h3 id="tools.deps.graph"><a
1175
+ href="https://github.com/clojure/tools.deps.graph">tools.deps.graph</a></h3>
1176
+ <p>In <code>deps.edn</code> create an alias:</p>
1177
+ <div class="sourceCode" id="cb94"><pre
1178
+ class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb94-1"><a href="#cb94-1" aria-hidden="true" tabindex="-1"></a><span class="at">:graph</span> {<span class="at">:deps</span> {org.babashka/cli {<span class="at">:mvn/version</span> <span class="st">&quot;&lt;latest-version&gt;&quot;</span>}</span>
1179
+ <span id="cb94-2"><a href="#cb94-2" aria-hidden="true" tabindex="-1"></a> org.clojure/tools.deps.graph {<span class="at">:mvn/version</span> <span class="st">&quot;1.1.68&quot;</span>}}</span>
1180
+ <span id="cb94-3"><a href="#cb94-3" aria-hidden="true" tabindex="-1"></a> <span class="at">:exec-fn</span> clojure.tools.deps.graph/graph</span>
1181
+ <span id="cb94-4"><a href="#cb94-4" aria-hidden="true" tabindex="-1"></a> <span class="at">:exec-args</span> {} <span class="co">;; insert default arguments here</span></span>
1182
+ <span id="cb94-5"><a href="#cb94-5" aria-hidden="true" tabindex="-1"></a> <span class="at">:org.babashka/cli</span> {<span class="at">:coerce</span> {<span class="at">:trace-omit</span> [<span class="at">:symbol</span>]}}</span>
1183
+ <span id="cb94-6"><a href="#cb94-6" aria-hidden="true" tabindex="-1"></a> <span class="at">:main-opts</span> [<span class="st">&quot;-m&quot;</span> <span class="st">&quot;babashka.cli.exec&quot;</span>]}</span></code></pre></div>
1184
+ <p>Then invoke on the command line:</p>
1185
+ <div class="sourceCode" id="cb95"><pre
1186
+ class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb95-1"><a href="#cb95-1" aria-hidden="true" tabindex="-1"></a>clj -M<span class="at">:graph</span> --size --output graph.png</span></code></pre></div>
1187
+ <h2 id="leiningen">Leiningen</h2>
1188
+ <p>This tool can be used to run clojure exec functions with <a
1189
+ href="https://leiningen.org/">lein</a>.</p>
1190
+ <p>An example with <code>clj-new</code>:</p>
1191
+ <p>In <code>~/.lein/profiles.clj</code> put:</p>
1192
+ <div class="sourceCode" id="cb96"><pre
1193
+ class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb96-1"><a href="#cb96-1" aria-hidden="true" tabindex="-1"></a>{<span class="at">:clj-1.11</span> {<span class="at">:dependencies</span> [[org.clojure/clojure <span class="st">&quot;1.11.1&quot;</span>]]}</span>
1194
+ <span id="cb96-2"><a href="#cb96-2" aria-hidden="true" tabindex="-1"></a> <span class="at">:clj-new</span> {<span class="at">:dependencies</span> [[org.babashka/cli <span class="st">&quot;&lt;latest-version&gt;&quot;</span>]</span>
1195
+ <span id="cb96-3"><a href="#cb96-3" aria-hidden="true" tabindex="-1"></a> [com.github.seancorfield/clj-new <span class="st">&quot;1.2.381&quot;</span>]]}</span>
1196
+ <span id="cb96-4"><a href="#cb96-4" aria-hidden="true" tabindex="-1"></a> <span class="at">:user</span> {<span class="at">:aliases</span> {<span class="st">&quot;clj-new&quot;</span> [<span class="st">&quot;with-profiles&quot;</span> <span class="st">&quot;+clj-1.11,+clj-new&quot;</span></span>
1197
+ <span id="cb96-5"><a href="#cb96-5" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;run&quot;</span> <span class="st">&quot;-m&quot;</span> <span class="st">&quot;babashka.cli.exec&quot;</span></span>
1198
+ <span id="cb96-6"><a href="#cb96-6" aria-hidden="true" tabindex="-1"></a> {<span class="at">:exec-args</span> {<span class="at">:env</span> {<span class="at">:description</span> <span class="st">&quot;My project&quot;</span>}}</span>
1199
+ <span id="cb96-7"><a href="#cb96-7" aria-hidden="true" tabindex="-1"></a> <span class="at">:coerce</span> {<span class="at">:verbose</span> <span class="at">:long</span></span>
1200
+ <span id="cb96-8"><a href="#cb96-8" aria-hidden="true" tabindex="-1"></a> <span class="at">:args</span> []}</span>
1201
+ <span id="cb96-9"><a href="#cb96-9" aria-hidden="true" tabindex="-1"></a> <span class="at">:alias</span> {<span class="at">:f</span> <span class="at">:force</span>}}</span>
1202
+ <span id="cb96-10"><a href="#cb96-10" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;clj-new&quot;</span>]}}}</span></code></pre></div>
1203
+ <p>After that you can use <code>lein clj-new app</code> to create a new
1204
+ app:</p>
1205
+ <div class="sourceCode" id="cb97"><pre
1206
+ class="sourceCode clojure"><code class="sourceCode clojure"><span id="cb97-1"><a href="#cb97-1" aria-hidden="true" tabindex="-1"></a>$ lein clj-new app --name foobar/baz --verbose <span class="dv">3</span> -f</span></code></pre></div>
1207
+ <!-- ## Future ideas -->
1208
+ <!-- ### Command line syntax for `:coerce` and `:collect` -->
1209
+ <!-- Perhaps this library can consider a command line syntax for `:coerce` and -->
1210
+ <!-- `:collect`, e.g.: -->
1211
+ <!-- ``` clojure -->
1212
+ <!-- $ clj -M:example --skip.0=github-actions --skip.1=clojure-cli -->
1213
+ <!-- ``` -->
1214
+ <!-- ``` clojure -->
1215
+ <!-- $ clj -M:example --lib%sym=org.babashka/cli -->
1216
+ <!-- ``` -->
1217
+ <!-- Things to look out for here is if the delimiter works well with bash / zsh / -->
1218
+ <!-- cmd.exe and Powershell. -->
1219
+ <!-- ### Merge args from a file -->
1220
+ <!-- Merge default arguments from a file so you don't have to write them on the command line: -->
1221
+ <!-- ``` clojure -->
1222
+ <!-- --org.babashka/cli-defaults=foo.edn -->
1223
+ <!-- ``` -->
1224
+ <h2 id="license">License</h2>
1225
+ <p>Copyright © 2022-2026 Michiel Borkent</p>
1226
+ <p>Distributed under the MIT License. See LICENSE.</p>
1227
+
1228
+ </body>
1229
+ </html>