diff --git a/apps/website/docs/api-reference/intro.md b/apps/website/docs/api-reference/intro.md
index e2311c4..7dea143 100644
--- a/apps/website/docs/api-reference/intro.md
+++ b/apps/website/docs/api-reference/intro.md
@@ -2,4 +2,4 @@
sidebar_position: 1
---
-# API reference intro
+# API reference
diff --git a/apps/website/docs/guide/01-installation.mdx b/apps/website/docs/guide/01-installation.mdx
new file mode 100644
index 0000000..c118168
--- /dev/null
+++ b/apps/website/docs/guide/01-installation.mdx
@@ -0,0 +1,92 @@
+---
+title: Installation
+description: Learn how to install CommandKit.
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+# Installation
+
+
+
+
+
+
+
+To install CommandKit, run one of the following commands based on your preferred package manager:
+
+
+
+ ```
+ npm install commandkit
+ ```
+
+
+ ```
+ yarn add commandkit
+ ```
+
+
+ ```
+ pnpm install commandkit
+ ```
+
+
+ ```
+ bun install commandkit
+ ```
+
+
+
+
+## Development version
+
+To install the development version of CommandKit, run one of the following commands:
+
+
+
+ ```
+ npm install commandkit@dev
+ ```
+
+
+ ```
+ yarn add commandkit@dev
+ ```
+
+
+ ```
+ pnpm install commandkit@dev
+ ```
+
+
+ ```
+ bun install commandkit@dev
+ ```
+
+
+
+
+:::warning
+The development version is likely to have bugs.
+:::
diff --git a/apps/website/docs/guide/02-create-commandkit.mdx b/apps/website/docs/guide/02-create-commandkit.mdx
new file mode 100644
index 0000000..6acaa08
--- /dev/null
+++ b/apps/website/docs/guide/02-create-commandkit.mdx
@@ -0,0 +1,42 @@
+---
+title: create-commandkit
+description: Create a new CommandKit application with the command-line utility.
+---
+
+# create-commandkit
+
+Besides installing CommandKit to your existing project, you can also start a new one with `create-commandkit` — a command-line utility used for creating new discord.js bots with CommandKit.
+
+## Usage
+
+If you want to create a new CommandKit application, simply run the following command:
+
+```
+npm create commandkit@latest
+```
+
+This will start the CLI in the current directory.
+
+### Development version
+
+Similar to the main package, you could try the `dev` version of create-commandkit:
+
+:::warning
+The development version is likely to have bugs.
+:::
+
+```
+npm create commandkit@dev
+```
+
+## Prompts
+
+When running create-commandkit, you should be asked the following questions:
+
+- **Directory:** Defines where your project will be located. It defaults to the current working directory.
+- **Package Manager:** Lets you choose which package manager to use — npm, pnpm, or Yarn.
+- **Language ([Development version](#development-version) only):** The language to use for your project — JavaScript or TypeScript.
+- **Module Type:** Allows you to pick between CommonJS (using require and module.exports), and ESM (using import and export).
+- **Bot Token:** Asks you for your bot token, and writes it into the `.env` file where it will be stored.
+
+After these questions are answered, your project solution should appear in the selected folder.
diff --git a/apps/website/docs/guide/03-commandkit-setup.mdx b/apps/website/docs/guide/03-commandkit-setup.mdx
new file mode 100644
index 0000000..52f20a4
--- /dev/null
+++ b/apps/website/docs/guide/03-commandkit-setup.mdx
@@ -0,0 +1,173 @@
+---
+title: CommandKit setup
+description: A simple overview of how to set up CommandKit with all the available options.
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+# CommandKit Setup
+
+This is a simple overview of how to set up CommandKit with all the available options. You’d usually set this up in your entry file (e.g. `index.js`) where you have access to your Discord.js client object.
+
+
+
+ ```js title="src/index.js" {2, 13-23}
+ const { Client, GatewayIntentBits } = require('discord.js');
+ const { CommandKit } = require('commandkit');
+ const path = require('path');
+
+ const client = new Client({
+ intents: [
+ GatewayIntentBits.Guilds,
+ GatewayIntentBits.GuildMessages,
+ GatewayIntentBits.MessageContent,
+ ],
+ });
+
+ new CommandKit({
+ client,
+ commandsPath: path.join(__dirname, 'commands'),
+ eventsPath: path.join(__dirname, 'events'),
+ validationsPath: path.join(__dirname, 'validations'),
+ devGuildIds: ['DEV_SERVER_ID_1', 'DEV_SERVER_ID_2'],
+ devUserIds: ['DEV_USER_ID_1', 'DEV_USER_ID_2'],
+ devRoleIds: ['DEV_ROLE_ID_1', 'DEV_ROLE_ID_2'],
+ skipBuiltInValidations: true,
+ bulkRegister: true,
+ });
+
+ client.login('YOUR_TOKEN_HERE');
+ ```
+
+
+ ```js title="src/index.js" {2, 16-26}
+ import { Client, GatewayIntentBits } from 'discord.js';
+ import { CommandKit } from 'commandkit';
+ import { fileURLToPath } from 'url';
+ import path from 'path';
+
+ const client = new Client({
+ intents: [
+ GatewayIntentBits.Guilds,
+ GatewayIntentBits.GuildMessages,
+ GatewayIntentBits.MessageContent
+ ],
+ });
+
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
+
+ new CommandKit({
+ client,
+ commandsPath: path.join(__dirname, 'commands'),
+ eventsPath: path.join(__dirname, 'events'),
+ validationsPath: path.join(__dirname, 'validations'),
+ devGuildIds: ['DEV_SERVER_ID_1', 'DEV_SERVER_ID_2'],
+ devUserIds: ['DEV_USER_ID_1', 'DEV_USER_ID_2'],
+ devRoleIds: ['DEV_ROLE_ID_1', 'DEV_ROLE_ID_2'],
+ skipBuiltInValidations: true,
+ bulkRegister: true,
+ });
+
+ client.login('YOUR_TOKEN_HERE');
+ ```
+
+
+ ```ts title="src/index.ts" {2, 13-23}
+ import { Client, GatewayIntentBits } from 'discord.js';
+ import { CommandKit } from 'commandkit';
+ import path from 'path';
+
+ const client = new Client({
+ intents: [
+ GatewayIntentBits.Guilds,
+ GatewayIntentBits.GuildMessages,
+ GatewayIntentBits.MessageContent
+ ],
+ });
+
+ new CommandKit({
+ client,
+ commandsPath: path.join(__dirname, 'commands'),
+ eventsPath: path.join(__dirname, 'events'),
+ validationsPath: path.join(__dirname, 'validations'),
+ devGuildIds: ['DEV_SERVER_ID_1', 'DEV_SERVER_ID_2'],
+ devUserIds: ['DEV_USER_ID_1', 'DEV_USER_ID_2'],
+ devRoleIds: ['DEV_ROLE_ID_1', 'DEV_ROLE_ID_2'],
+ skipBuiltInValidations: true,
+ bulkRegister: true,
+ });
+
+ client.login('YOUR_TOKEN_HERE');
+ ```
+
+
+
+
+:::info
+Some Discord.js properties are only accessible using the correct intents.
+:::
+
+## CommandKit options
+
+### `client`
+
+- Type: [`Client`](https://old.discordjs.dev/#/docs/discord.js/main/class/Client)
+
+This is your Discord.js client object. This is required to register and handle application commands and events.
+
+### `commandsPath` (optional)
+
+- Type: `string`
+
+This is the path to your commands directory. It's used to fetch, register, and listen for application commands.
+
+### `eventsPath` (optional)
+
+- Type: `string`
+
+This is the path to your events directory. It's used to fetch and set event listeners based on the folder names inside of it.
+
+### `validationsPath` (optional)
+
+- Type: `string`
+
+This is the path to your validations directory. It's used to fetch and call validation functions before running application commands.
+
+### `devGuildIds` (optional)
+
+- Type: `string[]`
+
+This is a list of development server IDs. It's used to restrict commands marked with `devOnly` to specific servers.
+
+If there is at least 1 guild ID provided, CommandKit will register any commands marked as `devOnly` inside the listed servers.
+
+### `devUserIds` (optional)
+
+- Type: `string[]`
+
+This is a list of developer user IDs. It's used to restrict commands marked with `devOnly` to specific users.
+
+Trying to execute a command when this is set to at-least 1 user ID will call a built-in validation function everytime to validate that the user running the command belongs to the provided `devUserIds` array.
+
+### `devRoleIds` (optional)
+
+- Type: `string[]`
+
+This is a list of developer role IDs. It's used to restrict commands marked with `devOnly` to specific roles.
+
+Trying to execute a command when this is set to at-least 1 role ID will call a built-in validation function everytime to validate that the user running the command has at least one of the provided roles.
+
+### `skipBuiltInValidations` (optional)
+
+- Type: `boolean`
+- Default: `false`
+
+This is used to disable CommandKit's built-in validation functions. Setting this to `true` will ignore the default behaviour of validating who is running commands marked with `devOnly`.
+
+### `bulkRegister` (optional)
+
+- Type: `boolean`
+- Default: `false`
+
+This is used to change the behaviour of how CommandKit loads application commands. By default it's one-by-one while comparing changes. Setting this option to `true` will load application commands all at once on every restart, and when [`reloadCommands()`](/docs/api-reference/classes/CommandKit#reloadcommands) is called.
diff --git a/apps/website/docs/guide/04-command-file-setup.mdx b/apps/website/docs/guide/04-command-file-setup.mdx
new file mode 100644
index 0000000..ebe1b57
--- /dev/null
+++ b/apps/website/docs/guide/04-command-file-setup.mdx
@@ -0,0 +1,310 @@
+---
+title: Commands setup
+description: Learn how to set up commands in CommandKit.
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+# Commands Setup
+
+CommandKit supports both slash commands and context menu commands. Since both have the same triggers (interactions) and similar command structure, they are both stored and handled from the same commands directory.
+
+## Slash Command
+
+Here's an example `/ping` slash command which replies with "Pong!"
+
+
+
+ ```js title="src/commands/Misc/ping.js"
+ module.exports = {
+ data: {
+ name: 'ping',
+ description: 'Pong!',
+ },
+
+ run: ({ interaction, client, handler }) => {
+ interaction.reply(`:ping_pong: Pong! ${client.ws.ping}ms`);
+ },
+
+ options: {
+ devOnly: true,
+ userPermissions: ['Administrator', 'AddReactions'],
+ botPermissions: ['Administrator', 'AddReactions'],
+ deleted: false,
+ },
+ };
+ ```
+
+
+ ```js title="src/commands/Misc/ping.js"
+ export const data = {
+ name: 'ping',
+ description: 'Pong!',
+ }
+
+ export function run({ interaction, client, handler }) {
+ interaction.reply(`:ping_pong: Pong! ${client.ws.ping}ms`);
+ }
+
+ export const options = {
+ devOnly: true,
+ userPermissions: ['Administrator', 'AddReactions'],
+ botPermissions: ['Administrator', 'AddReactions'],
+ deleted: false,
+ }
+ ```
+
+
+ ```ts title="src/commands/Misc/ping.ts"
+ import type { CommandData, SlashCommandProps, CommandOptions } from 'commandkit';
+
+ export const data: CommandData = {
+ name: 'ping',
+ description: 'Pong!',
+ }
+
+ export function run({ interaction, client, handler }: SlashCommandProps) {
+ interaction.reply(`:ping_pong: Pong! ${client.ws.ping}ms`);
+ }
+
+ export const options: CommandOptions = {
+ devOnly: true,
+ userPermissions: ['Administrator', 'AddReactions'],
+ botPermissions: ['Administrator', 'AddReactions'],
+ deleted: false,
+ }
+ ```
+
+
+
+
+## Context Menu Command
+
+Here's an example `content` command which replies with the content of the target message.
+
+
+
+ ```js title="src/commands/Misc/content.js"
+ const { ApplicationCommandType } = require("discord.js");
+
+ module.exports = {
+ data: {
+ name: 'content',
+ type: ApplicationCommandType.Message,
+ },
+
+ run: ({ interaction, client, handler }) => {
+ interaction.reply(`The message is: ${interaction.targetMessage.content}`);
+ },
+
+ options: {
+ devOnly: true,
+ userPermissions: ['Administrator', 'AddReactions'],
+ botPermissions: ['Administrator', 'AddReactions'],
+ deleted: false,
+ },
+ };
+ ```
+
+
+ ```js title="src/commands/Misc/content.js"
+ import { ApplicationCommandType } from "discord.js";
+
+ export const data = {
+ name: 'content',
+ type: ApplicationCommandType.Message,
+ }
+
+ export function run({ interaction, client, handler }) {
+ interaction.reply(`The message is: ${interaction.targetMessage.content}`);
+ }
+
+ export const options = {
+ devOnly: true,
+ userPermissions: ['Administrator', 'AddReactions'],
+ botPermissions: ['Administrator', 'AddReactions'],
+ deleted: false,
+ }
+ ```
+
+
+ ```ts title="src/commands/Misc/content.ts"
+ import { type CommandData, type ContextMenuCommandProps, type CommandOptions } from "commandkit";
+ import { ApplicationCommandType } from "discord.js";
+
+ export const data: CommandData = {
+ name: 'content',
+ type: ApplicationCommandType.Message,
+ }
+
+ export function run({ interaction, client, handler }: ContextMenuCommandProps) {
+ interaction.reply(`The message is: ${interaction.targetMessage.content}`);
+ }
+
+ export const options: CommandOptions = {
+ devOnly: true,
+ userPermissions: ['Administrator', 'AddReactions'],
+ botPermissions: ['Administrator', 'AddReactions'],
+ deleted: false,
+ }
+ ```
+
+
+
+
+## Autocomplete function
+
+In addition to the `run` function, you can also export an `autocomplete` function from your command file so that you don't have to create a new `"interactionCreate"` event listener.
+
+Here's an example of how to use the `autocomplete` function:
+
+
+
+ ```js title="src/commands/Fun/pet.js"
+ const pets = require('../data/pets.json');
+
+ module.exports = {
+ data: new SlashCommandBuilder()
+ .setName('pet')
+ .setDescription('Find a pet from a list of pets.')
+ .addStringOption((option) =>
+ option
+ .setName('pet')
+ .setDescription('The pet to check.')
+ .setRequired(true)
+ .setAutocomplete(true)
+ ),
+
+ run: ({ interaction, client, handler }) => {
+ const targetPetId = interaction.options.getString('pet');
+ const targetPetObj = pets.find((pet) => pet.id === targetPetId);
+
+ interaction.reply(`Your pet name is ${targetPetObj.name}.`);
+ },
+
+ autocomplete: ({ interaction, client, handler }) => {
+ const focusedPetOption = interaction.options.getFocused(true);
+
+ const filteredChoices = pets.filter((pet) => pet.name.startsWith(focusedPetOption));
+
+ const results = filteredChoices.map((pet) => {
+ return {
+ name: `${pet.name} | ${pet.type}`,
+ value: pet.id,
+ };
+ });
+
+ interaction.respond(results.slice(0, 25));
+ }
+ }
+ ```
+
+
+ ```js title="src/commands/Fun/pet.js"
+ import pets from '../data/pets.json';
+
+ export const data = new SlashCommandBuilder()
+ .setName('pet')
+ .setDescription('Find a pet from a list of pets.')
+ .addStringOption((option) =>
+ option
+ .setName('pet')
+ .setDescription('The pet to check.')
+ .setRequired(true)
+ .setAutocomplete(true)
+ );
+
+ export function run({ interaction, client, handler }) {
+ const targetPetId = interaction.options.getString('pet');
+ const targetPetObj = pets.find((pet) => pet.id === targetPetId);
+
+ interaction.reply(`Your pet name is ${targetPetObj.name}.`);
+ }
+
+ export function autocomplete({ interaction, client, handler }) {
+ const focusedPetOption = interaction.options.getFocused(true);
+
+ const filteredChoices = pets.filter((pet) => pet.name.startsWith(focusedPetOption));
+
+ const results = filteredChoices.map((pet) => {
+ return {
+ name: `${pet.name} | ${pet.type}`,
+ value: pet.id,
+ };
+ });
+
+ interaction.respond(results.slice(0, 25));
+ }
+ ```
+
+
+ ```ts title="commands/Fun/pet.ts"
+ import type { CommandData, AutocompleteProps, CommandOptions } from 'commandkit';
+ import pets from '../data/pets.json';
+
+ export const data = new SlashCommandBuilder()
+ .setName('pet')
+ .setDescription('Find a pet from a list of pets.')
+ .addStringOption((option) =>
+ option
+ .setName('pet')
+ .setDescription('The pet to check.')
+ .setRequired(true)
+ .setAutocomplete(true)
+ );
+
+ export function run({ interaction, client, handler }: SlashCommandProps) {
+ const targetPetId = interaction.options.getString('pet');
+ const targetPetObj = pets.find((pet) => pet.id === targetPetId);
+
+ interaction.reply(`Your pet name is ${targetPetObj.name}.`);
+ }
+
+ export function autocomplete({ interaction, client, handler }: AutocompleteProps) {
+ const focusedPetOption = interaction.options.getFocused(true);
+
+ const filteredChoices = pets.filter((pet) => pet.name.startsWith(focusedPetOption));
+
+ const results = filteredChoices.map((pet) => {
+ return {
+ name: `${pet.name} | ${pet.type}`,
+ value: pet.id,
+ };
+ });
+
+ interaction.respond(results.slice(0, 25));
+ }
+ ```
+
+
+
+
+## Properties and Methods
+
+### `data`
+
+- Type: [`CommandData`](/docs/types/CommandData) | [`SlashCommandBuilder`](https://discord.js.org/docs/packages/builders/main/SlashCommandBuilder:Class) | [`ContextMenuCommandBuilder`](https://discord.js.org/docs/packages/builders/main/ContextMenuCommandBuilder:Class)
+
+This property contains the command's structural information, and is required to register and handle commands.
+
+### `run`
+
+- Type: `void`
+
+- Props Type: [`SlashCommandProps`](/docs/types/SlashCommandProps) | [`ContextMenuCommandProps`](/docs/types/ContextMenuCommandProps)
+
+This function will be called when the command is executed.
+
+### `autocomplete`
+
+- Type: `void`
+- Props Type: [`AutocompleteProps`](/docs/types/AutocompleteProps)
+
+This function will be called when an autocomplete interaction event is triggered for any option set as autocomplete in this command's `data` object.
+
+### `options` (optional)
+
+- Type: [`CommandOptions`](/docs/types/CommandOptions)
+
+This property contains the command's registration/handling behaviour.
diff --git a/apps/website/docs/guide/05-event-file-setup.mdx b/apps/website/docs/guide/05-event-file-setup.mdx
new file mode 100644
index 0000000..de58499
--- /dev/null
+++ b/apps/website/docs/guide/05-event-file-setup.mdx
@@ -0,0 +1,167 @@
+---
+title: Events setup
+description: A simple overview of how to set up event functions.
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+# Events Setup
+
+This is a simple overview of how to set up a simple function that is called when the "ready" event is triggered. All this code does is log to the console when the bot is ready.
+
+
+
+ ```js title="src/events/ready/console-log.js"
+ module.exports = (c, client, handler) => {
+ console.log(`${c.user.username} is ready!`);
+ };
+ ```
+
+
+ ```js title="src/events/ready/console-log.js"
+ export default function (c, client, handler) {
+ console.log(`${c.user.username} is ready!`);
+ };
+ ```
+
+
+ ```ts title="src/events/ready/console-log.ts"
+ import type { Client } from 'discord.js';
+ import type { CommandKit } from 'commandkit';
+
+ export default function (c: Client, client: Client, handler: CommandKit) {
+ console.log(`${c.user.username} is ready!`);
+ };
+ ```
+
+
+
+
+
+## Parameters explained
+
+The parameters might look a bit confusing at first, but they're actually quite simple. The first parameter `c` is the client object that was returned as a parameter when the "ready" event was triggered. The second parameter `client` is the Discord.js client that was instantiated in your main entry point file. Finally, the `handler` parameter is the current CommandKit instance.
+
+To better understand how the parameters work, here's another example but with the "messageCreate" event listener.
+
+
+
+ ```js title="src/events/messageCreate/say-hi.js"
+ module.exports = (message, client) => {
+ if (message.content === 'hey') {
+ message.reply('Hi!');
+ }
+ };
+ ```
+
+
+ ```js title="src/events/messageCreate/say-hi.js"
+ export default function (message, client, handler) {
+ if (message.content === 'hey') {
+ message.reply('Hi!');
+ }
+ };
+ ```
+
+
+ ```ts title="src/events/messageCreate/say-hi.ts"
+ import type { Message, Client } from 'discord.js';
+ import type { CommandKit } from 'commandkit';
+
+ export default function (message: Message, client: Client, handler: CommandKit) {
+ if (message.content === 'hey') {
+ message.reply('Hi!');
+ }
+ };
+ ```
+
+
+
+
+
+In this example you can see that the first parameter is the `message` object that was returned as a parameter when the "messageCreate" event was triggered. The `client` parameter is the same as the previous example.
+
+## Multiple parameters explained
+
+But what if an event returns multiple parameters? Let's take for example the "messageUpdate" event. This event returns two parameters, `oldMessage` and `newMessage`. The parameters will follow the same behaviour as if you were using the `client.on()` method.
+
+
+
+ ```js title="src/events/messageUpdate/log-message-update.js"
+ module.exports = (oldMessage, newMessage, client) => {
+ console.log(`Message edited from ${oldMessage.content} to ${newMessage.content}`);
+ };
+ ```
+
+
+ ```js title="src/events/messageUpdate/log-message-update.js"
+ export default function (oldMessage, newMessage, client) {
+ console.log(`Message edited from ${oldMessage.content} to ${newMessage.content}`);
+ };
+ ```
+
+
+ ```ts title="src/events/messageUpdate/log-message-update.ts"
+ import type { Message, PartialMessage } from 'discord.js';
+ import type { CommandKit } from 'commandkit';
+
+ export default function (oldMessage: Message | PartialMessage, newMessage: Message | PartialMessage, client: Client, handler: CommandKit) {
+ console.log(`Message edited from ${oldMessage.content} to ${newMessage.content}`);
+ };
+ ```
+
+
+
+
+
+As you can see, even with multiple parameters, the last parameter will always be the `client` object.
+
+## Stopping the event loop
+
+The code above is just a simple example of how to set up an event function. But what if you want to stop the next event function in line from running? This is where the `return` statement comes in handy.
+
+
+
+ ```js title="src/events/messageCreate/say-hi.js"
+ module.exports = (message, client) => {
+ if (message.content === 'hey') {
+ message.reply('Hi!');
+
+ return true; // This stops the event loop
+ }
+ }
+ ```
+
+
+
+ ```js title="src/events/messageCreate/say-hi.js"
+ export default function (message, client) {
+ if (message.content === 'hey') {
+ message.reply('Hi!');
+
+ return true; // This stops the event loop
+ }
+ }
+ ```
+
+
+
+ ```js title="src/events/messageCreate/say-hi.ts"
+ import type { Message, Client } from 'discord.js';
+ import type { CommandKit } from 'commandkit';
+
+ export default function (message: Message, client: Client, handler: CommandKit) {
+ if (message.content === 'hey') {
+ message.reply('Hi!');
+
+ return true; // This stops the event loop
+ }
+ }
+ ```
+
+
+
+
+
+You can return any truthy value from this function to stop the next event function from running.
diff --git a/apps/website/docs/guide/06-validation-file-setup.mdx b/apps/website/docs/guide/06-validation-file-setup.mdx
new file mode 100644
index 0000000..682316e
--- /dev/null
+++ b/apps/website/docs/guide/06-validation-file-setup.mdx
@@ -0,0 +1,94 @@
+---
+title: Validations setup
+description: Learn how to set up validations for your commands.
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+# Validations Setup
+
+Validations are functions that are called before the `run` function from a command object is called. They can be used for many things, including validating your command object, or even implementing features such as cooldowns where the user will not be able to run the command if a validation functions says so.
+
+Validation functions are called on every command trigger, so it's important to keep them as lightweight as possible.
+
+:::info
+Custom validation functions are called before the built-in validations
+provided by CommandKit. This provides you with more control over the flow of
+commands.
+:::
+
+
+
+ ```js title="src/validations/cooldowns.js"
+ const cooldowns = require('../cooldowns-cache');
+
+ module.exports = ({ interaction, commandObj, handler }) => {
+ if (cooldowns.has(`${interaction.user.id}-${commandObj.data.name}`)) {
+ interaction.reply({
+ content: "You're on cooldown, please wait some time before running this command again.",
+ ephemeral: true,
+ });
+
+ return true; // This is important
+ }
+ };
+ ```
+
+
+
+ ```js title="src/validations/cooldowns.js"
+ import cooldowns from '../cooldowns-cache';
+
+ export default function ({ interaction, commandObj, handler }) {
+ if (cooldowns.has(`${interaction.user.id}-${commandObj.data.name}`)) {
+ interaction.reply({
+ content: "You're on cooldown, please wait some time before running this command again.",
+ ephemeral: true,
+ });
+
+ return true; // This is important
+ }
+ };
+ ```
+
+
+
+ ```ts title="src/validations/cooldowns.ts"
+ import type { ValidationProps } from 'commandkit';
+ import cooldowns from '../cooldowns-cache';
+
+ export default function ({ interaction, commandObj, handler }: ValidationProps) {
+ if (cooldowns.has(`${interaction.user.id}-${commandObj.data.name}`)) {
+ interaction.reply({
+ content: "You're on cooldown, please wait some time before running this command again.",
+ ephemeral: true,
+ });
+
+ return true; // This is important
+ }
+ };
+ ```
+
+
+
+
+
+### Parameters explained
+
+Within every validation function, you have the ability to check what command is trying to be executed using `commandObj` and the `interaction` object from the parameters and respond accordingly.
+
+The `commandObj` object is what's being exported from your command file (excluding the `run` function). This means you can access the `data` and `options` property from within your validation function.
+
+The `handler` object is the current CommandKit instance.
+
+### Stopping the command from running
+
+You may notice that the code above is returning `true`. This is important as it tells the command handler to not run any other validations and to not run the command. If you do not return `true` (or any truthy value), the command will run as normal.
+
+:::warning
+Interactions (commands in this case) must be handled within 3 seconds, so make
+sure your validations are not using up much of that time. If you're using a
+database or an external API, it's recommended to implement caching to keep
+things quick.
+:::
diff --git a/apps/website/docs/guide/07-buttonkit.mdx b/apps/website/docs/guide/07-buttonkit.mdx
new file mode 100644
index 0000000..7461519
--- /dev/null
+++ b/apps/website/docs/guide/07-buttonkit.mdx
@@ -0,0 +1,240 @@
+---
+title: Using ButtonKit
+description: ButtonKit is an enhanced version of the native Discord.js ButtonBuilder, designed to simplify the process of creating and handling button interactions in your Discord bot.
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+# Using ButtonKit
+
+ButtonKit is an enhanced version of the native Discord.js [`ButtonBuilder`](https://old.discordjs.dev/#/docs/discord.js/main/class/ButtonBuilder), designed to simplify the process of creating and handling button interactions in your Discord bot.
+
+It is not recommended to use this to listen for button clicks forever since it creates collectors. For that purpose, it's recommended to use a regular "interactionCreate" event listener.
+
+## Handle button clicks
+
+
+
+ ```js title="src/commands/counter.js"
+ const { ButtonKit } = require('commandkit');
+ const { ButtonStyle, ActionRowBuilder } = require('discord.js');
+
+ // Create a button
+ const button = new ButtonKit()
+ .setEmoji('👍')
+ .setStyle(ButtonStyle.Primary)
+ .setCustomId('button'); // Required to use onClick
+
+ const buttonRow = new ActionRowBuilder().addComponents(button);
+
+ const message = await channel.send({ components: [buttonRow] });
+
+ // Listen to the button interaction right away
+ button.onClick(
+ (interaction) => {
+ // Reply to the interaction
+ interaction.reply('You clicked the button!');
+ },
+ { message },
+ );
+ ```
+
+
+
+ ```js title="src/commands/counter.js"
+ import { ButtonKit } from 'commandkit';
+ import { ButtonStyle, ActionRowBuilder } from 'discord.js';
+
+ // Create a button
+ const button = new ButtonKit()
+ .setEmoji('👍')
+ .setStyle(ButtonStyle.Primary)
+ .setCustomId('button'); // Required to use onClick
+
+ const buttonRow = new ActionRowBuilder().addComponents(button);
+
+ const message = await channel.send({ components: [buttonRow] });
+
+ // Listen to the button interaction right away
+ button.onClick(
+ (interaction) => {
+ // Reply to the interaction
+ interaction.reply('You clicked the button!');
+ },
+ { message },
+ );
+ ```
+
+
+
+ ```ts title="src/commands/counter.ts"
+ import { ButtonKit } from 'commandkit';
+ import { type ButtonInteraction, ButtonStyle, ActionRowBuilder } from 'discord.js';
+
+ // Create a button
+ const button = new ButtonKit()
+ .setEmoji('👍')
+ .setStyle(ButtonStyle.Primary)
+ .setCustomId('button'); // Required to use onClick
+
+ const buttonRow = new ActionRowBuilder().addComponents(button);
+
+ const message = await channel.send({ components: [buttonRow] });
+
+ // Listen to the button interaction right away
+ button.onClick(
+ (interaction: ButtonInteraction) => {
+ // Reply to the interaction
+ interaction.reply('You clicked the button!');
+ },
+ { message },
+ );
+ ```
+
+
+
+
+
+In the above example, you may notice how similar `ButtonKit` is to the native Discord.js `ButtonBuilder` class. That's because it's built on top of it. It introduces a new method called `onClick` which will allow you to quickly handle button interactions without having to create collectors. CommandKit does that for you!
+
+:::warning
+ButtonKit doesn't work without a custom ID, so ensure you provide one whenever
+instantiating a button. This is required to keep track of what button was
+clicked.
+:::
+
+### Arguments Explained
+
+Here's an empty `onClick` method without any arguments:
+
+```js
+const myButton = new ButtonKit()
+ .setCustomId('custom_button')
+ .setLabel('Click me!')
+ .setStyle(ButtonStyle.Primary);
+
+myButton.onClick();
+```
+
+The first argument required by this function is your handler function which will acknowledge button clicks (interactions) handled by ButtonKit. You can handle them like so:
+
+```js
+myButton.onClick((buttonInteraction) => {
+ buttonInteraction.reply('You clicked a button!');
+});
+```
+
+However, the code above won't actually work since ButtonKit doesn't have any idea where it's supposed to specifically listen button clicks from. To fix that, you need to pass in a second argument, also known as your [options](/guide/buttonkit#buttonkit-onclick-options) which houses all the collector configuration. The `message` property is the message that ButtonKit will use to listen for button clicks.
+
+```js
+const row = new ActionRowBuilder().addComponents(myButton);
+const message = await channel.send({ components: [row] });
+
+myButton.onClick(
+ (buttonInteraction) => {
+ buttonInteraction.reply('You clicked a button!');
+ },
+ { message },
+);
+```
+
+This also works with interaction replies. Just ensure you pass `fetchReply` alongside your components:
+
+```js
+const row = new ActionRowBuilder().addComponents(myButton);
+const message = await interaction.reply({
+ components: [row],
+ fetchReply: true,
+});
+
+myButton.onClick(
+ (buttonInteraction) => {
+ buttonInteraction.reply('You clicked a button!');
+ },
+ { message },
+);
+```
+
+## ButtonKit `onClick` options
+
+### `message`
+
+- Type: [`Message`](https://old.discordjs.dev/#/docs/discord.js/main/class/Message)
+
+The message object that ButtonKit uses to listen for button clicks (interactions).
+
+### `time` (optional)
+
+- Type: `number`
+- Default: `86400000`
+
+The duration (in ms) the collector should run for and listen for button clicks.
+
+### `autoReset` (optional)
+
+- Type: `boolean`
+
+Whether or not the collector should automatically reset the timer when a button is clicked.
+
+### Additional optional options
+
+- Type: [`InteractionCollectorOptions`](https://old.discordjs.dev/#/docs/discord.js/main/typedef/InteractionCollectorOptions)
+
+## Handle collector end
+
+When setting up an `onClick()` method using ButtonKit, you may also want to run some code after the collector ends running. The default timeout is 1 day, but you can modify this in [`onClickOptions#time`](#time-optional). To handle when the collector ends, you can setup an `onEnd()` method like this:
+
+```js {16-21}
+const myButton = new ButtonKit()
+ .setCustomId('custom_button')
+ .setLabel('Click me!')
+ .setStyle(ButtonStyle.Primary);
+
+const row = new ActionRowBuilder().addComponents(myButton);
+const message = await interaction.reply({
+ components: [row],
+ fetchReply: true,
+});
+
+myButton
+ .onClick(
+ (buttonInteraction) => {
+ buttonInteraction.reply('You clicked a button!');
+ },
+ { message },
+ )
+ .onEnd(() => {
+ console.log('Button collector ended.');
+
+ myButton.setDisabled(true);
+ message.edit({ components: [row] });
+ });
+```
+
+## Dispose button collector
+
+:::warning
+This feature is currently only available in the [development
+version](/guide/installation#development-version).
+:::
+
+To dispose the button collector, you can make use of the `dispose` method. By disposing the collector like this, your `onEnd` handler (if any) will be called automatically.
+
+```js {15}
+myButton
+ .onClick(
+ (buttonInteraction) => {
+ buttonInteraction.reply('You clicked a button!');
+ },
+ { message },
+ )
+ .onEnd(() => {
+ console.log('Button collector ended.');
+
+ myButton.setDisabled(true);
+ message.edit({ components: [row] });
+ });
+
+myButton.dispose();
+```
diff --git a/apps/website/docs/guide/08-using-cli.mdx b/apps/website/docs/guide/08-using-cli.mdx
new file mode 100644
index 0000000..5ced5e7
--- /dev/null
+++ b/apps/website/docs/guide/08-using-cli.mdx
@@ -0,0 +1,57 @@
+---
+title: Using CommandKit CLI
+description: Learn how to use CommandKit CLI to start, build, and manage your bot application.
+---
+
+# Using CommandKit CLI
+
+CommandKit CLI allows you to start, build, and manage your bot application. It also includes features such as anti-crash so you can be rest assured your bot won't crash and go offline during production.
+
+:::warning
+For CommandKit CLI on version `0.1.7` it's recommended to use an ESM project
+instead of CommonJS due to the behavior of `require()`. If you're using
+CommonJS you'll have to use dynamic `import()`. However, this is not an issue
+for TypeScript CommonJS projects.
+:::
+
+To get a list of the available CLI commands, run the following command inside your project directory:
+
+```sh
+npx commandkit --help
+```
+
+The output should look something like this:
+
+```sh
+Usage: commandkit [options] [command]
+
+Options:
+ -h, --help display help for command
+
+Commands:
+ dev [options] Start your bot in development mode.
+ start [options] Start your bot in production mode after running the build command.
+ build [options] Build your project for production usage.
+ help [command] display help for command
+```
+
+:::info
+CommandKit does not perform type checking on your code. You need to do that
+yourself using `tsc --noEmit` command or any other tool of your choice.
+:::
+
+## Available commands
+
+### Build
+
+`commandkit build` creates an optimized production build of your bot application. By default, commandkit emits the build to `dist/` directory in your project. It supports typescript out of the box.
+
+CommandKit also injects anti-crash script in your production build to prevent your bot from crashing due to cases like unhandled promise rejections. Although, it's recommended to handle these cases yourself. You can disable this behavior by setting `antiCrash: false` in your `commandkit.config.js` file.
+
+### Development
+
+`commandkit dev` starts your bot in development mode. It supports hot reloading out of the box to automatically restart your bot when you make changes to your code. CommandKit automatically loads your environment variables from `.env` file in your project directory, without you having to worry about it. It also supports typescript out of the box.
+
+### Production
+
+`commandkit start` starts your bot in production mode. You need to run `commandkit build` before running this command. This command also loads the environment variables from `.env` file in your project directory.
diff --git a/apps/website/docs/guide/09-commandkit-config.mdx b/apps/website/docs/guide/09-commandkit-config.mdx
new file mode 100644
index 0000000..1d3a149
--- /dev/null
+++ b/apps/website/docs/guide/09-commandkit-config.mdx
@@ -0,0 +1,132 @@
+---
+title: CommandKit config file
+description: Learn how to configure CommandKit CLI for your project.
+---
+
+# CommandKit Configuration
+
+CommandKit CLI can be configured using `commandkit.config` file in the root of your project directory (for example, by `package.json`). You can use either of the following files:
+
+- `commandkit.js`
+- `commandkit.config.js`
+- `commandkit.mjs`
+- `commandkit.config.mjs`
+- `commandkit.cjs`
+- `commandkit.config.cjs`
+- `commandkit.json`
+- `commandkit.config.json`
+- `commandkit.ts`
+- `commandkit.config.ts`
+- `commandkit.cts`
+- `commandkit.config.cts`
+- `commandkit.mts`
+- `commandkit.config.mts`
+
+:::info
+TypeScript configuration files only work if `process.features.typescript` is
+available.
+:::
+
+Throughout this guide, we'll be using `commandkit.config.mjs` as an example.
+
+The following is the sample configuration required to run your bot:
+
+```js title="commandkit.config.mjs"
+import { defineConfig } from 'commandkit';
+
+export default defineConfig({
+ src: 'src', // The source directory of your project.
+ main: 'index.mjs', // The JavaScript entry point of your project.
+});
+```
+
+:::warning
+For CommandKit CLI on version `0.1.7` it's recommended to use an ESM project
+instead of CommonJS due to the behavior of `require()`. If you're using
+CommonJS you'll have to use dynamic `import()`. However, this is not an issue
+for TypeScript CommonJS projects.
+:::
+
+## Options
+
+### `src`
+
+- Type: `string`
+
+The source directory of the project where your source code lives.
+
+### `main`
+
+- Type: `string`
+
+The entry point to your project.
+
+Example: If your source is structured as `src/index.ts`, this option must be set to `index.mjs`. This is because CommandKit always compiles your source code to esm format.
+
+:::warning
+The "src" part in this option isn't required because it's already defined in
+the `src` option.
+:::
+
+### `watch` (optional)
+
+- Type: `boolean`
+- Default: `true`
+
+Whether to watch for file changes or not.
+
+### `outDir` (optional)
+
+- Type: `string`
+- Default: `"dist"`
+
+The output directory to emit the production build to.
+
+### `envExtra` (optional)
+
+- Type: `boolean`
+- Default: `true`
+
+Extra env utilities to load. This allows you to load environment variables in different formats, like `Date`, `JSON`, `Boolean`, etc.
+
+### `nodeOptions` (optional)
+
+- Type: `string[]`
+- Default: `[]`
+
+Options to pass to Node.js.
+
+### `clearRestartLogs` (optional)
+
+- Type: `boolean`
+- Default: `true`
+
+Whether or not to clear default restart logs.
+
+### `minify` (optional)
+
+- Type: `boolean`
+- Default: `false`
+
+Whether or not to minify the production build.
+
+### `sourcemap` (optional)
+
+- Type: `boolean` | `"inline"`
+- Default: `false`
+
+Whether or not to include sourcemaps in the production build.
+
+### `antiCrash` (optional)
+
+- Type: `boolean`
+- Default: `true`
+
+Whether or not to inject anti-crash script in the production build.
+
+### `requirePolyfill` (optional)
+
+- Type: `boolean`
+- Default: `true`
+
+Whether or not to polyfill `require` function.
diff --git a/apps/website/docs/guide/10-migrating-from-djs-commander.mdx b/apps/website/docs/guide/10-migrating-from-djs-commander.mdx
new file mode 100644
index 0000000..1200cc2
--- /dev/null
+++ b/apps/website/docs/guide/10-migrating-from-djs-commander.mdx
@@ -0,0 +1,118 @@
+---
+title: Migrating from djs-commander
+description: A guide on migrating from DJS-Commander to CommandKit.
+---
+
+# Migrating from DJS-Commander
+
+If you're trying to use CommandKit as a drop-in replacement for DJS-Commander, you'll need to make a few changes to your code.
+
+:::warning
+This guide is **not** introducing the features this library offers over
+DJS-Commander. It's just going over the changes you'll need to make to your
+code to get it working with this library.
+:::
+
+## Setting up the command handler
+
+In DJS-Commander, you'd import and instantiate the `CommandHandler` class. The only difference with CommandKit is the name of the class. Instead of `CommandHandler`, it's called `CommandKit`.
+
+```js
+const { CommandKit } = require('commandkit');
+
+new CommandKit({
+ client,
+ commandsPath,
+ eventsPath,
+});
+```
+
+## Setting up development servers
+
+In DJS-Commander only a single development server was supported, and it was setup under the property name `testServer` when instantiating `CommandHandler`.
+
+```js
+new CommandHandler({
+ client,
+ commandsPath,
+ eventsPath,
+ testServer: '123456789012345678', // ❌
+});
+```
+
+In CommandKit, you can setup multiple development servers under the property name `devGuildIds`
+
+```js
+new CommandKit({
+ client,
+ commandsPath,
+ eventsPath,
+ devGuildIds: ['123456789012345678', '876543210987654321'], // ✅
+});
+```
+
+However, this does not automatically register all the commands in those specific servers. By default, CommandKit will globally register all commands. If you want to register commands in specific servers, you'll need to use the `devOnly` property inside `options` from within the command file object.
+
+```js
+module.exports = {
+ data: {
+ name: 'ping',
+ description: 'Pong!',
+ },
+
+ run: ({ interaction }) => {
+ interaction.reply('Pong!');
+ },
+
+ options: {
+ devOnly: true, // ✅
+ },
+};
+```
+
+This command will now be registered in your development server, however you won't be able to run this command as you haven't configure the developer user IDs. Yes, CommandKit has built in support for developer user IDs! You can set them up under the property name `devUserIds` when instantiating `CommandKit`.
+
+```js
+new CommandKit({
+ client,
+ commandsPath,
+ eventsPath,
+ devGuildIds: ['123456789012345678', '876543210987654321'],
+ devUserIds: ['123456789012345678', '876543210987654321'], // ✅
+});
+```
+
+## Deleting commands
+
+Deleting commands with CommandKit is a bit different from DJS-Commander's. In DJS-Commander you'd directly export the `deleted` property from the command object. In CommandKit, you're required to export the `deleted` property inside another object called `options`.
+
+```js
+module.exports = {
+ data: {
+ name: 'ping',
+ description: 'Pong!',
+ },
+
+ run: ({ interaction }) => {
+ interaction.reply('Pong!');
+ },
+
+ deleted: true, // ❌
+
+ options: {
+ deleted: true, // ✅
+ },
+};
+```
+
+## Updating validations parameters
+
+In DJS-Commander, you'd get access to the `interaction` and `commandObj` in a specified order through the parameters. In CommandKit, you get access to the `interaction` and `commandObj` through an object.
+
+```js
+// DJS-Commander
+module.exports = (interaction, commandObj) => {}; // ❌
+
+// CommandKit
+module.exports = ({ interaction, commandObj }) => {}; // ✅
+```
diff --git a/apps/website/docs/guide/intro.md b/apps/website/docs/guide/intro.md
deleted file mode 100644
index 20930e1..0000000
--- a/apps/website/docs/guide/intro.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-sidebar_position: 1
----
-
-# Guide intro
diff --git a/apps/website/src/css/custom.css b/apps/website/src/css/custom.css
index 2bc6a4c..49b39f7 100644
--- a/apps/website/src/css/custom.css
+++ b/apps/website/src/css/custom.css
@@ -6,25 +6,25 @@
/* You can override the default Infima variables here. */
:root {
- --ifm-color-primary: #2e8555;
- --ifm-color-primary-dark: #29784c;
- --ifm-color-primary-darker: #277148;
- --ifm-color-primary-darkest: #205d3b;
- --ifm-color-primary-light: #33925d;
- --ifm-color-primary-lighter: #359962;
- --ifm-color-primary-lightest: #3cad6e;
+ --ifm-color-primary: #a46ef8;
+ --ifm-color-primary-dark: #9a60f7;
+ --ifm-color-primary-darker: #7e33f6;
+ --ifm-color-primary-darkest: #5e0ce2;
+ --ifm-color-primary-light: #7e33f6;
+ --ifm-color-primary-lighter: #9a60f7;
+ --ifm-color-primary-lightest: #a46ef8;
--ifm-code-font-size: 95%;
--docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1);
}
/* For readability concerns, you should choose a lighter palette in dark mode. */
[data-theme='dark'] {
- --ifm-color-primary: #25c2a0;
- --ifm-color-primary-dark: #21af90;
- --ifm-color-primary-darker: #1fa588;
- --ifm-color-primary-darkest: #1a8870;
- --ifm-color-primary-light: #29d5b0;
- --ifm-color-primary-lighter: #32d8b4;
- --ifm-color-primary-lightest: #4fddbf;
+ --ifm-color-primary: #a46ef8;
+ --ifm-color-primary-dark: #9a60f7;
+ --ifm-color-primary-darker: #7e33f6;
+ --ifm-color-primary-darkest: #5e0ce2;
+ --ifm-color-primary-light: #7e33f6;
+ --ifm-color-primary-lighter: #9a60f7;
+ --ifm-color-primary-lightest: #a46ef8;
--docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3);
}