Skip to main content

Basic command

import { defineCommand, opt } from '@impulsedev/chameleon'

const echo = defineCommand({
  name: 'echo',
  description: 'Echo text back',
  options: {
    text: opt.string('Text to send back', { required: true })
  },
  execute: async (ctx) => {
    await ctx.reply({
      content: ctx.options.text,
      ephemeral: true
    })
  }
})

Register it

client.commands.register(echo)
For guild-scoped iteration during development, you can register to a single guild:
client.commands.registerGuild(guildId, echo)

Subcommands

const admin = defineCommand({
  name: 'admin',
  description: 'Admin tools',
  subcommands: {
    ban: defineSubcommand({
      description: 'Ban a member',
      options: {
        target: opt.user('User to ban', { required: true }),
        reason: opt.string('Reason')
      },
      execute: async (ctx) => {
        const target = ctx.options.target
        const reason = ctx.options.reason
        await ctx.reply(`Would ban ${target.id} for ${reason ?? 'no reason'}`)
      }
    })
  }
})

Important behavior

defineCommand(...) will throw if you create a command with neither:
  • execute
  • nor any subcommands
That catches one class of invalid command definitions early.

Deployment note

The command manager deploys registered commands through the application command API. If the client is already ready, deployment happens immediately. Otherwise it is deferred until the ready flow completes.

Load commands from a directory

If you keep commands in separate files, the command manager can load them from disk:
await client.commands.load('./commands')
Each loaded module should default-export a command definition.
Last modified on June 13, 2026