@wooksjs/event-cli 0.6.6 → 0.7.0
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 +10 -34
- package/dist/index.cjs +59 -41
- package/dist/index.d.ts +23 -25
- package/dist/index.mjs +40 -35
- package/package.json +7 -7
- package/skills/wooksjs-event-cli/SKILL.md +15 -19
- package/skills/wooksjs-event-cli/commands.md +109 -428
- package/skills/wooksjs-event-cli/core.md +77 -294
- package/skills/wooksjs-event-cli/event-core.md +0 -562
|
@@ -1,541 +1,222 @@
|
|
|
1
|
-
# Commands &
|
|
1
|
+
# Commands & help — @wooksjs/event-cli
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> Command registration with options/args/aliases, help generation, and CLI composables.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Concepts
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Commands can be registered with rich metadata (description, options, args, aliases, examples) that drives auto-generated help output. The help system is powered by `@prostojs/cli-help`.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
CLI composables (`useCliOptions`, `useCliOption`, `useCliHelp`, `useAutoHelp`) provide access to parsed flags and help rendering from within handlers.
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
import { createCliApp } from '@wooksjs/event-cli'
|
|
13
|
-
|
|
14
|
-
const app = createCliApp()
|
|
11
|
+
## API Reference
|
|
15
12
|
|
|
16
|
-
|
|
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:
|
|
13
|
+
### Command registration with options
|
|
27
14
|
|
|
28
15
|
```ts
|
|
29
16
|
app.cli('deploy/:env', {
|
|
30
|
-
description: 'Deploy to
|
|
17
|
+
description: 'Deploy the application to the specified environment',
|
|
31
18
|
args: {
|
|
32
19
|
env: 'Target environment (staging, production)',
|
|
33
20
|
},
|
|
34
21
|
options: [
|
|
35
|
-
{ keys: ['
|
|
36
|
-
{ keys: ['
|
|
22
|
+
{ keys: ['verbose', 'v'], description: 'Enable verbose output' },
|
|
23
|
+
{ keys: ['dry-run', 'd'], description: 'Run without making changes' },
|
|
24
|
+
{ keys: ['config', 'c'], description: 'Path to config file', value: 'path' },
|
|
37
25
|
],
|
|
38
|
-
aliases: ['
|
|
26
|
+
aliases: ['dep'],
|
|
39
27
|
examples: [
|
|
40
28
|
{
|
|
41
|
-
description: 'Deploy to production with
|
|
42
|
-
cmd: 'production
|
|
29
|
+
description: 'Deploy to production with verbose output',
|
|
30
|
+
cmd: 'production --verbose',
|
|
43
31
|
},
|
|
44
32
|
],
|
|
45
33
|
handler: () => {
|
|
46
|
-
const {
|
|
47
|
-
const
|
|
48
|
-
return `Deploying to ${
|
|
34
|
+
const { params } = useRouteParams<{ env: string }>()
|
|
35
|
+
const verbose = useCliOption('verbose')
|
|
36
|
+
return `Deploying to ${params.env}${verbose ? ' (verbose)' : ''}`
|
|
49
37
|
},
|
|
50
38
|
})
|
|
51
39
|
```
|
|
52
40
|
|
|
53
|
-
|
|
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'
|
|
41
|
+
**`TWooksCliEntry<T>` fields:**
|
|
93
42
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
43
|
+
| Field | Type | Description |
|
|
44
|
+
| ------------- | ----------------------------------- | -------------------------------------------------------- |
|
|
45
|
+
| `handler` | `TWooksHandler<T>` | The command handler function |
|
|
46
|
+
| `description` | `string` | Command description for help |
|
|
47
|
+
| `args` | `Record<string, string>` | Argument descriptions (auto-populated from route params) |
|
|
48
|
+
| `options` | `TCliOption[]` | Flag/option definitions |
|
|
49
|
+
| `aliases` | `string[]` | Alternative command names |
|
|
50
|
+
| `examples` | `TCliExample[]` | Usage examples |
|
|
51
|
+
| `onRegister` | `(path, aliasType, route?) => void` | Callback when command/alias is registered |
|
|
103
52
|
|
|
104
|
-
|
|
53
|
+
**Option definition:**
|
|
105
54
|
|
|
106
55
|
```ts
|
|
107
|
-
|
|
108
|
-
|
|
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
|
|
56
|
+
{ keys: ['verbose', 'v'], description: 'Enable verbose output' }
|
|
57
|
+
{ keys: ['config', 'c'], description: 'Config file path', value: 'file' }
|
|
118
58
|
```
|
|
119
59
|
|
|
120
|
-
|
|
60
|
+
- `keys`: Array of flag names (first is primary, rest are aliases)
|
|
61
|
+
- `description`: Help text
|
|
62
|
+
- `value`: If present, the option expects a value (shown in help as `--config <file>`)
|
|
121
63
|
|
|
122
|
-
|
|
64
|
+
### `useCliOptions(): Record<string, boolean | string>`
|
|
123
65
|
|
|
124
|
-
|
|
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):
|
|
66
|
+
Returns all parsed CLI flags (via `minimist`). Includes positional args in `_`.
|
|
206
67
|
|
|
207
68
|
```ts
|
|
208
69
|
import { useCliOptions } from '@wooksjs/event-cli'
|
|
209
70
|
|
|
210
71
|
app.cli('build', () => {
|
|
211
|
-
const
|
|
212
|
-
// $ mycli build --
|
|
213
|
-
//
|
|
214
|
-
return `Building for ${flags.env}`
|
|
72
|
+
const opts = useCliOptions()
|
|
73
|
+
// For: $ mycli build --verbose --target=production
|
|
74
|
+
// opts = { _: ['build'], verbose: true, target: 'production' }
|
|
215
75
|
})
|
|
216
76
|
```
|
|
217
77
|
|
|
218
|
-
|
|
78
|
+
### `useCliOption(name: string): boolean | string | undefined`
|
|
219
79
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
Get a single option value. Resolves aliases — if you defined `keys: ['verbose', 'v']`, calling `useCliOption('verbose')` also checks `-v`:
|
|
80
|
+
Returns a single option value, resolving aliases from the command's option definitions.
|
|
223
81
|
|
|
224
82
|
```ts
|
|
225
83
|
import { useCliOption } from '@wooksjs/event-cli'
|
|
226
84
|
|
|
227
|
-
app.cli('
|
|
228
|
-
options: [
|
|
229
|
-
{ keys: ['env', 'e'], description: 'Target environment', value: 'dev' },
|
|
230
|
-
{ keys: ['verbose', 'v'], description: 'Verbose output' },
|
|
231
|
-
],
|
|
85
|
+
app.cli('deploy', {
|
|
86
|
+
options: [{ keys: ['verbose', 'v'], description: 'Verbose output' }],
|
|
232
87
|
handler: () => {
|
|
233
|
-
const
|
|
234
|
-
|
|
235
|
-
return `Building for ${env || 'dev'} (verbose: ${!!verbose})`
|
|
88
|
+
const verbose = useCliOption('verbose')
|
|
89
|
+
// Returns true for both --verbose and -v
|
|
236
90
|
},
|
|
237
91
|
})
|
|
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
92
|
```
|
|
282
93
|
|
|
283
94
|
### `useCliHelp()`
|
|
284
95
|
|
|
285
|
-
|
|
96
|
+
Provides access to the help renderer for the current command.
|
|
286
97
|
|
|
287
98
|
```ts
|
|
288
99
|
import { useCliHelp } from '@wooksjs/event-cli'
|
|
289
100
|
|
|
290
|
-
|
|
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
|
-
})
|
|
101
|
+
const { print, render, getEntry, getCliHelp } = useCliHelp()
|
|
303
102
|
```
|
|
304
103
|
|
|
104
|
+
| Method | Description |
|
|
105
|
+
| ----------------------------- | ------------------------------------------ |
|
|
106
|
+
| `print(withColors?)` | Print help to stdout |
|
|
107
|
+
| `render(width?, withColors?)` | Render help as string |
|
|
108
|
+
| `getEntry()` | Get the help entry for the current command |
|
|
109
|
+
| `getCliHelp()` | Get the full `CliHelpRenderer` instance |
|
|
110
|
+
|
|
305
111
|
### `useAutoHelp(keys?, colors?)`
|
|
306
112
|
|
|
307
|
-
|
|
113
|
+
Checks if `--help` (or custom flag) was passed; if so, prints help and returns `true`.
|
|
308
114
|
|
|
309
115
|
```ts
|
|
310
116
|
import { useAutoHelp } from '@wooksjs/event-cli'
|
|
311
117
|
|
|
312
|
-
app.cli('
|
|
313
|
-
description: '
|
|
118
|
+
app.cli('build', {
|
|
119
|
+
description: 'Build the project',
|
|
314
120
|
handler: () => {
|
|
315
|
-
if (useAutoHelp()) return
|
|
121
|
+
if (useAutoHelp()) return // prints help and returns true for --help
|
|
122
|
+
return 'Building...'
|
|
123
|
+
},
|
|
124
|
+
})
|
|
316
125
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
126
|
+
// Custom trigger flags
|
|
127
|
+
app.cli('test', {
|
|
128
|
+
handler: () => {
|
|
129
|
+
if (useAutoHelp(['help', 'h'], false)) return // -h, --help, no colors
|
|
320
130
|
},
|
|
321
131
|
})
|
|
322
|
-
// $ mycli deploy --help → prints formatted help
|
|
323
|
-
// $ mycli deploy staging → "Deploying to staging"
|
|
324
132
|
```
|
|
325
133
|
|
|
326
|
-
|
|
134
|
+
**Parameters:**
|
|
327
135
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
if (useAutoHelp(['help', 'h'], false)) {
|
|
331
|
-
process.exit(0)
|
|
332
|
-
}
|
|
333
|
-
```
|
|
136
|
+
- `keys` (default: `['help']`) — option names that trigger help
|
|
137
|
+
- `colors` (default: `true`) — whether to use ANSI colors
|
|
334
138
|
|
|
335
139
|
### `useCommandLookupHelp(lookupDepth?)`
|
|
336
140
|
|
|
337
|
-
|
|
141
|
+
Provides "did you mean?" suggestions for unknown commands. Best used in `onUnknownCommand`:
|
|
338
142
|
|
|
339
143
|
```ts
|
|
340
|
-
import { createCliApp
|
|
144
|
+
import { createCliApp } from '@wooksjs/event-cli'
|
|
145
|
+
import { useCommandLookupHelp } from '@wooksjs/event-cli'
|
|
341
146
|
|
|
342
147
|
const app = createCliApp({
|
|
343
|
-
onUnknownCommand: (
|
|
344
|
-
useCommandLookupHelp()
|
|
345
|
-
raiseError()
|
|
148
|
+
onUnknownCommand: (params, raiseError) => {
|
|
149
|
+
useCommandLookupHelp() // throws with suggestions if found
|
|
150
|
+
raiseError() // fallback: standard "unknown command" error
|
|
346
151
|
},
|
|
347
152
|
})
|
|
348
153
|
```
|
|
349
154
|
|
|
350
|
-
|
|
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'
|
|
155
|
+
For a command like `run test:drive dir`, it searches backwards:
|
|
368
156
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
// marks, width, etc.
|
|
374
|
-
},
|
|
375
|
-
})
|
|
157
|
+
1. `run test:drive dir` (depth 0)
|
|
158
|
+
2. `run test:drive` (depth 1)
|
|
159
|
+
3. `run test` (depth 2)
|
|
160
|
+
4. `run` (depth 3)
|
|
376
161
|
|
|
377
|
-
|
|
378
|
-
const renderer = new CliHelpRenderer({ name: 'mycli' })
|
|
379
|
-
const app = createCliApp({ cliHelp: renderer })
|
|
380
|
-
```
|
|
162
|
+
If a match has children, it suggests them. If a match expects args, it says which args are expected.
|
|
381
163
|
|
|
382
|
-
##
|
|
164
|
+
## Common Patterns
|
|
383
165
|
|
|
384
|
-
|
|
166
|
+
### Pattern: Auto-help with exit
|
|
385
167
|
|
|
386
168
|
```ts
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
169
|
+
app.cli('serve', {
|
|
170
|
+
description: 'Start the development server',
|
|
171
|
+
options: [{ keys: ['port', 'p'], description: 'Port number', value: 'number' }],
|
|
390
172
|
handler: () => {
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
}>()
|
|
395
|
-
|
|
396
|
-
get('action') // 'create', 'delete', etc.
|
|
397
|
-
get('id') // '42' or undefined
|
|
398
|
-
params // { action: 'create', id: '42' }
|
|
173
|
+
if (useAutoHelp()) process.exit(0)
|
|
174
|
+
const port = useCliOption('port') || '3000'
|
|
175
|
+
return `Server running on port ${port}`
|
|
399
176
|
},
|
|
400
177
|
})
|
|
401
178
|
```
|
|
402
179
|
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
## Common Patterns
|
|
406
|
-
|
|
407
|
-
### Pattern: Multi-command CLI with help
|
|
180
|
+
### Pattern: Boolean and value flags
|
|
408
181
|
|
|
409
182
|
```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
183
|
app.cli('build', {
|
|
432
|
-
description: 'Build the project',
|
|
433
184
|
options: [
|
|
434
|
-
{ keys: ['watch', 'w'], description: 'Watch mode' },
|
|
435
|
-
{ keys: ['
|
|
185
|
+
{ keys: ['watch', 'w'], description: 'Watch mode' }, // boolean
|
|
186
|
+
{ keys: ['output', 'o'], description: 'Output dir', value: 'path' }, // value
|
|
436
187
|
],
|
|
437
188
|
handler: () => {
|
|
438
|
-
|
|
439
|
-
const
|
|
440
|
-
return `Building... (watch=${!!flags.watch}, minify=${!!flags.minify})`
|
|
189
|
+
const watch = useCliOption('watch') // true | undefined
|
|
190
|
+
const output = useCliOption('output') // 'dist' | undefined
|
|
441
191
|
},
|
|
442
192
|
})
|
|
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
193
|
```
|
|
460
194
|
|
|
461
|
-
### Pattern:
|
|
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
|
-
```
|
|
195
|
+
### Pattern: Shared CLI and HTTP adapters
|
|
496
196
|
|
|
497
|
-
|
|
197
|
+
Both adapters can share the same `Wooks` instance:
|
|
498
198
|
|
|
499
199
|
```ts
|
|
500
|
-
|
|
501
|
-
|
|
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
|
-
}
|
|
200
|
+
import { createCliApp } from '@wooksjs/event-cli'
|
|
201
|
+
import { createHttpApp } from '@wooksjs/event-http'
|
|
513
202
|
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
return `Deploying to ${get('env')}`
|
|
520
|
-
},
|
|
521
|
-
})
|
|
203
|
+
const httpApp = createHttpApp()
|
|
204
|
+
const cliApp = createCliApp({}, httpApp)
|
|
205
|
+
|
|
206
|
+
// CLI commands and HTTP routes share the same router
|
|
207
|
+
cliApp.cli('start', () => httpApp.listen(3000))
|
|
522
208
|
```
|
|
523
209
|
|
|
524
210
|
## Best Practices
|
|
525
211
|
|
|
526
|
-
-
|
|
527
|
-
-
|
|
528
|
-
-
|
|
529
|
-
-
|
|
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.
|
|
212
|
+
- Always provide `description` and `options` for production CLI tools — it powers `--help`
|
|
213
|
+
- Use `useAutoHelp()` at the top of every command handler
|
|
214
|
+
- Use `useCommandLookupHelp()` in `onUnknownCommand` for better UX
|
|
215
|
+
- Use aliases for common abbreviations: `aliases: ['dep']` for `deploy`
|
|
533
216
|
|
|
534
217
|
## Gotchas
|
|
535
218
|
|
|
536
|
-
-
|
|
537
|
-
-
|
|
538
|
-
-
|
|
539
|
-
-
|
|
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`.
|
|
219
|
+
- `useCliOptions()` returns the raw `minimist` output — `_` contains positional args, everything else is flags
|
|
220
|
+
- `useCliOption(name)` resolves aliases from the option definitions — `useCliOptions()[name]` does not
|
|
221
|
+
- Help is auto-registered from `app.cli()` metadata — no separate help registration needed
|
|
222
|
+
- Colons in command names must be escaped: `'use\\:dev'` for command `use:dev`
|