First plugin
Build a small slash command plugin with typed configuration.
This page builds a welcome-helper plugin. It exposes one /greet slash command and one configurable greeting.
Create files
Start with this layout:
welcome-helper/
package.json
src/
index.ts
metadata.ts
config.ts
commands/
greet.ts
README.mdInstall the SDK:
pnpm add itsmybotpackage.json
Add scripts for local author workflow:
{
"name": "welcome-helper",
"private": true,
"type": "module",
"scripts": {
"bundle": "itsmybot bundle .",
"publish": "itsmybot publish ."
},
"dependencies": {
"itsmybot": "latest"
},
"devDependencies": {
"typescript": "latest"
}
}metadata.ts
Keep metadata separate from behavior. Reviewers should be able to inspect plugin identity without reading command logic.
import { PluginCapability, PluginCategory } from "itsmybot";
export const metadata = {
id: "welcome-helper",
version: "0.1.0",
name: "Welcome Helper",
summary: "Send configurable welcome messages.",
category: PluginCategory.Engagements,
compatibilityDate: "2026-04-20",
capabilities: [PluginCapability.DiscordInteractionReply] as const,
};Use a stable id. Changing it creates a different plugin.
config.ts
defineConfig() keeps defaults typed and lets bundle generate the config schema.
import { } from "itsmybot";
export interface Config extends <string, unknown> {
/**
* @title Greeting
* @description Text used in slash command replies.
* @minLength 1
* @maxLength 80
*/
: string;
/**
* @title Public replies
* @description When enabled, command replies are visible to everyone.
*/
: boolean;
}
export const = <Config>({
: "Welcome",
: false,
});
.greeting;commands/greet.ts
Command files should define options and command class together. That keeps context types tied to the actual option object.
import {
,
type ,
,
,
} from "itsmybot";
import type { Config } from "../config";
const = ({
: ({
: "Who should receive the greeting",
// Discord validates slash command string option limits during publish too.
: true,
: 1,
: 32,
}),
});
export class extends <Config, typeof > {
readonly = "greet";
readonly = "Send a configured greeting";
readonly = ;
(: <Config, typeof >) {
...target; return .(
`${..}, ${...}`,
{ : !.. },
);
}
}index.ts
The entrypoint composes metadata, config, and handlers.
import { } from "itsmybot";
import { } from "./commands/greet";
import type { Config } from "./config";
export { } from "./config";
import { } from "./metadata";
class extends <Config> {
readonly = .;
readonly = .;
readonly = .;
readonly = .;
readonly = .;
readonly = .;
readonly = .;
readonly = [new ()];
}
export default new ();Bundle
Generate the artifact:
pnpm bundleExpected output:
.itsmybot/
manifest.json
runtime.jsDo not edit generated files. Change source code, then bundle again.