Getting Started with Plugin Development
This guide will walk you through creating your first Gunshi plugin, from the simplest possible plugin to more advanced patterns with extensions and decorators.
Your First Minimal Plugin
Let's start with the absolute minimum (no extension) - a plugin that simply logs when it's loaded:
import { plugin } from 'gunshi/plugin'
// The simplest possible plugin
export default plugin({
id: 'hello',
name: 'Hello Plugin',
setup: ctx => {
console.log('Hello from plugin!')
}
})Use it in your CLI:
import { cli } from 'gunshi'
import hello from './plugin.js'
const entry = () => {}
await cli(process.argv.slice(2), entry, {
plugins: [hello]
})TIP
The example fully code is here.
Run your application with plugin:
# Run the entry with plugin
node cli.js
Hello from plugin!This plugin:
- Has a unique
idfor identification - Has a human-readable
name - Runs its
setupfunction during plugin initialization - Doesn't extend the command context
Adding Global Options
Let's create a plugin that adds a global --debug option to all commands:
import { plugin } from 'gunshi/plugin'
export default plugin({
id: 'debug',
name: 'Debug Plugin',
setup: ctx => {
// Add a global option available to all commands
ctx.addGlobalOption('debug', {
type: 'boolean',
short: 'd',
description: 'Enable debug output'
})
}
})Now all commands have access to --debug:
import { cli, define } from 'gunshi'
import debug from './plugin.js'
const command = define({
name: 'build',
run: ctx => {
if (ctx.values.debug) {
console.log('Debug mode enabled')
console.log('Context:', ctx)
}
console.log('Building...')
}
})
await cli(process.argv.slice(2), command, {
plugins: [debug]
})TIP
The example fully code is here.
Run your application with plugin:
# Run command with debug option
node cli.js --debug
Debug mode enabled
Context: ...
...
Building ...Adding Sub-Commands
Plugins can register sub-commands that become available to the CLI:
import { plugin } from 'gunshi/plugin'
export default plugin({
id: 'tools',
name: 'Developer Tools Plugin',
setup: ctx => {
// Add a new sub-command
ctx.addCommand('clean', {
name: 'clean',
description: 'Clean build artifacts',
args: {
cache: {
type: 'boolean',
description: 'Also clear cache',
default: false
}
},
run: ctx => {
console.log('Cleaning build artifacts...')
if (ctx.values.cache) {
console.log('Clearing cache...')
}
console.log('Clean complete!')
}
})
// Add another sub-command
ctx.addCommand('lint', {
name: 'lint',
description: 'Run linter',
run: ctx => {
console.log('Running linter...')
console.log('No issues found!')
}
})
}
})Now your CLI has additional commands:
import { cli, define } from 'gunshi'
import tools from './plugin.js'
// Main command
const command = define({
name: 'build',
run: ctx => console.log('Building project...')
})
await cli(process.argv.slice(2), command, {
plugins: [tools]
})TIP
The example fully code is here.
Run your application with the new sub-commands:
# Run main command
node cli.js
Building project...
# Run plugin's sub-command
node cli.js clean
Cleaning build artifacts...
Clean complete!
# With arguments
node cli.js clean --cache
Cleaning build artifacts...
Clearing cache...
Clean complete!
# Run another sub-command
node cli.js lint
Running linter...
No issues found!Advanced Plugin Features
Beyond basic setup and global options, plugins can provide much more powerful functionality:
Extensions
Plugins can extend the command context with new functionality that all commands can use.
// Simple example - adding logging functionality
export default plugin({
id: 'logger',
extension: () => ({
log: msg => console.log(msg)
})
})
// Commands can then use: ctx.extensions.logger.log('Hello')TIP
Extensions are the core feature for sharing functionality between plugins and commands. Learn more in Plugin Extensions.
Decorators
Plugins can decorate (wrap) existing functionality to enhance behavior:
// Customize how help text is displayed
ctx.decorateUsageRenderer(async (baseRenderer, ctx) => {
const baseUsage = await baseRenderer(ctx)
return `${baseUsage}\n\n📚 Documentation: https://example.com/docs`
})TIP
Decorators allow you to wrap commands, renderers, and more. Learn about all decorator types in Plugin Decorators.
Dependencies
Plugins can declare dependencies on other plugins:
export default plugin({
id: 'auth',
dependencies: ['logger'], // Requires logger plugin
setup: ctx => {
// Logger plugin is guaranteed to be loaded
}
})TIP
Dependencies ensure plugins load in the correct order. Learn more in Plugin Dependencies.
Summary
You've now learned the basics of Gunshi plugin development:
- Creating minimal plugins with setup functions
- Adding global options available to all commands
- Registering sub-commands through plugins
- Understanding advanced features (extensions, decorators, dependencies)
These fundamentals provide a solid foundation for building more complex plugins.
Next Steps
You've created your first Gunshi plugin and learned the fundamental concepts: setup functions, global options, sub-command registration, and basic plugin features.
With these foundations in place, you're ready to understand how plugins integrate with the CLI execution flow.
The next chapter, Plugin Lifecycle, will show you exactly when and how plugins execute during CLI runtime, giving you the knowledge to build more sophisticated plugins that interact with commands at the right moments.
