ItsMyBot Docs

Project structure

Organize plugin files so commands, events, config, and presentation stay easy to review.

Use this structure after the first prototype:

my-plugin/
  package.json
  src/
    index.ts
    metadata.ts
    config.ts
    commands/
      greet.ts
    events/
      mention-reply.ts
    messages/
      render-greeting.ts
  README.md
  icon.png
  screenshots/
    overview.png

src/index.ts stays the only required entrypoint. Imported files are still bundled, so splitting code does not create extra runtime setup.

File responsibilities

src/metadata.ts owns plugin identity:

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,
    PluginCapability.DiscordMessageWrite,
  ] as const,
};

src/config.ts owns dashboard config types and defaults:

import { defineConfig } from "itsmybot";

export interface Config extends Record<string, unknown> {
  /**
   * @title Greeting
   * @description Text used in slash command replies.
   * @minLength 1
   * @maxLength 80
   */
  greeting: string;
}

export const defaultConfig = defineConfig<Config>({
  greeting: "Welcome",
});

src/commands/* owns slash command classes and command option definitions.

src/events/* owns Discord event handlers.

src/messages/*, src/lib/*, or src/rules/* can hold pure helper logic when commands and events share behavior.

README.md, icon.png, and screenshots/* are marketplace presentation assets.

Why split files

Split files when each piece has different ownership:

  • metadata changes during release work.
  • config changes affect dashboard forms.
  • commands change command routing and options.
  • events change Discord trigger behavior.
  • message rendering can be tested without plugin runtime context.

Do not split only to make tiny wrappers. A one-command plugin can start in src/index.ts, then move pieces out when it grows.

Comment policy

Write comments where TypeScript cannot express intent:

  • JSDoc on exported config fields, because dashboard schema uses it.
  • capability comments when a Discord write is not obvious from nearby code.
  • storage index comments when query shape matters.
  • validation comments when Discord or platform limits explain a number.

Skip comments that restate code:

// Bad: creates command class.
class PreviewCommand {}

Use comments for intent:

stringOption({
  description: "Who should receive the greeting",
  // Discord slash command string option limits are enforced by publish validation too.
  minLength: 1,
  maxLength: 32,
});

Entrypoint stays small

Keep src/index.ts as composition glue:

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 ();

On this page