@gunshi/docs 0.27.0-beta.6 → 0.27.2
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/bin/init.js +6 -2
- package/package.json +1 -1
- package/src/api/default/functions/cli.md +28 -0
- package/src/api/default/index.md +1 -0
- package/src/api/default/interfaces/CliOptions.md +1 -1
- package/src/api/default/interfaces/SubCommandable.md +35 -0
- package/src/guide/advanced/command-hooks.md +21 -23
- package/src/guide/advanced/internationalization.md +3 -3
- package/src/guide/essentials/declarative.md +51 -28
- package/src/guide/essentials/getting-started.md +3 -3
- package/src/guide/essentials/plugin-system.md +5 -7
- package/src/guide/introduction/setup.md +8 -35
- package/src/guide/plugin/guidelines.md +3 -3
- package/src/guide/plugin/testing.md +14 -12
- package/src/release/v0.27.md +63 -8
package/bin/init.js
CHANGED
|
@@ -7,6 +7,10 @@ import readline from 'node:readline'
|
|
|
7
7
|
import { x } from 'tinyexec'
|
|
8
8
|
import { installPackage } from '@antfu/install-pkg'
|
|
9
9
|
|
|
10
|
+
const { version } = JSON.parse(
|
|
11
|
+
await fs.readFile(path.join(import.meta.dirname, '../package.json'), 'utf8')
|
|
12
|
+
)
|
|
13
|
+
|
|
10
14
|
const SKILL_NAME = 'use-gunshi-cli'
|
|
11
15
|
|
|
12
16
|
const USE_GUNSHI_PROMPT = `
|
|
@@ -141,9 +145,9 @@ if (answer === 'y' || answer === 'yes') {
|
|
|
141
145
|
console.log('\nInstalling gunshi and @gunshi/docs...')
|
|
142
146
|
try {
|
|
143
147
|
// Install gunshi as a production dependency
|
|
144
|
-
await installPackage([
|
|
148
|
+
await installPackage([`gunshi@${version}`], { dev: false })
|
|
145
149
|
// Install @gunshi/docs as a dev dependency (for LLM-assisted development)
|
|
146
|
-
await installPackage([
|
|
150
|
+
await installPackage([`@gunshi/docs@${version}`], { dev: true })
|
|
147
151
|
console.log('Successfully installed gunshi and @gunshi/docs')
|
|
148
152
|
} catch (error) {
|
|
149
153
|
console.error('Failed to install packages:', error.message)
|
package/package.json
CHANGED
|
@@ -22,6 +22,34 @@ A [CLI options](../interfaces/CliOptions.md)
|
|
|
22
22
|
|
|
23
23
|
## Call Signature
|
|
24
24
|
|
|
25
|
+
```ts
|
|
26
|
+
function cli(
|
|
27
|
+
args,
|
|
28
|
+
entry,
|
|
29
|
+
options?): Promise<string | undefined>;
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Run the command.
|
|
33
|
+
|
|
34
|
+
This overload accepts any command-like object using a loose structural type.
|
|
35
|
+
It bypasses TypeScript contravariance issues with callback properties.
|
|
36
|
+
|
|
37
|
+
### Parameters
|
|
38
|
+
|
|
39
|
+
| Parameter | Type | Description |
|
|
40
|
+
| ------ | ------ | ------ |
|
|
41
|
+
| `args` | `string`[] | Command line arguments |
|
|
42
|
+
| `entry` | [`SubCommandable`](../interfaces/SubCommandable.md) | A command-like object (command, command runner, or lazy command) |
|
|
43
|
+
| `options?` | [`CliOptions`](../interfaces/CliOptions.md)\<[`DefaultGunshiParams`](../type-aliases/DefaultGunshiParams.md)\> | A [CLI options](../interfaces/CliOptions.md) |
|
|
44
|
+
|
|
45
|
+
### Returns
|
|
46
|
+
|
|
47
|
+
`Promise`\<`string` \| `undefined`\>
|
|
48
|
+
|
|
49
|
+
A rendered usage or undefined. if you will use [`CliOptions.usageSilent`](../interfaces/CliOptions.md#usagesilent) option, it will return rendered usage string.
|
|
50
|
+
|
|
51
|
+
## Call Signature
|
|
52
|
+
|
|
25
53
|
```ts
|
|
26
54
|
function cli<G>(
|
|
27
55
|
args,
|
package/src/api/default/index.md
CHANGED
|
@@ -61,6 +61,7 @@ import { cli } from 'gunshi'
|
|
|
61
61
|
| [PluginWithExtension](interfaces/PluginWithExtension.md) | Plugin return type with extension, which includes the plugin ID, name, dependencies, and extension. |
|
|
62
62
|
| [PluginWithoutExtension](interfaces/PluginWithoutExtension.md) | Plugin return type without extension, which includes the plugin ID, name, and dependencies, but no extension. |
|
|
63
63
|
| [RenderingOptions](interfaces/RenderingOptions.md) | Rendering control options |
|
|
64
|
+
| [SubCommandable](interfaces/SubCommandable.md) | Sub-command entry type for use in subCommands. |
|
|
64
65
|
|
|
65
66
|
## References
|
|
66
67
|
|
|
@@ -27,7 +27,7 @@ CLI options of [`cli`](../functions/cli.md) function.
|
|
|
27
27
|
| <a id="renderheader"></a> `renderHeader?` | (`ctx`) => `Promise`\<`string`\> \| `null` | Render function the header section in the command usage. |
|
|
28
28
|
| <a id="renderusage"></a> `renderUsage?` | (`ctx`) => `Promise`\<`string`\> \| `null` | Render function the command usage. |
|
|
29
29
|
| <a id="rendervalidationerrors"></a> `renderValidationErrors?` | (`ctx`, `error`) => `Promise`\<`string`\> \| `null` | Render function the validation errors. |
|
|
30
|
-
| <a id="subcommands"></a> `subCommands?` | \| `
|
|
30
|
+
| <a id="subcommands"></a> `subCommands?` | \| `Record`\<`string`, [`SubCommandable`](SubCommandable.md)\> \| `Map`\<`string`, [`SubCommandable`](SubCommandable.md)\> | Sub commands. |
|
|
31
31
|
| <a id="usageoptiontype"></a> `usageOptionType?` | `boolean` | Whether to display the usage optional argument type. |
|
|
32
32
|
| <a id="usageoptionvalue"></a> `usageOptionValue?` | `boolean` | Whether to display the optional argument value. |
|
|
33
33
|
| <a id="usagesilent"></a> `usageSilent?` | `boolean` | Whether to display the command usage. |
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
[gunshi](../../index.md) / [default](../index.md) / SubCommandable
|
|
2
|
+
|
|
3
|
+
# Interface: SubCommandable
|
|
4
|
+
|
|
5
|
+
Sub-command entry type for use in subCommands.
|
|
6
|
+
|
|
7
|
+
This type uses a loose structural match to bypass TypeScript's contravariance issues
|
|
8
|
+
with function parameters, allowing any Command or LazyCommand to be used as a sub-command.
|
|
9
|
+
|
|
10
|
+
## Since
|
|
11
|
+
|
|
12
|
+
v0.27.1
|
|
13
|
+
|
|
14
|
+
## Indexable
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
[key: string]: any
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Index signature to allow additional properties
|
|
21
|
+
|
|
22
|
+
## Properties
|
|
23
|
+
|
|
24
|
+
| Property | Type | Description |
|
|
25
|
+
| ------ | ------ | ------ |
|
|
26
|
+
| <a id="args"></a> `args?` | [`Args`](Args.md) \| `Record`\<`string`, `any`\> | Command arguments - accepts any args structure |
|
|
27
|
+
| <a id="commandname"></a> `commandName?` | `string` | Command name for lazy commands |
|
|
28
|
+
| <a id="description"></a> `description?` | `string` | Command description |
|
|
29
|
+
| <a id="entry"></a> `entry?` | `boolean` | Whether this is an entry command |
|
|
30
|
+
| <a id="examples"></a> `examples?` | `string` \| (...`args`) => `any` | Command examples |
|
|
31
|
+
| <a id="internal"></a> `internal?` | `boolean` | Whether this is an internal command |
|
|
32
|
+
| <a id="name"></a> `name?` | `string` | Command name |
|
|
33
|
+
| <a id="rendering"></a> `rendering?` | `any` | Rendering options |
|
|
34
|
+
| <a id="run"></a> `run?` | (...`args`) => `any` | Command runner |
|
|
35
|
+
| <a id="tokebab"></a> `toKebab?` | `boolean` | Whether to convert camelCase to kebab-case |
|
|
@@ -52,34 +52,32 @@ The following example demonstrates how to configure lifecycle hooks when initial
|
|
|
52
52
|
In this setup, we define three hooks that will execute at different stages of the command lifecycle:
|
|
53
53
|
|
|
54
54
|
```ts [cli.ts]
|
|
55
|
-
import { cli } from 'gunshi'
|
|
55
|
+
import { cli, define } from 'gunshi'
|
|
56
56
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
{
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
version: '1.0.0',
|
|
57
|
+
const command = define({
|
|
58
|
+
name: 'server',
|
|
59
|
+
run: () => {
|
|
60
|
+
console.log('Starting server...')
|
|
61
|
+
}
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
await cli(process.argv.slice(2), command, {
|
|
65
|
+
name: 'my-app',
|
|
66
|
+
version: '1.0.0',
|
|
68
67
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
68
|
+
// Define lifecycle hooks
|
|
69
|
+
onBeforeCommand: ctx => {
|
|
70
|
+
console.log(`About to run: ${ctx.name}`)
|
|
71
|
+
},
|
|
73
72
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
73
|
+
onAfterCommand: (ctx, result) => {
|
|
74
|
+
console.log(`Command ${ctx.name} completed successfully`)
|
|
75
|
+
},
|
|
77
76
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
}
|
|
77
|
+
onErrorCommand: (ctx, error) => {
|
|
78
|
+
console.error(`Command ${ctx.name} failed:`, error)
|
|
81
79
|
}
|
|
82
|
-
)
|
|
80
|
+
})
|
|
83
81
|
```
|
|
84
82
|
|
|
85
83
|
<!-- eslint-disable markdown/no-missing-label-refs -->
|
|
@@ -153,16 +153,16 @@ MY_LANG=ja-JP node cli.ts --name 田中 --formal
|
|
|
153
153
|
The `@gunshi/resources` package provides pre-translated resources for common CLI terms:
|
|
154
154
|
|
|
155
155
|
```ts
|
|
156
|
-
import { cli } from 'gunshi'
|
|
156
|
+
import { cli, define } from 'gunshi'
|
|
157
157
|
import i18n from '@gunshi/plugin-i18n'
|
|
158
158
|
import resources from '@gunshi/resources'
|
|
159
159
|
|
|
160
|
-
const command = {
|
|
160
|
+
const command = define({
|
|
161
161
|
name: 'app',
|
|
162
162
|
run: ctx => {
|
|
163
163
|
console.log('Application running')
|
|
164
164
|
}
|
|
165
|
-
}
|
|
165
|
+
})
|
|
166
166
|
|
|
167
167
|
await cli(process.argv.slice(2), command, {
|
|
168
168
|
name: 'my-app',
|
|
@@ -13,7 +13,9 @@ In that guide, we created a simple greeting command. As your CLI grows with more
|
|
|
13
13
|
A declaratively configured command in Gunshi follows this structure:
|
|
14
14
|
|
|
15
15
|
```js
|
|
16
|
-
|
|
16
|
+
import { define } from 'gunshi'
|
|
17
|
+
|
|
18
|
+
const command = define({
|
|
17
19
|
// Command metadata
|
|
18
20
|
name: 'command-name',
|
|
19
21
|
description: 'Command description',
|
|
@@ -30,7 +32,7 @@ const command = {
|
|
|
30
32
|
run: ctx => {
|
|
31
33
|
// Command implementation
|
|
32
34
|
}
|
|
33
|
-
}
|
|
35
|
+
})
|
|
34
36
|
```
|
|
35
37
|
|
|
36
38
|
Let's see how this structure works in practice with a complete example.
|
|
@@ -42,10 +44,10 @@ Let's start with a simple example that demonstrates the declarative approach.
|
|
|
42
44
|
We'll build a greeting command with a few basic options:
|
|
43
45
|
|
|
44
46
|
```js [cli.js]
|
|
45
|
-
import { cli } from 'gunshi'
|
|
47
|
+
import { cli, define } from 'gunshi'
|
|
46
48
|
|
|
47
49
|
// Define a command with declarative configuration
|
|
48
|
-
const command = {
|
|
50
|
+
const command = define({
|
|
49
51
|
// Command metadata
|
|
50
52
|
name: 'greet',
|
|
51
53
|
description: 'A greeting command with declarative configuration',
|
|
@@ -98,7 +100,7 @@ $ node cli.js --name Charlie --uppercase
|
|
|
98
100
|
|
|
99
101
|
console.log(message)
|
|
100
102
|
}
|
|
101
|
-
}
|
|
103
|
+
})
|
|
102
104
|
|
|
103
105
|
// Run the command with the declarative configuration
|
|
104
106
|
await cli(process.argv.slice(2), command, {
|
|
@@ -163,7 +165,7 @@ Each option can have the following properties:
|
|
|
163
165
|
<!-- eslint-enable markdown/no-missing-label-refs -->
|
|
164
166
|
- `description`: A description of what the option does
|
|
165
167
|
- `default`: Default value if the option is not provided
|
|
166
|
-
- `required`: Set to `true` if the option is required (Note: Positional arguments defined with `type: 'positional'` are implicitly required by the parser).
|
|
168
|
+
- `required`: Set to `true` if the option is required (Note: Positional arguments defined with `type: 'positional'` without `multiple: true` are implicitly required by the parser).
|
|
167
169
|
- `multiple`: Set to `true` if multiple option values are allowed
|
|
168
170
|
- `toKebab`: Set to `true` to convert camelCase argument names to kebab-case in help text and command-line usage
|
|
169
171
|
- `parse`: A function to parse and validate the argument value. Required when `type` is 'custom'
|
|
@@ -178,7 +180,9 @@ The _key_ you use for the argument in the `args` object serves as its name for a
|
|
|
178
180
|
Here's how you define positional arguments in your command configuration:
|
|
179
181
|
|
|
180
182
|
```js
|
|
181
|
-
|
|
183
|
+
import { define } from 'gunshi'
|
|
184
|
+
|
|
185
|
+
const command = define({
|
|
182
186
|
args: {
|
|
183
187
|
// ... other options
|
|
184
188
|
|
|
@@ -195,7 +199,7 @@ const command = {
|
|
|
195
199
|
}
|
|
196
200
|
// ... potentially more positional arguments
|
|
197
201
|
}
|
|
198
|
-
}
|
|
202
|
+
})
|
|
199
203
|
```
|
|
200
204
|
|
|
201
205
|
- **Implicitly Required**: Unlike named options which can be optional, positional arguments must always be provided by the user. When you define an argument with `type: 'positional'` in the schema, Gunshi expects it to be present on the command line. If it's missing, a validation error will occur. They cannot be truly optional like named flags.
|
|
@@ -214,6 +218,7 @@ Gunshi supports custom argument types with user-defined parsing functions, allow
|
|
|
214
218
|
To define a custom argument type:
|
|
215
219
|
|
|
216
220
|
```js
|
|
221
|
+
import { define } from 'gunshi'
|
|
217
222
|
import { z } from 'zod'
|
|
218
223
|
|
|
219
224
|
// custom schema with `zod`
|
|
@@ -222,7 +227,7 @@ const config = z.object({
|
|
|
222
227
|
mode: z.string()
|
|
223
228
|
})
|
|
224
229
|
|
|
225
|
-
const command = {
|
|
230
|
+
const command = define({
|
|
226
231
|
name: 'example',
|
|
227
232
|
description: 'Example command with custom argument types',
|
|
228
233
|
args: {
|
|
@@ -264,7 +269,7 @@ const command = {
|
|
|
264
269
|
console.log('Config:', ctx.values.config) // Parsed JSON object
|
|
265
270
|
console.log('Port:', ctx.values.port) // Validated port number
|
|
266
271
|
}
|
|
267
|
-
}
|
|
272
|
+
})
|
|
268
273
|
```
|
|
269
274
|
|
|
270
275
|
Custom type arguments support:
|
|
@@ -295,7 +300,9 @@ Gunshi supports automatic conversion of camelCase argument names to kebab-case w
|
|
|
295
300
|
To apply kebab-case conversion to all arguments in a command, set the `toKebab` property at the command level:
|
|
296
301
|
|
|
297
302
|
```js
|
|
298
|
-
|
|
303
|
+
import { define } from 'gunshi'
|
|
304
|
+
|
|
305
|
+
const command = define({
|
|
299
306
|
name: 'example',
|
|
300
307
|
description: 'Example command',
|
|
301
308
|
toKebab: true, // Apply to all arguments
|
|
@@ -306,7 +313,7 @@ Gunshi supports automatic conversion of camelCase argument names to kebab-case w
|
|
|
306
313
|
run: ctx => {
|
|
307
314
|
/* ... */
|
|
308
315
|
}
|
|
309
|
-
}
|
|
316
|
+
})
|
|
310
317
|
```
|
|
311
318
|
|
|
312
319
|
2. **Argument level**: Apply to specific arguments only
|
|
@@ -314,7 +321,9 @@ Gunshi supports automatic conversion of camelCase argument names to kebab-case w
|
|
|
314
321
|
Alternatively, you can apply kebab-case conversion to specific arguments only by setting it at the argument level:
|
|
315
322
|
|
|
316
323
|
```js
|
|
317
|
-
|
|
324
|
+
import { define } from 'gunshi'
|
|
325
|
+
|
|
326
|
+
const command = define({
|
|
318
327
|
name: 'example',
|
|
319
328
|
description: 'Example command',
|
|
320
329
|
args: {
|
|
@@ -327,7 +336,7 @@ Gunshi supports automatic conversion of camelCase argument names to kebab-case w
|
|
|
327
336
|
run: ctx => {
|
|
328
337
|
/* ... */
|
|
329
338
|
}
|
|
330
|
-
}
|
|
339
|
+
})
|
|
331
340
|
```
|
|
332
341
|
|
|
333
342
|
When `toKebab` is enabled:
|
|
@@ -367,7 +376,9 @@ You can define mutually exclusive options using the `conflicts` property.
|
|
|
367
376
|
This ensures that conflicting options cannot be used together:
|
|
368
377
|
|
|
369
378
|
```js
|
|
370
|
-
|
|
379
|
+
import { define } from 'gunshi'
|
|
380
|
+
|
|
381
|
+
const command = define({
|
|
371
382
|
name: 'server',
|
|
372
383
|
description: 'Server configuration',
|
|
373
384
|
args: {
|
|
@@ -410,7 +421,7 @@ const command = {
|
|
|
410
421
|
// Minimal output
|
|
411
422
|
}
|
|
412
423
|
}
|
|
413
|
-
}
|
|
424
|
+
})
|
|
414
425
|
```
|
|
415
426
|
|
|
416
427
|
When conflicting options are used together, Gunshi will throw an error:
|
|
@@ -430,7 +441,9 @@ This helps users understand how to use your command correctly and is displayed i
|
|
|
430
441
|
You can provide examples as a simple string:
|
|
431
442
|
|
|
432
443
|
```js
|
|
433
|
-
|
|
444
|
+
import { define } from 'gunshi'
|
|
445
|
+
|
|
446
|
+
const command = define({
|
|
434
447
|
name: 'copy',
|
|
435
448
|
description: 'Copy files',
|
|
436
449
|
args: {
|
|
@@ -452,7 +465,7 @@ const command = {
|
|
|
452
465
|
run: ctx => {
|
|
453
466
|
// Implementation
|
|
454
467
|
}
|
|
455
|
-
}
|
|
468
|
+
})
|
|
456
469
|
```
|
|
457
470
|
|
|
458
471
|
#### Multiple Examples
|
|
@@ -460,7 +473,9 @@ const command = {
|
|
|
460
473
|
For multiple examples, use a multi-line string with clear formatting:
|
|
461
474
|
|
|
462
475
|
```js
|
|
463
|
-
|
|
476
|
+
import { define } from 'gunshi'
|
|
477
|
+
|
|
478
|
+
const command = define({
|
|
464
479
|
name: 'deploy',
|
|
465
480
|
description: 'Deploy application',
|
|
466
481
|
args: {
|
|
@@ -496,7 +511,7 @@ deploy --environment production --tag latest --dry-run
|
|
|
496
511
|
run: ctx => {
|
|
497
512
|
// Implementation
|
|
498
513
|
}
|
|
499
|
-
}
|
|
514
|
+
})
|
|
500
515
|
```
|
|
501
516
|
|
|
502
517
|
#### Dynamic Examples
|
|
@@ -506,7 +521,9 @@ You can also provide examples as a function that returns a string.
|
|
|
506
521
|
This is useful when examples need to be generated dynamically or localized:
|
|
507
522
|
|
|
508
523
|
```js
|
|
509
|
-
|
|
524
|
+
import { define } from 'gunshi'
|
|
525
|
+
|
|
526
|
+
const command = define({
|
|
510
527
|
name: 'serve',
|
|
511
528
|
description: 'Start development server',
|
|
512
529
|
args: {
|
|
@@ -543,7 +560,7 @@ ${appName} -h 192.168.1.100 -p 8080
|
|
|
543
560
|
run: ctx => {
|
|
544
561
|
console.log(`Server starting on ${ctx.values.host}:${ctx.values.port}`)
|
|
545
562
|
}
|
|
546
|
-
}
|
|
563
|
+
})
|
|
547
564
|
```
|
|
548
565
|
|
|
549
566
|
#### Formatted Examples with Descriptions
|
|
@@ -551,7 +568,9 @@ ${appName} -h 192.168.1.100 -p 8080
|
|
|
551
568
|
For better readability, you can include descriptions with your examples:
|
|
552
569
|
|
|
553
570
|
```js
|
|
554
|
-
|
|
571
|
+
import { define } from 'gunshi'
|
|
572
|
+
|
|
573
|
+
const command = define({
|
|
555
574
|
name: 'git-flow',
|
|
556
575
|
description: 'Git flow commands',
|
|
557
576
|
args: {
|
|
@@ -587,7 +606,7 @@ Notes:
|
|
|
587
606
|
run: ctx => {
|
|
588
607
|
// Implementation
|
|
589
608
|
}
|
|
590
|
-
}
|
|
609
|
+
})
|
|
591
610
|
```
|
|
592
611
|
|
|
593
612
|
#### Async Examples
|
|
@@ -597,7 +616,9 @@ For complex CLIs that need to load examples from external files or generate them
|
|
|
597
616
|
When using the function form, you can return a Promise for async example generation:
|
|
598
617
|
|
|
599
618
|
```js
|
|
600
|
-
|
|
619
|
+
import { define } from 'gunshi'
|
|
620
|
+
|
|
621
|
+
const command = define({
|
|
601
622
|
name: 'config',
|
|
602
623
|
description: 'Manage configuration',
|
|
603
624
|
args: {
|
|
@@ -627,7 +648,7 @@ config --get "api.key"
|
|
|
627
648
|
run: ctx => {
|
|
628
649
|
// Implementation
|
|
629
650
|
}
|
|
630
|
-
}
|
|
651
|
+
})
|
|
631
652
|
```
|
|
632
653
|
|
|
633
654
|
The examples are displayed when users run the command with `--help` flag, making it easier for them to understand the correct usage.
|
|
@@ -658,7 +679,9 @@ The `run` function receives a command context object (`ctx`) with:
|
|
|
658
679
|
Among the context properties, the `explicit` property deserves special attention as it allows you to determine whether an argument was explicitly provided by the user or if it's using a default value:
|
|
659
680
|
|
|
660
681
|
```js
|
|
661
|
-
|
|
682
|
+
import { define } from 'gunshi'
|
|
683
|
+
|
|
684
|
+
const command = define({
|
|
662
685
|
name: 'deploy',
|
|
663
686
|
args: {
|
|
664
687
|
environment: {
|
|
@@ -683,7 +706,7 @@ const command = {
|
|
|
683
706
|
console.log('User explicitly requested force mode')
|
|
684
707
|
}
|
|
685
708
|
}
|
|
686
|
-
}
|
|
709
|
+
})
|
|
687
710
|
```
|
|
688
711
|
|
|
689
712
|
This feature is particularly useful for:
|
|
@@ -86,9 +86,9 @@ Hello, Alice!
|
|
|
86
86
|
Let's add some options to our command:
|
|
87
87
|
|
|
88
88
|
```js [cli.js]
|
|
89
|
-
import { cli } from 'gunshi'
|
|
89
|
+
import { cli, define } from 'gunshi'
|
|
90
90
|
|
|
91
|
-
const command = {
|
|
91
|
+
const command = define({
|
|
92
92
|
name: 'greeter',
|
|
93
93
|
description: 'A simple greeting CLI',
|
|
94
94
|
args: {
|
|
@@ -113,7 +113,7 @@ const command = {
|
|
|
113
113
|
|
|
114
114
|
console.log(greeting)
|
|
115
115
|
}
|
|
116
|
-
}
|
|
116
|
+
})
|
|
117
117
|
|
|
118
118
|
await cli(process.argv.slice(2), command)
|
|
119
119
|
```
|
|
@@ -144,10 +144,9 @@ const resources = {
|
|
|
144
144
|
const command = defineI18n({
|
|
145
145
|
name: 'greet',
|
|
146
146
|
description: 'Greet someone in their language',
|
|
147
|
-
// Resource function
|
|
148
|
-
resource:
|
|
149
|
-
|
|
150
|
-
return resources[locale] || resources['en-US']
|
|
147
|
+
// Resource function receives locale and returns translations
|
|
148
|
+
resource: locale => {
|
|
149
|
+
return resources[locale.toString()] || resources['en-US']
|
|
151
150
|
},
|
|
152
151
|
args: {
|
|
153
152
|
name: {
|
|
@@ -333,9 +332,8 @@ const resources = {
|
|
|
333
332
|
const buildCommand = defineI18n({
|
|
334
333
|
name: 'build',
|
|
335
334
|
description: 'Build the project',
|
|
336
|
-
resource:
|
|
337
|
-
|
|
338
|
-
return resources[locale] || resources['en-US']
|
|
335
|
+
resource: locale => {
|
|
336
|
+
return resources[locale.toString()] || resources['en-US']
|
|
339
337
|
},
|
|
340
338
|
args: {
|
|
341
339
|
mode: {
|
|
@@ -31,33 +31,6 @@ bun add gunshi
|
|
|
31
31
|
|
|
32
32
|
:::
|
|
33
33
|
|
|
34
|
-
### v0.27 Beta
|
|
35
|
-
|
|
36
|
-
::: code-group
|
|
37
|
-
|
|
38
|
-
```sh [npm]
|
|
39
|
-
npm install --save gunshi@beta
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
```sh [pnpm]
|
|
43
|
-
pnpm add gunshi@beta
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
```sh [yarn]
|
|
47
|
-
yarn add gunshi@beta
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
```sh [deno]
|
|
51
|
-
## you can specify version with `@`
|
|
52
|
-
deno add jsr:@gunshi/gunshi@0.27.2
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
```sh [bun]
|
|
56
|
-
bun add gunshi@beta
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
:::
|
|
60
|
-
|
|
61
34
|
## LLM-Assisted Development
|
|
62
35
|
|
|
63
36
|
Gunshi provides tooling to integrate with AI coding assistants such as [Claude Code](https://claude.ai/code) and [Cursor](https://cursor.com/).
|
|
@@ -69,19 +42,19 @@ You can quickly set up LLM-assisted development for your project using the CLI t
|
|
|
69
42
|
::: code-group
|
|
70
43
|
|
|
71
44
|
```sh [npm]
|
|
72
|
-
npx @gunshi/docs
|
|
45
|
+
npx @gunshi/docs
|
|
73
46
|
```
|
|
74
47
|
|
|
75
48
|
```sh [pnpm]
|
|
76
|
-
pnpm dlx @gunshi/docs
|
|
49
|
+
pnpm dlx @gunshi/docs
|
|
77
50
|
```
|
|
78
51
|
|
|
79
52
|
```sh [yarn]
|
|
80
|
-
yarn dlx @gunshi/docs
|
|
53
|
+
yarn dlx @gunshi/docs
|
|
81
54
|
```
|
|
82
55
|
|
|
83
56
|
```sh [bun]
|
|
84
|
-
bun x @gunshi/docs
|
|
57
|
+
bun x @gunshi/docs
|
|
85
58
|
```
|
|
86
59
|
|
|
87
60
|
:::
|
|
@@ -98,19 +71,19 @@ If you prefer manual configuration, install the documentation package as a dev d
|
|
|
98
71
|
::: code-group
|
|
99
72
|
|
|
100
73
|
```sh [npm]
|
|
101
|
-
npm install --save-dev @gunshi/docs
|
|
74
|
+
npm install --save-dev @gunshi/docs
|
|
102
75
|
```
|
|
103
76
|
|
|
104
77
|
```sh [pnpm]
|
|
105
|
-
pnpm add -D @gunshi/docs
|
|
78
|
+
pnpm add -D @gunshi/docs
|
|
106
79
|
```
|
|
107
80
|
|
|
108
81
|
```sh [yarn]
|
|
109
|
-
yarn add -D @gunshi/docs
|
|
82
|
+
yarn add -D @gunshi/docs
|
|
110
83
|
```
|
|
111
84
|
|
|
112
85
|
```sh [bun]
|
|
113
|
-
bun add -D @gunshi/docs
|
|
86
|
+
bun add -D @gunshi/docs
|
|
114
87
|
```
|
|
115
88
|
|
|
116
89
|
:::
|
|
@@ -836,15 +836,15 @@ For other package managers, see [installation guide](./docs/install.md).
|
|
|
836
836
|
<!-- eslint-disable markdown/no-missing-label-refs, markdown/no-space-in-emphasis -->
|
|
837
837
|
|
|
838
838
|
\`\`\`ts
|
|
839
|
-
import { cli } from 'gunshi'
|
|
839
|
+
import { cli, define } from 'gunshi'
|
|
840
840
|
import myPlugin from '@yourorg/gunshi-plugin-{name}'
|
|
841
841
|
|
|
842
|
-
const command = {
|
|
842
|
+
const command = define({
|
|
843
843
|
name: 'example',
|
|
844
844
|
run: ctx => {
|
|
845
845
|
ctx.extensions['yourorg:{name}'].someMethod()
|
|
846
846
|
}
|
|
847
|
-
}
|
|
847
|
+
})
|
|
848
848
|
|
|
849
849
|
await cli(process.argv.slice(2), command, {
|
|
850
850
|
plugins: [myPlugin({ /* options */ })]
|
|
@@ -51,6 +51,7 @@ The following test file demonstrates basic plugin testing, including initializat
|
|
|
51
51
|
|
|
52
52
|
```ts [src/plugin.test.ts]
|
|
53
53
|
import { createCommandContext } from 'gunshi/plugin'
|
|
54
|
+
import { define } from 'gunshi'
|
|
54
55
|
import { describe, expect, test, vi } from 'vitest'
|
|
55
56
|
import { myPlugin } from './plugin.ts'
|
|
56
57
|
|
|
@@ -65,11 +66,11 @@ describe('plugin initialization', () => {
|
|
|
65
66
|
|
|
66
67
|
test('plugin extension factory creates correct methods', async () => {
|
|
67
68
|
const plugin = myPlugin()
|
|
68
|
-
const mockCommand = {
|
|
69
|
+
const mockCommand = define({
|
|
69
70
|
name: 'test',
|
|
70
71
|
description: 'Test command',
|
|
71
72
|
run: vi.fn()
|
|
72
|
-
}
|
|
73
|
+
})
|
|
73
74
|
|
|
74
75
|
const mockContext = await createCommandContext({
|
|
75
76
|
command: mockCommand
|
|
@@ -152,7 +153,7 @@ describe('configuration validation', () => {
|
|
|
152
153
|
|
|
153
154
|
test('plugin uses configuration in extension', async () => {
|
|
154
155
|
const plugin = myValidatingPlugin({ locale: 'ja-JP', timeout: 5000 })
|
|
155
|
-
const mockCommand = { name: 'test', description: 'Test', run: vi.fn() }
|
|
156
|
+
const mockCommand = define({ name: 'test', description: 'Test', run: vi.fn() })
|
|
156
157
|
|
|
157
158
|
const ctx = await createCommandContext({ command: mockCommand })
|
|
158
159
|
const extension = await plugin.extension.factory(ctx, mockCommand)
|
|
@@ -215,7 +216,7 @@ import { myPlugin } from './plugin.ts'
|
|
|
215
216
|
test('extension factory creates correct extension', async () => {
|
|
216
217
|
const plugin = myPlugin({ debug: true })
|
|
217
218
|
|
|
218
|
-
const command = { name: 'test', description: 'Test', run: vi.fn() }
|
|
219
|
+
const command = define({ name: 'test', description: 'Test', run: vi.fn() })
|
|
219
220
|
const ctx = await createCommandContext({
|
|
220
221
|
command,
|
|
221
222
|
values: { verbose: true },
|
|
@@ -238,6 +239,7 @@ The following example demonstrates how to test extension methods that interact w
|
|
|
238
239
|
|
|
239
240
|
```ts [src/plugin.test.ts]
|
|
240
241
|
import { createCommandContext } from 'gunshi/plugin'
|
|
242
|
+
import { define } from 'gunshi'
|
|
241
243
|
import { describe, expect, test, vi } from 'vitest'
|
|
242
244
|
import { myPlugin } from './plugin.ts'
|
|
243
245
|
|
|
@@ -245,7 +247,7 @@ describe('extension methods', () => {
|
|
|
245
247
|
test('showVersion displays version correctly', async () => {
|
|
246
248
|
const plugin = myPlugin()
|
|
247
249
|
|
|
248
|
-
const command = { name: 'app', description: 'App', run: vi.fn() }
|
|
250
|
+
const command = define({ name: 'app', description: 'App', run: vi.fn() })
|
|
249
251
|
const ctx = await createCommandContext({
|
|
250
252
|
command,
|
|
251
253
|
cliOptions: { version: '2.0.0', name: 'test-app' }
|
|
@@ -260,7 +262,7 @@ describe('extension methods', () => {
|
|
|
260
262
|
test('handles missing version', async () => {
|
|
261
263
|
const plugin = myPlugin()
|
|
262
264
|
|
|
263
|
-
const command = { name: 'app', description: 'App', run: vi.fn() }
|
|
265
|
+
const command = define({ name: 'app', description: 'App', run: vi.fn() })
|
|
264
266
|
const ctx = await createCommandContext({
|
|
265
267
|
command,
|
|
266
268
|
cliOptions: { version: undefined, name: 'test-app' }
|
|
@@ -290,7 +292,7 @@ describe('async extension', () => {
|
|
|
290
292
|
timeout: 5000
|
|
291
293
|
})
|
|
292
294
|
const plugin = myPlugin({ loadConfig })
|
|
293
|
-
const command = { name: 'test', description: 'Test', run: vi.fn() }
|
|
295
|
+
const command = define({ name: 'test', description: 'Test', run: vi.fn() })
|
|
294
296
|
const ctx = await createCommandContext({ command })
|
|
295
297
|
const extension = await plugin.extension.factory(ctx, command)
|
|
296
298
|
|
|
@@ -305,7 +307,7 @@ describe('async extension', () => {
|
|
|
305
307
|
const warnMock = vi.spyOn(console, 'warn').mockImplementation(() => {})
|
|
306
308
|
const loadConfig = vi.fn().mockRejectedValue(new Error('Config not found'))
|
|
307
309
|
const plugin = myPlugin({ loadConfig })
|
|
308
|
-
const command = { name: 'test', description: 'Test', run: vi.fn() }
|
|
310
|
+
const command = define({ name: 'test', description: 'Test', run: vi.fn() })
|
|
309
311
|
const ctx = await createCommandContext({ command })
|
|
310
312
|
const extension = await plugin.extension.factory(ctx, command)
|
|
311
313
|
|
|
@@ -453,7 +455,7 @@ import { trackerPlugin } from './init-tracker.ts'
|
|
|
453
455
|
describe('onExtension callback', () => {
|
|
454
456
|
test('extension not initialized before onExtension', async () => {
|
|
455
457
|
const plugin = trackerPlugin()
|
|
456
|
-
const command = { name: 'test', description: 'Test', run: vi.fn() }
|
|
458
|
+
const command = define({ name: 'test', description: 'Test', run: vi.fn() })
|
|
457
459
|
const ctx = await createCommandContext({ command })
|
|
458
460
|
const extension = await plugin.extension.factory(ctx, command)
|
|
459
461
|
|
|
@@ -463,7 +465,7 @@ describe('onExtension callback', () => {
|
|
|
463
465
|
|
|
464
466
|
test('onExtension initializes extension', async () => {
|
|
465
467
|
const plugin = trackerPlugin()
|
|
466
|
-
const command = { name: 'deploy', description: 'Deploy', run: vi.fn() }
|
|
468
|
+
const command = define({ name: 'deploy', description: 'Deploy', run: vi.fn() })
|
|
467
469
|
const ctx = await createCommandContext({ command })
|
|
468
470
|
const extension = await plugin.extension.factory(ctx, command)
|
|
469
471
|
await plugin.extension.onFactory?.(ctx, command)
|
|
@@ -695,7 +697,7 @@ import type { LoggingExtension, NotificationExtension } from './plugin.ts'
|
|
|
695
697
|
|
|
696
698
|
describe('plugin interactions', () => {
|
|
697
699
|
test('uses logger when available', async () => {
|
|
698
|
-
const command = { name: 'test', description: 'Test', run: vi.fn() }
|
|
700
|
+
const command = define({ name: 'test', description: 'Test', run: vi.fn() })
|
|
699
701
|
|
|
700
702
|
const logging = loggingPlugin()
|
|
701
703
|
const notification = notificationPlugin()
|
|
@@ -720,7 +722,7 @@ describe('plugin interactions', () => {
|
|
|
720
722
|
})
|
|
721
723
|
|
|
722
724
|
test('falls back when logger missing', async () => {
|
|
723
|
-
const command = { name: 'test', description: 'Test', run: vi.fn() }
|
|
725
|
+
const command = define({ name: 'test', description: 'Test', run: vi.fn() })
|
|
724
726
|
const notification = notificationPlugin()
|
|
725
727
|
|
|
726
728
|
const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {})
|
package/src/release/v0.27.md
CHANGED
|
@@ -180,6 +180,30 @@ Use cases:
|
|
|
180
180
|
- When built-in plugins conflict with your custom implementation
|
|
181
181
|
- For embedded CLIs where every byte counts
|
|
182
182
|
|
|
183
|
+
### `@gunshi/docs` - LLM-Assisted Development (Experimental)
|
|
184
|
+
|
|
185
|
+
New package for integrating with AI coding assistants such as [Claude Code](https://claude.ai/code) and [Cursor](https://cursor.com/):
|
|
186
|
+
|
|
187
|
+
```sh
|
|
188
|
+
npx @gunshi/docs
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
This command automatically configures:
|
|
192
|
+
|
|
193
|
+
- [Claude Code skills](https://docs.anthropic.com/en/docs/claude-code/skills) at `.claude/skills/use-gunshi-cli/SKILL.md`
|
|
194
|
+
- [Cursor rules](https://docs.cursor.com/context/rules-for-ai) at `.cursor/rules/use-gunshi-cli.mdc`
|
|
195
|
+
- Updates `CLAUDE.md` with instructions to use the Gunshi skill
|
|
196
|
+
|
|
197
|
+
Optionally, it can also install `gunshi` and `@gunshi/docs` packages for you.
|
|
198
|
+
|
|
199
|
+
For manual setup, install the documentation package as a dev dependency:
|
|
200
|
+
|
|
201
|
+
```sh
|
|
202
|
+
npm install --save-dev @gunshi/docs
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
The package includes guide content and API references as markdown files in [llms.txt](https://llmstxt.org/) format, enabling AI assistants to provide accurate guidance when developing Gunshi CLI applications.
|
|
206
|
+
|
|
183
207
|
### Fallback to Entry Command
|
|
184
208
|
|
|
185
209
|
New `fallbackToEntry` option enables graceful handling of unknown subcommands:
|
|
@@ -348,6 +372,35 @@ This feature enables:
|
|
|
348
372
|
|
|
349
373
|
## ⚡ Improvement Features
|
|
350
374
|
|
|
375
|
+
### Multiple Positional Argument Usage Display
|
|
376
|
+
|
|
377
|
+
The plugin-renderer now displays multiple positional arguments more clearly in usage output:
|
|
378
|
+
|
|
379
|
+
- Required positional: `<name>`
|
|
380
|
+
- Multiple positional: `[<name> ...]`
|
|
381
|
+
- Required + multiple: `<name> [<name> ...]`
|
|
382
|
+
|
|
383
|
+
This improvement helps users understand when a command accepts multiple positional arguments.
|
|
384
|
+
|
|
385
|
+
### i18n Plugin: `registerGlobalOptionResources` Extension
|
|
386
|
+
|
|
387
|
+
New extension method for dynamically registering global option resources:
|
|
388
|
+
|
|
389
|
+
```ts
|
|
390
|
+
const command = defineI18n({
|
|
391
|
+
name: 'app',
|
|
392
|
+
run: ctx => {
|
|
393
|
+
// Register custom global option descriptions
|
|
394
|
+
ctx.extensions['g:i18n'].registerGlobalOptionResources('custom-option', {
|
|
395
|
+
'en-US': 'Custom option description',
|
|
396
|
+
'ja-JP': 'カスタムオプションの説明'
|
|
397
|
+
})
|
|
398
|
+
}
|
|
399
|
+
})
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
This enables plugins to register localized descriptions for global options dynamically.
|
|
403
|
+
|
|
351
404
|
### Type Safety Enhancements
|
|
352
405
|
|
|
353
406
|
v0.27 brings comprehensive type safety improvements to all core APIs through generic type parameters, enabling full TypeScript inference for arguments and plugin extensions.
|
|
@@ -651,7 +704,7 @@ The i18n plugin comes with additional packages and helper functions to simplify
|
|
|
651
704
|
// Define new command with i18n support
|
|
652
705
|
const command = defineI18n({
|
|
653
706
|
name: 'deploy',
|
|
654
|
-
resource:
|
|
707
|
+
resource: locale => ({
|
|
655
708
|
/* translations */
|
|
656
709
|
}),
|
|
657
710
|
run: ctx => {
|
|
@@ -661,7 +714,7 @@ The i18n plugin comes with additional packages and helper functions to simplify
|
|
|
661
714
|
|
|
662
715
|
// Add i18n to existing command
|
|
663
716
|
const enhancedCommand = withI18nResource(existingCommand, {
|
|
664
|
-
resource:
|
|
717
|
+
resource: locale => ({
|
|
665
718
|
/* translations */
|
|
666
719
|
})
|
|
667
720
|
})
|
|
@@ -706,12 +759,14 @@ New playground examples demonstrating:
|
|
|
706
759
|
We'd like to thank all the contributors who made this release possible:
|
|
707
760
|
|
|
708
761
|
- [@kazupon](https://github.com/kazupon) - Core maintainer, plugin system architect, i18n extraction, lifecycle hooks
|
|
709
|
-
- [@yukukotani](https://github.com/yukukotani) - Fallback to entry command feature (#291)
|
|
710
|
-
- [@sushichan044](https://github.com/sushichan044) - Explicit argument detection feature (#232)
|
|
711
|
-
- [@43081j](https://github.com/43081j) - Exposed `Plugin` type (#159)
|
|
712
|
-
- [@theoephraim](https://github.com/theoephraim) - Documentation improvements (#249)
|
|
713
|
-
- [@lukekarrys](https://github.com/lukekarrys) - Documentation updates (#228)
|
|
714
|
-
- [@BobbieGoede](https://github.com/BobbieGoede) - Fixed FUNDING.yml (#303)
|
|
762
|
+
- [@yukukotani](https://github.com/yukukotani) - Fallback to entry command feature ([#291](https://github.com/kazupon/gunshi/pull/291))
|
|
763
|
+
- [@sushichan044](https://github.com/sushichan044) - Explicit argument detection feature ([#232](https://github.com/kazupon/gunshi/pull/232))
|
|
764
|
+
- [@43081j](https://github.com/43081j) - Exposed `Plugin` type ([#159](https://github.com/kazupon/gunshi/pull/159))
|
|
765
|
+
- [@theoephraim](https://github.com/theoephraim) - Documentation improvements ([#249](https://github.com/kazupon/gunshi/pull/249))
|
|
766
|
+
- [@lukekarrys](https://github.com/lukekarrys) - Documentation updates ([#228](https://github.com/kazupon/gunshi/pull/228))
|
|
767
|
+
- [@BobbieGoede](https://github.com/BobbieGoede) - Fixed FUNDING.yml ([#303](https://github.com/kazupon/gunshi/pull/303))
|
|
768
|
+
- [@ryoppippi](https://github.com/ryoppippi) - Docs init CLI feature ([#422](https://github.com/kazupon/gunshi/pull/422))
|
|
769
|
+
- [@ota-meshi](https://github.com/ota-meshi) - Multiple positional argument usage display improvement ([#432](https://github.com/kazupon/gunshi/pull/432))
|
|
715
770
|
|
|
716
771
|
Special thanks to the community for feedback and bug reports that helped shape v0.27!
|
|
717
772
|
|