@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 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(['gunshi'], { dev: false })
148
+ await installPackage([`gunshi@${version}`], { dev: false })
145
149
  // Install @gunshi/docs as a dev dependency (for LLM-assisted development)
146
- await installPackage(['@gunshi/docs'], { dev: true })
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
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@gunshi/docs",
3
3
  "description": "Documentation for gunshi",
4
- "version": "0.27.0-beta.6",
4
+ "version": "0.27.2",
5
5
  "author": {
6
6
  "name": "kazuya kawaguchi",
7
7
  "email": "kawakazu80@gmail.com"
@@ -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,
@@ -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?` | \| `Map`\<`string`, \| [`Command`](Command.md)\<`any`\> \| [`LazyCommand`](../type-aliases/LazyCommand.md)\<`any`, \{ \}\>\> \| `Record`\<`string`, \| [`Command`](Command.md)\<`any`\> \| [`LazyCommand`](../type-aliases/LazyCommand.md)\<`any`, \{ \}\>\> | Sub commands. |
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
- await cli(
58
- process.argv.slice(2),
59
- {
60
- name: 'server',
61
- run: () => {
62
- console.log('Starting server...')
63
- }
64
- },
65
- {
66
- name: 'my-app',
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
- // Define lifecycle hooks
70
- onBeforeCommand: ctx => {
71
- console.log(`About to run: ${ctx.name}`)
72
- },
68
+ // Define lifecycle hooks
69
+ onBeforeCommand: ctx => {
70
+ console.log(`About to run: ${ctx.name}`)
71
+ },
73
72
 
74
- onAfterCommand: (ctx, result) => {
75
- console.log(`Command ${ctx.name} completed successfully`)
76
- },
73
+ onAfterCommand: (ctx, result) => {
74
+ console.log(`Command ${ctx.name} completed successfully`)
75
+ },
77
76
 
78
- onErrorCommand: (ctx, error) => {
79
- console.error(`Command ${ctx.name} failed:`, error)
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
- const command = {
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
- const command = {
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
- const command = {
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
- const command = {
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
- const command = {
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
- const command = {
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
- const command = {
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
- const command = {
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
- const command = {
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
- const command = {
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
- const command = {
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 returns translations for the current locale
148
- resource: async ctx => {
149
- const locale = ctx.extensions['g:i18n'].locale
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: async ctx => {
337
- const locale = ctx.extensions['g:i18n'].locale
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@beta
45
+ npx @gunshi/docs
73
46
  ```
74
47
 
75
48
  ```sh [pnpm]
76
- pnpm dlx @gunshi/docs@beta
49
+ pnpm dlx @gunshi/docs
77
50
  ```
78
51
 
79
52
  ```sh [yarn]
80
- yarn dlx @gunshi/docs@beta
53
+ yarn dlx @gunshi/docs
81
54
  ```
82
55
 
83
56
  ```sh [bun]
84
- bun x @gunshi/docs@beta
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@beta gunshi@beta
74
+ npm install --save-dev @gunshi/docs
102
75
  ```
103
76
 
104
77
  ```sh [pnpm]
105
- pnpm add -D @gunshi/docs@beta gunshi@beta
78
+ pnpm add -D @gunshi/docs
106
79
  ```
107
80
 
108
81
  ```sh [yarn]
109
- yarn add -D @gunshi/docs@beta gunshi@beta
82
+ yarn add -D @gunshi/docs
110
83
  ```
111
84
 
112
85
  ```sh [bun]
113
- bun add -D @gunshi/docs@beta gunshi@beta
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(() => {})
@@ -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: async ctx => ({
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: async ctx => ({
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