bob-core 2.0.0-beta.7 → 2.0.0-beta.9

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 CHANGED
@@ -1,275 +1,416 @@
1
- # BOB Core - Bash Operation Buddy Core
1
+ <div align="center">
2
2
 
3
- ## Introduction
3
+ ```
4
+ ____ ____ ____
5
+ | __ ) / __ \| __ )
6
+ | _ \| | | | _ \
7
+ | |_) | | | | |_) |
8
+ |____/ \____/|____/
9
+ ```
10
+
11
+ # BOB Core
12
+
13
+ **Your Bash Operation Buddy** 💪
14
+
15
+ *Build powerful TypeScript CLIs with type-safe commands and beautiful interactive prompts*
16
+
17
+ [![npm version](https://img.shields.io/npm/v/bob-core.svg)](https://www.npmjs.com/package/bob-core)
18
+ [![License: ISC](https://img.shields.io/badge/License-ISC-blue.svg)](https://opensource.org/licenses/ISC)
19
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.7-blue)](https://www.typescriptlang.org/)
20
+
21
+ [Features](#features) • [Installation](#installation) • [Quick Start](#quick-start) • [Documentation](#documentation) • [Examples](#examples)
22
+
23
+ </div>
24
+
25
+ ---
26
+
27
+ ## Features
28
+
29
+ ✨ **Type-Safe Commands** - Full TypeScript support with type inference for arguments and options
30
+ 🎯 **Declarative API** - Define commands with simple schemas or string signatures
31
+ 💬 **Interactive Prompts** - Built-in support for confirmations, selections, inputs, and more
32
+ 🎨 **Beautiful Help** - Automatically generated, well-formatted help documentation
33
+ 🔍 **Smart Suggestions** - Fuzzy matching suggests similar commands when you make typos
34
+ 📦 **Zero Config** - Works out of the box with sensible defaults
35
+ 🚀 **Dual Module Support** - Both CommonJS and ESM supported
36
+ ⚡️ **Fast & Lightweight** - Minimal dependencies, maximum performance
4
37
 
5
- BOB (Bash Operation Buddy) Core is a library that provides a set of functions to create your own CLI in TypeScript easily.
38
+ ---
6
39
 
7
- ## Usage
40
+ ## Installation
8
41
 
9
42
  ```bash
10
43
  npm install bob-core
11
44
  ```
12
45
 
13
- Initialize the CLI:
46
+ ```bash
47
+ yarn add bob-core
48
+ ```
49
+
50
+ ```bash
51
+ pnpm add bob-core
52
+ ```
53
+
54
+ ---
55
+
56
+ ## Quick Start
57
+
58
+ ### Create your CLI
14
59
 
15
60
  ```typescript
16
- import { CLI } from 'bob-core';
61
+ // cli.ts
62
+ import { Cli } from 'bob-core';
17
63
 
18
- const cli = new CLI();
64
+ const cli = new Cli({
65
+ name: 'my-cli',
66
+ version: '1.0.0'
67
+ });
19
68
 
20
69
  await cli.withCommands('./commands');
21
70
 
22
- cli.run('commandName', 'arg1', 'arg2', 'arg3');
71
+ const exitCode = await cli.runCommand(process.argv[2], ...process.argv.slice(3));
72
+ process.exit(exitCode);
23
73
  ```
24
74
 
25
- Create a command in the `commands` folder:
75
+ ### Create a Command
26
76
 
27
- ```typescript
28
- import { LegacyCommand } from 'bob-core';
29
-
30
- export default class MyCommand extends LegacyCommand {
31
- public name = 'my-command {arg1} {--option1}';
32
- public description = 'This is my command';
33
-
34
- /**
35
- * Define the arguments and options help
36
- *
37
- * Optional
38
- */
39
- helpDefinition = {
40
- arg1: 'This is the first argument',
41
- '--option1': 'This is the first option'
42
- }
77
+ **Modern Schema-Based (Recommended):**
43
78
 
44
- /**
45
- * Provide examples of how to use the command
46
- *
47
- * Optional
48
- */
49
- commandsExamples = [
50
- {
51
- command: 'my-command value1 --option1',
52
- description: 'This is an example'
53
- }
54
- ]
55
-
56
- public async handle() {
57
- console.log('Hello World');
58
- console.log('Arguments:', this.argument('arg1'));
59
- console.log('Options:', this.option('option1'));
79
+ ```typescript
80
+ // commands/greet.ts
81
+ import { Command } from 'bob-core';
82
+
83
+ export default new Command('greet', {
84
+ description: 'Greet a user',
85
+ arguments: {
86
+ name: 'string'
87
+ },
88
+ options: {
89
+ enthusiastic: {
90
+ type: 'boolean',
91
+ alias: ['e'],
92
+ default: false,
93
+ description: 'Add enthusiasm!'
94
+ }
60
95
  }
61
- }
96
+ }).handler((ctx, { arguments: args, options }) => {
97
+ const greeting = `Hello, ${args.name}${options.enthusiastic ? '!' : '.'}`;
98
+ console.log(greeting);
99
+ });
62
100
  ```
63
101
 
64
- ## Cli Help
102
+ **Modern Class-Based:**
65
103
 
66
- The CLI provides a help command that displays all available commands and their descriptions.
104
+ ```typescript
105
+ // commands/greet.ts
106
+ import { Command, CommandHandlerOptions, OptionsSchema } from 'bob-core';
107
+
108
+ const GreetOptions = {
109
+ enthusiastic: {
110
+ type: 'boolean',
111
+ alias: ['e'],
112
+ default: false,
113
+ description: 'Add enthusiasm!'
114
+ }
115
+ } satisfies OptionsSchema;
116
+ type GreetOptions = typeof GreetOptions;
117
+
118
+ const GreetArguments = {
119
+ name: 'string'
120
+ } satisfies OptionsSchema;
121
+ type GreetArguments = typeof GreetArguments;
122
+
123
+ export default class GreetCommand extends Command<any, GreetOptions, GreetArguments> {
124
+ constructor() {
125
+ super('greet', {
126
+ description: 'Greet a user',
127
+ options: GreetOptions,
128
+ arguments: GreetArguments
129
+ });
130
+ }
67
131
 
68
- ```bash
69
- node cli.js help
132
+ handle(ctx: any, opts: CommandHandlerOptions<GreetOptions, GreetArguments>) {
133
+ const greeting = `Hello, ${opts.arguments.name}${opts.options.enthusiastic ? '!' : '.'}`;
134
+ console.log(greeting);
135
+ }
136
+ }
70
137
  ```
71
138
 
72
- ## Commands
73
-
74
- ```bash
75
- Bob CLI x.x.x 💪
139
+ **Signature-Based:**
76
140
 
77
- Usage:
78
- command [options] [arguments]
141
+ ```typescript
142
+ // commands/greet.ts
143
+ import { CommandWithSignature } from 'bob-core';
79
144
 
80
- Available commands:
145
+ export default class GreetCommand extends CommandWithSignature {
146
+ signature = 'greet {name} {--enthusiastic|-e}';
147
+ description = 'Greet a user';
81
148
 
82
- help Show help
83
- test test description
84
- sub:
85
- sub sub command description
86
- sub:sub sub:sub command description
149
+ protected async handle() {
150
+ const name = this.argument<string>('name');
151
+ const enthusiastic = this.option<boolean>('enthusiastic');
87
152
 
153
+ const greeting = `Hello, ${name}${enthusiastic ? '!' : '.'}`;
154
+ console.log(greeting);
155
+ }
156
+ }
88
157
  ```
89
158
 
90
- ## LegacyCommand help
91
-
92
- You can also display the help of a specific command.
159
+ ### Run It
93
160
 
94
161
  ```bash
95
- node cli.js test -h
96
- ```
162
+ $ node cli.js greet John
163
+ Hello, John.
97
164
 
98
- ```bash
165
+ $ node cli.js greet Jane --enthusiastic
166
+ Hello, Jane!
167
+
168
+ $ node cli.js help greet
99
169
  Description:
100
- test description
170
+ Greet a user
101
171
 
102
172
  Usage:
103
- test <user> [options]
173
+ greet <name> [options]
104
174
 
105
175
  Arguments:
106
- user user description
107
- test test description [default: null]
108
- test2 [default: []] (variadic)
176
+ name (string)
109
177
 
110
178
  Options:
111
- --option, -o, -b option description (boolean) [default: false]
112
- --flag flag description (string) [default: null]
113
- --arr arr description (array) [default: null]
114
- --flag2 flag2 description (string) [default: 2]
115
- --help, -h Display help for the given command. When no command is given display help for the list command (boolean)
116
-
117
- Examples:
118
- Example description 1
119
-
120
- node cli.js test yayo --option
121
-
122
- Example description 2
123
-
124
- node cli.js test anothervalue --flag=2
179
+ --enthusiastic, -e Add enthusiasm! (boolean) [default: false]
180
+ --help, -h Display help for the given command
125
181
  ```
126
182
 
127
- Depending on the command, the help will display the command signature, description, arguments, options and examples.
128
-
183
+ ---
129
184
 
130
- ## Commands signature
185
+ ## Interactive Prompts
131
186
 
132
- The command signature is a string that defines the command name, arguments and options.
187
+ Build beautiful interactive CLIs with built-in prompts:
133
188
 
134
- Example:
135
189
  ```typescript
136
- signature = 'my-command {arg1} {arg2} {arg3} {--option1} {--option2}';
190
+ import { Command } from 'bob-core';
191
+
192
+ export default new Command('setup', {
193
+ description: 'Interactive project setup'
194
+ }).handler(async () => {
195
+ // Text input
196
+ const name = await this.io.askForInput('Project name:');
197
+
198
+ // Confirmation
199
+ const useTypeScript = await this.io.askForConfirmation('Use TypeScript?', true);
200
+
201
+ // Selection
202
+ const framework = await this.io.askForSelect('Framework:', [
203
+ { title: 'React', value: 'react' },
204
+ { title: 'Vue', value: 'vue' },
205
+ { title: 'Svelte', value: 'svelte' }
206
+ ]);
207
+
208
+ // Multi-select
209
+ const features = await this.io.askForSelect(
210
+ 'Features:',
211
+ ['ESLint', 'Prettier', 'Testing'],
212
+ { type: 'multiselect' }
213
+ );
214
+
215
+ // Spinner/loader
216
+ using loader = this.io.newLoader('Creating project...');
217
+ await createProject({ name, framework, features });
218
+ loader.stop();
219
+
220
+ this.io.info('✅ Project created!');
221
+ });
137
222
  ```
138
223
 
139
- ### Arguments
224
+ ---
140
225
 
141
- All user supplied arguments and options are wrapped in curly braces.
142
- In the following example, the command defines three **required** arguments `arg1`, `arg2` and `arg3`.
143
-
144
- ```typescript
145
- signature = 'my-command {arg1} {arg2} {arg3}';
146
- ```
226
+ ## Context Injection
147
227
 
148
- You may want to make an argument optional by adding a `?` after the argument name or by providing a default value with `=`.
228
+ Pass shared dependencies and configuration to your commands:
149
229
 
150
230
  ```typescript
151
- signature = 'my-command {arg1} {arg2?} {arg3=defaultValue}';
231
+ interface AppContext {
232
+ config: Config;
233
+ database: Database;
234
+ logger: Logger;
235
+ }
152
236
 
153
- handle() {
154
- this.argument('arg1'); // 'value' or throw an error if not provided
155
- this.argument('arg2'); // 'value' or null if not provided
156
- this.argument('arg3'); // 'value' or 'defaultValue' if not provided
157
- }
237
+ const context: AppContext = {
238
+ config: loadConfig(),
239
+ database: new Database(),
240
+ logger: new Logger()
241
+ };
242
+
243
+ const cli = new Cli<AppContext>({
244
+ ctx: context,
245
+ name: 'my-app',
246
+ version: '1.0.0'
247
+ });
248
+
249
+ // Access context in commands
250
+ export default new Command<AppContext>('users:list', {
251
+ description: 'List users'
252
+ }).handler(async (ctx) => {
253
+ const users = await ctx.database.getUsers();
254
+ users.forEach(user => console.log(user.name));
255
+ });
158
256
  ```
159
257
 
160
- You can also define a variadic argument by adding `*` after the argument name.
161
- Variadic arguments are stored in an array.
258
+ ---
162
259
 
163
- ```typescript
164
- signature = 'my-command {arg1} {arg2*}';
260
+ ## Documentation
165
261
 
166
- handle() {
167
- this.argument('arg1'); // 'value1'
168
- this.argument('arg2'); // ['value2', 'value3']
169
- }
170
- ```
262
+ 📚 **[Getting Started](./docs/getting-started.md)** - Installation and first CLI
263
+ 🔨 **[Creating Commands](./docs/creating-commands.md)** - Schema-based and signature-based approaches
264
+ ⚙️ **[Arguments & Options](./docs/arguments-and-options.md)** - Type-safe parameters
265
+ 💬 **[Interactive Prompts](./docs/interactive-prompts.md)** - Build interactive CLIs
266
+ 🚀 **[Advanced Topics](./docs/advanced.md)** - Context, resolvers, error handling
267
+ ❓ **[Help System](./docs/help-system.md)** - Customize help output
268
+ 📖 **[API Reference](./docs/api-reference.md)** - Complete API documentation
269
+ 💡 **[Examples](./docs/examples.md)** - Real-world examples
171
270
 
172
- Variadic arguments can also be optional.
271
+ ---
272
+
273
+ ## Examples
274
+
275
+ ### Type-Safe Arguments
173
276
 
174
277
  ```typescript
175
- signature = 'my-command {arg1} {arg2*?}';
278
+ export default new Command('deploy', {
279
+ arguments: {
280
+ environment: 'string', // Required
281
+ region: { // Optional with default
282
+ type: 'string',
283
+ default: 'us-east-1',
284
+ required: false
285
+ }
286
+ }
287
+ }).handler((ctx, { arguments: args }) => {
288
+ // args.environment is string
289
+ // args.region is string | null
290
+ });
176
291
  ```
177
292
 
178
- ### Options
179
-
180
- Options are defined by `{--optionName}`.
293
+ ### Variadic Arguments
181
294
 
182
295
  ```typescript
183
- signature = 'my-command {--option1} {--option2} {--option3}';
296
+ export default new Command('delete', {
297
+ arguments: {
298
+ files: ['string'] // Array of strings
299
+ }
300
+ }).handler((ctx, { arguments: args }) => {
301
+ // args.files is string[]
302
+ args.files.forEach(file => deleteFile(file));
303
+ });
184
304
  ```
185
305
 
186
- By default options are boolean with a default value of `false`.
187
- You can also change the option type to string by adding `=` to the option definition.
188
- You can also provide a default value by adding `=value`.
189
-
190
- If the value is 'true' or 'false', the option will be converted to a boolean.
306
+ ### Options with Aliases
191
307
 
192
308
  ```typescript
193
- signature = 'my-command {--option1} {--option2=true} {--option3=} {--option4=defaultValue} {--option5=}';
194
-
195
- handle() {
196
- this.option('option1'); // by default `false` and can be set to `true` by the user
197
- this.option('option2'); // by default `true` and can be set to `false` by the user
198
- this.option('option3'); // by default `null` and can be set to "value" by the user
199
- this.option('option4'); // by default "defaultValue" and can be set to "value" by the user
309
+ export default new Command('serve', {
310
+ options: {
311
+ port: {
312
+ type: 'number',
313
+ alias: ['p'],
314
+ default: 3000
315
+ },
316
+ verbose: {
317
+ type: 'boolean',
318
+ alias: ['v', 'V'],
319
+ default: false
200
320
  }
321
+ }
322
+ });
323
+
324
+ // Usage: serve --port=8080 -v
325
+ // Usage: serve -p 8080 --verbose
201
326
  ```
202
327
 
203
- You can also define a variadic option by adding `*` as option value. (e.g. `{--option2=*}`)
204
- Variadic options are stored in an array.
328
+ ### Pre-Handlers for Validation
205
329
 
206
330
  ```typescript
207
- signature = 'my-command {--option1} {--option2=*}';
208
-
209
- handle() {
210
- this.option('option1'); // 'value1'
211
- this.option('option2'); // ['value2', 'value3'] // or [] if not provided
331
+ export default new Command('deploy')
332
+ .preHandler(async (ctx) => {
333
+ if (!ctx.isAuthenticated) {
334
+ console.error('Not authenticated');
335
+ return 1; // Stop execution
212
336
  }
337
+ })
338
+ .handler(async (ctx) => {
339
+ // Only runs if authenticated
340
+ });
213
341
  ```
214
342
 
215
- ## Commands I/O
216
-
217
- ### Arguments
343
+ ### Command Groups
218
344
 
219
345
  ```typescript
220
- this.argument('arg1');
346
+ // commands/db/migrate.ts
347
+ export default new Command('db:migrate', {
348
+ description: 'Run migrations',
349
+ group: 'Database'
350
+ });
351
+
352
+ // commands/db/seed.ts
353
+ export default new Command('db:seed', {
354
+ description: 'Seed database',
355
+ group: 'Database'
356
+ });
357
+
358
+ // Displayed as:
359
+ // Database:
360
+ // db:migrate Run migrations
361
+ // db:seed Seed database
221
362
  ```
222
363
 
223
- You can also provide a default value if the argument is optional.
364
+ ---
224
365
 
225
- ```typescript
226
- this.argument('arg1', 'defaultValue');
227
- ```
366
+ ## Why BOB Core?
228
367
 
229
- If you always need a boolean value, you can use the `argumentBoolean` method.
368
+ BOB Core makes CLI development in TypeScript a breeze:
230
369
 
231
- ```typescript
232
- this.argumentBoolean('arg1');
233
- ```
370
+ - **No Boilerplate** - Define commands declaratively, not imperatively
371
+ - **Type Safety** - Catch errors at compile time, not runtime
372
+ - **Great DX** - Intelligent auto-complete, clear error messages
373
+ - **User Friendly** - Beautiful help, smart suggestions, interactive prompts
374
+ - **Flexible** - Multiple command styles, extend anything
375
+ - **Well Tested** - Comprehensive test suite with Vitest
234
376
 
235
- If you always need a number value, you can use the `argumentNumber` method.
377
+ ---
236
378
 
237
- ```typescript
238
- this.argumentNumber('arg1');
239
- ```
379
+ ## Supported Types
240
380
 
241
- If you always need a array value, you can use the `argumentArray` method.
381
+ | Type | Description | Example |
382
+ |------|-------------|---------|
383
+ | `'string'` | Text value | `'hello'` |
384
+ | `'number'` | Numeric value | `42` |
385
+ | `'boolean'` | True/false | `true` |
386
+ | `'secret'` | Masked input | `'****'` |
387
+ | `['string']` | String array | `['a', 'b']` |
388
+ | `['number']` | Number array | `[1, 2, 3]` |
242
389
 
243
- ```typescript
244
- this.argumentArray('arg1');
245
- ```
390
+ ---
246
391
 
247
- ### Options
392
+ ## Contributing
248
393
 
249
- ```typescript
250
- this.option('option1');
251
- ```
394
+ Contributions are welcome! Please feel free to submit a Pull Request.
252
395
 
253
- You can also provide a default value if the option is optional.
396
+ 1. Fork the repository
397
+ 2. Create your feature branch (`git checkout -b feature/amazing-feature`)
398
+ 3. Commit your changes (`git commit -m 'Add amazing feature'`)
399
+ 4. Push to the branch (`git push origin feature/amazing-feature`)
400
+ 5. Open a Pull Request
254
401
 
255
- ```typescript
256
- this.option('option1', 'defaultValue');
257
- ```
402
+ ---
258
403
 
259
- If you always need a boolean value, you can use the `optionBoolean` method.
404
+ ## License
260
405
 
261
- ```typescript
262
- this.optionBoolean('option1');
263
- ```
406
+ [ISC](LICENSE) © Léo Hubert
264
407
 
265
- If you always need a number value, you can use the `optionNumber` method.
408
+ ---
266
409
 
267
- ```typescript
268
- this.optionNumber('option1');
269
- ```
410
+ <div align="center">
270
411
 
271
- If you always need a array value, you can use the `optionArray` method.
412
+ **[⬆ back to top](#bob-core)**
272
413
 
273
- ```typescript
274
- this.optionArray('option1');
275
- ```
414
+ Made with ❤️ by developers, for developers
415
+
416
+ </div>