@wooksjs/event-cli 0.6.1 → 0.6.3
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.md +24 -0
- package/dist/index.cjs +22 -6
- package/dist/index.d.ts +29 -6
- package/dist/index.mjs +22 -6
- package/package.json +46 -37
- package/scripts/setup-skills.js +70 -0
- package/skills/wooksjs-event-cli/SKILL.md +35 -0
- package/skills/wooksjs-event-cli/commands.md +541 -0
- package/skills/wooksjs-event-cli/core.md +353 -0
- package/skills/wooksjs-event-cli/event-core.md +562 -0
|
@@ -0,0 +1,541 @@
|
|
|
1
|
+
# Commands & Help — @wooksjs/event-cli
|
|
2
|
+
|
|
3
|
+
> Covers command registration, command path syntax (arguments, aliases, options, examples), flag/option composables, help generation, and route parameters in CLI context.
|
|
4
|
+
|
|
5
|
+
## Command Registration
|
|
6
|
+
|
|
7
|
+
### `app.cli(path, handler)`
|
|
8
|
+
|
|
9
|
+
Register a command with a simple handler:
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
import { createCliApp } from '@wooksjs/event-cli'
|
|
13
|
+
|
|
14
|
+
const app = createCliApp()
|
|
15
|
+
|
|
16
|
+
app.cli('deploy', () => {
|
|
17
|
+
return 'Deploying...'
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
app.run()
|
|
21
|
+
// $ mycli deploy → "Deploying..."
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### `app.cli(path, options)`
|
|
25
|
+
|
|
26
|
+
Register a command with full metadata for help generation:
|
|
27
|
+
|
|
28
|
+
```ts
|
|
29
|
+
app.cli('deploy/:env', {
|
|
30
|
+
description: 'Deploy to a target environment',
|
|
31
|
+
args: {
|
|
32
|
+
env: 'Target environment (staging, production)',
|
|
33
|
+
},
|
|
34
|
+
options: [
|
|
35
|
+
{ keys: ['force', 'f'], description: 'Skip confirmation prompt' },
|
|
36
|
+
{ keys: ['tag', 't'], description: 'Docker image tag', value: 'latest' },
|
|
37
|
+
],
|
|
38
|
+
aliases: ['d'],
|
|
39
|
+
examples: [
|
|
40
|
+
{
|
|
41
|
+
description: 'Deploy to production with a specific tag',
|
|
42
|
+
cmd: 'production -t=v2.1.0',
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
handler: () => {
|
|
46
|
+
const { get } = useRouteParams<{ env: string }>()
|
|
47
|
+
const flags = useCliOptions()
|
|
48
|
+
return `Deploying to ${get('env')} (force=${flags.force || false})`
|
|
49
|
+
},
|
|
50
|
+
})
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Options object shape
|
|
54
|
+
|
|
55
|
+
```ts
|
|
56
|
+
interface TWooksCliEntry<T> {
|
|
57
|
+
handler: TWooksHandler<T> // the command handler function
|
|
58
|
+
description?: string // command description for help text
|
|
59
|
+
args?: Record<string, string> // { argName: 'description' }
|
|
60
|
+
options?: Array<{
|
|
61
|
+
keys: string[] // ['verbose', 'v'] → --verbose or -v
|
|
62
|
+
description?: string // option description for help
|
|
63
|
+
value?: string // default/example value shown in help
|
|
64
|
+
}>
|
|
65
|
+
aliases?: string[] // alternative command names
|
|
66
|
+
examples?: Array<{
|
|
67
|
+
description: string // example description
|
|
68
|
+
cmd: string // example command (without CLI name)
|
|
69
|
+
}>
|
|
70
|
+
onRegister?: (path: string, aliasType: number, route?: any) => void
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Command Path Syntax
|
|
75
|
+
|
|
76
|
+
Command paths use the same `@prostojs/router` syntax as HTTP routes. Space and `/` separators are equivalent.
|
|
77
|
+
|
|
78
|
+
### Static commands
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
app.cli('deploy', handler) // $ mycli deploy
|
|
82
|
+
app.cli('db migrate', handler) // $ mycli db migrate
|
|
83
|
+
app.cli('db/migrate', handler) // same as above
|
|
84
|
+
app.cli('config set', handler) // $ mycli config set
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Named arguments (`:argName`)
|
|
88
|
+
|
|
89
|
+
Captures a single positional argument:
|
|
90
|
+
|
|
91
|
+
```ts
|
|
92
|
+
import { useRouteParams } from '@wooksjs/event-core'
|
|
93
|
+
|
|
94
|
+
app.cli('greet/:name', {
|
|
95
|
+
args: { name: 'Person to greet' },
|
|
96
|
+
handler: () => {
|
|
97
|
+
const { get } = useRouteParams<{ name: string }>()
|
|
98
|
+
return `Hello, ${get('name')}!`
|
|
99
|
+
},
|
|
100
|
+
})
|
|
101
|
+
// $ mycli greet Alice → "Hello, Alice!"
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Multiple arguments
|
|
105
|
+
|
|
106
|
+
```ts
|
|
107
|
+
app.cli('copy/:source/:dest', {
|
|
108
|
+
args: {
|
|
109
|
+
source: 'Source file path',
|
|
110
|
+
dest: 'Destination file path',
|
|
111
|
+
},
|
|
112
|
+
handler: () => {
|
|
113
|
+
const { params } = useRouteParams<{ source: string; dest: string }>()
|
|
114
|
+
return `Copying ${params.source} to ${params.dest}`
|
|
115
|
+
},
|
|
116
|
+
})
|
|
117
|
+
// $ mycli copy file.txt backup/file.txt
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Optional arguments (`:arg?`)
|
|
121
|
+
|
|
122
|
+
Append `?` to make an argument optional. Optional args must be at the end:
|
|
123
|
+
|
|
124
|
+
```ts
|
|
125
|
+
app.cli('logs/:service/:lines?', {
|
|
126
|
+
args: {
|
|
127
|
+
service: 'Service name',
|
|
128
|
+
lines: 'Number of lines (default: 50)',
|
|
129
|
+
},
|
|
130
|
+
handler: () => {
|
|
131
|
+
const { get } = useRouteParams<{ service: string; lines?: string }>()
|
|
132
|
+
const lines = get('lines') || '50'
|
|
133
|
+
return `Showing last ${lines} lines of ${get('service')}`
|
|
134
|
+
},
|
|
135
|
+
})
|
|
136
|
+
// $ mycli logs api → "Showing last 50 lines of api"
|
|
137
|
+
// $ mycli logs api 100 → "Showing last 100 lines of api"
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Wildcards (`*`)
|
|
141
|
+
|
|
142
|
+
Capture arbitrary remaining arguments:
|
|
143
|
+
|
|
144
|
+
```ts
|
|
145
|
+
app.cli('exec/*', {
|
|
146
|
+
handler: () => {
|
|
147
|
+
const { get } = useRouteParams<{ '*': string }>()
|
|
148
|
+
return `Running: ${get('*')}`
|
|
149
|
+
},
|
|
150
|
+
})
|
|
151
|
+
// $ mycli exec npm install → "Running: npm/install"
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Regex-constrained arguments
|
|
155
|
+
|
|
156
|
+
Restrict what an argument matches:
|
|
157
|
+
|
|
158
|
+
```ts
|
|
159
|
+
app.cli('migrate/:version(\\d+)', {
|
|
160
|
+
args: { version: 'Migration version number' },
|
|
161
|
+
handler: () => {
|
|
162
|
+
const { get } = useRouteParams<{ version: string }>()
|
|
163
|
+
return `Migrating to version ${get('version')}`
|
|
164
|
+
},
|
|
165
|
+
})
|
|
166
|
+
// $ mycli migrate 42 → matches
|
|
167
|
+
// $ mycli migrate abc → does NOT match (unknown command)
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### Hyphen-separated arguments
|
|
171
|
+
|
|
172
|
+
```ts
|
|
173
|
+
app.cli('schedule/:from-:to', {
|
|
174
|
+
handler: () => {
|
|
175
|
+
const { get } = useRouteParams<{ from: string; to: string }>()
|
|
176
|
+
return `Scheduled from ${get('from')} to ${get('to')}`
|
|
177
|
+
},
|
|
178
|
+
})
|
|
179
|
+
// $ mycli schedule 09:00-17:00
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
## Command Aliases
|
|
183
|
+
|
|
184
|
+
Aliases register additional paths that invoke the same handler:
|
|
185
|
+
|
|
186
|
+
```ts
|
|
187
|
+
app.cli('deploy/:env', {
|
|
188
|
+
aliases: ['d'],
|
|
189
|
+
args: { env: 'Target environment' },
|
|
190
|
+
handler: () => {
|
|
191
|
+
const { get } = useRouteParams<{ env: string }>()
|
|
192
|
+
return `Deploying to ${get('env')}`
|
|
193
|
+
},
|
|
194
|
+
})
|
|
195
|
+
// $ mycli deploy staging → works
|
|
196
|
+
// $ mycli d staging → also works (alias)
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
Aliases automatically inherit the command's arguments. The alias `'d'` becomes `'d/:env'`.
|
|
200
|
+
|
|
201
|
+
## Accessing Flags and Options
|
|
202
|
+
|
|
203
|
+
### `useCliOptions()`
|
|
204
|
+
|
|
205
|
+
Returns all parsed flags as an object (uses `minimist` under the hood):
|
|
206
|
+
|
|
207
|
+
```ts
|
|
208
|
+
import { useCliOptions } from '@wooksjs/event-cli'
|
|
209
|
+
|
|
210
|
+
app.cli('build', () => {
|
|
211
|
+
const flags = useCliOptions()
|
|
212
|
+
// $ mycli build --env production --verbose -p 8080
|
|
213
|
+
// flags = { _: ['build'], env: 'production', verbose: true, p: 8080 }
|
|
214
|
+
return `Building for ${flags.env}`
|
|
215
|
+
})
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
The `_` property contains all positional arguments (including command segments).
|
|
219
|
+
|
|
220
|
+
### `useCliOption(name)`
|
|
221
|
+
|
|
222
|
+
Get a single option value. Resolves aliases — if you defined `keys: ['verbose', 'v']`, calling `useCliOption('verbose')` also checks `-v`:
|
|
223
|
+
|
|
224
|
+
```ts
|
|
225
|
+
import { useCliOption } from '@wooksjs/event-cli'
|
|
226
|
+
|
|
227
|
+
app.cli('build', {
|
|
228
|
+
options: [
|
|
229
|
+
{ keys: ['env', 'e'], description: 'Target environment', value: 'dev' },
|
|
230
|
+
{ keys: ['verbose', 'v'], description: 'Verbose output' },
|
|
231
|
+
],
|
|
232
|
+
handler: () => {
|
|
233
|
+
const env = useCliOption('env') // checks --env and -e
|
|
234
|
+
const verbose = useCliOption('verbose') // checks --verbose and -v
|
|
235
|
+
return `Building for ${env || 'dev'} (verbose: ${!!verbose})`
|
|
236
|
+
},
|
|
237
|
+
})
|
|
238
|
+
// $ mycli build -e production -v
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### Minimist parsing options
|
|
242
|
+
|
|
243
|
+
Control how flags are parsed by passing options to `app.run()`:
|
|
244
|
+
|
|
245
|
+
```ts
|
|
246
|
+
// Ensure -A is parsed as boolean (true/false), not string
|
|
247
|
+
await app.run(['build', '-cA'], { boolean: ['A'] })
|
|
248
|
+
|
|
249
|
+
// Negate boolean flags with --no- prefix
|
|
250
|
+
await app.run(['build', '--no-cache'], { boolean: ['cache'] })
|
|
251
|
+
// → { cache: false }
|
|
252
|
+
|
|
253
|
+
// Default values
|
|
254
|
+
await app.run(['build'], { default: { env: 'development' } })
|
|
255
|
+
// → { env: 'development' }
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
## Help System
|
|
259
|
+
|
|
260
|
+
### Registering help metadata
|
|
261
|
+
|
|
262
|
+
Help metadata is defined alongside the command:
|
|
263
|
+
|
|
264
|
+
```ts
|
|
265
|
+
app.cli('db migrate/:direction?', {
|
|
266
|
+
description: 'Run database migrations',
|
|
267
|
+
args: {
|
|
268
|
+
direction: 'Migration direction: up or down (default: up)',
|
|
269
|
+
},
|
|
270
|
+
options: [
|
|
271
|
+
{ keys: ['seed', 's'], description: 'Run seeds after migration' },
|
|
272
|
+
{ keys: ['dry-run'], description: 'Preview changes without applying' },
|
|
273
|
+
],
|
|
274
|
+
examples: [
|
|
275
|
+
{ description: 'Run all pending migrations', cmd: 'up' },
|
|
276
|
+
{ description: 'Rollback last migration', cmd: 'down' },
|
|
277
|
+
{ description: 'Preview migration with seeding', cmd: 'up -s --dry-run' },
|
|
278
|
+
],
|
|
279
|
+
handler: () => { /* ... */ },
|
|
280
|
+
})
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
### `useCliHelp()`
|
|
284
|
+
|
|
285
|
+
Access the help system from within a handler:
|
|
286
|
+
|
|
287
|
+
```ts
|
|
288
|
+
import { useCliHelp } from '@wooksjs/event-cli'
|
|
289
|
+
|
|
290
|
+
app.cli('help', () => {
|
|
291
|
+
const { print, render, getEntry, getCliHelp } = useCliHelp()
|
|
292
|
+
|
|
293
|
+
print(true) // print help to stdout (with colors)
|
|
294
|
+
print(false) // print without colors
|
|
295
|
+
|
|
296
|
+
const lines = render(80, true) // render as string array (width, colors)
|
|
297
|
+
|
|
298
|
+
const entry = getEntry() // get the matched help entry
|
|
299
|
+
// entry.description, entry.options, entry.args, entry.examples
|
|
300
|
+
|
|
301
|
+
const cliHelp = getCliHelp() // access the CliHelpRenderer directly
|
|
302
|
+
})
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
### `useAutoHelp(keys?, colors?)`
|
|
306
|
+
|
|
307
|
+
Automatically prints help when `--help` is passed. Returns `true` if help was printed:
|
|
308
|
+
|
|
309
|
+
```ts
|
|
310
|
+
import { useAutoHelp } from '@wooksjs/event-cli'
|
|
311
|
+
|
|
312
|
+
app.cli('deploy/:env', {
|
|
313
|
+
description: 'Deploy to environment',
|
|
314
|
+
handler: () => {
|
|
315
|
+
if (useAutoHelp()) return // prints help and returns if --help was passed
|
|
316
|
+
|
|
317
|
+
// Normal handler logic
|
|
318
|
+
const { get } = useRouteParams<{ env: string }>()
|
|
319
|
+
return `Deploying to ${get('env')}`
|
|
320
|
+
},
|
|
321
|
+
})
|
|
322
|
+
// $ mycli deploy --help → prints formatted help
|
|
323
|
+
// $ mycli deploy staging → "Deploying to staging"
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
Customize the trigger keys and color setting:
|
|
327
|
+
|
|
328
|
+
```ts
|
|
329
|
+
// Trigger on --help or -h, without colors
|
|
330
|
+
if (useAutoHelp(['help', 'h'], false)) {
|
|
331
|
+
process.exit(0)
|
|
332
|
+
}
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
### `useCommandLookupHelp(lookupDepth?)`
|
|
336
|
+
|
|
337
|
+
Searches for similar valid commands when a wrong command is entered. Throws an error with suggestions if found. Best used in `onUnknownCommand`:
|
|
338
|
+
|
|
339
|
+
```ts
|
|
340
|
+
import { createCliApp, useCommandLookupHelp } from '@wooksjs/event-cli'
|
|
341
|
+
|
|
342
|
+
const app = createCliApp({
|
|
343
|
+
onUnknownCommand: (path, raiseError) => {
|
|
344
|
+
useCommandLookupHelp() // throws with suggestions
|
|
345
|
+
raiseError() // fallback if no suggestions found
|
|
346
|
+
},
|
|
347
|
+
})
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
The lookup works backwards through the command path:
|
|
351
|
+
|
|
352
|
+
```
|
|
353
|
+
For command "run test:drive dir":
|
|
354
|
+
lookup 1: "run test:drive dir" (depth 0)
|
|
355
|
+
lookup 2: "run test:drive" (depth 1)
|
|
356
|
+
lookup 3: "run test" (depth 2)
|
|
357
|
+
lookup 4: "run" (depth 3)
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
Default `lookupDepth` is 3. If a partial match with children is found, it suggests child commands. If arguments are expected, it says what arguments are missing.
|
|
361
|
+
|
|
362
|
+
### Help Renderer Configuration
|
|
363
|
+
|
|
364
|
+
Customize the help renderer when creating the app:
|
|
365
|
+
|
|
366
|
+
```ts
|
|
367
|
+
import { CliHelpRenderer } from '@prostojs/cli-help'
|
|
368
|
+
|
|
369
|
+
// Option 1: Pass options
|
|
370
|
+
const app = createCliApp({
|
|
371
|
+
cliHelp: {
|
|
372
|
+
name: 'mycli', // CLI name shown in help and examples
|
|
373
|
+
// marks, width, etc.
|
|
374
|
+
},
|
|
375
|
+
})
|
|
376
|
+
|
|
377
|
+
// Option 2: Pass a pre-configured renderer
|
|
378
|
+
const renderer = new CliHelpRenderer({ name: 'mycli' })
|
|
379
|
+
const app = createCliApp({ cliHelp: renderer })
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
## Route Parameters in CLI
|
|
383
|
+
|
|
384
|
+
CLI commands use the same `useRouteParams()` from `@wooksjs/event-core` as HTTP routes:
|
|
385
|
+
|
|
386
|
+
```ts
|
|
387
|
+
import { useRouteParams } from '@wooksjs/event-core'
|
|
388
|
+
|
|
389
|
+
app.cli('user/:action/:id?', {
|
|
390
|
+
handler: () => {
|
|
391
|
+
const { params, get } = useRouteParams<{
|
|
392
|
+
action: string
|
|
393
|
+
id?: string
|
|
394
|
+
}>()
|
|
395
|
+
|
|
396
|
+
get('action') // 'create', 'delete', etc.
|
|
397
|
+
get('id') // '42' or undefined
|
|
398
|
+
params // { action: 'create', id: '42' }
|
|
399
|
+
},
|
|
400
|
+
})
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
Parameters are always `string` (or `string[]` for repeated params). Cast numerics yourself: `Number(get('id'))`.
|
|
404
|
+
|
|
405
|
+
## Common Patterns
|
|
406
|
+
|
|
407
|
+
### Pattern: Multi-command CLI with help
|
|
408
|
+
|
|
409
|
+
```ts
|
|
410
|
+
const app = createCliApp({
|
|
411
|
+
onUnknownCommand: (path, raiseError) => {
|
|
412
|
+
useCommandLookupHelp()
|
|
413
|
+
raiseError()
|
|
414
|
+
},
|
|
415
|
+
})
|
|
416
|
+
|
|
417
|
+
app.cli('init/:name?', {
|
|
418
|
+
description: 'Initialize a new project',
|
|
419
|
+
args: { name: 'Project name (default: current directory)' },
|
|
420
|
+
options: [
|
|
421
|
+
{ keys: ['template', 't'], description: 'Project template', value: 'default' },
|
|
422
|
+
],
|
|
423
|
+
handler: () => {
|
|
424
|
+
if (useAutoHelp()) return
|
|
425
|
+
const { get } = useRouteParams<{ name?: string }>()
|
|
426
|
+
const template = useCliOption('template') || 'default'
|
|
427
|
+
return `Initialized ${get('name') || '.'} with template ${template}`
|
|
428
|
+
},
|
|
429
|
+
})
|
|
430
|
+
|
|
431
|
+
app.cli('build', {
|
|
432
|
+
description: 'Build the project',
|
|
433
|
+
options: [
|
|
434
|
+
{ keys: ['watch', 'w'], description: 'Watch mode' },
|
|
435
|
+
{ keys: ['minify', 'm'], description: 'Minify output' },
|
|
436
|
+
],
|
|
437
|
+
handler: () => {
|
|
438
|
+
if (useAutoHelp()) return
|
|
439
|
+
const flags = useCliOptions()
|
|
440
|
+
return `Building... (watch=${!!flags.watch}, minify=${!!flags.minify})`
|
|
441
|
+
},
|
|
442
|
+
})
|
|
443
|
+
|
|
444
|
+
app.cli('serve', {
|
|
445
|
+
description: 'Start development server',
|
|
446
|
+
options: [
|
|
447
|
+
{ keys: ['port', 'p'], description: 'Port number', value: '3000' },
|
|
448
|
+
{ keys: ['host', 'h'], description: 'Host address', value: 'localhost' },
|
|
449
|
+
],
|
|
450
|
+
handler: () => {
|
|
451
|
+
if (useAutoHelp()) return
|
|
452
|
+
const port = useCliOption('port') || '3000'
|
|
453
|
+
const host = useCliOption('host') || 'localhost'
|
|
454
|
+
return `Serving at http://${host}:${port}`
|
|
455
|
+
},
|
|
456
|
+
})
|
|
457
|
+
|
|
458
|
+
app.run()
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
### Pattern: Subcommands with shared logic
|
|
462
|
+
|
|
463
|
+
```ts
|
|
464
|
+
// Shared composable for database connection
|
|
465
|
+
function useDbConnection() {
|
|
466
|
+
const { store } = useCliContext<{ db?: { conn?: any } }>()
|
|
467
|
+
const { init } = store('db')
|
|
468
|
+
return {
|
|
469
|
+
getConnection: () => init('conn', () => {
|
|
470
|
+
const flags = useCliOptions()
|
|
471
|
+
return connectToDb(flags['db-url'] as string || 'localhost:5432')
|
|
472
|
+
}),
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
app.cli('db migrate', {
|
|
477
|
+
description: 'Run pending migrations',
|
|
478
|
+
handler: async () => {
|
|
479
|
+
const { getConnection } = useDbConnection()
|
|
480
|
+
const db = getConnection()
|
|
481
|
+
await db.migrate()
|
|
482
|
+
return 'Migrations complete'
|
|
483
|
+
},
|
|
484
|
+
})
|
|
485
|
+
|
|
486
|
+
app.cli('db seed', {
|
|
487
|
+
description: 'Run database seeds',
|
|
488
|
+
handler: async () => {
|
|
489
|
+
const { getConnection } = useDbConnection()
|
|
490
|
+
const db = getConnection()
|
|
491
|
+
await db.seed()
|
|
492
|
+
return 'Seeding complete'
|
|
493
|
+
},
|
|
494
|
+
})
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
### Pattern: Global flags
|
|
498
|
+
|
|
499
|
+
```ts
|
|
500
|
+
// Check global flags in every handler
|
|
501
|
+
function useGlobalFlags() {
|
|
502
|
+
const flags = useCliOptions()
|
|
503
|
+
if (flags.verbose || flags.v) {
|
|
504
|
+
const logger = useEventLogger('cli')
|
|
505
|
+
logger.log('Verbose mode enabled')
|
|
506
|
+
}
|
|
507
|
+
return {
|
|
508
|
+
verbose: !!(flags.verbose || flags.v),
|
|
509
|
+
quiet: !!(flags.quiet || flags.q),
|
|
510
|
+
dryRun: !!(flags['dry-run']),
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
app.cli('deploy/:env', {
|
|
515
|
+
handler: () => {
|
|
516
|
+
const { verbose, dryRun } = useGlobalFlags()
|
|
517
|
+
const { get } = useRouteParams<{ env: string }>()
|
|
518
|
+
if (dryRun) return `[DRY RUN] Would deploy to ${get('env')}`
|
|
519
|
+
return `Deploying to ${get('env')}`
|
|
520
|
+
},
|
|
521
|
+
})
|
|
522
|
+
```
|
|
523
|
+
|
|
524
|
+
## Best Practices
|
|
525
|
+
|
|
526
|
+
- **Use `app.cli(path, options)` with metadata** — Always provide `description` and `args` so help generation works out of the box.
|
|
527
|
+
- **Use `useAutoHelp()` at the top of every handler** — Gives users consistent `--help` behavior across all commands.
|
|
528
|
+
- **Use `useCliOption(name)` over `useCliOptions()[name]`** — `useCliOption` resolves key aliases (e.g., `--verbose` / `-v`), so it respects your option definitions.
|
|
529
|
+
- **Use `onUnknownCommand` with `useCommandLookupHelp()`** — Provides a much better user experience than a generic "unknown command" error.
|
|
530
|
+
- **Return values from handlers** — Don't call `console.log` directly; the framework formats and outputs return values automatically. This also makes testing easier.
|
|
531
|
+
- **Type your route params** — Use `useRouteParams<{ env: string }>()` for type-safe access.
|
|
532
|
+
- **Use regex constraints for numeric args** — `:version(\\d+)` prevents non-numeric values from matching.
|
|
533
|
+
|
|
534
|
+
## Gotchas
|
|
535
|
+
|
|
536
|
+
- **Spaces and slashes are equivalent** — `'cmd test'` and `'cmd/test'` register the same command path. When displayed, they appear as spaces.
|
|
537
|
+
- **Flags containing colons** — If a command segment contains a colon that isn't a parameter, escape it: `'config set\\:key'` → matches `$ mycli config set:key`.
|
|
538
|
+
- **`useCliOptions()` includes `_` array** — The `_` property from minimist contains all positional args, including command segments. Use `useRouteParams()` for named arguments instead.
|
|
539
|
+
- **Aliases inherit arguments** — An alias like `'d'` for `'deploy/:env'` automatically becomes `'d/:env'`. You don't need to add the `:env` yourself.
|
|
540
|
+
- **Boolean flag negation** — `--no-cache` with `{ boolean: ['cache'] }` sets `cache: false`. Without the boolean option, it becomes `{ 'no-cache': true }`.
|
|
541
|
+
- **Route precedence** — Static segments match before parametric, parametric before wildcard. `'deploy staging'` is preferred over `'deploy/:env'` when the input is `deploy staging`.
|