Recipes

Hot Reload

During development, Seyfert allows you to reload commands, events, components, and language files without restarting the bot.

Reloading Commands

// Reload all commands
await client.commands.reloadAll();

// Reload a specific command by name
await client.commands.reload('ping');

Reloading Events

await client.events.reloadAll();

Reloading Components

await client.components.reloadAll();

Reloading Language Files

// Reload all languages
await client.langs.reloadAll();

// Reload a specific locale
await client.langs.reload('es');

Reload Command

A practical pattern is to create a developer-only command for hot reloading:

import {
    Command,
    Declare,
    Options,
    createStringOption,
    type CommandContext,
} from 'seyfert';

const options = {
    target: createStringOption({
        description: 'What to reload',
        required: true,
        choices: [
            { name: 'Commands', value: 'commands' },
            { name: 'Events', value: 'events' },
            { name: 'Components', value: 'components' },
            { name: 'Languages', value: 'langs' },
            { name: 'All', value: 'all' },
        ] as const,
    }),
};

@Declare({
    name: 'reload',
    description: 'Reload bot modules',
})
@Options(options)
export default class ReloadCommand extends Command {
    async run(ctx: CommandContext<typeof options>) {
        const target = ctx.options.target;

        if (target === 'all' || target === 'commands') await ctx.client.commands.reloadAll();
        if (target === 'all' || target === 'events') await ctx.client.events?.reloadAll();
        if (target === 'all' || target === 'components') await ctx.client.components.reloadAll();
        if (target === 'all' || target === 'langs') await ctx.client.langs?.reloadAll();

        await ctx.editOrReply({ content: `Reloaded **${target}** successfully.` });
    }
}

Automatic Reload with @slipher/watcher

Manually restarting after every change gets old fast. @slipher/watcher keeps the gateway alive while it watches your source files, rebuilds on change, and restarts the worker.

pnpm add -D @slipher/watcher
src/watcher.ts
import { join } from 'node:path';
import { Watcher } from '@slipher/watcher';

const watcher = new Watcher({
    // This is the compiled WorkerClient entry, not the watcher entry below.
    filePath: join(process.cwd(), 'dist', 'index.js'),
    srcPath: join(process.cwd(), 'src'),
    transpileCommand: 'tsc --project tsconfig.json',
});

await watcher.spawnShards();
package.json
{
    "scripts": {
        "watch": "pnpm build && node dist/watcher.js"
    }
}
pnpm watch

Watcher is a library entry point, not a CLI. It extends Seyfert's ShardManager: the watcher process owns the gateway connection, forwards payloads to the compiled worker entry, and replaces that worker after any event reported by Chokidar under srcPath.

transpileCommand is optional and runs synchronously from the current working directory before each replacement. The package logs a build failure but still attempts to start filePath, so keep the initial pnpm build in the script and make sure failed builds cannot leave a partial output in that location.