CommandKit’s plugin system allows you to extend the framework with additional functionality. Plugins can hook into various lifecycle events, transform code at compile time, and add new features to your bot.
Plugin types
CommandKit supports two types of plugins:
Runtime plugins
Compiler plugins
Runtime plugins execute during bot runtime and can hook into lifecycle events: import { RuntimePlugin } from 'commandkit' ;
class MyPlugin extends RuntimePlugin {
async onBeforeCommandsLoad ( ctx ) {
console . log ( 'Commands are about to load' );
}
async onAfterCommandsLoad ( ctx ) {
console . log ( 'Commands have been loaded' );
}
}
Compiler plugins transform code at compile time: import { CompilerPlugin } from 'commandkit' ;
class MyCompilerPlugin extends CompilerPlugin {
async transform ({ code , id }) {
// Transform code before it's executed
return {
code: transformedCode ,
map: sourceMap ,
};
}
}
Installing plugins
Add plugins to your commandkit.config.ts file:
import { defineConfig } from 'commandkit/config' ;
import { i18n } from '@commandkit/i18n' ;
import { cache } from '@commandkit/cache' ;
import { devtools } from '@commandkit/devtools' ;
export default defineConfig ({
plugins: [
i18n (),
cache (),
devtools (),
] ,
}) ;
Plugins are initialized in the order they appear in the array.
Official plugins
CommandKit provides several official plugins:
i18n Plugin
Add internationalization support to your bot:
npm install @commandkit/i18n
import { i18n } from '@commandkit/i18n' ;
export default defineConfig ({
plugins: [
i18n ({
locales: [ 'en' , 'es' , 'fr' ],
defaultLocale: 'en' ,
}),
] ,
}) ;
Use in commands:
export const chatInput : ChatInputCommand = async ( ctx ) => {
const { t } = ctx . locale ();
await ctx . interaction . reply ( t ( 'welcome' , { user: ctx . interaction . user . username }));
};
Cache Plugin
Add caching capabilities to your bot:
npm install @commandkit/cache
import { cache } from '@commandkit/cache' ;
export default defineConfig ({
plugins: [
cache ({
ttl: 300 , // 5 minutes
}),
] ,
}) ;
Use in commands:
import { getCached , setCached } from '@commandkit/cache' ;
export const chatInput : ChatInputCommand = async ( ctx ) => {
const cached = await getCached ( 'user-data' );
if ( cached ) {
return ctx . interaction . reply ( cached );
}
const data = await fetchUserData ();
await setCached ( 'user-data' , data , { ttl: 300 });
await ctx . interaction . reply ( data );
};
Debug and monitor your bot in development:
npm install @commandkit/devtools
import { devtools } from '@commandkit/devtools' ;
export default defineConfig ({
plugins: [
devtools ({
port: 3000 ,
}),
] ,
}) ;
Access the DevTools UI at http://localhost:3000
AI Plugin
Integrate AI capabilities into your bot:
npm install @commandkit/ai
import { ai } from '@commandkit/ai' ;
export default defineConfig ({
plugins: [
ai ({
provider: 'openai' ,
apiKey: process . env . OPENAI_API_KEY ,
}),
] ,
}) ;
Tasks Plugin
Schedule and manage background tasks:
npm install @commandkit/tasks
import { tasks } from '@commandkit/tasks' ;
import { SQLiteDriver } from '@commandkit/tasks/sqlite' ;
export default defineConfig ({
plugins: [
tasks ({
driver: new SQLiteDriver (),
}),
] ,
}) ;
Create tasks:
export default async function () {
console . log ( 'Running cleanup task...' );
// Your cleanup logic
}
export const schedule = '0 0 * * *' ; // Run daily at midnight
Workflow Plugin
Orchestrate complex bot workflows:
npm install @commandkit/workflow
import { workflow } from '@commandkit/workflow' ;
export default defineConfig ({
plugins: [
workflow (),
] ,
}) ;
Legacy Plugin
Migrate from CommandKit v3 to v4:
npm install @commandkit/legacy
import { legacy } from '@commandkit/legacy' ;
export default defineConfig ({
plugins: [
legacy (),
] ,
}) ;
Creating custom plugins
Runtime plugin
Create a custom runtime plugin by extending RuntimePlugin:
import { RuntimePlugin } from 'commandkit' ;
import type { CommandKitPluginRuntime } from 'commandkit' ;
import type { CommandKitEnvironment } from 'commandkit' ;
export class AnalyticsPlugin extends RuntimePlugin {
async onBeforeCommandsLoad ( ctx : CommandKitPluginRuntime ) {
console . log ( 'Analytics: Tracking commands load' );
}
async onAfterCommand ( ctx : CommandKitPluginRuntime , env : CommandKitEnvironment ) {
const commandName = env . variables . get ( 'commandName' );
console . log ( `Analytics: Command executed - ${ commandName } ` );
}
}
Use in config:
import { AnalyticsPlugin } from './plugins/analytics' ;
export default defineConfig ({
plugins: [
new AnalyticsPlugin (),
] ,
}) ;
Compiler plugin
Create a custom compiler plugin by extending CompilerPlugin:
import { CompilerPlugin } from 'commandkit' ;
import type { PluginTransformParameters , TransformedResult } from 'commandkit' ;
export class TransformPlugin extends CompilerPlugin {
async transform ( params : PluginTransformParameters ) : Promise < TransformedResult | null > {
const { code , id } = params ;
// Skip non-command files
if ( ! id . includes ( '/commands/' )) {
return null ;
}
// Transform the code
const transformedCode = code . replace (
/console \. log/ g ,
'Logger.log'
);
return {
code: transformedCode ,
map: null ,
};
}
}
Plugin lifecycle hooks
Runtime plugins can hook into various lifecycle events:
onBeforeCommandsLoad
(ctx: CommandKitPluginRuntime) => Promise<void>
Called before commands are loaded
onAfterCommandsLoad
(ctx: CommandKitPluginRuntime) => Promise<void>
Called after commands are loaded
onBeforeEventsLoad
(ctx: CommandKitPluginRuntime) => Promise<void>
Called before events are loaded
onAfterEventsLoad
(ctx: CommandKitPluginRuntime) => Promise<void>
Called after events are loaded
onBeforeClientLogin
(ctx: CommandKitPluginRuntime) => Promise<void>
Called before the Discord client logs in
onAfterClientLogin
(ctx: CommandKitPluginRuntime) => Promise<void>
Called after the Discord client logs in
onBeforeInteraction
(ctx: CommandKitPluginRuntime, interaction: Interaction) => Promise<void>
Called before each interaction is handled
onBeforeMessageCommand
(ctx: CommandKitPluginRuntime, message: Message) => Promise<void>
Called before each message command is processed
executeCommand
(ctx, env, source, command, execute) => Promise<boolean>
Intercept command execution. Return true to handle execution yourself. async executeCommand ( ctx , env , source , command , execute ) {
console . log ( 'Before command execution' );
await execute ();
console . log ( 'After command execution' );
return true ; // We handled it
}
onAfterCommand
(ctx: CommandKitPluginRuntime, env: CommandKitEnvironment) => Promise<void>
Called after command and all deferred functions are executed
prepareCommand
(ctx, commands) => Promise<CommandBuilderLike | null>
Modify command data before registration
willEmitEvent
(ctx, event) => Promise<void>
Called before an event is emitted
Plugin configuration
Plugins can accept configuration options:
class MyPlugin extends RuntimePlugin <{ apiKey : string ; timeout : number }> {
constructor ( options : { apiKey : string ; timeout ?: number }) {
super ({
apiKey: options . apiKey ,
timeout: options . timeout ?? 5000 ,
});
}
async onBeforeCommandsLoad ( ctx ) {
console . log ( `API Key: ${ this . options . apiKey } ` );
console . log ( `Timeout: ${ this . options . timeout } ` );
}
}
Use with options:
export default defineConfig ({
plugins: [
new MyPlugin ({
apiKey: process . env . API_KEY ,
timeout: 10000 ,
}),
] ,
}) ;
Plugin context
Plugins receive a CommandKitPluginRuntime context with access to:
async onAfterCommandsLoad ( ctx : CommandKitPluginRuntime ) {
// Access CommandKit instance
const commandkit = ctx . commandkit ;
// Access Discord client
const client = commandkit . client ;
// Access loaded commands
const commands = commandkit . commandHandler . loadedCommands ;
// Access configuration
const config = commandkit . appConfig ;
}
Best practices
Keep plugins focused
Each plugin should handle one concern (caching, logging, analytics, etc.)
Use TypeScript
Type your plugin options and use the provided types for hooks
Document your plugin
Provide clear documentation on how to use your plugin and its options
Handle errors gracefully
Plugins shouldn’t crash the bot. Catch and log errors appropriately
Test thoroughly
Test plugins with different configurations and edge cases
Example: Custom logging plugin
import { RuntimePlugin } from 'commandkit' ;
import type { CommandKitPluginRuntime , CommandKitEnvironment } from 'commandkit' ;
import type { Interaction , Message } from 'discord.js' ;
interface LoggerOptions {
logFile ?: string ;
logLevel ?: 'info' | 'debug' | 'warn' | 'error' ;
}
export class LoggerPlugin extends RuntimePlugin < LoggerOptions > {
private logFile : string ;
private logLevel : string ;
constructor ( options : LoggerOptions = {}) {
super ( options );
this . logFile = options . logFile ?? 'bot.log' ;
this . logLevel = options . logLevel ?? 'info' ;
}
async onAfterClientLogin ( ctx : CommandKitPluginRuntime ) {
const client = ctx . commandkit . client ;
this . log ( 'info' , `Bot logged in as ${ client . user ?. tag } ` );
}
async onBeforeInteraction (
ctx : CommandKitPluginRuntime ,
interaction : Interaction
) {
if ( ! interaction . isCommand ()) return ;
this . log ( 'info' , `Command: ${ interaction . commandName } by ${ interaction . user . tag } ` );
}
async onAfterCommand (
ctx : CommandKitPluginRuntime ,
env : CommandKitEnvironment
) {
const commandName = env . variables . get ( 'commandName' );
const duration = env . variables . get ( 'duration' );
this . log ( 'debug' , `Command ${ commandName } completed in ${ duration } ms` );
}
private log ( level : string , message : string ) {
if ( this . shouldLog ( level )) {
const timestamp = new Date (). toISOString ();
console . log ( `[ ${ timestamp } ] [ ${ level . toUpperCase () } ] ${ message } ` );
// Write to file logic here
}
}
private shouldLog ( level : string ) : boolean {
const levels = [ 'debug' , 'info' , 'warn' , 'error' ];
return levels . indexOf ( level ) >= levels . indexOf ( this . logLevel );
}
}
Use the custom plugin:
import { LoggerPlugin } from './plugins/logger' ;
export default defineConfig ({
plugins: [
new LoggerPlugin ({
logFile: 'app.log' ,
logLevel: 'debug' ,
}),
] ,
}) ;
Next steps
Configuration Configure CommandKit options
Commands Back to commands documentation