@ls-stack/cli 0.1.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 +532 -0
- package/dist/main.d.mts +409 -0
- package/dist/main.mjs +723 -0
- package/dist/main.mjs.map +1 -0
- package/package.json +47 -0
package/README.md
ADDED
|
@@ -0,0 +1,532 @@
|
|
|
1
|
+
# @ls-stack/cli
|
|
2
|
+
|
|
3
|
+
A TypeScript library for building interactive command-line interfaces with type-safe prompts and ESC-to-cancel support.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add @ls-stack/cli
|
|
9
|
+
# or
|
|
10
|
+
yarn add @ls-stack/cli
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
**Requirements:** Node.js >= 21.5.0
|
|
14
|
+
|
|
15
|
+
## Quick Start
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import { cliInput } from '@ls-stack/cli';
|
|
19
|
+
|
|
20
|
+
const name = await cliInput.text('What is your name?');
|
|
21
|
+
const proceed = await cliInput.confirm('Continue?', { initial: true });
|
|
22
|
+
|
|
23
|
+
console.log(`Hello, ${name}!`);
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## API Reference
|
|
27
|
+
|
|
28
|
+
### Types
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
type ValidateFn = (
|
|
32
|
+
value: string,
|
|
33
|
+
) => boolean | string | Promise<boolean | string>;
|
|
34
|
+
|
|
35
|
+
type SelectOption<T extends string> = {
|
|
36
|
+
value: T;
|
|
37
|
+
label?: string;
|
|
38
|
+
hint?: string;
|
|
39
|
+
};
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### `cliInput.select()`
|
|
43
|
+
|
|
44
|
+
Single selection from a list of options.
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
const choice = await cliInput.select<'dev' | 'staging' | 'prod'>(
|
|
48
|
+
'Select environment',
|
|
49
|
+
{
|
|
50
|
+
options: [
|
|
51
|
+
{ value: 'dev', label: 'Development', hint: 'Local development server' },
|
|
52
|
+
{ value: 'staging', label: 'Staging', hint: 'Pre-production testing' },
|
|
53
|
+
{ value: 'prod', label: 'Production', hint: 'Live environment' },
|
|
54
|
+
],
|
|
55
|
+
},
|
|
56
|
+
);
|
|
57
|
+
// choice: 'dev' | 'staging' | 'prod'
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
**Parameters:**
|
|
61
|
+
|
|
62
|
+
| Name | Type | Description |
|
|
63
|
+
| ----------------- | ------------------- | --------------------------- |
|
|
64
|
+
| `title` | `string` | The prompt message |
|
|
65
|
+
| `options.options` | `SelectOption<T>[]` | Array of selectable options |
|
|
66
|
+
|
|
67
|
+
**Returns:** `Promise<T>` - The selected option's value
|
|
68
|
+
|
|
69
|
+
### `cliInput.multipleSelect()`
|
|
70
|
+
|
|
71
|
+
Multi-select with checkboxes. Requires at least one selection.
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
const features = await cliInput.multipleSelect<'ts' | 'eslint' | 'prettier'>(
|
|
75
|
+
'Select features to enable',
|
|
76
|
+
{
|
|
77
|
+
options: [
|
|
78
|
+
{ value: 'ts', label: 'TypeScript' },
|
|
79
|
+
{ value: 'eslint', label: 'ESLint', hint: 'Code linting' },
|
|
80
|
+
{ value: 'prettier', label: 'Prettier', hint: 'Code formatting' },
|
|
81
|
+
],
|
|
82
|
+
},
|
|
83
|
+
);
|
|
84
|
+
// features: ('ts' | 'eslint' | 'prettier')[]
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
**Parameters:**
|
|
88
|
+
|
|
89
|
+
| Name | Type | Description |
|
|
90
|
+
| ----------------- | ------------------- | --------------------------- |
|
|
91
|
+
| `title` | `string` | The prompt message |
|
|
92
|
+
| `options.options` | `SelectOption<T>[]` | Array of selectable options |
|
|
93
|
+
|
|
94
|
+
**Returns:** `Promise<T[]>` - Array of selected values
|
|
95
|
+
|
|
96
|
+
### `cliInput.text()`
|
|
97
|
+
|
|
98
|
+
Text input with optional validation.
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
const projectName = await cliInput.text('Enter project name', {
|
|
102
|
+
initial: 'my-project',
|
|
103
|
+
validate: (value) => {
|
|
104
|
+
if (!/^[a-z0-9-]+$/.test(value)) {
|
|
105
|
+
return 'Only lowercase letters, numbers, and hyphens allowed';
|
|
106
|
+
}
|
|
107
|
+
return true;
|
|
108
|
+
},
|
|
109
|
+
});
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
**Parameters:**
|
|
113
|
+
|
|
114
|
+
| Name | Type | Description |
|
|
115
|
+
| ------------------ | ------------- | ------------------- |
|
|
116
|
+
| `title` | `string` | The prompt message |
|
|
117
|
+
| `options.initial` | `string?` | Default value |
|
|
118
|
+
| `options.validate` | `ValidateFn?` | Validation function |
|
|
119
|
+
|
|
120
|
+
**Returns:** `Promise<string>` - The entered text
|
|
121
|
+
|
|
122
|
+
### `cliInput.textWithAutocomplete()`
|
|
123
|
+
|
|
124
|
+
Text input with autocomplete suggestions. Searches across value, label, and hint.
|
|
125
|
+
|
|
126
|
+
```typescript
|
|
127
|
+
const framework = await cliInput.textWithAutocomplete<
|
|
128
|
+
'react' | 'vue' | 'svelte'
|
|
129
|
+
>('Select a framework', {
|
|
130
|
+
options: [
|
|
131
|
+
{
|
|
132
|
+
value: 'react',
|
|
133
|
+
label: 'React',
|
|
134
|
+
hint: 'A JavaScript library for building UIs',
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
value: 'vue',
|
|
138
|
+
label: 'Vue',
|
|
139
|
+
hint: 'The progressive JavaScript framework',
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
value: 'svelte',
|
|
143
|
+
label: 'Svelte',
|
|
144
|
+
hint: 'Cybernetically enhanced web apps',
|
|
145
|
+
},
|
|
146
|
+
],
|
|
147
|
+
validate: (value) => value.length > 0 || 'Please select a framework',
|
|
148
|
+
});
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
**Parameters:**
|
|
152
|
+
|
|
153
|
+
| Name | Type | Description |
|
|
154
|
+
| ------------------ | ------------------- | --------------------------------- |
|
|
155
|
+
| `title` | `string` | The prompt message |
|
|
156
|
+
| `options.options` | `SelectOption<T>[]` | Array of autocomplete suggestions |
|
|
157
|
+
| `options.validate` | `ValidateFn?` | Validation function |
|
|
158
|
+
|
|
159
|
+
**Returns:** `Promise<T>` - The selected or entered value
|
|
160
|
+
|
|
161
|
+
### `cliInput.confirm()`
|
|
162
|
+
|
|
163
|
+
Yes/No boolean prompt.
|
|
164
|
+
|
|
165
|
+
```typescript
|
|
166
|
+
const shouldDeploy = await cliInput.confirm('Deploy to production?', {
|
|
167
|
+
initial: false,
|
|
168
|
+
});
|
|
169
|
+
// shouldDeploy: boolean
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
**Parameters:**
|
|
173
|
+
|
|
174
|
+
| Name | Type | Description |
|
|
175
|
+
| ----------------- | ---------- | ---------------------------------------------- |
|
|
176
|
+
| `title` | `string` | The prompt message |
|
|
177
|
+
| `options.initial` | `boolean?` | Default value (`true` for yes, `false` for no) |
|
|
178
|
+
|
|
179
|
+
**Returns:** `Promise<boolean>` - The user's choice
|
|
180
|
+
|
|
181
|
+
### `cliInput.number()`
|
|
182
|
+
|
|
183
|
+
Numeric input.
|
|
184
|
+
|
|
185
|
+
```typescript
|
|
186
|
+
const port = await cliInput.number('Enter port number', {
|
|
187
|
+
initial: 3000,
|
|
188
|
+
});
|
|
189
|
+
// port: number | null
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
**Parameters:**
|
|
193
|
+
|
|
194
|
+
| Name | Type | Description |
|
|
195
|
+
| ----------------- | --------- | ------------------ |
|
|
196
|
+
| `title` | `string` | The prompt message |
|
|
197
|
+
| `options.initial` | `number?` | Default value |
|
|
198
|
+
|
|
199
|
+
**Returns:** `Promise<number | null>` - The entered number, or `null` on error
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
## CLI Framework
|
|
204
|
+
|
|
205
|
+
Build complete CLI applications with typed commands, automatic help generation, and interactive mode.
|
|
206
|
+
|
|
207
|
+
### Quick Start
|
|
208
|
+
|
|
209
|
+
```typescript
|
|
210
|
+
import { createCLI, createCmd } from '@ls-stack/cli';
|
|
211
|
+
|
|
212
|
+
await createCLI(
|
|
213
|
+
{ name: 'My CLI', baseCmd: 'my-cli' },
|
|
214
|
+
{
|
|
215
|
+
hello: createCmd({
|
|
216
|
+
short: 'hi',
|
|
217
|
+
description: 'Say hello',
|
|
218
|
+
run: async () => {
|
|
219
|
+
console.log('Hello, World!');
|
|
220
|
+
},
|
|
221
|
+
}),
|
|
222
|
+
},
|
|
223
|
+
);
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### Argument Types
|
|
227
|
+
|
|
228
|
+
```typescript
|
|
229
|
+
type Arg =
|
|
230
|
+
| { type: 'positional-string'; name: string; description: string; default?: string }
|
|
231
|
+
| { type: 'positional-number'; name: string; description: string; default?: number }
|
|
232
|
+
| { type: 'flag'; name: string; description: string }
|
|
233
|
+
| { type: 'value-string-flag'; name: string; description: string; default?: string }
|
|
234
|
+
| { type: 'value-number-flag'; name: string; description: string; default?: number };
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
| Type | CLI Usage | TypeScript Type |
|
|
238
|
+
| -------------------- | ---------------------- | ------------------------------------------------- |
|
|
239
|
+
| `positional-string` | `my-cli cmd value` | `string` (or `string \| undefined` if no default) |
|
|
240
|
+
| `positional-number` | `my-cli cmd 42` | `number` (or `number \| undefined` if no default) |
|
|
241
|
+
| `flag` | `my-cli cmd --verbose` | `boolean` (defaults to `false`) |
|
|
242
|
+
| `value-string-flag` | `my-cli cmd --env dev` | `string \| undefined` (or `string` if default) |
|
|
243
|
+
| `value-number-flag` | `my-cli cmd --port 80` | `number \| undefined` (or `number` if default) |
|
|
244
|
+
|
|
245
|
+
### `createCmd()`
|
|
246
|
+
|
|
247
|
+
Creates a type-safe command definition.
|
|
248
|
+
|
|
249
|
+
```typescript
|
|
250
|
+
const deploy = createCmd({
|
|
251
|
+
short: 'd',
|
|
252
|
+
description: 'Deploy the application',
|
|
253
|
+
args: {
|
|
254
|
+
env: {
|
|
255
|
+
type: 'positional-string',
|
|
256
|
+
name: 'env',
|
|
257
|
+
description: 'Target environment',
|
|
258
|
+
},
|
|
259
|
+
port: {
|
|
260
|
+
type: 'value-number-flag',
|
|
261
|
+
name: 'port',
|
|
262
|
+
description: 'Port number',
|
|
263
|
+
default: 3000,
|
|
264
|
+
},
|
|
265
|
+
verbose: {
|
|
266
|
+
type: 'flag',
|
|
267
|
+
name: 'verbose',
|
|
268
|
+
description: 'Enable verbose logging',
|
|
269
|
+
},
|
|
270
|
+
},
|
|
271
|
+
examples: [
|
|
272
|
+
{ args: ['production'], description: 'Deploy to production' },
|
|
273
|
+
{ args: ['staging', '--port', '8080'], description: 'Deploy to staging on port 8080' },
|
|
274
|
+
],
|
|
275
|
+
run: async ({ env, port, verbose }) => {
|
|
276
|
+
// Types are inferred: env: string, port: number, verbose: boolean
|
|
277
|
+
console.log(`Deploying to ${env} on port ${port}`);
|
|
278
|
+
if (verbose) console.log('Verbose mode enabled');
|
|
279
|
+
},
|
|
280
|
+
});
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
**Parameters:**
|
|
284
|
+
|
|
285
|
+
| Name | Type | Description |
|
|
286
|
+
| ------------- | ----------------------------- | ---------------------------------------------- |
|
|
287
|
+
| `description` | `string` | Command description shown in help |
|
|
288
|
+
| `short` | `string?` | Single-character alias (cannot be 'i' or 'h') |
|
|
289
|
+
| `args` | `Record<string, Arg>?` | Typed argument definitions |
|
|
290
|
+
| `run` | `(args) => void \| Promise` | Handler function receiving parsed arguments |
|
|
291
|
+
| `examples` | `{ args, description }[]?` | Usage examples for help text |
|
|
292
|
+
|
|
293
|
+
### `createCLI()`
|
|
294
|
+
|
|
295
|
+
Creates and runs a CLI application.
|
|
296
|
+
|
|
297
|
+
```typescript
|
|
298
|
+
await createCLI(
|
|
299
|
+
{
|
|
300
|
+
name: 'My CLI',
|
|
301
|
+
baseCmd: 'my-cli',
|
|
302
|
+
sort: ['deploy', 'build', 'test'], // Optional: custom command order
|
|
303
|
+
},
|
|
304
|
+
{
|
|
305
|
+
deploy: deployCmd,
|
|
306
|
+
build: buildCmd,
|
|
307
|
+
test: testCmd,
|
|
308
|
+
},
|
|
309
|
+
);
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
**Parameters:**
|
|
313
|
+
|
|
314
|
+
| Name | Type | Description |
|
|
315
|
+
| -------------- | ----------------- | ------------------------------------- |
|
|
316
|
+
| `name` | `string` | CLI display name shown in header |
|
|
317
|
+
| `baseCmd` | `string` | Command prefix for help text |
|
|
318
|
+
| `sort` | `string[]?` | Custom command display order |
|
|
319
|
+
| `cmds` | `Record<C, Cmd>` | Commands created with `createCmd` |
|
|
320
|
+
|
|
321
|
+
### Built-in Commands
|
|
322
|
+
|
|
323
|
+
| Command | Description |
|
|
324
|
+
| ------------------ | ---------------------------------------- |
|
|
325
|
+
| `h`, `--help` | Show help with all commands |
|
|
326
|
+
| `i` | Interactive mode (select from list) |
|
|
327
|
+
| `<command> -h` | Show help for a specific command |
|
|
328
|
+
|
|
329
|
+
### CLI Usage Examples
|
|
330
|
+
|
|
331
|
+
```bash
|
|
332
|
+
my-cli # Show interactive menu
|
|
333
|
+
my-cli h # Show help
|
|
334
|
+
my-cli --help # Show help
|
|
335
|
+
my-cli i # Interactive mode
|
|
336
|
+
my-cli deploy prod # Run deploy with positional arg
|
|
337
|
+
my-cli d prod # Run deploy via short alias
|
|
338
|
+
my-cli deploy -h # Show deploy command help
|
|
339
|
+
my-cli deploy prod --port 8080 --verbose
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
### Complete Example
|
|
343
|
+
|
|
344
|
+
```typescript
|
|
345
|
+
import { createCLI, createCmd } from '@ls-stack/cli';
|
|
346
|
+
|
|
347
|
+
await createCLI(
|
|
348
|
+
{ name: 'Project CLI', baseCmd: 'project' },
|
|
349
|
+
{
|
|
350
|
+
create: createCmd({
|
|
351
|
+
short: 'c',
|
|
352
|
+
description: 'Create a new project',
|
|
353
|
+
args: {
|
|
354
|
+
name: {
|
|
355
|
+
type: 'positional-string',
|
|
356
|
+
name: 'name',
|
|
357
|
+
description: 'Project name',
|
|
358
|
+
},
|
|
359
|
+
template: {
|
|
360
|
+
type: 'value-string-flag',
|
|
361
|
+
name: 'template',
|
|
362
|
+
description: 'Project template',
|
|
363
|
+
default: 'basic',
|
|
364
|
+
},
|
|
365
|
+
},
|
|
366
|
+
examples: [
|
|
367
|
+
{ args: ['my-app'], description: 'Create with default template' },
|
|
368
|
+
{ args: ['my-app', '--template', 'react'], description: 'Create React project' },
|
|
369
|
+
],
|
|
370
|
+
run: async ({ name, template }) => {
|
|
371
|
+
console.log(`Creating ${name} with template: ${template}`);
|
|
372
|
+
},
|
|
373
|
+
}),
|
|
374
|
+
|
|
375
|
+
build: createCmd({
|
|
376
|
+
short: 'b',
|
|
377
|
+
description: 'Build the project',
|
|
378
|
+
args: {
|
|
379
|
+
watch: {
|
|
380
|
+
type: 'flag',
|
|
381
|
+
name: 'watch',
|
|
382
|
+
description: 'Watch for changes',
|
|
383
|
+
},
|
|
384
|
+
},
|
|
385
|
+
run: async ({ watch }) => {
|
|
386
|
+
console.log(watch ? 'Building in watch mode...' : 'Building...');
|
|
387
|
+
},
|
|
388
|
+
}),
|
|
389
|
+
|
|
390
|
+
serve: createCmd({
|
|
391
|
+
short: 's',
|
|
392
|
+
description: 'Start development server',
|
|
393
|
+
args: {
|
|
394
|
+
port: {
|
|
395
|
+
type: 'value-number-flag',
|
|
396
|
+
name: 'port',
|
|
397
|
+
description: 'Port number',
|
|
398
|
+
default: 3000,
|
|
399
|
+
},
|
|
400
|
+
},
|
|
401
|
+
run: async ({ port }) => {
|
|
402
|
+
console.log(`Server running on http://localhost:${port}`);
|
|
403
|
+
},
|
|
404
|
+
}),
|
|
405
|
+
},
|
|
406
|
+
);
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
## Features
|
|
410
|
+
|
|
411
|
+
### ESC-to-Cancel
|
|
412
|
+
|
|
413
|
+
All prompts support pressing ESC to cancel. When cancelled, the process exits cleanly with code 0.
|
|
414
|
+
|
|
415
|
+
### Type Safety
|
|
416
|
+
|
|
417
|
+
All prompts are fully typed. When using generic type parameters with `select`, `multipleSelect`, or `textWithAutocomplete`, the return type is narrowed to the union of option values.
|
|
418
|
+
|
|
419
|
+
```typescript
|
|
420
|
+
// Return type is automatically 'small' | 'medium' | 'large'
|
|
421
|
+
const size = await cliInput.select<'small' | 'medium' | 'large'>(
|
|
422
|
+
'Select size',
|
|
423
|
+
{
|
|
424
|
+
options: [{ value: 'small' }, { value: 'medium' }, { value: 'large' }],
|
|
425
|
+
},
|
|
426
|
+
);
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
### Validation
|
|
430
|
+
|
|
431
|
+
Text inputs support synchronous or asynchronous validation:
|
|
432
|
+
|
|
433
|
+
```typescript
|
|
434
|
+
const email = await cliInput.text('Enter email', {
|
|
435
|
+
validate: async (value) => {
|
|
436
|
+
if (!value.includes('@')) {
|
|
437
|
+
return 'Invalid email format';
|
|
438
|
+
}
|
|
439
|
+
const exists = await checkEmailExists(value);
|
|
440
|
+
if (exists) {
|
|
441
|
+
return 'Email already registered';
|
|
442
|
+
}
|
|
443
|
+
return true;
|
|
444
|
+
},
|
|
445
|
+
});
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
## Examples
|
|
449
|
+
|
|
450
|
+
### Interactive Setup Wizard
|
|
451
|
+
|
|
452
|
+
```typescript
|
|
453
|
+
import { cliInput } from '@ls-stack/cli';
|
|
454
|
+
|
|
455
|
+
async function setupWizard() {
|
|
456
|
+
const projectName = await cliInput.text('Project name', {
|
|
457
|
+
validate: (v) => v.length >= 3 || 'Name must be at least 3 characters',
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
const template = await cliInput.select('Select template', {
|
|
461
|
+
options: [
|
|
462
|
+
{ value: 'blank', label: 'Blank', hint: 'Empty project' },
|
|
463
|
+
{ value: 'react', label: 'React', hint: 'React with Vite' },
|
|
464
|
+
{ value: 'next', label: 'Next.js', hint: 'Full-stack React' },
|
|
465
|
+
],
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
const features = await cliInput.multipleSelect('Enable features', {
|
|
469
|
+
options: [
|
|
470
|
+
{ value: 'typescript', label: 'TypeScript' },
|
|
471
|
+
{ value: 'eslint', label: 'ESLint' },
|
|
472
|
+
{ value: 'prettier', label: 'Prettier' },
|
|
473
|
+
{ value: 'testing', label: 'Testing (Vitest)' },
|
|
474
|
+
],
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
const installDeps = await cliInput.confirm('Install dependencies?', {
|
|
478
|
+
initial: true,
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
return { projectName, template, features, installDeps };
|
|
482
|
+
}
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
### Configuration Menu
|
|
486
|
+
|
|
487
|
+
```typescript
|
|
488
|
+
import { cliInput } from '@ls-stack/cli';
|
|
489
|
+
|
|
490
|
+
async function configMenu() {
|
|
491
|
+
const action = await cliInput.select('What would you like to configure?', {
|
|
492
|
+
options: [
|
|
493
|
+
{ value: 'port', label: 'Server Port' },
|
|
494
|
+
{ value: 'host', label: 'Host Address' },
|
|
495
|
+
{ value: 'timeout', label: 'Request Timeout' },
|
|
496
|
+
],
|
|
497
|
+
});
|
|
498
|
+
|
|
499
|
+
switch (action) {
|
|
500
|
+
case 'port': {
|
|
501
|
+
const port = await cliInput.number('Enter port', { initial: 3000 });
|
|
502
|
+
console.log(`Port set to ${port}`);
|
|
503
|
+
break;
|
|
504
|
+
}
|
|
505
|
+
case 'host': {
|
|
506
|
+
const host = await cliInput.text('Enter host', { initial: 'localhost' });
|
|
507
|
+
console.log(`Host set to ${host}`);
|
|
508
|
+
break;
|
|
509
|
+
}
|
|
510
|
+
case 'timeout': {
|
|
511
|
+
const timeout = await cliInput.number('Timeout (seconds)', {
|
|
512
|
+
initial: 30,
|
|
513
|
+
});
|
|
514
|
+
console.log(`Timeout set to ${timeout}s`);
|
|
515
|
+
break;
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
```
|
|
520
|
+
|
|
521
|
+
## Error Handling
|
|
522
|
+
|
|
523
|
+
All prompts handle errors gracefully:
|
|
524
|
+
|
|
525
|
+
- **User cancellation (ESC/Ctrl+C):** Process exits with code 0
|
|
526
|
+
- **Other errors:** Error is logged and process exits with code 1
|
|
527
|
+
|
|
528
|
+
For the `number()` prompt specifically, non-cancellation errors return `null` instead of exiting.
|
|
529
|
+
|
|
530
|
+
## License
|
|
531
|
+
|
|
532
|
+
MIT
|