@gunshi/docs 0.27.0-beta.4

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.
@@ -0,0 +1,68 @@
1
+ # What's Gunshi?
2
+
3
+ Gunshi is a modern JavaScript command-line library designed to simplify the creation of command-line interfaces (CLIs).
4
+
5
+ ## Origin of the Name
6
+
7
+ The name "gunshi" (č»åø«) refers to a position in ancient Japanese samurai battles where samurai devised strategies and gave orders. This name is inspired by the word "command", reflecting the library's purpose of handling command-line commands.
8
+
9
+ ## Key Features
10
+
11
+ Gunshi is designed with several powerful features to make CLI development easier and more maintainable:
12
+
13
+ - šŸ“ **Simple & Universal**: Run commands with simple API and support for universal runtime (Node.js, Deno, Bun).
14
+ - āš™ļø **Declarative & Type Safe**: Configure commands declaratively with full TypeScript support and type-safe argument parsing by [args-tokens](https://github.com/kazupon/args-tokens)
15
+ - 🧩 **Composable & Lazy**: Create modular sub-commands with context sharing and lazy loading for better performance.
16
+ - šŸŽØ **Flexible Rendering**: Customize usage generation, validation errors, and help messages with pluggable renderers.
17
+ - šŸŒ **Internationalization**: Built with global users in mind, featuring locale-aware design, resource management, and multi-language support.
18
+ - šŸ”Œ **Pluggable**: Extensible plugin system with dependency management and lifecycle hooks for modular CLI development.
19
+
20
+ ## Why Gunshi?
21
+
22
+ Gunshi provides a modern approach to building command-line interfaces in JavaScript and TypeScript. It's designed to be:
23
+
24
+ - **Developer-friendly**: Simple API with TypeScript support
25
+ - **Flexible**: Compose commands and customize behavior as needed
26
+ - **Maintainable**: Declarative configuration makes code easier to understand and maintain
27
+ - **Performant**: Lazy loading ensures resources are only loaded when needed
28
+
29
+ Whether you're building a simple CLI tool or a complex command-line application with multiple sub-commands, Gunshi provides the features you need to create a great user experience.
30
+
31
+ ## Next Steps
32
+
33
+ Now that you understand what Gunshi is and its key features, here's how to proceed with the documentation:
34
+
35
+ ### Documentation Structure
36
+
37
+ The Gunshi documentation is organized into three main sections:
38
+
39
+ - **Essentials**: Learn the fundamental concepts of Gunshi through a step-by-step tutorial format. This section covers everything from basic usage to composable commands and lazy loading.
40
+
41
+ - **Advanced**: Explore specialized features organized by topic. Each chapter focuses on a specific advanced capability like internationalization, custom rendering, or type system extensions.
42
+
43
+ - **Plugin**: Understand the plugin system through a tutorial approach. Learn how to create, configure, and distribute plugins for extending Gunshi's functionality.
44
+
45
+ ### Where to Go Next
46
+
47
+ <!-- eslint-disable markdown/no-missing-label-refs -->
48
+
49
+ > [!TIP]
50
+ > Start with the **Setup** guide to install Gunshi in your project, then proceed through the **Essentials** section in order. Each chapter builds upon previous concepts.
51
+
52
+ <!-- eslint-enable markdown/no-missing-label-refs -->
53
+
54
+ 1. **[Setup](./setup.md)** - Install and configure Gunshi in your project
55
+ 2. **[Getting Started](../essentials/getting-started.md)** - Create your first CLI application
56
+ 3. Continue through the **Essentials** section to learn core concepts
57
+
58
+ After completing the essentials, you can explore the **Advanced** section based on your specific needs. The chapters there are independent and can be read in any order.
59
+
60
+ If you're interested in extending Gunshi with plugins, the **Plugin** section provides comprehensive guidance on creating and using plugins.
61
+
62
+ ## Credits
63
+
64
+ Gunshi project is made possible by the open-source community and the many innovative tools in the JavaScript and TypeScript ecosystem. We extend our gratitude to all contributors and maintainers whose work has laid the foundation for this project.
65
+
66
+ - [`citty`](https://github.com/unjs/citty), Provided the original inspiration for modern JavaScript command-line library.
67
+ - [`ordana`](https://github.com/sapphi-red/ordana), Inspired documentation generation in Gunshi
68
+ - [`@bomb.sh/tab`](https://github.com/bombshell-dev/tab), Powered by shell auto-completion for the JavaScript ecosystem
@@ -0,0 +1,545 @@
1
+ # Plugin Decorators
2
+
3
+ Decorators are a powerful mechanism in Gunshi's plugin system that allows you to wrap and enhance existing functionality.
4
+
5
+ This guide explains how to effectively use decorators in your plugins.
6
+
7
+ ## Understanding Decorator Mechanism
8
+
9
+ In Gunshi, decorators create a wrapping structure around the original functionality.
10
+
11
+ Gunshi implements two types of decorators with different processing methods:
12
+
13
+ - **Command Decorators**: Processed using `reduceRight`, creating a nested wrapper structure
14
+ - **Renderer Decorators**: Processed using a `for` loop, building a chain of transformations
15
+
16
+ ## Command Decorators
17
+
18
+ Command decorators wrap command execution for cross-cutting concerns like logging, authentication, and error handling.
19
+
20
+ Unlike renderer decorators that only affect output formatting, command decorators can control the entire execution flow, including validation, authentication, logging, and error handling.
21
+
22
+ ### How Command Decorators Work
23
+
24
+ Command decorators use the `decorateCommand()` method provided by the `PluginContext`.
25
+
26
+ Each decorator receives a runner function (the next decorator or original command) and returns a new function that wraps it:
27
+
28
+ ```js
29
+ ctx.decorateCommand(runner => async ctx => {
30
+ // Pre-execution logic
31
+ console.log('Before command')
32
+
33
+ // Call the next decorator or original command
34
+ const result = await runner(ctx)
35
+
36
+ // Post-execution logic
37
+ console.log('After command')
38
+
39
+ return result
40
+ })
41
+ ```
42
+
43
+ ### Command Decorator Execution Order
44
+
45
+ Gunshi applies command decorators using the `reduceRight` method, which processes the decorator array from the last element to the first.
46
+
47
+ This approach creates a nested wrapper structure where the first registered decorator becomes the outermost layer.
48
+
49
+ The following diagram illustrates the wrapper structure:
50
+
51
+ ```mermaid
52
+ graph TB
53
+ A["User Input"] --> B["Decorator A<br/>First registered - Outermost wrapper<br/>&nbsp;"]
54
+ B --> C["Decorator B<br/>Second registered"]
55
+ C --> D["Decorator C<br/>Last registered - Innermost wrapper<br/>&nbsp;"]
56
+ D --> E["Original Command"]
57
+ E --> F["Result"]
58
+
59
+ style B fill:#9B59B6,stroke:#633974,stroke-width:2px,color:#fff
60
+ style C fill:#4A90E2,stroke:#2E5A8E,stroke-width:2px,color:#fff
61
+ style D fill:#468c56,stroke:#2e5936,stroke-width:2px,color:#fff
62
+ ```
63
+
64
+ ### Basic Command Decorator Example
65
+
66
+ The following example demonstrates the execution order when using `reduceRight`:
67
+
68
+ ```js [plugin.js]
69
+ import { plugin } from 'gunshi/plugin'
70
+
71
+ export default plugin({
72
+ id: 'my-plugin',
73
+ setup(ctx) {
74
+ // Registered first
75
+ ctx.decorateCommand(runner => async ctx => {
76
+ console.log('Decorator A: before')
77
+ const result = await runner(ctx)
78
+ console.log('Decorator A: after')
79
+ return result
80
+ })
81
+
82
+ // Registered second
83
+ ctx.decorateCommand(runner => async ctx => {
84
+ console.log('Decorator B: before')
85
+ const result = await runner(ctx)
86
+ console.log('Decorator B: after')
87
+ return result
88
+ })
89
+
90
+ // Registered third (executes first!)
91
+ ctx.decorateCommand(runner => async ctx => {
92
+ console.log('Decorator C: before')
93
+ const result = await runner(ctx)
94
+ console.log('Decorator C: after')
95
+ return result
96
+ })
97
+ }
98
+ })
99
+ ```
100
+
101
+ Application codes:
102
+
103
+ ```js [cli.js]
104
+ import { cli } from 'gunshi'
105
+ import lifo from './plugin.js'
106
+
107
+ await cli(
108
+ process.argv.slice(2),
109
+ () => {
110
+ console.log('Original command execution')
111
+ },
112
+ {
113
+ plugins: [lifo]
114
+ }
115
+ )
116
+ ```
117
+
118
+ <!-- eslint-disable markdown/no-missing-label-refs -->
119
+
120
+ > [!TIP]
121
+ > The example fully code is [here](https://github.com/kazupon/gunshi/tree/main/playground/plugins/decorators/lifo).
122
+
123
+ <!-- eslint-enable markdown/no-missing-label-refs -->
124
+
125
+ When executed, `reduceRight` creates a wrapper structure where Decorator A wraps B, B wraps C, and C wraps the original command:
126
+
127
+ ```sh
128
+ node cli.js
129
+ Decorator A: before # Outermost wrapper executes first
130
+ Decorator B: before # Middle wrapper
131
+ Decorator C: before # Innermost wrapper
132
+ Original command execution
133
+ Decorator C: after # Innermost completes first
134
+ Decorator B: after # Middle completes
135
+ Decorator A: after # Outermost completes last
136
+ ```
137
+
138
+ ### Advanced Command Decorator Example
139
+
140
+ Here's a complete example demonstrating how multiple command decorators work together for different purposes:
141
+
142
+ ```js [plugin.js]
143
+ import { plugin } from 'gunshi/plugin'
144
+
145
+ const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
146
+
147
+ export default plugin({
148
+ id: 'multi-decorator',
149
+ setup(ctx) {
150
+ // First decorator: Logging
151
+ ctx.decorateCommand(runner => async ctx => {
152
+ console.log('[LOG] Command started:', ctx.name)
153
+ const result = await runner(ctx)
154
+ console.log('[LOG] Command completed')
155
+ return result
156
+ })
157
+
158
+ // Second decorator: Timing
159
+ ctx.decorateCommand(runner => async ctx => {
160
+ const start = Date.now()
161
+ await sleep(10)
162
+ const result = await runner(ctx)
163
+ console.log(`[TIME] Execution: ${Date.now() - start}ms`)
164
+ return result
165
+ })
166
+
167
+ // Third decorator: Error wrapper
168
+ ctx.decorateCommand(runner => async ctx => {
169
+ try {
170
+ console.log('[ERROR] Monitoring enabled')
171
+ return await runner(ctx)
172
+ } catch (error) {
173
+ console.error('[ERROR] Command failed:', error.message)
174
+ throw error
175
+ }
176
+ })
177
+ }
178
+ })
179
+ ```
180
+
181
+ ```js [cli.js]
182
+ import { cli, define } from 'gunshi'
183
+ import multi from './plugin.js'
184
+
185
+ const command = define({
186
+ name: 'process',
187
+ run: ctx => {
188
+ console.log('>>> Executing actual command <<<')
189
+ return 'Command result'
190
+ }
191
+ })
192
+
193
+ await cli(process.argv.slice(2), command, {
194
+ plugins: [multi]
195
+ })
196
+ ```
197
+
198
+ <!-- eslint-disable markdown/no-missing-label-refs -->
199
+
200
+ > [!TIP]
201
+ > The example fully code is [here](https://github.com/kazupon/gunshi/tree/main/playground/plugins/decorators/command).
202
+
203
+ <!-- eslint-enable markdown/no-missing-label-refs -->
204
+
205
+ Running `node cli.js` outputs:
206
+
207
+ ```sh
208
+ [LOG] Command started: process
209
+ [ERROR] Monitoring enabled
210
+ >>> Executing actual command <<<
211
+ [TIME] Execution: 11ms
212
+ [LOG] Command completed
213
+ ```
214
+
215
+ <!-- eslint-disable markdown/no-missing-label-refs -->
216
+
217
+ > [!NOTE]
218
+ > The `@gunshi/plugin-global` plugin uses a command decorator to intercept `--help` and `--version` options, preventing normal command execution and triggering rendering instead.
219
+
220
+ <!-- eslint-enable markdown/no-missing-label-refs -->
221
+
222
+ ## Renderer Decorators
223
+
224
+ Gunshi provides a powerful API for customizing how your CLI displays information through renderer decorators.
225
+
226
+ These decorators allow you to wrap and enhance the rendering of headers, usage/help messages, and validation errors, enabling consistent styling, branding, and enhanced user experience across your CLI application.
227
+
228
+ ### How Renderer Decorators Work
229
+
230
+ Gunshi applies renderer decorators using a standard `for` loop that iterates through the decorator array from first to last.
231
+
232
+ Each iteration wraps the previous renderer function, building a chain of decorators.
233
+
234
+ This approach means that each decorator in the array wraps the accumulated result of all previous decorators, with each decorator receiving the previous renderer as its `baseRenderer` parameter.
235
+
236
+ ### Available Renderer Decorator Methods
237
+
238
+ Gunshi provides three renderer decorator methods via `PluginContext`:
239
+
240
+ - **`decorateHeaderRenderer`**: Customizes command headers (title/branding)
241
+ - **`decorateUsageRenderer`**: Enhances usage and help message display
242
+ - **`decorateValidationErrorsRenderer`**: Formats validation error messages
243
+
244
+ Each decorator receives the base renderer function and must call it to maintain the decorator chain.
245
+
246
+ This ensures that multiple plugins can cooperatively enhance the output.
247
+
248
+ ### Complete Rendering Customization Example
249
+
250
+ Here's a comprehensive example showing how to customize all three renderers in a single plugin.
251
+
252
+ This plugin adds branding to headers, appends metadata to usage messages, and enhances error formatting:
253
+
254
+ ```js [plugin.js]
255
+ import { plugin } from 'gunshi/plugin'
256
+
257
+ export default plugin({
258
+ id: 'custom-renderer',
259
+ setup(ctx) {
260
+ // Add branding to header
261
+ ctx.decorateHeaderRenderer(async (baseRenderer, ctx) => {
262
+ const header = await baseRenderer(ctx)
263
+ return `šŸš€ My CLI v${ctx.env.version}\n${header}`
264
+ })
265
+
266
+ // Append timestamp to usage
267
+ ctx.decorateUsageRenderer(async (baseRenderer, ctx) => {
268
+ const usage = await baseRenderer(ctx)
269
+ return `${usage}\n\nGenerated: ${new Date().toISOString()}`
270
+ })
271
+
272
+ // Format validation errors with emoji
273
+ ctx.decorateValidationErrorsRenderer(async (baseRenderer, ctx, error) => {
274
+ const errors = await baseRenderer(ctx, error)
275
+ return `āŒ Validation Error:\n${errors}`
276
+ })
277
+ }
278
+ })
279
+ ```
280
+
281
+ Application code:
282
+
283
+ ```js [cli.js]
284
+ import { cli, define } from 'gunshi'
285
+ import customRenderer from './plugin.js'
286
+
287
+ await cli(
288
+ process.argv.slice(2),
289
+ define({
290
+ name: 'build',
291
+ args: {
292
+ output: { type: 'string', required: true }
293
+ },
294
+ run: ctx => console.log(`Building to ${ctx.values.output}`)
295
+ }),
296
+ {
297
+ name: 'my-cli',
298
+ version: '1.0.0',
299
+ plugins: [customRenderer]
300
+ }
301
+ )
302
+ ```
303
+
304
+ <!-- eslint-disable markdown/no-missing-label-refs -->
305
+
306
+ > [!TIP]
307
+ > The example fully code is [here](https://github.com/kazupon/gunshi/tree/main/playground/plugins/decorators/renderers).
308
+
309
+ <!-- eslint-enable markdown/no-missing-label-refs -->
310
+
311
+ Run with `--help` to see customized output:
312
+
313
+ ```sh
314
+ node cli.js --help
315
+ šŸš€ My CLI v1.0.0
316
+ my-cli (my-cli v1.0.0)
317
+
318
+ USAGE:
319
+ my-cli <OPTIONS>
320
+
321
+ OPTIONS:
322
+ -h, --help Display this help message
323
+ -v, --version Display this version
324
+ --output <output>
325
+
326
+
327
+ Generated: 2025-08-15T14:26:43.121Z
328
+ ```
329
+
330
+ ### Multiple Plugin Decorator Execution Order
331
+
332
+ When multiple plugins register renderer decorators, the order matters.
333
+
334
+ Gunshi uses two built-in plugins by default: `@gunshi/plugin-global` (adds `--help` and `--version` options) and `@gunshi/plugin-renderer` (provides default rendering).
335
+
336
+ When you add your own plugins, they interact with these default plugins in a specific order based on how the `for` loop processes the decorators.
337
+
338
+ #### Plugin Registration and Decorator Chain Building
339
+
340
+ The following diagram shows how plugins are registered and how the `for` loop builds the decorator chain:
341
+
342
+ <h5 style="text-align: center; padding: 1em; margin: 1em;">Plugin Registration Order</h5>
343
+
344
+ ```mermaid
345
+ graph TB
346
+ R1["1. plugin-global<br/>Command Decorator"]
347
+ R2["2. plugin-renderer<br/>Renderer Decorators"]
348
+ R3["3. custom-plugin-A<br/>Renderer Decorator"]
349
+ R4["4. custom-plugin-B<br/>Renderer Decorator"]
350
+ R1 --> R2
351
+ R2 --> R3
352
+ R3 --> R4
353
+
354
+ style R1 fill:#9B59B6,stroke:#633974,stroke-width:2px,color:#fff
355
+ style R2 fill:#4A90E2,stroke:#2E5A8E,stroke-width:2px,color:#fff
356
+ style R3 fill:#E67E22,stroke:#A35D18,stroke-width:2px,color:#fff
357
+ style R4 fill:#E67E22,stroke:#A35D18,stroke-width:2px,color:#fff
358
+ ```
359
+
360
+ <h5 style="text-align: center; padding: 1em; margin: 1em">Renderer Decorator Chain (for loop builds)</h5>
361
+
362
+ ```mermaid
363
+ graph TB
364
+ E1["Base Renderer<br/>empty string<br/>&nbsp;"]
365
+ E2["plugin-renderer wraps base"]
366
+ E3["custom-A wraps plugin-renderer"]
367
+ E4["custom-B wraps custom-A"]
368
+ E1 --> E2
369
+ E2 --> E3
370
+ E3 --> E4
371
+
372
+ style E1 fill:#468c56,stroke:#2e5936,stroke-width:2px,color:#fff
373
+ style E2 fill:#4A90E2,stroke:#2E5A8E,stroke-width:2px,color:#fff
374
+ style E3 fill:#E67E22,stroke:#A35D18,stroke-width:2px,color:#fff
375
+ style E4 fill:#E67E22,stroke:#A35D18,stroke-width:2px,color:#fff
376
+ ```
377
+
378
+ #### How Default and Custom Plugins Interact
379
+
380
+ Here's an example showing how the default Gunshi plugins work together with custom plugins:
381
+
382
+ custom-plugin-A:
383
+
384
+ ```js [plugin-a.js]
385
+ import { plugin } from 'gunshi/plugin'
386
+
387
+ export default plugin({
388
+ id: 'custom-a',
389
+ setup(ctx) {
390
+ ctx.decorateUsageRenderer(async (baseRenderer, ctx) => {
391
+ const usage = await baseRenderer(ctx) // Call next decorator first
392
+ console.log('[custom-a] Decorating usage')
393
+ return `${usage}\nšŸ“¦ Enhanced by Plugin A`
394
+ })
395
+ }
396
+ })
397
+ ```
398
+
399
+ custom-plugin-B:
400
+
401
+ ```js [plugin-b.js]
402
+ import { plugin } from 'gunshi/plugin'
403
+
404
+ export default plugin({
405
+ id: 'custom-b',
406
+ setup(ctx) {
407
+ ctx.decorateUsageRenderer(async (baseRenderer, ctx) => {
408
+ const usage = await baseRenderer(ctx) // Call next decorator first
409
+ console.log('[custom-b] Decorating usage')
410
+ return `${usage}\nšŸŽØ Styled by Plugin B`
411
+ })
412
+ }
413
+ })
414
+ ```
415
+
416
+ Last, install all plugins on CLI application:
417
+
418
+ ```js [cli.js]
419
+ import { cli, define } from 'gunshi' // Includes plugin-global and plugin-renderer by default
420
+ import pluginA from './plugin-a.js'
421
+ import pluginB from './plugin-b.js'
422
+
423
+ await cli(
424
+ process.argv.slice(2),
425
+ define({
426
+ name: 'demo',
427
+ run: () => console.log('Demo command')
428
+ }),
429
+ {
430
+ name: 'my-cli',
431
+ version: '1.0.0',
432
+ renderHeader: null, // Disable default header rendering
433
+ // Custom plugins are added after default plugins
434
+ plugins: [pluginA, pluginB]
435
+ }
436
+ )
437
+ ```
438
+
439
+ <!-- eslint-disable markdown/no-missing-label-refs -->
440
+
441
+ > [!TIP]
442
+ > The example fully code is [here](https://github.com/kazupon/gunshi/tree/main/playground/plugins/decorators/multiple-order).
443
+
444
+ <!-- eslint-enable markdown/no-missing-label-refs -->
445
+
446
+ #### Execution Flow Breakdown
447
+
448
+ When you run `node index.js --help`, two different types of decorators work together:
449
+
450
+ **1. Command Decorator (`@gunshi/plugin-global`):**
451
+
452
+ - Intercepts the `--help` option
453
+ - Calls the renderer functions to generate output
454
+
455
+ **2. Renderer Decorators (chain built by for loop):**
456
+
457
+ The `for` loop builds a chain where:
458
+
459
+ - custom-plugin-B wraps custom-plugin-A
460
+ - custom-plugin-A wraps plugin-renderer
461
+ - plugin-renderer wraps the base renderer (empty string)
462
+
463
+ **Execution flow when each decorator calls `baseRenderer` first:**
464
+
465
+ 1. custom-plugin-B decorator starts → calls `baseRenderer`
466
+ 2. custom-plugin-A decorator starts → calls `baseRenderer`
467
+ 3. plugin-renderer decorator executes → returns full usage
468
+ 4. custom-plugin-A continues → logs and adds "šŸ“¦ Enhanced by Plugin A"
469
+ 5. custom-plugin-B continues → logs and adds "šŸŽØ Styled by Plugin B"
470
+
471
+ The console output in this example:
472
+
473
+ ```sh
474
+ [custom-a] Decorating usage // Logs after its baseRenderer returns
475
+ [custom-b] Decorating usage // Logs after its baseRenderer returns
476
+ ```
477
+
478
+ And the final rendered output:
479
+
480
+ ```sh
481
+ USAGE:
482
+ my-cli <OPTIONS>
483
+
484
+ OPTIONS:
485
+ -h, --help Display this help message
486
+ -v, --version Display this version
487
+
488
+ šŸ“¦ Enhanced by Plugin A
489
+ šŸŽØ Styled by Plugin B
490
+ ```
491
+
492
+ #### Understanding the Chain
493
+
494
+ The renderer decorator chain works differently than you might expect:
495
+
496
+ ```js
497
+ // Actual execution flow for renderer decorators
498
+ const base = await baseRenderer(ctx) // Returns ""
499
+ const afterRenderer = await rendererDecorator(base, ctx) // Doesn't call base, returns full usage
500
+ const afterCustomA = await customADecorator(afterRenderer, ctx) // Adds "Enhanced by Plugin A"
501
+ const final = await customBDecorator(afterCustomA, ctx) // Adds "Styled by Plugin B"
502
+ ```
503
+
504
+ <!-- eslint-disable markdown/no-missing-label-refs -->
505
+
506
+ > [!NOTE]
507
+ > `@gunshi/plugin-global` uses a **command decorator** to handle `--help`/`--version` options, while `@gunshi/plugin-renderer` uses **renderer decorators** to format the output. The base renderer returns an empty string, and `@gunshi/plugin-renderer` provides the actual implementation.
508
+
509
+ <!-- eslint-enable markdown/no-missing-label-refs -->
510
+
511
+ <!-- eslint-disable markdown/no-missing-label-refs -->
512
+
513
+ > [!IMPORTANT]
514
+ > Always call `baseRenderer` in your decorator to maintain the decorator chain. While `@gunshi/plugin-renderer` replaces the empty base renderer with full implementation, your custom decorators should enhance the output from previous decorators in the chain.
515
+
516
+ <!-- eslint-enable markdown/no-missing-label-refs -->
517
+
518
+ ### Important Considerations
519
+
520
+ **Always call `baseRenderer` in your decorator to maintain the decorator chain. Skipping it will break other plugins that may depend on the output.**
521
+
522
+ <!-- eslint-disable markdown/no-missing-label-refs -->
523
+
524
+ > [!NOTE]
525
+ > Renderer decorators have the lowest priority in Gunshi's rendering system. Command-level and CLI-level renderers will override plugin decorators. See [Rendering Customization](../advanced/custom-rendering.md) for details on renderer priority.
526
+
527
+ <!-- eslint-enable markdown/no-missing-label-refs -->
528
+
529
+ ## Command vs Renderer Decorators
530
+
531
+ Understanding the difference between these two decorator types is crucial:
532
+
533
+ | Aspect | Command Decorator | Renderer Decorator |
534
+ | -------------- | ---------------------------------- | --------------------------------------- |
535
+ | **Purpose** | Wraps command execution | Wraps output rendering |
536
+ | **Method** | `ctx.decorateCommand()` | `ctx.decorateUsageRenderer()`, etc. |
537
+ | **Can modify** | Command behavior, flow control | Output formatting only |
538
+ | **Can access** | Full CommandContext | CommandContext + render-specific params |
539
+ | **Use cases** | Auth, logging, validation, caching | Styling, i18n, branding |
540
+
541
+ ## Next Steps
542
+
543
+ With decorators, you've learned how to wrap and enhance command behavior and rendering output. This mechanism enables cross-cutting concerns like authentication, logging, and custom formatting without modifying command implementations.
544
+
545
+ The next chapter, [Plugin Extensions](./extensions.md), will show you how plugins can share functionality with commands through context extensions, creating a communication channel between plugins and the rest of your CLI application.