Plugin System
What's a Plugin?
A plugin in Gunshi is a modular extension that adds functionality to your CLI application without modifying its core code.
Think of plugins as building blocks that you can snap together to create powerful command-line tools.
Why Use Plugins?
Plugins solve common CLI development challenges:
- Separation of Concerns: Plugins keep your command logic clean by handling cross-cutting functionality like logging, authentication, or database connections separately. Your commands focus on their primary task while plugins handle the supporting infrastructure.
- Reusability: Write functionality once, use it everywhere. A plugin created for one command can be reused across your entire CLI or even in different projects, saving development time and ensuring consistency.
- Composability: Plugins work together seamlessly. You can combine multiple plugins—an authentication plugin with a logging plugin and a database plugin—and they'll integrate naturally into your CLI's lifecycle.
How Plugins Work
Plugins integrate at specific points in your CLI's execution:
- Registration: When your CLI starts, plugins are registered and their dependencies are resolved
- Setup: Plugins initialize and configure themselves, adding any global options (like
--debug) - Extension: Plugins extend your command context with new functionality (like translation methods or API clients)
- Decoration: Plugins can modify how commands execute or how help text is displayed
- Execution: Your commands run with all plugin enhancements seamlessly integrated
This lifecycle integration means plugins can:
- Add global options available to all commands (like
--debugor--verbose) - Provide utilities accessible in any command (like API clients or database connections)
- Modify how your CLI displays help text or handles errors
- Intercept command execution for logging or validation
The beauty of Gunshi's plugin system is that it handles all the complexity behind the scenes.
You simply declare which plugins you want to use, and they automatically become part of your CLI's capabilities.
Now that you understand how plugins work, let's explore the plugins that come with Gunshi.
These built-in plugins provide essential CLI functionality out of the box.
Built-in Plugins
Gunshi provides a standard cli() function that comes pre-configured with two essential plugins.
These built-in plugins give your CLI the familiar behavior users expect from command-line tools.
Let's explore what each plugin provides:
@gunshi/plugin-global
This plugin adds --help and --version options to all your commands automatically.
When either option is used, the plugin intercepts command execution to display the appropriate information.
The following example demonstrates how the cli() function automatically includes the global plugin:
import { cli, define } from 'gunshi'
const command = define({
name: 'app',
description: 'My CLI application',
run: ctx => {
console.log('Running application')
}
})
await cli(process.argv.slice(2), command, {
name: 'my-app',
version: '1.0.0'
})Now your CLI automatically supports:
my-app --helpdisplays usage information rendered by the renderer pluginmy-app --versiondisplays the version number- The plugin decorates command execution to handle these options before your command runs
@gunshi/plugin-renderer
The renderer plugin automatically formats help text, usage information, and validation errors.
It works behind the scenes to ensure consistent, readable output across your entire CLI.
Using Optional Plugins
Optional plugins add specialized features to your CLI.
Install them separately and configure them based on your needs.
Internationalization
Add multi-language support with the i18n plugin:
npm install --save @gunshi/plugin-i18npnpm add @gunshi/plugin-i18nyarn add @gunshi/plugin-i18n# For Deno projects, you can add Gunshi from JSR:
deno add jsr:@gunshi/plugin-i18nbun add @gunshi/plugin-i18nHere's a simple example using the defineI18n helper:
import { cli } from 'gunshi'
import i18n, { defineI18n, resolveKey } from '@gunshi/plugin-i18n'
// Define resources inline for each locale
const resources = {
'en-US': {
greeting: 'Hello, {$name}!',
farewell: 'Goodbye!'
},
'ja-JP': {
greeting: 'こんにちは、{$name}さん!',
farewell: 'さようなら!'
},
'es-ES': {
greeting: '¡Hola, {$name}!',
farewell: '¡Adiós!'
}
}
const command = defineI18n({
name: 'greet',
description: 'Greet someone in their language',
// Resource function returns translations for the current locale
resource: async ctx => {
const locale = ctx.extensions['g:i18n'].locale
return resources[locale] || resources['en-US']
},
args: {
name: {
type: 'string',
required: true,
description: 'Name to greet'
}
},
run: ctx => {
// Access the i18n plugin extension via its namespaced ID 'g:i18n'
// (All Gunshi plugins use the 'g:' prefix to prevent naming conflicts)
const t = ctx.extensions['g:i18n'].translate
console.log(t(resolveKey('greeting', ctx), { name: ctx.values.name }))
console.log(t(resolveKey('farewell', ctx)))
}
})
await cli(process.argv.slice(2), command, {
name: 'greet-cli',
version: '1.0.0',
plugins: [
i18n({
locale: process.env.LANG || 'en-US'
})
]
})NOTE
Plugin IDs use namespacing to prevent conflicts and identify ownership. Official Gunshi plugins use the g: prefix (e.g., g:i18n, g:completion). When developing your own plugins, use your organization's namespace (e.g., myorg:logger) or scoped package format (e.g., @company/auth). For detailed naming conventions and guidelines, see the Plugin ID Guidelines.
Key benefits:
- The
defineI18nhelper simplifies i18n setup for commands - Automatic locale detection from system settings
- Simple interpolation syntax for dynamic values
- Fallback to default locale when translation is missing
- Plugin functionality is accessible via
ctx.extensionsin your command runners
NOTE
The ctx.extensions object is how plugins extend your command context with additional functionality. The i18n plugin adds translation capabilities through ctx.extensions['g:i18n']. To learn more about working with plugin extensions and best practices for accessing them, see the Context Extensions guide.
TIP
This example demonstrates basic internationalization setup. For comprehensive coverage including external resource files, TypeScript support, dynamic locale switching, and production deployment strategies, see the Advanced Internationalization Guide.
Shell Completion
Enable tab completion across different shells:
npm install --save @gunshi/plugin-completionpnpm add @gunshi/plugin-completionyarn add @gunshi/plugin-completionIMPORTANT
Shell completion currently requires Node.js. The completion feature is not available when running your CLI with Deno or Bun runtimes.
Here's how to add completion support:
import { cli, define } from 'gunshi'
import completion from '@gunshi/plugin-completion'
const command = define({
name: 'deploy',
args: {
environment: {
type: 'string',
required: true,
description: 'Target environment'
}
},
run: ctx => {
console.log(`Deploying to ${ctx.values.environment}`)
}
})
await cli(process.argv.slice(2), command, {
name: 'deploy-cli',
version: '1.0.0',
plugins: [
completion({
config: {
entry: {
args: {
environment: {
handler: () => [
{ value: 'production', description: 'Production' },
{ value: 'staging', description: 'Staging' },
{ value: 'development', description: 'Development' }
]
}
}
}
}
})
]
})The completion plugin adds a special complete command to your CLI that generates shell-specific completion scripts.
Installing Completion for End Users
Once you've added the completion plugin to your CLI, your users need to perform a one-time setup to enable tab completion on their system.
Basic Setup Example (Bash)
Users generate a completion script for their shell and add it to their shell configuration:
# Generate completion script and save it
deploy-cli complete bash > ~/.local/share/bash-completion/completions/deploy-cli
# Reload your shell configuration
source ~/.bashrc
# Now tab completion works!
deploy-cli dep<TAB> # Completes to: deploy-cli deployTIP
For detailed setup instructions for all supported shells (Bash, Zsh, Fish, PowerShell), including directory creation and configuration steps, see the @gunshi/plugin-completion README.
How It Works
The completion plugin adds a special complete command to your CLI that generates shell-specific completion scripts.
Users run this command once to generate the script for their shell, then source it in their shell configuration to enable tab completion.
Combining Plugins
Plugins work seamlessly together. Here's an example using both i18n and completion:
import { cli } from 'gunshi'
import i18n, { defineI18n, resolveKey } from '@gunshi/plugin-i18n'
import completion from '@gunshi/plugin-completion'
// Define your resources
const resources = {
'en-US': {
start: 'Starting build for {$mode} mode...',
success: 'Build completed successfully!'
},
'ja-JP': {
start: '{$mode}モードでビルドを開始しています...',
success: 'ビルドが正常に完了しました!'
}
}
const buildCommand = defineI18n({
name: 'build',
description: 'Build the project',
resource: async ctx => {
const locale = ctx.extensions['g:i18n'].locale
return resources[locale] || resources['en-US']
},
args: {
mode: {
type: 'string',
default: 'development',
description: 'Build mode'
}
},
run: ctx => {
const t = ctx.extensions['g:i18n'].translate
console.log(t(resolveKey('start', ctx), { mode: ctx.values.mode }))
// Build logic here
console.log(t(resolveKey('success', ctx)))
}
})
await cli(process.argv.slice(2), buildCommand, {
name: 'build-tool',
version: '2.0.0',
plugins: [
i18n({
locale: process.env.LANG || 'en-US'
}),
completion({
config: {
entry: {
args: {
mode: {
handler: () => [
{ value: 'development', description: 'Development build' },
{ value: 'production', description: 'Production build' }
]
}
}
}
}
})
]
})Both plugins enhance your CLI without interfering with each other - i18n handles translations while completion provides tab suggestions.
Plugin Configuration
Configure plugins based on your environment or user preferences.
Environment-based Configuration
Load plugins conditionally:
import { cli } from 'gunshi'
const plugins = []
// Add completion in development only
if (process.env.NODE_ENV === 'development') {
const completion = await import('@gunshi/plugin-completion')
plugins.push(completion.default())
}
// Add i18n if locale is set
if (process.env.LANG) {
const i18n = await import('@gunshi/plugin-i18n')
plugins.push(i18n.default({ locale: process.env.LANG }))
}
await cli(process.argv.slice(2), command, {
name: 'my-cli',
plugins
})This approach keeps your production builds lean while providing developer features during development.
Minimal Setup
If you need precise control over your CLI's functionality and bundle size, you can use @gunshi/bone - Gunshi's bare-bones foundation package.
Unlike the standard cli() function which includes plugins automatically, @gunshi/bone starts with zero plugins, letting you add only what you need:
npm install --save @gunshi/bone @gunshi/plugin-global @gunshi/plugin-rendererpnpm add @gunshi/bone @gunshi/plugin-global @gunshi/plugin-rendereryarn add @gunshi/bone @gunshi/plugin-global @gunshi/plugin-rendererimport { cli } from '@gunshi/bone'
import global from '@gunshi/plugin-global'
import renderer from '@gunshi/plugin-renderer'
// Only includes plugins you explicitly add
await cli(process.argv.slice(2), command, {
name: 'minimal-cli',
plugins: [
global(), // Adds --help and --version options
renderer() // Adds help text formatting
]
})This approach gives you explicit control over every aspect of your CLI, making it ideal for applications where bundle size or specific feature control is critical.
Common use cases for @gunshi/bone:
- Embedded CLIs with size constraints
- Custom implementations of help or version handling
- Testing plugin interactions in isolation
- Applications requiring precise control over all CLI behavior
Next Steps
Now that you understand the plugin system basics, you can explore more advanced topics and start building with Gunshi's plugin ecosystem.
To deepen your understanding of the plugin system:
- TypeScript Support: Explore type-safe plugin usage in the Type System Guide
- Context Extensions: Learn how plugins extend functionality through Context Extensions
Ready to create your own plugins?
- Plugin Development: Build custom plugins with the Plugin Development Guide
