diff --git a/.github/workflows/code-quality.yaml b/.github/workflows/code-quality.yaml
new file mode 100644
index 00000000..a14e4019
--- /dev/null
+++ b/.github/workflows/code-quality.yaml
@@ -0,0 +1,52 @@
+name: Code Quality
+
+on:
+ push:
+ branches:
+ - '**'
+ - '!renovate/*'
+
+jobs:
+ check-types:
+ name: Check Types
+ runs-on: ubuntu-latest
+ timeout-minutes: 10
+ steps:
+ - uses: pnpm/action-setup@v4
+ with:
+ version: '10.14.0'
+
+ - uses: actions/checkout@v4
+
+ - uses: actions/setup-node@v4
+ with:
+ node-version: 24
+ registry-url: https://registry.npmjs.org
+
+ - name: Install Dependencies
+ run: pnpm install --frozen-lockfile
+
+ - name: Check TypeScript Types
+ run: pnpm dlx turbo check-types
+
+ prettier:
+ name: Prettier Check
+ runs-on: ubuntu-latest
+ timeout-minutes: 10
+ steps:
+ - uses: pnpm/action-setup@v4
+ with:
+ version: '10.14.0'
+
+ - uses: actions/checkout@v4
+
+ - uses: actions/setup-node@v4
+ with:
+ node-version: 24
+ registry-url: https://registry.npmjs.org
+
+ - name: Install Dependencies
+ run: pnpm install --frozen-lockfile
+
+ - name: Check Prettier Formatting
+ run: pnpm prettier:check
diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml
deleted file mode 100644
index 1a66a29d..00000000
--- a/.github/workflows/lint.yaml
+++ /dev/null
@@ -1,30 +0,0 @@
-name: Lint
-
-on:
- push:
- branches:
- - '**'
- - '!renovate/*'
-
-jobs:
- build:
- name: Lint
- runs-on: ubuntu-latest
- timeout-minutes: 10
- steps:
- - uses: pnpm/action-setup@v4
- with:
- version: '10.14.0'
-
- - uses: actions/checkout@v4
-
- - uses: actions/setup-node@v4
- with:
- node-version: 22
- registry-url: https://registry.npmjs.org
-
- - name: Install dependencies
- run: pnpm install --frozen-lockfile
-
- - name: Lint
- run: pnpm dlx turbo lint
diff --git a/.github/workflows/publish-dev.yaml b/.github/workflows/publish-dev.yaml
index 71cf5cca..48d0ecac 100644
--- a/.github/workflows/publish-dev.yaml
+++ b/.github/workflows/publish-dev.yaml
@@ -19,7 +19,7 @@ jobs:
- uses: actions/setup-node@v4
with:
- node-version: 22
+ node-version: 24
registry-url: https://registry.npmjs.org
- name: Install dependencies
diff --git a/.github/workflows/publish-latest.yaml b/.github/workflows/publish-latest.yaml
index ebb3f64b..9863e80e 100644
--- a/.github/workflows/publish-latest.yaml
+++ b/.github/workflows/publish-latest.yaml
@@ -18,7 +18,7 @@ jobs:
- uses: actions/setup-node@v4
with:
- node-version: 22
+ node-version: 24
registry-url: https://registry.npmjs.org
- name: Install dependencies
@@ -39,6 +39,8 @@ jobs:
"@commandkit/i18n:packages/i18n"
"@commandkit/devtools:packages/devtools"
"@commandkit/cache:packages/cache"
+ "@commandkit/analytics:packages/analytics"
+ "@commandkit/ai:packages/ai"
"@commandkit/queue:packages/queue"
"@commandkit/tasks:packages/tasks"
)
@@ -46,5 +48,21 @@ jobs:
for entry in "${PACKAGES[@]}"; do
IFS=":" read -r name path <<< "$entry"
echo "Publishing $name..."
- (pnpm --filter="$name" publish --no-git-checks --access public && echo "✅ Published $name") || echo "❌ Failed to publish $name"
+
+ VERSION=$(node -p "require('./$path/package.json').version")
+
+ if npm view "$name@$VERSION" version >/dev/null 2>&1; then
+ echo "📦 $name@$VERSION already exists on npm, skipping..."
+ else
+ if pnpm --filter="$name" publish --no-git-checks --access public; then
+ echo "✅ Published $name@$VERSION"
+ else
+ if npm view "$name@$VERSION" version >/dev/null 2>&1; then
+ echo "📦 $name@$VERSION was published by another process, skipping..."
+ else
+ echo "❌ Failed to publish $name@$VERSION"
+ exit 1
+ fi
+ fi
+ fi
done
diff --git a/.github/workflows/publish-rc.yaml b/.github/workflows/publish-rc.yaml
new file mode 100644
index 00000000..07ad23f2
--- /dev/null
+++ b/.github/workflows/publish-rc.yaml
@@ -0,0 +1,104 @@
+name: Publish release candidate builds
+
+on:
+ workflow_dispatch:
+ inputs:
+ rc_version:
+ description: 'Release candidate version number (e.g., 1 for -rc1, 2 for -rc2)'
+ required: true
+ type: string
+
+jobs:
+ publish:
+ name: Publish release candidate builds
+ runs-on: ubuntu-latest
+ steps:
+ - uses: pnpm/action-setup@v4
+ with:
+ version: '10.14.0'
+
+ - uses: actions/checkout@v4
+
+ - uses: actions/setup-node@v4
+ with:
+ node-version: 24
+ registry-url: https://registry.npmjs.org
+
+ - name: Install dependencies
+ run: pnpm install --frozen-lockfile
+
+ - name: Validate RC version input
+ run: |
+ if ! [[ "${{ github.event.inputs.rc_version }}" =~ ^[0-9]+$ ]]; then
+ echo "❌ RC version must be a positive integer"
+ exit 1
+ fi
+
+ - name: Update package versions
+ run: |
+ rc_suffix="-rc${{ github.event.inputs.rc_version }}"
+ echo "Adding suffix: $rc_suffix"
+
+ for dir in packages/*; do
+ [ -f "$dir/package.json" ] || continue
+ echo "Updating $dir/package.json..."
+ node -e "
+ const fs = require('fs');
+ const path = './$dir/package.json';
+ const pkg = require(path);
+ pkg.version += '$rc_suffix';
+ fs.writeFileSync(path, JSON.stringify(pkg, null, 2));
+ console.log('Updated ' + pkg.name + ' to version ' + pkg.version);
+ "
+ done
+
+ - name: Build packages
+ run: pnpm dlx turbo build --filter='./packages/*'
+
+ - name: Publish packages
+ env:
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }}
+ run: |
+ PACKAGES=(
+ "commandkit:packages/commandkit"
+ "create-commandkit:packages/create-commandkit"
+ "@commandkit/legacy:packages/legacy"
+ "@commandkit/redis:packages/redis"
+ "@commandkit/i18n:packages/i18n"
+ "@commandkit/devtools:packages/devtools"
+ "@commandkit/cache:packages/cache"
+ "@commandkit/analytics:packages/analytics"
+ "@commandkit/ai:packages/ai"
+ "@commandkit/queue:packages/queue"
+ "@commandkit/tasks:packages/tasks"
+ )
+
+ for entry in "${PACKAGES[@]}"; do
+ IFS=":" read -r name path <<< "$entry"
+ echo "Publishing $name..."
+
+ VERSION=$(node -p "require('./$path/package.json').version")
+
+ if npm view "$name@$VERSION" version >/dev/null 2>&1; then
+ echo "📦 $name@$VERSION already exists on npm, skipping..."
+ else
+ if pnpm --filter="$name" publish --no-git-checks --access public --tag next; then
+ echo "✅ Published $name@$VERSION under 'next' tag"
+ else
+ if npm view "$name@$VERSION" version >/dev/null 2>&1; then
+ echo "📦 $name@$VERSION was published by another process, skipping..."
+ else
+ echo "❌ Failed to publish $name@$VERSION"
+ exit 1
+ fi
+ fi
+ fi
+ done
+
+ - name: Summary
+ run: |
+ echo "🎉 Release candidate build completed!"
+ echo "📋 Summary:"
+ echo " RC Version: ${{ github.event.inputs.rc_version }}"
+ echo " Tag: next"
+ echo " Suffix: -rc${{ github.event.inputs.rc_version }}"
diff --git a/.prettierignore b/.prettierignore
index 66e75dec..ad824971 100644
--- a/.prettierignore
+++ b/.prettierignore
@@ -1,6 +1,7 @@
node_modules
**/website/docs/api-reference
+**/website/docs/guide.old
**/website/versioned_docs
**/website/versioned_sidebars
.docusaurus
diff --git a/.prettierrc.json b/.prettierrc.json
index 69a3d7f8..00b6afdb 100644
--- a/.prettierrc.json
+++ b/.prettierrc.json
@@ -7,5 +7,14 @@
"semi": true,
"endOfLine": "lf",
"useTabs": false,
- "jsxSingleQuote": false
+ "jsxSingleQuote": false,
+ "overrides": [
+ {
+ "files": ["*.md", "*.mdx"],
+ "options": {
+ "proseWrap": "always",
+ "printWidth": 70
+ }
+ }
+ ]
}
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
index 23416733..7c90f078 100644
--- a/CODE_OF_CONDUCT.md
+++ b/CODE_OF_CONDUCT.md
@@ -2,131 +2,139 @@
## Our Pledge
-We as members, contributors, and leaders pledge to make participation in our
-community a harassment-free experience for everyone, regardless of age, body
-size, visible or invisible disability, ethnicity, sex characteristics, gender
-identity and expression, level of experience, education, socio-economic status,
-nationality, personal appearance, race, caste, color, religion, or sexual
-identity and orientation.
+We as members, contributors, and leaders pledge to make participation
+in our community a harassment-free experience for everyone, regardless
+of age, body size, visible or invisible disability, ethnicity, sex
+characteristics, gender identity and expression, level of experience,
+education, socio-economic status, nationality, personal appearance,
+race, caste, color, religion, or sexual identity and orientation.
-We pledge to act and interact in ways that contribute to an open, welcoming,
-diverse, inclusive, and healthy community.
+We pledge to act and interact in ways that contribute to an open,
+welcoming, diverse, inclusive, and healthy community.
## Our Standards
-Examples of behavior that contributes to a positive environment for our
-community include:
+Examples of behavior that contributes to a positive environment for
+our community include:
- Demonstrating empathy and kindness toward other people
- Being respectful of differing opinions, viewpoints, and experiences
- Giving and gracefully accepting constructive feedback
-- Accepting responsibility and apologizing to those affected by our mistakes,
- and learning from the experience
-- Focusing on what is best not just for us as individuals, but for the overall
- community
+- Accepting responsibility and apologizing to those affected by our
+ mistakes, and learning from the experience
+- Focusing on what is best not just for us as individuals, but for the
+ overall community
Examples of unacceptable behavior include:
-- The use of sexualized language or imagery, and sexual attention or advances of
- any kind
-- Trolling, insulting or derogatory comments, and personal or political attacks
+- The use of sexualized language or imagery, and sexual attention or
+ advances of any kind
+- Trolling, insulting or derogatory comments, and personal or
+ political attacks
- Public or private harassment
-- Publishing others' private information, such as a physical or email address,
- without their explicit permission
-- Other conduct which could reasonably be considered inappropriate in a
- professional setting
+- Publishing others' private information, such as a physical or email
+ address, without their explicit permission
+- Other conduct which could reasonably be considered inappropriate in
+ a professional setting
## Enforcement Responsibilities
-Community leaders are responsible for clarifying and enforcing our standards of
-acceptable behavior and will take appropriate and fair corrective action in
-response to any behavior that they deem inappropriate, threatening, offensive,
-or harmful.
+Community leaders are responsible for clarifying and enforcing our
+standards of acceptable behavior and will take appropriate and fair
+corrective action in response to any behavior that they deem
+inappropriate, threatening, offensive, or harmful.
-Community leaders have the right and responsibility to remove, edit, or reject
-comments, commits, code, wiki edits, issues, and other contributions that are
-not aligned to this Code of Conduct, and will communicate reasons for moderation
-decisions when appropriate.
+Community leaders have the right and responsibility to remove, edit,
+or reject comments, commits, code, wiki edits, issues, and other
+contributions that are not aligned to this Code of Conduct, and will
+communicate reasons for moderation decisions when appropriate.
## Scope
-This Code of Conduct applies within all community spaces, and also applies when
-an individual is officially representing the community in public spaces.
-Examples of representing our community include using an official email address,
-posting via an official social media account, or acting as an appointed
-representative at an online or offline event.
+This Code of Conduct applies within all community spaces, and also
+applies when an individual is officially representing the community in
+public spaces. Examples of representing our community include using an
+official email address, posting via an official social media account,
+or acting as an appointed representative at an online or offline
+event.
## Enforcement
-Instances of abusive, harassing, or otherwise unacceptable behavior may be
-reported to the community leaders responsible for enforcement at
-support@underctrl.io.
-All complaints will be reviewed and investigated promptly and fairly.
+Instances of abusive, harassing, or otherwise unacceptable behavior
+may be reported to the community leaders responsible for enforcement
+at support@underctrl.io. All complaints will be reviewed and
+investigated promptly and fairly.
-All community leaders are obligated to respect the privacy and security of the
-reporter of any incident.
+All community leaders are obligated to respect the privacy and
+security of the reporter of any incident.
## Enforcement Guidelines
-Community leaders will follow these Community Impact Guidelines in determining
-the consequences for any action they deem in violation of this Code of Conduct:
+Community leaders will follow these Community Impact Guidelines in
+determining the consequences for any action they deem in violation of
+this Code of Conduct:
### 1. Correction
-**Community Impact**: Use of inappropriate language or other behavior deemed
-unprofessional or unwelcome in the community.
+**Community Impact**: Use of inappropriate language or other behavior
+deemed unprofessional or unwelcome in the community.
-**Consequence**: A private, written warning from community leaders, providing
-clarity around the nature of the violation and an explanation of why the
-behavior was inappropriate. A public apology may be requested.
+**Consequence**: A private, written warning from community leaders,
+providing clarity around the nature of the violation and an
+explanation of why the behavior was inappropriate. A public apology
+may be requested.
### 2. Warning
-**Community Impact**: A violation through a single incident or series of
-actions.
+**Community Impact**: A violation through a single incident or series
+of actions.
-**Consequence**: A warning with consequences for continued behavior. No
-interaction with the people involved, including unsolicited interaction with
-those enforcing the Code of Conduct, for a specified period of time. This
-includes avoiding interactions in community spaces as well as external channels
-like social media. Violating these terms may lead to a temporary or permanent
-ban.
+**Consequence**: A warning with consequences for continued behavior.
+No interaction with the people involved, including unsolicited
+interaction with those enforcing the Code of Conduct, for a specified
+period of time. This includes avoiding interactions in community
+spaces as well as external channels like social media. Violating these
+terms may lead to a temporary or permanent ban.
### 3. Temporary Ban
-**Community Impact**: A serious violation of community standards, including
-sustained inappropriate behavior.
+**Community Impact**: A serious violation of community standards,
+including sustained inappropriate behavior.
-**Consequence**: A temporary ban from any sort of interaction or public
-communication with the community for a specified period of time. No public or
-private interaction with the people involved, including unsolicited interaction
-with those enforcing the Code of Conduct, is allowed during this period.
-Violating these terms may lead to a permanent ban.
+**Consequence**: A temporary ban from any sort of interaction or
+public communication with the community for a specified period of
+time. No public or private interaction with the people involved,
+including unsolicited interaction with those enforcing the Code of
+Conduct, is allowed during this period. Violating these terms may lead
+to a permanent ban.
### 4. Permanent Ban
-**Community Impact**: Demonstrating a pattern of violation of community
-standards, including sustained inappropriate behavior, harassment of an
-individual, or aggression toward or disparagement of classes of individuals.
+**Community Impact**: Demonstrating a pattern of violation of
+community standards, including sustained inappropriate behavior,
+harassment of an individual, or aggression toward or disparagement of
+classes of individuals.
-**Consequence**: A permanent ban from any sort of public interaction within the
-community.
+**Consequence**: A permanent ban from any sort of public interaction
+within the community.
## Attribution
-This Code of Conduct is adapted from the [Contributor Covenant][homepage],
-version 2.1, available at
+This Code of Conduct is adapted from the [Contributor
+Covenant][homepage], version 2.1, available at
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
-Community Impact Guidelines were inspired by
-[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
+Community Impact Guidelines were inspired by [Mozilla's code of
+conduct enforcement ladder][Mozilla CoC].
-For answers to common questions about this code of conduct, see the FAQ at
-[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
+For answers to common questions about this code of conduct, see the
+FAQ at [https://www.contributor-covenant.org/faq][FAQ]. Translations
+are available at
[https://www.contributor-covenant.org/translations][translations].
[homepage]: https://www.contributor-covenant.org
-[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
+[v2.1]:
+ https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
[Mozilla CoC]: https://github.com/mozilla/diversity
[FAQ]: https://www.contributor-covenant.org/faq
[translations]: https://www.contributor-covenant.org/translations
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 524a9a1f..9943aec3 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,6 +1,10 @@
# Contributing to CommandKit
-Firstly, thank you for considering contributing to CommandKit! Whether you're looking to fix bugs, add new features, or just ask questions, your input is valuable to us. Before actually opening a pull request, we recommend that you first open an issue (or check existing ones) to discuss the feature/bug you intend to add/fix.
+Firstly, thank you for considering contributing to CommandKit! Whether
+you're looking to fix bugs, add new features, or just ask questions,
+your input is valuable to us. Before actually opening a pull request,
+we recommend that you first open an issue (or check existing ones) to
+discuss the feature/bug you intend to add/fix.
## Setup
@@ -16,12 +20,13 @@ Firstly, thank you for considering contributing to CommandKit! Whether you're lo
2. Clone your fork to your local machine:
```bash
-git clone https://github.com/underctrl-io/commandkit.git
+git clone https://github.com/your_username/commandkit.git
```
### Installing Dependencies
-Since we use `pnpm` workspaces, it's essential to use `pnpm` for installing dependencies.
+Since we use `pnpm` workspaces, it's essential to use `pnpm` for
+installing dependencies.
```bash
cd commandkit
@@ -36,13 +41,27 @@ pnpm install # Make sure to run this from the root directory
git checkout -b your-feature-or-bugfix
```
-2. Make your changes. Please make sure to use the [Prettier](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) extension for consistent formatting and comments wherever necessary.
-
-3. Ensure that your changes don't break any existing functionality. You can test the functionality of your code depending on where you've made changes:
- 1. If you've made changes to the CommandKit package, you can use the "apps/test-bot" project to test your own bot. Just make sure to create a new `.env` file with the template from the `.env.example` file provided. This also requires you to build the commandkit package locally (after you make your changes) because it's symlinked with pnpm workspaces.
- 2. If you've made changes to the docs, you can run `pnpm dev` inside "apps/website" to spin up a local development server.
-
-4. Run `pnpm lint` from the root directory to ensure all lint scripts and formatting is valid.
+2. Make your changes. Please make sure to use the
+ [Prettier](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode)
+ extension for consistent formatting and comments wherever
+ necessary. Alternatively, you can run `pnpm prettier:format` after
+ you've made your changes.
+
+3. Ensure that your changes don't break any existing functionality.
+ You can test the functionality of your code depending on where
+ you've made changes:
+ 1. If you've made changes to the CommandKit package, you can use
+ the "apps/test-bot" project to test your own bot. Just make sure
+ to create a new `.env` file with the template from the
+ `.env.example` file provided. This also requires you to build
+ the CommandKit package (at `packages/commandkit`) locally (after
+ you make your changes) because it's symlinked with pnpm
+ workspaces.
+ 2. If you've made changes to the docs, you can run `pnpm dev`
+ inside "apps/website" to spin up a local development server.
+
+4. Run `pnpm check-types` and `pnpm prettier:check` from the root
+ directory to ensure all type checking and formatting is valid.
5. Commit your changes:
@@ -56,12 +75,16 @@ git commit -m "Describe your change here"
git push origin your-feature-or-bugfix
```
-7. Open a pull request in the main project repository (main branch). Describe your changes and any relevant information.
+7. Open a pull request in the main project repository (`main` branch).
+ Describe your changes and any relevant information.
## Submitting Issues
-When submitting a new issue, please provide a detailed description of the problem, steps to reproduce it, and any relevant screenshots or error messages.
+When submitting a new issue, please provide a detailed description of
+the problem, steps to reproduce it, and any relevant screenshots or
+error messages.
---
-Thank you for making CommandKit better! We appreciate your effort and look forward to collaborating with you.
+Thank you for making CommandKit better! We appreciate your effort and
+look forward to collaborating with you.
diff --git a/apps/test-bot/src/app/commands/(general)/+middleware.ts b/apps/test-bot/src/app/commands/(general)/+middleware.ts
new file mode 100644
index 00000000..089016cb
--- /dev/null
+++ b/apps/test-bot/src/app/commands/(general)/+middleware.ts
@@ -0,0 +1,13 @@
+import { Logger, MiddlewareContext } from 'commandkit';
+
+export function beforeExecute(ctx: MiddlewareContext) {
+ Logger.info(
+ `Directory-scoped middleware: ${ctx.commandName} will be executed!`,
+ );
+}
+
+export function afterExecute(ctx: MiddlewareContext) {
+ Logger.info(
+ `Directory-scoped middleware: ${ctx.commandName} has been executed!`,
+ );
+}
diff --git a/apps/test-bot/src/app/commands/(general)/+ping.middleware.ts b/apps/test-bot/src/app/commands/(general)/+ping.middleware.ts
new file mode 100644
index 00000000..7f5573c4
--- /dev/null
+++ b/apps/test-bot/src/app/commands/(general)/+ping.middleware.ts
@@ -0,0 +1,13 @@
+import { Logger, MiddlewareContext } from 'commandkit';
+
+export function beforeExecute(ctx: MiddlewareContext) {
+ Logger.info(
+ `Command-scoped middleware: ${ctx.commandName} will be executed!`,
+ );
+}
+
+export function afterExecute(ctx: MiddlewareContext) {
+ Logger.info(
+ `Command-scoped middleware: ${ctx.commandName} has been executed!`,
+ );
+}
diff --git a/apps/test-bot/src/app/commands/+global-middleware.ts b/apps/test-bot/src/app/commands/+global-middleware.ts
index fd7c930e..f0e02933 100644
--- a/apps/test-bot/src/app/commands/+global-middleware.ts
+++ b/apps/test-bot/src/app/commands/+global-middleware.ts
@@ -1,9 +1,9 @@
import { Logger, MiddlewareContext } from 'commandkit';
export function beforeExecute(ctx: MiddlewareContext) {
- Logger.info(`${ctx.commandName} will be executed!`);
+ Logger.info(`Global middleware: ${ctx.commandName} will be executed!`);
}
export function afterExecute(ctx: MiddlewareContext) {
- Logger.info(`${ctx.commandName} has been executed!`);
+ Logger.info(`Global middleware: ${ctx.commandName} has been executed!`);
}
diff --git a/apps/test-bot/src/app/events/messageCreate/give-xp.ts b/apps/test-bot/src/app/events/messageCreate/give-xp.ts
index 94b9f592..6707ba76 100644
--- a/apps/test-bot/src/app/events/messageCreate/give-xp.ts
+++ b/apps/test-bot/src/app/events/messageCreate/give-xp.ts
@@ -1,8 +1,8 @@
-import type { Message } from 'discord.js';
-import { database } from '../../../database/store.ts';
import { revalidateTag } from '@commandkit/cache';
+import type { EventHandler } from 'commandkit';
+import { database } from '../../../database/store.ts';
-export default async function (message: Message) {
+const handler: EventHandler<'messageCreate'> = async (message) => {
if (message.author.bot || !message.inGuild()) return;
const key = `xp:${message.guildId}:${message.author.id}`;
@@ -13,4 +13,6 @@ export default async function (message: Message) {
database.set(key, newXp);
await revalidateTag(key);
-}
+};
+
+export default handler;
diff --git a/apps/website/docs/api-reference/commandkit/interfaces/command-context.mdx b/apps/website/docs/api-reference/commandkit/interfaces/command-context.mdx
index bb9d68b2..5d8b439c 100644
--- a/apps/website/docs/api-reference/commandkit/interfaces/command-context.mdx
+++ b/apps/website/docs/api-reference/commandkit/interfaces/command-context.mdx
@@ -13,7 +13,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
## CommandContext
-
+
Represents a command context.
diff --git a/apps/website/docs/api-reference/commandkit/interfaces/command-kit-options.mdx b/apps/website/docs/api-reference/commandkit/interfaces/command-kit-options.mdx
index a4f10acf..b7ba311d 100644
--- a/apps/website/docs/api-reference/commandkit/interfaces/command-kit-options.mdx
+++ b/apps/website/docs/api-reference/commandkit/interfaces/command-kit-options.mdx
@@ -13,7 +13,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
## CommandKitOptions
-
+
Options for instantiating a CommandKit handler.
diff --git a/apps/website/docs/api-reference/commandkit/types/command-data.mdx b/apps/website/docs/api-reference/commandkit/types/command-data.mdx
index e6385440..8faa1350 100644
--- a/apps/website/docs/api-reference/commandkit/types/command-data.mdx
+++ b/apps/website/docs/api-reference/commandkit/types/command-data.mdx
@@ -13,7 +13,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
## CommandData
-
+
Represents a command that can be executed by CommandKit.
diff --git a/apps/website/docs/api-reference/commandkit/types/event-handler.mdx b/apps/website/docs/api-reference/commandkit/types/event-handler.mdx
new file mode 100644
index 00000000..258bbde0
--- /dev/null
+++ b/apps/website/docs/api-reference/commandkit/types/event-handler.mdx
@@ -0,0 +1,24 @@
+---
+title: "EventHandler"
+isDefaultIndex: false
+generated: true
+---
+
+import MemberInfo from '@site/src/components/MemberInfo';
+import GenerationInfo from '@site/src/components/GenerationInfo';
+import MemberDescription from '@site/src/components/MemberDescription';
+
+
+
+
+## EventHandler
+
+
+
+Represents an event handler for a specific event.
+
+```ts title="Signature"
+type EventHandler = (
+ ...args: ClientEvents[K]
+) => void | Promise
+```
diff --git a/apps/website/docs/guide.old/01-getting-started/01-introduction.mdx b/apps/website/docs/guide.old/01-getting-started/01-introduction.mdx
new file mode 100644
index 00000000..fffce6da
--- /dev/null
+++ b/apps/website/docs/guide.old/01-getting-started/01-introduction.mdx
@@ -0,0 +1,81 @@
+---
+title: Introduction
+description: A brief intro to CommandKit
+---
+
+
+
+CommandKit is a powerful meta-framework for building Discord bots with
+[discord.js](https://discord.js.org/). It provides a simple and
+intuitive API for creating commands, handling interactions, and
+managing events. With CommandKit, you can focus on building your bot's
+features without worrying about the underlying complexities.
+
+## Key Features
+
+- Beginner friendly 🚀
+- Suitable for both beginners and advanced users 👶👨💻
+- Slash + context menu commands + prefix commands support ✅
+- Automatic command registration and updates 🤖
+- Command middlewares for easy command management 🛠️
+- Localization support through `@commandkit/i18n` plugin 🌍
+- Plugin system to extend functionality 🔌
+- Built-in command line interface for easy development 🖥️
+- Out-of-the-box support for TypeScript and JavaScript 📜
+- Built-in customizable cache system for speedy data storage and
+ retrieval 🗄️
+- User installable/guild scoped commands 🔧
+- Custom events support 🔔
+- JSX support for declaring Discord interaction components and modals
+ 🎨
+- Easy to use interaction components and modals system (forget about
+ collectors) 🧩
+- Less boilerplate code, more productivity 💪
+- and much more...
+
+## Documentation
+
+You can start with the
+[setting up CommandKit](./02-setup-commandkit.mdx) guide to get your
+bot up and running quickly. The documentation covers everything from
+installation to advanced features.
+
+## Support and Suggestions
+
+Submit any queries or suggestions in our
+[Discord community](https://ctrl.lol/discord).
diff --git a/apps/website/docs/guide.old/01-getting-started/02-setup-commandkit.mdx b/apps/website/docs/guide.old/01-getting-started/02-setup-commandkit.mdx
new file mode 100644
index 00000000..4bc4972e
--- /dev/null
+++ b/apps/website/docs/guide.old/01-getting-started/02-setup-commandkit.mdx
@@ -0,0 +1,84 @@
+---
+title: Setup CommandKit
+description:
+ Setup a new CommandKit project manually, or using the
+ create-commandkit CLI
+---
+
+You can quickly setup a new CommandKit project using
+`create-commandkit` — a command-line utility used for creating new
+discord.js applications with CommandKit. To get started, run the
+following command:
+
+```sh npm2yarn
+npm create commandkit@latest
+```
+
+This will start the CLI in the current directory which will help you
+quickly setup a base CommandKit project.
+
+## Project structure
+
+By using the CLI to create a base project, you should get a project
+structure that looks like this:
+
+```
+.
+├── src/
+│ ├── app/
+│ │ ├── commands/
+│ │ │ └── ping.ts
+│ │ └── events/
+│ │ └── ready/
+│ │ └── log.ts
+│ └── app.ts
+├── .env
+├── .gitignore
+├── commandkit.config.ts
+├── package.json
+└── tsconfig.json
+```
+
+:::info The `src/app.ts` file is the main entry point for your
+application. This file default exports the discord.js client which
+CommandKit loads at runtime. For example, the `src/app.ts` file looks
+like this:
+
+```ts
+import { Client } from 'discord.js';
+
+const client = new Client({
+ intents: [
+ /* add stuff */
+ ],
+});
+
+// setting up the token manually
+client.token = process.env.MY_BOT_TOKEN;
+
+export default client;
+```
+
+Notice how we are not calling `client.login()` in this file. This is
+because CommandKit will automatically call `client.login()` for you
+when the application starts.
+
+Also, you might have noticed that we are not using anything from
+CommandKit in this file. This is because CommandKit is designed to
+reduce boilerplate code and make it easier to build discord bot
+applications. :::
+
+:::info The `src/app` directory is a special directory that CommandKit
+understands. All the commands and events in this directory will be
+automatically registered when the application starts. :::
+
+## Development version
+
+:::warning The development version is likely to have bugs. :::
+
+If you'd like to try the latest development builds, you can use the
+`@dev` tag like so:
+
+```sh npm2yarn
+npm create commandkit@dev
+```
diff --git a/apps/website/docs/guide.old/01-getting-started/03-setup-commandkit-manually.mdx b/apps/website/docs/guide.old/01-getting-started/03-setup-commandkit-manually.mdx
new file mode 100644
index 00000000..3ec89823
--- /dev/null
+++ b/apps/website/docs/guide.old/01-getting-started/03-setup-commandkit-manually.mdx
@@ -0,0 +1,163 @@
+---
+title: Setup CommandKit manually
+description: Learn how to install CommandKit.
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+If you don't want to use the CLI to automatically generate a base
+CommandKit project, you can integrate CommandKit manually into your
+application.
+
+:::info While this guide primarily uses TypeScript, you can
+alternatively use JavaScript files to follow along if that's what
+you're comfortable with (e.g. `commandkit.config.js`, `app.js`, etc)
+:::
+
+## Configuration file
+
+To get started, create a file called `commandkit.config.ts` at the
+root of your project. This file is used to configure CommandKit and
+its plugins.
+
+```ts title="commandkit.config.ts"
+import { defineConfig } from 'commandkit';
+
+export default defineConfig({});
+```
+
+## Entrypoint file
+
+Then, create a folder called `src`, followed by a file called `app.ts`
+inside it. The file should look like this:
+
+```ts title="src/app.ts"
+import { Client } from 'discord.js';
+
+const client = new Client({
+ intents: ['Guilds', 'GuildMembers'],
+});
+
+client.token = '...'; // Optional: You can manually set your bot's token
+
+// must export the `client` here
+export default client;
+```
+
+With the current entrypoint file created, it's important to understand
+that:
+
+1. The `app.ts` file is the entrypoint for this application. This file
+ **must** export your discord.js client instance.
+2. You don't have to call `client.login()` as CommandKit will handle
+ that for you.
+3. You should store your bot token as an environment variable with
+ either `DISCORD_TOKEN` or `TOKEN` as the name.
+
+## Adding commands
+
+To add a command, create a folder inside the `src/app` directory
+called `commands` and create your command file (e.g. `ping.ts`). This
+example will use a simple ping/pong command which will register as a
+chat input (slash) and message (legacy) command.
+
+```ts title="src/app/commands/ping.ts"
+import type {
+ ChatInputCommand,
+ CommandData,
+ MessageCommand,
+} from 'commandkit';
+
+export const command: CommandData = {
+ name: 'ping',
+ description: 'Pong!',
+};
+
+export const chatInput: ChatInputCommand = async ({
+ interaction,
+}) => {
+ await interaction.reply('Pong!');
+};
+
+export const message: MessageCommand = async ({ message }) => {
+ await message.reply('Pong!');
+};
+```
+
+This command will reply with "Pong!" when the user runs the `/ping`
+slash command, or sends `!ping` in the chat.
+
+:::tip Prefixes for your message (legacy) commands can be changed.
+Learn more [here](/docs/next/guide/resolve-message-commands-prefix).
+:::
+
+## Adding events
+
+To register and handle events emitted by discord.js, create a folder
+inside the `src/app` directory called `events` and create a folder
+with the name of the discord.js event you'd like to handle (e.g.
+ready, messageCreate, etc). This example will use the `ready` event.
+
+In the `src/app/events/{eventName}` directory, you can create files
+which will export default functions that will be called when the
+respective event is emitted by discord.js. Following the `ready` event
+example mentioned above, you may want to log when your bot comes
+online. The function for that will look like so:
+
+```ts title="src/app/events/log.ts"
+import type { Client } from 'discord.js';
+
+export default function (client: Client) {
+ console.log(`Logged in as ${client.user.username}!`);
+}
+```
+
+## Running the app in development
+
+To run your application in development mode, you can use the
+`commandkit dev` command in your terminal. This will automatically
+reload the bot when you make changes to your code.
+
+```sh npm2yarn
+npx commandkit dev
+```
+
+:::warning When running in development mode, CommandKit will generate
+a `.commandkit` folder in the root of your project. This folder
+contains the compiled files used in development mode. You should not
+commit this folder to your version control system by making sure you
+add it to your `.gitignore` file. :::
+
+## Building for production
+
+When you are ready to deploy your bot, you can use `commandkit build`
+to create a production build of your bot. This will create a `dist`
+folder in your project directory containing the compiled files.
+
+```sh
+npx commandkit build
+```
+
+:::warning After running your build script, CommandKit will generate a
+`dist` folder in the root of your project. This folder contains the
+compiled files used for production. You should not commit this folder
+to your version control system by making sure you add it to your
+`.gitignore` file. :::
+
+## Running the app in production
+
+You can use `commandkit start` to start the bot in production mode.
+This will load the environment variables from the `.env` file in the
+root of your project directory.
+
+```sh
+npx commandkit start
+```
+
+If you want to manually start the bot in production mode, you can
+alternatively use the following command:
+
+```sh
+node dist/index.js
+```
diff --git a/apps/website/docs/guide/01-getting-started/04-commandkit-config.mdx b/apps/website/docs/guide.old/01-getting-started/04-commandkit-config.mdx
similarity index 85%
rename from apps/website/docs/guide/01-getting-started/04-commandkit-config.mdx
rename to apps/website/docs/guide.old/01-getting-started/04-commandkit-config.mdx
index 62b58f3a..d6379e2f 100644
--- a/apps/website/docs/guide/01-getting-started/04-commandkit-config.mdx
+++ b/apps/website/docs/guide.old/01-getting-started/04-commandkit-config.mdx
@@ -1,16 +1,21 @@
---
title: CommandKit Configuration
-description: Learn how to configure CommandKit CLI for your project with detailed examples and best practices.
+description:
+ Learn how to configure CommandKit CLI for your project with detailed
+ examples and best practices.
---
-CommandKit CLI can be configured using a `commandkit.config` file in the root of your project directory (typically alongside `package.json`). You can use any of the following file formats:
+CommandKit CLI can be configured using a `commandkit.config` file in
+the root of your project directory (typically alongside
+`package.json`). You can use any of the following file formats:
- `commandkit.config.js`
- `commandkit.config.mjs`
- `commandkit.config.cjs`
- `commandkit.config.ts`
-Throughout this guide, we'll be using `commandkit.config.ts` as an example.
+Throughout this guide, we'll be using `commandkit.config.ts` as an
+example.
## Basic Configuration
@@ -26,7 +31,8 @@ export default defineConfig({});
### Plugins
-The `plugins` array allows you to extend CommandKit's functionality with additional features:
+The `plugins` array allows you to extend CommandKit's functionality
+with additional features:
```ts title="commandkit.config.ts"
import { defineConfig } from 'commandkit';
@@ -106,11 +112,14 @@ export default defineConfig({
## Best Practices
-1. **Type Safety**: Always use `commandkit.config.ts` for better type checking and IDE support.
-2. **Security**: Always store sensitive information in `.env` file and make sure it is not committed to your version control system.
+1. **Type Safety**: Always use `commandkit.config.ts` for better type
+ checking and IDE support.
+2. **Security**: Always store sensitive information in `.env` file and
+ make sure it is not committed to your version control system.
3. **Plugin Management**:
- Only include necessary plugins to keep build times fast
- - Plugins have full access to your discord bot. So always be careful when installing the plugins.
+ - Plugins have full access to your discord bot. So always be
+ careful when installing the plugins.
## Complete Example
diff --git a/apps/website/docs/guide/01-getting-started/05-using-commandkit-cli.mdx b/apps/website/docs/guide.old/01-getting-started/05-using-commandkit-cli.mdx
similarity index 81%
rename from apps/website/docs/guide/01-getting-started/05-using-commandkit-cli.mdx
rename to apps/website/docs/guide.old/01-getting-started/05-using-commandkit-cli.mdx
index 8849212f..d6aca54c 100644
--- a/apps/website/docs/guide/01-getting-started/05-using-commandkit-cli.mdx
+++ b/apps/website/docs/guide.old/01-getting-started/05-using-commandkit-cli.mdx
@@ -1,13 +1,20 @@
---
title: Using CommandKit CLI
-description: Learn how to use CommandKit CLI to start, build, and manage your Discord bot application with comprehensive examples and best practices.
+description:
+ Learn how to use CommandKit CLI to start, build, and manage your
+ Discord bot application with comprehensive examples and best
+ practices.
---
-CommandKit CLI is a powerful tool that streamlines the development and deployment of your Discord bot application. It provides essential features like Hot Module Replacement (HMR), automatic crash recovery, and TypeScript support out of the box.
+CommandKit CLI is a powerful tool that streamlines the development and
+deployment of your Discord bot application. It provides essential
+features like Hot Module Replacement (HMR), automatic crash recovery,
+and TypeScript support out of the box.
## Getting Started
-To view all available CLI commands, run the following in your project directory:
+To view all available CLI commands, run the following in your project
+directory:
```sh
npx commandkit --help
@@ -33,10 +40,13 @@ Commands:
### Development Mode (`commandkit dev`)
-The development mode is optimized for local development with features like:
+The development mode is optimized for local development with features
+like:
-- **Hot Module Replacement (HMR)**: Automatically reloads your bot when you make changes to commands or events
-- **TypeScript Support**: Built-in TypeScript compilation and type checking
+- **Hot Module Replacement (HMR)**: Automatically reloads your bot
+ when you make changes to commands or events
+- **TypeScript Support**: Built-in TypeScript compilation and type
+ checking
- **Environment Variables**: Automatic loading of `.env` files
- **Error Handling**: Detailed error messages and stack traces
@@ -93,11 +103,13 @@ npx commandkit create event ready
## Best Practices
1. **Development Workflow**:
+
- Use `commandkit dev` during development for instant feedback
- Enable debug mode when troubleshooting issues
- Keep your `.env` file updated with all required variables
2. **Production Deployment**:
+
- Always run `commandkit build` before deploying
- Test the production build locally before deployment
- Use environment variables for sensitive data
@@ -111,11 +123,17 @@ npx commandkit create event ready
Common issues and solutions:
-- **Build Errors**: Check TypeScript configuration and type definitions
-- **Environment Variables**: Ensure `.env` file exists and contains required variables
-- **Hot Reload Issues**: Verify file paths and module exports. Hot reload only works for commands, events and the paths supported by the active plugins. Changes made to other paths will trigger full reload.
-
-For more detailed information about specific commands and options, use:
+- **Build Errors**: Check TypeScript configuration and type
+ definitions
+- **Environment Variables**: Ensure `.env` file exists and contains
+ required variables
+- **Hot Reload Issues**: Verify file paths and module exports. Hot
+ reload only works for commands, events and the paths supported by
+ the active plugins. Changes made to other paths will trigger full
+ reload.
+
+For more detailed information about specific commands and options,
+use:
```sh
npx commandkit help
diff --git a/apps/website/docs/guide/01-getting-started/06-onApplicationBootstrap-function.mdx b/apps/website/docs/guide.old/01-getting-started/06-onApplicationBootstrap-function.mdx
similarity index 50%
rename from apps/website/docs/guide/01-getting-started/06-onApplicationBootstrap-function.mdx
rename to apps/website/docs/guide.old/01-getting-started/06-onApplicationBootstrap-function.mdx
index eb064075..3db0a72c 100644
--- a/apps/website/docs/guide/01-getting-started/06-onApplicationBootstrap-function.mdx
+++ b/apps/website/docs/guide.old/01-getting-started/06-onApplicationBootstrap-function.mdx
@@ -1,9 +1,15 @@
---
title: onApplicationBootstrap function
-description: The onApplicationBootstrap function is called when the application is ready.
+description:
+ The onApplicationBootstrap function is called when the application
+ is ready.
---
-The `onApplicationBootstrap` is a lifecycle hook in CommandKit that allows you to execute code when the application is fully initialized and ready to run. This is particularly useful for setting up things like CommandKit cache providers, message command prefix resolvers, and other application-wide settings.
+The `onApplicationBootstrap` is a lifecycle hook in CommandKit that
+allows you to execute code when the application is fully initialized
+and ready to run. This is particularly useful for setting up things
+like CommandKit cache providers, message command prefix resolvers, and
+other application-wide settings.
## Basic Usage
@@ -16,4 +22,5 @@ onApplicationBootstrap((commandkit) => {
});
```
-You can register multiple `onApplicationBootstrap` hooks, and they will be called in the order they were registered.
+You can register multiple `onApplicationBootstrap` hooks, and they
+will be called in the order they were registered.
diff --git a/apps/website/docs/guide/01-getting-started/99-migrating-from-v0.mdx b/apps/website/docs/guide.old/01-getting-started/99-migrating-from-v0.mdx
similarity index 70%
rename from apps/website/docs/guide/01-getting-started/99-migrating-from-v0.mdx
rename to apps/website/docs/guide.old/01-getting-started/99-migrating-from-v0.mdx
index 72bec5a5..d25830fa 100644
--- a/apps/website/docs/guide/01-getting-started/99-migrating-from-v0.mdx
+++ b/apps/website/docs/guide.old/01-getting-started/99-migrating-from-v0.mdx
@@ -1,24 +1,29 @@
---
title: Migrating from CommandKit v0
-description: Learn how to migrate your CommandKit version from v0 to v1
+description:
+ Learn how to migrate your CommandKit version from v0 to v1
---
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
-This comprehensive guide will walk you through migrating your Discord bot from CommandKit v0 to v1. CommandKit v1 introduces significant architectural improvements, including a framework-based approach with enhanced developer experience features.
+This comprehensive guide will walk you through migrating your Discord
+bot from CommandKit v0 to v1. CommandKit v1 introduces significant
+architectural improvements, including a framework-based approach with
+enhanced developer experience features.
-:::info
-This guide uses TypeScript examples, but all concepts apply to JavaScript projects as well. Simply use the corresponding JavaScript file extensions (e.g., `app.js`, `commandkit.config.js`).
-:::
+:::info This guide uses TypeScript examples, but all concepts apply to
+JavaScript projects as well. Simply use the corresponding JavaScript
+file extensions (e.g., `app.js`, `commandkit.config.js`). :::
-:::warning
-**Minimum Requirements**: CommandKit v1 requires Node.js version 22 or higher. Please ensure your environment meets this requirement before proceeding.
-:::
+:::warning **Minimum Requirements**: CommandKit v1 requires Node.js
+version 22 or higher. Please ensure your environment meets this
+requirement before proceeding. :::
-:::warning
-**Migration Focus**: This guide specifically covers converting existing v0 code to v1. For information about new v1 features and capabilities, please refer to the rest of the documentation after completing your migration.
-:::
+:::warning **Migration Focus**: This guide specifically covers
+converting existing v0 code to v1. For information about new v1
+features and capabilities, please refer to the rest of the
+documentation after completing your migration. :::
## Updating CommandKit
@@ -28,11 +33,15 @@ Begin your migration by updating to the latest version of CommandKit:
npm install commandkit@dev
```
-This command will install CommandKit v1 and update your `package.json` with the latest version.
+This command will install CommandKit v1 and update your `package.json`
+with the latest version.
## Project Structure Migration
-CommandKit v1 adopts a framework-based approach with a structured `app` directory that serves as the primary location for your bot's functionality. This new structure provides better organization and enables advanced features like automatic route discovery.
+CommandKit v1 adopts a framework-based approach with a structured
+`app` directory that serves as the primary location for your bot's
+functionality. This new structure provides better organization and
+enables advanced features like automatic route discovery.
@@ -74,7 +83,10 @@ CommandKit v1 adopts a framework-based approach with a structured `app` director
## Configuration File Updates
-CommandKit v1 significantly simplifies configuration by automatically detecting your project structure and entry points. The framework now supports a standardized set of configuration file names for consistency.
+CommandKit v1 significantly simplifies configuration by automatically
+detecting your project structure and entry points. The framework now
+supports a standardized set of configuration file names for
+consistency.
**Supported Configuration Files:**
@@ -109,13 +121,16 @@ CommandKit v1 significantly simplifies configuration by automatically detecting
-:::info
-The CommandKit CLI commands (`commandkit dev`, `commandkit build`, `commandkit start`) continue to work seamlessly with the new configuration system.
-:::
+:::info The CommandKit CLI commands (`commandkit dev`,
+`commandkit build`, `commandkit start`) continue to work seamlessly
+with the new configuration system. :::
## Entry Point Transformation
-CommandKit v1 introduces a dramatically simplified entry point system. Instead of manually managing the CommandKit instance, bot login, and path configurations, you now simply export a configured Discord.js client.
+CommandKit v1 introduces a dramatically simplified entry point system.
+Instead of manually managing the CommandKit instance, bot login, and
+path configurations, you now simply export a configured Discord.js
+client.
@@ -168,7 +183,10 @@ CommandKit v1 introduces a dramatically simplified entry point system. Instead o
## Command File Structure
-CommandKit v1 modernizes the command API with more intuitive naming and cleaner type definitions. The new structure separates command metadata from execution logic more clearly, while introducing room for new APIs such as message (legacy) commands.
+CommandKit v1 modernizes the command API with more intuitive naming
+and cleaner type definitions. The new structure separates command
+metadata from execution logic more clearly, while introducing room for
+new APIs such as message (legacy) commands.
@@ -205,7 +223,10 @@ CommandKit v1 modernizes the command API with more intuitive naming and cleaner
## Middleware System (Formerly Validations)
-CommandKit v1 renames and enhances the validation system to "middleware," which better reflects its purpose and capabilities. Middleware can now run in various contexts and provides more granular control over command execution.
+CommandKit v1 renames and enhances the validation system to
+"middleware," which better reflects its purpose and capabilities.
+Middleware can now run in various contexts and provides more granular
+control over command execution.
@@ -238,11 +259,16 @@ CommandKit v1 renames and enhances the validation system to "middleware," which
-For comprehensive middleware documentation, including command-specific and conditional middleware, refer to the [middleware documentation](../07-file-system-conventions/02-+middleware.ts.mdx).
+For comprehensive middleware documentation, including command-specific
+and conditional middleware, refer to the
+[middleware documentation](../07-file-system-conventions/02-+middleware.ts.mdx).
## Development Environment
-CommandKit v1 introduces advanced development features that require using the CommandKit CLI. The development server provides Hot Module Replacement (HMR) for rapid iteration and supports modern features like JSX components.
+CommandKit v1 introduces advanced development features that require
+using the CommandKit CLI. The development server provides Hot Module
+Replacement (HMR) for rapid iteration and supports modern features
+like JSX components.
### Starting Development Server
@@ -250,8 +276,9 @@ CommandKit v1 introduces advanced development features that require using the Co
npx commandkit dev
```
-:::warning
-**Generated Files**: The development server creates a `.commandkit` directory for temporary files. Add this to your `.gitignore` to prevent committing generated files:
+:::warning **Generated Files**: The development server creates a
+`.commandkit` directory for temporary files. Add this to your
+`.gitignore` to prevent committing generated files:
```gitignore
.commandkit/
@@ -281,8 +308,9 @@ Option 2 - Direct Node.js execution:
node dist/index.js
```
-:::warning
-**Generated Build Files**: The build process creates a `dist` directory. Add this to your `.gitignore` to prevent committing build artifacts:
+:::warning **Generated Build Files**: The build process creates a
+`dist` directory. Add this to your `.gitignore` to prevent committing
+build artifacts:
```gitignore
dist/
diff --git a/apps/website/docs/guide/02-message-components/01-buttonkit.mdx b/apps/website/docs/guide.old/02-message-components/01-buttonkit.mdx
similarity index 75%
rename from apps/website/docs/guide/02-message-components/01-buttonkit.mdx
rename to apps/website/docs/guide.old/02-message-components/01-buttonkit.mdx
index 82b5c3ce..705526be 100644
--- a/apps/website/docs/guide/02-message-components/01-buttonkit.mdx
+++ b/apps/website/docs/guide.old/02-message-components/01-buttonkit.mdx
@@ -1,16 +1,25 @@
---
title: 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.
+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';
-ButtonKit extends Discord.js's [`ButtonBuilder`](https://discord.js.org/docs/packages/builders/1.9.0/ButtonBuilder:Class) to provide a simpler way to handle button interactions. It adds methods like `onClick()` to handle button clicks without manually setting up collectors.
+ButtonKit extends Discord.js's
+[`ButtonBuilder`](https://discord.js.org/docs/packages/builders/1.9.0/ButtonBuilder:Class)
+to provide a simpler way to handle button interactions. It adds
+methods like `onClick()` to handle button clicks without manually
+setting up collectors.
-:::info
-CommandKit provides JSX version of ButtonKit as an alternative syntax to the standard Discord.js builders. This is not required, but can be useful for those who prefer a more declarative style. See [``](../03-jsx-components/components-v1/02-button.mdx) for more details.
-:::
+:::info CommandKit provides JSX version of ButtonKit as an alternative
+syntax to the standard Discord.js builders. This is not required, but
+can be useful for those who prefer a more declarative style. See
+[``](../03-jsx-components/components-v1/02-button.mdx) for
+more details. :::
## Basic Usage
@@ -72,9 +81,8 @@ CommandKit provides JSX version of ButtonKit as an alternative syntax to the sta
-:::warning Important
-Always set a `customId` when using ButtonKit. This is required to track button interactions.
-:::
+:::warning Important Always set a `customId` when using ButtonKit.
+This is required to track button interactions. :::
## Configuration Options
@@ -133,7 +141,8 @@ button
## Manually Stopping the Collector
-Use `dispose()` to manually stop the collector. This will trigger the `onEnd` handler if one exists.
+Use `dispose()` to manually stop the collector. This will trigger the
+`onEnd` handler if one exists.
```js
// Stop listening for clicks
diff --git a/apps/website/docs/guide/02-message-components/02-modalkit.mdx b/apps/website/docs/guide.old/02-message-components/02-modalkit.mdx
similarity index 85%
rename from apps/website/docs/guide/02-message-components/02-modalkit.mdx
rename to apps/website/docs/guide.old/02-message-components/02-modalkit.mdx
index a6195a23..8d3ac062 100644
--- a/apps/website/docs/guide/02-message-components/02-modalkit.mdx
+++ b/apps/website/docs/guide.old/02-message-components/02-modalkit.mdx
@@ -1,16 +1,24 @@
---
title: ModalKit
-description: ModalKit is an enhanced version of the native Discord.js ModalBuilder, designed to simplify the process of creating and handling modal submissions in your Discord bot.
+description:
+ ModalKit is an enhanced version of the native Discord.js
+ ModalBuilder, designed to simplify the process of creating and
+ handling modal submissions in your Discord bot.
---
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
-ModalKit is an enhanced version of the native Discord.js [`ModalBuilder`](https://discord.js.org/docs/packages/builders/1.9.0/ModalBuilder:Class), designed to simplify the process of creating and handling modal submissions in your Discord bot.
+ModalKit is an enhanced version of the native Discord.js
+[`ModalBuilder`](https://discord.js.org/docs/packages/builders/1.9.0/ModalBuilder:Class),
+designed to simplify the process of creating and handling modal
+submissions in your Discord bot.
-:::info
-CommandKit provides JSX version of ModalKit as an alternative syntax to the standard Discord.js builders. This is not required, but can be useful for those who prefer a more declarative style. See [``](../03-jsx-components/components-v1/04-modal.mdx) for more details.
-:::
+:::info CommandKit provides JSX version of ModalKit as an alternative
+syntax to the standard Discord.js builders. This is not required, but
+can be useful for those who prefer a more declarative style. See
+[``](../03-jsx-components/components-v1/04-modal.mdx) for more
+details. :::
## Creating and handling modals
@@ -132,15 +140,15 @@ CommandKit provides JSX version of ModalKit as an alternative syntax to the stan
-:::warning
-ModalKit requires a custom ID. Always provide one when creating a modal using `setCustomId()`.
-:::
+:::warning ModalKit requires a custom ID. Always provide one when
+creating a modal using `setCustomId()`. :::
## ModalKit Options
### Using `onSubmit()`
-The `onSubmit()` method requires a handler function and accepts an optional options object:
+The `onSubmit()` method requires a handler function and accepts an
+optional options object:
```js
modal.onSubmit(
@@ -165,7 +173,8 @@ modal.onSubmit(
| `autoReset` | boolean | false | Whether to reset the timer on submission |
| `once` | boolean | true | Listen for only one submission |
-You can also use any valid [Discord.js InteractionCollectorOptions](https://discord.js.org/docs/packages/discord.js/14.16.3/InteractionCollectorOptions:Interface).
+You can also use any valid
+[Discord.js InteractionCollectorOptions](https://discord.js.org/docs/packages/discord.js/14.16.3/InteractionCollectorOptions:Interface).
### Handling Collector End
diff --git a/apps/website/docs/guide/02-message-components/03-selectmenukit.mdx b/apps/website/docs/guide.old/02-message-components/03-selectmenukit.mdx
similarity index 87%
rename from apps/website/docs/guide/02-message-components/03-selectmenukit.mdx
rename to apps/website/docs/guide.old/02-message-components/03-selectmenukit.mdx
index 0b8095ac..adeb00e2 100644
--- a/apps/website/docs/guide/02-message-components/03-selectmenukit.mdx
+++ b/apps/website/docs/guide.old/02-message-components/03-selectmenukit.mdx
@@ -1,24 +1,34 @@
---
title: SelectMenuKit
-description: SelectMenuKit is a set of enhanced versions of Discord.js select menu builders, designed to simplify the process of creating and handling select menu interactions in your Discord bot.
+description:
+ SelectMenuKit is a set of enhanced versions of Discord.js select
+ menu builders, designed to simplify the process of creating and
+ handling select menu interactions in your Discord bot.
---
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
-SelectMenuKit consists of several enhanced versions of Discord.js select menu builders:
+SelectMenuKit consists of several enhanced versions of Discord.js
+select menu builders:
- `StringSelectMenuKit` - For text-based selection
- `UserSelectMenuKit` - For selecting Discord users
- `RoleSelectMenuKit` - For selecting Discord roles
- `ChannelSelectMenuKit` - For selecting Discord channels
-- `MentionableSelectMenuKit` - For selecting mentionable Discord entities
+- `MentionableSelectMenuKit` - For selecting mentionable Discord
+ entities
-Each extends its respective Discord.js counterpart while adding methods like `onSelect()` to handle selections without manually setting up collectors.
+Each extends its respective Discord.js counterpart while adding
+methods like `onSelect()` to handle selections without manually
+setting up collectors.
-:::info
-CommandKit provides JSX version of SelectMenuKit as an alternative syntax to the standard Discord.js builders. This is not required, but can be useful for those who prefer a more declarative style. See [``](../03-jsx-components/components-v1/03-select-menu.mdx) for more details.
-:::
+:::info CommandKit provides JSX version of SelectMenuKit as an
+alternative syntax to the standard Discord.js builders. This is not
+required, but can be useful for those who prefer a more declarative
+style. See
+[``](../03-jsx-components/components-v1/03-select-menu.mdx)
+for more details. :::
## Basic Usage
@@ -170,9 +180,8 @@ CommandKit provides JSX version of SelectMenuKit as an alternative syntax to the
-:::warning Important
-Always set a `customId` when using any SelectMenuKit. This is required to track select menu interactions.
-:::
+:::warning Important Always set a `customId` when using any
+SelectMenuKit. This is required to track select menu interactions. :::
## Other Select Menu Types
@@ -259,7 +268,9 @@ Example with options:
```js
selectMenu.onSelect(
(interaction) => {
- interaction.reply(`You selected ${interaction.values.join(', ')}!`);
+ interaction.reply(
+ `You selected ${interaction.values.join(', ')}!`,
+ );
},
{
time: 120000, // 2 minutes
@@ -277,7 +288,9 @@ Use `filter()` to only process selections that meet specific criteria:
```js
selectMenu
- .filter((interaction) => interaction.user.id === '123456789012345678')
+ .filter(
+ (interaction) => interaction.user.id === '123456789012345678',
+ )
.onSelect((interaction) => {
interaction.reply('Only a specific user can use this menu!');
});
@@ -301,7 +314,8 @@ selectMenu
### Error Handling
-Use `onError()` to handle any errors that occur during the interaction:
+Use `onError()` to handle any errors that occur during the
+interaction:
```js
selectMenu
@@ -315,7 +329,8 @@ selectMenu
### Manually Stopping the Collector
-Use `dispose()` to manually stop the collector. This will trigger the `onEnd` handler if one exists.
+Use `dispose()` to manually stop the collector. This will trigger the
+`onEnd` handler if one exists.
```js
// Stop listening for selections
diff --git a/apps/website/docs/guide/03-jsx-components/01-using-jsx.mdx b/apps/website/docs/guide.old/03-jsx-components/01-using-jsx.mdx
similarity index 85%
rename from apps/website/docs/guide/03-jsx-components/01-using-jsx.mdx
rename to apps/website/docs/guide.old/03-jsx-components/01-using-jsx.mdx
index a20462d9..82a07689 100644
--- a/apps/website/docs/guide/03-jsx-components/01-using-jsx.mdx
+++ b/apps/website/docs/guide.old/03-jsx-components/01-using-jsx.mdx
@@ -5,15 +5,19 @@ description: Use JSX to declare Discord message components
# Using JSX
-CommandKit provides first-class JSX support for both TypeScript and JavaScript projects. This allows you to write Discord message components using a familiar and intuitive syntax.
+CommandKit provides first-class JSX support for both TypeScript and
+JavaScript projects. This allows you to write Discord message
+components using a familiar and intuitive syntax.
## Why Use JSX Instead of Builders?
-Using JSX for Discord message components offers several advantages over traditional builder patterns:
+Using JSX for Discord message components offers several advantages
+over traditional builder patterns:
### Cleaner and More Readable
-JSX makes your code significantly more readable by providing a declarative structure that resembles the final output:
+JSX makes your code significantly more readable by providing a
+declarative structure that resembles the final output:
```tsx
// JSX approach - clean and intuitive
@@ -73,7 +77,8 @@ const response = await interaction.reply({
withResponse: true,
});
-const confirmation = await response.resource.message.awaitMessageComponent();
+const confirmation =
+ await response.resource.message.awaitMessageComponent();
if (confirmation.customId === 'confirm') {
await confirmation.reply({
@@ -90,7 +95,8 @@ if (confirmation.customId === 'confirm') {
### Less Boilerplate
-JSX reduces the amount of code you need to write. No more chaining multiple method calls or creating separate builder instances:
+JSX reduces the amount of code you need to write. No more chaining
+multiple method calls or creating separate builder instances:
- No repetitive `.addComponents()` calls
- No need to explicitly create builder instances
@@ -104,7 +110,9 @@ JSX reduces the amount of code you need to write. No more chaining multiple meth
### Familiar Syntax
-If you've worked with React or other modern frameworks, JSX will feel immediately familiar, reducing the learning curve for building Discord interfaces.
+If you've worked with React or other modern frameworks, JSX will feel
+immediately familiar, reducing the learning curve for building Discord
+interfaces.
### Maintainability
@@ -116,7 +124,9 @@ JSX makes your code more maintainable:
## Setup
-CommandKit automatically configures JSX support in your project. If you are doing a manual setup, ensure you have the following in your `tsconfig.json` or `jsconfig.json`:
+CommandKit automatically configures JSX support in your project. If
+you are doing a manual setup, ensure you have the following in your
+`tsconfig.json` or `jsconfig.json`:
```json
{
@@ -167,12 +177,17 @@ const message = (
## TypeScript Support
-CommandKit provides full TypeScript support for JSX components. All components are properly typed, providing autocomplete and type checking:
+CommandKit provides full TypeScript support for JSX components. All
+components are properly typed, providing autocomplete and type
+checking:
```tsx
import { Button, OnButtonKitClick } from 'commandkit';
-const handleClick: OnButtonKitClick = async (interaction, context) => {
+const handleClick: OnButtonKitClick = async (
+ interaction,
+ context,
+) => {
await interaction.reply('Button clicked!');
context.dispose();
};
@@ -182,7 +197,9 @@ const button = ;
## Custom Components
-You can create your own JSX components by defining functions that return Discord.js objects. These components can be used anywhere in your code:
+You can create your own JSX components by defining functions that
+return Discord.js objects. These components can be used anywhere in
+your code:
```tsx
import { Client, ClientOptions } from 'discord.js';
@@ -196,7 +213,8 @@ const client = ;
## JSX Fragments
-CommandKit fully supports JSX fragments for grouping multiple elements without adding extra nodes:
+CommandKit fully supports JSX fragments for grouping multiple elements
+without adding extra nodes:
```tsx
const elements = (
@@ -216,7 +234,8 @@ The CommandKit compiler automatically configures:
- Component type definitions
- Build pipeline optimizations
-No additional configuration is needed to use JSX in your CommandKit project.
+No additional configuration is needed to use JSX in your CommandKit
+project.
## Best Practices
@@ -256,7 +275,9 @@ function CustomEmbed({
title: string;
description: string;
}) {
- return new EmbedBuilder().setTitle(title).setDescription(description);
+ return new EmbedBuilder()
+ .setTitle(title)
+ .setDescription(description);
}
const embed = ;
diff --git a/apps/website/docs/guide/03-jsx-components/components-v1/01-action-row.mdx b/apps/website/docs/guide.old/03-jsx-components/components-v1/01-action-row.mdx
similarity index 74%
rename from apps/website/docs/guide/03-jsx-components/components-v1/01-action-row.mdx
rename to apps/website/docs/guide.old/03-jsx-components/components-v1/01-action-row.mdx
index b886b63f..cf022cc1 100644
--- a/apps/website/docs/guide/03-jsx-components/components-v1/01-action-row.mdx
+++ b/apps/website/docs/guide.old/03-jsx-components/components-v1/01-action-row.mdx
@@ -1,11 +1,15 @@
---
title: Action Row
-description: Learn how to use the ActionRow component to group interactive components
+description:
+ Learn how to use the ActionRow component to group interactive
+ components
---
# Action Row
-The ActionRow component is a container that groups interactive components like buttons and select menus together in a single row. In Discord, you can have up to 5 components in a single row.
+The ActionRow component is a container that groups interactive
+components like buttons and select menus together in a single row. In
+Discord, you can have up to 5 components in a single row.
## Basic Usage
diff --git a/apps/website/docs/guide/03-jsx-components/components-v1/02-button.mdx b/apps/website/docs/guide.old/03-jsx-components/components-v1/02-button.mdx
similarity index 85%
rename from apps/website/docs/guide/03-jsx-components/components-v1/02-button.mdx
rename to apps/website/docs/guide.old/03-jsx-components/components-v1/02-button.mdx
index dd63d0dd..3173cde0 100644
--- a/apps/website/docs/guide/03-jsx-components/components-v1/02-button.mdx
+++ b/apps/website/docs/guide.old/03-jsx-components/components-v1/02-button.mdx
@@ -1,11 +1,14 @@
---
title: Button
-description: Learn how to use the Button component to create interactive buttons
+description:
+ Learn how to use the Button component to create interactive buttons
---
# Button
-The Button component allows you to create interactive buttons in your Discord messages. Buttons can have different styles, labels, and emojis.
+The Button component allows you to create interactive buttons in your
+Discord messages. Buttons can have different styles, labels, and
+emojis.
## Basic Usage
@@ -61,7 +64,10 @@ const button = ;
```tsx
import { Button, OnButtonKitClick } from 'commandkit';
-const handleClick: OnButtonKitClick = async (interaction, context) => {
+const handleClick: OnButtonKitClick = async (
+ interaction,
+ context,
+) => {
await interaction.reply('Button clicked!');
context.dispose();
};
diff --git a/apps/website/docs/guide/03-jsx-components/components-v1/03-select-menu.mdx b/apps/website/docs/guide.old/03-jsx-components/components-v1/03-select-menu.mdx
similarity index 84%
rename from apps/website/docs/guide/03-jsx-components/components-v1/03-select-menu.mdx
rename to apps/website/docs/guide.old/03-jsx-components/components-v1/03-select-menu.mdx
index 875efbb4..ad23afc3 100644
--- a/apps/website/docs/guide/03-jsx-components/components-v1/03-select-menu.mdx
+++ b/apps/website/docs/guide.old/03-jsx-components/components-v1/03-select-menu.mdx
@@ -1,15 +1,20 @@
---
title: Select Menu
-description: Learn how to use various Select Menu components to create interactive dropdowns
+description:
+ Learn how to use various Select Menu components to create
+ interactive dropdowns
---
# Select Menu
-CommandKit provides several types of select menus for different use cases. Each type allows users to select from a list of options in a dropdown menu.
+CommandKit provides several types of select menus for different use
+cases. Each type allows users to select from a list of options in a
+dropdown menu.
## String Select Menu
-The most common type of select menu that allows users to select from predefined string options.
+The most common type of select menu that allows users to select from
+predefined string options.
```tsx
import {
@@ -50,7 +55,10 @@ const select = (
Allows users to select a channel from the server.
```tsx
-import { ChannelSelectMenu, OnChannelSelectMenuKitSubmit } from 'commandkit';
+import {
+ ChannelSelectMenu,
+ OnChannelSelectMenuKitSubmit,
+} from 'commandkit';
const handleSelect: OnChannelSelectMenuKitSubmit = async (
interaction,
@@ -69,7 +77,10 @@ const select = ;
Allows users to select a role from the server.
```tsx
-import { RoleSelectMenu, OnRoleSelectMenuKitSubmit } from 'commandkit';
+import {
+ RoleSelectMenu,
+ OnRoleSelectMenuKitSubmit,
+} from 'commandkit';
const handleSelect: OnRoleSelectMenuKitSubmit = async (
interaction,
@@ -88,7 +99,10 @@ const select = ;
Allows users to select a user from the server.
```tsx
-import { UserSelectMenu, OnUserSelectMenuKitSubmit } from 'commandkit';
+import {
+ UserSelectMenu,
+ OnUserSelectMenuKitSubmit,
+} from 'commandkit';
const handleSelect: OnUserSelectMenuKitSubmit = async (
interaction,
@@ -129,7 +143,8 @@ const select = ;
- All select menus must be placed inside an ActionRow
- You can have only one select menu per row
- String select menus can have up to 25 options
-- Other select menus (channel, role, user, mentionable) don't need options as they are populated automatically
+- Other select menus (channel, role, user, mentionable) don't need
+ options as they are populated automatically
- Select menus can be disabled using the `disabled` prop
- Custom IDs are automatically generated if not provided
- For string select menus, each option must have a unique value
diff --git a/apps/website/docs/guide/03-jsx-components/components-v1/04-modal.mdx b/apps/website/docs/guide.old/03-jsx-components/components-v1/04-modal.mdx
similarity index 86%
rename from apps/website/docs/guide/03-jsx-components/components-v1/04-modal.mdx
rename to apps/website/docs/guide.old/03-jsx-components/components-v1/04-modal.mdx
index f8a20860..d61fd3bf 100644
--- a/apps/website/docs/guide/03-jsx-components/components-v1/04-modal.mdx
+++ b/apps/website/docs/guide.old/03-jsx-components/components-v1/04-modal.mdx
@@ -1,11 +1,14 @@
---
title: Modal
-description: Learn how to use the Modal component to create interactive forms
+description:
+ Learn how to use the Modal component to create interactive forms
---
# Modal
-The Modal component allows you to create interactive forms that appear as popups in Discord. Modals are useful for collecting user input in a structured way.
+The Modal component allows you to create interactive forms that appear
+as popups in Discord. Modals are useful for collecting user input in a
+structured way.
## Basic Usage
@@ -17,9 +20,13 @@ import {
OnModalKitSubmit,
} from 'commandkit';
-const handleSubmit: OnModalKitSubmit = async (interaction, context) => {
+const handleSubmit: OnModalKitSubmit = async (
+ interaction,
+ context,
+) => {
const name = interaction.fields.getTextInputValue('name');
- const description = interaction.fields.getTextInputValue('description');
+ const description =
+ interaction.fields.getTextInputValue('description');
await interaction.reply({
content: `Name: ${name}\nDescription: ${description}`,
diff --git a/apps/website/docs/guide/03-jsx-components/components-v2/01-text-display.mdx b/apps/website/docs/guide.old/03-jsx-components/components-v2/01-text-display.mdx
similarity index 81%
rename from apps/website/docs/guide/03-jsx-components/components-v2/01-text-display.mdx
rename to apps/website/docs/guide.old/03-jsx-components/components-v2/01-text-display.mdx
index 9de46890..85823031 100644
--- a/apps/website/docs/guide/03-jsx-components/components-v2/01-text-display.mdx
+++ b/apps/website/docs/guide.old/03-jsx-components/components-v2/01-text-display.mdx
@@ -1,9 +1,13 @@
---
title: Text Display
-description: Display text content in your Discord messages using the TextDisplay component
+description:
+ Display text content in your Discord messages using the TextDisplay
+ component
---
-The `TextDisplay` component is the most basic component in CommandKit that allows you to show text content in your Discord messages. It supports Discord's markdown syntax for rich text formatting.
+The `TextDisplay` component is the most basic component in CommandKit
+that allows you to show text content in your Discord messages. It
+supports Discord's markdown syntax for rich text formatting.
## Basic Usage
@@ -23,7 +27,8 @@ export const chatInput: ChatInputCommand = async (ctx) => {
## Text Formatting
-The `TextDisplay` component supports Discord's markdown syntax. Here are some examples:
+The `TextDisplay` component supports Discord's markdown syntax. Here
+are some examples:
```tsx title="src/app/commands/formatting.tsx"
import { type ChatInputCommand, TextDisplay } from 'commandkit';
@@ -60,5 +65,7 @@ export const chatInput: ChatInputCommand = async (ctx) => {
- Always wrap your text content in the `TextDisplay` component
- Use Discord's markdown syntax for rich text formatting
-- For multi-line text, use template literals (\`\`) for better readability
-- Remember to include the `MessageFlags.IsComponentsV2` flag in your reply
+- For multi-line text, use template literals (\`\`) for better
+ readability
+- Remember to include the `MessageFlags.IsComponentsV2` flag in your
+ reply
diff --git a/apps/website/docs/guide/03-jsx-components/components-v2/02-container.mdx b/apps/website/docs/guide.old/03-jsx-components/components-v2/02-container.mdx
similarity index 73%
rename from apps/website/docs/guide/03-jsx-components/components-v2/02-container.mdx
rename to apps/website/docs/guide.old/03-jsx-components/components-v2/02-container.mdx
index 82059f49..11b3f2df 100644
--- a/apps/website/docs/guide/03-jsx-components/components-v2/02-container.mdx
+++ b/apps/website/docs/guide.old/03-jsx-components/components-v2/02-container.mdx
@@ -1,14 +1,23 @@
---
title: Container
-description: Organize and style your message components using the Container component
+description:
+ Organize and style your message components using the Container
+ component
---
-The `Container` component is a fundamental building block in CommandKit that allows you to organize and style multiple components together. It provides a consistent layout and can be customized with an accent color.
+The `Container` component is a fundamental building block in
+CommandKit that allows you to organize and style multiple components
+together. It provides a consistent layout and can be customized with
+an accent color.
## Basic Usage
```tsx title="src/app/commands/container-example.tsx"
-import { type ChatInputCommand, Container, TextDisplay } from 'commandkit';
+import {
+ type ChatInputCommand,
+ Container,
+ TextDisplay,
+} from 'commandkit';
import { Colors, MessageFlags } from 'discord.js';
export const chatInput: ChatInputCommand = async (ctx) => {
@@ -28,10 +37,15 @@ export const chatInput: ChatInputCommand = async (ctx) => {
## Styling
-The `Container` component accepts an `accentColor` prop that can be used to customize its appearance:
+The `Container` component accepts an `accentColor` prop that can be
+used to customize its appearance:
```tsx title="src/app/commands/container-styling.tsx"
-import { type ChatInputCommand, Container, TextDisplay } from 'commandkit';
+import {
+ type ChatInputCommand,
+ Container,
+ TextDisplay,
+} from 'commandkit';
import { Colors, MessageFlags } from 'discord.js';
export const chatInput: ChatInputCommand = async (ctx) => {
@@ -60,7 +74,11 @@ import {
Section,
Separator,
} from 'commandkit';
-import { Colors, MessageFlags, SeparatorSpacingSize } from 'discord.js';
+import {
+ Colors,
+ MessageFlags,
+ SeparatorSpacingSize,
+} from 'discord.js';
export const chatInput: ChatInputCommand = async (ctx) => {
const container = (
@@ -85,10 +103,14 @@ export const chatInput: ChatInputCommand = async (ctx) => {
## Best Practices
-1. **Organization**: Use containers to group related components together
-2. **Visual Hierarchy**: Create clear visual sections using nested components
-3. **Consistency**: Maintain consistent accent colors across related containers
-4. **Readability**: Keep container content organized and well-structured
+1. **Organization**: Use containers to group related components
+ together
+2. **Visual Hierarchy**: Create clear visual sections using nested
+ components
+3. **Consistency**: Maintain consistent accent colors across related
+ containers
+4. **Readability**: Keep container content organized and
+ well-structured
## Available Props
diff --git a/apps/website/docs/guide/03-jsx-components/components-v2/03-media-gallery.mdx b/apps/website/docs/guide.old/03-jsx-components/components-v2/03-media-gallery.mdx
similarity index 83%
rename from apps/website/docs/guide/03-jsx-components/components-v2/03-media-gallery.mdx
rename to apps/website/docs/guide.old/03-jsx-components/components-v2/03-media-gallery.mdx
index abee39fe..e4b7bbc6 100644
--- a/apps/website/docs/guide/03-jsx-components/components-v2/03-media-gallery.mdx
+++ b/apps/website/docs/guide.old/03-jsx-components/components-v2/03-media-gallery.mdx
@@ -1,9 +1,13 @@
---
title: Media Gallery
-description: Display multiple images in a gallery format using the MediaGallery component
+description:
+ Display multiple images in a gallery format using the MediaGallery
+ component
---
-The `MediaGallery` component allows you to display multiple images in a grid layout, perfect for showcasing multiple images or avatars in a single message.
+The `MediaGallery` component allows you to display multiple images in
+a grid layout, perfect for showcasing multiple images or avatars in a
+single message.
## Basic Usage
@@ -28,7 +32,10 @@ export const chatInput: ChatInputCommand = async (ctx) => {
{images.map((url, index) => (
-
+
))}
>
@@ -66,7 +73,10 @@ export const chatInput: ChatInputCommand = async (ctx) => {
{mediaItems.map((url, index) => (
-
+
))}
>
@@ -83,7 +93,8 @@ export const chatInput: ChatInputCommand = async (ctx) => {
1. **Image URLs**: Always use valid, accessible image URLs
2. **Descriptions**: Provide meaningful descriptions for each image
-3. **Performance**: Limit the number of images to avoid message size limits
+3. **Performance**: Limit the number of images to avoid message size
+ limits
4. **Organization**: Group related images together in the gallery
## Available Props
diff --git a/apps/website/docs/guide/03-jsx-components/components-v2/04-section-separator.mdx b/apps/website/docs/guide.old/03-jsx-components/components-v2/04-section-separator.mdx
similarity index 87%
rename from apps/website/docs/guide/03-jsx-components/components-v2/04-section-separator.mdx
rename to apps/website/docs/guide.old/03-jsx-components/components-v2/04-section-separator.mdx
index 4e1afecb..f64abbdd 100644
--- a/apps/website/docs/guide/03-jsx-components/components-v2/04-section-separator.mdx
+++ b/apps/website/docs/guide.old/03-jsx-components/components-v2/04-section-separator.mdx
@@ -3,7 +3,8 @@ title: Section and Separator
description: Organize your content with Sections and Separators
---
-The `Section` and `Separator` components help you organize and structure your message content in a clear and visually appealing way.
+The `Section` and `Separator` components help you organize and
+structure your message content in a clear and visually appealing way.
## Section Component
@@ -38,7 +39,8 @@ export const chatInput: ChatInputCommand = async (ctx) => {
## Separator Component
-The `Separator` component creates visual spacing between sections. It supports different spacing sizes and can include a divider line.
+The `Separator` component creates visual spacing between sections. It
+supports different spacing sizes and can include a divider line.
```tsx title="src/app/commands/separator-example.tsx"
import {
@@ -48,7 +50,11 @@ import {
Separator,
TextDisplay,
} from 'commandkit';
-import { Colors, MessageFlags, SeparatorSpacingSize } from 'discord.js';
+import {
+ Colors,
+ MessageFlags,
+ SeparatorSpacingSize,
+} from 'discord.js';
export const chatInput: ChatInputCommand = async (ctx) => {
const container = (
@@ -80,7 +86,8 @@ export const chatInput: ChatInputCommand = async (ctx) => {
## Combining Sections and Separators
-Here's an example of how to combine both components for a well-structured message:
+Here's an example of how to combine both components for a
+well-structured message:
```tsx title="src/app/commands/combined-example.tsx"
import {
@@ -147,5 +154,7 @@ export const chatInput: ChatInputCommand = async (ctx) => {
1. **Organization**: Use sections to group related content
2. **Visual Hierarchy**: Use separators to create clear visual breaks
-3. **Consistency**: Maintain consistent spacing throughout your message
-4. **Readability**: Don't overuse separators - use them to enhance readability
+3. **Consistency**: Maintain consistent spacing throughout your
+ message
+4. **Readability**: Don't overuse separators - use them to enhance
+ readability
diff --git a/apps/website/docs/guide/03-jsx-components/components-v2/05-file.mdx b/apps/website/docs/guide.old/03-jsx-components/components-v2/05-file.mdx
similarity index 90%
rename from apps/website/docs/guide/03-jsx-components/components-v2/05-file.mdx
rename to apps/website/docs/guide.old/03-jsx-components/components-v2/05-file.mdx
index 0ccb60fd..c06a8567 100644
--- a/apps/website/docs/guide/03-jsx-components/components-v2/05-file.mdx
+++ b/apps/website/docs/guide.old/03-jsx-components/components-v2/05-file.mdx
@@ -1,9 +1,12 @@
---
title: File
-description: Display file attachments in your messages using the File component
+description:
+ Display file attachments in your messages using the File component
---
-The `File` component allows you to display file attachments in your Discord messages. This is useful for sharing text files, markdown files, or other file types.
+The `File` component allows you to display file attachments in your
+Discord messages. This is useful for sharing text files, markdown
+files, or other file types.
## Basic Usage
@@ -91,13 +94,15 @@ export const chatInput: ChatInputCommand = async (ctx) => {
2. **File Types**: Ensure the file type matches the content
3. **File Size**: Keep files small to avoid Discord's size limits
4. **Organization**: Group related files together
-5. **Context**: Provide context about the files using TextDisplay components
+5. **Context**: Provide context about the files using TextDisplay
+ components
## Available Props
### File
-- `url`: The URL of the file attachment (must start with "attachment://")
+- `url`: The URL of the file attachment (must start with
+ "attachment://")
## Common Use Cases
diff --git a/apps/website/docs/guide/04-caching/01-caching-in-commandkit.mdx b/apps/website/docs/guide.old/04-caching/01-caching-in-commandkit.mdx
similarity index 85%
rename from apps/website/docs/guide/04-caching/01-caching-in-commandkit.mdx
rename to apps/website/docs/guide.old/04-caching/01-caching-in-commandkit.mdx
index ee727bdc..11b7ebd0 100644
--- a/apps/website/docs/guide/04-caching/01-caching-in-commandkit.mdx
+++ b/apps/website/docs/guide.old/04-caching/01-caching-in-commandkit.mdx
@@ -1,11 +1,15 @@
---
title: Caching in CommandKit
-description: Learn how to use caching to improve your bot's performance and reduce API calls.
+description:
+ Learn how to use caching to improve your bot's performance and
+ reduce API calls.
---
# Caching in CommandKit
-CommandKit provides a powerful caching system that helps you optimize your bot's performance by storing frequently accessed data in memory. This guide will show you how to use caching effectively in your bot.
+CommandKit provides a powerful caching system that helps you optimize
+your bot's performance by storing frequently accessed data in memory.
+This guide will show you how to use caching effectively in your bot.
## Installation
@@ -29,7 +33,8 @@ export default defineConfig({
### Optional: Custom Cache Provider
-You can set up a custom cache provider (like Redis) for distributed caching:
+You can set up a custom cache provider (like Redis) for distributed
+caching:
```ts
import { setCacheProvider } from '@commandkit/cache';
@@ -65,20 +70,27 @@ async function fetchUserData(userId: string) {
}
```
-When you call this function multiple times with the same `userId`, it will only perform the database query once and return the cached result for subsequent calls.
+When you call this function multiple times with the same `userId`, it
+will only perform the database query once and return the cached result
+for subsequent calls.
## How Caching Works
CommandKit's caching system works by:
-1. **Generating a Cache Key**: Each cached function call generates a unique key based on:
+1. **Generating a Cache Key**: Each cached function call generates a
+ unique key based on:
+
- The function's identity
- The arguments passed to the function
- A build ID for stability
2. **Storing Results**: When a function is called:
- - If the result isn't cached, the function executes and stores its result
- - If the result is cached, it's returned immediately without executing the function
+
+ - If the result isn't cached, the function executes and stores its
+ result
+ - If the result is cached, it's returned immediately without
+ executing the function
3. **Automatic Cleanup**: The cache system automatically:
- Removes stale entries
@@ -87,7 +99,8 @@ CommandKit's caching system works by:
## Cache Configuration
-You can configure caching behavior using the `cacheTag` and `cacheLife` functions:
+You can configure caching behavior using the `cacheTag` and
+`cacheLife` functions:
```ts
async function fetchUserData(userId: string) {
@@ -105,16 +118,19 @@ async function fetchUserData(userId: string) {
## Best Practices
1. **Choose What to Cache**:
+
- Cache expensive operations (API calls, database queries)
- Cache data that doesn't change frequently
- Don't cache sensitive or frequently changing data
2. **Set Appropriate TTL**:
+
- Use shorter TTL for frequently changing data
- Use longer TTL for static data
- Consider your data's update frequency
3. **Use Tags for Revalidation**:
+
- Add meaningful tags to your cache entries
- Group related cache entries with the same tag
- Use tags for targeted cache invalidation
@@ -176,7 +192,8 @@ async function calculateUserStats(userId: string) {
### Custom Cache Provider
-You can create a custom cache provider by extending the `CacheProvider` class:
+You can create a custom cache provider by extending the
+`CacheProvider` class:
```ts
import { CacheProvider } from '@commandkit/cache';
@@ -212,6 +229,9 @@ await cleanup(24 * 60 * 60 * 1000);
## Next Steps
-- Learn about [cacheTag](./02-cacheTag-function.mdx) for tag-based cache management
-- Discover [cacheLife](./03-cacheLife-function.mdx) for controlling cache duration
-- Master [revalidateTag](./05-revalidateTag-function.mdx) for cache invalidation
+- Learn about [cacheTag](./02-cacheTag-function.mdx) for tag-based
+ cache management
+- Discover [cacheLife](./03-cacheLife-function.mdx) for controlling
+ cache duration
+- Master [revalidateTag](./05-revalidateTag-function.mdx) for cache
+ invalidation
diff --git a/apps/website/docs/guide/04-caching/02-cacheTag-function.mdx b/apps/website/docs/guide.old/04-caching/02-cacheTag-function.mdx
similarity index 92%
rename from apps/website/docs/guide/04-caching/02-cacheTag-function.mdx
rename to apps/website/docs/guide.old/04-caching/02-cacheTag-function.mdx
index 190c2bc7..d3570fca 100644
--- a/apps/website/docs/guide/04-caching/02-cacheTag-function.mdx
+++ b/apps/website/docs/guide.old/04-caching/02-cacheTag-function.mdx
@@ -1,11 +1,15 @@
---
title: cacheTag Function
-description: Learn how to use cacheTag to manage cache entries with tags for targeted revalidation.
+description:
+ Learn how to use cacheTag to manage cache entries with tags for
+ targeted revalidation.
---
# cacheTag Function
-The `cacheTag` function allows you to add tags to your cache entries, making it easier to manage and invalidate related cache entries together.
+The `cacheTag` function allows you to add tags to your cache entries,
+making it easier to manage and invalidate related cache entries
+together.
## Basic Usage
@@ -79,11 +83,13 @@ await revalidateTag('user:123');
## Best Practices
1. **Use Consistent Naming**:
+
- Follow a consistent pattern (e.g., `type:id`)
- Make tags descriptive and meaningful
- Use hierarchical tags when appropriate
2. **Group Related Data**:
+
- Tag related cache entries with the same tag
- Use multiple tags for cross-category entries
- Consider data relationships when tagging
@@ -153,7 +159,11 @@ async function fetchSystemData() {
cacheTag('system:stats');
cacheTag('system:logs');
- return await Promise.all([fetchConfig(), fetchStats(), fetchLogs()]);
+ return await Promise.all([
+ fetchConfig(),
+ fetchStats(),
+ fetchLogs(),
+ ]);
}
```
diff --git a/apps/website/docs/guide/04-caching/03-cacheLife-function.mdx b/apps/website/docs/guide.old/04-caching/03-cacheLife-function.mdx
similarity index 93%
rename from apps/website/docs/guide/04-caching/03-cacheLife-function.mdx
rename to apps/website/docs/guide.old/04-caching/03-cacheLife-function.mdx
index b3088481..363c3166 100644
--- a/apps/website/docs/guide/04-caching/03-cacheLife-function.mdx
+++ b/apps/website/docs/guide.old/04-caching/03-cacheLife-function.mdx
@@ -1,11 +1,15 @@
---
title: cacheLife Function
-description: Learn how to use cacheLife to control how long your cache entries live.
+description:
+ Learn how to use cacheLife to control how long your cache entries
+ live.
---
# cacheLife Function
-The `cacheLife` function allows you to control how long a cache entry should live before it's automatically invalidated. This is useful for managing cache freshness and memory usage.
+The `cacheLife` function allows you to control how long a cache entry
+should live before it's automatically invalidated. This is useful for
+managing cache freshness and memory usage.
## Basic Usage
@@ -99,11 +103,13 @@ async function fetchData(type: 'frequent' | 'rare') {
## Best Practices
1. **Choose Appropriate Duration**:
+
- Use shorter TTL for frequently changing data
- Use longer TTL for static content
- Consider your data update patterns
2. **Balance Freshness and Performance**:
+
- Don't cache too long if data changes often
- Don't cache too short if data is static
- Consider your application's needs
diff --git a/apps/website/docs/guide/04-caching/05-revalidateTag-function.mdx b/apps/website/docs/guide.old/04-caching/05-revalidateTag-function.mdx
similarity index 88%
rename from apps/website/docs/guide/04-caching/05-revalidateTag-function.mdx
rename to apps/website/docs/guide.old/04-caching/05-revalidateTag-function.mdx
index 1f6cd6b4..f2e86b3f 100644
--- a/apps/website/docs/guide/04-caching/05-revalidateTag-function.mdx
+++ b/apps/website/docs/guide.old/04-caching/05-revalidateTag-function.mdx
@@ -1,11 +1,15 @@
---
title: revalidateTag Function
-description: Learn how to use revalidateTag to invalidate cache entries by their tags.
+description:
+ Learn how to use revalidateTag to invalidate cache entries by their
+ tags.
---
# revalidateTag Function
-The `revalidateTag` function allows you to invalidate cache entries that are tagged with a specific tag. This is useful for refreshing data when it changes or when you need to force a cache update.
+The `revalidateTag` function allows you to invalidate cache entries
+that are tagged with a specific tag. This is useful for refreshing
+data when it changes or when you need to force a cache update.
## Basic Usage
@@ -41,7 +45,10 @@ async function updateUserProfile(userId: string, data: UserData) {
### Bulk Updates
```ts
-async function updateGuildSettings(guildId: string, settings: GuildSettings) {
+async function updateGuildSettings(
+ guildId: string,
+ settings: GuildSettings,
+) {
// Update multiple settings
await db.guilds.update(guildId, settings);
@@ -66,11 +73,13 @@ setInterval(
## Best Practices
1. **Strategic Invalidation**:
+
- Invalidate only what needs to be refreshed
- Use specific tags for targeted invalidation
- Consider the impact on performance
2. **Timing**:
+
- Invalidate after data changes
- Consider using scheduled revalidation
- Balance freshness and performance
diff --git a/apps/website/docs/guide/04-caching/06-cleanup-function.mdx b/apps/website/docs/guide.old/04-caching/06-cleanup-function.mdx
similarity index 90%
rename from apps/website/docs/guide/04-caching/06-cleanup-function.mdx
rename to apps/website/docs/guide.old/04-caching/06-cleanup-function.mdx
index 5cf46b14..8f0ac257 100644
--- a/apps/website/docs/guide/04-caching/06-cleanup-function.mdx
+++ b/apps/website/docs/guide.old/04-caching/06-cleanup-function.mdx
@@ -1,11 +1,16 @@
---
title: cleanup Function
-description: Learn how to use the cleanup function to manage stale cache entries and optimize memory usage.
+description:
+ Learn how to use the cleanup function to manage stale cache entries
+ and optimize memory usage.
---
# cleanup Function
-The `cleanup` function allows you to manually remove stale cache entries that haven't been accessed for a specified period. This is useful for managing memory usage and ensuring your cache doesn't grow indefinitely.
+The `cleanup` function allows you to manually remove stale cache
+entries that haven't been accessed for a specified period. This is
+useful for managing memory usage and ensuring your cache doesn't grow
+indefinitely.
## Basic Usage
@@ -21,7 +26,8 @@ await cleanup(24 * 60 * 60 * 1000);
When you call `cleanup`:
1. It scans all cache entries
-2. Identifies entries that haven't been accessed within the specified time period
+2. Identifies entries that haven't been accessed within the specified
+ time period
3. Removes those entries from the cache
4. Frees up memory for new cache entries
@@ -66,11 +72,13 @@ async function performConservativeCleanup() {
## Best Practices
1. **Choose Appropriate Time Period**:
+
- Consider your application's memory constraints
- Balance between memory usage and cache effectiveness
- Monitor cache hit rates
2. **Schedule Regular Cleanup**:
+
- Set up automated cleanup tasks
- Choose appropriate intervals
- Handle cleanup errors gracefully
diff --git a/apps/website/docs/guide/05-message-commands/01-message-commands.mdx b/apps/website/docs/guide.old/05-message-commands/01-message-commands.mdx
similarity index 68%
rename from apps/website/docs/guide/05-message-commands/01-message-commands.mdx
rename to apps/website/docs/guide.old/05-message-commands/01-message-commands.mdx
index ec40b2fa..aea4be2a 100644
--- a/apps/website/docs/guide/05-message-commands/01-message-commands.mdx
+++ b/apps/website/docs/guide.old/05-message-commands/01-message-commands.mdx
@@ -1,17 +1,28 @@
---
title: Message Commands
-description: Learn how to create and manage message commands in your Discord bot using CommandKit.
+description:
+ Learn how to create and manage message commands in your Discord bot
+ using CommandKit.
---
-Message commands are the primitive way to interact with discord bots. They are typically prefixed with a special character (like `!`, `?`, or `>`), and are used to trigger specific actions or responses from the bot. This guide will help you understand how to implement and manage message commands in your Discord bot using CommandKit.
+Message commands are the primitive way to interact with discord bots.
+They are typically prefixed with a special character (like `!`, `?`,
+or `>`), and are used to trigger specific actions or responses from
+the bot. This guide will help you understand how to implement and
+manage message commands in your Discord bot using CommandKit.
-:::info
-CommandKit supports message commands out of the box, allowing you to create and manage them easily. You can also customize the prefix used for message commands, making it flexible for different use cases. See [Resolve Message Commands Prefix](./02-resolve-message-commands-prefix.mdx) for more details.
-:::
+:::info CommandKit supports message commands out of the box, allowing
+you to create and manage them easily. You can also customize the
+prefix used for message commands, making it flexible for different use
+cases. See
+[Resolve Message Commands Prefix](./02-resolve-message-commands-prefix.mdx)
+for more details. :::
## Creating Message Commands
-To create a message command, you can just export a function named `message` from your command file. This function will be called whenever a message command is triggered.
+To create a message command, you can just export a function named
+`message` from your command file. This function will be called
+whenever a message command is triggered.
```ts
import { MessageCommand } from 'commandkit';
@@ -23,9 +34,14 @@ export const message: MessageCommand = async (ctx) => {
};
```
-It's that simple! The `ctx` object contains the message context, which includes the message itself, the user who sent it, and other useful information.
+It's that simple! The `ctx` object contains the message context, which
+includes the message itself, the user who sent it, and other useful
+information.
-CommandKit also offers interaction-like message options parser, allowing you to define options for your message commands. This is useful for creating more complex commands that require additional parameters.
+CommandKit also offers interaction-like message options parser,
+allowing you to define options for your message commands. This is
+useful for creating more complex commands that require additional
+parameters.
```ts
import { MessageCommand } from 'commandkit';
@@ -39,7 +55,8 @@ export const message: MessageCommand = async (ctx) => {
};
```
-Alternatively, you can also use the arguments approach to get the options:
+Alternatively, you can also use the arguments approach to get the
+options:
```ts
import { MessageCommand } from 'commandkit';
@@ -60,7 +77,9 @@ export const message: MessageCommand = async (ctx) => {
### Hybrid command example
-The following example demonstrates how to create a command that works both as a slash command and a message command. This is useful for commands that you want to be accessible in both contexts.
+The following example demonstrates how to create a command that works
+both as a slash command and a message command. This is useful for
+commands that you want to be accessible in both contexts.
```ts
import { MessageCommand, ChatInputCommand } from 'commandkit';
diff --git a/apps/website/docs/guide/05-message-commands/02-resolve-message-commands-prefix.mdx b/apps/website/docs/guide.old/05-message-commands/02-resolve-message-commands-prefix.mdx
similarity index 62%
rename from apps/website/docs/guide/05-message-commands/02-resolve-message-commands-prefix.mdx
rename to apps/website/docs/guide.old/05-message-commands/02-resolve-message-commands-prefix.mdx
index 90916625..81175a4f 100644
--- a/apps/website/docs/guide/05-message-commands/02-resolve-message-commands-prefix.mdx
+++ b/apps/website/docs/guide.old/05-message-commands/02-resolve-message-commands-prefix.mdx
@@ -1,13 +1,18 @@
---
title: Custom command prefixes
-description: Learn how to implement and manage custom prefixes for message commands in your Discord bot
+description:
+ Learn how to implement and manage custom prefixes for message
+ commands in your Discord bot
---
-Message commands in Discord bots traditionally use prefixes to distinguish commands from regular messages. CommandKit allows you to customize these prefixes to suit your bot's needs.
+Message commands in Discord bots traditionally use prefixes to
+distinguish commands from regular messages. CommandKit allows you to
+customize these prefixes to suit your bot's needs.
## Basic Usage
-The simplest way to set a custom prefix is using the `setPrefixResolver` method:
+The simplest way to set a custom prefix is using the
+`setPrefixResolver` method:
```ts title="src/app.ts"
import { commandkit } from 'commandkit';
@@ -17,10 +22,11 @@ commandkit.setPrefixResolver(async (message) => {
});
```
-This sets a global prefix of `?` for all message commands. Your bot will now respond to commands like `?help` or `?ping`.
+This sets a global prefix of `?` for all message commands. Your bot
+will now respond to commands like `?help` or `?ping`.
-:::tip
-You can return an array of strings to support multiple prefixes simultaneously:
+:::tip You can return an array of strings to support multiple prefixes
+simultaneously:
```ts
return ['?', '!', '>'];
@@ -32,7 +38,8 @@ return ['?', '!', '>'];
### Guild-Specific Prefixes
-For more complex applications, you might want different prefixes for different servers. Here's how to implement that:
+For more complex applications, you might want different prefixes for
+different servers. Here's how to implement that:
```ts title="src/app.ts"
import { onApplicationBootstrap, cacheTag } from 'commandkit';
@@ -59,16 +66,20 @@ onApplicationBootstrap((commandkit) => {
});
```
-:::info
-The `"use cache"` directive is a custom directive that tells CommandKit to cache the result of the function automatically. See the [Caching in CommandKit](../04-caching/01-caching-in-commandkit.mdx) guide for more details.
-:::
+:::info The `"use cache"` directive is a custom directive that tells
+CommandKit to cache the result of the function automatically. See the
+[Caching in CommandKit](../04-caching/01-caching-in-commandkit.mdx)
+guide for more details. :::
### Best Practices
-1. **Caching**: Use CommandKit's built-in caching system to reduce database queries
+1. **Caching**: Use CommandKit's built-in caching system to reduce
+ database queries
2. **Fallback**: Always provide a default prefix as fallback
-3. **Validation**: Consider adding prefix validation to prevent conflicts
-4. **Performance**: Cache frequently used prefixes to improve response time
+3. **Validation**: Consider adding prefix validation to prevent
+ conflicts
+4. **Performance**: Cache frequently used prefixes to improve response
+ time
### Example with Validation
@@ -105,10 +116,10 @@ commandkit.setPrefixResolver((message) => {
});
```
-:::note
-The prefix resolver is called for every message, so ensure your implementation is efficient and properly cached when possible.
+:::note The prefix resolver is called for every message, so ensure
+your implementation is efficient and properly cached when possible.
:::
-:::warning
-Avoid using prefixes that might conflict with other bots or common Discord features. For example, `/` is reserved for slash commands.
-:::
+:::warning Avoid using prefixes that might conflict with other bots or
+common Discord features. For example, `/` is reserved for slash
+commands. :::
diff --git a/apps/website/docs/guide.old/06-plugins/01-commandkit-plugins.mdx b/apps/website/docs/guide.old/06-plugins/01-commandkit-plugins.mdx
new file mode 100644
index 00000000..658337e4
--- /dev/null
+++ b/apps/website/docs/guide.old/06-plugins/01-commandkit-plugins.mdx
@@ -0,0 +1,42 @@
+---
+title: CommandKit Plugins
+description:
+ CommandKit plugins are a powerful way to extend the functionality of
+ your CommandKit application. They allow you to add new features,
+ modify existing ones, and customize the behavior of your
+ application.
+---
+
+CommandKit offers a powerful plugin api that allows you to extend the
+functionality of your CommandKit application. This means you can
+create custom plugins that add new features, modify existing ones, or
+even change the way CommandKit works. CommandKit offers two types of
+plugins:
+
+1. **Compiler Plugins**: These plugins are used at compile time to
+ modify the way CommandKit compiles your application. They are used
+ to add new features or modify existing ones. For example, you can
+ use a compiler plugin to transform the source code itself (e.g.
+ CommandKit's `use cache` or `use macro` directives).
+2. **Runtime Plugins**: These plugins are used at runtime to modify
+ the way CommandKit works. They are used to add new features or
+ modify existing ones. For example, you can use a runtime plugin to
+ add new commands or even custom handlers.
+
+## Official Plugins
+
+CommandKit provides a set of official plugins that you can use to
+extend the functionality of your application.
+
+- **[@commandkit/redis](./official-plugins/01-redis.mdx)**: This
+ plugin registers a redis cache provider for CommandKit. It allows
+ you to store cached data in redis.
+- **[@commandkit/i18n](./official-plugins/02-i18n.mdx)**: This plugin
+ enables internationalization (i18n) for CommandKit using
+ [i18next](https://www.i18next.com/). It allows you to translate your
+ application into different languages.
+- **[@commandkit/legacy](./official-plugins/03-legacy.mdx)**: This
+ plugin enables legacy commands and events handler in CommandKit.
+ This is useful if you are migrating from an older version of
+ CommandKit and want to incrementally upgrade your application
+ without making major changes.
diff --git a/apps/website/docs/guide/06-plugins/02-creating-a-compiler-plugin.mdx b/apps/website/docs/guide.old/06-plugins/02-creating-a-compiler-plugin.mdx
similarity index 60%
rename from apps/website/docs/guide/06-plugins/02-creating-a-compiler-plugin.mdx
rename to apps/website/docs/guide.old/06-plugins/02-creating-a-compiler-plugin.mdx
index 069beabc..641344a9 100644
--- a/apps/website/docs/guide/06-plugins/02-creating-a-compiler-plugin.mdx
+++ b/apps/website/docs/guide.old/06-plugins/02-creating-a-compiler-plugin.mdx
@@ -1,15 +1,25 @@
---
title: Creating a Compiler Plugin
-description: This guide will walk you through the process of creating a compiler plugin for CommandKit. Compiler plugins are used at compile time to modify the way CommandKit compiles your application. They are used to add new features or modify existing ones.
+description:
+ This guide will walk you through the process of creating a compiler
+ plugin for CommandKit. Compiler plugins are used at compile time to
+ modify the way CommandKit compiles your application. They are used
+ to add new features or modify existing ones.
---
-CommandKit Compiler Plugins are a powerful way to extend the functionality of your CommandKit application by modifying the way CommandKit compiles your application. They are used to add new features or modify existing ones. For example, you can use a compiler plugin to transform the source code itself (e.g. CommandKit's `use cache` or `use macro` directives).
+CommandKit Compiler Plugins are a powerful way to extend the
+functionality of your CommandKit application by modifying the way
+CommandKit compiles your application. They are used to add new
+features or modify existing ones. For example, you can use a compiler
+plugin to transform the source code itself (e.g. CommandKit's
+`use cache` or `use macro` directives).
## Creating a Compiler Plugin
### Using rolldown plugins
-You can also use rolldown plugins as compiler plugins. This is useful if you want to use rolldown plugins to transform your source code.
+You can also use rolldown plugins as compiler plugins. This is useful
+if you want to use rolldown plugins to transform your source code.
```ts title="commandkit.config.ts"
import { defineConfig } from 'commandkit';
@@ -22,7 +32,9 @@ export default defineConfig({
### Using custom compiler plugins
-You can also create custom compiler plugins by extending the `CompilerPlugin` class. This is useful if you want to create a custom plugin that modifies the way CommandKit compiles your application.
+You can also create custom compiler plugins by extending the
+`CompilerPlugin` class. This is useful if you want to create a custom
+plugin that modifies the way CommandKit compiles your application.
```ts title="commandkit.config.ts"
import { defineConfig } from 'commandkit';
@@ -35,7 +47,11 @@ export default defineConfig({
## Creating a Compiler Plugin
-To create a compiler plugin, you need to create a new class that extends the `CompilerPlugin` class. This class should implement the `transform` method, which is called by CommandKit to transform the source code. The `transform` method should return the transformed source code.
+To create a compiler plugin, you need to create a new class that
+extends the `CompilerPlugin` class. This class should implement the
+`transform` method, which is called by CommandKit to transform the
+source code. The `transform` method should return the transformed
+source code.
```ts title="my-plugin.ts"
import {
@@ -79,7 +95,11 @@ export class MyPlugin extends CompilerPlugin {
## Extending commandkit templates
-CommandKit templates are a way to autogenerate files and other contents through `commandkit create` command. CommandKit by default comes with `command` and `event` templates. Compiler plugins can register custom templates to be used by the `commandkit create` command.
+CommandKit templates are a way to autogenerate files and other
+contents through `commandkit create` command. CommandKit by default
+comes with `command` and `event` templates. Compiler plugins can
+register custom templates to be used by the `commandkit create`
+command.
```ts title="my-plugin.ts"
import { CompilerPlugin, CompilerPluginRuntime } from 'commandkit';
@@ -109,10 +129,9 @@ export class MyPlugin extends CompilerPlugin {
}
```
-:::warning
-The `registerTemplate` method must be called inside the `activate` method, and the `unregisterTemplate` method must be called inside the `deactivate` method.
-:::
+:::warning The `registerTemplate` method must be called inside the
+`activate` method, and the `unregisterTemplate` method must be called
+inside the `deactivate` method. :::
-:::info
-Plugins may override the default templates by registering their own templates with the same name.
-:::
+:::info Plugins may override the default templates by registering
+their own templates with the same name. :::
diff --git a/apps/website/docs/guide/06-plugins/03-creating-a-runtime-plugin.mdx b/apps/website/docs/guide.old/06-plugins/03-creating-a-runtime-plugin.mdx
similarity index 82%
rename from apps/website/docs/guide/06-plugins/03-creating-a-runtime-plugin.mdx
rename to apps/website/docs/guide.old/06-plugins/03-creating-a-runtime-plugin.mdx
index 678026c8..bfa36ad6 100644
--- a/apps/website/docs/guide/06-plugins/03-creating-a-runtime-plugin.mdx
+++ b/apps/website/docs/guide.old/06-plugins/03-creating-a-runtime-plugin.mdx
@@ -1,13 +1,24 @@
---
title: Creating a Runtime Plugin
-description: Creating a runtime plugin for CommandKit. This is useful if you want to create a custom plugin that modifies the way CommandKit runs your application.
+description:
+ Creating a runtime plugin for CommandKit. This is useful if you want
+ to create a custom plugin that modifies the way CommandKit runs your
+ application.
---
-Runtime plugins are used to modify the way CommandKit runs your application. They are executed at runtime and can be used to modify the behavior of CommandKit. These plugins can register new commands, modify existing ones, or even change the way CommandKit works. For example, you can use a runtime plugin to add new commands or even custom handlers.
+Runtime plugins are used to modify the way CommandKit runs your
+application. They are executed at runtime and can be used to modify
+the behavior of CommandKit. These plugins can register new commands,
+modify existing ones, or even change the way CommandKit works. For
+example, you can use a runtime plugin to add new commands or even
+custom handlers.
## Getting Started with Runtime Plugins
-To create a runtime plugin, you need to create a class that extends the `RuntimePlugin` class from CommandKit. This class provides various hooks that you can use to modify the behavior of CommandKit at different stages of execution.
+To create a runtime plugin, you need to create a class that extends
+the `RuntimePlugin` class from CommandKit. This class provides various
+hooks that you can use to modify the behavior of CommandKit at
+different stages of execution.
Here's a basic example of a runtime plugin:
@@ -17,13 +28,16 @@ import type { CommandKitPluginRuntime } from 'commandkit';
export class MyRuntimePlugin extends RuntimePlugin {
// Override any hooks you want to use
- async onAfterClientLogin(ctx: CommandKitPluginRuntime): Promise {
+ async onAfterClientLogin(
+ ctx: CommandKitPluginRuntime,
+ ): Promise {
console.log('Bot has logged in!');
}
}
```
-To use this plugin in your application, you can add it to your CommandKit instance:
+To use this plugin in your application, you can add it to your
+CommandKit instance:
```typescript
import { CommandKit } from 'commandkit';
@@ -37,11 +51,14 @@ const commandkit = new CommandKit({
## Lifecycle Hooks
-Runtime plugins provide hooks at different stages of your application's lifecycle. These hooks allow you to perform actions before or after key events in your bot's execution.
+Runtime plugins provide hooks at different stages of your
+application's lifecycle. These hooks allow you to perform actions
+before or after key events in your bot's execution.
### Initialization Hooks
-These hooks are called during the initialization of your CommandKit application.
+These hooks are called during the initialization of your CommandKit
+application.
#### `onBeforeCommandsLoad(ctx)`
@@ -49,9 +66,11 @@ Called before commands are loaded into CommandKit.
**Parameters:**
-- `ctx`: The runtime context, providing access to CommandKit internals like the client, command context, etc.
+- `ctx`: The runtime context, providing access to CommandKit internals
+ like the client, command context, etc.
-**Example use case:** Initialize resources needed by commands, or modify command loading behavior.
+**Example use case:** Initialize resources needed by commands, or
+modify command loading behavior.
```typescript
async onBeforeCommandsLoad(ctx: CommandKitPluginRuntime): Promise {
@@ -68,7 +87,8 @@ Called after all commands have been loaded into CommandKit.
- `ctx`: The runtime context.
-**Example use case:** Log the loaded commands, perform validation, or add metadata to loaded commands.
+**Example use case:** Log the loaded commands, perform validation, or
+add metadata to loaded commands.
```typescript
async onAfterCommandsLoad(ctx: CommandKitPluginRuntime): Promise {
@@ -102,7 +122,8 @@ Called after all event handlers have been loaded.
- `ctx`: The runtime context.
-**Example use case:** Log the loaded events, add custom event processing.
+**Example use case:** Log the loaded events, add custom event
+processing.
```typescript
async onAfterEventsLoad(ctx: CommandKitPluginRuntime): Promise {
@@ -119,7 +140,8 @@ Called right before the Discord client attempts to log in.
- `ctx`: The runtime context.
-**Example use case:** Perform last-minute setup before connecting to Discord.
+**Example use case:** Perform last-minute setup before connecting to
+Discord.
```typescript
async onBeforeClientLogin(ctx: CommandKitPluginRuntime): Promise {
@@ -136,7 +158,8 @@ Called after the Discord client has successfully logged in.
- `ctx`: The runtime context.
-**Example use case:** Start additional services once the bot is online, log connection details.
+**Example use case:** Start additional services once the bot is
+online, log connection details.
```typescript
async onAfterClientLogin(ctx: CommandKitPluginRuntime): Promise {
@@ -147,7 +170,8 @@ async onAfterClientLogin(ctx: CommandKitPluginRuntime): Promise {
### Router Initialization Hooks
-These hooks are called when the command and event routers are initialized.
+These hooks are called when the command and event routers are
+initialized.
#### `onCommandsRouterInit(ctx)`
@@ -196,7 +220,8 @@ Called before an event is emitted.
- `ctx`: The runtime context.
- `event`: The event object being emitted.
-**Example use case:** Modify event data before it's emitted, or cancel the event.
+**Example use case:** Modify event data before it's emitted, or cancel
+the event.
```typescript
async willEmitEvent(
@@ -214,11 +239,14 @@ Now the event listener will receive the modified event data.
## Command Registration Hooks
-These hooks are called during the command registration process, allowing you to modify commands before they're registered with Discord.
+These hooks are called during the command registration process,
+allowing you to modify commands before they're registered with
+Discord.
#### `prepareCommand(ctx, commands)`
-Called before a command is loaded for registration, allowing you to modify the command data.
+Called before a command is loaded for registration, allowing you to
+modify the command data.
**Parameters:**
@@ -227,7 +255,8 @@ Called before a command is loaded for registration, allowing you to modify the c
**Returns:** The modified command or `null` to use the original.
-**Example use case:** Add default options to commands, modify command properties.
+**Example use case:** Add default options to commands, modify command
+properties.
```typescript
async prepareCommand(
@@ -244,14 +273,16 @@ async prepareCommand(
#### `onBeforeRegisterCommands(ctx, event)`
-Called before command registration process starts, allowing you to cancel or modify the registration.
+Called before command registration process starts, allowing you to
+cancel or modify the registration.
**Parameters:**
- `ctx`: The runtime context.
- `event`: The command registration event data.
-**Example use case:** Log commands being registered, filter commands, or handle registration manually.
+**Example use case:** Log commands being registered, filter commands,
+or handle registration manually.
```typescript
async onBeforeRegisterCommands(
@@ -286,7 +317,8 @@ async onBeforeRegisterGlobalCommands(
#### `onBeforePrepareGuildCommandsRegistration(ctx, event)`
-Called before guild commands are prepared for registration, before guilds are resolved.
+Called before guild commands are prepared for registration, before
+guilds are resolved.
**Parameters:**
@@ -307,7 +339,8 @@ async onBeforePrepareGuildCommandsRegistration(
#### `onBeforeRegisterGuildCommands(ctx, event)`
-Called before guild commands are registered, after guilds have been resolved.
+Called before guild commands are registered, after guilds have been
+resolved.
**Parameters:**
@@ -339,7 +372,8 @@ Called before an interaction is handled.
- `ctx`: The runtime context.
- `interaction`: The Discord.js Interaction object.
-**Example use case:** Log interactions, perform checks before handling, add analytics.
+**Example use case:** Log interactions, perform checks before
+handling, add analytics.
```typescript
async onBeforeInteraction(
@@ -376,7 +410,8 @@ async onBeforeMessageCommand(
#### `executeCommand(ctx, env, source, command, execute)`
-Called before a command is executed, allowing you to handle command execution yourself.
+Called before a command is executed, allowing you to handle command
+execution yourself.
**Parameters:**
@@ -386,9 +421,11 @@ Called before a command is executed, allowing you to handle command execution yo
- `command`: The prepared command execution.
- `execute`: The function that would normally execute the command.
-**Returns:** `true` if you handled the command execution, `false` to let CommandKit handle it.
+**Returns:** `true` if you handled the command execution, `false` to
+let CommandKit handle it.
-**Example use case:** Custom command execution, permission checking, rate limiting.
+**Example use case:** Custom command execution, permission checking,
+rate limiting.
```typescript
async executeCommand(
@@ -420,7 +457,8 @@ Called after a command and all its deferred functions are executed.
- `ctx`: The runtime context.
- `env`: The CommandKitEnvironment containing command context.
-**Example use case:** Logging, analytics, cleanup after command execution.
+**Example use case:** Logging, analytics, cleanup after command
+execution.
```typescript
async onAfterCommand(
@@ -438,14 +476,16 @@ These hooks are used during development.
#### `performHMR(ctx, event)`
-Called when a Hot Module Replacement event is received in development mode.
+Called when a Hot Module Replacement event is received in development
+mode.
**Parameters:**
- `ctx`: The runtime context.
- `event`: The HMR event data.
-**Example use case:** Handle reloading of specific resources during development.
+**Example use case:** Handle reloading of specific resources during
+development.
```typescript
async performHMR(
@@ -459,19 +499,25 @@ async performHMR(
## Creating a Complete Runtime Plugin
-Now that we've covered all available hooks, let's put together a more complete example of a runtime plugin that implements several hooks:
+Now that we've covered all available hooks, let's put together a more
+complete example of a runtime plugin that implements several hooks:
```typescript
import { RuntimePlugin } from 'commandkit';
import type { CommandKitPluginRuntime } from 'commandkit';
import { CommandKitEnvironment } from 'commandkit';
import { Interaction, Message } from 'discord.js';
-import { CommandBuilderLike, PreparedAppCommandExecution } from 'commandkit';
+import {
+ CommandBuilderLike,
+ PreparedAppCommandExecution,
+} from 'commandkit';
export class LoggingPlugin extends RuntimePlugin {
private commandUsage = new Map();
- async onAfterClientLogin(ctx: CommandKitPluginRuntime): Promise {
+ async onAfterClientLogin(
+ ctx: CommandKitPluginRuntime,
+ ): Promise {
console.log(`Bot logged in as ${ctx.client.user?.tag}!`);
console.log(`Serving ${ctx.client.guilds.cache.size} guilds`);
}
@@ -501,7 +547,9 @@ export class LoggingPlugin extends RuntimePlugin {
// Log execution time
const execTime = Date.now() - startTime;
- console.log(`Command "${command.name}" executed in ${execTime}ms`);
+ console.log(
+ `Command "${command.name}" executed in ${execTime}ms`,
+ );
// Track usage
this.commandUsage.set(
@@ -531,11 +579,20 @@ export class LoggingPlugin extends RuntimePlugin {
When creating runtime plugins, here are some best practices to follow:
1. **Keep it focused** - Each plugin should have a specific purpose.
-2. **Handle errors** - Wrap your hook implementations in try/catch blocks to prevent crashing your bot.
-3. **Be performance-conscious** - Some hooks may be called frequently, so keep your code efficient.
-4. **Respect the return values** - Make sure to return the appropriate values from hooks, especially for hooks like `executeCommand` that can change the execution flow.
-5. **Document your plugins** - If you're creating plugins for others to use, document how they should be configured and used.
+2. **Handle errors** - Wrap your hook implementations in try/catch
+ blocks to prevent crashing your bot.
+3. **Be performance-conscious** - Some hooks may be called frequently,
+ so keep your code efficient.
+4. **Respect the return values** - Make sure to return the appropriate
+ values from hooks, especially for hooks like `executeCommand` that
+ can change the execution flow.
+5. **Document your plugins** - If you're creating plugins for others
+ to use, document how they should be configured and used.
## Conclusion
-Runtime plugins provide powerful hooks into CommandKit's execution flow, allowing you to modify and extend its behavior in many ways. From adding logging and analytics to implementing custom command systems, runtime plugins give you full control over how your Discord bot functions.
+Runtime plugins provide powerful hooks into CommandKit's execution
+flow, allowing you to modify and extend its behavior in many ways.
+From adding logging and analytics to implementing custom command
+systems, runtime plugins give you full control over how your Discord
+bot functions.
diff --git a/apps/website/docs/guide/06-plugins/official-plugins/00-cache.mdx b/apps/website/docs/guide.old/06-plugins/official-plugins/00-cache.mdx
similarity index 65%
rename from apps/website/docs/guide/06-plugins/official-plugins/00-cache.mdx
rename to apps/website/docs/guide.old/06-plugins/official-plugins/00-cache.mdx
index d7a31b99..c95b3418 100644
--- a/apps/website/docs/guide/06-plugins/official-plugins/00-cache.mdx
+++ b/apps/website/docs/guide.old/06-plugins/official-plugins/00-cache.mdx
@@ -1,6 +1,9 @@
---
title: Cache Plugin
-description: The cache plugin for CommandKit enables caching APIs in your project. It provides a simple and efficient way to cache data, reducing the need for repeated database queries or API calls.
+description:
+ The cache plugin for CommandKit enables caching APIs in your
+ project. It provides a simple and efficient way to cache data,
+ reducing the need for repeated database queries or API calls.
---
import Tabs from '@theme/Tabs';
@@ -8,7 +11,9 @@ import TabItem from '@theme/TabItem';
# Cache Plugin
-The cache plugin for CommandKit enables caching APIs in your project. It provides a simple and efficient way to cache data, reducing the need for repeated database queries or API calls.
+The cache plugin for CommandKit enables caching APIs in your project.
+It provides a simple and efficient way to cache data, reducing the
+need for repeated database queries or API calls.
## Installation
@@ -38,7 +43,8 @@ pnpm add @commandkit/cache
## Usage
-This plugin automatically registers the in-memory cache provider with your CommandKit instance.
+This plugin automatically registers the in-memory cache provider with
+your CommandKit instance.
```js
import { defineConfig } from 'commandkit';
@@ -49,7 +55,8 @@ export default defineConfig({
});
```
-Then, you can add the `"use cache"` directive to the function you want to cache. For example:
+Then, you can add the `"use cache"` directive to the function you want
+to cache. For example:
```ts
async function getCachedData() {
@@ -62,4 +69,6 @@ async function getCachedData() {
}
```
-To understand more about the caching system, check out the [Caching in CommandKit](../../04-caching/01-caching-in-commandkit.mdx) guide.
+To understand more about the caching system, check out the
+[Caching in CommandKit](../../04-caching/01-caching-in-commandkit.mdx)
+guide.
diff --git a/apps/website/docs/guide/06-plugins/official-plugins/01-redis.mdx b/apps/website/docs/guide.old/06-plugins/official-plugins/01-redis.mdx
similarity index 77%
rename from apps/website/docs/guide/06-plugins/official-plugins/01-redis.mdx
rename to apps/website/docs/guide.old/06-plugins/official-plugins/01-redis.mdx
index dc9003a9..0525624a 100644
--- a/apps/website/docs/guide/06-plugins/official-plugins/01-redis.mdx
+++ b/apps/website/docs/guide.old/06-plugins/official-plugins/01-redis.mdx
@@ -1,6 +1,8 @@
---
title: Redis Plugin
-description: The Redis plugin for CommandKit registers a redis cache provider for CommandKit. It allows you to store cached data in redis.
+description:
+ The Redis plugin for CommandKit registers a redis cache provider for
+ CommandKit. It allows you to store cached data in redis.
---
import Tabs from '@theme/Tabs';
@@ -8,7 +10,8 @@ import TabItem from '@theme/TabItem';
# Redis Plugin
-The Redis plugin provides a cache provider for CommandKit that allows you to store cached data in Redis.
+The Redis plugin provides a cache provider for CommandKit that allows
+you to store cached data in Redis.
## Installation
@@ -38,7 +41,8 @@ pnpm add @commandkit/redis
## Usage
-This plugin automatically registers the Redis cache provider with your CommandKit instance.
+This plugin automatically registers the Redis cache provider with your
+CommandKit instance.
```js
import { defineConfig } from 'commandkit';
@@ -49,7 +53,8 @@ export default defineConfig({
});
```
-With this plugin configured, your cache functions will automatically use Redis as the storage backend:
+With this plugin configured, your cache functions will automatically
+use Redis as the storage backend:
```ts
async function getCachedData() {
@@ -64,7 +69,8 @@ async function getCachedData() {
## Manual Configuration
-If you need more control over the Redis client configuration, you can set up the cache provider manually instead of using the plugin:
+If you need more control over the Redis client configuration, you can
+set up the cache provider manually instead of using the plugin:
```ts
import { setCacheProvider } from '@commandkit/cache';
@@ -84,4 +90,6 @@ const redisProvider = new RedisCacheProvider(redis);
setCacheProvider(redisProvider);
```
-This approach gives you full control over the Redis client configuration while still integrating with CommandKit's caching system.
+This approach gives you full control over the Redis client
+configuration while still integrating with CommandKit's caching
+system.
diff --git a/apps/website/docs/guide.old/06-plugins/official-plugins/02-i18n.mdx b/apps/website/docs/guide.old/06-plugins/official-plugins/02-i18n.mdx
new file mode 100644
index 00000000..639b8e22
--- /dev/null
+++ b/apps/website/docs/guide.old/06-plugins/official-plugins/02-i18n.mdx
@@ -0,0 +1,20 @@
+---
+title: i18n Plugin
+description:
+ The i18n plugin for CommandKit enables internationalization (i18n)
+ for CommandKit using i18next. It allows you to translate your
+ application into different languages.
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+# i18n Plugin
+
+The i18n plugin integrates i18next into CommandKit, allowing you to
+internationalize your Discord bot and translate your commands,
+responses, and other content into multiple languages.
+
+Check out
+[Localization with i18n](../../11-localization/01-introduction.mdx)
+for more details on how to use the i18n plugin.
diff --git a/apps/website/docs/guide/06-plugins/official-plugins/03-legacy.mdx b/apps/website/docs/guide.old/06-plugins/official-plugins/03-legacy.mdx
similarity index 53%
rename from apps/website/docs/guide/06-plugins/official-plugins/03-legacy.mdx
rename to apps/website/docs/guide.old/06-plugins/official-plugins/03-legacy.mdx
index 5fe6e31f..01aa0abe 100644
--- a/apps/website/docs/guide/06-plugins/official-plugins/03-legacy.mdx
+++ b/apps/website/docs/guide.old/06-plugins/official-plugins/03-legacy.mdx
@@ -1,6 +1,10 @@
---
title: Legacy Plugin
-description: The legacy plugin for CommandKit enables legacy commands and events handler in CommandKit. This is useful if you are migrating from an older version of CommandKit and want to incrementally upgrade your application without making major changes.
+description:
+ The legacy plugin for CommandKit enables legacy commands and events
+ handler in CommandKit. This is useful if you are migrating from an
+ older version of CommandKit and want to incrementally upgrade your
+ application without making major changes.
---
import Tabs from '@theme/Tabs';
@@ -8,7 +12,11 @@ import TabItem from '@theme/TabItem';
# Legacy Plugin
-The Legacy plugin for CommandKit enables support for legacy command and event handlers. This is particularly useful when migrating from an older version of CommandKit or another framework, as it allows you to incrementally upgrade your application without requiring immediate major changes to your existing code.
+The Legacy plugin for CommandKit enables support for legacy command
+and event handlers. This is particularly useful when migrating from an
+older version of CommandKit or another framework, as it allows you to
+incrementally upgrade your application without requiring immediate
+major changes to your existing code.
## Installation
@@ -53,20 +61,31 @@ export default defineConfig({
The Legacy plugin provides the following features:
-1. **Legacy Command Handler Support**: Enables support for older command handler formats, making migration easier.
+1. **Legacy Command Handler Support**: Enables support for older
+ command handler formats, making migration easier.
-2. **Legacy Event Handler Support**: Allows you to continue using your existing event handler structure.
+2. **Legacy Event Handler Support**: Allows you to continue using your
+ existing event handler structure.
-3. **Legacy Validation Support**: Maintains compatibility with existing validation methods.
+3. **Legacy Validation Support**: Maintains compatibility with
+ existing validation methods.
-4. **Hot Module Replacement (HMR)**: The plugin automatically enables HMR for legacy commands, events, and validations, improving the development experience by allowing you to see changes without restarting your bot.
+4. **Hot Module Replacement (HMR)**: The plugin automatically enables
+ HMR for legacy commands, events, and validations, improving the
+ development experience by allowing you to see changes without
+ restarting your bot.
## Migration Strategy
-The Legacy plugin is designed as a transitional tool. While it allows you to continue using your existing code structure, it's recommended to gradually migrate your commands, events, and validators to the new CommandKit format over time.
+The Legacy plugin is designed as a transitional tool. While it allows
+you to continue using your existing code structure, it's recommended
+to gradually migrate your commands, events, and validators to the new
+CommandKit format over time.
This approach enables you to:
-1. Upgrade your bot's framework immediately without breaking existing functionality
+1. Upgrade your bot's framework immediately without breaking existing
+ functionality
2. Incrementally refactor your code at your own pace
-3. Take advantage of new CommandKit features for new commands while maintaining backward compatibility
+3. Take advantage of new CommandKit features for new commands while
+ maintaining backward compatibility
diff --git a/apps/website/docs/guide.old/06-plugins/official-plugins/04-devtools.mdx b/apps/website/docs/guide.old/06-plugins/official-plugins/04-devtools.mdx
new file mode 100644
index 00000000..dfd1b452
--- /dev/null
+++ b/apps/website/docs/guide.old/06-plugins/official-plugins/04-devtools.mdx
@@ -0,0 +1,18 @@
+---
+title: DevTools Plugin
+description:
+ The DevTools plugin for CommandKit provides a set of tools and
+ utilities to enhance the development experience. It includes
+ features like command inspection, performance monitoring, and
+ debugging tools.
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+The DevTools plugin for CommandKit provides a set of tools and
+utilities to enhance the development experience. It includes features
+like command inspection, performance monitoring, and debugging tools.
+
+Check out the [DevTools Guide](../../12-devtools/01-introduction.mdx)
+for more details on how to use the DevTools plugin.
diff --git a/apps/website/docs/guide/06-plugins/official-plugins/05-analytics.mdx b/apps/website/docs/guide.old/06-plugins/official-plugins/05-analytics.mdx
similarity index 65%
rename from apps/website/docs/guide/06-plugins/official-plugins/05-analytics.mdx
rename to apps/website/docs/guide.old/06-plugins/official-plugins/05-analytics.mdx
index 8dec544d..5a9026b1 100644
--- a/apps/website/docs/guide/06-plugins/official-plugins/05-analytics.mdx
+++ b/apps/website/docs/guide.old/06-plugins/official-plugins/05-analytics.mdx
@@ -1,6 +1,9 @@
---
title: Analytics Plugin
-description: The analytics plugin for CommandKit enables analytics in your project. It provides a simple and efficient way to track events and metrics.
+description:
+ The analytics plugin for CommandKit enables analytics in your
+ project. It provides a simple and efficient way to track events and
+ metrics.
---
import Tabs from '@theme/Tabs';
@@ -8,7 +11,8 @@ import TabItem from '@theme/TabItem';
# Analytics Plugin
-The analytics plugin for CommandKit enables analytics in your project. It provides a simple and efficient way to track events and metrics.
+The analytics plugin for CommandKit enables analytics in your project.
+It provides a simple and efficient way to track events and metrics.
## Installation
@@ -38,7 +42,8 @@ pnpm add @commandkit/analytics
## Usage
-This plugin automatically registers the analytics provider with your CommandKit instance.
+This plugin automatically registers the analytics provider with your
+CommandKit instance.
```js
import { defineConfig } from 'commandkit';
@@ -49,4 +54,6 @@ export default defineConfig({
});
```
-To understand more about analytics, check out the [Analytics in CommandKit](../../10-analytics/01-analytics-in-commandkit.mdx) guide.
+To understand more about analytics, check out the
+[Analytics in CommandKit](../../10-analytics/01-analytics-in-commandkit.mdx)
+guide.
diff --git a/apps/website/docs/guide.old/06-plugins/official-plugins/06-ai.mdx b/apps/website/docs/guide.old/06-plugins/official-plugins/06-ai.mdx
new file mode 100644
index 00000000..ec58f2be
--- /dev/null
+++ b/apps/website/docs/guide.old/06-plugins/official-plugins/06-ai.mdx
@@ -0,0 +1,16 @@
+---
+title: AI Plugin
+description:
+ Learn how to use the AI plugin to power your bot's commands and
+ events.
+---
+
+## AI Plugin
+
+The AI plugin allows you to execute your bot commands using large
+language models. This enables you to use your bot's features entirely
+through natural language.
+
+Please refer to the
+[AI Powered Commands](../../13-ai-powered-commands/01-introduction.mdx)
+guide for more details.
diff --git a/apps/website/docs/guide/07-file-system-conventions/01-app.ts.mdx b/apps/website/docs/guide.old/07-file-system-conventions/01-app.ts.mdx
similarity index 72%
rename from apps/website/docs/guide/07-file-system-conventions/01-app.ts.mdx
rename to apps/website/docs/guide.old/07-file-system-conventions/01-app.ts.mdx
index 29878450..a99d73f4 100644
--- a/apps/website/docs/guide/07-file-system-conventions/01-app.ts.mdx
+++ b/apps/website/docs/guide.old/07-file-system-conventions/01-app.ts.mdx
@@ -3,7 +3,9 @@ title: app.ts
description: The app.ts file is the entry point for your application.
---
-The `src/app.ts` (or `app.js`) is a special file that acts as the entry point for your application. It is where you define and export your `Discord.js` client instance.
+The `src/app.ts` (or `app.js`) is a special file that acts as the
+entry point for your application. It is where you configure and export
+your `Discord.js` client instance.
```ts
import { Client } from 'discord.js';
diff --git a/apps/website/docs/guide/07-file-system-conventions/02-+middleware.ts.mdx b/apps/website/docs/guide.old/07-file-system-conventions/02-+middleware.ts.mdx
similarity index 51%
rename from apps/website/docs/guide/07-file-system-conventions/02-+middleware.ts.mdx
rename to apps/website/docs/guide.old/07-file-system-conventions/02-+middleware.ts.mdx
index 3f3e570a..98c7a999 100644
--- a/apps/website/docs/guide/07-file-system-conventions/02-+middleware.ts.mdx
+++ b/apps/website/docs/guide.old/07-file-system-conventions/02-+middleware.ts.mdx
@@ -1,9 +1,14 @@
---
title: '+middleware.ts'
-description: The +middleware.ts file is used to define middleware functions for your application.
+description:
+ The +middleware.ts file is used to define middleware functions for
+ your application.
---
-The `src/app/commands/+middleware.ts` (or `+middleware.js`) file is used to define middleware functions for your application. Middleware functions are functions that execute before and after the command execution.
+The `src/app/commands/+middleware.ts` (or `+middleware.js`) file is
+used to define middleware functions for your application. Middleware
+functions are functions that execute before and after the command
+execution.
```ts title="src/app/commands/+middleware.ts"
import { MiddlewareContext } from 'commandkit';
@@ -21,8 +26,17 @@ export function afterExecute(context: MiddlewareContext) {
There are 3 types of middleware files you can create:
-- `+middleware.ts` (or `+middleware.js`): This file is used to define middleware functions that will be executed for all sibling commands in the application.
-- `+middleware..ts` (or `+middleware..js`): This file is used to define middleware functions that will be executed for a specific command in the application. The `` part of the filename should match the name of the command file.
-- `+global-middleware.ts` (or `+global-middleware.js`): This file is used to define middleware functions that will be executed for all commands in the application, regardless of their location in the file system. This is useful for defining global middleware functions that should be applied to all commands.
+- `+middleware.ts` (or `+middleware.js`): This file is used to define
+ middleware functions that will be executed for all sibling commands
+ in the application.
+- `+middleware..ts` (or `+middleware..js`): This
+ file is used to define middleware functions that will be executed
+ for a specific command in the application. The `` part of
+ the filename should match the name of the command file.
+- `+global-middleware.ts` (or `+global-middleware.js`): This file is
+ used to define middleware functions that will be executed for all
+ commands in the application, regardless of their location in the
+ file system. This is useful for defining global middleware functions
+ that should be applied to all commands.
The code pattern is same for all middleware types.
diff --git a/apps/website/docs/guide.old/07-file-system-conventions/03-category-directory.mdx b/apps/website/docs/guide.old/07-file-system-conventions/03-category-directory.mdx
new file mode 100644
index 00000000..24543de5
--- /dev/null
+++ b/apps/website/docs/guide.old/07-file-system-conventions/03-category-directory.mdx
@@ -0,0 +1,17 @@
+---
+title: '(category) directory'
+description:
+ The (category) directory is used to group related commands together.
+---
+
+Category directories are special directories inside
+`src/app/commands`, with `()` as their name. The
+`(category)` directory is used to group related commands together.
+This is useful for organizing your commands into logical groups,
+making it easier to manage and maintain your code. This directory has
+no effect on the command execution, but it is a good practice to use
+it to keep your code organized. You can nest multiple `(category)`
+directories to create a hierarchy of commands. For example, you can
+have a `fun` directory with a `jokes` subdirectory, and inside the
+`jokes` subdirectory, you can have multiple command files related to
+jokes.
diff --git a/apps/website/docs/guide.old/07-file-system-conventions/04-commands-directory.mdx b/apps/website/docs/guide.old/07-file-system-conventions/04-commands-directory.mdx
new file mode 100644
index 00000000..e419f0a7
--- /dev/null
+++ b/apps/website/docs/guide.old/07-file-system-conventions/04-commands-directory.mdx
@@ -0,0 +1,19 @@
+---
+title: Commands Directory
+description:
+ The commands directory is used to define the commands for your
+ application.
+---
+
+The `src/app/commands` directory is where you define the commands for
+your application. Each command is defined in its own file, and the
+file name should match the command name. For example, if you have a
+command called `ping`, you should create a file called `ping.ts` (or
+`ping.js`) in the `src/app/commands` directory.
+
+:::info Plugins can register external commands, which may be located
+in a different directory. For more information, see the
+[Plugins](../06-plugins/03-creating-a-runtime-plugin.mdx) section. As
+an example, the
+[legacy plugin](../06-plugins/official-plugins/03-legacy.mdx) can
+register commands from custom directories. :::
diff --git a/apps/website/docs/guide.old/07-file-system-conventions/05-events-directory.mdx b/apps/website/docs/guide.old/07-file-system-conventions/05-events-directory.mdx
new file mode 100644
index 00000000..fa4a2f46
--- /dev/null
+++ b/apps/website/docs/guide.old/07-file-system-conventions/05-events-directory.mdx
@@ -0,0 +1,59 @@
+---
+title: Events Directory
+description:
+ The events directory is used to define the events for your
+ Discord.js application.
+---
+
+CommandKit provides a special directory called `events` inside the
+`src/app` directory. This directory is used to define the events for
+your Discord.js application. Each event is defined in its own
+directory, and the directory name should match the Discord.js event
+name. Inside each event directory, you can create multiple function
+files that will be executed when the event is triggered. For example,
+if you have an event called `messageCreate`, you should create a
+directory called `messageCreate` in the `src/app/events` directory,
+and place your event handler functions inside it.
+
+```
+.
+└── src/
+ └── app/
+ └── events/
+ ├── messageCreate/
+ │ ├── give-xp.ts
+ │ └── log.ts
+ └── ready/
+ ├── log.ts
+ └── initialize.ts
+```
+
+## Custom events
+
+CommandKit also supports using custom events in the `events`
+directory. Custom events must be defined in a namespaced directory,
+which are handled by the active plugins. For example, if you have a
+custom event called `guildMemberBoost`, you can create a file called
+`src/app/events/(custom)/guildMemberBoost/handler.ts` which will be
+handled by the appropriate active plugins.
+
+:::info Event namespace directories are not same as
+[category directories](./03-category-directory.mdx) as they serve a
+different purpose. :::
+
+You can use the custom events the same way as the built-in events. The
+only difference is that these events are namespaced and are handled by
+the plugins.
+
+## How plugins execute namespaced events
+
+The following example shows how you can trigger a custom event in your
+application. The `guildMemberBoost` event under the `custom` namespace
+is triggered when a user boosts the server.
+
+```ts
+commandkit.events.to('custom').emit('guildMemberBoost', {
+ member: member,
+ guild: guild,
+});
+```
diff --git a/apps/website/docs/guide/07-file-system-conventions/06-locales-directory.mdx b/apps/website/docs/guide.old/07-file-system-conventions/06-locales-directory.mdx
similarity index 59%
rename from apps/website/docs/guide/07-file-system-conventions/06-locales-directory.mdx
rename to apps/website/docs/guide.old/07-file-system-conventions/06-locales-directory.mdx
index d145f444..e76a57aa 100644
--- a/apps/website/docs/guide/07-file-system-conventions/06-locales-directory.mdx
+++ b/apps/website/docs/guide.old/07-file-system-conventions/06-locales-directory.mdx
@@ -1,11 +1,24 @@
---
title: Locales directory
-description: The locales directory is used to define the locales for your application.
+description:
+ The locales directory is used to define the locales for your
+ application.
---
-The `src/app/locales` directory is managed by the [@commandkit/i18n](../06-plugins/official-plugins/02-i18n.mdx) plugin. This directory can be used to store the translation files for your commands. The translation files should be named according to the locale they represent. For example, if you have a translation file for `ping` command in English, you should name it `src/app/locales/en-US/ping.json`.
+The `src/app/locales` directory is managed by the
+[@commandkit/i18n](../06-plugins/official-plugins/02-i18n.mdx) plugin.
+This directory can be used to store the translation files for your
+commands. The translation files should be named according to the
+locale they represent. For example, if you have a translation file for
+`ping` command in English, you should name it
+`src/app/locales/en-US/ping.json`.
-The translation file contains one special key called `$command`, which is used to define the localization for the command. CommandKit will automatically merge these localizations before registering the command. The `$command` object looks similar to the shape of a typical interaction command. The following example shows how to define the localization for the `ping` command in English:
+The translation file contains one special key called `$command`, which
+is used to define the localization for the command. CommandKit will
+automatically merge these localizations before registering the
+command. The `$command` object looks similar to the shape of a typical
+interaction command. The following example shows how to define the
+localization for the `ping` command in English:
```json title="src/app/locales/en-US/ping.json"
{
@@ -22,7 +35,8 @@ The translation file contains one special key called `$command`, which is used t
}
```
-The below is an example of a typical translation file for the `ping` command in English:
+The below is an example of a typical translation file for the `ping`
+command in English:
```json title="src/app/locales/en/ping.json"
{
@@ -40,7 +54,9 @@ The below is an example of a typical translation file for the `ping` command in
}
```
-Then you can use the `t` function to get the translation for the current command. The `t` function is returned by the `ctx.locale()` function.
+Then you can use the `t` function to get the translation for the
+current command. The `t` function is returned by the `ctx.locale()`
+function.
```ts title="src/app/commands/ping.ts"
import { CommandData } from 'commandkit';
@@ -60,7 +76,10 @@ export const command: CommandData = {
export const chatInput: ChatInputCommand = async (ctx) => {
const ping = Date.now() - ctx.interaction.createdTimestamp;
- const verbose = ctx.interaction.options.getBoolean('verbose', false);
+ const verbose = ctx.interaction.options.getBoolean(
+ 'verbose',
+ false,
+ );
// locale is auto inferred from the interaction if not specified
const { t } = ctx.locale();
diff --git a/apps/website/docs/guide/07-file-system-conventions/07-command-file.mdx b/apps/website/docs/guide.old/07-file-system-conventions/07-command-file.mdx
similarity index 70%
rename from apps/website/docs/guide/07-file-system-conventions/07-command-file.mdx
rename to apps/website/docs/guide.old/07-file-system-conventions/07-command-file.mdx
index 9deeabfc..1c3cebc5 100644
--- a/apps/website/docs/guide/07-file-system-conventions/07-command-file.mdx
+++ b/apps/website/docs/guide.old/07-file-system-conventions/07-command-file.mdx
@@ -1,18 +1,32 @@
---
title: Command File
-description: The command file is used to define the commands for your application.
+description:
+ The command file is used to define the commands for your
+ application.
---
-The `src/app/commands/.ts` (or `src/app/commands/.js`) file is used to define the commands for your application. Each command is defined in its own file, and the file name should match the command name. For example, if you have a command called `ping`, you should create a file called `ping.ts` (or `ping.js`) in the `src/app/commands` directory.
+The `src/app/commands/.ts` (or
+`src/app/commands/.js`) file is used to define the commands
+for your application. Each command is defined in its own file, and the
+file name should match the command name. For example, if you have a
+command called `ping`, you should create a file called `ping.ts` (or
+`ping.js`) in the `src/app/commands` directory.
The command file can export multiple handler functions, such as:
-- `chatInput`: This function is executed when the command is triggered by a chat input event (slash command).
-- `message`: This function is executed when the command is triggered by a message event (text command).
-- `userContextMenu`: This function is executed when the command is triggered by a user context menu event.
-- `messageContextMenu`: This function is executed when the command is triggered by a message context menu event.
+- `chatInput`: This function is executed when the command is triggered
+ by a chat input event (slash command).
+- `message`: This function is executed when the command is triggered
+ by a message event (text command).
+- `userContextMenu`: This function is executed when the command is
+ triggered by a user context menu event.
+- `messageContextMenu`: This function is executed when the command is
+ triggered by a message context menu event.
-When defining a command, if you export `userContextMenu` or `messageContextMenu`, CommandKit will automatically register the command as a context menu command without you having to explicitly define them as one.
+When defining a command, if you export `userContextMenu` or
+`messageContextMenu`, CommandKit will automatically register the
+command as a context menu command without you having to explicitly
+define them as one.
## Example command
@@ -37,7 +51,9 @@ export const command = {
],
};
-export const userContextMenu: UserContextMenuCommand = async (ctx) => {
+export const userContextMenu: UserContextMenuCommand = async (
+ ctx,
+) => {
const target = ctx.interaction.targetUser;
await ctx.interaction.reply({
@@ -53,7 +69,9 @@ export const userContextMenu: UserContextMenuCommand = async (ctx) => {
});
};
-export const messageContextMenu: MessageContextMenuCommand = async (ctx) => {
+export const messageContextMenu: MessageContextMenuCommand = async (
+ ctx,
+) => {
const target = ctx.interaction.targetMessage.author;
await ctx.interaction.reply({
diff --git a/apps/website/docs/guide.old/08-sharding/01-sharding.mdx b/apps/website/docs/guide.old/08-sharding/01-sharding.mdx
new file mode 100644
index 00000000..6c371bd0
--- /dev/null
+++ b/apps/website/docs/guide.old/08-sharding/01-sharding.mdx
@@ -0,0 +1,64 @@
+---
+title: Sharding your bot
+description: Learn how to shard your bot in CommandKit.
+---
+
+# Sharding your bot
+
+Sharding is a method of splitting your bot into multiple processes, or
+"shards". This is useful for large bots that have a lot of guilds, as
+it allows you to distribute the load across multiple processes.
+Discord actually requires sharding for bots in more than 2,500 guilds,
+so understanding how to implement it is crucial as your bot grows.
+
+Sharding is a built-in feature of discord.js, and CommandKit does not
+alter the way sharding works. In this guide, we will cover how to
+shard your bot using CommandKit. To learn more about sharding in
+discord.js, check out the
+[discord.js documentation](https://discordjs.guide/sharding).
+
+## When to use sharding
+
+You should consider implementing sharding in the following scenarios:
+
+- Your bot is in, or approaching, 2,500 guilds (required by Discord)
+- You're experiencing memory or performance issues with a single
+ process
+- You want to distribute your bot's workload across multiple
+ cores/machines
+- You're planning for future scaling of your bot
+
+## Creating a sharding manager file
+
+You can simply create a new file in your source directory named
+`sharding-manager.ts` and CommandKit will automatically detect it and
+use it as the sharding manager. This file will be responsible for
+creating the shards and managing them.
+
+```ts
+import { ShardingManager } from 'discord.js';
+import { join } from 'node:path';
+
+process.loadEnvFile('./.env');
+
+const manager = new ShardingManager(
+ join(import.meta.dirname, 'index.js'),
+ {
+ token: process.env.DISCORD_TOKEN,
+ totalShards: 2,
+ mode: 'worker',
+ },
+);
+
+manager.on('shardCreate', (shard) =>
+ console.log(`Launched shard ${shard.id}`),
+);
+
+await manager.spawn();
+```
+
+:::info If you're confused about `index.js` being used, this is an
+autogenerated entrypoint file that sets up the CommandKit environment.
+When running `commandkit start` or `commandkit dev`, CommandKit
+automatically detects the entrypoint file (either
+`sharding-manager.js` or `index.js`) and loads it. :::
diff --git a/apps/website/docs/guide/09-feature-flags/01-feature-flags.mdx b/apps/website/docs/guide.old/09-feature-flags/01-feature-flags.mdx
similarity index 52%
rename from apps/website/docs/guide/09-feature-flags/01-feature-flags.mdx
rename to apps/website/docs/guide.old/09-feature-flags/01-feature-flags.mdx
index c07cebcc..06706f14 100644
--- a/apps/website/docs/guide/09-feature-flags/01-feature-flags.mdx
+++ b/apps/website/docs/guide.old/09-feature-flags/01-feature-flags.mdx
@@ -1,25 +1,56 @@
---
title: Feature Flags
-description: Feature flags are a powerful tool for controlling the visibility of features in your application. They allow you to enable or disable features for specific users or groups, making it easier to test and roll out new functionality.
+description:
+ Feature flags are a powerful tool for controlling the visibility of
+ features in your application. They allow you to enable or disable
+ features for specific users or groups, making it easier to test and
+ roll out new functionality.
---
-Feature flags are a powerful tool for controlling the visibility of features in your application. They allow you to enable or disable features for specific users or groups, making it easier to test and roll out new functionality. This is particularly useful to perform A/B testing, gradual rollouts, and canary releases.
+Feature flags are a powerful tool for controlling the visibility of
+features in your application. They allow you to enable or disable
+features for specific users or groups, making it easier to test and
+roll out new functionality. This is particularly useful to perform A/B
+testing, gradual rollouts, and canary releases.
-CommandKit natively offers a feature flag system that allows you to define flags in your configuration and use them in your code. This system is designed to be simple and flexible, allowing you to easily manage feature flags across your application.
+CommandKit natively offers a feature flag system that allows you to
+define flags in your configuration and use them in your code. This
+system is designed to be simple and flexible, allowing you to easily
+manage feature flags across your application.
## Possible Use Cases
-- **Maintenance Mode**: You can use feature flags to enable or disable features in your application during maintenance. This allows you to perform maintenance tasks without affecting the user experience.
-- **A/B Testing**: You can use feature flags to test different versions of a feature with different users. For example, you can show one version of a button to 50% of users and another version to the other 50%.
-- **Gradual Rollouts**: You can use feature flags to gradually roll out a new feature to a small percentage of users before rolling it out to everyone. This allows you to monitor the feature's performance and make adjustments as needed.
-- **Canary Releases**: You can use feature flags to release a new feature to a small group of users before rolling it out to everyone. This allows you to test the feature in a production environment and catch any issues before they affect all users.
-- **Feature Toggles**: You can use feature flags to enable or disable features in your application without deploying new code. This allows you to quickly turn features on or off as needed.
-- **User Segmentation**: You can use feature flags to enable or disable features for specific users or groups. This allows you to customize the user experience based on user preferences or behavior.
-- **Testing and Debugging**: You can use feature flags to enable or disable features for testing and debugging purposes. This allows you to isolate issues and test specific features without affecting the entire application.
+- **Maintenance Mode**: You can use feature flags to enable or disable
+ features in your application during maintenance. This allows you to
+ perform maintenance tasks without affecting the user experience.
+- **A/B Testing**: You can use feature flags to test different
+ versions of a feature with different users. For example, you can
+ show one version of a button to 50% of users and another version to
+ the other 50%.
+- **Gradual Rollouts**: You can use feature flags to gradually roll
+ out a new feature to a small percentage of users before rolling it
+ out to everyone. This allows you to monitor the feature's
+ performance and make adjustments as needed.
+- **Canary Releases**: You can use feature flags to release a new
+ feature to a small group of users before rolling it out to everyone.
+ This allows you to test the feature in a production environment and
+ catch any issues before they affect all users.
+- **Feature Toggles**: You can use feature flags to enable or disable
+ features in your application without deploying new code. This allows
+ you to quickly turn features on or off as needed.
+- **User Segmentation**: You can use feature flags to enable or
+ disable features for specific users or groups. This allows you to
+ customize the user experience based on user preferences or behavior.
+- **Testing and Debugging**: You can use feature flags to enable or
+ disable features for testing and debugging purposes. This allows you
+ to isolate issues and test specific features without affecting the
+ entire application.
## Defining Feature Flags
-Feature flags may be defined in any file in your project (except `app.ts`). The recommended place to define them is in `src/flags` directory.
+Feature flags may be defined in any file in your project (except
+`app.ts`). The recommended place to define them is in `src/flags`
+directory.
```ts title="src/flags/embedColorFlag.ts"
import { flag } from 'commandkit';
@@ -30,7 +61,8 @@ export const embedColorFlag = flag({
description: 'Show red color instead of blue in embeds',
identify(ctx) {
const command = ctx.command;
- const id = command?.interaction?.user.id ?? command?.message?.author.id;
+ const id =
+ command?.interaction?.user.id ?? command?.message?.author.id;
return { id: id ?? null };
},
@@ -61,7 +93,9 @@ export const embedColorFlag = flag({
## Using Feature Flags
-Once you have defined a feature flag, you can use it in your code to control the visibility of features. You can use the function returned by the `flag` function to check if a feature flag is enabled or not.
+Once you have defined a feature flag, you can use it in your code to
+control the visibility of features. You can use the function returned
+by the `flag` function to check if a feature flag is enabled or not.
```ts title="src/commands/hello.ts"
import { ChatInputCommand, CommandData } from 'commandkit';
@@ -87,16 +121,25 @@ export const chatInput: ChatInputCommand = async (ctx) => {
};
```
-Now there's a 50% chance that the embed will be red instead of blue. You can use this feature flag to test the new color scheme with a subset of users before rolling it out to everyone.
+Now there's a 50% chance that the embed will be red instead of blue.
+You can use this feature flag to test the new color scheme with a
+subset of users before rolling it out to everyone.
## Collecting analytics
-You may want to see how many users are using a specific feature flag. You can do this by using the `analytics` plugin of CommandKit, which automatically collects the analytics data in the background whenever the feature flags are used. See [Analytics in CommandKit](../10-analytics/01-analytics-in-commandkit.mdx) guide for more information on how to set it up.
+You may want to see how many users are using a specific feature flag.
+You can do this by using the `analytics` plugin of CommandKit, which
+automatically collects the analytics data in the background whenever
+the feature flags are used. See
+[Analytics in CommandKit](../10-analytics/01-analytics-in-commandkit.mdx)
+guide for more information on how to set it up.
## Context Identification
-Sometimes you may want to identify specific users or groups for feature flags. For example, you may want to show a secret feature to server boosters only.
-In order to identify the context of the flag, you can add the `identify` method to the flag definition.
+Sometimes you may want to identify specific users or groups for
+feature flags. For example, you may want to show a secret feature to
+server boosters only. In order to identify the context of the flag,
+you can add the `identify` method to the flag definition.
```ts title="src/flags/embedColorFlag.ts"
import { flag } from 'commandkit';
@@ -118,8 +161,9 @@ export const embedColorFlag = flag({
} else if (ctx.event) {
// handle event specific context
if (ctx.event.event === 'guildMemberUpdate') {
- const [_oldMember, newMember] =
- ctx.event.argumentsAs('guildMemberUpdate');
+ const [_oldMember, newMember] = ctx.event.argumentsAs(
+ 'guildMemberUpdate',
+ );
member = newMember as GuildMember;
}
@@ -133,17 +177,22 @@ export const embedColorFlag = flag({
});
```
-:::info **Understanding Flag Context**
-Feature flags are **context-aware**, meaning they automatically detect and provide the necessary context (like user information, guild data, etc.) when used within CommandKit's execution environment.
+:::info **Understanding Flag Context** Feature flags are
+**context-aware**, meaning they automatically detect and provide the
+necessary context (like user information, guild data, etc.) when used
+within CommandKit's execution environment.
**Automatic Context Detection:**
-- ✅ **Commands** - Flags automatically access interaction/message data
+- ✅ **Commands** - Flags automatically access interaction/message
+ data
- ✅ **Events** - Flags automatically access event-specific data
-- ✅ **Middlewares** - Flags automatically access the current execution context
+- ✅ **Middlewares** - Flags automatically access the current
+ execution context
-**Manual Context Required:**
-If you need to use flags outside of these contexts (e.g., in utility functions, background jobs, or external API handlers), you must manually provide the identification data:
+**Manual Context Required:** If you need to use flags outside of these
+contexts (e.g., in utility functions, background jobs, or external API
+handlers), you must manually provide the identification data:
```ts
// Option 1: Pass values directly
@@ -155,5 +204,8 @@ const result = await myFlag.run({
});
```
-**Best Practice:** Keep flag usage within commands, events, and middlewares whenever possible. Manual context passing should only be used when absolutely necessary, as it bypasses the automatic context detection which causes flags to be less efficient and more error-prone.
-:::
+**Best Practice:** Keep flag usage within commands, events, and
+middlewares whenever possible. Manual context passing should only be
+used when absolutely necessary, as it bypasses the automatic context
+detection which causes flags to be less efficient and more
+error-prone. :::
diff --git a/apps/website/docs/guide/09-feature-flags/02-custom-providers.mdx b/apps/website/docs/guide.old/09-feature-flags/02-custom-providers.mdx
similarity index 81%
rename from apps/website/docs/guide/09-feature-flags/02-custom-providers.mdx
rename to apps/website/docs/guide.old/09-feature-flags/02-custom-providers.mdx
index 50c02573..34f23008 100644
--- a/apps/website/docs/guide/09-feature-flags/02-custom-providers.mdx
+++ b/apps/website/docs/guide.old/09-feature-flags/02-custom-providers.mdx
@@ -1,13 +1,22 @@
---
title: Custom Providers
-description: Learn how to integrate external feature flag management systems with CommandKit using custom providers.
+description:
+ Learn how to integrate external feature flag management systems with
+ CommandKit using custom providers.
---
-While CommandKit's built-in feature flag system is powerful for local flag management, you may want to integrate with external feature flag services like LaunchDarkly, Split, Unleash, or your own custom backend. CommandKit supports this through the provider pattern.
+While CommandKit's built-in feature flag system is powerful for local
+flag management, you may want to integrate with external feature flag
+services like LaunchDarkly, Split, Unleash, or your own custom
+backend. CommandKit supports this through the provider pattern.
## What are Flag Providers?
-Flag providers are adapters that allow CommandKit to fetch feature flag configurations from external systems. When a provider is configured, your local `decide` function receives additional context from the external system, giving you the flexibility to combine local logic with remote configuration.
+Flag providers are adapters that allow CommandKit to fetch feature
+flag configurations from external systems. When a provider is
+configured, your local `decide` function receives additional context
+from the external system, giving you the flexibility to combine local
+logic with remote configuration.
## Setting Up a Provider
@@ -28,7 +37,10 @@ export class LaunchDarklyProvider implements FlagProvider {
console.log('LaunchDarkly provider initialized');
}
- async getFlag(key: string, context?: any): Promise {
+ async getFlag(
+ key: string,
+ context?: any,
+ ): Promise {
try {
// Fetch flag from LaunchDarkly
// const variation = await this.client.variation(key, context, false);
@@ -45,7 +57,10 @@ export class LaunchDarklyProvider implements FlagProvider {
},
};
} catch (error) {
- console.error(`LaunchDarkly flag evaluation failed for ${key}:`, error);
+ console.error(
+ `LaunchDarkly flag evaluation failed for ${key}:`,
+ error,
+ );
return null;
}
}
@@ -68,7 +83,9 @@ export class LaunchDarklyProvider implements FlagProvider {
import { setFlagProvider } from 'commandkit';
import { LaunchDarklyProvider } from './providers/LaunchDarklyProvider';
-const provider = new LaunchDarklyProvider(process.env.LAUNCHDARKLY_API_KEY!);
+const provider = new LaunchDarklyProvider(
+ process.env.LAUNCHDARKLY_API_KEY!,
+);
// Initialize and set the global provider
await provider.initialize();
@@ -83,10 +100,12 @@ import murmurhash from 'murmurhash';
export const embedColorFlag = flag({
key: 'embed-color-flag',
- description: 'Show different embed colors based on external configuration',
+ description:
+ 'Show different embed colors based on external configuration',
identify(ctx) {
const command = ctx.command;
- const id = command?.interaction?.user.id ?? command?.message?.author.id;
+ const id =
+ command?.interaction?.user.id ?? command?.message?.author.id;
return { id: id ?? null };
},
decide({ entities, provider }) {
@@ -121,7 +140,8 @@ export const embedColorFlag = flag({
## Built-in JSON Provider
-CommandKit includes a simple JSON-based provider for basic external configuration:
+CommandKit includes a simple JSON-based provider for basic external
+configuration:
```ts title="src/providers/config.ts"
import { JsonFlagProvider, setFlagProvider } from 'commandkit';
@@ -192,7 +212,10 @@ import { FlagProvider, FlagConfiguration } from 'commandkit';
export class MultiProvider implements FlagProvider {
constructor(private providers: FlagProvider[]) {}
- async getFlag(key: string, context?: any): Promise {
+ async getFlag(
+ key: string,
+ context?: any,
+ ): Promise {
// Try providers in order, return first successful result
for (const provider of this.providers) {
try {
@@ -219,7 +242,8 @@ export class MultiProvider implements FlagProvider {
### 1. Always Provide Fallbacks
-Your `decide` function should handle cases where the provider is unavailable:
+Your `decide` function should handle cases where the provider is
+unavailable:
```ts
decide({ entities, provider }) {
@@ -236,11 +260,14 @@ decide({ entities, provider }) {
### 2. Handle Provider Errors Gracefully
-Providers may fail due to network issues, API limits, or configuration problems. CommandKit automatically catches these errors and continues with local evaluation.
+Providers may fail due to network issues, API limits, or configuration
+problems. CommandKit automatically catches these errors and continues
+with local evaluation.
### 3. Use Provider Configuration Wisely
-The provider's `config` object can contain any data structure your external system provides:
+The provider's `config` object can contain any data structure your
+external system provides:
```ts
decide({ entities, provider }) {
@@ -255,7 +282,8 @@ decide({ entities, provider }) {
### 4. Disable Analytics When Needed
-For high-frequency flags or privacy-sensitive scenarios, you can disable analytics:
+For high-frequency flags or privacy-sensitive scenarios, you can
+disable analytics:
```ts
export const highFrequencyFlag = flag({
@@ -273,7 +301,10 @@ export const highFrequencyFlag = flag({
```ts
interface FlagProvider {
initialize?(): Promise;
- getFlag(key: string, context?: any): Promise;
+ getFlag(
+ key: string,
+ context?: any,
+ ): Promise;
hasFlag(key: string): Promise;
destroy?(): Promise;
}
@@ -292,4 +323,7 @@ interface FlagConfiguration {
}
```
-The provider pattern makes CommandKit's feature flags extremely flexible while maintaining the simplicity of the core API. You can start with local flags and gradually migrate to external providers as your needs grow.
+The provider pattern makes CommandKit's feature flags extremely
+flexible while maintaining the simplicity of the core API. You can
+start with local flags and gradually migrate to external providers as
+your needs grow.
diff --git a/apps/website/docs/guide/10-analytics/01-analytics-in-commandkit.mdx b/apps/website/docs/guide.old/10-analytics/01-analytics-in-commandkit.mdx
similarity index 81%
rename from apps/website/docs/guide/10-analytics/01-analytics-in-commandkit.mdx
rename to apps/website/docs/guide.old/10-analytics/01-analytics-in-commandkit.mdx
index 17053234..3c9aa075 100644
--- a/apps/website/docs/guide/10-analytics/01-analytics-in-commandkit.mdx
+++ b/apps/website/docs/guide.old/10-analytics/01-analytics-in-commandkit.mdx
@@ -1,19 +1,26 @@
---
title: Analytics in CommandKit
-description: Learn how to use the analytics plugin in CommandKit to track events and metrics.
+description:
+ Learn how to use the analytics plugin in CommandKit to track events
+ and metrics.
---
# Analytics in CommandKit
-The analytics plugin for CommandKit enables analytics in your project. It provides a simple and efficient way to track events and metrics. CommandKit itself does not store any analytics data - it acts as a bridge between your application and your chosen analytics provider.
+The analytics plugin for CommandKit enables analytics in your project.
+It provides a simple and efficient way to track events and metrics.
+CommandKit itself does not store any analytics data - it acts as a
+bridge between your application and your chosen analytics provider.
## How it Works
-CommandKit's analytics system is designed to be provider-agnostic. This means:
+CommandKit's analytics system is designed to be provider-agnostic.
+This means:
1. CommandKit doesn't store any analytics data itself
2. All data is sent directly to your configured analytics provider
-3. You can easily switch between different providers or use multiple providers
+3. You can easily switch between different providers or use multiple
+ providers
4. The system is extensible, allowing you to create custom providers
## Automatic Tracking
@@ -83,18 +90,23 @@ analytics.setFilter((engine, event) => {
## Available Providers
-CommandKit comes with built-in support for popular analytics providers:
+CommandKit comes with built-in support for popular analytics
+providers:
- [PostHog](./02-posthog.mdx)
- [Umami](./03-umami.mdx)
## Creating Custom Providers
-You can create your own analytics provider by implementing the `AnalyticsProvider` interface. See our [Custom Providers](./04-custom-providers.mdx) guide for detailed instructions.
+You can create your own analytics provider by implementing the
+`AnalyticsProvider` interface. See our
+[Custom Providers](./04-custom-providers.mdx) guide for detailed
+instructions.
## Disabling Analytics
-You can disable analytics for specific requests (i.e. command or event scoped) using the `noAnalytics` function:
+You can disable analytics for specific requests (i.e. command or event
+scoped) using the `noAnalytics` function:
```ts
import { noAnalytics } from 'commandkit/analytics';
diff --git a/apps/website/docs/guide/10-analytics/02-posthog.mdx b/apps/website/docs/guide.old/10-analytics/02-posthog.mdx
similarity index 90%
rename from apps/website/docs/guide/10-analytics/02-posthog.mdx
rename to apps/website/docs/guide.old/10-analytics/02-posthog.mdx
index 481d0ef5..6741118d 100644
--- a/apps/website/docs/guide/10-analytics/02-posthog.mdx
+++ b/apps/website/docs/guide.old/10-analytics/02-posthog.mdx
@@ -1,11 +1,14 @@
---
title: PostHog Analytics
-description: Learn how to set up and use PostHog analytics with CommandKit.
+description:
+ Learn how to set up and use PostHog analytics with CommandKit.
---
# PostHog Analytics
-PostHog is an open-source product analytics platform that helps you understand user behavior. CommandKit provides a simple way to integrate PostHog into your Discord bot.
+PostHog is an open-source product analytics platform that helps you
+understand user behavior. CommandKit provides a simple way to
+integrate PostHog into your Discord bot.
## Setup
@@ -33,7 +36,8 @@ export default defineConfig({
## Usage
-Once configured, you can track anonymous events using the `track` function:
+Once configured, you can track anonymous events using the `track`
+function:
```ts
import { track } from 'commandkit/analytics';
@@ -54,7 +58,8 @@ await track({
## Identifying Anonymous Sessions
-You can identify anonymous sessions in PostHog using the `identify` function:
+You can identify anonymous sessions in PostHog using the `identify`
+function:
```ts
import { useAnalytics } from 'commandkit/analytics';
@@ -74,7 +79,8 @@ await analytics.identify({
## Automatic Events
-CommandKit automatically tracks the following anonymous events in PostHog:
+CommandKit automatically tracks the following anonymous events in
+PostHog:
- `command_execution`: Command execution events
- `cache_hit`: Cache hit events
diff --git a/apps/website/docs/guide/10-analytics/03-umami.mdx b/apps/website/docs/guide.old/10-analytics/03-umami.mdx
similarity index 87%
rename from apps/website/docs/guide/10-analytics/03-umami.mdx
rename to apps/website/docs/guide.old/10-analytics/03-umami.mdx
index 35de11b5..856fcda5 100644
--- a/apps/website/docs/guide/10-analytics/03-umami.mdx
+++ b/apps/website/docs/guide.old/10-analytics/03-umami.mdx
@@ -1,11 +1,14 @@
---
title: Umami Analytics
-description: Learn how to set up and use Umami analytics with CommandKit.
+description:
+ Learn how to set up and use Umami analytics with CommandKit.
---
# Umami Analytics
-Umami is a simple, fast, privacy-focused alternative to Google Analytics. CommandKit provides seamless integration with Umami for tracking anonymous bot metrics.
+Umami is a simple, fast, privacy-focused alternative to Google
+Analytics. CommandKit provides seamless integration with Umami for
+tracking anonymous bot metrics.
## Setup
@@ -37,7 +40,8 @@ export default defineConfig({
## Usage
-Once configured, you can track anonymous events using the `track` function:
+Once configured, you can track anonymous events using the `track`
+function:
```ts
import { track } from 'commandkit/analytics';
@@ -58,7 +62,8 @@ await track({
## Automatic Events
-CommandKit automatically tracks the following anonymous events in Umami:
+CommandKit automatically tracks the following anonymous events in
+Umami:
- `command_execution`: Command execution events
- `cache_hit`: Cache hit events
diff --git a/apps/website/docs/guide/10-analytics/04-custom-providers.mdx b/apps/website/docs/guide.old/10-analytics/04-custom-providers.mdx
similarity index 83%
rename from apps/website/docs/guide/10-analytics/04-custom-providers.mdx
rename to apps/website/docs/guide.old/10-analytics/04-custom-providers.mdx
index 3eba053d..7d3a751a 100644
--- a/apps/website/docs/guide/10-analytics/04-custom-providers.mdx
+++ b/apps/website/docs/guide.old/10-analytics/04-custom-providers.mdx
@@ -1,15 +1,18 @@
---
title: Custom Analytics Providers
-description: Learn how to create custom analytics providers for CommandKit.
+description:
+ Learn how to create custom analytics providers for CommandKit.
---
# Custom Analytics Providers
-CommandKit's analytics system is designed to be extensible, allowing you to create custom analytics providers for your specific needs.
+CommandKit's analytics system is designed to be extensible, allowing
+you to create custom analytics providers for your specific needs.
## Creating a Custom Provider
-To create a custom provider, implement the `AnalyticsProvider` interface:
+To create a custom provider, implement the `AnalyticsProvider`
+interface:
```ts
import {
@@ -23,7 +26,10 @@ class AnonymousAnalyticsProvider implements AnalyticsProvider {
constructor(private readonly analytics: YourAnalyticsService) {}
- async track(engine: AnalyticsEngine, event: AnalyticsEvent): Promise {
+ async track(
+ engine: AnalyticsEngine,
+ event: AnalyticsEvent,
+ ): Promise {
// Implement your tracking logic here
const { name, data } = event;
@@ -40,7 +46,10 @@ class AnonymousAnalyticsProvider implements AnalyticsProvider {
});
}
- async identify(engine: AnalyticsEngine, event: IdentifyEvent): Promise {
+ async identify(
+ engine: AnalyticsEngine,
+ event: IdentifyEvent,
+ ): Promise {
// Implement anonymous session identification
await this.analytics.identify({
// Use anonymous session identifiers
@@ -65,7 +74,10 @@ class AnonymousAnalyticsProvider implements AnalyticsProvider {
Create a plugin class that extends `RuntimePlugin`:
```ts
-import { CommandKitPluginRuntime, RuntimePlugin } from 'commandkit/plugin';
+import {
+ CommandKitPluginRuntime,
+ RuntimePlugin,
+} from 'commandkit/plugin';
import { AnonymousAnalyticsProvider } from './provider';
export interface AnonymousAnalyticsPluginOptions {
@@ -91,7 +103,9 @@ export class AnonymousAnalyticsPlugin extends RuntimePlugin {
+ public async deactivate(
+ ctx: CommandKitPluginRuntime,
+ ): Promise {
if (!this.provider) return;
// Cleanup if needed
@@ -127,7 +141,8 @@ export default defineConfig({
1. Keep your provider implementation focused and single-purpose
2. Handle errors gracefully and log them appropriately
-3. Implement both `track` and `identify` methods if your analytics service supports them
+3. Implement both `track` and `identify` methods if your analytics
+ service supports them
4. Use TypeScript for better type safety and developer experience
5. Document your provider's configuration options and requirements
6. Consider Discord's rate limits when sending data
@@ -138,7 +153,8 @@ export default defineConfig({
## Example: Simple Console Provider
-Here's a simple example of a provider that logs anonymous events to the console:
+Here's a simple example of a provider that logs anonymous events to
+the console:
```ts
import {
@@ -150,7 +166,10 @@ import {
class ConsoleProvider implements AnalyticsProvider {
readonly name = 'console';
- async track(engine: AnalyticsEngine, event: AnalyticsEvent): Promise {
+ async track(
+ engine: AnalyticsEngine,
+ event: AnalyticsEvent,
+ ): Promise {
console.log('Analytics Event:', {
name: event.name,
// Only log anonymous data
@@ -161,7 +180,10 @@ class ConsoleProvider implements AnalyticsProvider {
});
}
- async identify(engine: AnalyticsEngine, event: IdentifyEvent): Promise {
+ async identify(
+ engine: AnalyticsEngine,
+ event: IdentifyEvent,
+ ): Promise {
console.log('Session Identified:', {
sessionId: 'session_' + Math.random().toString(36).substring(7),
timestamp: Date.now(),
@@ -181,7 +203,9 @@ export class ConsoleAnalyticsPlugin extends RuntimePlugin {
ctx.commandkit.analytics.registerProvider(this.provider);
}
- public async deactivate(ctx: CommandKitPluginRuntime): Promise {
+ public async deactivate(
+ ctx: CommandKitPluginRuntime,
+ ): Promise {
if (!this.provider) return;
ctx.commandkit.analytics.removeProvider(this.provider);
}
diff --git a/apps/website/docs/guide/11-localization/01-introduction.mdx b/apps/website/docs/guide.old/11-localization/01-introduction.mdx
similarity index 78%
rename from apps/website/docs/guide/11-localization/01-introduction.mdx
rename to apps/website/docs/guide.old/11-localization/01-introduction.mdx
index eba0599b..077333af 100644
--- a/apps/website/docs/guide/11-localization/01-introduction.mdx
+++ b/apps/website/docs/guide.old/11-localization/01-introduction.mdx
@@ -1,6 +1,9 @@
---
title: i18n Plugin
-description: The i18n plugin for CommandKit enables internationalization (i18n) for CommandKit using i18next. It allows you to translate your application into different languages.
+description:
+ The i18n plugin for CommandKit enables internationalization (i18n)
+ for CommandKit using i18next. It allows you to translate your
+ application into different languages.
---
import Tabs from '@theme/Tabs';
@@ -8,16 +11,24 @@ import TabItem from '@theme/TabItem';
# i18n Plugin
-The i18n plugin integrates [i18next](https://www.i18next.com/) into CommandKit, enabling you to create multilingual Discord bots that can automatically adapt to your users' preferred languages. This plugin provides seamless internationalization support for commands, events, and all bot interactions.
+The i18n plugin integrates [i18next](https://www.i18next.com/) into
+CommandKit, enabling you to create multilingual Discord bots that can
+automatically adapt to your users' preferred languages. This plugin
+provides seamless internationalization support for commands, events,
+and all bot interactions.
## Features
-- 🌍 **Automatic locale detection** - Automatically uses Discord's guild preferred locale
+- 🌍 **Automatic locale detection** - Automatically uses Discord's
+ guild preferred locale
- 🔧 **Easy setup** - Simple configuration with sensible defaults
- 📁 **File-based translations** - Organize translations in JSON files
-- 🎯 **Context-aware** - Access translations in commands, events, and legacy handlers
-- 🔌 **i18next ecosystem** - Full compatibility with i18next plugins and features
-- 📝 **Command metadata localization** - Localize command names, descriptions, and options
+- 🎯 **Context-aware** - Access translations in commands, events, and
+ legacy handlers
+- 🔌 **i18next ecosystem** - Full compatibility with i18next plugins
+ and features
+- 📝 **Command metadata localization** - Localize command names,
+ descriptions, and options
## Installation
@@ -78,7 +89,9 @@ export default defineConfig({
## Translation Files Structure
-Create a `locales` directory inside your `src/app` folder with subdirectories for each language. Each language directory should contain JSON files for your translations.
+Create a `locales` directory inside your `src/app` folder with
+subdirectories for each language. Each language directory should
+contain JSON files for your translations.
```
src
@@ -100,7 +113,9 @@ src
### Supported Locales
-CommandKit uses Discord's locale identifiers. Please refer to [Discord's Locales documentation](https://discord.com/developers/docs/reference#locales) for a complete list.
+CommandKit uses Discord's locale identifiers. Please refer to
+[Discord's Locales documentation](https://discord.com/developers/docs/reference#locales)
+for a complete list.
## Quick Start Example
@@ -158,4 +173,5 @@ export const chatInput: ChatInputCommand = async (ctx) => {
};
```
-That's it! Your bot will now automatically respond in the user's guild preferred language.
+That's it! Your bot will now automatically respond in the user's guild
+preferred language.
diff --git a/apps/website/docs/guide/11-localization/02-commands-localization.mdx b/apps/website/docs/guide.old/11-localization/02-commands-localization.mdx
similarity index 92%
rename from apps/website/docs/guide/11-localization/02-commands-localization.mdx
rename to apps/website/docs/guide.old/11-localization/02-commands-localization.mdx
index cb48cfbd..c819435d 100644
--- a/apps/website/docs/guide/11-localization/02-commands-localization.mdx
+++ b/apps/website/docs/guide.old/11-localization/02-commands-localization.mdx
@@ -1,15 +1,22 @@
---
title: Commands Localization
-description: Learn how to localize your commands using the i18n plugin in CommandKit.
+description:
+ Learn how to localize your commands using the i18n plugin in
+ CommandKit.
---
# Commands Localization
-CommandKit's i18n plugin provides powerful localization features for slash commands, allowing you to translate command metadata (names, descriptions, options) and responses to match your users' preferred languages.
+CommandKit's i18n plugin provides powerful localization features for
+slash commands, allowing you to translate command metadata (names,
+descriptions, options) and responses to match your users' preferred
+languages.
## Translation File Structure
-Translation files should be placed in your `locales` directory and named after the command they translate. For example, translations for a `ping` command should be in `ping.json`.
+Translation files should be placed in your `locales` directory and
+named after the command they translate. For example, translations for
+a `ping` command should be in `ping.json`.
### Basic Translation File
@@ -23,7 +30,8 @@ Translation files should be placed in your `locales` directory and named after t
### Command Metadata Localization
-Use the special `$command` key to localize command metadata that appears in Discord's interface:
+Use the special `$command` key to localize command metadata that
+appears in Discord's interface:
```json title="src/app/locales/en-US/ping.json"
{
@@ -56,18 +64,22 @@ Use the special `$command` key to localize command metadata that appears in Disc
}
```
-The `$command` object structure mirrors Discord's application command structure:
+The `$command` object structure mirrors Discord's application command
+structure:
- `name`: Command name (shown in Discord's command picker)
-- `description`: Command description (shown in Discord's command picker)
+- `description`: Command description (shown in Discord's command
+ picker)
- `options`: Array of option localizations
- `name`: Option name
- `description`: Option description
- - `choices`: Array of choice localizations (for string options with predefined choices)
+ - `choices`: Array of choice localizations (for string options with
+ predefined choices)
## Using Translations in Commands
-The `locale()` function in your command context provides access to translations and i18next features:
+The `locale()` function in your command context provides access to
+translations and i18next features:
```ts title="src/app/commands/ping.ts"
import type { ChatInputCommand } from 'commandkit';
@@ -88,7 +100,8 @@ export const chatInput: ChatInputCommand = async (ctx) => {
### Manual Locale Override
-You can specify a particular locale instead of using the guild's preferred locale:
+You can specify a particular locale instead of using the guild's
+preferred locale:
```ts
export const chatInput: ChatInputCommand = async (ctx) => {
diff --git a/apps/website/docs/guide/11-localization/03-events-localization.mdx b/apps/website/docs/guide.old/11-localization/03-events-localization.mdx
similarity index 91%
rename from apps/website/docs/guide/11-localization/03-events-localization.mdx
rename to apps/website/docs/guide.old/11-localization/03-events-localization.mdx
index 556053b9..baa6da99 100644
--- a/apps/website/docs/guide/11-localization/03-events-localization.mdx
+++ b/apps/website/docs/guide.old/11-localization/03-events-localization.mdx
@@ -1,15 +1,19 @@
---
title: Events Localization
-description: Learn how to localize events using the i18n plugin in CommandKit.
+description:
+ Learn how to localize events using the i18n plugin in CommandKit.
---
# Events Localization
-Event handlers in CommandKit can also benefit from localization, allowing you to create multilingual responses and messages for various Discord events like message creation, member joins, and more.
+Event handlers in CommandKit can also benefit from localization,
+allowing you to create multilingual responses and messages for various
+Discord events like message creation, member joins, and more.
## Using Translations in Event Handlers
-Since event handlers don't have the same context as commands, you need to import the `locale` function directly from the i18n plugin:
+Since event handlers don't have the same context as commands, you need
+to import the `locale` function directly from the i18n plugin:
```ts title="src/app/events/messageCreate/handler.ts"
import { locale } from '@commandkit/i18n';
@@ -23,7 +27,9 @@ export default async function onMessageCreate(message: Message) {
const { t } = locale(message.guild?.preferredLocale);
if (message.content.toLowerCase() === 'hello') {
- await message.reply(t('greeting', { user: message.author.displayName }));
+ await message.reply(
+ t('greeting', { user: message.author.displayName }),
+ );
}
if (message.content.toLowerCase() === 'help') {
@@ -128,7 +134,8 @@ export default async function onGuildMemberAdd(member: GuildMember) {
### Locale Detection Strategies
-When working with events, you have several options for determining the appropriate locale:
+When working with events, you have several options for determining the
+appropriate locale:
#### 1. Guild Preferred Locale (Recommended)
@@ -228,8 +235,13 @@ export default async function autoModerator(message: Message) {
## Best Practices for Event Localization
-1. **Always provide fallbacks**: Event handlers should gracefully handle missing translations
-2. **Use appropriate locale sources**: Choose between guild, user, or custom locale detection based on context
-3. **Keep translations consistent**: Use the same tone and style across events and commands
-4. **Test with different locales**: Ensure your events work correctly with various language settings
-5. **Cache translations when possible**: For high-frequency events, consider caching translation functions
+1. **Always provide fallbacks**: Event handlers should gracefully
+ handle missing translations
+2. **Use appropriate locale sources**: Choose between guild, user, or
+ custom locale detection based on context
+3. **Keep translations consistent**: Use the same tone and style
+ across events and commands
+4. **Test with different locales**: Ensure your events work correctly
+ with various language settings
+5. **Cache translations when possible**: For high-frequency events,
+ consider caching translation functions
diff --git a/apps/website/docs/guide/11-localization/04-usage-with-legacy-plugin.mdx b/apps/website/docs/guide.old/11-localization/04-usage-with-legacy-plugin.mdx
similarity index 89%
rename from apps/website/docs/guide/11-localization/04-usage-with-legacy-plugin.mdx
rename to apps/website/docs/guide.old/11-localization/04-usage-with-legacy-plugin.mdx
index 02f4396d..01e992c4 100644
--- a/apps/website/docs/guide/11-localization/04-usage-with-legacy-plugin.mdx
+++ b/apps/website/docs/guide.old/11-localization/04-usage-with-legacy-plugin.mdx
@@ -1,15 +1,19 @@
---
title: Usage with Legacy Plugin
-description: Learn how to use the i18n plugin with legacy commands in CommandKit.
+description:
+ Learn how to use the i18n plugin with legacy commands in CommandKit.
---
# Usage with Legacy Plugin
-The i18n plugin is fully compatible with CommandKit's legacy command system, allowing you to add internationalization to existing projects without major refactoring.
+The i18n plugin is fully compatible with CommandKit's legacy command
+system, allowing you to add internationalization to existing projects
+without major refactoring.
## Translations in Legacy Commands
-For legacy commands, import the `locale` function directly from the i18n plugin:
+For legacy commands, import the `locale` function directly from the
+i18n plugin:
```ts title="src/app/commands/legacy/ping.js"
import { locale } from '@commandkit/i18n';
@@ -52,7 +56,8 @@ export async function run({ interaction, client }) {
## Legacy Command Translation Files
-Translation files for legacy commands work the same way as modern commands:
+Translation files for legacy commands work the same way as modern
+commands:
```json title="src/app/locales/en-US/ping.json"
{
@@ -76,7 +81,8 @@ Here's how to migrate an existing legacy command to use i18n:
```ts title="src/app/commands/legacy/userinfo.js"
export async function run({ interaction }) {
- const user = interaction.options.getUser('user') || interaction.user;
+ const user =
+ interaction.options.getUser('user') || interaction.user;
const member = interaction.guild?.members.cache.get(user.id);
const embed = {
@@ -133,13 +139,18 @@ import { locale } from '@commandkit/i18n';
export async function run({ interaction }) {
const { t } = locale();
- const user = interaction.options.getUser('user') || interaction.user;
+ const user =
+ interaction.options.getUser('user') || interaction.user;
const member = interaction.guild?.members.cache.get(user.id);
const embed = {
title: t('embed.title', { username: user.username }),
fields: [
- { name: t('embed.fields.username'), value: user.username, inline: true },
+ {
+ name: t('embed.fields.username'),
+ value: user.username,
+ inline: true,
+ },
{ name: t('embed.fields.id'), value: user.id, inline: true },
{
name: t('embed.fields.created'),
@@ -216,14 +227,19 @@ export const data = {
## Best Practices for Legacy Commands
-1. **Gradual Migration**: You can migrate commands one at a time without affecting others
-2. **Consistent Naming**: Use the same translation keys across legacy and modern commands when possible
-3. **Error Handling**: Always provide fallback text for missing translations
-4. **Testing**: Test legacy commands with different locales to ensure compatibility
+1. **Gradual Migration**: You can migrate commands one at a time
+ without affecting others
+2. **Consistent Naming**: Use the same translation keys across legacy
+ and modern commands when possible
+3. **Error Handling**: Always provide fallback text for missing
+ translations
+4. **Testing**: Test legacy commands with different locales to ensure
+ compatibility
## Mixed Command Types
-You can use both legacy and modern commands with i18n in the same project:
+You can use both legacy and modern commands with i18n in the same
+project:
```ts title="commandkit.config.ts"
import { defineConfig } from 'commandkit';
diff --git a/apps/website/docs/guide/12-devtools/01-introduction.mdx b/apps/website/docs/guide.old/12-devtools/01-introduction.mdx
similarity index 55%
rename from apps/website/docs/guide/12-devtools/01-introduction.mdx
rename to apps/website/docs/guide.old/12-devtools/01-introduction.mdx
index 82fa85a3..f9b48e2b 100644
--- a/apps/website/docs/guide/12-devtools/01-introduction.mdx
+++ b/apps/website/docs/guide.old/12-devtools/01-introduction.mdx
@@ -1,16 +1,22 @@
---
title: DevTools Plugin
-description: The DevTools plugin for CommandKit provides a set of tools and utilities to enhance the development experience. It includes features like command inspection, performance monitoring, and debugging tools.
+description:
+ The DevTools plugin for CommandKit provides a set of tools and
+ utilities to enhance the development experience. It includes
+ features like command inspection, performance monitoring, and
+ debugging tools.
---
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
-The DevTools plugin for CommandKit provides a set of tools and utilities to enhance the development experience. It includes features like command inspection, performance monitoring, and debugging tools.
+The DevTools plugin for CommandKit provides a set of tools and
+utilities to enhance the development experience. It includes features
+like command inspection, performance monitoring, and debugging tools.
-:::warning
-This plugin is in early development and may not be fully functional. It is recommended to use it for testing and feedback purposes only.
-:::
+:::warning This plugin is in early development and may not be fully
+functional. It is recommended to use it for testing and feedback
+purposes only. :::
## Installation
@@ -51,7 +57,8 @@ export default defineConfig({
});
```
-That's it! The DevTools plugin will run in http://localhost:4356 when you start your application with `commandkit dev`.
+That's it! The DevTools plugin will run in http://localhost:4356 when
+you start your application with `commandkit dev`.
### Preview
diff --git a/apps/website/docs/guide/13-ai-powered-commands/01-introduction.mdx b/apps/website/docs/guide.old/13-ai-powered-commands/01-introduction.mdx
similarity index 82%
rename from apps/website/docs/guide/13-ai-powered-commands/01-introduction.mdx
rename to apps/website/docs/guide.old/13-ai-powered-commands/01-introduction.mdx
index 4512e31e..25473135 100644
--- a/apps/website/docs/guide/13-ai-powered-commands/01-introduction.mdx
+++ b/apps/website/docs/guide.old/13-ai-powered-commands/01-introduction.mdx
@@ -1,14 +1,17 @@
---
title: AI System Overview
-description: Complete overview of CommandKit's AI system architecture and capabilities.
+description:
+ Complete overview of CommandKit's AI system architecture and
+ capabilities.
---
# AI System Overview
-CommandKit's `@commandkit/ai` plugin allows you to execute your bot commands using large language models. This enables you to use your bot's features entirely through natural language.
+CommandKit's `@commandkit/ai` plugin allows you to execute your bot
+commands using large language models. This enables you to use your
+bot's features entirely through natural language.
-:::warning
-This is an experimental feature and is subject to change.
+:::warning This is an experimental feature and is subject to change.
:::
## Installation
@@ -17,13 +20,16 @@ This is an experimental feature and is subject to change.
npm install @commandkit/ai
```
-You also need to install the AI SDK for the model you want to use. For example, if you want to use Google Gemini, you can install the `@ai-sdk/google` package:
+You also need to install the AI SDK for the model you want to use. For
+example, if you want to use Google Gemini, you can install the
+`@ai-sdk/google` package:
```bash
npm install @ai-sdk/google
```
-Refer to the [AI SDKs documentation](https://ai-sdk.dev) for more information on how to set up the AI SDK for your model.
+Refer to the [AI SDKs documentation](https://ai-sdk.dev) for more
+information on how to set up the AI SDK for your model.
## Usage
@@ -57,17 +63,22 @@ graph TD
### Core Components
-1. **AI Plugin** - Manages the AI system lifecycle and message processing
-2. **Configuration System** - Handles AI model selection and behavior customization
-3. **Context Management** - Provides request context and state management
+1. **AI Plugin** - Manages the AI system lifecycle and message
+ processing
+2. **Configuration System** - Handles AI model selection and behavior
+ customization
+3. **Context Management** - Provides request context and state
+ management
4. **Tool System** - Enables AI to execute functions and commands
-5. **Command Integration** - Allows existing commands to be AI-accessible
+5. **Command Integration** - Allows existing commands to be
+ AI-accessible
## Key Features
### Natural Language Processing
-The AI system can understand natural language requests and map them to specific commands:
+The AI system can understand natural language requests and map them to
+specific commands:
```
User: "Can you ban @user for spamming?"
@@ -229,12 +240,13 @@ const rateLimiter = new RateLimiter(5, 60000); // 5 requests per minute
```ts
const cache = new Map();
-const cachedResult = cache.get(cacheKey) || (await expensiveOperation());
+const cachedResult =
+ cache.get(cacheKey) || (await expensiveOperation());
```
-:::info
-For a better cache management solution, check out the [@commandkit/cache](../04-caching/01-caching-in-commandkit.mdx) plugin guide.
-:::
+:::info For a better cache management solution, check out the
+[@commandkit/cache](../04-caching/01-caching-in-commandkit.mdx) plugin
+guide. :::
### Async Processing
@@ -278,7 +290,8 @@ catch (error) {
## Best Practices
-1. **Always validate permissions** before executing sensitive operations
+1. **Always validate permissions** before executing sensitive
+ operations
2. **Sanitize user inputs** to prevent injection attacks
3. **Implement rate limiting** to prevent abuse
4. **Use structured error handling** for better user experience
@@ -310,13 +323,17 @@ catch (error) {
1. **Install the AI package**: `npm install @commandkit/ai`
2. **Configure your AI model** in `src/ai.ts`
-3. **Create AI-compatible commands** with `aiConfig` and `ai` functions
+3. **Create AI-compatible commands** with `aiConfig` and `ai`
+ functions
4. **Test your setup** with simple commands
5. **Gradually add more complex functionality**
## Next Steps
-- [AI Configuration](./02-ai-configuration.mdx) - Set up your AI models
-- [Creating AI Commands](./03-creating-ai-commands.mdx) - Make commands AI-accessible
+- [AI Configuration](./02-ai-configuration.mdx) - Set up your AI
+ models
+- [Creating AI Commands](./03-creating-ai-commands.mdx) - Make
+ commands AI-accessible
- [Custom Tools](./06-custom-tools.mdx) - Extend AI capabilities
-- [Best Practices](./07-best-practices.mdx) - Production-ready implementations
+- [Best Practices](./07-best-practices.mdx) - Production-ready
+ implementations
diff --git a/apps/website/docs/guide/13-ai-powered-commands/02-ai-configuration.mdx b/apps/website/docs/guide.old/13-ai-powered-commands/02-ai-configuration.mdx
similarity index 89%
rename from apps/website/docs/guide/13-ai-powered-commands/02-ai-configuration.mdx
rename to apps/website/docs/guide.old/13-ai-powered-commands/02-ai-configuration.mdx
index bddbd294..d479a3d6 100644
--- a/apps/website/docs/guide/13-ai-powered-commands/02-ai-configuration.mdx
+++ b/apps/website/docs/guide.old/13-ai-powered-commands/02-ai-configuration.mdx
@@ -5,22 +5,30 @@ description: Learn how to configure AI models for CommandKit.
# AI Configuration
-CommandKit provides comprehensive AI configuration options to customize how your bot processes and responds to messages. You can configure AI models, message filters, system prompts, and processing hooks.
+CommandKit provides comprehensive AI configuration options to
+customize how your bot processes and responds to messages. You can
+configure AI models, message filters, system prompts, and processing
+hooks.
## Configuration Overview
The `configureAI` function accepts several configuration options:
-- **`selectAiModel`** (required) - Function to choose which AI model to use
-- **`messageFilter`** (optional) - Filter which messages trigger AI processing
-- **`disableBuiltInTools`** (optional) - Disable built-in Discord tools
-- **`prepareSystemPrompt`** (optional) - Custom system prompt generation
+- **`selectAiModel`** (required) - Function to choose which AI model
+ to use
+- **`messageFilter`** (optional) - Filter which messages trigger AI
+ processing
+- **`disableBuiltInTools`** (optional) - Disable built-in Discord
+ tools
+- **`prepareSystemPrompt`** (optional) - Custom system prompt
+ generation
- **`preparePrompt`** (optional) - Custom user prompt preparation
- **Lifecycle hooks** - Functions called during AI processing
## Creating a Configuration File
-Create a dedicated configuration file, typically `src/ai.ts`, to set up your AI model and processing options:
+Create a dedicated configuration file, typically `src/ai.ts`, to set
+up your AI model and processing options:
```ts title="src/ai.ts"
import { createGoogleGenerativeAI } from '@ai-sdk/google';
@@ -47,7 +55,8 @@ configureAI({
messageFilter: async (commandkit, message) => {
// Only respond when the bot is mentioned
return (
- message.inGuild() && message.mentions.users.has(message.client.user.id)
+ message.inGuild() &&
+ message.mentions.users.has(message.client.user.id)
);
},
@@ -64,7 +73,8 @@ configureAI({
### Model Selection (`selectAiModel`)
-The `selectAiModel` function is called for each message and must return model configuration:
+The `selectAiModel` function is called for each message and must
+return model configuration:
```ts
selectAiModel: async (ctx, message) => {
@@ -142,7 +152,9 @@ configureAI({
// Called when AI processing starts
onProcessingStart: async (ctx, message) => {
await message.channel.sendTyping();
- console.log(`AI processing started for ${message.author.username}`);
+ console.log(
+ `AI processing started for ${message.author.username}`,
+ );
},
// Called when AI processing finishes
@@ -208,7 +220,8 @@ export default client;
## Multiple AI Providers
-You can configure multiple AI providers and choose between them dynamically:
+You can configure multiple AI providers and choose between them
+dynamically:
```ts title="src/ai.ts"
import { createGoogleGenerativeAI } from '@ai-sdk/google';
@@ -216,7 +229,9 @@ import { createOpenAI } from '@ai-sdk/openai';
import { configureAI } from '@commandkit/ai';
// Configure multiple providers
-const google = createGoogleGenerativeAI({ apiKey: process.env.GOOGLE_API_KEY });
+const google = createGoogleGenerativeAI({
+ apiKey: process.env.GOOGLE_API_KEY,
+});
const openai = createOpenAI({ apiKey: process.env.OPENAI_API_KEY });
configureAI({
@@ -267,7 +282,9 @@ configureAI({
console.error(error.stack);
} else {
// Generic error in production
- await message.reply('An error occurred while processing your request.');
+ await message.reply(
+ 'An error occurred while processing your request.',
+ );
}
},
});
diff --git a/apps/website/docs/guide/13-ai-powered-commands/03-creating-ai-commands.mdx b/apps/website/docs/guide.old/13-ai-powered-commands/03-creating-ai-commands.mdx
similarity index 75%
rename from apps/website/docs/guide/13-ai-powered-commands/03-creating-ai-commands.mdx
rename to apps/website/docs/guide.old/13-ai-powered-commands/03-creating-ai-commands.mdx
index 95835b68..75b62cc0 100644
--- a/apps/website/docs/guide/13-ai-powered-commands/03-creating-ai-commands.mdx
+++ b/apps/website/docs/guide.old/13-ai-powered-commands/03-creating-ai-commands.mdx
@@ -1,13 +1,19 @@
---
title: Creating AI Commands
-description: Learn how to create commands that can be executed by AI models.
+description:
+ Learn how to create commands that can be executed by AI models.
---
## Creating AI commands
-AI commands can be created by exporting a function called `ai` from your regular command file. You can also export `aiConfig` object along with the `ai` function to specify the parameters for the command.
+AI commands can be created by exporting a function called `ai` from
+your regular command file. You can also export `aiConfig` object along
+with the `ai` function to specify the parameters for the command.
-The `ai` function behaves similar to a `message` command, but it receives an additional `ctx.ai` property (which is an instance of [`AiContext`](../../api-reference/ai/classes/ai-context.mdx)) that contains the parameters passed by the AI model.
+The `ai` function behaves similar to a `message` command, but it
+receives an additional `ctx.ai` property (which is an instance of
+[`AiContext`](../../api-reference/ai/classes/ai-context.mdx)) that
+contains the parameters passed by the AI model.
```typescript title="src/app/commands/balance.ts"
import { ApplicationCommandOptionType } from 'discord.js';
@@ -29,7 +35,9 @@ export const command: CommandData = {
export const aiConfig: AiConfig = {
parameters: z.object({
- userId: z.string().describe('The ID of the user to get the balance of'),
+ userId: z
+ .string()
+ .describe('The ID of the user to get the balance of'),
}),
};
@@ -56,7 +64,8 @@ export const ai: AiCommand = async (ctx) => {
};
```
-Now, you can simply mention the bot in a message to get the balance of the user. Eg:
+Now, you can simply mention the bot in a message to get the balance of
+the user. Eg:
```text
@bot what is the balance of @user?
@@ -68,10 +77,12 @@ AI can also call multiple commands in a single message. Eg:
@bot show me the basic details of the user @user and also include the balance of that user.
```
-The above prompt will call the built-in `getUserInfo` tool and the `balance` command.
+The above prompt will call the built-in `getUserInfo` tool and the
+`balance` command.
## Example of AI creating a poll
-This is an example of how AI can create a poll using the `createPoll` command. The AI will generate a poll based on the user's input.
+This is an example of how AI can create a poll using the `createPoll`
+command. The AI will generate a poll based on the user's input.

diff --git a/apps/website/docs/guide/13-ai-powered-commands/04-ai-context-hooks.mdx b/apps/website/docs/guide.old/13-ai-powered-commands/04-ai-context-hooks.mdx
similarity index 88%
rename from apps/website/docs/guide/13-ai-powered-commands/04-ai-context-hooks.mdx
rename to apps/website/docs/guide.old/13-ai-powered-commands/04-ai-context-hooks.mdx
index 5ede3c5b..5fde3bf0 100644
--- a/apps/website/docs/guide/13-ai-powered-commands/04-ai-context-hooks.mdx
+++ b/apps/website/docs/guide.old/13-ai-powered-commands/04-ai-context-hooks.mdx
@@ -1,15 +1,19 @@
---
title: AI Context and Hooks
-description: Learn about AI context, built-in tools, and custom hooks for AI commands.
+description:
+ Learn about AI context, built-in tools, and custom hooks for AI
+ commands.
---
# AI Context and Hooks
-CommandKit's AI system provides powerful context management and built-in tools to enhance your AI-powered commands.
+CommandKit's AI system provides powerful context management and
+built-in tools to enhance your AI-powered commands.
## AI Context
-The `AiContext` class provides comprehensive access to the AI execution environment:
+The `AiContext` class provides comprehensive access to the AI
+execution environment:
### Context Properties
@@ -96,7 +100,8 @@ async function executeAICommand(message: Message) {
## Built-in AI Tools
-CommandKit provides several built-in tools that the AI can use to gather information:
+CommandKit provides several built-in tools that the AI can use to
+gather information:
### Available Tools
@@ -108,7 +113,9 @@ CommandKit provides several built-in tools that the AI can use to gather informa
### Example Tool Usage
-The AI can automatically use these tools. For example, if a user asks "What commands are available?", the AI will use the `getAvailableCommands` tool:
+The AI can automatically use these tools. For example, if a user asks
+"What commands are available?", the AI will use the
+`getAvailableCommands` tool:
```ts
// This happens automatically when the AI needs command information
@@ -133,7 +140,9 @@ export const getWeather = createTool({
name: 'getWeather',
description: 'Get current weather information for a location',
parameters: z.object({
- location: z.string().describe('The city or location to get weather for'),
+ location: z
+ .string()
+ .describe('The city or location to get weather for'),
units: z.enum(['celsius', 'fahrenheit']).default('celsius'),
}),
async execute(ctx, params) {
@@ -144,7 +153,8 @@ export const getWeather = createTool({
return {
location,
- temperature: units === 'celsius' ? weatherData.tempC : weatherData.tempF,
+ temperature:
+ units === 'celsius' ? weatherData.tempC : weatherData.tempF,
condition: weatherData.condition,
humidity: weatherData.humidity,
windSpeed: weatherData.windSpeed,
@@ -229,7 +239,9 @@ export const ai: AiCommand = async (ctx) => {
const { userId } = ctx.ai.params;
// Attempt to fetch user
- const user = await ctx.client.users.fetch(userId).catch(() => null);
+ const user = await ctx.client.users
+ .fetch(userId)
+ .catch(() => null);
if (!user) {
await ctx.message.reply('❌ User not found.');
@@ -254,7 +266,8 @@ export const ai: AiCommand = async (ctx) => {
## Context Lifecycle
-Understanding the AI context lifecycle helps with proper resource management:
+Understanding the AI context lifecycle helps with proper resource
+management:
```ts
export const ai: AiCommand = async (ctx) => {
@@ -274,9 +287,12 @@ export const ai: AiCommand = async (ctx) => {
## Best Practices
-1. **Resource Cleanup**: The context store is automatically cleared, but clean up external resources manually
-2. **Error Boundaries**: Always wrap potentially failing operations in try-catch blocks
-3. **Validation**: Validate AI-generated parameters even with Zod schemas
+1. **Resource Cleanup**: The context store is automatically cleared,
+ but clean up external resources manually
+2. **Error Boundaries**: Always wrap potentially failing operations in
+ try-catch blocks
+3. **Validation**: Validate AI-generated parameters even with Zod
+ schemas
4. **Logging**: Use the context information for detailed logging
5. **Performance**: Avoid storing large objects in the context store
@@ -297,10 +313,14 @@ export const ai: AiCommand = async (ctx) => {
// Good: Handle external API failures
try {
const result = await processPayment(amount);
- await ctx.message.reply(`✅ Payment processed: ${result.transactionId}`);
+ await ctx.message.reply(
+ `✅ Payment processed: ${result.transactionId}`,
+ );
} catch (error) {
console.error('Payment failed:', error);
- await ctx.message.reply('❌ Payment processing failed. Please try again.');
+ await ctx.message.reply(
+ '❌ Payment processing failed. Please try again.',
+ );
}
};
```
diff --git a/apps/website/docs/guide/13-ai-powered-commands/05-advanced-configuration.mdx b/apps/website/docs/guide.old/13-ai-powered-commands/05-advanced-configuration.mdx
similarity index 90%
rename from apps/website/docs/guide/13-ai-powered-commands/05-advanced-configuration.mdx
rename to apps/website/docs/guide.old/13-ai-powered-commands/05-advanced-configuration.mdx
index db514f4e..b2236ad5 100644
--- a/apps/website/docs/guide/13-ai-powered-commands/05-advanced-configuration.mdx
+++ b/apps/website/docs/guide.old/13-ai-powered-commands/05-advanced-configuration.mdx
@@ -1,21 +1,26 @@
---
title: Advanced AI Configuration
-description: Deep dive into advanced AI configuration options and customization.
+description:
+ Deep dive into advanced AI configuration options and customization.
---
# Advanced AI Configuration
-CommandKit provides extensive configuration options to customize the AI behavior, from system prompts to error handling.
+CommandKit provides extensive configuration options to customize the
+AI behavior, from system prompts to error handling.
## Complete Configuration Options
-The `configureAI` function accepts a comprehensive configuration object:
+The `configureAI` function accepts a comprehensive configuration
+object:
```ts title="src/ai.ts"
import { configureAI } from '@commandkit/ai';
import { createGoogleGenerativeAI } from '@ai-sdk/google';
-const google = createGoogleGenerativeAI({ apiKey: process.env.GOOGLE_API_KEY });
+const google = createGoogleGenerativeAI({
+ apiKey: process.env.GOOGLE_API_KEY,
+});
configureAI({
// Required: Select which AI model to use
@@ -145,18 +150,28 @@ messageFilter: async (commandkit, message) => {
// Only process in specific channels
const allowedChannels = ['ai-chat', 'bot-commands'];
- if (message.inGuild() && !allowedChannels.includes(message.channel.name)) {
+ if (
+ message.inGuild() &&
+ !allowedChannels.includes(message.channel.name)
+ ) {
return false;
}
// Check user permissions
- if (message.inGuild() && !message.member?.permissions.has('SendMessages')) {
+ if (
+ message.inGuild() &&
+ !message.member?.permissions.has('SendMessages')
+ ) {
return false;
}
// Check for mentions or specific keywords
- const hasMention = message.mentions.users.has(message.client.user.id);
- const hasKeyword = /\b(hey bot|help me|ai)\b/i.test(message.content);
+ const hasMention = message.mentions.users.has(
+ message.client.user.id,
+ );
+ const hasKeyword = /\b(hey bot|help me|ai)\b/i.test(
+ message.content,
+ );
return hasMention || hasKeyword;
};
@@ -171,7 +186,9 @@ messageFilter: async (commandkit, message) => {
const member = message.member;
const allowedRoles = ['Premium', 'Moderator', 'AI User'];
- return member.roles.cache.some((role) => allowedRoles.includes(role.name));
+ return member.roles.cache.some((role) =>
+ allowedRoles.includes(role.name),
+ );
};
```
@@ -198,7 +215,10 @@ prepareSystemPrompt: async (ctx, message) => {
${ctx.commandkit.commandHandler
.getCommandsArray()
.filter((cmd) => 'ai' in cmd.data)
- .map((cmd) => `- ${cmd.data.command.name}: ${cmd.data.command.description}`)
+ .map(
+ (cmd) =>
+ `- ${cmd.data.command.name}: ${cmd.data.command.description}`,
+ )
.join('\n')}
Keep responses under 2000 characters and be helpful!`;
@@ -220,7 +240,9 @@ prepareSystemPrompt: async (ctx, message) => {
? 'Good afternoon'
: 'Good evening';
- const userHistory = await getUserInteractionHistory(message.author.id);
+ const userHistory = await getUserInteractionHistory(
+ message.author.id,
+ );
const personalizedNote =
userHistory.length > 0
? `You've helped this user ${userHistory.length} times before.`
@@ -290,7 +312,10 @@ preparePrompt: async (ctx, message) => {
const conversation: AiMessage = recentMessages
// Filter out non-text messages and other bots
- .filter((msg) => msg.content && (!msg.author.bot || isMe(msg.author.id)))
+ .filter(
+ (msg) =>
+ msg.content && (!msg.author.bot || isMe(msg.author.id)),
+ )
.reverse()
.map((msg) => ({
role: isMe(msg.author.id) ? 'assistant' : 'user',
@@ -362,7 +387,9 @@ onResult: async (ctx, message, result) => {
console.log(`Tokens used: ${usage?.totalTokens || 'unknown'}`);
if (!text?.trim()) {
- await message.reply('I processed your request but have nothing to say.');
+ await message.reply(
+ 'I processed your request but have nothing to say.',
+ );
return;
}
@@ -490,7 +517,9 @@ configureAI({
console.error(error.stack);
} else {
// Generic error in production
- await message.reply('An error occurred while processing your request.');
+ await message.reply(
+ 'An error occurred while processing your request.',
+ );
// Log to production monitoring
logProductionError(error, ctx, message);
}
diff --git a/apps/website/docs/guide/13-ai-powered-commands/06-custom-tools.mdx b/apps/website/docs/guide.old/13-ai-powered-commands/06-custom-tools.mdx
similarity index 89%
rename from apps/website/docs/guide/13-ai-powered-commands/06-custom-tools.mdx
rename to apps/website/docs/guide.old/13-ai-powered-commands/06-custom-tools.mdx
index 36af1638..c50a6052 100644
--- a/apps/website/docs/guide/13-ai-powered-commands/06-custom-tools.mdx
+++ b/apps/website/docs/guide.old/13-ai-powered-commands/06-custom-tools.mdx
@@ -1,16 +1,27 @@
---
title: Custom AI Tools
-description: Learn how to create custom tools for AI models to extend functionality.
+description:
+ Learn how to create custom tools for AI models to extend
+ functionality.
---
# Custom AI Tools
-CommandKit allows you to create custom tools that AI models can use to extend their capabilities beyond built-in Discord functions. You can define tools in two ways,
+CommandKit allows you to create custom tools that AI models can use to
+extend their capabilities beyond built-in Discord functions. You can
+define tools in two ways,
-- one with the `createTool` function, which is a simple way to create tools that can be used by AI models. This is a wrapper around the `tool` function of `ai-sdk` which enables the tool to receive context (`AiContext` which contains client and message objects) and parameters.
-- another with the `tool` function directly, which does not provide context argument. You may use the `useAIContext` hook to access the context in the tool function if needed.
+- one with the `createTool` function, which is a simple way to create
+ tools that can be used by AI models. This is a wrapper around the
+ `tool` function of `ai-sdk` which enables the tool to receive
+ context (`AiContext` which contains client and message objects) and
+ parameters.
+- another with the `tool` function directly, which does not provide
+ context argument. You may use the `useAIContext` hook to access the
+ context in the tool function if needed.
-This guide will walk you through creating custom tools, validating parameters, and integrating with external APIs.
+This guide will walk you through creating custom tools, validating
+parameters, and integrating with external APIs.
## Creating Basic Tools
@@ -53,7 +64,8 @@ export const calculator = createTool({
});
```
-See the [Tool Registration](#tool-registration) section to learn how to register this tool with your AI model.
+See the [Tool Registration](#tool-registration) section to learn how
+to register this tool with your AI model.
## Parameter Validation
@@ -215,7 +227,10 @@ export const searchGitHubRepos = createTool({
description: 'Search for GitHub repositories',
parameters: z.object({
query: z.string().describe('Search query for repositories'),
- language: z.string().optional().describe('Filter by programming language'),
+ language: z
+ .string()
+ .optional()
+ .describe('Filter by programming language'),
sort: z.enum(['stars', 'forks', 'updated']).default('stars'),
limit: z.number().min(1).max(20).default(5),
}),
@@ -223,7 +238,9 @@ export const searchGitHubRepos = createTool({
const { query, language, sort, limit } = params;
try {
- const searchQuery = language ? `${query} language:${language}` : query;
+ const searchQuery = language
+ ? `${query} language:${language}`
+ : query;
const response = await fetch(
`https://api.github.com/search/repositories?q=${encodeURIComponent(searchQuery)}&sort=${sort}&per_page=${limit}`,
@@ -297,9 +314,13 @@ export const listFiles = createTool({
throw new Error('Invalid directory path');
}
- const entries = await fs.readdir(safePath, { withFileTypes: true });
+ const entries = await fs.readdir(safePath, {
+ withFileTypes: true,
+ });
const files = entries
- .filter((entry) => includeHidden || !entry.name.startsWith('.'))
+ .filter(
+ (entry) => includeHidden || !entry.name.startsWith('.'),
+ )
.map((entry) => ({
name: entry.name,
type: entry.isDirectory() ? 'directory' : 'file',
@@ -369,7 +390,9 @@ export const moderateContent = createTool({
}
return {
- content: content.substring(0, 100) + (content.length > 100 ? '...' : ''),
+ content:
+ content.substring(0, 100) +
+ (content.length > 100 ? '...' : ''),
violations,
isClean: violations.length === 0,
severity:
@@ -401,7 +424,8 @@ export const timeoutUser = createTool({
// Permission check
if (!ctx.message.member?.permissions.has('ModerateMembers')) {
return {
- error: 'You need Moderate Members permission to use this command',
+ error:
+ 'You need Moderate Members permission to use this command',
};
}
@@ -414,7 +438,10 @@ export const timeoutUser = createTool({
};
}
- await member.timeout(duration * 1000, reason || 'No reason provided');
+ await member.timeout(
+ duration * 1000,
+ reason || 'No reason provided',
+ );
return {
success: true,
@@ -541,7 +568,8 @@ export const shortenUrl = createTool({
## Tool Registration
-Register your custom tools by adding them to the AI model configuration:
+Register your custom tools by adding them to the AI model
+configuration:
```ts title="src/ai.ts"
import { configureAI } from '@commandkit/ai';
@@ -565,11 +593,15 @@ configureAI({
## Best Practices
-1. **Security**: Always validate user permissions for sensitive operations
-2. **Error Handling**: Return structured error information instead of throwing
+1. **Security**: Always validate user permissions for sensitive
+ operations
+2. **Error Handling**: Return structured error information instead of
+ throwing
3. **Rate Limiting**: Implement rate limiting for API calls
-4. **Validation**: Use Zod schemas for comprehensive parameter validation
-5. **Documentation**: Provide clear descriptions for tools and parameters
+4. **Validation**: Use Zod schemas for comprehensive parameter
+ validation
+5. **Documentation**: Provide clear descriptions for tools and
+ parameters
6. **Testing**: Test tools independently before integration
```ts
diff --git a/apps/website/docs/guide/13-ai-powered-commands/07-best-practices.mdx b/apps/website/docs/guide.old/13-ai-powered-commands/07-best-practices.mdx
similarity index 86%
rename from apps/website/docs/guide/13-ai-powered-commands/07-best-practices.mdx
rename to apps/website/docs/guide.old/13-ai-powered-commands/07-best-practices.mdx
index 5dd2c989..32fa0917 100644
--- a/apps/website/docs/guide/13-ai-powered-commands/07-best-practices.mdx
+++ b/apps/website/docs/guide.old/13-ai-powered-commands/07-best-practices.mdx
@@ -1,17 +1,20 @@
---
title: AI Best Practices & Examples
-description: Best practices and real-world examples for AI-powered Discord bots.
+description:
+ Best practices and real-world examples for AI-powered Discord bots.
---
# AI Best Practices & Examples
-This guide covers best practices for implementing AI-powered Discord bots and provides real-world examples.
+This guide covers best practices for implementing AI-powered Discord
+bots and provides real-world examples.
## Security Best Practices
### Permission Validation
-Always validate user permissions before executing sensitive operations:
+Always validate user permissions before executing sensitive
+operations:
```ts
export const ai: AiCommand = async (ctx) => {
@@ -26,12 +29,14 @@ export const ai: AiCommand = async (ctx) => {
}
// Additional role-based checks
- const hasModeratorRole = ctx.message.member.roles.cache.some((role) =>
- role.name.toLowerCase().includes('moderator'),
+ const hasModeratorRole = ctx.message.member.roles.cache.some(
+ (role) => role.name.toLowerCase().includes('moderator'),
);
if (action === 'ban' && !hasModeratorRole) {
- await ctx.message.reply('❌ Only moderators can perform ban actions.');
+ await ctx.message.reply(
+ '❌ Only moderators can perform ban actions.',
+ );
return;
}
@@ -56,7 +61,9 @@ export const aiConfig = {
(text) => !containsProfanity(text),
'Message contains inappropriate content',
),
- userId: z.string().regex(/^\d{17,19}$/, 'Invalid Discord user ID format'),
+ userId: z
+ .string()
+ .regex(/^\d{17,19}$/, 'Invalid Discord user ID format'),
}),
} satisfies AiConfig;
@@ -65,7 +72,9 @@ export const ai: AiCommand = async (ctx) => {
// Additional runtime validation
const sanitizedMessage = sanitizeHtml(message);
- const isValidUser = await ctx.client.users.fetch(userId).catch(() => null);
+ const isValidUser = await ctx.client.users
+ .fetch(userId)
+ .catch(() => null);
if (!isValidUser) {
await ctx.message.reply('❌ Invalid user specified.');
@@ -239,7 +248,9 @@ export const ai: AiCommand = async (ctx) => {
const track = await searchTrack(query);
if (!track) {
- await ctx.message.reply(`❌ No results found for "${query}".`);
+ await ctx.message.reply(
+ `❌ No results found for "${query}".`,
+ );
return;
}
@@ -275,7 +286,10 @@ export const ai: AiCommand = async (ctx) => {
const queueList = queue
.slice(0, 10)
- .map((track, i) => `${i + 1}. **${track.title}** by ${track.artist}`)
+ .map(
+ (track, i) =>
+ `${i + 1}. **${track.title}** by ${track.artist}`,
+ )
.join('\n');
await ctx.message.reply(`🎵 **Queue:**\n${queueList}`);
@@ -300,7 +314,8 @@ export const ai: AiCommand = async (ctx) => {
```ts title="src/commands/server-admin.ts"
export const aiConfig = {
- description: 'Manage server settings, roles, and channels (admin only)',
+ description:
+ 'Manage server settings, roles, and channels (admin only)',
parameters: z.object({
action: z.enum([
'create-channel',
@@ -341,7 +356,9 @@ export const ai: AiCommand = async (ctx) => {
switch (action) {
case 'create-channel':
if (!channelName || !channelType) {
- await ctx.message.reply('❌ Channel name and type are required.');
+ await ctx.message.reply(
+ '❌ Channel name and type are required.',
+ );
return;
}
@@ -356,7 +373,9 @@ export const ai: AiCommand = async (ctx) => {
type: channelTypeMap[channelType],
});
- await ctx.message.reply(`✅ Created ${channelType} channel: ${channel}`);
+ await ctx.message.reply(
+ `✅ Created ${channelType} channel: ${channel}`,
+ );
break;
case 'create-role':
@@ -376,12 +395,16 @@ export const ai: AiCommand = async (ctx) => {
case 'update-settings':
if (!setting || value === undefined) {
- await ctx.message.reply('❌ Setting name and value are required.');
+ await ctx.message.reply(
+ '❌ Setting name and value are required.',
+ );
return;
}
await updateGuildSetting(guild.id, setting, value);
- await ctx.message.reply(`✅ Updated setting "${setting}" to "${value}"`);
+ await ctx.message.reply(
+ `✅ Updated setting "${setting}" to "${value}"`,
+ );
break;
case 'cleanup':
@@ -398,15 +421,30 @@ export const ai: AiCommand = async (ctx) => {
```ts title="src/commands/economy.ts"
export const aiConfig = {
- description: 'Manage user economy - check balance, transfer money, shop',
+ description:
+ 'Manage user economy - check balance, transfer money, shop',
parameters: z.object({
- action: z.enum(['balance', 'transfer', 'shop', 'buy', 'work', 'daily']),
+ action: z.enum([
+ 'balance',
+ 'transfer',
+ 'shop',
+ 'buy',
+ 'work',
+ 'daily',
+ ]),
targetUser: z
.string()
.optional()
.describe('User ID for balance check or transfer'),
- amount: z.number().min(1).optional().describe('Amount to transfer'),
- item: z.string().optional().describe('Item name to buy from shop'),
+ amount: z
+ .number()
+ .min(1)
+ .optional()
+ .describe('Amount to transfer'),
+ item: z
+ .string()
+ .optional()
+ .describe('Item name to buy from shop'),
}),
} satisfies AiConfig;
@@ -420,12 +458,15 @@ export const ai: AiCommand = async (ctx) => {
const user = await getEconomyUser(checkUserId);
if (targetUser && targetUser !== userId) {
- const targetUserObj = await ctx.client.users.fetch(targetUser);
+ const targetUserObj =
+ await ctx.client.users.fetch(targetUser);
await ctx.message.reply(
`💰 ${targetUserObj.username}'s balance: ${user.balance} coins`,
);
} else {
- await ctx.message.reply(`💰 Your balance: ${user.balance} coins`);
+ await ctx.message.reply(
+ `💰 Your balance: ${user.balance} coins`,
+ );
}
break;
@@ -438,13 +479,17 @@ export const ai: AiCommand = async (ctx) => {
}
if (targetUser === userId) {
- await ctx.message.reply('❌ You cannot transfer money to yourself.');
+ await ctx.message.reply(
+ '❌ You cannot transfer money to yourself.',
+ );
return;
}
const sender = await getEconomyUser(userId);
if (sender.balance < amount) {
- await ctx.message.reply('❌ Insufficient balance for this transfer.');
+ await ctx.message.reply(
+ '❌ Insufficient balance for this transfer.',
+ );
return;
}
@@ -470,7 +515,9 @@ export const ai: AiCommand = async (ctx) => {
case 'buy':
if (!item) {
- await ctx.message.reply('❌ Item name is required for purchases.');
+ await ctx.message.reply(
+ '❌ Item name is required for purchases.',
+ );
return;
}
@@ -525,7 +572,9 @@ export const ai: AiCommand = async (ctx) => {
try {
// Fallback functionality
const fallbackResult = await fallbackOperation(ctx.ai.params);
- await ctx.message.reply(`⚠️ Used fallback method: ${fallbackResult}`);
+ await ctx.message.reply(
+ `⚠️ Used fallback method: ${fallbackResult}`,
+ );
} catch (fallbackError) {
console.error('Fallback also failed:', fallbackError.message);
await ctx.message.reply(
@@ -545,7 +594,8 @@ function getErrorMessage(error: Error): string {
"❌ You don't have permission to perform this action.",
USER_NOT_FOUND: '❌ The specified user was not found.',
CHANNEL_NOT_FOUND: '❌ The specified channel was not found.',
- RATE_LIMITED: "⏰ You're doing that too quickly. Please wait a moment.",
+ RATE_LIMITED:
+ "⏰ You're doing that too quickly. Please wait a moment.",
NETWORK_ERROR:
'🌐 Network error. Please check your connection and try again.',
DATABASE_ERROR:
@@ -589,7 +639,11 @@ describe('Economy AI Command', () => {
it('should handle insufficient balance for transfers', async () => {
const mockContext = {
- params: { action: 'transfer', targetUser: '987654321', amount: 150 },
+ params: {
+ action: 'transfer',
+ targetUser: '987654321',
+ amount: 150,
+ },
message: { author: { id: '123456789' } },
} as unknown as AiContext;
diff --git a/apps/website/docs/guide/13-ai-powered-commands/08-troubleshooting.mdx b/apps/website/docs/guide.old/13-ai-powered-commands/08-troubleshooting.mdx
similarity index 92%
rename from apps/website/docs/guide/13-ai-powered-commands/08-troubleshooting.mdx
rename to apps/website/docs/guide.old/13-ai-powered-commands/08-troubleshooting.mdx
index ef40c8ec..eaac6e3e 100644
--- a/apps/website/docs/guide/13-ai-powered-commands/08-troubleshooting.mdx
+++ b/apps/website/docs/guide.old/13-ai-powered-commands/08-troubleshooting.mdx
@@ -1,11 +1,14 @@
---
title: AI Troubleshooting
-description: Common issues and solutions when working with CommandKit's AI system.
+description:
+ Common issues and solutions when working with CommandKit's AI
+ system.
---
# AI Troubleshooting
-This guide covers common issues you might encounter when working with CommandKit's AI system and how to resolve them.
+This guide covers common issues you might encounter when working with
+CommandKit's AI system and how to resolve them.
## Common Issues
@@ -31,7 +34,9 @@ This guide covers common issues you might encounter when working with CommandKit
// Check your message filter
messageFilter: async (commandkit, message) => {
console.log('Filtering message:', message.content);
- const shouldProcess = message.mentions.users.has(message.client.user.id);
+ const shouldProcess = message.mentions.users.has(
+ message.client.user.id,
+ );
console.log('Should process:', shouldProcess);
return shouldProcess;
};
@@ -70,7 +75,9 @@ This guide covers common issues you might encounter when working with CommandKit
selectAiModel: async (ctx, message) => {
// Make sure this function is defined and returns a model
if (!process.env.GOOGLE_API_KEY) {
- throw new Error('GOOGLE_API_KEY environment variable not set');
+ throw new Error(
+ 'GOOGLE_API_KEY environment variable not set',
+ );
}
return {
@@ -107,7 +114,10 @@ This guide covers common issues you might encounter when working with CommandKit
parameters: z.object({
// Make sure parameter names match what the AI should provide
username: z.string().describe('The username to greet'),
- message: z.string().optional().describe('Optional greeting message'),
+ message: z
+ .string()
+ .optional()
+ .describe('Optional greeting message'),
}),
} satisfies AiConfig;
```
@@ -134,11 +144,16 @@ This guide covers common issues you might encounter when working with CommandKit
parameters: z.object({
userId: z
.string()
- .describe('Discord user ID (numbers only, like 123456789012345678)'),
+ .describe(
+ 'Discord user ID (numbers only, like 123456789012345678)',
+ ),
duration: z
.number()
.describe('Duration in minutes (e.g., 30 for 30 minutes)'),
- reason: z.string().optional().describe('Optional reason for the action'),
+ reason: z
+ .string()
+ .optional()
+ .describe('Optional reason for the action'),
});
```
@@ -336,7 +351,9 @@ if (process.env.NODE_ENV === 'production') {
});
// Send generic error to user
- await message.reply('An error occurred. Please try again later.');
+ await message.reply(
+ 'An error occurred. Please try again later.',
+ );
},
});
}
@@ -371,7 +388,10 @@ export const ai: AiCommand = async (ctx) => {
messageFilter: async (commandkit, message) => {
// Check if bot can send messages in the channel
if (!message.channel.isSendable()) {
- console.log('Cannot send messages in channel:', message.channelId);
+ console.log(
+ 'Cannot send messages in channel:',
+ message.channelId,
+ );
return false;
}
diff --git a/apps/website/docs/guide.old/99-helper-functions/01-after.mdx b/apps/website/docs/guide.old/99-helper-functions/01-after.mdx
new file mode 100644
index 00000000..137e8d64
--- /dev/null
+++ b/apps/website/docs/guide.old/99-helper-functions/01-after.mdx
@@ -0,0 +1,33 @@
+---
+title: after Function
+description:
+ The after function is a utility function that allows you to execute
+ a callback after current command has been executed. This is useful
+ for performing actions that should occur after the command has
+ completed, such as logging or cleanup tasks.
+---
+
+The `after()` function is a utility function that allows you to
+execute a callback after the current command has been executed. This
+is useful for performing actions that should occur after the command
+has completed, such as logging or cleanup tasks. It is guaranteed to
+be called after the command has been executed, regardless of whether
+the command was successful or not.
+
+## Usage
+
+```ts
+import { after, ChatInputCommand } from 'commandkit';
+
+export const chatInput: ChatInputCommand = async (ctx) => {
+ after(() => {
+ // This code will be executed after the command has been executed
+ console.log('Command has been executed');
+ });
+
+ await ctx.interaction.reply({
+ content: 'Hello World',
+ ephemeral: true,
+ });
+};
+```
diff --git a/apps/website/docs/guide/99-helper-functions/02-stopEvents.mdx b/apps/website/docs/guide.old/99-helper-functions/02-stopEvents.mdx
similarity index 51%
rename from apps/website/docs/guide/99-helper-functions/02-stopEvents.mdx
rename to apps/website/docs/guide.old/99-helper-functions/02-stopEvents.mdx
index 0f53098b..c801aff0 100644
--- a/apps/website/docs/guide/99-helper-functions/02-stopEvents.mdx
+++ b/apps/website/docs/guide.old/99-helper-functions/02-stopEvents.mdx
@@ -1,10 +1,15 @@
---
title: stopEvents Function
-description: The stopEvents function is a utility function that allows you to stop the execution of events chain in CommandKit.
+description:
+ The stopEvents function is a utility function that allows you to
+ stop the execution of events chain in CommandKit.
---
-The `stopEvents()` function is a utility function that allows you to stop the execution of the events chain in CommandKit. This is useful when you want to prevent further event handlers from being executed after a certain point.
-It is typically used in the context of event handlers to control the flow of execution.
+The `stopEvents()` function is a utility function that allows you to
+stop the execution of the events chain in CommandKit. This is useful
+when you want to prevent further event handlers from being executed
+after a certain point. It is typically used in the context of event
+handlers to control the flow of execution.
## Usage
@@ -21,6 +26,5 @@ export default async function onMessageCreate(message: Message) {
}
```
-:::info
-You must not call `stopEvents()` in try-catch block, otherwise it will not work as expected.
-:::
+:::info You must not call `stopEvents()` in try-catch block, otherwise
+it will not work as expected. :::
diff --git a/apps/website/docs/guide/01-getting-started/01-introduction.mdx b/apps/website/docs/guide/01-getting-started/01-introduction.mdx
index f6bae71d..dbe26883 100644
--- a/apps/website/docs/guide/01-getting-started/01-introduction.mdx
+++ b/apps/website/docs/guide/01-getting-started/01-introduction.mdx
@@ -27,44 +27,50 @@ description: A brief intro to CommandKit
-CommandKit is a powerful meta-framework for building Discord bots with [discord.js](https://discord.js.org/). It provides a simple and intuitive API for creating commands, handling interactions, and managing events. With CommandKit, you can focus on building your bot's features without worrying about the underlying complexities.
+CommandKit is a powerful meta-framework for building Discord
+applications with [discord.js](https://discord.js.org/). It provides a
+simple and intuitive API for creating commands, handling interactions,
+and managing events. With CommandKit, you can focus on building your
+Discord application without worrying about the underlying
+complexities.
-## Key Features
+## Key features
- Beginner friendly 🚀
- Suitable for both beginners and advanced users 👶👨💻
-- Slash + context menu commands + prefix commands support ✅
+- Slash + context menu commands + message commands support ✅
- Automatic command registration and updates 🤖
- Command middlewares for easy command management 🛠️
-- Localization support through `@commandkit/i18n` plugin 🌍
-- Plugin system to extend functionality 🔌
+- Localization support through
+ [`@commandkit/i18n`](../05-official-plugins/05-commandkit-i18n.mdx)
+ plugin 🌍
+- Powerful plugin system to extend functionality 🔌
- Built-in command line interface for easy development 🖥️
- Out-of-the-box support for TypeScript and JavaScript 📜
-- Built-in customizable cache system for speedy data storage and retrieval 🗄️
+- Customizable caching using the
+ [`@commandkit/cache`](../05-official-plugins/03-commandkit-cache.mdx)
+ plugin for speedy data fetching 🗄️
- User installable/guild scoped commands 🔧
- Custom events support 🔔
-- JSX support for declaring Discord interaction components and modals 🎨
-- Easy to use interaction components and modals system (forget about collectors) 🧩
+- JSX support for easily managing Discord components 🎨
+- Easy to use interaction components (forget about collectors) 🧩
- Less boilerplate code, more productivity 💪
-- and much more...
+- ...and much more!
-## Documentation
+## Support and suggestions
-You can start with the [setting up CommandKit](./02-setup-commandkit.mdx) guide to get your bot up and running quickly. The documentation covers everything from installation to advanced features.
-
-## Support and Suggestions
-
-Submit any queries or suggestions in our [Discord community](https://ctrl.lol/discord).
+- [GitHub repository](https://github.com/underctrl-io/commandkit)
+- [Discord community](https://ctrl.lol/discord)
diff --git a/apps/website/docs/guide/01-getting-started/02-setup-commandkit.mdx b/apps/website/docs/guide/01-getting-started/02-setup-commandkit.mdx
index f18564ec..61385b82 100644
--- a/apps/website/docs/guide/01-getting-started/02-setup-commandkit.mdx
+++ b/apps/website/docs/guide/01-getting-started/02-setup-commandkit.mdx
@@ -1,19 +1,34 @@
---
title: Setup CommandKit
-description: Setup a new CommandKit project manually, or using the create-commandkit CLI
+description:
+ Setup a new CommandKit project using the create-commandkit CLI
---
-You can quickly setup a new CommandKit project using `create-commandkit` — a command-line utility used for creating new discord.js applications with CommandKit. To get started, run the following command:
+:::info
+
+- CommandKit requires at least [Node.js](https://nodejs.org/) v24
+- This documentation assumes that you have a basic understanding of
+ how to use [discord.js](https://discord.js.org/). If you are new to
+ discord.js, we recommend you to read the
+ [discord.js guide](https://discordjs.guide/) first.
+
+:::
+
+The quickest way to setup a new CommandKit project is to use the
+`create-commandkit` CLI. To get started, run the following command in
+your terminal:
```sh npm2yarn
-npx create-commandkit@latest
+npm create commandkit@next
```
-This will start the CLI in the current directory which will help you quickly setup a base CommandKit project.
+This will start the CLI in an interactive mode. You can follow the
+prompts to setup your project.
## Project structure
-By using the CLI to create a base project, you should get a project structure that looks like this:
+By using the CLI to create a base project, you should get a file tree
+that looks something like this:
```
.
@@ -32,41 +47,46 @@ By using the CLI to create a base project, you should get a project structure th
└── tsconfig.json
```
-:::info
-The `src/app.ts` file is the main entry point for your application. This file default exports the discord.js client which CommandKit loads at runtime. For example, the `src/app.ts` file looks like this:
+## Entry point
+
+The `src/app.ts` file is the main entry point for your application.
+This file default exports the discord.js client instance which
+CommandKit loads at runtime.
-```ts
+```ts title="src/app.ts"
import { Client } from 'discord.js';
const client = new Client({
- intents: [
- /* add stuff */
- ],
+ intents: ['Guilds', 'GuildMessages', 'MessageContent'],
});
-// setting up the token manually
+// Optional: Setting up the token manually
client.token = process.env.MY_BOT_TOKEN;
export default client;
```
-Notice how we are not calling `client.login()` in this file. This is because CommandKit will automatically call `client.login()` for you when the application starts.
+Notice how there's no `client.login()` in this file. This is because
+CommandKit will automatically handle the login process for you when
+the application starts.
-Also, you might have noticed that we are not using anything from CommandKit in this file. This is because CommandKit is designed to reduce boilerplate code and make it easier to build discord bot applications.
-:::
+## Development version
-:::info
-The `src/app` directory is a special directory that CommandKit understands. All the commands and events in this directory will be automatically registered when the application starts.
-:::
+If you would like to try the latest development builds, you can use
+the `@dev` tag like so:
-## Development version
+```sh npm2yarn
+npm create commandkit@dev
+```
:::warning
+
The development version is likely to have bugs.
+
:::
-If you'd like to try the latest development builds, you can use the `@dev` tag like so:
+## Manual setup
-```sh npm2yarn
-npx create-commandkit@dev
-```
+Alternatively, if you would like to setup a new CommandKit project
+manually, you can follow
+[this guide](../08-advanced/01-setup-commandkit-manually.mdx).
diff --git a/apps/website/docs/guide/01-getting-started/03-setup-commandkit-manually.mdx b/apps/website/docs/guide/01-getting-started/03-setup-commandkit-manually.mdx
deleted file mode 100644
index 710637f6..00000000
--- a/apps/website/docs/guide/01-getting-started/03-setup-commandkit-manually.mdx
+++ /dev/null
@@ -1,125 +0,0 @@
----
-title: Setup CommandKit manually
-description: Learn how to install CommandKit.
----
-
-import Tabs from '@theme/Tabs';
-import TabItem from '@theme/TabItem';
-
-If you don't want to use the CLI to automatically generate a base CommandKit project, you can integrate CommandKit manually into your application.
-
-:::info
-While this guide primarily uses TypeScript, you can alternatively use JavaScript files to follow along if that's what you're comfortable with (e.g. `commandkit.config.js`, `app.js`, etc)
-:::
-
-## Configuration file
-
-To get started, create a file called `commandkit.config.ts` at the root of your project. This file is used to configure CommandKit and its plugins.
-
-```ts title="commandkit.config.ts"
-import { defineConfig } from 'commandkit';
-
-export default defineConfig({});
-```
-
-## Entrypoint file
-
-Then, create a folder called `src`, followed by a file called `app.ts` inside it. The file should look like this:
-
-```ts title="src/app.ts"
-import { Client } from 'discord.js';
-
-const client = new Client({
- intents: ['Guilds', 'GuildMembers'],
-});
-
-client.token = '...'; // Optional: You can manually set your bot's token
-
-// must export the `client` here
-export default client;
-```
-
-With the current entrypoint file created, it's important to understand that:
-
-1. The `app.ts` file is the entrypoint for this application. This file **must** export your discord.js client instance.
-2. You don't have to call `client.login()` as CommandKit will handle that for you.
-3. You should store your bot token as an environment variable with either `DISCORD_TOKEN` or `TOKEN` as the name.
-
-## Adding commands
-
-To add a command, create a folder inside the `src/app` directory called `commands` and create your command file (e.g. `ping.ts`). This example will use a simple ping/pong command which will register as a chat input (slash) and message (legacy) command.
-
-```ts title="src/app/commands/ping.ts"
-import type { ChatInputCommand, CommandData, MessageCommand } from 'commandkit';
-
-export const command: CommandData = {
- name: 'ping',
- description: 'Pong!',
-};
-
-export const chatInput: ChatInputCommand = async ({ interaction }) => {
- await interaction.reply('Pong!');
-};
-
-export const message: MessageCommand = async ({ message }) => {
- await message.reply('Pong!');
-};
-```
-
-This command will reply with "Pong!" when the user runs the `/ping` slash command, or sends `!ping` in the chat.
-
-:::tip
-Prefixes for your message (legacy) commands can be changed. Learn more [here](/docs/next/guide/resolve-message-commands-prefix).
-:::
-
-## Adding events
-
-To register and handle events emitted by discord.js, create a folder inside the `src/app` directory called `events` and create a folder with the name of the discord.js event you'd like to handle (e.g. ready, messageCreate, etc). This example will use the `ready` event.
-
-In the `src/app/events/{eventName}` directory, you can create files which will export default functions that will be called when the respective event is emitted by discord.js. Following the `ready` event example mentioned above, you may want to log when your bot comes online. The function for that will look like so:
-
-```ts title="src/app/events/log.ts"
-import type { Client } from 'discord.js';
-
-export default function (client: Client) {
- console.log(`Logged in as ${client.user.username}!`);
-}
-```
-
-## Running the app in development
-
-To run your application in development mode, you can use the `commandkit dev` command in your terminal. This will automatically reload the bot when you make changes to your code.
-
-```sh npm2yarn
-npx commandkit dev
-```
-
-:::warning
-When running in development mode, CommandKit will generate a `.commandkit` folder in the root of your project. This folder contains the compiled files used in development mode. You should not commit this folder to your version control system by making sure you add it to your `.gitignore` file.
-:::
-
-## Building for production
-
-When you are ready to deploy your bot, you can use `commandkit build` to create a production build of your bot. This will create a `dist` folder in your project directory containing the compiled files.
-
-```sh
-npx commandkit build
-```
-
-:::warning
-After running your build script, CommandKit will generate a `dist` folder in the root of your project. This folder contains the compiled files used for production. You should not commit this folder to your version control system by making sure you add it to your `.gitignore` file.
-:::
-
-## Running the app in production
-
-You can use `commandkit start` to start the bot in production mode. This will load the environment variables from the `.env` file in the root of your project directory.
-
-```sh
-npx commandkit start
-```
-
-If you want to manually start the bot in production mode, you can alternatively use the following command:
-
-```sh
-node dist/index.js
-```
diff --git a/apps/website/docs/guide/02-commands/01-chat-input-commands.mdx b/apps/website/docs/guide/02-commands/01-chat-input-commands.mdx
new file mode 100644
index 00000000..674a984b
--- /dev/null
+++ b/apps/website/docs/guide/02-commands/01-chat-input-commands.mdx
@@ -0,0 +1,67 @@
+---
+title: Chat Input Commands
+---
+
+Chat input commands, more commonly known as 'Slash Commands', are a
+way for users on Discord to perform actions using your bot.
+
+CommandKit provides a very simple way to manage your chat input
+commands, including registering them to the Discord API, as well as
+handling their execution.
+
+To create a new chat input command, create a new file in the
+`src/app/commands` directory with the name of the command. This guide
+will create a basic ping command which will respond with "Pong!" when
+executed.
+
+```ts title="src/app/commands/ping.ts"
+import type { CommandData, ChatInputCommand } from 'commandkit';
+
+export const command: CommandData = {
+ name: 'ping',
+ description: 'Replies with Pong!',
+};
+
+export const chatInput: ChatInputCommand = async (ctx) => {
+ await ctx.interaction.reply('Pong!');
+};
+```
+
+## Exports explained
+
+### `command`
+
+This is the command data object which defines the shape of your
+command. This is directly registered to the Discord API everytime your
+commands are refreshed.
+
+### `chatInput`
+
+This is the main handler function for your chat input command. By
+exporting this function, CommandKit will know that it's a chat input
+command and will register and handle it accordingly.
+
+:::tip
+
+Chat input commands are just one of the command types that CommandKit
+supports. Learn more about
+[message commands](./04-message-commands.mdx) and
+[context menu commands](./03-context-menu-commands.mdx).
+
+:::
+
+## Guild-based commands
+
+You can register your chat input command to specific guilds by using
+the `guilds` property in the `command` object which accepts an array
+of guild IDs.
+
+```ts title="src/app/commands/ping.ts" {6}
+import type { CommandData } from 'commandkit';
+
+export const command: CommandData = {
+ name: 'ping',
+ description: 'Replies with Pong!',
+ guilds: ['1055188344188973066'],
+};
+```
diff --git a/apps/website/docs/guide/02-commands/02-command-options-autocomplete.mdx b/apps/website/docs/guide/02-commands/02-command-options-autocomplete.mdx
new file mode 100644
index 00000000..7768b225
--- /dev/null
+++ b/apps/website/docs/guide/02-commands/02-command-options-autocomplete.mdx
@@ -0,0 +1,101 @@
+---
+title: Options Autocomplete
+---
+
+:::warning
+
+This feature is only available for
+[chat input commands](./01-chat-input-commands.mdx).
+
+:::
+
+In your chat input command options, autocomplete allows users on
+Discord to see a list of suggested choices based on what they're
+actively typing.
+
+## Managing autocomplete choices
+
+In addition to registering your application commands, CommandKit can
+also handle any options you mark as 'autocomplete'. This means that
+CommandKit will automatically identify which command the autocomplete
+interaction belongs to. This will save you the hassle of registering
+and managing your own `interactionCreate` event listener.
+
+To begin, export the `autocomplete` function from the command where
+you have an option with `autocomplete` set to `true`.
+
+```ts title="src/app/commands/pet.ts"
+import type { CommandData, AutocompleteCommand } from 'commandkit';
+import { ApplicationCommandOptionType } from 'discord.js';
+
+export const command: CommandData = {
+ name: 'pet',
+ description: 'Check in one of your pets.',
+ options: [
+ {
+ name: 'name',
+ description: 'The name of your pet',
+ type: ApplicationCommandOptionType.String,
+ autocomplete: true,
+ required: true,
+ },
+ ],
+};
+
+const pets = Array.from({ length: 20 }, (_, i) => ({
+ name: `Pet ${i + 1}`, // e.g. Pet 1, Pet 2, Pet 3, etc.
+ value: `petId_${i}`,
+}));
+
+export const autocomplete: AutocompleteCommand = async ({
+ interaction,
+}) => {
+ try {
+ // Get the input value of your autocomplete option
+ const input = interaction.options.getString('name', false);
+ if (!input) {
+ interaction.respond(pets);
+ return;
+ }
+
+ const filteredPets = pets.filter((pet) =>
+ pet.name.toLowerCase().includes(input.toLowerCase()),
+ );
+
+ // Respond with what you think the user may trying to find
+ interaction.respond(filteredPets);
+ } catch (error) {
+ console.error('Autocomplete error', error);
+ }
+};
+
+export const chatInput: ChatInputCommand = async ({
+ interaction,
+}) => {
+ const chosenPetId = interaction.options.getString('name', true);
+ const chosenPet = pets.find((pet) => pet.value === chosenPetId);
+
+ if (!chosenPet) {
+ interaction.reply('Pet not found.');
+ return;
+ }
+
+ interaction.reply(`You chose ${chosenPet.name}`);
+};
+```
+
+:::warning
+
+The Discord API only allows a maximum of 25 autocomplete choices per
+response. To ensure you don't run into errors, it's highly recommended
+to manually limit your choices response array to this amount.
+
+:::
+
+:::tip
+
+It's highly recommended to use try/catch blocks in your `autocomplete`
+function as it's very common for autocomplete responses to run into
+errors, as they may take longer due to their dynamic nature.
+
+:::
diff --git a/apps/website/docs/guide/02-commands/03-context-menu-commands.mdx b/apps/website/docs/guide/02-commands/03-context-menu-commands.mdx
new file mode 100644
index 00000000..a6a9eb53
--- /dev/null
+++ b/apps/website/docs/guide/02-commands/03-context-menu-commands.mdx
@@ -0,0 +1,80 @@
+---
+title: Context Menu Commands
+---
+
+Context menu commands allows users on Discord to run commands by
+either referencing a message or a user. Context menu commands can be
+grouped to either 'Message' or 'User' context menu commands.
+
+Alongside chat input commands, you can use CommandKit to also register
+and handle context menu commands.
+
+Context menu commands live in the same directory as your chat input
+commands. To begin, create a command file in your `src/app/commands`
+directory.
+
+This guide will create 2 simple `report-message` and `report-user`
+commands. It's not an actual reporting system, but it should make
+understanding the nature of these context menu commands easier.
+
+## Message context menu command
+
+```ts title="src/app/commands/report-message.ts"
+import type {
+ CommandData,
+ MessageContextMenuCommand,
+} from 'commandkit';
+import { MessageFlags } from 'discord.js';
+
+export const command: CommandData = {
+ name: 'report-message',
+ description: 'Report a message to moderators.',
+};
+
+export const messageContextMenu: MessageContextMenuCommand = async ({
+ interaction,
+}) => {
+ const content = interaction.targetMessage.content;
+
+ // you could add your message reporting logic here
+
+ await interaction.reply({
+ content: `You have reported the following message: ${content}`,
+ flags: MessageFlags.Ephemeral,
+ });
+};
+```
+
+By just exporting the `messageContextMenu` function from your command
+file, CommandKit will properly update your command type before
+registering it to the Discord API. You don't have to manually set a
+`type` in your `command` object, as you would with most command
+handlers.
+
+## User context menu command
+
+The logic for registering and handling user context menu commands is
+very similar to message context menu commands.
+
+```ts title="src/app/commands/report-user.ts"
+import type { CommandData, UserContextMenuCommand } from 'commandkit';
+import { MessageFlags } from 'discord.js';
+
+export const command: CommandData = {
+ name: 'report-user',
+ description: 'Report a user to moderators.',
+};
+
+export const userContextMenu: UserContextMenuCommand = async ({
+ interaction,
+}) => {
+ const userId = interaction.targetUser.id;
+
+ // you could add your user reporting logic here
+
+ await interaction.reply({
+ content: `You have reported a user with the ID: ${userId}`,
+ flags: MessageFlags.Ephemeral,
+ });
+};
+```
diff --git a/apps/website/docs/guide/02-commands/04-message-commands.mdx b/apps/website/docs/guide/02-commands/04-message-commands.mdx
new file mode 100644
index 00000000..74090009
--- /dev/null
+++ b/apps/website/docs/guide/02-commands/04-message-commands.mdx
@@ -0,0 +1,127 @@
+---
+title: Message Commands
+---
+
+Message commands, which are generally considered 'Legacy', are a way
+to run commands on Discord bots using regular messages. Before the
+introduction of
+[Slash/Chat Input Commands](./01-chat-input-commands.mdx), this was
+the primary way commands were handled. They were usually triggered by
+using a prefix set by the developer of the bot (for example: `!ping`).
+
+:::warning
+
+While CommandKit allows developers to create and handle message
+commands, it's important to note that Discord generally discourages
+message commands in favor of the more modern
+[Chat Input Commands](./01-chat-input-commands.mdx).
+
+:::
+
+## Basic setup
+
+Message commands, like all the other command types, live in your
+`src/app/commands` directory. To let CommandKit know that this is a
+message command, just export the `message` function from your command
+file.
+
+```ts title="src/app/commands/ping.ts"
+import type { CommandData, MessageCommand } from 'commandkit';
+
+export const command: CommandData = {
+ name: 'ping',
+};
+
+export const message: MessageCommand = async (ctx) => {
+ await ctx.message.reply('Pong!');
+};
+```
+
+## Message command options (arguments)
+
+Since a message command - on a technical level - is just a string, the
+idea of message command options will be different compared to
+[chat input command](./01-chat-input-commands.mdx) options. Instead of
+getting parsed data, you'll essentially be dealing with string
+'arguments'.
+
+```ts title="src/app/commands/ping.ts" {8}
+import type { CommandData, MessageCommand } from 'commandkit';
+
+export const command: CommandData = {
+ name: 'ping',
+};
+
+export const message: MessageCommand = async (ctx) => {
+ const args = ctx.message.args(); // string[]
+};
+```
+
+With the above command as an example, if a user on Discord runs
+`!ping john jack jane`, the value of `args` will equal
+`['john', 'jack', 'jane']`.
+
+## Custom prefixes
+
+The simplest way to set a custom prefix is using the
+`setPrefixResolver` method on the `commandkit` instance:
+
+```ts title="src/app.ts"
+import { commandkit } from 'commandkit';
+
+commandkit.setPrefixResolver(async (message) => {
+ return '?';
+});
+```
+
+This sets a global prefix of `?` for all message commands. Your bot
+will now respond to commands like `?help` or `?ping`.
+
+:::tip
+
+You can return an array of strings to support multiple prefixes
+simultaneously:
+
+```ts
+return ['?', '!', '>'];
+```
+
+:::
+
+### Guild-specific prefixes
+
+For a more dynamic approach, you might want different prefixes for
+different guilds. Here's how to implement that:
+
+```ts title="src/app.ts"
+import { commandkit } from 'commandkit';
+import { database } from '../database'; // This is a mock database
+
+commandkit.setPrefixResolver(async (message) => {
+ if (!message.guildId) {
+ return '!'; // Fallback to '!' if command is used in a DM
+ }
+
+ const guildSettings = await database.findUnique({
+ where: {
+ guildId: message.guildId,
+ },
+ select: {
+ prefix: true,
+ },
+ });
+
+ return guildSettings?.prefix ?? '!'; // Fallback to '!' if no prefix is found
+});
+```
+
+:::warning
+
+The `setPrefixResolver` method runs on every message, so it's
+important to make sure that it's as lightweight as possible. To help
+with performance, you may want to implement caching, which CommandKit
+also supports with the
+[`@commandkit/cache`](../05-official-plugins/03-commandkit-cache.mdx)
+official plugin.
+
+:::
diff --git a/apps/website/docs/guide/02-commands/05-after-function.mdx b/apps/website/docs/guide/02-commands/05-after-function.mdx
new file mode 100644
index 00000000..5b8233d5
--- /dev/null
+++ b/apps/website/docs/guide/02-commands/05-after-function.mdx
@@ -0,0 +1,39 @@
+---
+title: after Function
+---
+
+The `after()` function allows you to execute a callback after a
+command has been executed. This is useful for performing actions that
+should occur after the command has completed, such as logging or
+cleanup tasks.
+
+## Usage
+
+```ts title="src/app/commands/ping.ts"
+import {
+ type CommandData,
+ type ChatInputCommand,
+ after,
+} from 'commandkit';
+
+export const command: CommandData = {};
+
+export const chatInput: ChatInputCommand = async (ctx) => {
+ after(() => {
+ // This code will be executed after the command has been executed
+ console.log('Command has been executed');
+ });
+
+ await ctx.interaction.reply({
+ content: 'Hello World',
+ ephemeral: true,
+ });
+};
+```
+
+:::info
+
+The `after()` function will always be called, regardless of whether
+the command execution was successful or not.
+
+:::
diff --git a/apps/website/docs/guide/02-commands/06-category-directory.mdx b/apps/website/docs/guide/02-commands/06-category-directory.mdx
new file mode 100644
index 00000000..e727384c
--- /dev/null
+++ b/apps/website/docs/guide/02-commands/06-category-directory.mdx
@@ -0,0 +1,42 @@
+---
+title: Category Directory
+---
+
+Category directories are special directories inside
+`src/app/commands`, with `(categoryName)` as their name.
+
+It is primarily used to group related commands together. This is
+useful for organizing your commands into logical groups, making it
+easier to manage and maintain your code. You can even nest multiple
+category directories to create a hierarchy of commands. This can then
+be used across your application in places such as middlewares,
+plugins, or even other commands through your `commandkit` instance.
+
+## Example structure
+
+```title="" {4-15}
+.
+├── src/
+│ ├── app/
+│ │ ├── commands/
+│ │ │ ├── (Fun)/
+│ │ │ │ ├── (Jokes)/
+│ │ │ │ │ ├── random-joke.ts
+│ │ │ │ │ └── dad-joke.ts
+│ │ │ │ ├── 8ball.ts
+│ │ │ │ ├── cat.ts
+│ │ │ │ └── dog.ts
+│ │ │ └── (Moderation)/
+│ │ │ ├── timeout.ts
+│ │ │ ├── kick.ts
+│ │ │ └── ban.ts
+│ │ └── events/
+│ │ └── ready/
+│ │ └── log.ts
+│ └── app.ts
+├── .env
+├── .gitignore
+├── commandkit.config.ts
+├── package.json
+└── tsconfig.json
+```
diff --git a/apps/website/docs/guide/02-commands/07-middlewares.mdx b/apps/website/docs/guide/02-commands/07-middlewares.mdx
new file mode 100644
index 00000000..ff11230c
--- /dev/null
+++ b/apps/website/docs/guide/02-commands/07-middlewares.mdx
@@ -0,0 +1,95 @@
+---
+title: Middlewares
+---
+
+Middlewares in CommandKit allow you to execute code before and after
+command execution. This is incredibly powerful for implementing
+features like logging, authentication, permission checks, or any other
+cross-cutting concerns for your Discord bot.
+
+CommandKit supports three different types of middleware files, each
+serving different scoping purposes for your commands.
+
+## Basic middleware structure
+
+All middleware files follow the same export pattern. You can export
+`beforeExecute` and `afterExecute` functions that will run at their
+respective times during command execution.
+
+```ts title="src/app/commands/+middleware.ts"
+import { MiddlewareContext } from 'commandkit';
+
+export function beforeExecute(ctx: MiddlewareContext) {
+ // This function will be executed before the command is executed
+ console.log(
+ `User ${ctx.interaction.user.id} is about to execute a command`,
+ );
+}
+
+export function afterExecute(ctx: MiddlewareContext) {
+ // This function will be executed after the command is executed
+ console.log(
+ `Command execution completed for user ${ctx.interaction.user.id}`,
+ );
+}
+```
+
+## Middleware types
+
+### Directory-scoped middleware
+
+Create a `+middleware.ts` file in any commands directory to apply
+middleware to all commands within that directory and its
+subdirectories.
+
+```ts title="src/app/commands/(Moderation)/+middleware.ts"
+import { MiddlewareContext } from 'commandkit';
+
+export function beforeExecute(ctx: MiddlewareContext) {
+ // This middleware will run before any moderation command
+ if (!ctx.interaction.member.permissions.has('KickMembers')) {
+ throw new Error(
+ 'You need moderation permissions to use this command',
+ );
+ }
+}
+```
+
+### Command-specific middleware
+
+For command-specific middleware, create a file named
+`+.middleware.ts` where `` matches your
+command file name.
+
+```ts title="src/app/commands/+ban.middleware.ts"
+import { MiddlewareContext } from 'commandkit';
+
+export function beforeExecute(ctx: MiddlewareContext) {
+ // This middleware only runs before the ban command
+ console.log('Ban command is about to be executed');
+}
+```
+
+### Global middleware
+
+Create a `+global-middleware.ts` file in your commands directory to
+apply middleware to every command in your entire Discord bot.
+
+```ts title="src/app/commands/+global-middleware.ts"
+import { MiddlewareContext } from 'commandkit';
+
+export function beforeExecute(ctx: MiddlewareContext) {
+ // This middleware runs before ANY command in your bot
+ console.log(
+ `Command executed by ${ctx.interaction.user.tag} in ${ctx.interaction.guild?.name || 'DMs'}`,
+ );
+}
+```
+
+:::tip
+
+Middleware execution (both before and after) follows a hierarchy:
+directory-scoped middleware runs first, then command-specific
+middleware, and finally global middleware.
+
+:::
diff --git a/apps/website/docs/guide/03-events/01-discordjs-events.mdx b/apps/website/docs/guide/03-events/01-discordjs-events.mdx
new file mode 100644
index 00000000..8ad4131c
--- /dev/null
+++ b/apps/website/docs/guide/03-events/01-discordjs-events.mdx
@@ -0,0 +1,162 @@
+---
+title: Discord.js Events
+---
+
+Events allow your Discord bot to react to various actions happening on
+Discord, such as when users send messages, join guilds, or when your
+bot comes online.
+
+CommandKit provides a simple way to organize and handle Discord.js
+events using the `events` directory inside your `src/app` directory.
+This approach makes it easy to manage multiple events and keep your
+code organized.
+
+## Basic event directory structure
+
+Each Discord.js event gets its own directory within `src/app/events`,
+and the directory name must match the exact Discord.js event name.
+Inside each event directory, you can create multiple handler files
+that will all execute when that event is triggered.
+
+Here's how your project structure might look with the
+`"messageCreate"` and `"ready"` events:
+
+```title="" {4-9}
+.
+├── src/
+│ ├── app/
+│ │ └── events/
+│ │ ├── messageCreate/
+│ │ │ ├── give-xp.ts
+│ │ │ └── log-message.ts
+│ │ └── ready/
+│ │ └── log.ts
+│ └── app.ts
+├── .env
+├── .gitignore
+├── commandkit.config.ts
+├── package.json
+└── tsconfig.json
+```
+
+:::tip
+
+You can see what events are available in Discord.js by checking the
+[Discord.js documentation](https://discord.js.org/docs/packages/discord.js/main/ClientEventTypes:Interface).
+
+:::
+
+## Creating an event handler
+
+To create an event handler, create a folder inside the
+`src/app/events` directory which should match the event name from
+Discord.js. This example will use the `"ready"` event.
+
+```ts title="src/app/events/ready/log.ts"
+import type { Client } from 'commandkit';
+
+export default function (client: Client) {
+ console.log(`🤖 ${client.user.displayName} is online!`);
+}
+```
+
+That's it! CommandKit will automatically detect this file and register
+it as one of the handler functions for the `"ready"` event.
+
+### Event parameters
+
+Just like the Discord.js `client.on()` method, the parameters you
+receive in your event file will be based on what event you're trying
+to handle. In the example above, the `"ready"` event should give you a
+[`Client`](https://discord.js.org/docs/packages/discord.js/main/Client:Class)
+instance.
+
+If you were to, for example, handle the `"messageCreate"` event, you
+would get a
+[`Message`](https://discord.js.org/docs/packages/discord.js/main/Message:Class)
+instance.
+
+### Events with multiple parameters
+
+Some Discord.js events have multiple parameters, such as the
+`"messageUpdate"` event.
+
+```ts title="src/app/events/messageUpdate/log-message-update.ts"
+import type { Message } from 'discord.js';
+
+export default function (oldMessage: Message, newMessage: Message) {
+ console.log(
+ `Message from ${oldMessage.author.username} updated: ${oldMessage.content} -> ${newMessage.content}`,
+ );
+}
+```
+
+### Additional parameters
+
+Outside of the parameters provided by Discord.js, CommandKit will pass
+additional parameters to the event handler, including a
+[`Client`](https://discord.js.org/docs/packages/discord.js/main/Client:Class)
+and
+[`CommandKit`](../../api-reference/commandkit/classes/command-kit.mdx)
+instance.
+
+```ts title="src/app/events/messageCreate/log.ts"
+import type { CommandKit } from 'commandkit';
+import type { Message } from 'discord.js';
+
+export default function (
+ message: Message,
+ client: Client,
+ commandkit: CommandKit,
+) {
+ console.log(
+ `Message from ${message.author.username}: ${message.content}`,
+ );
+}
+```
+
+## Multiple handlers for the same event
+
+One of the powerful features of CommandKit's event system is the
+ability to have multiple handlers for the same event. Each handler
+file you create in an event directory will be executed when that event
+is called.
+
+For example, you might want to do several things when a message is
+created:
+
+```ts title="src/app/events/messageCreate/give-xp.ts"
+import type { MessageCreateEvent } from 'commandkit';
+
+export default function ({ message }: MessageCreateEvent) {
+ // Don't give XP to bots
+ if (message.author.bot) return;
+
+ // Give XP to the user
+ console.log(`Giving XP to ${message.author.username}`);
+}
+```
+
+```ts title="src/app/events/messageCreate/log-message.ts"
+import type { MessageCreateEvent } from 'commandkit';
+
+export default function ({ message }: MessageCreateEvent) {
+ console.log(
+ `Message from ${message.author.username}: ${message.content}`,
+ );
+}
+```
+
+Both handler functions will be called whenever a message is sent in
+Discord.
+
+:::tip
+
+Event handler files can be named anything you want - CommandKit
+identifies them by their location in the event directory, not by their
+filename. However, since each file is treated as a handler function,
+they're executed one-by-one in the order of their file name.
+
+Example: `01-give-xp.ts` will be executed before `02-log-message.ts`.
+
+:::
diff --git a/apps/website/docs/guide/03-events/02-custom-events.mdx b/apps/website/docs/guide/03-events/02-custom-events.mdx
new file mode 100644
index 00000000..48094dd5
--- /dev/null
+++ b/apps/website/docs/guide/03-events/02-custom-events.mdx
@@ -0,0 +1,44 @@
+---
+title: Custom Events
+---
+
+CommandKit also supports using custom events in the `events`
+directory. Custom events must be defined in a namespaced directory,
+which are primarily handled by plugins.
+
+For example, if you have a plugin that is handling a custom event
+called `"guildMemberBoost"` under a namespace called `"custom"`, you
+can create a file in the `src/app/events/(custom)/guildMemberBoost`
+directory which will be handled by the appropriate plugin.
+
+:::warning
+
+The `"custom"` namespace above is just an example. Namespaces will
+generally be set by plugins, so make sure to check the plugin's
+documentation for the correct one.
+
+:::
+
+:::danger
+
+Event namespace directories are **not** the same as
+[command categories](../02-commands/06-category-directory.mdx).
+
+:::
+
+## How plugins execute namespaced events
+
+If you're developing a plugin, you can trigger a custom event in your
+application. Here's an example of how you can trigger a custom
+`guildMemberBoost` event under the `custom` namespace.
+
+```ts
+commandkit.events.to('custom').emit('guildMemberBoost', {
+ member: member,
+ guild: guild,
+});
+```
+
+This will ensure every file inside the
+`src/app/events/(custom)/guildMemberBoost` directory is executed just
+like regular [Discord.js events](./01-discordjs-events.mdx)
diff --git a/apps/website/docs/guide/03-events/03-stopevents-function.mdx b/apps/website/docs/guide/03-events/03-stopevents-function.mdx
new file mode 100644
index 00000000..4bb41518
--- /dev/null
+++ b/apps/website/docs/guide/03-events/03-stopevents-function.mdx
@@ -0,0 +1,31 @@
+---
+title: stopEvents Function
+---
+
+The `stopEvents()` function is a utility function that allows you to
+stop the execution of the events chain in CommandKit. This is useful
+when you want to prevent further event handlers from being executed
+after a certain point. It is typically used in the context of event
+handlers to control the flow of execution.
+
+## Usage
+
+```ts title="src/app/events/messageCreate/event.ts"
+import type { Message } from 'discord.js';
+import { stopEvents } from 'commandkit';
+
+export default async function (message: Message) {
+ console.log('Message received:', message.content);
+
+ // Stop further event handlers of messageCreate from being executed after this point
+ stopEvents();
+ // code below will not be executed
+}
+```
+
+:::warning
+
+Calling `stopEvents()` in a try/catch block may lead to unexpected
+behavior.
+
+:::
diff --git a/apps/website/docs/guide/04-jsx-components/01-using-jsx.mdx b/apps/website/docs/guide/04-jsx-components/01-using-jsx.mdx
new file mode 100644
index 00000000..cd59bb5c
--- /dev/null
+++ b/apps/website/docs/guide/04-jsx-components/01-using-jsx.mdx
@@ -0,0 +1,67 @@
+---
+title: Using JSX
+---
+
+CommandKit provides first-class JSX support for both TypeScript and
+JavaScript projects. This allows you to write Discord message
+components using a familiar and intuitive syntax if you're already
+familiar with React.
+
+## Setup
+
+CommandKit automatically configures JSX support in your project. If
+you are doing a manual setup, ensure you have the following in your
+`tsconfig.json` or `jsconfig.json`:
+
+```json
+{
+ "compilerOptions": {
+ "jsx": "react-jsx",
+ "jsxImportSource": "commandkit"
+ }
+}
+```
+
+## Available components
+
+CommandKit provides JSX versions for the following Discord.js
+builders:
+
+- [`ActionRow`](./02-discord-components-v1/01-action-row.mdx)
+- [`Button`](./02-discord-components-v1/02-button.mdx)
+- [`SelectMenu`](./02-discord-components-v1/03-select-menu.mdx)
+- [`Modal`](./02-discord-components-v1/04-modal.mdx)
+- [`TextDisplay`](./03-discord-components-v2/01-text-display.mdx)
+- [`Container`](./03-discord-components-v2/02-container.mdx)
+- [`MediaGallery`](./03-discord-components-v2/03-media-gallery.mdx)
+- [`Separator`](./03-discord-components-v2/04-separator.mdx)
+- [`File`](./03-discord-components-v2/05-file.mdx)
+
+## Example usage
+
+```tsx
+import { Button, ActionRow } from 'commandkit';
+
+const message = (
+
+ Welcome to our server!
+
+
+
+
+);
+```
+
+## JSX fragments
+
+CommandKit fully supports JSX fragments for grouping multiple elements
+without adding extra nodes:
+
+```tsx
+const elements = (
+ <>
+ First message
+ Second message
+ >
+);
+```
diff --git a/apps/website/docs/guide/04-jsx-components/02-discord-components-v1/01-action-row.mdx b/apps/website/docs/guide/04-jsx-components/02-discord-components-v1/01-action-row.mdx
new file mode 100644
index 00000000..24b0a17a
--- /dev/null
+++ b/apps/website/docs/guide/04-jsx-components/02-discord-components-v1/01-action-row.mdx
@@ -0,0 +1,91 @@
+---
+title: Action Row
+---
+
+The `ActionRow` component is a container that groups interactive
+components like buttons and select menus together in a single row.
+
+## Basic usage
+
+```tsx title="src/app/commands/example.tsx"
+import type { ChatInputCommand } from 'commandkit';
+import { ActionRow, Button } from 'commandkit';
+
+export const chatInput: ChatInputCommand = async ({
+ interaction,
+}) => {
+ const row = (
+
+
+
+
+ );
+
+ await interaction.reply({
+ content: 'Choose an option:',
+ components: [row],
+ });
+};
+```
+
+## With select menus
+
+```tsx title="src/app/commands/select-example.tsx"
+import type { ChatInputCommand } from 'commandkit';
+import {
+ ActionRow,
+ StringSelectMenu,
+ StringSelectMenuOption,
+} from 'commandkit';
+
+export const chatInput: ChatInputCommand = async ({
+ interaction,
+}) => {
+ const row = (
+
+
+
+
+
+
+ );
+
+ await interaction.reply({
+ content: 'Select from the dropdown:',
+ components: [row],
+ });
+};
+```
+
+## Multiple action rows
+
+You can use multiple ActionRows in a single message to organize your
+components:
+
+```tsx title="src/app/commands/multiple-rows.tsx"
+import type { ChatInputCommand } from 'commandkit';
+import { ActionRow, Button } from 'commandkit';
+
+export const chatInput: ChatInputCommand = async ({
+ interaction,
+}) => {
+ const firstRow = (
+
+
+
+
+ );
+
+ const secondRow = (
+
+
+
+
+ );
+
+ await interaction.reply({
+ content: 'Multiple action rows:',
+ components: [firstRow, secondRow],
+ });
+};
+```
diff --git a/apps/website/docs/guide/04-jsx-components/02-discord-components-v1/02-button.mdx b/apps/website/docs/guide/04-jsx-components/02-discord-components-v1/02-button.mdx
new file mode 100644
index 00000000..3fe3502f
--- /dev/null
+++ b/apps/website/docs/guide/04-jsx-components/02-discord-components-v1/02-button.mdx
@@ -0,0 +1,170 @@
+---
+title: Button
+---
+
+The `Button` component allows you to create interactive buttons in
+your Discord messages. Buttons can have different styles, labels, and
+emojis.
+
+## Basic usage
+
+```tsx title="src/app/commands/button-example.tsx"
+import type { ChatInputCommand } from 'commandkit';
+import { ActionRow, Button } from 'commandkit';
+
+export const chatInput: ChatInputCommand = async ({
+ interaction,
+}) => {
+ const button = (
+
+
+
+ );
+
+ await interaction.reply({
+ content: "Here's a button:",
+ components: [button],
+ });
+};
+```
+
+## Button styles
+
+Discord provides several built-in button styles:
+
+```tsx title="src/app/commands/button-styles.tsx"
+import type { ChatInputCommand } from 'commandkit';
+import { ActionRow, Button } from 'commandkit';
+
+export const chatInput: ChatInputCommand = async ({
+ interaction,
+}) => {
+ const buttons = (
+
+
+
+
+
+
+ );
+
+ await interaction.reply({
+ content: 'Different button styles:',
+ components: [buttons],
+ });
+};
+```
+
+## Link buttons
+
+Link buttons redirect users to external URLs:
+
+```tsx title="src/app/commands/link-button.tsx"
+import type { ChatInputCommand } from 'commandkit';
+import { ActionRow, Button } from 'commandkit';
+
+export const chatInput: ChatInputCommand = async ({
+ interaction,
+}) => {
+ const linkButton = (
+
+
+
+ );
+
+ await interaction.reply({
+ content: 'Click to visit Discord:',
+ components: [linkButton],
+ });
+};
+```
+
+## Buttons with emojis
+
+Add emojis to make your buttons more visually appealing:
+
+```tsx title="src/app/commands/emoji-button.tsx"
+import type { ChatInputCommand } from 'commandkit';
+import { ActionRow, Button } from 'commandkit';
+
+export const chatInput: ChatInputCommand = async ({
+ interaction,
+}) => {
+ const emojiButton = (
+
+
+
+
+ );
+
+ await interaction.reply({
+ content: 'Buttons with emojis:',
+ components: [emojiButton],
+ });
+};
+```
+
+## Disabled buttons
+
+Disable buttons to prevent interaction:
+
+```tsx title="src/app/commands/disabled-button.tsx"
+import type { ChatInputCommand } from 'commandkit';
+import { ActionRow, Button } from 'commandkit';
+
+export const chatInput: ChatInputCommand = async ({
+ interaction,
+}) => {
+ const disabledButton = (
+
+
+
+
+ );
+
+ await interaction.reply({
+ content: 'Disabled vs enabled buttons:',
+ components: [disabledButton],
+ });
+};
+```
+
+## Handling button clicks
+
+Handle button interactions with the `onClick` prop:
+
+```tsx title="src/app/commands/interactive-button.tsx"
+import type { ChatInputCommand, OnButtonKitClick } from 'commandkit';
+import { ActionRow, Button } from 'commandkit';
+import { MessageFlags } from 'discord.js';
+
+const handleClick: OnButtonKitClick = async (
+ interaction,
+ context,
+) => {
+ await interaction.reply({
+ content: 'Button clicked!',
+ flags: MessageFlags.Ephemeral,
+ });
+
+ // Clean up the button context
+ context.dispose();
+};
+
+export const chatInput: ChatInputCommand = async ({
+ interaction,
+}) => {
+ const interactiveButton = (
+
+
+
+ );
+
+ await interaction.reply({
+ content: 'Click the button to see it work:',
+ components: [interactiveButton],
+ });
+};
+```
diff --git a/apps/website/docs/guide/04-jsx-components/02-discord-components-v1/03-select-menu.mdx b/apps/website/docs/guide/04-jsx-components/02-discord-components-v1/03-select-menu.mdx
new file mode 100644
index 00000000..9eeb0e6e
--- /dev/null
+++ b/apps/website/docs/guide/04-jsx-components/02-discord-components-v1/03-select-menu.mdx
@@ -0,0 +1,316 @@
+---
+title: Select Menu
+---
+
+CommandKit provides several types of select menus for different use
+cases.
+
+## String select menu
+
+The most common type of select menu that allows users to select from
+predefined string options:
+
+```tsx title="src/app/commands/string-select.tsx"
+import type {
+ ChatInputCommand,
+ OnStringSelectMenuKitSubmit,
+} from 'commandkit';
+import {
+ ActionRow,
+ StringSelectMenu,
+ StringSelectMenuOption,
+} from 'commandkit';
+import { MessageFlags } from 'discord.js';
+
+const handleSelect: OnStringSelectMenuKitSubmit = async (
+ interaction,
+ context,
+) => {
+ const selection = interaction.values[0];
+ await interaction.reply({
+ content: `You selected: ${selection}`,
+ flags: MessageFlags.Ephemeral,
+ });
+ context.dispose();
+};
+
+export const chatInput: ChatInputCommand = async ({
+ interaction,
+}) => {
+ const selectMenu = (
+
+
+
+
+
+
+
+ );
+
+ await interaction.reply({
+ content: 'Please make a selection:',
+ components: [selectMenu],
+ });
+};
+```
+
+## Channel select menu
+
+Allows users to select a channel from the server:
+
+```tsx title="src/app/commands/channel-select.tsx"
+import type {
+ ChatInputCommand,
+ OnChannelSelectMenuKitSubmit,
+} from 'commandkit';
+import { ActionRow, ChannelSelectMenu } from 'commandkit';
+import { MessageFlags } from 'discord.js';
+
+const handleSelect: OnChannelSelectMenuKitSubmit = async (
+ interaction,
+ context,
+) => {
+ const channel = interaction.values[0];
+ await interaction.reply({
+ content: `Selected channel: <#${channel}>`,
+ flags: MessageFlags.Ephemeral,
+ });
+ context.dispose();
+};
+
+export const chatInput: ChatInputCommand = async ({
+ interaction,
+}) => {
+ const selectMenu = (
+
+
+
+ );
+
+ await interaction.reply({
+ content: 'Select a channel:',
+ components: [selectMenu],
+ });
+};
+```
+
+## Role select menu
+
+Allows users to select a role from the server:
+
+```tsx title="src/app/commands/role-select.tsx"
+import type {
+ ChatInputCommand,
+ OnRoleSelectMenuKitSubmit,
+} from 'commandkit';
+import { ActionRow, RoleSelectMenu } from 'commandkit';
+import { MessageFlags } from 'discord.js';
+
+const handleSelect: OnRoleSelectMenuKitSubmit = async (
+ interaction,
+ context,
+) => {
+ const role = interaction.values[0];
+ await interaction.reply({
+ content: `Selected role: <@&${role}>`,
+ flags: MessageFlags.Ephemeral,
+ });
+ context.dispose();
+};
+
+export const chatInput: ChatInputCommand = async ({
+ interaction,
+}) => {
+ const selectMenu = (
+
+
+
+ );
+
+ await interaction.reply({
+ content: 'Select a role:',
+ components: [selectMenu],
+ });
+};
+```
+
+## User select menu
+
+Allows users to select a user from the server:
+
+```tsx title="src/app/commands/user-select.tsx"
+import type {
+ ChatInputCommand,
+ OnUserSelectMenuKitSubmit,
+} from 'commandkit';
+import { ActionRow, UserSelectMenu } from 'commandkit';
+import { MessageFlags } from 'discord.js';
+
+const handleSelect: OnUserSelectMenuKitSubmit = async (
+ interaction,
+ context,
+) => {
+ const user = interaction.values[0];
+ await interaction.reply({
+ content: `Selected user: <@${user}>`,
+ flags: MessageFlags.Ephemeral,
+ });
+ context.dispose();
+};
+
+export const chatInput: ChatInputCommand = async ({
+ interaction,
+}) => {
+ const selectMenu = (
+
+
+
+ );
+
+ await interaction.reply({
+ content: 'Select a user:',
+ components: [selectMenu],
+ });
+};
+```
+
+## Mentionable select menu
+
+Allows users to select a user or role from the server:
+
+```tsx title="src/app/commands/mentionable-select.tsx"
+import type {
+ ChatInputCommand,
+ OnMentionableSelectMenuKitSubmit,
+} from 'commandkit';
+import { ActionRow, MentionableSelectMenu } from 'commandkit';
+import { MessageFlags } from 'discord.js';
+
+const handleSelect: OnMentionableSelectMenuKitSubmit = async (
+ interaction,
+ context,
+) => {
+ const mentionable = interaction.values[0];
+ await interaction.reply({
+ content: `Selected: <@${mentionable}>`,
+ flags: MessageFlags.Ephemeral,
+ });
+ context.dispose();
+};
+
+export const chatInput: ChatInputCommand = async ({
+ interaction,
+}) => {
+ const selectMenu = (
+
+
+
+ );
+
+ await interaction.reply({
+ content: 'Select a user or role:',
+ components: [selectMenu],
+ });
+};
+```
+
+## Multiple selection
+
+You can allow multiple selections by setting the `minValues` and
+`maxValues` properties:
+
+```tsx title="src/app/commands/multi-select.tsx"
+import type {
+ ChatInputCommand,
+ OnStringSelectMenuKitSubmit,
+} from 'commandkit';
+import {
+ ActionRow,
+ StringSelectMenu,
+ StringSelectMenuOption,
+} from 'commandkit';
+import { MessageFlags } from 'discord.js';
+
+const handleSelect: OnStringSelectMenuKitSubmit = async (
+ interaction,
+ context,
+) => {
+ const selections = interaction.values;
+ await interaction.reply({
+ content: `You selected: ${selections.join(', ')}`,
+ flags: MessageFlags.Ephemeral,
+ });
+ context.dispose();
+};
+
+export const chatInput: ChatInputCommand = async ({
+ interaction,
+}) => {
+ const selectMenu = (
+
+
+
+
+
+
+
+
+ );
+
+ await interaction.reply({
+ content: 'Select your favorite foods (1-3 options):',
+ components: [selectMenu],
+ });
+};
+```
diff --git a/apps/website/docs/guide/04-jsx-components/02-discord-components-v1/04-modal.mdx b/apps/website/docs/guide/04-jsx-components/02-discord-components-v1/04-modal.mdx
new file mode 100644
index 00000000..597a4d80
--- /dev/null
+++ b/apps/website/docs/guide/04-jsx-components/02-discord-components-v1/04-modal.mdx
@@ -0,0 +1,143 @@
+---
+title: Modal
+---
+
+The `Modal` component allows you to create interactive forms that
+appear as popups in Discord.
+
+## Basic usage
+
+```tsx title="src/app/commands/user-profile.tsx"
+import type { ChatInputCommand, OnModalKitSubmit } from 'commandkit';
+import { Modal, ShortInput, ParagraphInput } from 'commandkit';
+import { MessageFlags } from 'discord.js';
+
+const handleSubmit: OnModalKitSubmit = async (
+ interaction,
+ context,
+) => {
+ const name = interaction.fields.getTextInputValue('name');
+ const description =
+ interaction.fields.getTextInputValue('description');
+
+ await interaction.reply({
+ content: `**Profile Created!**\n**Name:** ${name}\n**Description:** ${description}`,
+ flags: MessageFlags.Ephemeral,
+ });
+
+ context.dispose();
+};
+
+export const chatInput: ChatInputCommand = async ({
+ interaction,
+}) => {
+ const modal = (
+
+
+
+
+ );
+
+ await interaction.showModal(modal);
+};
+```
+
+## Input types
+
+### Short input
+
+For single-line text input:
+
+```tsx title="src/app/commands/username.tsx"
+import type { ChatInputCommand, OnModalKitSubmit } from 'commandkit';
+import { Modal, ShortInput } from 'commandkit';
+import { MessageFlags } from 'discord.js';
+
+const handleSubmit: OnModalKitSubmit = async (
+ interaction,
+ context,
+) => {
+ const username = interaction.fields.getTextInputValue('username');
+
+ await interaction.reply({
+ content: `Username set to: ${username}`,
+ flags: MessageFlags.Ephemeral,
+ });
+
+ context.dispose();
+};
+
+export const chatInput: ChatInputCommand = async ({
+ interaction,
+}) => {
+ const modal = (
+
+
+
+ );
+
+ await interaction.showModal(modal);
+};
+```
+
+### Paragraph input
+
+For multi-line text input:
+
+```tsx title="src/app/commands/feedback.tsx"
+import type { ChatInputCommand, OnModalKitSubmit } from 'commandkit';
+import { Modal, ParagraphInput } from 'commandkit';
+import { MessageFlags } from 'discord.js';
+
+const handleSubmit: OnModalKitSubmit = async (
+ interaction,
+ context,
+) => {
+ const feedback = interaction.fields.getTextInputValue('feedback');
+
+ await interaction.reply({
+ content: "Thank you for your feedback! We'll review it soon.",
+ flags: MessageFlags.Ephemeral,
+ });
+
+ // Here you could save the feedback to a database
+ console.log(`Feedback received: ${feedback}`);
+
+ context.dispose();
+};
+
+export const chatInput: ChatInputCommand = async ({
+ interaction,
+}) => {
+ const modal = (
+
+
+
+ );
+
+ await interaction.showModal(modal);
+};
+```
diff --git a/apps/website/docs/guide/04-jsx-components/03-discord-components-v2/01-text-display.mdx b/apps/website/docs/guide/04-jsx-components/03-discord-components-v2/01-text-display.mdx
new file mode 100644
index 00000000..3494ee8b
--- /dev/null
+++ b/apps/website/docs/guide/04-jsx-components/03-discord-components-v2/01-text-display.mdx
@@ -0,0 +1,25 @@
+---
+title: Text Display
+---
+
+The `TextDisplay` component is the most basic component that allows
+you to show text content in your Discord messages.
+
+## Basic usage
+
+```tsx title="src/app/commands/text-example.tsx"
+import type { ChatInputCommand } from 'commandkit';
+import { TextDisplay } from 'commandkit';
+import { MessageFlags } from 'discord.js';
+
+export const chatInput: ChatInputCommand = async ({
+ interaction,
+}) => {
+ const textComponent = ;
+
+ await interaction.reply({
+ components: [textComponent],
+ flags: MessageFlags.IsComponentsV2,
+ });
+};
+```
diff --git a/apps/website/docs/guide/04-jsx-components/03-discord-components-v2/02-container.mdx b/apps/website/docs/guide/04-jsx-components/03-discord-components-v2/02-container.mdx
new file mode 100644
index 00000000..13c7151b
--- /dev/null
+++ b/apps/website/docs/guide/04-jsx-components/03-discord-components-v2/02-container.mdx
@@ -0,0 +1,148 @@
+---
+title: Container
+---
+
+The `Container` component is a building block that allows you to
+organize and style multiple components together.
+
+## Basic usage
+
+```tsx title="src/app/commands/container-example.tsx"
+import type { ChatInputCommand } from 'commandkit';
+import { Container, TextDisplay } from 'commandkit';
+import { Colors, MessageFlags } from 'discord.js';
+
+export const chatInput: ChatInputCommand = async ({
+ interaction,
+}) => {
+ const container = (
+
+
+
+
+ );
+
+ await interaction.reply({
+ components: [container],
+ flags: MessageFlags.IsComponentsV2,
+ });
+};
+```
+
+## Container styling
+
+The `Container` component accepts an `accentColor` prop that can be
+used to customize its appearance:
+
+```tsx title="src/app/commands/styled-container.tsx"
+import type { ChatInputCommand } from 'commandkit';
+import { Container, TextDisplay } from 'commandkit';
+import { Colors, MessageFlags } from 'discord.js';
+
+export const chatInput: ChatInputCommand = async ({
+ interaction,
+}) => {
+ const containers = [
+
+
+ ,
+
+
+ ,
+
+
+ ,
+
+
+ ,
+ ];
+
+ await interaction.reply({
+ components: containers,
+ flags: MessageFlags.IsComponentsV2,
+ });
+};
+```
+
+## Nesting components
+
+You can nest various components inside a Container:
+
+```tsx title="src/app/commands/nested-container.tsx"
+import type { ChatInputCommand } from 'commandkit';
+import { Container, TextDisplay, Separator } from 'commandkit';
+import {
+ Colors,
+ MessageFlags,
+ SeparatorSpacingSize,
+} from 'discord.js';
+
+export const chatInput: ChatInputCommand = async ({
+ interaction,
+}) => {
+ const container = (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+
+ await interaction.reply({
+ components: [container],
+ flags: MessageFlags.IsComponentsV2,
+ });
+};
+```
+
+## Multiple containers
+
+You can use multiple containers to organize different sections:
+
+```tsx title="src/app/commands/multi-container.tsx"
+import type { ChatInputCommand } from 'commandkit';
+import { Container, TextDisplay } from 'commandkit';
+import { Colors, MessageFlags } from 'discord.js';
+
+export const chatInput: ChatInputCommand = async ({
+ interaction,
+}) => {
+ const headerContainer = (
+
+
+
+
+ );
+
+ const statsContainer = (
+
+
+
+
+
+
+ );
+
+ const alertsContainer = (
+
+
+
+
+
+ );
+
+ await interaction.reply({
+ components: [headerContainer, statsContainer, alertsContainer],
+ flags: MessageFlags.IsComponentsV2,
+ });
+};
+```
diff --git a/apps/website/docs/guide/04-jsx-components/03-discord-components-v2/03-media-gallery.mdx b/apps/website/docs/guide/04-jsx-components/03-discord-components-v2/03-media-gallery.mdx
new file mode 100644
index 00000000..da364a8e
--- /dev/null
+++ b/apps/website/docs/guide/04-jsx-components/03-discord-components-v2/03-media-gallery.mdx
@@ -0,0 +1,46 @@
+---
+title: Media Gallery
+---
+
+The `MediaGallery` component allows you to display multiple media
+items in a grid layout.
+
+## Basic usage
+
+```tsx title="src/app/commands/gallery-example.tsx"
+import type { ChatInputCommand } from 'commandkit';
+import {
+ MediaGallery,
+ MediaGalleryItem,
+ TextDisplay,
+} from 'commandkit';
+import { MessageFlags } from 'discord.js';
+
+export const chatInput: ChatInputCommand = async ({
+ interaction,
+}) => {
+ const images = [
+ 'https://cdn.discordapp.com/embed/avatars/0.png',
+ 'https://cdn.discordapp.com/embed/avatars/1.png',
+ 'https://cdn.discordapp.com/embed/avatars/2.png',
+ ];
+
+ const components = [
+ ,
+
+ {images.map((url, index) => (
+
+ ))}
+ ,
+ ];
+
+ await interaction.reply({
+ components: components,
+ flags: MessageFlags.IsComponentsV2,
+ });
+};
+```
diff --git a/apps/website/docs/guide/04-jsx-components/03-discord-components-v2/04-separator.mdx b/apps/website/docs/guide/04-jsx-components/03-discord-components-v2/04-separator.mdx
new file mode 100644
index 00000000..605979fa
--- /dev/null
+++ b/apps/website/docs/guide/04-jsx-components/03-discord-components-v2/04-separator.mdx
@@ -0,0 +1,39 @@
+---
+title: Separator
+---
+
+The `Separator` component creates visual spacing between sections in
+your message components.
+
+## Basic usage
+
+```tsx title="src/app/commands/separator-example.tsx"
+import type { ChatInputCommand } from 'commandkit';
+import { Container, TextDisplay, Separator } from 'commandkit';
+import {
+ Colors,
+ MessageFlags,
+ SeparatorSpacingSize,
+} from 'discord.js';
+
+export const chatInput: ChatInputCommand = async ({
+ interaction,
+}) => {
+ const container = (
+
+
+
+
+
+
+
+
+
+ );
+
+ await interaction.reply({
+ components: [container],
+ flags: MessageFlags.IsComponentsV2,
+ });
+};
+```
diff --git a/apps/website/docs/guide/04-jsx-components/03-discord-components-v2/05-file.mdx b/apps/website/docs/guide/04-jsx-components/03-discord-components-v2/05-file.mdx
new file mode 100644
index 00000000..72a5e578
--- /dev/null
+++ b/apps/website/docs/guide/04-jsx-components/03-discord-components-v2/05-file.mdx
@@ -0,0 +1,37 @@
+---
+title: File
+---
+
+The `File` component allows you to display file attachments in your
+Discord messages.
+
+## Basic usage
+
+```tsx title="src/app/commands/file-example.tsx"
+import type { ChatInputCommand } from 'commandkit';
+import { Container, File, TextDisplay } from 'commandkit';
+import { AttachmentBuilder, Colors, MessageFlags } from 'discord.js';
+
+export const chatInput: ChatInputCommand = async ({
+ interaction,
+}) => {
+ const fileContent = '# Hello World\nThis is a test markdown file.';
+
+ const container = (
+
+
+
+
+ );
+
+ await interaction.reply({
+ components: [container],
+ files: [
+ new AttachmentBuilder(Buffer.from(fileContent), {
+ name: 'example.md',
+ }),
+ ],
+ flags: MessageFlags.IsComponentsV2,
+ });
+};
+```
diff --git a/apps/website/docs/guide/05-official-plugins/01-commandkit-ai.mdx b/apps/website/docs/guide/05-official-plugins/01-commandkit-ai.mdx
new file mode 100644
index 00000000..a20b751f
--- /dev/null
+++ b/apps/website/docs/guide/05-official-plugins/01-commandkit-ai.mdx
@@ -0,0 +1,544 @@
+---
+title: '@commandkit/ai'
+---
+
+The `@commandkit/ai` plugin allows you to execute your bot commands
+using large language models, enabling users to interact with your bot
+entirely through natural language.
+
+:::warning
+
+This is an experimental feature and is subject to change.
+
+:::
+
+## Architecture overview
+
+```mermaid
+graph TD
+ A[Discord Message] --> B[Message Filter]
+ B --> C[AI Model Selection]
+ C --> D[System Prompt Generation]
+ D --> E[AI Processing]
+ E --> F[Tool Execution]
+ F --> G[Response Generation]
+ G --> H[Discord Reply]
+
+ I[Built-in Tools] --> F
+ J[Custom Tools] --> F
+ K[AI Commands] --> F
+```
+
+The `@commandkit/ai` plugin basically acts as a smart command handler
+that decides which command to execute based on the given prompt.
+Similarly, the AI commands are basically an abstraction for tool
+calling feature of the large language models. This plugin uses the
+[AI SDK](https://ai-sdk.dev) under the hood to interact with large
+language models.
+
+## Installation
+
+Install the plugin and your preferred AI SDK:
+
+```bash npm2yarn
+npm install @commandkit/ai
+```
+
+You also need to install the AI SDK for the model you want to use. For
+example, if you want to use Google Gemini:
+
+```bash npm2yarn
+npm install @ai-sdk/google
+```
+
+Refer to the [AI SDK documentation](https://ai-sdk.dev) for more
+information on setting up different AI providers.
+
+## Basic setup
+
+First, add the AI plugin to your CommandKit configuration:
+
+```ts title="commandkit.config.ts"
+import { defineConfig } from 'commandkit';
+import { ai } from '@commandkit/ai';
+
+export default defineConfig({
+ plugins: [ai()],
+});
+```
+
+Then create an AI configuration file to set up your AI model:
+
+```ts title="src/ai.ts"
+import { createGoogleGenerativeAI } from '@ai-sdk/google';
+import { configureAI } from '@commandkit/ai';
+
+const google = createGoogleGenerativeAI({
+ apiKey: process.env.GOOGLE_API_KEY,
+});
+
+configureAI({
+ selectAiModel: async (ctx, message) => ({
+ model: google.languageModel('gemini-2.0-flash'),
+ maxSteps: 5,
+ temperature: 0.7,
+ }),
+
+ messageFilter: async (commandkit, message) => {
+ // Only respond when the bot is mentioned
+ return message.mentions.users.has(message.client.user.id);
+ },
+});
+```
+
+Finally, import the configuration in your main application file:
+
+```ts title="src/app.ts"
+import { Client, GatewayIntentBits } from 'discord.js';
+import './ai'; // Import AI configuration
+
+const client = new Client({
+ intents: [
+ GatewayIntentBits.Guilds,
+ GatewayIntentBits.GuildMessages,
+ GatewayIntentBits.MessageContent, // Required for AI to read message content
+ ],
+});
+
+export default client;
+```
+
+## Creating AI commands
+
+To make a command accessible to AI, export an `ai` function and
+optional `aiConfig` object from your command file:
+
+```ts title="src/app/commands/greet.ts"
+import type { CommandData, ChatInputCommand } from 'commandkit';
+import type { AiConfig, AiCommand } from '@commandkit/ai';
+import { z } from 'zod';
+
+export const command: CommandData = {
+ name: 'greet',
+ description: 'Greet a user',
+};
+
+export const aiConfig: AiConfig = {
+ parameters: z.object({
+ username: z.string().describe('The username to greet'),
+ message: z
+ .string()
+ .optional()
+ .describe('Optional custom greeting message'),
+ }),
+};
+
+export const chatInput: ChatInputCommand = async (ctx) => {
+ const { interaction } = ctx;
+ // Handle slash command normally
+ await interaction.reply('Hello!');
+};
+
+// AI will call this function with natural language parameters
+export const ai: AiCommand = async (ctx) => {
+ const { username, message } = ctx.ai.params;
+
+ const greeting = message || `Hello, ${username}!`;
+ await ctx.message.reply(greeting);
+
+ return { greeting, username };
+};
+```
+
+Now users can interact with your command naturally:
+
+```
+@bot greet John with a friendly message
+@bot say hello to Sarah
+```
+
+## Configuration options
+
+The `configureAI` function accepts several configuration options:
+
+### Model selection
+
+The `selectAiModel` function is required and determines which AI model
+to use:
+
+```ts
+configureAI({
+ selectAiModel: async (ctx, message) => {
+ // Use different models for different contexts
+ if (message.member?.permissions.has('Administrator')) {
+ return {
+ model: google.languageModel('gemini-2.0-flash'),
+ maxSteps: 10,
+ temperature: 0.6,
+ };
+ }
+
+ // Default model for regular users
+ return {
+ model: google.languageModel('gemini-1.5-flash'),
+ maxSteps: 5,
+ temperature: 0.7,
+ };
+ },
+});
+```
+
+### Message filtering
+
+Control which messages trigger AI processing:
+
+```ts
+messageFilter: async (commandkit, message) => {
+ return (
+ !message.author.bot && // Ignore bots
+ message.inGuild() && // Only in servers
+ (message.mentions.users.has(message.client.user.id) || // Bot mentions
+ message.content.toLowerCase().includes('hey bot')) // Keywords
+ );
+},
+```
+
+### System prompts
+
+Customize the AI's behavior and context:
+
+```ts
+prepareSystemPrompt: async (ctx, message) => {
+ const serverName = message.guild?.name || 'Direct Message';
+
+ return `You are ${message.client.user.username}, a helpful Discord bot.
+
+**Context:**
+- Server: ${serverName}
+- Channel: ${message.channel.name || 'DM'}
+
+**Guidelines:**
+- Keep responses under 2000 characters
+- Be helpful and friendly
+- Use available commands when appropriate`;
+},
+```
+
+### Lifecycle hooks
+
+Configure functions that run during AI processing:
+
+```ts
+configureAI({
+ // Called when AI processing starts
+ onProcessingStart: async (ctx, message) => {
+ await message.channel.sendTyping();
+ },
+
+ // Called with the AI response
+ onResult: async (ctx, message, result) => {
+ if (result.text) {
+ await message.reply({
+ content: result.text.substring(0, 2000),
+ allowedMentions: { parse: [] },
+ });
+ }
+ },
+
+ // Called when an error occurs
+ onError: async (ctx, message, error) => {
+ console.error('AI error:', error.message);
+ await message.reply(
+ 'Sorry, I encountered an error processing your request.',
+ );
+ },
+});
+```
+
+## Built-in tools
+
+CommandKit provides built-in tools that the AI can use automatically:
+
+- **`getAvailableCommands`** - Lists all available bot commands
+- **`getChannelById`** - Fetches channel information by ID
+- **`getCurrentClientInfo`** - Gets information about the bot
+- **`getGuildById`** - Fetches server information by ID
+- **`getUserById`** - Fetches user information by ID
+
+You can disable these tools if needed:
+
+```ts
+configureAI({
+ disableBuiltInTools: true,
+ // ... other options
+});
+```
+
+## Creating custom tools
+
+Extend the AI's capabilities by creating custom tools:
+
+```ts title="src/tools/weather.ts"
+import { createTool } from '@commandkit/ai';
+import { z } from 'zod';
+
+export const getWeather = createTool({
+ name: 'getWeather',
+ description: 'Get current weather information for a location',
+ parameters: z.object({
+ location: z
+ .string()
+ .describe('The city or location to get weather for'),
+ units: z.enum(['celsius', 'fahrenheit']).default('celsius'),
+ }),
+ async execute(ctx, params) {
+ const { location, units } = params;
+
+ try {
+ const weatherData = await fetchWeatherAPI(location);
+
+ return {
+ location,
+ temperature:
+ units === 'celsius' ? weatherData.tempC : weatherData.tempF,
+ condition: weatherData.condition,
+ humidity: weatherData.humidity,
+ };
+ } catch (error) {
+ return {
+ error: `Failed to get weather: ${error.message}`,
+ };
+ }
+ },
+});
+```
+
+Register your custom tools with the AI model:
+
+```ts title="src/ai.ts"
+import { getWeather } from './tools/weather';
+
+configureAI({
+ selectAiModel: async (ctx, message) => ({
+ model: google.languageModel('gemini-2.0-flash'),
+ tools: {
+ getWeather, // Add your custom tools here
+ },
+ }),
+});
+```
+
+## AI context and hooks
+
+The AI system provides helpful context and hooks for accessing the
+current AI execution environment:
+
+### Using AI context
+
+```ts
+export const ai: AiCommand = async (ctx) => {
+ // AI-generated parameters based on your schema
+ const params = ctx.ai.params;
+
+ // Original Discord message that triggered the AI
+ const message = ctx.message;
+
+ // CommandKit instance
+ const commandkit = ctx.commandkit;
+
+ // Key-value store for temporary data
+ ctx.store.set('startTime', Date.now());
+ const startTime = ctx.store.get('startTime');
+};
+```
+
+### Using hooks
+
+Access the AI context from anywhere in your code:
+
+```ts
+import { useAIContext } from '@commandkit/ai';
+
+function someUtilityFunction() {
+ try {
+ const ctx = useAIContext();
+ console.log('Current AI parameters:', ctx.ai.params);
+ return ctx.message.author.id;
+ } catch (error) {
+ // Not in an AI context
+ return null;
+ }
+}
+```
+
+## Multiple AI providers
+
+You can configure multiple AI providers and choose between them
+dynamically:
+
+```ts title="src/ai.ts"
+import { createGoogleGenerativeAI } from '@ai-sdk/google';
+import { createOpenAI } from '@ai-sdk/openai';
+
+const google = createGoogleGenerativeAI({
+ apiKey: process.env.GOOGLE_API_KEY,
+});
+const openai = createOpenAI({
+ apiKey: process.env.OPENAI_API_KEY,
+});
+
+configureAI({
+ selectAiModel: async (ctx, message) => {
+ // Use different models for different channels
+ if (message.channelId === 'premium-channel-id') {
+ return {
+ model: openai('gpt-4'),
+ maxSteps: 10,
+ };
+ }
+
+ // Default to Google Gemini
+ return {
+ model: google.languageModel('gemini-2.0-flash'),
+ maxSteps: 5,
+ };
+ },
+});
+```
+
+## Best practices
+
+### Security
+
+Always validate user permissions before executing sensitive
+operations:
+
+```ts
+export const ai: AiCommand = async (ctx) => {
+ const { action } = ctx.ai.params;
+
+ // Check permissions
+ if (!ctx.message.member?.permissions.has('ManageMessages')) {
+ await ctx.message.reply(
+ '❌ You need "Manage Messages" permission to use this command.',
+ );
+ return;
+ }
+
+ // Validate AI-generated parameters
+ if (action === 'ban' && !isModeratorRole(ctx.message.member)) {
+ await ctx.message.reply(
+ '❌ Only moderators can perform ban actions.',
+ );
+ return;
+ }
+
+ // Proceed with the action
+};
+```
+
+### Rate limiting
+
+Implement rate limiting to prevent abuse:
+
+```ts
+const userCooldowns = new Map();
+const COOLDOWN_DURATION = 30000; // 30 seconds
+
+export const ai: AiCommand = async (ctx) => {
+ const userId = ctx.message.author.id;
+ const now = Date.now();
+ const cooldownEnd = userCooldowns.get(userId) || 0;
+
+ if (now < cooldownEnd) {
+ const remaining = Math.ceil((cooldownEnd - now) / 1000);
+ await ctx.message.reply(
+ `⏰ Please wait ${remaining} seconds before using this command again.`,
+ );
+ return;
+ }
+
+ userCooldowns.set(userId, now + COOLDOWN_DURATION);
+
+ // Process command
+ await processCommand(ctx);
+};
+```
+
+### Error handling
+
+Implement robust error handling:
+
+```ts
+export const ai: AiCommand = async (ctx) => {
+ try {
+ const result = await performOperation(ctx.ai.params);
+ await ctx.message.reply(`✅ ${result}`);
+ } catch (error) {
+ console.error('AI command error:', {
+ command: 'example',
+ userId: ctx.message.author.id,
+ error: error.message,
+ });
+
+ await ctx.message.reply('❌ An unexpected error occurred.');
+ }
+};
+```
+
+## Troubleshooting
+
+### AI not responding
+
+If the AI isn't responding to messages:
+
+1. **Check the AI plugin is registered** in your
+ `commandkit.config.ts`
+2. **Verify your message filter** isn't too restrictive
+3. **Ensure required Discord intents** are enabled (especially
+ `MessageContent`)
+
+### Model configuration errors
+
+1. **Check API keys** are set in environment variables
+2. **Verify model initialization** doesn't throw errors
+3. **Handle model availability** gracefully
+
+### Parameter issues
+
+1. **Check Zod schema definitions** match expected parameters
+2. **Add parameter validation** in your AI commands
+3. **Improve parameter descriptions** for better AI understanding
+
+For more detailed troubleshooting, check the console logs for specific
+error messages and verify your environment configuration.
+
+## Environment configuration
+
+Configure different settings for development and production:
+
+```ts title="src/ai.ts"
+const isDevelopment = process.env.NODE_ENV === 'development';
+
+configureAI({
+ selectAiModel: async (ctx, message) => ({
+ model: google.languageModel(
+ isDevelopment ? 'gemini-1.5-flash' : 'gemini-2.0-flash',
+ ),
+ maxSteps: isDevelopment ? 3 : 8,
+ temperature: isDevelopment ? 0.8 : 0.6,
+ }),
+
+ onError: async (ctx, message, error) => {
+ if (isDevelopment) {
+ // Show detailed errors in development
+ await message.reply(`Debug Error: ${error.message}`);
+ } else {
+ // Generic error in production
+ await message.reply(
+ 'An error occurred while processing your request.',
+ );
+ }
+ },
+});
+```
diff --git a/apps/website/docs/guide/05-official-plugins/02-commandkit-analytics.mdx b/apps/website/docs/guide/05-official-plugins/02-commandkit-analytics.mdx
new file mode 100644
index 00000000..480a8b5f
--- /dev/null
+++ b/apps/website/docs/guide/05-official-plugins/02-commandkit-analytics.mdx
@@ -0,0 +1,396 @@
+---
+title: '@commandkit/analytics'
+---
+
+CommandKit provides a powerful analytics system that helps you track
+events and metrics in your Discord bot while maintaining user privacy.
+The `@commandkit/analytics` plugin acts as a bridge between your
+application and your chosen analytics provider, without storing any
+data itself.
+
+## Installation
+
+Install the analytics package to get started:
+
+```bash npm2yarn
+npm install @commandkit/analytics
+```
+
+## How it works
+
+CommandKit's analytics system is designed to be provider-agnostic and
+privacy-focused:
+
+1. **No data storage**: CommandKit doesn't store any analytics data
+ itself
+2. **Direct transmission**: All data is sent directly to your
+ configured analytics provider
+3. **Multiple providers**: You can easily switch between different
+ providers or use multiple providers simultaneously
+4. **Extensible**: The system allows you to create custom providers
+ for your specific needs
+5. **Anonymous tracking**: Focus on anonymous, aggregated metrics to
+ respect user privacy
+
+## Automatic tracking
+
+CommandKit automatically tracks various anonymous metrics when
+analytics is enabled:
+
+- **Command execution events** (`command_execution`)
+- **Cache performance metrics**:
+ - Cache hits (`cache_hit`)
+ - Cache misses (`cache_miss`)
+ - Cache revalidations (`cache_revalidated`)
+- **Feature flag metrics**:
+ - Feature flag metrics (`feature_flag_metrics`)
+ - Feature flag decisions (`feature_flag_decision`)
+- **Anonymous interaction patterns**
+
+## Available providers
+
+CommandKit comes with built-in support for popular analytics
+providers:
+
+### PostHog
+
+PostHog is an open-source product analytics platform that helps you
+understand user behavior:
+
+```ts title="commandkit.config.ts"
+import { defineConfig } from 'commandkit';
+import { posthog } from '@commandkit/analytics/posthog';
+
+export default defineConfig({
+ plugins: [
+ posthog({
+ posthogOptions: {
+ apiKey: 'YOUR_POSTHOG_API_KEY',
+ },
+ }),
+ ],
+});
+```
+
+### Umami
+
+Umami is a simple, fast, privacy-focused alternative to Google
+Analytics:
+
+```ts title="commandkit.config.ts"
+import { defineConfig } from 'commandkit';
+import { umami } from '@commandkit/analytics/umami';
+
+export default defineConfig({
+ plugins: [
+ umami({
+ umamiOptions: {
+ hostUrl: 'YOUR_UMAMI_HOST_URL',
+ websiteId: 'YOUR_UMAMI_WEBSITE_ID',
+ // Optional: Additional configuration
+ sessionId: 'YOUR_UMAMI_SESSION_ID',
+ userAgent: 'YOUR_UMAMI_USER_AGENT',
+ },
+ }),
+ ],
+});
+```
+
+## Basic usage
+
+Track custom events in your Discord bot using the `track` function:
+
+```ts title="src/app/commands/ban.ts"
+import { track } from 'commandkit/analytics';
+import type { ChatInputCommand, CommandData } from 'commandkit';
+
+export const command: CommandData = {
+ name: 'ban',
+ description: 'Ban a user from the server',
+ options: [
+ {
+ name: 'user',
+ description: 'The user to ban',
+ type: 6, // User
+ required: true,
+ },
+ ],
+};
+
+export const chatInput: ChatInputCommand = async ({
+ interaction,
+}) => {
+ const user = interaction.options.getUser('user', true);
+
+ // Perform the ban
+ await interaction.guild?.members.ban(user);
+
+ // Track the moderation action with anonymous data
+ await track({
+ name: 'moderation_action',
+ data: {
+ actionType: 'ban',
+ // Track performance metrics
+ responseTime: Date.now() - interaction.createdTimestamp,
+ // Track anonymous usage patterns
+ timeOfDay: new Date().getHours(),
+ dayOfWeek: new Date().getDay(),
+ },
+ });
+
+ await interaction.reply(`Successfully banned ${user.tag}`);
+};
+```
+
+## Identifying anonymous sessions
+
+You can identify anonymous sessions in your analytics provider using
+the `identify` function:
+
+```ts
+import { useAnalytics } from 'commandkit/analytics';
+
+const analytics = useAnalytics();
+
+await analytics.identify({
+ // Use a hashed or anonymous identifier
+ distinctId: 'session_' + Math.random().toString(36).substring(7),
+ properties: {
+ // Track anonymous session properties
+ sessionStart: Date.now(),
+ environment: process.env.NODE_ENV,
+ version: '1.0.0',
+ },
+});
+```
+
+## Filtering events
+
+You can filter out specific events using the `setFilter` function:
+
+```ts
+import { useAnalytics } from 'commandkit/analytics';
+
+const analytics = useAnalytics();
+
+// Filter out events based on anonymous criteria
+analytics.setFilter((engine, event) => {
+ // Skip events from development environments
+ if (process.env.NODE_ENV === 'development') {
+ return false; // false means skip this event
+ }
+
+ // Skip specific event types
+ if (event.name === 'debug_event') {
+ return false;
+ }
+
+ return true; // true means track this event
+});
+```
+
+## Disabling analytics
+
+You can disable analytics for specific requests using the
+`noAnalytics` function:
+
+```ts
+import { noAnalytics } from 'commandkit/analytics';
+
+// Disable analytics for specific commands or events
+if (interaction.commandName === 'debug') {
+ noAnalytics();
+}
+```
+
+## Creating custom providers
+
+You can create your own analytics provider by implementing the
+`AnalyticsProvider` interface:
+
+```ts title="src/providers/custom-analytics.ts"
+import {
+ AnalyticsProvider,
+ AnalyticsEvent,
+ IdentifyEvent,
+} from 'commandkit/analytics';
+
+class CustomAnalyticsProvider implements AnalyticsProvider {
+ readonly name = 'custom-analytics';
+
+ constructor(private readonly analytics: YourAnalyticsService) {}
+
+ async track(
+ engine: AnalyticsEngine,
+ event: AnalyticsEvent,
+ ): Promise {
+ // Implement your tracking logic here
+ const { name, data } = event;
+
+ // Example: Send anonymous data to your analytics service
+ await this.analytics.track({
+ name,
+ // Only include anonymous data
+ data: {
+ ...data,
+ // Add anonymous session info
+ sessionId: this.generateSessionId(),
+ timestamp: Date.now(),
+ },
+ });
+ }
+
+ async identify(
+ engine: AnalyticsEngine,
+ event: IdentifyEvent,
+ ): Promise {
+ // Implement anonymous session identification
+ await this.analytics.identify({
+ // Use anonymous session identifiers
+ sessionId: this.generateSessionId(),
+ timestamp: Date.now(),
+ // Include anonymous session properties
+ properties: {
+ environment: process.env.NODE_ENV,
+ version: '1.0.0',
+ },
+ });
+ }
+
+ private generateSessionId(): string {
+ return 'session_' + Math.random().toString(36).substring(7);
+ }
+}
+```
+
+### Creating a plugin
+
+Create a plugin class that extends `RuntimePlugin`:
+
+```ts title="src/plugins/custom-analytics-plugin.ts"
+import {
+ CommandKitPluginRuntime,
+ RuntimePlugin,
+} from 'commandkit/plugin';
+import { CustomAnalyticsProvider } from '../providers/custom-analytics';
+
+export interface CustomAnalyticsPluginOptions {
+ analyticsOptions: {
+ apiKey: string;
+ options?: YourAnalyticsOptions;
+ };
+}
+
+export class CustomAnalyticsPlugin extends RuntimePlugin {
+ public readonly name = 'CustomAnalytics';
+
+ private provider: CustomAnalyticsProvider | null = null;
+
+ public async activate(ctx: CommandKitPluginRuntime): Promise {
+ const analytics = new YourAnalyticsService(
+ this.options.analyticsOptions.apiKey,
+ this.options.analyticsOptions.options,
+ );
+
+ this.provider = new CustomAnalyticsProvider(analytics);
+
+ ctx.commandkit.analytics.registerProvider(this.provider);
+ }
+
+ public async deactivate(
+ ctx: CommandKitPluginRuntime,
+ ): Promise {
+ if (!this.provider) return;
+
+ // Cleanup if needed
+ await this.provider.analytics.shutdown?.();
+
+ ctx.commandkit.analytics.removeProvider(this.provider);
+ }
+}
+```
+
+### Using your custom provider
+
+Add your custom provider to your CommandKit configuration:
+
+```ts title="commandkit.config.ts"
+import { defineConfig } from 'commandkit';
+import { CustomAnalyticsPlugin } from './src/plugins/custom-analytics-plugin';
+
+export default defineConfig({
+ plugins: [
+ new CustomAnalyticsPlugin({
+ analyticsOptions: {
+ apiKey: 'YOUR_API_KEY',
+ options: {
+ // Your analytics service options
+ },
+ },
+ }),
+ ],
+});
+```
+
+## Privacy considerations
+
+When implementing analytics in your bot, always consider:
+
+1. **Anonymous data only**: Only track anonymous, aggregated data
+2. **No personal information**: Avoid storing personal information
+3. **Hashed identifiers**: Use hashed identifiers when necessary
+4. **Transparency**: Be transparent about what data you collect
+5. **Discord compliance**: Respect user privacy and Discord's Terms of
+ Service
+6. **Data minimization**: Only collect data that's necessary for your
+ use case
+
+## Common use cases
+
+### Performance monitoring
+
+```ts
+await track({
+ name: 'command_performance',
+ data: {
+ commandName: 'complex-calculation',
+ executionTime: Date.now() - startTime,
+ memoryUsage: process.memoryUsage().heapUsed,
+ },
+});
+```
+
+### Feature usage analytics
+
+```ts
+await track({
+ name: 'feature_usage',
+ data: {
+ featureName: 'auto-moderation',
+ configurationUsed: 'strict',
+ triggerCount: violations.length,
+ },
+});
+```
+
+### Error tracking
+
+```ts
+await track({
+ name: 'error_occurred',
+ data: {
+ errorType: 'api_timeout',
+ commandContext: 'user-info',
+ recoveryAction: 'retry_successful',
+ },
+});
+```
+
+:::tip
+
+The `@commandkit/analytics` plugin works seamlessly with other
+CommandKit features and plugins. You can use it alongside features
+like caching and feature flags to get comprehensive insights into your
+bot's performance and usage patterns.
+
+:::
diff --git a/apps/website/docs/guide/05-official-plugins/03-commandkit-cache.mdx b/apps/website/docs/guide/05-official-plugins/03-commandkit-cache.mdx
new file mode 100644
index 00000000..6ce28fc5
--- /dev/null
+++ b/apps/website/docs/guide/05-official-plugins/03-commandkit-cache.mdx
@@ -0,0 +1,259 @@
+---
+title: '@commandkit/cache'
+---
+
+CommandKit provides a powerful caching system that helps you optimize
+your bot's performance by storing frequently accessed data in memory.
+The `@commandkit/cache` plugin allows you to cache expensive
+operations like API calls and database queries, significantly
+improving response times and reducing external service usage.
+
+## Installation
+
+Install the cache package to get started:
+
+```bash npm2yarn
+npm install @commandkit/cache
+```
+
+## Setup
+
+Add the cache plugin to your CommandKit configuration:
+
+```ts title="commandkit.config.ts"
+import { defineConfig } from 'commandkit';
+import { cache } from '@commandkit/cache';
+
+export default defineConfig({
+ plugins: [cache()],
+});
+```
+
+## Basic usage
+
+The simplest way to use caching is with the `"use cache"` directive.
+This tells CommandKit to cache the result of your function:
+
+```ts title="src/app/commands/weather.ts"
+import type { ChatInputCommand, CommandData } from 'commandkit';
+
+export const command: CommandData = {
+ name: 'weather',
+ description: 'Get weather information for a city',
+ options: [
+ {
+ name: 'city',
+ description: 'The city to get weather for',
+ type: 3, // String
+ required: true,
+ },
+ ],
+};
+
+async function fetchWeatherData(city: string) {
+ 'use cache';
+
+ // This expensive API call will only run once for each unique city
+ const response = await fetch(
+ `https://api.weather.com/v1/current?q=${city}`,
+ );
+ return response.json();
+}
+
+export const chatInput: ChatInputCommand = async ({
+ interaction,
+}) => {
+ const city = interaction.options.getString('city', true);
+ const weather = await fetchWeatherData(city);
+
+ await interaction.reply(
+ `Weather in ${city}: ${weather.description}`,
+ );
+};
+```
+
+When you call `fetchWeatherData` multiple times with the same city, it
+will only perform the API call once and return the cached result for
+subsequent calls.
+
+## How caching works
+
+CommandKit's caching system works by:
+
+1. **Generating a cache key**: Each cached function call generates a
+ unique key based on the function's identity, arguments, and build
+ ID
+2. **Storing results**: When a function is called, if the result isn't
+ cached, the function executes and stores its result. If cached, it
+ returns immediately
+3. **Automatic cleanup**: The cache system automatically removes stale
+ entries and manages memory usage
+
+## Controlling cache behavior
+
+### Setting cache duration
+
+Use `cacheLife` to control how long entries stay in the cache:
+
+```ts
+import { cacheLife } from '@commandkit/cache';
+
+async function fetchUserData(userId: string) {
+ 'use cache';
+
+ // Cache for 1 hour
+ cacheLife('1h');
+
+ const userData = await db.users.findOne(userId);
+ return userData;
+}
+```
+
+Supported time formats:
+
+- `'5s'` - 5 seconds
+- `'1m'` - 1 minute
+- `'2h'` - 2 hours
+- `'1d'` - 1 day
+- `60000` - Direct milliseconds
+
+### Using cache tags
+
+Use `cacheTag` to group related cache entries for easier management:
+
+```ts
+import { cacheTag } from '@commandkit/cache';
+
+async function fetchGuildSettings(guildId: string) {
+ 'use cache';
+
+ // Tag this entry for easy invalidation
+ cacheTag(`guild:${guildId}`);
+ cacheTag('settings');
+ cacheLife('1h');
+
+ return await db.guilds.findOne(guildId);
+}
+```
+
+### Invalidating cache entries
+
+Use `revalidateTag` to invalidate specific cache entries:
+
+```ts
+import { revalidateTag } from '@commandkit/cache';
+
+async function updateGuildSettings(guildId: string, settings: any) {
+ // Update the database
+ await db.guilds.update(guildId, settings);
+
+ // Invalidate all cached guild data
+ await revalidateTag(`guild:${guildId}`);
+}
+```
+
+## Common use cases
+
+### Caching API responses
+
+```ts
+async function fetchGameStats(gameId: string) {
+ 'use cache';
+
+ cacheTag(`game:${gameId}`);
+ cacheLife('30m'); // API updates every 30 minutes
+
+ const response = await fetch(
+ `https://api.game.com/stats/${gameId}`,
+ );
+ return response.json();
+}
+```
+
+### Caching database queries
+
+```ts
+async function getUserProfile(userId: string) {
+ 'use cache';
+
+ cacheTag(`user:${userId}`);
+ cacheLife('1h'); // User profiles don't change often
+
+ return await db.users.findOne({
+ where: { id: userId },
+ include: ['profile', 'settings'],
+ });
+}
+```
+
+### Caching computed results
+
+```ts
+async function calculateUserStats(userId: string) {
+ 'use cache';
+
+ cacheTag(`user:${userId}`);
+ cacheTag('stats');
+ cacheLife('5m'); // Recalculate every 5 minutes
+
+ const user = await db.users.findOne(userId);
+ return {
+ level: calculateLevel(user.xp),
+ rank: await calculateRank(userId),
+ achievements: await getAchievements(userId),
+ };
+}
+```
+
+## Advanced configuration
+
+### Custom cache provider
+
+For distributed caching, you can use a custom cache provider like
+Redis:
+
+```ts title="commandkit.config.ts"
+import { defineConfig } from 'commandkit';
+import { cache, setCacheProvider } from '@commandkit/cache';
+import { RedisCache } from '@commandkit/redis';
+
+// Set up Redis as the cache provider
+setCacheProvider(
+ new RedisCache({
+ host: 'localhost',
+ port: 6379,
+ }),
+);
+
+export default defineConfig({
+ plugins: [cache()],
+});
+```
+
+### Manual cache cleanup
+
+Clean up stale cache entries to manage memory usage:
+
+```ts
+import { cleanup } from '@commandkit/cache';
+
+// Clean up entries older than 24 hours
+await cleanup(24 * 60 * 60 * 1000);
+
+// Set up regular cleanup
+setInterval(
+ async () => {
+ await cleanup(24 * 60 * 60 * 1000);
+ },
+ 24 * 60 * 60 * 1000,
+); // Run daily
+```
+
+:::tip
+
+The `@commandkit/cache` plugin works seamlessly with other CommandKit
+features and plugins. You can use it alongside the
+[`@commandkit/redis`](./07-commandkit-redis.mdx) plugin for
+distributed caching across multiple bot instances.
+
+:::
diff --git a/apps/website/docs/guide/05-official-plugins/04-commandkit-devtools.mdx b/apps/website/docs/guide/05-official-plugins/04-commandkit-devtools.mdx
new file mode 100644
index 00000000..a319d51b
--- /dev/null
+++ b/apps/website/docs/guide/05-official-plugins/04-commandkit-devtools.mdx
@@ -0,0 +1,196 @@
+---
+title: '@commandkit/devtools'
+---
+
+The `@commandkit/devtools` plugin provides a comprehensive set of
+development tools and utilities designed to enhance your CommandKit
+development experience. It includes features like command inspection,
+performance monitoring, debugging tools, and a web-based interface for
+managing your Discord bot during development.
+
+:::warning
+
+This plugin is currently in early development and may not be fully
+stable. It is recommended to use it for testing and feedback purposes
+only. Features and APIs may change in future releases.
+
+:::
+
+## Features
+
+- **Command Inspection**: View and analyze all registered commands in
+ your bot
+- **Performance Monitoring**: Track command execution times and
+ performance metrics
+- **Debugging Tools**: Enhanced logging and debugging capabilities
+- **Web Interface**: Browser-based dashboard for managing your bot
+- **Real-time Updates**: Live updates of bot status and metrics
+- **Development Utilities**: Additional tools to streamline
+ development workflow
+
+## Installation
+
+Install the devtools plugin using your preferred package manager:
+
+```sh npm2yarn
+npm install @commandkit/devtools@dev
+```
+
+:::info
+
+Currently, the devtools plugin is only available as a development
+version. Use the `@dev` tag when installing to get the latest
+development build.
+
+:::
+
+## Setup
+
+Add the devtools plugin to your CommandKit configuration:
+
+```ts title="commandkit.config.ts"
+import { defineConfig } from 'commandkit';
+import { devtools } from '@commandkit/devtools';
+
+export default defineConfig({
+ plugins: [devtools()],
+});
+```
+
+That's it! The devtools plugin will automatically start when you run
+your application in development mode.
+
+## Using the devtools
+
+Once the plugin is installed and configured, the devtools interface
+will be available at `http://localhost:4356` when you start your
+application with `commandkit dev`.
+
+```bash
+npx commandkit dev
+```
+
+The web interface provides access to all devtools features and allows
+you to:
+
+- Monitor your bot's status and performance
+- Inspect registered commands and their configurations
+- View real-time logs and debug information
+- Access development utilities and tools
+
+## Configuration options
+
+The devtools plugin accepts several configuration options to customize
+its behavior:
+
+```ts title="commandkit.config.ts"
+import { defineConfig } from 'commandkit';
+import { devtools } from '@commandkit/devtools';
+
+export default defineConfig({
+ plugins: [
+ devtools({
+ // Custom port for the devtools interface
+ port: 4356,
+
+ // Enable or disable specific features
+ features: {
+ commandInspection: true,
+ performanceMonitoring: true,
+ debugging: true,
+ },
+
+ // Additional configuration options
+ enableLogging: true,
+ }),
+ ],
+});
+```
+
+### Configuration properties
+
+- `port` (number): The port number for the devtools web interface
+ (default: `4356`)
+- `features` (object): Enable or disable specific devtools features
+- `enableLogging` (boolean): Enable enhanced logging capabilities
+
+## Development workflow
+
+The devtools plugin integrates seamlessly with CommandKit's
+development workflow:
+
+1. **Start Development**: Run `commandkit dev` to start your bot in
+ development mode
+2. **Access Devtools**: Open `http://localhost:4356` in your browser
+3. **Monitor & Debug**: Use the web interface to monitor your bot and
+ debug issues
+4. **Hot Reload**: Make changes to your code and see updates in
+ real-time
+
+:::tip Development Only
+
+The devtools plugin is designed for development use only and should
+not be included in production builds. Consider using environment-based
+configuration to conditionally include the plugin:
+
+```ts title="commandkit.config.ts"
+import { defineConfig } from 'commandkit';
+import { devtools } from '@commandkit/devtools';
+
+export default defineConfig({
+ plugins: [
+ // Only include devtools in development
+ ...(process.env.NODE_ENV === 'development' ? [devtools()] : []),
+ ],
+});
+```
+
+:::
+
+## Preview
+
+The devtools interface provides a modern, intuitive dashboard for
+managing your Discord bot during development:
+
+
+
+## Troubleshooting
+
+### Port already in use
+
+If port `4356` is already in use, you can specify a different port in
+the configuration:
+
+```ts
+devtools({ port: 4357 });
+```
+
+### Interface not loading
+
+Ensure that:
+
+1. The devtools plugin is properly configured
+2. Your application is running in development mode
+3. No firewall is blocking the specified port
+4. You're accessing the correct URL (`http://localhost:4356`)
+
+### Performance issues
+
+If you experience performance issues:
+
+1. Disable unnecessary features in the configuration
+2. Reduce logging verbosity
+3. Consider using a different port
+
+## Feedback and contributions
+
+As this plugin is in early development, your feedback is valuable for
+improving the devtools experience. Please report issues, suggest
+features, or contribute to the development through:
+
+- [GitHub Issues](https://github.com/underctrl-io/commandkit/issues)
+- [Discord Community](https://ctrl.lol/discord)
diff --git a/apps/website/docs/guide/05-official-plugins/05-commandkit-i18n.mdx b/apps/website/docs/guide/05-official-plugins/05-commandkit-i18n.mdx
new file mode 100644
index 00000000..9167bbb2
--- /dev/null
+++ b/apps/website/docs/guide/05-official-plugins/05-commandkit-i18n.mdx
@@ -0,0 +1,901 @@
+---
+title: '@commandkit/i18n'
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+The `@commandkit/i18n` plugin integrates
+[i18next](https://www.i18next.com/) into CommandKit, enabling you to
+create multilingual Discord bots that automatically adapt to your
+users' preferred languages. This plugin provides seamless
+internationalization support for commands, events, and all bot
+interactions.
+
+## Features
+
+- **Automatic locale detection** - Automatically uses Discord's guild
+ preferred locale
+- **Easy setup** - Simple configuration with sensible defaults
+- **File-based translations** - Organize translations in JSON files
+- **Context-aware** - Access translations in commands, events, and
+ legacy handlers
+- **i18next ecosystem** - Full compatibility with i18next plugins and
+ features
+- **Command metadata localization** - Localize command names,
+ descriptions, and options
+
+## Installation
+
+```sh npm2yarn
+npm install @commandkit/i18n
+```
+
+## Basic setup
+
+Add the i18n plugin to your CommandKit configuration:
+
+```ts title="commandkit.config.ts"
+import { defineConfig } from 'commandkit';
+import { i18n } from '@commandkit/i18n';
+
+export default defineConfig({
+ plugins: [i18n()],
+});
+```
+
+## Advanced configuration
+
+You can customize the i18n plugin by passing options to it:
+
+```ts title="commandkit.config.ts"
+import { defineConfig } from 'commandkit';
+import { i18n } from '@commandkit/i18n';
+
+export default defineConfig({
+ plugins: [
+ i18n({
+ plugins: [someI18nextPlugin],
+ // Add other i18next configuration options as needed
+ }),
+ ],
+});
+```
+
+## Translation files structure
+
+Create a `locales` directory inside your `src/app` folder with
+subdirectories for each language. Each language directory should
+contain JSON files for your translations.
+
+```
+src
+└── app
+ ├── locales
+ │ ├── en-US
+ │ │ └── ping.json
+ │ │ └── messageCreate.event.json
+ │ └── fr
+ │ └── ping.json
+ │ └── messageCreate.event.json
+ ├── commands
+ │ ├── ping.ts
+ │ └── help.ts
+ └── events
+ └── messageCreate
+ └── handler.ts
+```
+
+### Supported locales
+
+CommandKit uses Discord's locale identifiers. Please refer to
+[Discord's Locales documentation](https://discord.com/developers/docs/reference#locales)
+for a complete list.
+
+## Quick start example
+
+Here's a complete example to get you started:
+
+1. **Configure the plugin**:
+
+```ts title="commandkit.config.ts"
+import { defineConfig } from 'commandkit';
+import { i18n } from '@commandkit/i18n';
+
+export default defineConfig({
+ plugins: [i18n()],
+});
+```
+
+2. **Create translation files**:
+
+```json title="src/app/locales/en-US/ping.json"
+{
+ "$command": {
+ "name": "ping",
+ "description": "Check the bot's latency"
+ },
+ "response": "🏓 Pong! Latency: **{{latency}}ms**"
+}
+```
+
+```json title="src/app/locales/fr/ping.json"
+{
+ "$command": {
+ "name": "ping",
+ "description": "Vérifier la latence du bot"
+ },
+ "response": "🏓 Pong! Latence: **{{latency}}ms**"
+}
+```
+
+3. **Use translations in your command**:
+
+```ts title="src/app/commands/ping.ts"
+import type { ChatInputCommand, CommandData } from 'commandkit';
+
+export const command: CommandData = {
+ name: 'ping',
+ description: "Check the bot's latency",
+};
+
+export const chatInput: ChatInputCommand = async (ctx) => {
+ const { t } = ctx.locale();
+
+ await ctx.interaction.reply({
+ content: t('response', { latency: ctx.client.ws.ping }),
+ });
+};
+```
+
+That's it! Your bot will now automatically respond in the user's guild
+preferred language.
+
+## Commands localization
+
+CommandKit's i18n plugin provides powerful localization features for
+chat input commands, allowing you to translate command metadata
+(names, descriptions, options) and responses to match your users'
+preferred languages.
+
+### Translation file structure
+
+Translation files should be placed in your `locales` directory and
+named after the command they translate. For example, translations for
+a `ping` command should be in `ping.json`.
+
+#### Basic translation file
+
+```json title="src/app/locales/en-US/ping.json"
+{
+ "response": "🏓 Pong! Latency: **{{latency}}ms**",
+ "error": "❌ Failed to ping the server",
+ "database_response": "📊 Database latency: **{{dbLatency}}ms**"
+}
+```
+
+#### Command metadata localization
+
+Use the special `$command` key to localize command metadata that
+appears in Discord's interface:
+
+```json title="src/app/locales/en-US/ping.json"
+{
+ "$command": {
+ "name": "ping",
+ "description": "Check the bot's latency and response time",
+ "options": [
+ {
+ "name": "database",
+ "description": "Also check database connection latency"
+ },
+ {
+ "name": "target",
+ "description": "Specify a target server to ping",
+ "choices": [
+ {
+ "name": "Main Server",
+ "value": "main"
+ },
+ {
+ "name": "Backup Server",
+ "value": "backup"
+ }
+ ]
+ }
+ ]
+ },
+ "response": "🏓 Pong! Latency: **{{latency}}ms**",
+ "database_response": "📊 Database latency: **{{dbLatency}}ms**"
+}
+```
+
+The `$command` object structure mirrors Discord's application command
+structure:
+
+- `name`: Command name (shown in Discord's command picker)
+- `description`: Command description (shown in Discord's command
+ picker)
+- `options`: Array of option localizations
+ - `name`: Option name
+ - `description`: Option description
+ - `choices`: Array of choice localizations (for string options with
+ predefined choices)
+
+### Using translations in commands
+
+The `locale()` function in your command context provides access to
+translations and i18next features:
+
+```ts title="src/app/commands/ping.ts"
+import type { ChatInputCommand } from 'commandkit';
+
+export const chatInput: ChatInputCommand = async (ctx) => {
+ // Get translation function and i18next instance for the current guild's locale
+ const { t, i18n } = ctx.locale();
+
+ const latency = ctx.client.ws.ping;
+
+ // Use the translation function with interpolation
+ await ctx.interaction.reply({
+ content: t('response', { latency }),
+ ephemeral: true,
+ });
+};
+```
+
+#### Manual locale override
+
+You can specify a particular locale instead of using the guild's
+preferred locale:
+
+```ts
+export const chatInput: ChatInputCommand = async (ctx) => {
+ // Force French locale
+ const { t } = ctx.locale('fr');
+
+ await ctx.interaction.reply({
+ content: t('response', { latency: ctx.client.ws.ping }),
+ });
+};
+```
+
+### Advanced translation features
+
+#### Pluralization
+
+i18next supports automatic pluralization:
+
+```json title="locales/en-US/user.json"
+{
+ "member_count": "{{count}} member",
+ "member_count_plural": "{{count}} members"
+}
+```
+
+```ts
+const { t } = ctx.locale();
+const memberCount = guild.memberCount;
+
+// Automatically chooses singular or plural form
+const message = t('member_count', { count: memberCount });
+```
+
+#### Nested Translations
+
+Organize translations using nested objects:
+
+```json title="locales/en-US/errors.json"
+{
+ "validation": {
+ "required": "This field is required",
+ "invalid_format": "Invalid format provided",
+ "too_long": "Input is too long (max {{max}} characters)"
+ },
+ "permissions": {
+ "insufficient": "You don't have permission to use this command",
+ "missing_role": "You need the {{role}} role to use this command"
+ }
+}
+```
+
+```ts
+const { t } = ctx.locale();
+
+// Access nested translations with dot notation
+await ctx.interaction.reply({
+ content: t('errors.permissions.insufficient'),
+ ephemeral: true,
+});
+```
+
+#### Context and Namespaces
+
+Use different translation contexts for better organization:
+
+```ts
+const { t } = ctx.locale();
+
+// Default namespace (command file name)
+t('response');
+
+// Specific namespace
+t('common:greeting', { name: user.displayName });
+
+// Multiple namespaces
+t(['errors:validation.required', 'common:error']);
+```
+
+## Events localization
+
+Event handlers in CommandKit can also benefit from localization,
+allowing you to create multilingual responses and messages for various
+Discord events like message creation, member joins, and more.
+
+### Using translations in event handlers
+
+Since event handlers don't have the same context as commands, you need
+to import the `locale` function directly from the i18n plugin:
+
+```ts title="src/app/events/messageCreate/handler.ts"
+import { locale } from '@commandkit/i18n';
+import type { Message } from 'discord.js';
+
+export default async function onMessageCreate(message: Message) {
+ // Skip bot messages
+ if (message.author.bot) return;
+
+ // Get translations for the guild's preferred locale
+ const { t } = locale(message.guild?.preferredLocale);
+
+ if (message.content.toLowerCase() === 'hello') {
+ await message.reply(
+ t('greeting', { user: message.author.displayName }),
+ );
+ }
+
+ if (message.content.toLowerCase() === 'help') {
+ await message.reply(t('help_message'));
+ }
+}
+```
+
+### Event translation files
+
+Create translation files for your events using descriptive names:
+
+```json title="src/app/locales/en-US/messageCreate.event.json"
+{
+ "greeting": "👋 Hello {{user}}! Welcome to our server!",
+ "help_message": "📖 Use `/help` to see all available commands",
+ "auto_mod": {
+ "warning": "⚠️ {{user}}, please watch your language!",
+ "timeout": "🔇 {{user}} has been timed out for inappropriate language"
+ }
+}
+```
+
+```json title="src/app/locales/fr/messageCreate.event.json"
+{
+ "greeting": "👋 Salut {{user}} ! Bienvenue sur notre serveur !",
+ "help_message": "📖 Utilisez `/help` pour voir toutes les commandes disponibles",
+ "auto_mod": {
+ "warning": "⚠️ {{user}}, attention à votre langage !",
+ "timeout": "🔇 {{user}} a été mis en sourdine pour langage inapproprié"
+ }
+}
+```
+
+### Advanced event localization
+
+#### Guild welcome messages
+
+```ts title="src/app/events/guildMemberAdd/handler.ts"
+import { locale } from '@commandkit/i18n';
+import type { GuildMember } from 'discord.js';
+
+export default async function onGuildMemberAdd(member: GuildMember) {
+ const { t } = locale(member.guild.preferredLocale);
+
+ // Find welcome channel
+ const welcomeChannel = member.guild.channels.cache.find(
+ (channel) => channel.name === 'welcome',
+ );
+
+ if (welcomeChannel?.isTextBased()) {
+ const memberCount = member.guild.memberCount;
+
+ await welcomeChannel.send({
+ content: t('welcome.message', {
+ user: member.displayName,
+ guild: member.guild.name,
+ count: memberCount,
+ }),
+ // You can also send embeds with localized content
+ embeds: [
+ {
+ title: t('welcome.embed.title'),
+ description: t('welcome.embed.description', {
+ user: member.displayName,
+ }),
+ color: 0x00ff00,
+ fields: [
+ {
+ name: t('welcome.embed.fields.rules'),
+ value: t('welcome.embed.fields.rules_description'),
+ },
+ {
+ name: t('welcome.embed.fields.channels'),
+ value: t('welcome.embed.fields.channels_description'),
+ },
+ ],
+ },
+ ],
+ });
+ }
+}
+```
+
+```json title="src/app/locales/en-US/guildMemberAdd.event.json"
+{
+ "welcome": {
+ "message": "🎉 Welcome {{user}} to **{{guild}}**! You're our **{{count}}** member!",
+ "embed": {
+ "title": "Welcome to the Server!",
+ "description": "Hi {{user}}, we're glad to have you here!",
+ "fields": {
+ "rules": "📋 Rules",
+ "rules_description": "Please read our rules in #rules channel",
+ "channels": "💬 Important Channels",
+ "channels_description": "Check out #announcements and #general"
+ }
+ }
+ }
+}
+```
+
+#### Locale detection strategies
+
+When working with events, you have several options for determining the
+appropriate locale:
+
+##### 1. Guild preferred locale (recommended)
+
+```ts
+// Use the guild's preferred locale set by server admins
+const { t } = locale(message.guild?.preferredLocale);
+```
+
+##### 2. User locale
+
+```ts
+// Use the individual user's Discord locale
+const { t } = locale(message.author.locale);
+```
+
+##### 3. Fallback chain
+
+```ts
+// Try multiple locale sources with fallback
+const detectedLocale =
+ message.guild?.preferredLocale || message.author.locale || 'en-US';
+
+const { t } = locale(detectedLocale);
+```
+
+##### 4. Custom locale detection
+
+```ts
+async function detectLocale(message: Message): Promise {
+ // Custom logic: check database for user preferences
+ const userSettings = await getUserSettings(message.author.id);
+ if (userSettings?.language) {
+ return userSettings.language;
+ }
+
+ // Fall back to guild locale
+ return message.guild?.preferredLocale || 'en-US';
+}
+
+export default async function onMessageCreate(message: Message) {
+ const userLocale = await detectLocale(message);
+ const { t } = locale(userLocale);
+
+ // Use translations...
+}
+```
+
+### Error handling with localization
+
+Handle translation errors gracefully in event handlers:
+
+```ts title="src/app/events/messageCreate/autoMod.ts"
+import { locale } from '@commandkit/i18n';
+import type { Message } from 'discord.js';
+
+const FORBIDDEN_WORDS = ['spam', 'scam', 'hack'];
+
+export default async function autoModerator(message: Message) {
+ if (message.author.bot) return;
+
+ try {
+ const { t } = locale(message.guild?.preferredLocale);
+
+ const hasViolation = FORBIDDEN_WORDS.some((word) =>
+ message.content.toLowerCase().includes(word),
+ );
+
+ if (hasViolation) {
+ // Delete the message
+ await message.delete();
+
+ // Send warning
+ await message.channel.send({
+ content: t('moderation.auto_warning', {
+ user: message.author.displayName,
+ }),
+ });
+
+ // Log the action
+ console.log(
+ t('moderation.log_message', {
+ user: message.author.tag,
+ guild: message.guild?.name,
+ channel: message.channel.name,
+ }),
+ );
+ }
+ } catch (error) {
+ // Fallback to English if translation fails
+ console.error('Translation error:', error);
+ await message.channel.send(
+ `⚠️ ${message.author.displayName}, please follow our community guidelines.`,
+ );
+ }
+}
+```
+
+### Best practices for event localization
+
+1. **Always provide fallbacks**: Event handlers should gracefully
+ handle missing translations
+2. **Use appropriate locale sources**: Choose between guild, user, or
+ custom locale detection based on context
+3. **Keep translations consistent**: Use the same tone and style
+ across events and commands
+4. **Test with different locales**: Ensure your events work correctly
+ with various language settings
+5. **Cache translations when possible**: For high-frequency events,
+ consider caching translation functions
+
+## Usage with legacy commands
+
+The i18n plugin is fully compatible with the
+[`@commandkit/legacy`](./06-commandkit-legacy.mdx) plugin, allowing
+you to add internationalization to existing projects without major
+refactoring.
+
+### Translations in legacy commands
+
+For legacy commands, import the `locale` function directly from the
+i18n plugin:
+
+```ts title="src/app/commands/legacy/ping.js"
+import { locale } from '@commandkit/i18n';
+
+export async function run({ interaction, client }) {
+ // The locale function can automatically infer the locale from the interaction
+ const { t } = locale();
+
+ const latency = client.ws.ping;
+
+ return interaction.reply({
+ content: t('response', { latency }),
+ ephemeral: true,
+ });
+}
+
+// Legacy command metadata
+export const data = {
+ name: 'ping',
+ description: 'Check bot latency',
+};
+```
+
+#### Manual locale specification
+
+You can also specify a locale manually:
+
+```ts
+export async function run({ interaction, client }) {
+ // Use a specific locale
+ const guildLocale = interaction.guild?.preferredLocale || 'en-US';
+ const { t } = locale(guildLocale);
+
+ return interaction.reply({
+ content: t('response', { latency: client.ws.ping }),
+ ephemeral: true,
+ });
+}
+```
+
+### Legacy command translation files
+
+Translation files for legacy commands work the same way as modern
+commands:
+
+```json title="src/app/locales/en-US/ping.json"
+{
+ "response": "🏓 Pong! Latency: **{{latency}}ms**",
+ "error": "❌ Could not determine latency"
+}
+```
+
+```json title="src/app/locales/fr/ping.json"
+{
+ "response": "🏓 Pong! Latence: **{{latency}}ms**",
+ "error": "❌ Impossible de déterminer la latence"
+}
+```
+
+### Migrating legacy commands
+
+Here's how to migrate an existing legacy command to use i18n:
+
+#### Before (No localization)
+
+```ts title="src/app/commands/legacy/userinfo.js"
+export async function run({ interaction }) {
+ const user =
+ interaction.options.getUser('user') || interaction.user;
+ const member = interaction.guild?.members.cache.get(user.id);
+
+ const embed = {
+ title: `User Information - ${user.username}`,
+ fields: [
+ { name: 'Username', value: user.username, inline: true },
+ { name: 'ID', value: user.id, inline: true },
+ {
+ name: 'Account Created',
+ value: user.createdAt.toDateString(),
+ inline: true,
+ },
+ ],
+ color: 0x0099ff,
+ };
+
+ if (member) {
+ embed.fields.push(
+ {
+ name: 'Joined Server',
+ value: member.joinedAt?.toDateString() || 'Unknown',
+ inline: true,
+ },
+ {
+ name: 'Roles',
+ value: member.roles.cache.map((role) => role.name).join(', '),
+ inline: false,
+ },
+ );
+ }
+
+ return interaction.reply({ embeds: [embed] });
+}
+
+export const data = {
+ name: 'userinfo',
+ description: 'Get information about a user',
+ options: [
+ {
+ name: 'user',
+ description: 'The user to get information about',
+ type: 6, // User
+ required: false,
+ },
+ ],
+};
+```
+
+#### After (With localization)
+
+```ts title="src/app/commands/legacy/userinfo.js"
+import { locale } from '@commandkit/i18n';
+
+export async function run({ interaction }) {
+ const { t } = locale();
+
+ const user =
+ interaction.options.getUser('user') || interaction.user;
+ const member = interaction.guild?.members.cache.get(user.id);
+
+ const embed = {
+ title: t('embed.title', { username: user.username }),
+ fields: [
+ {
+ name: t('embed.fields.username'),
+ value: user.username,
+ inline: true,
+ },
+ { name: t('embed.fields.id'), value: user.id, inline: true },
+ {
+ name: t('embed.fields.created'),
+ value: user.createdAt.toDateString(),
+ inline: true,
+ },
+ ],
+ color: 0x0099ff,
+ };
+
+ if (member) {
+ embed.fields.push(
+ {
+ name: t('embed.fields.joined'),
+ value: member.joinedAt?.toDateString() || t('embed.unknown'),
+ inline: true,
+ },
+ {
+ name: t('embed.fields.roles'),
+ value: member.roles.cache.map((role) => role.name).join(', '),
+ inline: false,
+ },
+ );
+ }
+
+ return interaction.reply({ embeds: [embed] });
+}
+
+export const data = {
+ name: 'userinfo',
+ description: 'Get information about a user',
+ options: [
+ {
+ name: 'user',
+ description: 'The user to get information about',
+ type: 6, // User
+ required: false,
+ },
+ ],
+};
+```
+
+```json title="src/app/locales/en-US/userinfo.json"
+{
+ "embed": {
+ "title": "User Information - {{username}}",
+ "unknown": "Unknown",
+ "fields": {
+ "username": "Username",
+ "id": "ID",
+ "created": "Account Created",
+ "joined": "Joined Server",
+ "roles": "Roles"
+ }
+ }
+}
+```
+
+```json title="src/app/locales/es/userinfo.json"
+{
+ "embed": {
+ "title": "Información del Usuario - {{username}}",
+ "unknown": "Desconocido",
+ "fields": {
+ "username": "Nombre de Usuario",
+ "id": "ID",
+ "created": "Cuenta Creada",
+ "joined": "Se Unió al Servidor",
+ "roles": "Roles"
+ }
+ }
+}
+```
+
+### Best practices for legacy commands
+
+1. **Gradual Migration**: You can migrate commands one at a time
+ without affecting others
+2. **Consistent Naming**: Use the same translation keys across legacy
+ and modern commands when possible
+3. **Error Handling**: Always provide fallback text for missing
+ translations
+4. **Testing**: Test legacy commands with different locales to ensure
+ compatibility
+
+### Mixed command types
+
+You can use both legacy and modern commands with i18n in the same
+project:
+
+```ts title="commandkit.config.ts"
+import { defineConfig } from 'commandkit';
+import { i18n } from '@commandkit/i18n';
+
+export default defineConfig({
+ // Enable both legacy and modern command support
+ plugins: [i18n()],
+
+ // Configuration for mixed command types
+ paths: {
+ commands: 'src/app/commands',
+ events: 'src/app/events',
+ },
+});
+```
+
+This allows you to:
+
+- Keep existing legacy commands working
+- Add new commands using the modern syntax
+- Gradually migrate legacy commands when convenient
+- Maintain consistent localization across all command types
+
+## Complete example
+
+Here's a comprehensive example showing various localization features:
+
+```json title="src/app/locales/en-US/moderation.json"
+{
+ "$command": {
+ "name": "ban",
+ "description": "Ban a user from the server",
+ "options": [
+ {
+ "name": "user",
+ "description": "The user to ban"
+ },
+ {
+ "name": "reason",
+ "description": "Reason for the ban"
+ },
+ {
+ "name": "duration",
+ "description": "Ban duration",
+ "choices": [
+ { "name": "Permanent", "value": "permanent" },
+ { "name": "1 Day", "value": "1d" },
+ { "name": "1 Week", "value": "1w" }
+ ]
+ }
+ ]
+ },
+ "success": "✅ **{{user}}** has been banned",
+ "success_with_reason": "✅ **{{user}}** has been banned\n**Reason:** {{reason}}",
+ "errors": {
+ "user_not_found": "❌ User not found",
+ "insufficient_permissions": "❌ I don't have permission to ban this user",
+ "cannot_ban_self": "❌ You cannot ban yourself"
+ }
+}
+```
+
+```ts title="src/app/commands/moderation.ts"
+import type { ChatInputCommand } from 'commandkit';
+
+export const chatInput: ChatInputCommand = async (ctx) => {
+ const { t } = ctx.locale();
+ const user = ctx.interaction.options.getUser('user', true);
+ const reason = ctx.interaction.options.getString('reason');
+
+ try {
+ // Attempt to ban the user
+ await ctx.interaction.guild?.members.ban(user, {
+ reason: reason || undefined,
+ });
+
+ // Send localized success message
+ const successKey = reason ? 'success_with_reason' : 'success';
+ await ctx.interaction.reply({
+ content: t(successKey, {
+ user: user.displayName,
+ reason,
+ }),
+ });
+ } catch (error) {
+ // Send localized error message
+ await ctx.interaction.reply({
+ content: t('errors.insufficient_permissions'),
+ ephemeral: true,
+ });
+ }
+};
+```
diff --git a/apps/website/docs/guide/05-official-plugins/06-commandkit-legacy.mdx b/apps/website/docs/guide/05-official-plugins/06-commandkit-legacy.mdx
new file mode 100644
index 00000000..9a84a857
--- /dev/null
+++ b/apps/website/docs/guide/05-official-plugins/06-commandkit-legacy.mdx
@@ -0,0 +1,71 @@
+---
+title: '@commandkit/legacy'
+---
+
+:::danger
+
+This plugin is not recommended as we do not plan to continue
+supporting the legacy structure. We only recommend this if you're
+trying to incrementally upgrade your application. If you prefer the
+old way, you can use v0.1.10, but please note, that version is not
+actively maintained.
+
+:::
+
+The `@commandkit/legacy` plugin enables support for legacy command and
+event handlers. This is particularly useful when migrating from an
+older version of CommandKit or another framework, as it allows you to
+incrementally upgrade your application without requiring immediate
+major changes to your existing code.
+
+## Installation
+
+```sh npm2yarn
+npm install @commandkit/legacy
+```
+
+## Usage
+
+Add the Legacy plugin to your CommandKit configuration:
+
+```js
+import { defineConfig } from 'commandkit';
+import { legacy } from '@commandkit/legacy';
+
+export default defineConfig({
+ plugins: [legacy()],
+});
+```
+
+## Features
+
+The Legacy plugin provides the following features:
+
+1. **Legacy Command Handler Support**: Enables support for older
+ command handler formats, making migration easier.
+
+2. **Legacy Event Handler Support**: Allows you to continue using your
+ existing event handler structure.
+
+3. **Legacy Validation Support**: Maintains compatibility with
+ existing validation methods.
+
+4. **Hot Module Replacement (HMR)**: The plugin automatically enables
+ HMR for legacy commands, events, and validations, improving the
+ development experience by allowing you to see changes without
+ restarting your bot.
+
+## Migration Strategy
+
+The Legacy plugin is designed as a transitional tool. While it allows
+you to continue using your existing code structure, it's recommended
+to gradually migrate your commands, events, and validators to the new
+CommandKit format over time.
+
+This approach enables you to:
+
+1. Upgrade your bot's framework immediately without breaking existing
+ functionality
+2. Incrementally refactor your code at your own pace
+3. Take advantage of new CommandKit features for new commands while
+ maintaining backward compatibility
diff --git a/apps/website/docs/guide/05-official-plugins/07-commandkit-redis.mdx b/apps/website/docs/guide/05-official-plugins/07-commandkit-redis.mdx
new file mode 100644
index 00000000..a09fb015
--- /dev/null
+++ b/apps/website/docs/guide/05-official-plugins/07-commandkit-redis.mdx
@@ -0,0 +1,68 @@
+---
+title: '@commandkit/redis'
+---
+
+The `@commandkit/redis` plugin provides a cache provider for
+CommandKit that allows you to store data in Redis. It works out of the
+box with the [`@commandkit/cache`](./03-commandkit-cache.mdx) plugin.
+
+## Installation
+
+```sh npm2yarn
+npm install @commandkit/redis
+```
+
+## Usage
+
+This plugin will automatically register the Redis cache provider with
+your CommandKit instance.
+
+```js
+import { defineConfig } from 'commandkit';
+import { redis } from '@commandkit/redis';
+
+export default defineConfig({
+ plugins: [redis()],
+});
+```
+
+Once configured, your cache functions will automatically use Redis as
+the storage backend:
+
+```ts
+async function getCachedData() {
+ 'use cache'; // This directive enables caching for the function
+
+ // Your data retrieval logic
+ const data = await getFromDatabase('something');
+
+ return data;
+}
+```
+
+## Manual Configuration
+
+If you need more control over the Redis client configuration, you can
+set up the cache provider manually instead of using the plugin:
+
+```ts
+import { setCacheProvider } from '@commandkit/cache';
+import { RedisCacheProvider } from '@commandkit/redis';
+import { Redis } from 'ioredis';
+
+// Configure the Redis client with custom options
+const redis = new Redis({
+ host: 'your-redis-host',
+ port: 6379,
+ // Add other Redis options as needed
+});
+
+const redisProvider = new RedisCacheProvider(redis);
+
+// Register the provider with CommandKit
+setCacheProvider(redisProvider);
+```
+
+This approach gives you full control over the Redis client
+configuration while still integrating with CommandKit's caching
+system.
diff --git a/apps/website/docs/guide/05-official-plugins/08-commandkit-tasks.mdx b/apps/website/docs/guide/05-official-plugins/08-commandkit-tasks.mdx
new file mode 100644
index 00000000..69eaaad5
--- /dev/null
+++ b/apps/website/docs/guide/05-official-plugins/08-commandkit-tasks.mdx
@@ -0,0 +1,679 @@
+---
+title: '@commandkit/tasks'
+---
+
+The CommandKit tasks plugin provides a powerful way to schedule and
+manage background tasks in your Discord application. Whether you need
+to run periodic maintenance, send scheduled reminders, or perform data
+cleanup, the tasks plugin has you covered.
+
+## Installation
+
+```bash
+npm install @commandkit/tasks
+```
+
+## Basic setup
+
+Add the tasks plugin to your CommandKit configuration:
+
+```ts title="commandkit.config.ts"
+import { defineConfig } from 'commandkit';
+import { tasks } from '@commandkit/tasks';
+
+export default defineConfig({
+ plugins: [tasks()],
+});
+```
+
+## Setting up a driver
+
+By default, the plugin will initialize the SQLite driver. You can set
+up a different driver by calling `setDriver` function from the
+`@commandkit/tasks` package. If you want to disable the default driver
+initialization behavior, you can pass `initializeDefaultDriver: false`
+to the `tasks()` options in your commandkit config.
+
+```ts
+import { setDriver } from '@commandkit/tasks';
+import { SQLiteDriver } from '@commandkit/tasks/sqlite';
+
+// For development
+setDriver(new SQLiteDriver('./tasks.db'));
+
+// For production, use BullMQ with Redis
+// setDriver(new BullMQDriver({ host: 'localhost', port: 6379 }));
+```
+
+## Available drivers
+
+### SQLite driver (Development)
+
+The SQLite driver provides persistent, file-based task scheduling.
+It's perfect for development environments and single-instance
+applications with job recovery on restart.
+
+**Pros:**
+
+- Persistent job storage
+- Jobs recoverable on restart
+- No external dependencies
+- Lightweight and reliable
+
+**Cons:**
+
+- Single instance only
+- No distributed scheduling support
+- Intended for development use
+
+**Usage:**
+
+```ts
+import { SQLiteDriver } from '@commandkit/tasks/sqlite';
+import { setDriver } from '@commandkit/tasks';
+
+setDriver(new SQLiteDriver('./tasks.db'));
+```
+
+### BullMQ driver (Production)
+
+The BullMQ driver provides robust, distributed task scheduling using
+Redis as the backend. It's ideal for production environments with
+multiple bot instances.
+
+**Pros:**
+
+- Distributed scheduling across multiple instances
+- Persistent task storage
+- Built-in retry mechanisms
+- Production-ready with Redis
+
+**Cons:**
+
+- Requires Redis server
+- More complex setup
+- Additional dependency
+
+**Installation:**
+
+```bash
+npm install bullmq
+```
+
+**Usage:**
+
+```ts
+import { BullMQDriver } from '@commandkit/tasks/bullmq';
+import { setDriver } from '@commandkit/tasks';
+
+setDriver(
+ new BullMQDriver({
+ host: 'localhost',
+ port: 6379,
+ }),
+);
+```
+
+**Advanced Redis configuration:**
+
+```ts
+const driver = new BullMQDriver({
+ host: 'redis.example.com',
+ port: 6379,
+ password: 'your-password',
+ tls: true,
+ retryDelayOnFailover: 100,
+ maxRetriesPerRequest: 3,
+});
+```
+
+### Environment-specific configuration
+
+```ts
+import { tasks } from '@commandkit/tasks';
+import { SQLiteDriver } from '@commandkit/tasks/sqlite';
+import { BullMQDriver } from '@commandkit/tasks/bullmq';
+import { setDriver } from '@commandkit/tasks';
+import { COMMANDKIT_IS_DEV } from 'commandkit';
+
+// Choose driver based on environment
+if (COMMANDKIT_IS_DEV) {
+ setDriver(new SQLiteDriver('./tasks.db'));
+} else {
+ setDriver(
+ new BullMQDriver({
+ host: process.env.REDIS_HOST || 'localhost',
+ port: parseInt(process.env.REDIS_PORT || '6379'),
+ password: process.env.REDIS_PASSWORD,
+ }),
+ );
+}
+
+export default {
+ plugins: [tasks()],
+};
+```
+
+## Creating your first task
+
+Create a file in `src/app/tasks/` to define your tasks:
+
+```ts
+import { task } from '@commandkit/tasks';
+
+export default task({
+ name: 'daily-backup',
+ schedule: '0 0 * * *', // Daily at midnight (cron string)
+ async execute(ctx) {
+ // Your task logic here
+ await performBackup();
+ console.log('Daily backup completed!');
+ },
+});
+```
+
+## Task structure
+
+Every task has the following components:
+
+- **name**: A unique identifier for the task
+- **schedule**: When the task should run (optional for manual
+ execution)
+- **execute**: The main function that performs the task work
+- **prepare**: Optional function to determine if the task should run
+
+## Schedule types
+
+### Cron schedules
+
+Use cron expressions for recurring tasks:
+
+```ts
+export default task({
+ name: 'hourly-task',
+ schedule: '0 * * * *', // Every hour
+ async execute(ctx) {
+ // Task logic
+ },
+});
+```
+
+### Date schedules
+
+Schedule tasks for specific times:
+
+```ts
+export default task({
+ name: 'reminder',
+ schedule: new Date('2024-01-01T12:00:00Z'), // Specific date
+ async execute(ctx) {
+ // Send reminder
+ },
+});
+
+// Or use timestamps
+export default task({
+ name: 'timestamp-task',
+ schedule: Date.now() + 60000, // 1 minute from now
+ async execute(ctx) {
+ // Task logic
+ },
+});
+```
+
+## Task context
+
+The `execute` function receives a context object with useful
+properties:
+
+```ts
+export default task({
+ name: 'context-example',
+ schedule: '0 */6 * * *', // Every 6 hours
+ async execute(ctx) {
+ // Access the Discord.js client
+ const client = ctx.commandkit.client;
+
+ // Access custom data (for dynamic tasks)
+ const { userId, message } = ctx.data;
+
+ // Use the temporary store
+ ctx.store.set('lastRun', Date.now());
+
+ // Send a message to a channel
+ const channel = client.channels.cache.get('channel-id');
+ if (channel?.isTextBased()) {
+ await channel.send('Task executed!');
+ }
+ },
+});
+```
+
+## Conditional execution
+
+Use the `prepare` function to conditionally execute tasks:
+
+```ts
+export default task({
+ name: 'conditional-task',
+ schedule: '0 */2 * * *', // Every 2 hours
+ async prepare(ctx) {
+ // Only run if maintenance mode is not enabled
+ return !ctx.commandkit.store.get('maintenance-mode');
+ },
+ async execute(ctx) {
+ await performMaintenanceChecks();
+ },
+});
+```
+
+## Dynamic tasks
+
+While static tasks are great for recurring operations, you often need
+to create tasks dynamically based on user interactions or events. The
+tasks plugin provides utilities for creating tasks on-demand.
+
+### Creating dynamic tasks
+
+Use the `createTask` function to create tasks programmatically:
+
+```ts
+import { createTask } from '@commandkit/tasks';
+
+// Create a task that runs in 5 minutes
+const taskId = await createTask({
+ name: 'reminder',
+ data: { userId: '123', message: "Don't forget your meeting!" },
+ schedule: Date.now() + 5 * 60 * 1000, // 5 minutes from now
+});
+```
+
+### Reminder system example
+
+Here's a complete example of a reminder command that creates dynamic
+tasks:
+
+```ts
+import type { CommandData, ChatInputCommand } from 'commandkit';
+import { ApplicationCommandOptionType } from 'discord.js';
+import ms from 'ms';
+import { createTask } from '@commandkit/tasks';
+
+export const command: CommandData = {
+ name: 'remind',
+ description: 'remind command',
+ options: [
+ {
+ name: 'time',
+ description: 'The time to remind after. Eg: 6h, 10m, 1d',
+ type: ApplicationCommandOptionType.String,
+ required: true,
+ },
+ {
+ name: 'message',
+ description: 'The message to remind about.',
+ type: ApplicationCommandOptionType.String,
+ required: true,
+ },
+ ],
+};
+
+export const chatInput: ChatInputCommand = async (ctx) => {
+ const time = ctx.options.getString('time', true);
+ const message = ctx.options.getString('message', true);
+ const timeMs = Date.now() + ms(time as `${number}`);
+
+ await createTask({
+ name: 'remind',
+ data: {
+ userId: ctx.interaction.user.id,
+ message,
+ channelId: ctx.interaction.channelId,
+ setAt: Date.now(),
+ },
+ schedule: timeMs,
+ });
+
+ await ctx.interaction.reply(
+ `I will remind you for \`${message}\``,
+ );
+};
+```
+
+### The reminder task
+
+Create a static task definition that handles the actual reminder:
+
+```ts
+// src/app/tasks/remind.ts
+import { task } from '@commandkit/tasks';
+
+export interface RemindTaskData {
+ userId: string;
+ message: string;
+ channelId: string;
+ setAt: number;
+}
+
+export default task({
+ name: 'remind',
+ async execute(ctx) {
+ const { userId, message, channelId, setAt } = ctx.data;
+
+ const channel = await ctx.client.channels.fetch(channelId);
+
+ if (!channel?.isTextBased() || !channel.isSendable()) return;
+
+ await channel.send({
+ content: `<@${userId}>`,
+ embeds: [
+ {
+ title: `You asked me to remind you about:`,
+ description: message,
+ color: 0x0099ff,
+ timestamp: new Date(setAt).toISOString(),
+ },
+ ],
+ });
+ },
+});
+```
+
+### Managing dynamic tasks
+
+You can also delete tasks programmatically:
+
+```ts
+import { deleteTask } from '@commandkit/tasks';
+
+// Cancel a scheduled task
+try {
+ await deleteTask(taskId);
+ console.log('Task cancelled successfully');
+} catch (error) {
+ console.error('Failed to cancel task:', error);
+}
+```
+
+## Advanced patterns
+
+### Task workflows
+
+Organize related tasks to create complex workflows:
+
+```ts
+// src/app/tasks/data-processing.ts
+import { task } from '@commandkit/tasks';
+
+export default task({
+ name: 'data-processing',
+ schedule: '0 2 * * *', // Daily at 2 AM
+ async execute(ctx) {
+ // Step 1: Collect data
+ const data = await collectData();
+ ctx.store.set('collectedData', data);
+
+ // Step 2: Process data immediately
+ const processedData = await processData(data);
+
+ // Step 3: Send notification
+ const channel =
+ ctx.commandkit.client.channels.cache.get('log-channel');
+ if (channel?.isTextBased()) {
+ await channel.send(
+ `Data processing completed for ID: ${data.id}`,
+ );
+ }
+ },
+});
+```
+
+### Task batching
+
+Process multiple items in batches:
+
+```ts
+// src/app/tasks/batch-processing.ts
+import { task } from '@commandkit/tasks';
+
+export default task({
+ name: 'batch-processor',
+ schedule: '0 */6 * * *', // Every 6 hours
+ async execute(ctx) {
+ // Get items to process
+ const items = await getItemsToProcess();
+
+ if (items.length === 0) {
+ console.log('No items to process');
+ return;
+ }
+
+ // Process in batches of 10
+ const batchSize = 10;
+ let processedCount = 0;
+
+ for (let i = 0; i < items.length; i += batchSize) {
+ const batch = items.slice(i, i + batchSize);
+ console.log(
+ `Processing batch ${Math.floor(i / batchSize) + 1}/${Math.ceil(items.length / batchSize)}`,
+ );
+
+ // Process the batch
+ for (const item of batch) {
+ await processItem(item);
+ processedCount++;
+ }
+ }
+
+ console.log(`Completed processing ${processedCount} items`);
+ },
+});
+```
+
+### Task state management
+
+Use the context store to manage state across task executions:
+
+```ts
+// src/app/tasks/state-management.ts
+import { task } from '@commandkit/tasks';
+
+export default task({
+ name: 'stateful-task',
+ schedule: '0 */2 * * *', // Every 2 hours
+ async prepare(ctx) {
+ // Check if we're in a cooldown period
+ const lastRun = ctx.commandkit.store.get('last-run');
+ const cooldown = 30 * 60 * 1000; // 30 minutes
+
+ if (lastRun && Date.now() - lastRun < cooldown) {
+ return false; // Skip execution
+ }
+
+ return true;
+ },
+ async execute(ctx) {
+ // Update last run time
+ ctx.commandkit.store.set('last-run', Date.now());
+
+ // Get or initialize counter
+ const counter =
+ ctx.commandkit.store.get('execution-counter') || 0;
+ ctx.commandkit.store.set('execution-counter', counter + 1);
+
+ console.log(`Task executed ${counter + 1} times`);
+
+ // Perform the actual work
+ await performWork();
+ },
+});
+```
+
+### Task cleanup
+
+Implement cleanup tasks for resource management:
+
+```ts
+// src/app/tasks/cleanup.ts
+import { task } from '@commandkit/tasks';
+
+export default task({
+ name: 'cleanup',
+ schedule: '0 3 * * *', // Daily at 3 AM
+ async execute(ctx) {
+ const cutoffDate = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000); // 7 days ago
+
+ // Clean up old data
+ await cleanupOldRecords(cutoffDate);
+
+ // Clean up expired tasks
+ await cleanupExpiredTasks();
+
+ // Clean up temporary files
+ await cleanupTempFiles();
+
+ console.log('Cleanup completed successfully');
+ },
+});
+```
+
+## Best practices
+
+### Use descriptive task names
+
+```ts
+// Good
+await createTask({
+ name: 'user-reminder',
+ data: { userId, message },
+ schedule: reminderTime,
+});
+
+// Avoid
+await createTask({
+ name: 'task',
+ data: { userId, message },
+ schedule: reminderTime,
+});
+```
+
+### Validate input data
+
+```ts
+import type { CommandData, ChatInputCommand } from 'commandkit';
+import { ApplicationCommandOptionType } from 'discord.js';
+import ms from 'ms';
+import { createTask } from '@commandkit/tasks';
+
+export const chatInput: ChatInputCommand = async (ctx) => {
+ const timeStr = ctx.options.getString('time', true);
+ const message = ctx.options.getString('message', true);
+
+ // Validate time format
+ const delay = ms(timeStr as `${number}`);
+ if (!delay || delay < 60000 || delay > 30 * 24 * 60 * 60 * 1000) {
+ await ctx.interaction.reply(
+ 'Please specify a time between 1 minute and 30 days.',
+ );
+ return;
+ }
+
+ // Validate message length
+ if (message.length > 1000) {
+ await ctx.interaction.reply(
+ 'Message too long. Please keep it under 1000 characters.',
+ );
+ return;
+ }
+
+ // Create task
+ await createTask({
+ name: 'reminder',
+ data: { userId: ctx.interaction.user.id, message },
+ schedule: Date.now() + delay,
+ });
+
+ await ctx.interaction.reply('Reminder scheduled successfully!');
+};
+```
+
+### Handle errors gracefully
+
+```ts
+await receive('user-events', async (message) => {
+ try {
+ // Process the message
+ await processUserEvent(message);
+ } catch (error) {
+ // Log error but don't crash
+ console.error('Failed to process event:', error);
+
+ // Optionally retry or send to dead letter queue
+ await handleFailedMessage(message, error);
+ }
+});
+```
+
+### Implement proper error handling
+
+```ts
+export default task({
+ name: 'resilient-task',
+ async execute(ctx) {
+ try {
+ // Try the primary operation
+ await performOperation();
+ } catch (error) {
+ console.error('Primary operation failed:', error);
+
+ // Try fallback operation
+ try {
+ await performFallbackOperation();
+ console.log('Fallback operation succeeded');
+ } catch (fallbackError) {
+ console.error(
+ 'Fallback operation also failed:',
+ fallbackError,
+ );
+
+ // Log the failure for manual intervention
+ const channel =
+ ctx.commandkit.client.channels.cache.get('error-channel');
+ if (channel?.isTextBased()) {
+ await channel.send(
+ `Task failed: ${error.message}. Fallback also failed: ${fallbackError.message}`,
+ );
+ }
+ }
+ }
+ },
+});
+```
+
+## Driver comparison
+
+| Feature | SQLite | BullMQ |
+| -------------------- | ----------- | --------------- |
+| **Setup Complexity** | Simple | Moderate |
+| **Dependencies** | None | `bullmq`, Redis |
+| **Persistence** | File-based | Redis |
+| **Distributed** | No | Yes |
+| **Job Recovery** | Yes | Yes |
+| **Production Ready** | Development | Yes |
+| **Memory Usage** | Low | Moderate |
+
+## Choosing the right driver
+
+### Use SQLite when:
+
+- You're developing locally
+- You want persistent job storage during development
+- You have a single bot instance
+- You need job recovery on restart
+
+### Use BullMQ when:
+
+- You have multiple bot instances
+- You need distributed task scheduling
+- You're deploying to production
+- You need advanced features like retries and monitoring
diff --git a/apps/website/docs/guide/06-community-plugins.mdx b/apps/website/docs/guide/06-community-plugins.mdx
new file mode 100644
index 00000000..c86fa70c
--- /dev/null
+++ b/apps/website/docs/guide/06-community-plugins.mdx
@@ -0,0 +1,30 @@
+---
+title: Community Plugins
+---
+
+CommandKit plugins are a new way to extend the functionality of your
+Discord application.
+
+If you've built a plugin that you'd like to share with the community,
+please submit a pull request to the
+[CommandKit GitHub repository](https://github.com/underctrl-io/commandkit)
+and adding your plugin to the list below.
+
+:::info
+
+If you'd like to learn how to create a plugin for CommandKit, you can
+check out the following guides:
+
+- [Creating a runtime plugin](./07-creating-plugins/01-creating-runtime-plugin.mdx)
+- [Creating a compiler plugin](./07-creating-plugins/02-creating-compiler-plugin.mdx)
+
+:::
+
+### [Example plugin](#)
+
+This is a short placeholder description to help with the structure of
+this list, and future pull requests.
+
+- [Documentation](#)
+- [GitHub repository](#)
+- [npm package](#)
diff --git a/apps/website/docs/guide/06-plugins/01-commandkit-plugins.mdx b/apps/website/docs/guide/06-plugins/01-commandkit-plugins.mdx
deleted file mode 100644
index ce1643fd..00000000
--- a/apps/website/docs/guide/06-plugins/01-commandkit-plugins.mdx
+++ /dev/null
@@ -1,17 +0,0 @@
----
-title: CommandKit Plugins
-description: CommandKit plugins are a powerful way to extend the functionality of your CommandKit application. They allow you to add new features, modify existing ones, and customize the behavior of your application.
----
-
-CommandKit offers a powerful plugin api that allows you to extend the functionality of your CommandKit application. This means you can create custom plugins that add new features, modify existing ones, or even change the way CommandKit works. CommandKit offers two types of plugins:
-
-1. **Compiler Plugins**: These plugins are used at compile time to modify the way CommandKit compiles your application. They are used to add new features or modify existing ones. For example, you can use a compiler plugin to transform the source code itself (e.g. CommandKit's `use cache` or `use macro` directives).
-2. **Runtime Plugins**: These plugins are used at runtime to modify the way CommandKit works. They are used to add new features or modify existing ones. For example, you can use a runtime plugin to add new commands or even custom handlers.
-
-## Official Plugins
-
-CommandKit provides a set of official plugins that you can use to extend the functionality of your application.
-
-- **[@commandkit/redis](./official-plugins/01-redis.mdx)**: This plugin registers a redis cache provider for CommandKit. It allows you to store cached data in redis.
-- **[@commandkit/i18n](./official-plugins/02-i18n.mdx)**: This plugin enables internationalization (i18n) for CommandKit using [i18next](https://www.i18next.com/). It allows you to translate your application into different languages.
-- **[@commandkit/legacy](./official-plugins/03-legacy.mdx)**: This plugin enables legacy commands and events handler in CommandKit. This is useful if you are migrating from an older version of CommandKit and want to incrementally upgrade your application without making major changes.
diff --git a/apps/website/docs/guide/06-plugins/official-plugins/02-i18n.mdx b/apps/website/docs/guide/06-plugins/official-plugins/02-i18n.mdx
deleted file mode 100644
index fcbc2eea..00000000
--- a/apps/website/docs/guide/06-plugins/official-plugins/02-i18n.mdx
+++ /dev/null
@@ -1,13 +0,0 @@
----
-title: i18n Plugin
-description: The i18n plugin for CommandKit enables internationalization (i18n) for CommandKit using i18next. It allows you to translate your application into different languages.
----
-
-import Tabs from '@theme/Tabs';
-import TabItem from '@theme/TabItem';
-
-# i18n Plugin
-
-The i18n plugin integrates i18next into CommandKit, allowing you to internationalize your Discord bot and translate your commands, responses, and other content into multiple languages.
-
-Check out [Localization with i18n](../../11-localization/01-introduction.mdx) for more details on how to use the i18n plugin.
diff --git a/apps/website/docs/guide/06-plugins/official-plugins/04-devtools.mdx b/apps/website/docs/guide/06-plugins/official-plugins/04-devtools.mdx
deleted file mode 100644
index 675b4ca4..00000000
--- a/apps/website/docs/guide/06-plugins/official-plugins/04-devtools.mdx
+++ /dev/null
@@ -1,11 +0,0 @@
----
-title: DevTools Plugin
-description: The DevTools plugin for CommandKit provides a set of tools and utilities to enhance the development experience. It includes features like command inspection, performance monitoring, and debugging tools.
----
-
-import Tabs from '@theme/Tabs';
-import TabItem from '@theme/TabItem';
-
-The DevTools plugin for CommandKit provides a set of tools and utilities to enhance the development experience. It includes features like command inspection, performance monitoring, and debugging tools.
-
-Check out the [DevTools Guide](../../12-devtools/01-introduction.mdx) for more details on how to use the DevTools plugin.
diff --git a/apps/website/docs/guide/06-plugins/official-plugins/06-ai.mdx b/apps/website/docs/guide/06-plugins/official-plugins/06-ai.mdx
deleted file mode 100644
index be579bb5..00000000
--- a/apps/website/docs/guide/06-plugins/official-plugins/06-ai.mdx
+++ /dev/null
@@ -1,10 +0,0 @@
----
-title: AI Plugin
-description: Learn how to use the AI plugin to power your bot's commands and events.
----
-
-## AI Plugin
-
-The AI plugin allows you to execute your bot commands using large language models. This enables you to use your bot's features entirely through natural language.
-
-Please refer to the [AI Powered Commands](../../13-ai-powered-commands/01-introduction.mdx) guide for more details.
diff --git a/apps/website/docs/guide/07-creating-plugins/01-creating-runtime-plugin.mdx b/apps/website/docs/guide/07-creating-plugins/01-creating-runtime-plugin.mdx
new file mode 100644
index 00000000..68beda03
--- /dev/null
+++ b/apps/website/docs/guide/07-creating-plugins/01-creating-runtime-plugin.mdx
@@ -0,0 +1,591 @@
+---
+title: Creating a Runtime Plugin
+---
+
+Runtime plugins are used to modify the way CommandKit runs your
+application. They are executed at runtime and can be used to modify
+the behavior of CommandKit. These plugins can register new commands,
+modify existing ones, or even change the way CommandKit works. For
+example, you can use a runtime plugin to add new commands or even
+custom handlers.
+
+## Getting started with runtime plugins
+
+To create a runtime plugin, you need to create a class that extends
+the
+[`RuntimePlugin`](../../api-reference/commandkit/classes/runtime-plugin.mdx)
+class from CommandKit. This class provides various hooks that you can
+use to modify the behavior of CommandKit at different stages of
+execution.
+
+Here's a basic example of a runtime plugin that logs when the bot logs
+in:
+
+```ts title="my-runtime-plugin.ts"
+import {
+ type CommandKitPluginRuntime,
+ RuntimePlugin,
+} from 'commandkit';
+
+export class MyRuntimePlugin extends RuntimePlugin {
+ // Override any hooks you want to use
+ async onAfterClientLogin(
+ ctx: CommandKitPluginRuntime,
+ ): Promise {
+ console.log('Bot has logged in!');
+ }
+}
+```
+
+To use this plugin in your application, you can add it to your
+CommandKit instance.
+
+```ts title="commandkit.config.ts"
+import { CommandKit } from 'commandkit';
+import { MyRuntimePlugin } from './plugins/MyRuntimePlugin';
+
+const commandkit = new CommandKit({
+ // ... other options
+ plugins: [new MyRuntimePlugin()],
+});
+```
+
+## Lifecycle hooks
+
+Runtime plugins provide hooks at different stages of your
+application's lifecycle. These hooks allow you to perform actions
+before or after key events in your bot's execution.
+
+### Initialization hooks
+
+These hooks are called during the initialization of your CommandKit
+application.
+
+#### `onBeforeCommandsLoad(ctx)`
+
+Called before commands are loaded into CommandKit.
+
+**Parameters:**
+
+- `ctx`: The runtime context, providing access to CommandKit internals
+ like the client, command context, etc.
+
+**Example use case:** Initialize resources needed by commands, or
+modify command loading behavior.
+
+```ts title="my-runtime-plugin.ts"
+async onBeforeCommandsLoad(ctx: CommandKitPluginRuntime): Promise {
+ console.log('Loading commands soon...');
+ // Initialize resources needed for commands
+}
+```
+
+#### `onAfterCommandsLoad(ctx)`
+
+Called after all commands have been loaded into CommandKit.
+
+**Parameters:**
+
+- `ctx`: The runtime context.
+
+**Example use case:** Log the loaded commands, perform validation, or
+add metadata to loaded commands.
+
+```ts title="my-runtime-plugin.ts"
+async onAfterCommandsLoad(ctx: CommandKitPluginRuntime): Promise {
+ console.log(`Loaded ${ctx.commands.size} commands!`);
+ // Do something with the loaded commands
+}
+```
+
+#### `onBeforeEventsLoad(ctx)`
+
+Called before event handlers are loaded into CommandKit.
+
+**Parameters:**
+
+- `ctx`: The runtime context.
+
+**Example use case:** Set up prerequisites for event handlers.
+
+```ts title="my-runtime-plugin.ts"
+async onBeforeEventsLoad(ctx: CommandKitPluginRuntime): Promise {
+ console.log('Loading event handlers soon...');
+ // Prepare event handling prerequisites
+}
+```
+
+#### `onAfterEventsLoad(ctx)`
+
+Called after all event handlers have been loaded.
+
+**Parameters:**
+
+- `ctx`: The runtime context.
+
+**Example use case:** Log the loaded events, add custom event
+processing.
+
+```ts title="my-runtime-plugin.ts"
+async onAfterEventsLoad(ctx: CommandKitPluginRuntime): Promise {
+ console.log('All event handlers loaded!');
+ // Perform post-event-loading actions
+}
+```
+
+#### `onBeforeClientLogin(ctx)`
+
+Called right before the Discord client attempts to log in.
+
+**Parameters:**
+
+- `ctx`: The runtime context.
+
+**Example use case:** Perform last-minute setup before connecting to
+Discord.
+
+```ts title="my-runtime-plugin.ts"
+async onBeforeClientLogin(ctx: CommandKitPluginRuntime): Promise {
+ console.log('Bot is about to connect to Discord...');
+ // Last minute preparations before login
+}
+```
+
+#### `onAfterClientLogin(ctx)`
+
+Called after the Discord client has successfully logged in.
+
+**Parameters:**
+
+- `ctx`: The runtime context.
+
+**Example use case:** Start additional services once the bot is
+online, log connection details.
+
+```ts title="my-runtime-plugin.ts"
+async onAfterClientLogin(ctx: CommandKitPluginRuntime): Promise {
+ console.log(`Bot logged in as ${ctx.client.user?.tag}!`);
+ // Start additional services, initialize post-login resources
+}
+```
+
+### Router initialization hooks
+
+These hooks are called when the command and event routers are
+initialized.
+
+#### `onCommandsRouterInit(ctx)`
+
+Called after the commands router is initialized.
+
+**Parameters:**
+
+- `ctx`: The runtime context.
+
+**Example use case:** Add custom command routing logic or middleware.
+
+```ts title="my-runtime-plugin.ts"
+async onCommandsRouterInit(ctx: CommandKitPluginRuntime): Promise {
+ console.log('Commands router initialized!');
+ // Add custom middleware or routing logic
+}
+```
+
+#### `onEventsRouterInit(ctx)`
+
+Called after the events router is initialized.
+
+**Parameters:**
+
+- `ctx`: The runtime context.
+
+**Example use case:** Add custom event handling logic.
+
+```ts title="my-runtime-plugin.ts"
+async onEventsRouterInit(ctx: CommandKitPluginRuntime): Promise {
+ console.log('Events router initialized!');
+ // Set up custom event processing
+}
+```
+
+## Event handler hooks
+
+These hooks are called when processing events.
+
+#### `willEmitEvent(ctx, event)`
+
+Called before an event is emitted.
+
+**Parameters:**
+
+- `ctx`: The runtime context.
+- `event`: The event object being emitted.
+
+**Example use case:** Modify event data before it's emitted, or cancel
+the event.
+
+```ts title="my-runtime-plugin.ts"
+async willEmitEvent(
+ ctx: CommandKitPluginRuntime,
+ event: CommandKitEventDispatch
+): Promise {
+ // prevent other plugins from handling this event
+ event.accept();
+ // Modify event data
+ event.args.push('This is a custom argument');
+}
+```
+
+Now the event listener will receive the modified event data.
+
+## Command registration hooks
+
+These hooks are called during the command registration process,
+allowing you to modify commands before they're registered with
+Discord.
+
+#### `prepareCommand(ctx, commands)`
+
+Called before a command is loaded for registration, allowing you to
+modify the command data.
+
+**Parameters:**
+
+- `ctx`: The runtime context.
+- `commands`: The command object being loaded (CommandBuilderLike).
+
+**Returns:** The modified command or `null` to use the original.
+
+**Example use case:** Add default options to commands, modify command
+properties.
+
+```ts title="my-runtime-plugin.ts"
+async prepareCommand(
+ ctx: CommandKitPluginRuntime,
+ commands: CommandBuilderLike
+): Promise {
+ // Add a disclaimer to all command descriptions
+ if (commands.description) {
+ commands.description = `[BETA] ${commands.description}`;
+ }
+ return commands;
+}
+```
+
+#### `onBeforeRegisterCommands(ctx, event)`
+
+Called before command registration process starts, allowing you to
+cancel or modify the registration.
+
+**Parameters:**
+
+- `ctx`: The runtime context.
+- `event`: The command registration event data.
+
+**Example use case:** Log commands being registered, filter commands,
+or handle registration manually.
+
+```ts title="my-runtime-plugin.ts"
+async onBeforeRegisterCommands(
+ ctx: CommandKitPluginRuntime,
+ event: PreRegisterCommandsEvent
+): Promise {
+ console.log(`Registering ${event.commands.length} commands...`);
+ // You can modify event.handled = true to cancel standard registration
+}
+```
+
+#### `onBeforeRegisterGlobalCommands(ctx, event)`
+
+Called before global commands are registered with Discord.
+
+**Parameters:**
+
+- `ctx`: The runtime context.
+- `event`: The command registration event data.
+
+**Example use case:** Handle global command registration differently.
+
+```ts title="my-runtime-plugin.ts"
+async onBeforeRegisterGlobalCommands(
+ ctx: CommandKitPluginRuntime,
+ event: PreRegisterCommandsEvent
+): Promise {
+ console.log(`Registering ${event.commands.length} global commands...`);
+ // Custom global command registration logic
+}
+```
+
+#### `onBeforePrepareGuildCommandsRegistration(ctx, event)`
+
+Called before guild commands are prepared for registration, before
+guilds are resolved.
+
+**Parameters:**
+
+- `ctx`: The runtime context.
+- `event`: The command registration event data.
+
+**Example use case:** Modify guild commands before guild resolution.
+
+```ts title="my-runtime-plugin.ts"
+async onBeforePrepareGuildCommandsRegistration(
+ ctx: CommandKitPluginRuntime,
+ event: PreRegisterCommandsEvent
+): Promise {
+ console.log('Preparing guild commands for registration...');
+ // Modify guild commands before guilds are resolved
+}
+```
+
+#### `onBeforeRegisterGuildCommands(ctx, event)`
+
+Called before guild commands are registered, after guilds have been
+resolved.
+
+**Parameters:**
+
+- `ctx`: The runtime context.
+- `event`: The command registration event data.
+
+**Example use case:** Custom guild command registration logic.
+
+```ts title="my-runtime-plugin.ts"
+async onBeforeRegisterGuildCommands(
+ ctx: CommandKitPluginRuntime,
+ event: PreRegisterCommandsEvent
+): Promise {
+ console.log('Registering guild commands...');
+ // Custom guild command registration
+}
+```
+
+## Interaction hooks
+
+These hooks are called when processing interactions and messages.
+
+#### `onBeforeInteraction(ctx, interaction)`
+
+Called before an interaction is handled.
+
+**Parameters:**
+
+- `ctx`: The runtime context.
+- `interaction`: The Discord.js Interaction object.
+
+**Example use case:** Log interactions, perform checks before
+handling, add analytics.
+
+```ts title="my-runtime-plugin.ts"
+async onBeforeInteraction(
+ ctx: CommandKitPluginRuntime,
+ interaction: Interaction
+): Promise {
+ if (interaction.isCommand()) {
+ console.log(`Command used: ${interaction.commandName}`);
+ }
+ // Add analytics, perform checks, etc.
+}
+```
+
+#### `onBeforeMessageCommand(ctx, message)`
+
+Called before a message command is processed.
+
+**Parameters:**
+
+- `ctx`: The runtime context.
+- `message`: The Discord.js Message object.
+
+**Example use case:** Filter messages, add logging, perform checks.
+
+```ts title="my-runtime-plugin.ts"
+async onBeforeMessageCommand(
+ ctx: CommandKitPluginRuntime,
+ message: Message
+): Promise {
+ console.log(`Potential message command: ${message.content}`);
+ // Message filtering, logging, etc.
+}
+```
+
+#### `executeCommand(ctx, env, source, command, execute)`
+
+Called before a command is executed, allowing you to handle command
+execution yourself.
+
+**Parameters:**
+
+- `ctx`: The runtime context.
+- `env`: The CommandKitEnvironment containing command context.
+- `source`: The interaction or message that triggered the command.
+- `command`: The prepared command execution.
+- `execute`: The function that would normally execute the command.
+
+**Returns:** `true` if you handled the command execution, `false` to
+let CommandKit handle it.
+
+**Example use case:** Custom command execution, permission checking,
+rate limiting.
+
+```ts title="my-runtime-plugin.ts"
+async executeCommand(
+ ctx: CommandKitPluginRuntime,
+ env: CommandKitEnvironment,
+ source: Interaction | Message,
+ command: PreparedAppCommandExecution,
+ execute: () => Promise
+): Promise {
+ // Check if command is being rate limited
+ if (this.isRateLimited(command.name, source.user.id)) {
+ if (source instanceof Interaction && source.isRepliable()) {
+ await source.reply('You are being rate limited! Try again later.');
+ }
+ return true; // We handled it, don't execute the normal way
+ }
+
+ // Let CommandKit handle it normally
+ return false;
+}
+```
+
+#### `onAfterCommand(ctx, env)`
+
+Called after a command and all its deferred functions are executed.
+
+**Parameters:**
+
+- `ctx`: The runtime context.
+- `env`: The CommandKitEnvironment containing command context.
+
+**Example use case:** Logging, analytics, cleanup after command
+execution.
+
+```ts title="my-runtime-plugin.ts"
+async onAfterCommand(
+ ctx: CommandKitPluginRuntime,
+ env: CommandKitEnvironment
+): Promise {
+ console.log(`Command ${env.command.name} executed by ${env.user?.tag}`);
+ // Log command usage, perform cleanup, etc.
+}
+```
+
+## Development hooks
+
+These hooks are used during development.
+
+#### `performHMR(ctx, event)`
+
+Called when a Hot Module Replacement event is received in development
+mode.
+
+**Parameters:**
+
+- `ctx`: The runtime context.
+- `event`: The HMR event data.
+
+**Example use case:** Handle reloading of specific resources during
+development.
+
+```ts title="my-runtime-plugin.ts"
+async performHMR(
+ ctx: CommandKitPluginRuntime,
+ event: CommandKitHMREvent
+): Promise {
+ console.log(`HMR event: ${event.type}`);
+ // Custom HMR handling
+}
+```
+
+## Creating a Complete Runtime Plugin
+
+Now that we've covered all available hooks, let's put together a more
+complete example of a runtime plugin that implements several hooks:
+
+```ts title="my-runtime-plugin.ts"
+import {
+ type PreparedAppCommandExecution,
+ type CommandKitEnvironment,
+ type CommandKitPluginRuntime,
+ type CommandBuilderLike,
+ RuntimePlugin,
+} from 'commandkit';
+import type { Interaction, Message } from 'discord.js';
+
+export class LoggingPlugin extends RuntimePlugin {
+ private commandUsage = new Map();
+
+ async onAfterClientLogin(
+ ctx: CommandKitPluginRuntime,
+ ): Promise {
+ console.log(`Bot logged in as ${ctx.client.user?.tag}!`);
+ console.log(`Serving ${ctx.client.guilds.cache.size} guilds`);
+ }
+
+ async onBeforeInteraction(
+ ctx: CommandKitPluginRuntime,
+ interaction: Interaction,
+ ): Promise {
+ if (interaction.isCommand()) {
+ console.log(
+ `Command "${interaction.commandName}" used by ${interaction.user.tag}`,
+ );
+ }
+ }
+
+ async executeCommand(
+ ctx: CommandKitPluginRuntime,
+ env: CommandKitEnvironment,
+ source: Interaction | Message,
+ command: PreparedAppCommandExecution,
+ execute: () => Promise,
+ ): Promise {
+ const startTime = Date.now();
+
+ // Let CommandKit handle normal execution
+ await execute();
+
+ // Log execution time
+ const execTime = Date.now() - startTime;
+ console.log(
+ `Command "${command.name}" executed in ${execTime}ms`,
+ );
+
+ // Track usage
+ this.commandUsage.set(
+ command.name,
+ (this.commandUsage.get(command.name) || 0) + 1,
+ );
+
+ return true; // We've handled it
+ }
+
+ async prepareCommand(
+ ctx: CommandKitPluginRuntime,
+ command: CommandBuilderLike,
+ ): Promise {
+ // Add dev tag to command names in development environment
+ if (process.env.NODE_ENV === 'development') {
+ command.name = `dev_${command.name}`;
+ }
+
+ return command;
+ }
+}
+```
+
+## Best practices
+
+When creating runtime plugins, here are some best practices to follow:
+
+1. **Keep it focused** - Each plugin should have a specific purpose.
+2. **Handle errors** - Wrap your hook implementations in try/catch
+ blocks to prevent crashing your bot.
+3. **Be performance-conscious** - Some hooks may be called frequently,
+ so keep your code efficient.
+4. **Respect the return values** - Make sure to return the appropriate
+ values from hooks, especially for hooks like `executeCommand` that
+ can change the execution flow.
+5. **Document your plugins** - If you're creating plugins for others
+ to use, document how they should be configured and used.
diff --git a/apps/website/docs/guide/07-creating-plugins/02-creating-compiler-plugin.mdx b/apps/website/docs/guide/07-creating-plugins/02-creating-compiler-plugin.mdx
new file mode 100644
index 00000000..726de687
--- /dev/null
+++ b/apps/website/docs/guide/07-creating-plugins/02-creating-compiler-plugin.mdx
@@ -0,0 +1,128 @@
+---
+title: Creating a Compiler Plugin
+---
+
+CommandKit compiler plugins are a powerful way to extend the
+functionality of your CommandKit application by modifying the way
+CommandKit compiles your application. They are used to add new
+features or modify existing ones. For example, you can use a compiler
+plugin to transform the source code itself (e.g. CommandKit's
+`"use cache"` or `"use macro"` directives).
+
+## Creating a compiler plugin
+
+To create a compiler plugin, you need to create a new class that
+extends the
+[`CompilerPlugin`](../../api-reference/commandkit/classes/compiler-plugin.mdx)
+class. This class should implement the `transform` method, which is
+called by CommandKit to transform the source code. The `transform`
+method should return the transformed source code.
+
+```ts title="my-compiler-plugin.ts"
+import {
+ type CompilerPluginRuntime,
+ CompilerPlugin,
+ MaybeFalsey,
+ PluginTransformParameters,
+ TransformedResult,
+} from 'commandkit';
+
+export class MyPlugin extends CompilerPlugin {
+ // this is the name of the plugin
+ public readonly name = 'my-plugin';
+
+ public async activate(ctx: CompilerPluginRuntime): Promise {
+ // this is where you can keep initialization logic
+ }
+
+ public async deactivate(ctx: CompilerPluginRuntime): Promise {
+ // this is where you can keep cleanup logic
+ }
+
+ public async transform(
+ params: PluginTransformParameters,
+ ): Promise> {
+ const { contents } = params;
+
+ // this is just an example, you can do whatever you want with the contents
+ const transformedContents = contents.replace(
+ /console\.log/g,
+ 'console.warn',
+ );
+
+ // return the transformed contents
+ return {
+ contents: result,
+ loader: params.loader,
+ };
+ }
+}
+```
+
+## Extending CommandKit templates
+
+CommandKit templates are a way to autogenerate files and other
+contents through the `commandkit create` CLI command. CommandKit by
+default comes with `command` and `event` templates. Compiler plugins
+can register custom templates to be used by the `commandkit create`
+command.
+
+```ts title="my-compiler-plugin.ts"
+import {
+ type CompilerPluginRuntime,
+ CompilerPlugin,
+} from 'commandkit';
+
+export class MyPlugin extends CompilerPlugin {
+ public readonly name = 'my-plugin';
+
+ public async activate(ctx: CompilerPluginRuntime): Promise {
+ // Registers `commandkit create event ` template
+ ctx.registerTemplate('event', async (args: string[]) => {
+ const [name, path] = args;
+
+ const template = `
+ export default async function on${name[0].toUpperCase() + name.slice(1)}() {
+ console.log('${name} event fired!');
+ };
+ `.trim();
+
+ await writeFile(join(path, 'event.ts'), template);
+ });
+ }
+
+ public async deactivate(ctx: CompilerPluginRuntime): Promise {
+ // remove the template from the registry when the plugin is deactivated
+ ctx.unregisterTemplate('event');
+ }
+}
+```
+
+:::warning
+
+The `registerTemplate` method must be called inside the `activate`
+method, and the `unregisterTemplate` method must be called inside the
+`deactivate` method.
+
+:::
+
+:::info
+
+Plugins may override the default templates by registering their own
+templates with the same name.
+
+:::
+
+## Using rolldown plugins
+
+You can also use rolldown plugins as compiler plugins to transform
+your source code.
+
+```ts title="commandkit.config.ts"
+import { defineConfig } from 'commandkit';
+import { someRolldownPlugin } from 'some-rolldown-plugin';
+
+export default defineConfig({
+ rolldownPlugins: [someRolldownPlugin()],
+});
+```
diff --git a/apps/website/docs/guide/07-file-system-conventions/03-category-directory.mdx b/apps/website/docs/guide/07-file-system-conventions/03-category-directory.mdx
deleted file mode 100644
index 40ffd221..00000000
--- a/apps/website/docs/guide/07-file-system-conventions/03-category-directory.mdx
+++ /dev/null
@@ -1,6 +0,0 @@
----
-title: '(category) directory'
-description: The (category) directory is used to group related commands together.
----
-
-Category directories are special directories inside `src/app/commands`, with `()` as their name. The `(category)` directory is used to group related commands together. This is useful for organizing your commands into logical groups, making it easier to manage and maintain your code. This directory has no effect on the command execution, but it is a good practice to use it to keep your code organized. You can nest multiple `(category)` directories to create a hierarchy of commands. For example, you can have a `fun` directory with a `jokes` subdirectory, and inside the `jokes` subdirectory, you can have multiple command files related to jokes.
diff --git a/apps/website/docs/guide/07-file-system-conventions/04-commands-directory.mdx b/apps/website/docs/guide/07-file-system-conventions/04-commands-directory.mdx
deleted file mode 100644
index ed7342b8..00000000
--- a/apps/website/docs/guide/07-file-system-conventions/04-commands-directory.mdx
+++ /dev/null
@@ -1,10 +0,0 @@
----
-title: Commands Directory
-description: The commands directory is used to define the commands for your application.
----
-
-The `src/app/commands` directory is where you define the commands for your application. Each command is defined in its own file, and the file name should match the command name. For example, if you have a command called `ping`, you should create a file called `ping.ts` (or `ping.js`) in the `src/app/commands` directory.
-
-:::info
-Plugins can register external commands, which may be located in a different directory. For more information, see the [Plugins](../06-plugins/03-creating-a-runtime-plugin.mdx) section. As an example, the [legacy plugin](../06-plugins/official-plugins/03-legacy.mdx) can register commands from custom directories.
-:::
diff --git a/apps/website/docs/guide/07-file-system-conventions/05-events-directory.mdx b/apps/website/docs/guide/07-file-system-conventions/05-events-directory.mdx
deleted file mode 100644
index badc3d8c..00000000
--- a/apps/website/docs/guide/07-file-system-conventions/05-events-directory.mdx
+++ /dev/null
@@ -1,40 +0,0 @@
----
-title: Events Directory
-description: The events directory is used to define the events for your Discord.js application.
----
-
-CommandKit provides a special directory called `events` inside the `src/app` directory. This directory is used to define the events for your Discord.js application. Each event is defined in its own directory, and the directory name should match the Discord.js event name. Inside each event directory, you can create multiple function files that will be executed when the event is triggered. For example, if you have an event called `messageCreate`, you should create a directory called `messageCreate` in the `src/app/events` directory, and place your event handler functions inside it.
-
-```
-.
-└── src/
- └── app/
- └── events/
- ├── messageCreate/
- │ ├── give-xp.ts
- │ └── log.ts
- └── ready/
- ├── log.ts
- └── initialize.ts
-```
-
-## Custom events
-
-CommandKit also supports using custom events in the `events` directory. Custom events must be defined in a namespaced directory, which are handled by the active plugins. For example, if you have a custom event called `guildMemberBoost`, you can create a file called `src/app/events/(custom)/guildMemberBoost/handler.ts` which will be handled by the appropriate active plugins.
-
-:::info
-Event namespace directories are not same as [category directories](./03-category-directory.mdx) as they serve a different purpose.
-:::
-
-You can use the custom events the same way as the built-in events. The only difference is that these events are namespaced and are handled by the plugins.
-
-## How plugins execute namespaced events
-
-The following example shows how you can trigger a custom event in your application. The `guildMemberBoost` event under the `custom` namespace is triggered when a user boosts the server.
-
-```ts
-commandkit.events.to('custom').emit('guildMemberBoost', {
- member: member,
- guild: guild,
-});
-```
diff --git a/apps/website/docs/guide/08-advanced/01-setup-commandkit-manually.mdx b/apps/website/docs/guide/08-advanced/01-setup-commandkit-manually.mdx
new file mode 100644
index 00000000..707315e4
--- /dev/null
+++ b/apps/website/docs/guide/08-advanced/01-setup-commandkit-manually.mdx
@@ -0,0 +1,190 @@
+---
+title: Setup CommandKit manually
+description: Learn how to manually setup CommandKit
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+If you don't want to use the
+[CLI](../01-getting-started/02-setup-commandkit.mdx) to automatically
+generate a base CommandKit project, you can integrate CommandKit
+manually into your application.
+
+:::info
+
+While this guide primarily uses TypeScript, you can use JavaScript
+files without any problems to follow along if that's what you're
+comfortable with (e.g. `commandkit.config.js`, `app.js`, etc)
+
+:::
+
+## Configuration file
+
+To get started, create a file called `commandkit.config.ts` at the
+root of your project. This file is used to configure CommandKit and
+optionally its plugins.
+
+```ts title="commandkit.config.ts"
+import { defineConfig } from 'commandkit';
+
+export default defineConfig({});
+```
+
+## Entrypoint file
+
+Then, create a folder called `src`, followed by a file called `app.ts`
+inside it. The file should look like this:
+
+```ts title="src/app.ts"
+import { Client } from 'discord.js';
+
+const client = new Client({
+ intents: ['Guilds'],
+});
+
+client.token = '...'; // Optional: Manually set a bot token
+
+export default client;
+```
+
+With the current entrypoint file created, it's important to understand
+that:
+
+1. The `app.ts` file is the entrypoint for this application. This file
+ **must** export your discord.js client instance.
+2. You don't have to call `client.login()` as CommandKit will handle
+ that for you.
+3. You should store your bot token as an environment variable with
+ either `DISCORD_TOKEN` or `TOKEN` as the name.
+4. If you prefer to have a custom environment variable for your bot
+ token, you can manually assign it to the `client.token` property.
+
+## Adding commands
+
+To add a command, create a folder inside the `src/app` directory
+called `commands` and create your command file (e.g. `ping.ts`). This
+example will use a simple ping/pong command which will register as a
+chat input (slash) and message command.
+
+```ts title="src/app/commands/ping.ts"
+import type {
+ ChatInputCommand,
+ CommandData,
+ MessageCommand,
+} from 'commandkit';
+
+export const command: CommandData = {
+ name: 'ping',
+ description: 'Pong!',
+};
+
+export const chatInput: ChatInputCommand = async ({
+ interaction,
+}) => {
+ await interaction.reply('Pong!');
+};
+
+export const message: MessageCommand = async ({ message }) => {
+ await message.reply('Pong!');
+};
+```
+
+This command will reply with "Pong!" when the user runs the `/ping`
+slash command, or sends `!ping` in the chat.
+
+:::tip
+
+The prefix for message commands can be changed globally or per-guild.
+Learn more
+[here](../02-commands/05-custom-message-commands-prefix.mdx).
+
+:::
+
+## Adding events
+
+To register and handle events emitted by your discord.js client,
+create a folder inside the `src/app` directory called `events` and
+create a folder with the name of the discord.js event you'd like to
+handle (e.g. ready, messageCreate, etc). This example will use the
+`ready` event.
+
+In the `src/app/events/ready` directory, you can create files which
+will export default functions that will be called when the respective
+event is emitted by discord.js. Following the `ready` event example
+mentioned above, you may want to log when your bot comes online. The
+function for that will look like so:
+
+```ts title="src/app/events/log.ts"
+import type { Client } from 'discord.js';
+
+export default function (client: Client) {
+ console.log(`Logged in as ${client.user.username}!`);
+}
+```
+
+:::tip
+
+You can find a full list of events under the
+[discord.js Client class](https://discord.js.org/docs/packages/discord.js/main/Client:Class).
+
+:::
+
+## Running the app in development
+
+To run your application in development mode, you can use the
+`commandkit dev` command in your terminal. This will allow you to
+quickly reload specific parts of your application using Hot Module
+Replacement (HMR) when you make changes to your code, without having
+to restart the entire application.
+
+```sh npm2yarn
+npx commandkit dev
+```
+
+:::warning
+
+When running in development mode, CommandKit will generate a
+`.commandkit` folder in the root of your project. This folder contains
+the compiled files used in development mode. You should not commit
+this folder to your version control system by making sure you add it
+to your `.gitignore` file.
+
+:::
+
+## Building for production
+
+When you are ready to deploy your bot, you can use `commandkit build`
+to create a production build of your bot. This will create a `dist`
+folder in your project directory containing the compiled files.
+
+```sh
+npx commandkit build
+```
+
+:::warning
+
+After running your build script, CommandKit will generate a `dist`
+folder in the root of your project. This folder contains the compiled
+files used for production. You should not commit this folder to your
+version control system by making sure you add it to your `.gitignore`
+file.
+
+:::
+
+## Running the app in production
+
+You can use `commandkit start` to start the bot in production mode.
+This will load the environment variables from the `.env` file in the
+root of your project directory.
+
+```sh
+npx commandkit start
+```
+
+Alternatively, you can manually start the bot in production mode using
+a runtime environment of your choice:
+
+```sh
+node dist/index.js
+```
diff --git a/apps/website/docs/guide/08-advanced/02-file-naming-conventions.mdx b/apps/website/docs/guide/08-advanced/02-file-naming-conventions.mdx
new file mode 100644
index 00000000..d33901fe
--- /dev/null
+++ b/apps/website/docs/guide/08-advanced/02-file-naming-conventions.mdx
@@ -0,0 +1,91 @@
+---
+title: File Naming Conventions
+---
+
+## app.ts
+
+The `src/app.ts` (or `app.js`) is a special file that acts as the
+entry point for your application. It is where you define and export
+your `Discord.js` client instance.
+
+```ts title="src/app.ts"
+import { Client } from 'discord.js';
+
+const client = new Client({
+ /* options */
+});
+
+// Optional: Override the default DISCORD_TOKEN environment variable
+client.token = 'YOUR_BOT_TOKEN';
+
+export default client;
+```
+
+## +middleware.ts
+
+The `src/app/commands/+middleware.ts` (or `+middleware.js`) file is
+used to define middleware functions for your application. Middleware
+functions get called before and after the command execution.
+
+```ts title="src/app/commands/+middleware.ts"
+import { MiddlewareContext } from 'commandkit';
+
+export function beforeExecute(context: MiddlewareContext) {
+ // This function will be executed before the command is executed
+ console.log('Before command execution');
+}
+
+export function afterExecute(context: MiddlewareContext) {
+ // This function will be executed after the command is executed
+ console.log('After command execution');
+}
+```
+
+There are 3 types of middlewares you can create:
+
+- `+middleware.ts` (or `+middleware.js`): This file is used to define
+ middleware functions that will be executed for all sibling commands
+ in the application.
+- `+.middleware.ts` (or `+.middleware.js`): This
+ file is used to define middleware functions that will be executed
+ for a specific command in the application. The `` part of
+ the filename should match the name of the command file. This is
+ useful for defining middleware functions that should be applied to a
+ specific command.
+- `+global-middleware.ts` (or `+global-middleware.js`): This file is
+ used to define middleware functions that will be executed for all
+ commands in the application, regardless of their location in the
+ file system.
+
+:::info
+
+You can learn more about middlewares
+[here](../02-commands/07-middlewares.mdx).
+
+:::
+
+## (category) directory
+
+In your commands directory, you can create a category using
+parenthesis (e.g. `(Moderation)`). This is useful for organizing your
+commands into logical groups, making it easier to manage and maintain
+your code.
+
+```
+src/app/commands/
+├── (Moderation)
+│ ├── ban.ts
+│ ├── kick.ts
+│ ├── mute.ts
+│ ├── unmute.ts
+│ ├── warn.ts
+│ ├── warn-list.ts
+│ └── warn-remove.ts
+```
+
+:::info
+
+You can learn more about command categories
+[here](../02-commands/06-category-directory.mdx).
+
+:::
diff --git a/apps/website/docs/guide/08-advanced/03-sharding-your-bot.mdx b/apps/website/docs/guide/08-advanced/03-sharding-your-bot.mdx
new file mode 100644
index 00000000..39374370
--- /dev/null
+++ b/apps/website/docs/guide/08-advanced/03-sharding-your-bot.mdx
@@ -0,0 +1,66 @@
+---
+title: Sharding Your Discord Bot
+---
+
+Sharding is a method of splitting your bot into multiple processes, or
+"shards". This is useful for large bots that have a lot of guilds, as
+it allows you to distribute the load across multiple processes.
+Discord actually requires sharding for bots in more than 2,500 guilds,
+so understanding how to implement it is crucial as your bot grows.
+
+Sharding is a built-in feature of discord.js, and CommandKit does not
+alter the way sharding works. While this guide covers how to shard
+your bot using CommandKit, you can learn more about sharding in
+discord.js by checking out the
+[discord.js guide](https://discordjs.guide/sharding).
+
+## When to use sharding
+
+You should consider implementing sharding in the following scenarios:
+
+- Your bot is in, or approaching, 2,500 guilds (required by Discord)
+- You're experiencing memory or performance issues with a single
+ process
+- You want to distribute your bot's workload across multiple
+ cores/machines
+- You're considering scaling your bot in the future
+
+## Creating a sharding manager file
+
+You can simply create a new file in your source directory named
+`sharding-manager.ts` (or `sharding-manager.js` if you're using
+JavaScript) and CommandKit will automatically detect it and use it as
+the sharding manager. This file will be responsible for creating the
+shards and managing them.
+
+```ts title="src/sharding-manager.ts"
+import { ShardingManager } from 'discord.js';
+import { join } from 'node:path';
+
+process.loadEnvFile('./.env');
+
+const manager = new ShardingManager(
+ join(import.meta.dirname, 'index.js'),
+ {
+ token: process.env.DISCORD_TOKEN,
+ totalShards: 2,
+ mode: 'worker',
+ },
+);
+
+manager.on('shardCreate', (shard) =>
+ console.log(`Launched shard ${shard.id}`),
+);
+
+await manager.spawn();
+```
+
+:::info
+
+If you're confused about `index.js` being used, this is an
+autogenerated entrypoint file that sets up the CommandKit environment.
+When running `commandkit start` or `commandkit dev`, CommandKit
+automatically detects the entrypoint file (either
+`sharding-manager.js` or `index.js`) and loads it.
+
+:::
diff --git a/apps/website/docs/guide/08-advanced/04-migrating-from-v0.mdx b/apps/website/docs/guide/08-advanced/04-migrating-from-v0.mdx
new file mode 100644
index 00000000..425f9980
--- /dev/null
+++ b/apps/website/docs/guide/08-advanced/04-migrating-from-v0.mdx
@@ -0,0 +1,333 @@
+---
+title: Migrating From v0
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+:::warning
+
+**Minimum Requirements**: CommandKit v1 requires
+[Node.js](https://nodejs.org) version 24 or higher. Please ensure your
+environment meets this requirement before proceeding.
+
+**Migration Focus**: This guide specifically covers converting
+existing v0 code to v1. For information about new v1 features and
+capabilities, please refer to the rest of the documentation after
+completing your migration.
+
+:::
+
+:::info
+
+This guide uses TypeScript examples, but all concepts apply to
+JavaScript projects as well. Simply use the corresponding JavaScript
+file extensions (e.g., `app.js`, `commandkit.config.js`).
+
+:::
+
+This comprehensive guide will walk you through migrating your Discord
+bot from CommandKit v0 to v1. CommandKit v1 introduces significant
+architectural improvements, including a framework-based approach with
+enhanced developer experience features.
+
+## Updating CommandKit
+
+Begin your migration by updating to the latest version of CommandKit:
+
+```bash npm2yarn
+npm install commandkit@next
+```
+
+This command will install CommandKit v1 and update your `package.json`
+with the latest version.
+
+## Project structure migration
+
+CommandKit v1 adopts a framework-based approach with a structured
+`app` directory that serves as the primary location for your bot's
+functionality. This new structure provides better organization and
+enables advanced features like automatic route discovery.
+
+
+
+ ```
+ .
+ ├── src/
+ │ ├── app/
+ │ │ ├── commands/
+ │ │ │ └── ping.ts
+ │ │ └── events/
+ │ │ └── ready/
+ │ │ └── log.ts
+ │ └── app.ts
+ ├── .env
+ ├── commandkit.config.ts
+ ├── package.json
+ └── tsconfig.json
+ ```
+
+
+
+ ```
+ .
+ ├── src/
+ │ ├── commands/
+ │ │ └── ping.ts
+ │ ├── events/
+ │ │ └── ready/
+ │ │ └── log.ts
+ │ └── index.ts
+ ├── .env
+ ├── commandkit.config.mjs
+ ├── package.json
+ └── tsconfig.json
+ ```
+
+
+
+
+## Configuration file updates
+
+CommandKit v1 significantly simplifies configuration by automatically
+detecting your project structure and entry points. The framework now
+supports a standardized set of configuration file names for
+consistency.
+
+**Supported configuration files:**
+
+- `commandkit.config.js`
+- `commandkit.config.mjs`
+- `commandkit.config.cjs`
+- `commandkit.config.ts`
+
+
+
+ ```ts title='commandkit.config.ts'
+ import { defineConfig } from 'commandkit';
+
+ export default defineConfig({
+ // Configuration is now optional for most use cases
+ // CommandKit automatically detects your app structure
+ });
+ ```
+
+
+
+ ```ts title='commandkit.config.mjs'
+ import { defineConfig } from 'commandkit';
+
+ export default defineConfig({
+ src: 'src',
+ main: 'index.mjs',
+ // Manual configuration was required
+ });
+ ```
+
+
+
+
+:::info
+
+The CommandKit CLI commands (`commandkit dev`, `commandkit build`,
+`commandkit start`) continue to work seamlessly with the new
+configuration system.
+
+:::
+
+## Entry point transformation
+
+CommandKit v1 introduces a dramatically simplified entry point system.
+Instead of manually managing the CommandKit instance, bot login, and
+path configurations, you now simply export a configured Discord.js
+client.
+
+
+
+ ```ts title='src/app.ts'
+ import { Client } from 'discord.js';
+
+ const client = new Client({
+ intents: [
+ 'Guilds',
+ 'GuildMessages',
+ 'MessageContent',
+ ],
+ });
+
+ // Optional: Override the default DISCORD_TOKEN env variable
+ client.token = 'CUSTOM_BOT_TOKEN';
+
+ export default client; // CommandKit handles the rest automatically
+ ```
+
+
+
+ ```ts title='src/index.ts'
+ import { Client, GatewayIntentBits } from 'discord.js';
+ import { CommandKit } from 'commandkit';
+ import path from 'path';
+
+ const client = new Client({
+ intents: [
+ 'Guilds',
+ 'GuildMessages',
+ 'MessageContent',
+ ],
+ });
+
+ new CommandKit({
+ client,
+ commandsPath: path.join(__dirname, 'commands'),
+ eventsPath: path.join(__dirname, 'events'),
+ validationsPath: path.join(__dirname, 'validations'),
+ skipBuiltInValidations: true,
+ bulkRegister: true,
+ });
+
+ client.login('YOUR_TOKEN_HERE');
+ ```
+
+
+
+
+## Command file structure
+
+CommandKit v1 modernizes the command API with more intuitive naming
+and cleaner type definitions. The new structure separates command
+metadata from execution logic more clearly, while introducing room for
+new APIs such as message (legacy) commands.
+
+
+
+ ```ts title='src/app/commands/ping.ts'
+ import type { CommandData, ChatInputCommandContext } from 'commandkit';
+
+ export const command: CommandData = {
+ name: 'ping',
+ description: 'Pong!',
+ };
+
+ export function chatInput({ interaction }: ChatInputCommandContext) {
+ interaction.reply('Pong!');
+ }
+ ```
+
+
+
+ ```ts title='src/commands/ping.ts'
+ import type { CommandData, SlashCommandProps } from 'commandkit';
+
+ export const data: CommandData = {
+ name: 'ping',
+ description: 'Pong!',
+ };
+
+ export function run({ interaction }: SlashCommandProps) {
+ interaction.reply('Pong!');
+ }
+ ```
+
+
+
+
+## Middleware system (formerly validations)
+
+CommandKit v1 renames and enhances the validation system to
+"middleware," which better reflects its purpose and capabilities.
+Middleware can now run in various contexts and provides more granular
+control over command execution.
+
+
+
+ ```ts title='src/app/commands/+global-middleware.ts'
+ import type { MiddlewareContext } from 'commandkit';
+
+ export function beforeExecute(ctx: MiddlewareContext) {
+ // Example: Block command execution based on conditions
+ if (ctx.interaction.isRepliable()) {
+ ctx.interaction.reply('Access denied: Command execution blocked.');
+ }
+
+ ctx.cancel(); // Prevents command execution
+ }
+ ```
+
+
+
+ ```ts title='src/validations/block-execution.ts'
+ import type { ValidationProps } from 'commandkit';
+
+ export default function (ctx: ValidationProps) {
+ if (ctx.interaction.isRepliable()) {
+ ctx.interaction.reply('You are blocked from running the command!');
+ }
+ return true; // command will not be executed
+ };
+ ```
+
+
+
+
+For comprehensive middleware documentation, including command and
+directory-specific middlewares, refer to the
+[middleware documentation](../02-commands/07-middlewares.mdx).
+
+## Development environment
+
+CommandKit v1 introduces advanced development features that require
+using the CommandKit CLI. The development server provides Hot Module
+Replacement (HMR) for rapid iteration and supports modern features
+like JSX components.
+
+### Starting development server
+
+```bash npm2yarn
+npx commandkit dev
+```
+
+:::warning
+
+**Generated Files**: The development server creates a `.commandkit`
+directory for temporary files. Add this to your `.gitignore` to
+prevent committing generated files:
+
+```gitignore
+.commandkit/
+```
+
+:::
+
+## Production deployment
+
+### Building your application
+
+```bash npm2yarn
+npx commandkit build
+```
+
+### Starting production application
+
+Option 1 - Using CommandKit CLI (recommended):
+
+```bash npm2yarn
+npx commandkit start
+```
+
+Option 2 - Direct Node.js execution:
+
+```bash
+node dist/index.js
+```
+
+:::warning
+
+**Generated Build Files**: The build process creates a `dist`
+directory. Add this to your `.gitignore` to prevent committing build
+artifacts:
+
+```gitignore
+dist/
+```
+
+:::
diff --git a/apps/website/docs/guide/08-sharding/01-sharding.mdx b/apps/website/docs/guide/08-sharding/01-sharding.mdx
deleted file mode 100644
index eb1efc3c..00000000
--- a/apps/website/docs/guide/08-sharding/01-sharding.mdx
+++ /dev/null
@@ -1,44 +0,0 @@
----
-title: Sharding your bot
-description: Learn how to shard your bot in CommandKit.
----
-
-# Sharding your bot
-
-Sharding is a method of splitting your bot into multiple processes, or "shards". This is useful for large bots that have a lot of guilds, as it allows you to distribute the load across multiple processes. Discord actually requires sharding for bots in more than 2,500 guilds, so understanding how to implement it is crucial as your bot grows.
-
-Sharding is a built-in feature of discord.js, and CommandKit does not alter the way sharding works. In this guide, we will cover how to shard your bot using CommandKit. To learn more about sharding in discord.js, check out the [discord.js documentation](https://discordjs.guide/sharding).
-
-## When to use sharding
-
-You should consider implementing sharding in the following scenarios:
-
-- Your bot is in, or approaching, 2,500 guilds (required by Discord)
-- You're experiencing memory or performance issues with a single process
-- You want to distribute your bot's workload across multiple cores/machines
-- You're planning for future scaling of your bot
-
-## Creating a sharding manager file
-
-You can simply create a new file in your source directory named `sharding-manager.ts` and CommandKit will automatically detect it and use it as the sharding manager. This file will be responsible for creating the shards and managing them.
-
-```ts
-import { ShardingManager } from 'discord.js';
-import { join } from 'node:path';
-
-process.loadEnvFile('./.env');
-
-const manager = new ShardingManager(join(import.meta.dirname, 'index.js'), {
- token: process.env.DISCORD_TOKEN,
- totalShards: 2,
- mode: 'worker',
-});
-
-manager.on('shardCreate', (shard) => console.log(`Launched shard ${shard.id}`));
-
-await manager.spawn();
-```
-
-:::info
-If you're confused about `index.js` being used, this is an autogenerated entrypoint file that sets up the CommandKit environment. When running `commandkit start` or `commandkit dev`, CommandKit automatically detects the entrypoint file (either `sharding-manager.js` or `index.js`) and loads it.
-:::
diff --git a/apps/website/docs/guide/09-useful-utilities/01-feature-flags.mdx b/apps/website/docs/guide/09-useful-utilities/01-feature-flags.mdx
new file mode 100644
index 00000000..451454d1
--- /dev/null
+++ b/apps/website/docs/guide/09-useful-utilities/01-feature-flags.mdx
@@ -0,0 +1,497 @@
+---
+title: Feature Flags
+description:
+ Control feature visibility with CommandKit's built-in feature flag
+ system
+---
+
+Feature flags are a powerful tool for controlling the visibility of
+features in your application. They allow you to enable or disable
+features for specific users or groups, making it easier to test and
+roll out new functionality. This is particularly useful for A/B
+testing, gradual rollouts, and canary releases.
+
+CommandKit natively offers a feature flag system that allows you to
+define flags in your configuration and use them in your code. This
+system is designed to be simple and flexible, allowing you to easily
+manage feature flags across your application.
+
+## Use cases
+
+Feature flags can be used in various scenarios to improve your
+application's development and deployment process:
+
+- **Maintenance Mode**: Enable or disable features during maintenance
+ without affecting the user experience
+- **A/B Testing**: Test different versions of a feature with different
+ user groups (e.g., show one version to 50% of users)
+- **Gradual Rollouts**: Gradually roll out new features to a small
+ percentage of users before full deployment
+- **Canary Releases**: Release features to a small group of users to
+ test in production before wider rollout
+- **Feature Toggles**: Enable or disable features without deploying
+ new code
+- **User Segmentation**: Customize the user experience based on user
+ preferences or behavior
+- **Testing and Debugging**: Isolate features for testing without
+ affecting the entire application
+
+## Defining feature flags
+
+Feature flags can be defined in any file in your project (except
+`app.ts`). The recommended place to define them is in the `src/flags`
+directory.
+
+```ts title="src/flags/embedColorFlag.ts"
+import { flag } from 'commandkit';
+import murmurhash from 'murmurhash';
+
+export const embedColorFlag = flag({
+ name: 'embed-color-flag',
+ description: 'Show red color instead of blue in embeds',
+ identify(ctx) {
+ const command = ctx.command;
+ const id =
+ command?.interaction?.user.id ?? command?.message?.author.id;
+
+ return { id: id ?? null };
+ },
+ decide({ entities }) {
+ if (!entities.id) {
+ // if no ID is provided, we cannot determine the feature flag
+ // so fall back to the default behavior
+ return false;
+ }
+
+ // use murmurhash to hash the ID and determine the bucket
+ // this allows us to have a consistent way to determine the feature flag
+ // based on the user ID, so that the same user will always see the same feature flag
+ // this is useful for A/B testing, where you want to show the same feature to the same user
+ // without changing it every time they use the command
+ // you can use any other hashing function or logic here as needed
+ const hash = murmurhash.v3(entities.id);
+ const bucket = hash % 100;
+
+ // show red color instead of blue in embeds
+ // to 50% of users, based on their ID
+ // this is a simple A/B test
+ // you can use any other logic here
+ return bucket < 50;
+ },
+});
+```
+
+## Using feature flags
+
+Once you have defined a feature flag, you can use it in your code to
+control the visibility of features. You can use the function returned
+by the `flag` function to check if a feature flag is enabled or not.
+
+```ts title="src/commands/hello.ts"
+import { ChatInputCommand, CommandData } from 'commandkit';
+import { embedColorFlag } from '../flags/embedColorFlag';
+
+export const command: CommandData = {
+ name: 'hello',
+ description: 'Hello world command',
+};
+
+export const chatInput: ChatInputCommand = async (ctx) => {
+ const showRedColor = await embedColorFlag();
+
+ await ctx.interaction.reply({
+ embeds: [
+ {
+ title: 'Hello world',
+ description: 'This is a hello world command',
+ color: showRedColor ? 0xff0000 : 0x0000ff,
+ },
+ ],
+ });
+};
+```
+
+Now there's a 50% chance that the embed will be red instead of blue.
+You can use this feature flag to test the new color scheme with a
+subset of users before rolling it out to everyone.
+
+## Context identification
+
+Sometimes you may want to identify specific users or groups for
+feature flags. For example, you may want to show a secret feature to
+server boosters only. In order to identify the context of the flag,
+you can add the `identify` method to the flag definition.
+
+```ts title="src/flags/embedColorFlag.ts"
+import { flag } from 'commandkit';
+import { GuildMember } from 'discord.js';
+
+interface Entity {
+ isBooster: boolean;
+}
+
+export const embedColorFlag = flag({
+ key: 'embed-color-flag',
+ description: 'Show red color instead of blue in embeds',
+ identify(ctx) {
+ let member: GuildMember | null = null;
+
+ if (ctx.command) {
+ member = (ctx.command.interaction || ctx.command.message)
+ ?.member as GuildMember;
+ } else if (ctx.event) {
+ // handle event specific context
+ if (ctx.event.event === 'guildMemberUpdate') {
+ const [_oldMember, newMember] = ctx.event.argumentsAs(
+ 'guildMemberUpdate',
+ );
+
+ member = newMember as GuildMember;
+ }
+ }
+
+ return { isBooster: member?.premiumSince !== null };
+ },
+ decide({ entities }) {
+ return entities.isBooster;
+ },
+});
+```
+
+:::info Understanding Flag Context
+
+Feature flags are **context-aware**, meaning they automatically detect
+and provide the necessary context (like user information, guild data,
+etc.) when used within CommandKit's execution environment.
+
+**Automatic Context Detection:**
+
+- ✅ **Commands** - Flags automatically access interaction/message
+ data
+- ✅ **Events** - Flags automatically access event-specific data
+- ✅ **Middlewares** - Flags automatically access the current
+ execution context
+
+**Manual Context Required:** If you need to use flags outside of these
+contexts (e.g., in utility functions, background jobs, or external API
+handlers), you must manually provide the identification data:
+
+```ts
+// Option 1: Pass values directly
+const result = await myFlag.run({ identify: { isBooster: true } });
+
+// Option 2: Pass as a function (useful for dynamic values)
+const result = await myFlag.run({
+ identify: () => ({ isBooster: user.premiumSince !== null }),
+});
+```
+
+**Best Practice:** Keep flag usage within commands, events, and
+middlewares whenever possible. Manual context passing should only be
+used when absolutely necessary, as it bypasses the automatic context
+detection which causes flags to be less efficient and more
+error-prone.
+
+:::
+
+## Analytics integration
+
+You may want to see how many users are using a specific feature flag.
+You can do this by using the `analytics` plugin of CommandKit, which
+automatically collects the analytics data in the background whenever
+the feature flags are used. See
+[Analytics in CommandKit](../05-official-plugins/02-commandkit-analytics.mdx)
+for more information on how to set it up.
+
+## Custom providers
+
+While CommandKit's built-in feature flag system is powerful for local
+flag management, you may want to integrate with external feature flag
+services like LaunchDarkly, Split, Unleash, or your own custom
+backend. CommandKit supports this through the provider pattern.
+
+### What are flag providers?
+
+Flag providers are adapters that allow CommandKit to fetch feature
+flag configurations from external systems. When a provider is
+configured, your local `decide` function receives additional context
+from the external system, giving you the flexibility to combine local
+logic with remote configuration.
+
+### Setting up a provider
+
+#### Step 1: Implement the FlagProvider interface
+
+```ts title="src/providers/LaunchDarklyProvider.ts"
+import { FlagProvider, FlagConfiguration } from 'commandkit';
+
+export class LaunchDarklyProvider implements FlagProvider {
+ private client: any; // LaunchDarkly SDK client
+
+ constructor(private apiKey: string) {}
+
+ async initialize(): Promise {
+ // Initialize LaunchDarkly client
+ // this.client = LaunchDarkly.initialize(this.apiKey);
+ // await this.client.waitUntilReady();
+ console.log('LaunchDarkly provider initialized');
+ }
+
+ async getFlag(
+ key: string,
+ context?: any,
+ ): Promise {
+ try {
+ // Fetch flag from LaunchDarkly
+ // const variation = await this.client.variation(key, context, false);
+ // const flagDetail = await this.client.variationDetail(key, context, false);
+
+ // Return provider configuration
+ return {
+ enabled: true, // Whether the flag is enabled
+ percentage: 75, // Optional: percentage rollout
+ config: {
+ // Custom configuration from LaunchDarkly
+ colors: ['#ff0000', '#00ff00'],
+ feature: 'enhanced-ui',
+ },
+ };
+ } catch (error) {
+ console.error(
+ `LaunchDarkly flag evaluation failed for ${key}:`,
+ error,
+ );
+ return null;
+ }
+ }
+
+ async hasFlag(key: string): Promise {
+ // Check if flag exists in LaunchDarkly
+ return true;
+ }
+
+ async destroy(): Promise {
+ // Clean up LaunchDarkly client
+ // await this.client.close();
+ }
+}
+```
+
+#### Step 2: Configure the global provider
+
+```ts title="src/app.ts"
+import { setFlagProvider } from 'commandkit';
+import { LaunchDarklyProvider } from './providers/LaunchDarklyProvider';
+
+const provider = new LaunchDarklyProvider(
+ process.env.LAUNCHDARKLY_API_KEY!,
+);
+
+// Initialize and set the global provider
+await provider.initialize();
+setFlagProvider(provider);
+```
+
+#### Step 3: Use provider data in your flags
+
+```ts title="src/flags/embedColorFlag.ts"
+import { flag } from 'commandkit';
+import murmurhash from 'murmurhash';
+
+export const embedColorFlag = flag({
+ key: 'embed-color-flag',
+ description:
+ 'Show different embed colors based on external configuration',
+ identify(ctx) {
+ const command = ctx.command;
+ const id =
+ command?.interaction?.user.id ?? command?.message?.author.id;
+ return { id: id ?? null };
+ },
+ decide({ entities, provider }) {
+ // Local fallback when provider is unavailable
+ if (!provider) {
+ // Use local hashing logic
+ const hash = murmurhash.v3(entities.id || 'anonymous');
+ return hash % 100 < 50;
+ }
+
+ // Provider-based logic
+ if (!provider.enabled) {
+ return false; // Provider disabled this flag
+ }
+
+ // Use provider's percentage rollout
+ if (provider.percentage) {
+ const hash = murmurhash.v3(entities.id || 'anonymous');
+ const bucket = hash % 100;
+ return bucket < provider.percentage;
+ }
+
+ // Use provider's custom config
+ if (provider.config?.colors) {
+ return provider.config.colors[0]; // Return specific color
+ }
+
+ return provider.enabled;
+ },
+});
+```
+
+### Built-in JSON provider
+
+CommandKit includes a simple JSON-based provider for basic external
+configuration:
+
+```ts title="src/providers/config.ts"
+import { JsonFlagProvider, setFlagProvider } from 'commandkit';
+
+const provider = new JsonFlagProvider({
+ 'embed-color-flag': {
+ enabled: true,
+ percentage: 75,
+ config: {
+ colors: ['#ff0000', '#00ff00', '#0000ff'],
+ theme: 'dark',
+ },
+ },
+ 'premium-features': {
+ enabled: true,
+ targeting: {
+ segments: ['premium_users', 'beta_testers'],
+ },
+ },
+});
+
+await provider.initialize();
+setFlagProvider(provider);
+```
+
+### Advanced provider examples
+
+#### Database-backed provider
+
+```ts title="src/providers/DatabaseProvider.ts"
+import { FlagProvider, FlagConfiguration } from 'commandkit';
+
+export class DatabaseProvider implements FlagProvider {
+ constructor(private db: any) {}
+
+ async getFlag(key: string): Promise {
+ const result = await this.db.query(
+ 'SELECT * FROM feature_flags WHERE key = ?',
+ [key],
+ );
+
+ if (!result.length) return null;
+
+ const flag = result[0];
+ return {
+ enabled: flag.enabled,
+ percentage: flag.rollout_percentage,
+ config: JSON.parse(flag.config || '{}'),
+ targeting: JSON.parse(flag.targeting || '{}'),
+ };
+ }
+
+ async hasFlag(key: string): Promise {
+ const result = await this.db.query(
+ 'SELECT 1 FROM feature_flags WHERE key = ?',
+ [key],
+ );
+ return result.length > 0;
+ }
+}
+```
+
+#### Multi-source provider
+
+```ts title="src/providers/MultiProvider.ts"
+import { FlagProvider, FlagConfiguration } from 'commandkit';
+
+export class MultiProvider implements FlagProvider {
+ constructor(private providers: FlagProvider[]) {}
+
+ async getFlag(
+ key: string,
+ context?: any,
+ ): Promise {
+ // Try providers in order, return first successful result
+ for (const provider of this.providers) {
+ try {
+ const result = await provider.getFlag(key, context);
+ if (result) return result;
+ } catch (error) {
+ console.warn(`Provider failed for ${key}:`, error);
+ continue;
+ }
+ }
+ return null;
+ }
+
+ async hasFlag(key: string): Promise {
+ for (const provider of this.providers) {
+ if (await provider.hasFlag(key)) return true;
+ }
+ return false;
+ }
+}
+```
+
+## Best practices
+
+### Always provide fallbacks
+
+Your `decide` function should handle cases where the provider is
+unavailable:
+
+```ts
+decide({ entities, provider }) {
+ // Always check if provider data is available
+ if (!provider) {
+ // Local fallback logic
+ return defaultBehavior(entities);
+ }
+
+ // Provider-based logic
+ return providerBasedLogic(entities, provider);
+}
+```
+
+### Handle provider errors gracefully
+
+Providers may fail due to network issues, API limits, or configuration
+problems. CommandKit automatically catches these errors and continues
+with local evaluation.
+
+### Use provider configuration wisely
+
+The provider's `config` object can contain any data structure your
+external system provides:
+
+```ts
+decide({ entities, provider }) {
+ if (provider?.config?.experimentConfig) {
+ const experiment = provider.config.experimentConfig;
+ return evaluateExperiment(entities, experiment);
+ }
+
+ return defaultLogic(entities);
+}
+```
+
+### Disable analytics when needed
+
+For high-frequency flags or privacy-sensitive scenarios, you can
+disable analytics:
+
+```ts
+export const highFrequencyFlag = flag({
+ key: 'rate-limiting-flag',
+ description: 'High frequency flag for rate limiting',
+ disableAnalytics: true, // Skip analytics tracking
+ decide({ entities }) {
+ return entities.requestCount > 100;
+ },
+});
+```
diff --git a/apps/website/docs/guide/09-useful-utilities/02-commandkit-ratelimit.mdx b/apps/website/docs/guide/09-useful-utilities/02-commandkit-ratelimit.mdx
new file mode 100644
index 00000000..fafb184f
--- /dev/null
+++ b/apps/website/docs/guide/09-useful-utilities/02-commandkit-ratelimit.mdx
@@ -0,0 +1,115 @@
+---
+title: commandkit/ratelimit
+---
+
+CommandKit provides a rate limiter utility that controls the frequency
+of operations such as user actions or API requests. This helps prevent
+system overload by enforcing configurable request limits.
+
+## Basic usage
+
+The simplest way to use rate limiting is with the default settings:
+
+```typescript
+import { ratelimit } from 'commandkit/ratelimit';
+
+// Check if this user can make another request
+const allowed = await ratelimit('user:123');
+if (allowed) {
+ // Process the request
+ console.log('Request processed successfully');
+} else {
+ // User has made too many requests
+ console.log('Please wait before making another request');
+}
+```
+
+## Custom configuration
+
+You can create a custom rate limiter with specific settings:
+
+```typescript
+import { createRateLimiter } from 'commandkit/ratelimit';
+
+// Create a stricter rate limiter for API endpoints
+const apiLimiter = createRateLimiter({
+ maxRequests: 5, // Allow 5 requests
+ interval: 30000, // Per 30 seconds
+});
+
+const allowed = await apiLimiter.limit('api:endpoint');
+```
+
+## Advanced usage
+
+### Checking remaining requests
+
+You can check how many requests a user has left and when the limit
+resets:
+
+```typescript
+import {
+ getRemainingRequests,
+ getResetTime,
+} from 'commandkit/ratelimit';
+
+const remaining = await getRemainingRequests('user:123');
+const resetTime = await getResetTime('user:123');
+
+console.log(`${remaining} requests remaining`);
+console.log(
+ `Limit resets in ${Math.round(resetTime / 1000)} seconds`,
+);
+```
+
+### Manual reset
+
+You can manually reset a user's rate limit when needed:
+
+```typescript
+import { resetRateLimit } from 'commandkit/ratelimit';
+
+// Give the user a fresh start
+await resetRateLimit('user:123');
+```
+
+### Using external storage
+
+Rate limiters store data in memory by default. For multi-server
+deployments, use external storage like Redis to share rate limit data
+across all servers:
+
+```typescript
+import { RateLimiter, RateLimitStorage } from 'commandkit/ratelimit';
+import { RedisRateLimitStorage } from '@commandkit/redis';
+import { Redis } from 'ioredis';
+
+// Create Redis client
+const redis = new Redis();
+
+// Use Redis-based rate limit storage
+const limiter = new RateLimiter(
+ 10,
+ 60000,
+ new RedisRateLimitStorage(redis),
+);
+```
+
+You can also use the convenience function:
+
+```typescript
+import { createRateLimiter } from 'commandkit/ratelimit';
+import { RedisRateLimitStorage } from '@commandkit/redis';
+
+const limiter = createRateLimiter({
+ maxRequests: 10,
+ interval: 60000,
+ storage: new RedisRateLimitStorage(redis),
+});
+```
+
+## Default settings
+
+- **Max Requests**: 10 requests
+- **Time Window**: 60 seconds (1 minute)
+- **Storage**: In-memory (works for single-server applications)
diff --git a/apps/website/docs/guide/14-useful-utilities/02-mutex.mdx b/apps/website/docs/guide/09-useful-utilities/03-commandkit-mutex.mdx
similarity index 56%
rename from apps/website/docs/guide/14-useful-utilities/02-mutex.mdx
rename to apps/website/docs/guide/09-useful-utilities/03-commandkit-mutex.mdx
index 1f80fcf3..8acfd783 100644
--- a/apps/website/docs/guide/14-useful-utilities/02-mutex.mdx
+++ b/apps/website/docs/guide/09-useful-utilities/03-commandkit-mutex.mdx
@@ -1,15 +1,16 @@
---
-title: Mutex
-description: Ensure exclusive access to shared resources with async mutex locks
+title: commandkit/mutex
---
-# Mutex
+A mutex (mutual exclusion) ensures that only one operation can access
+a shared resource at a time. This prevents race conditions and data
+corruption when multiple operations attempt to modify the same
+resource simultaneously.
-A mutex is like a "do not disturb" sign for your data. It ensures that only one operation can access a shared resource at a time. Imagine multiple people trying to edit the same document - a mutex makes sure only one person can edit it at once.
+## Basic usage
-## Basic Usage
-
-The easiest way to use a mutex is with the `withMutex` function, which automatically handles locking and unlocking:
+The easiest way to use a mutex is with the `withMutex` function, which
+automatically handles locking and unlocking:
```typescript
import { withMutex } from 'commandkit/mutex';
@@ -21,7 +22,7 @@ const result = await withMutex('shared-resource', async () => {
});
```
-## Custom Configuration
+## Custom configuration
You can create a custom mutex with different timeout settings:
@@ -37,11 +38,12 @@ const result = await mutex.withLock('resource', async () => {
});
```
-## Advanced Usage
+## Advanced usage
-### Manual Lock Management
+### Manual lock management
-Sometimes you need more control over when locks are acquired and released:
+Sometimes you need more control over when locks are acquired and
+released:
```typescript
import { acquireLock, releaseLock, isLocked } from 'commandkit/mutex';
@@ -63,9 +65,9 @@ const locked = await isLocked('resource');
console.log(`Resource is ${locked ? 'locked' : 'available'}`);
```
-### Cancelling Operations
+### Cancelling operations
-You can cancel a mutex operation if it takes too long or if you need to stop it for any reason:
+You can cancel a mutex operation using an AbortSignal:
```typescript
import { withMutex } from 'commandkit/mutex';
@@ -89,9 +91,10 @@ try {
}
```
-### Using External Storage
+### Using external storage
-By default, mutexes store lock information in memory. If you're running multiple servers, you'll want to use external storage like Redis:
+Mutexes store lock information in memory by default. For multi-server
+deployments, use external storage like Redis:
```typescript
import { Mutex, MutexStorage } from 'commandkit/mutex';
@@ -120,24 +123,7 @@ const mutex = createMutex({
});
```
-## Default Settings
+## Default settings
- **Timeout**: 30 seconds (30000ms)
- **Storage**: In-memory (works for single-server applications)
-
-## Common Use Cases
-
-- **Database Transactions**: Ensure only one operation can modify data at a time
-- **File System Access**: Prevent multiple operations from writing to the same file
-- **Configuration Updates**: Make sure configuration changes don't conflict
-- **Cache Invalidation**: Control when cache is cleared to prevent race conditions
-- **Resource Pool Management**: Manage access to limited resources
-
-## Tips for Beginners
-
-1. **Use `withMutex` When Possible**: It automatically handles cleanup, so you don't forget to release locks
-2. **Set Reasonable Timeouts**: Don't make timeouts too short (might fail unnecessarily) or too long (might hang forever)
-3. **Use Descriptive Names**: Give your resources meaningful names like `user:123:profile` or `database:users`
-4. **Handle Errors**: Always handle cases where lock acquisition fails
-5. **Think About Deadlocks**: Be careful not to create situations where two operations wait for each other
-6. **Consider Your Setup**: Use external storage if you have multiple servers
diff --git a/apps/website/docs/guide/14-useful-utilities/04-async-queue.mdx b/apps/website/docs/guide/09-useful-utilities/04-commandkit-async-queue.mdx
similarity index 64%
rename from apps/website/docs/guide/14-useful-utilities/04-async-queue.mdx
rename to apps/website/docs/guide/09-useful-utilities/04-commandkit-async-queue.mdx
index 950da684..f4dc9f28 100644
--- a/apps/website/docs/guide/14-useful-utilities/04-async-queue.mdx
+++ b/apps/website/docs/guide/09-useful-utilities/04-commandkit-async-queue.mdx
@@ -1,15 +1,15 @@
---
-title: Async Queue
-description: Process tasks sequentially or with limited concurrency using async queues
+title: commandkit/async-queue
---
-# Async Queue
+An async queue manages task execution by processing them sequentially
+or with controlled concurrency. Tasks are queued and executed in
+order, with configurable limits on how many can run simultaneously.
-An async queue is like a line at a bank. Tasks wait in line and get processed one by one (or a few at a time). This helps you control how many things happen simultaneously and ensures everything gets done in an orderly way.
+## Basic usage
-## Basic Usage
-
-The simplest way to use a queue is with the default sequential processing:
+The simplest way to use a queue is with the default sequential
+processing:
```typescript
import { createAsyncQueue } from 'commandkit/async-queue';
@@ -27,7 +27,7 @@ const result2 = await queue.add(async () => {
});
```
-## Custom Configuration
+## Custom configuration
You can create a queue that processes multiple tasks at the same time:
@@ -53,9 +53,9 @@ const promises = [
const results = await Promise.all(promises);
```
-## Advanced Usage
+## Advanced usage
-### Queue Control
+### Queue control
You can pause and resume the queue, and check its status:
@@ -80,9 +80,9 @@ console.log(`Pending: ${queue.getPending()}`);
console.log(`Paused: ${queue.isPaused()}`);
```
-### Cancelling Operations
+### Cancelling operations
-You can cancel queue operations if they take too long or if you need to stop them:
+You can cancel queue operations using an AbortSignal:
```typescript
import { createAsyncQueue } from 'commandkit/async-queue';
@@ -115,7 +115,7 @@ try {
}
```
-### Batch Processing
+### Batch processing
Queues are great for processing large batches of items efficiently:
@@ -136,15 +136,17 @@ const processItem = async (item: number) => {
};
// Add all items to queue
-const promises = items.map((item) => queue.add(() => processItem(item)));
+const promises = items.map((item) =>
+ queue.add(() => processItem(item)),
+);
// Wait for all to complete
const results = await Promise.all(promises);
```
-### Error Handling
+### Error handling
-Queues handle errors gracefully, so one failed task doesn't stop the others:
+Queues handle errors gracefully without stopping other tasks:
```typescript
import { createAsyncQueue } from 'commandkit/async-queue';
@@ -173,27 +175,8 @@ results.forEach((result, index) => {
});
```
-## Default Settings
+## Default settings
- **Concurrency**: 1 (sequential processing)
- **Storage**: In-memory
- **Abort Support**: Yes
-
-## Common Use Cases
-
-- **API Rate Limiting**: Control how many API calls are made at once
-- **File Processing Pipelines**: Process files one by one or in small batches
-- **Database Operation Batching**: Execute database operations in controlled batches
-- **Image Processing Queues**: Process images without overwhelming the system
-- **Email Sending Queues**: Send emails in an orderly way
-- **Background Job Processing**: Handle background tasks efficiently
-
-## Tips for Beginners
-
-1. **Start with Sequential**: Use the default concurrency of 1 for simple cases
-2. **Set Reasonable Limits**: Don't set concurrency too high (might overwhelm resources) or too low (might be too slow)
-3. **Use `onDrain` Callback**: Get notified when all tasks are complete
-4. **Handle Errors**: Use `Promise.allSettled()` to handle individual task failures
-5. **Monitor Queue State**: Check running and pending counts for debugging
-6. **Use Timeouts**: Set timeouts to prevent tasks from hanging forever
-7. **Think About Your Resources**: Set concurrency based on what your system can handle
diff --git a/apps/website/docs/guide/09-useful-utilities/05-commandkit-kv.mdx b/apps/website/docs/guide/09-useful-utilities/05-commandkit-kv.mdx
new file mode 100644
index 00000000..a63d75b8
--- /dev/null
+++ b/apps/website/docs/guide/09-useful-utilities/05-commandkit-kv.mdx
@@ -0,0 +1,245 @@
+---
+title: commandkit/kv
+---
+
+CommandKit provides a built-in key-value store that offers simple,
+persistent storage using SQLite. It supports storing any
+JSON-serializable data types directly, including objects, arrays,
+dates, maps, sets, and more.
+
+## Basic usage
+
+The simplest way to use the key-value store is to create an instance
+and start storing data:
+
+```typescript
+import { KV } from 'commandkit/kv';
+
+// Create a new KV store
+const kv = new KV('data.db');
+
+// Store data directly
+kv.set('user:123', { name: 'John', age: 30 });
+kv.set('counter', 42);
+kv.set('active', true);
+
+// Retrieve data
+const user = kv.get('user:123'); // { name: 'John', age: 30 }
+const counter = kv.get('counter'); // 42
+```
+
+## Configuration options
+
+You can customize the key-value store with various options:
+
+```typescript
+import { openKV } from 'commandkit/kv';
+
+// Create with custom database file
+const kv = openKV('my-bot-data.db');
+
+// In-memory store for testing
+const testKv = openKV(':memory:');
+
+// Create with specific namespace
+const userKv = openKV('data.db', {
+ namespace: 'users',
+});
+```
+
+## Supported data types
+
+The KV store supports storing and retrieving these data types:
+
+- **Primitives**: `string`, `number`, `boolean`, `bigint`, `null`,
+ `undefined`
+- **Objects**: Plain objects, nested objects
+- **Arrays**: Any array of supported types
+- **Dates**: JavaScript Date objects
+- **Collections**: `Map`, `Set`
+- **Buffers**: Node.js Buffer objects
+- **Regular Expressions**: RegExp objects
+- **Functions**: Function objects (serialized as strings)
+
+```typescript
+// Store different data types
+kv.set('user', { name: 'John', preferences: { theme: 'dark' } });
+kv.set('tags', ['javascript', 'typescript', 'discord']);
+kv.set('created_at', new Date());
+kv.set(
+ 'permissions',
+ new Map([
+ ['admin', true],
+ ['user', false],
+ ]),
+);
+kv.set('unique_ids', new Set([1, 2, 3]));
+```
+
+## Dot notation for nested access
+
+Access and modify nested properties using dot notation:
+
+```typescript
+// Store an object
+kv.set('user:123', {
+ name: 'John Doe',
+ settings: {
+ theme: 'dark',
+ notifications: { email: true, push: false },
+ },
+});
+
+// Access nested properties
+const theme = kv.get('user:123.settings.theme'); // 'dark'
+const emailNotifications = kv.get(
+ 'user:123.settings.notifications.email',
+); // true
+
+// Set nested properties
+kv.set('user:123.settings.theme', 'light');
+kv.set('user:123.settings.notifications.push', true);
+```
+
+## Namespaces
+
+Organize your data into logical groups using namespaces:
+
+```typescript
+const kv = openKV('bot-data.db');
+
+// Create namespace instances
+const userKv = kv.namespace('users');
+const configKv = kv.namespace('config');
+const guildKv = kv.namespace('guilds');
+
+// Store data in different namespaces
+userKv.set('123', { name: 'John', level: 5 });
+configKv.set('theme', 'dark');
+guildKv.set('456', { name: 'My Server', premium: true });
+
+// Same key in different namespaces
+userKv.set('settings', { theme: 'light' });
+configKv.set('settings', { maintenance: false });
+```
+
+### Namespace operations
+
+Each namespace has its own isolated operations:
+
+```typescript
+// Check current namespace
+console.log(userKv.getCurrentNamespace()); // 'users'
+
+// List all namespaces
+const namespaces = kv.namespaces();
+console.log('Available namespaces:', namespaces);
+
+// Namespace-specific counts and data
+console.log(`Users: ${userKv.count()}`);
+console.log('User keys:', userKv.keys());
+console.log('All users:', userKv.all());
+```
+
+## Expiration support
+
+Set automatic expiration for temporary data:
+
+```typescript
+// Set data with expiration (1 hour)
+kv.setex(
+ 'session:123',
+ { userId: 123, token: 'abc123' },
+ 60 * 60 * 1000,
+);
+
+// Set expiration for existing keys
+kv.set('user:123', { name: 'John' });
+kv.expire('user:123', 30 * 60 * 1000); // 30 minutes
+
+// Check time to live
+const ttl = kv.ttl('user:123');
+if (ttl > 0) {
+ console.log(`Expires in ${Math.floor(ttl / 1000)} seconds`);
+}
+```
+
+## Transactions
+
+Execute multiple operations atomically:
+
+```typescript
+// Basic transaction
+kv.transaction(() => {
+ kv.set('user:123', { balance: 100 });
+ kv.set('user:456', { balance: 200 });
+ // If any operation fails, all changes are rolled back
+});
+
+// Async transactions
+await kv.transaction(async () => {
+ const user = kv.get('user:123') || { balance: 0 };
+ user.balance += 50;
+ kv.set('user:123', user);
+
+ // Log the transaction
+ kv.set(`transaction:${Date.now()}`, {
+ type: 'deposit',
+ amount: 50,
+ timestamp: new Date(),
+ });
+});
+```
+
+## Bulk operations
+
+Work with multiple entries efficiently:
+
+```typescript
+// Get all data
+const allData = kv.all();
+const keys = kv.keys();
+const values = kv.values();
+const count = kv.count();
+
+// Iterate over entries
+for (const [key, value] of kv) {
+ console.log(`${key}:`, value);
+}
+
+// Filter entries
+const userEntries = [...kv].filter(([key]) =>
+ key.startsWith('user:'),
+);
+
+// Check existence
+if (kv.has('user:123')) {
+ console.log('User exists');
+}
+
+// Delete data
+kv.delete('user:123');
+kv.clear(); // Clear all data in current namespace
+```
+
+## Resource management
+
+Use proper resource management to ensure database connections are
+closed:
+
+```typescript
+// Automatic cleanup with using statement
+{
+ using kv = openKV('data.db');
+ kv.set('key', 'value');
+ // kv is automatically closed when the block ends
+}
+
+// Manual cleanup
+const kv = openKV('data.db');
+try {
+ kv.set('key', 'value');
+} finally {
+ kv.close();
+}
+```
diff --git a/apps/website/docs/guide/14-useful-utilities/03-semaphore.mdx b/apps/website/docs/guide/09-useful-utilities/06-commandkit-semaphore.mdx
similarity index 53%
rename from apps/website/docs/guide/14-useful-utilities/03-semaphore.mdx
rename to apps/website/docs/guide/09-useful-utilities/06-commandkit-semaphore.mdx
index d2249f60..c8ac9508 100644
--- a/apps/website/docs/guide/14-useful-utilities/03-semaphore.mdx
+++ b/apps/website/docs/guide/09-useful-utilities/06-commandkit-semaphore.mdx
@@ -1,15 +1,16 @@
---
-title: Semaphore
-description: Control concurrent access to limited resources with async semaphores
+title: commandkit/semaphore
---
-# Semaphore
+A semaphore controls the number of operations that can access a
+resource concurrently. It maintains a set number of permits, allowing
+only that many operations to proceed simultaneously while others wait
+in queue.
-A semaphore is like a parking lot with a limited number of spaces. It allows a specific number of operations to happen at the same time, but no more. Perfect for controlling access to limited resources like database connections.
+## Basic usage
-## Basic Usage
-
-The easiest way to use a semaphore is with the `withPermit` function, which automatically handles getting and releasing permits:
+The easiest way to use a semaphore is with the `withPermit` function,
+which automatically handles getting and releasing permits:
```typescript
import { withPermit } from 'commandkit/semaphore';
@@ -21,7 +22,7 @@ const result = await withPermit('database-connection', async () => {
});
```
-## Custom Configuration
+## Custom configuration
You can create a semaphore with specific limits and timeout settings:
@@ -33,16 +34,20 @@ const semaphore = createSemaphore({
timeout: 60000, // 60 second timeout
});
-const result = await semaphore.withPermit('api-endpoint', async () => {
- return await apiCall();
-});
+const result = await semaphore.withPermit(
+ 'api-endpoint',
+ async () => {
+ return await apiCall();
+ },
+);
```
-## Advanced Usage
+## Advanced usage
-### Manual Permit Management
+### Manual permit management
-Sometimes you need more control over when permits are acquired and released:
+Sometimes you need more control over when permits are acquired and
+released:
```typescript
import {
@@ -68,9 +73,9 @@ const available = await getAvailablePermits('resource');
console.log(`${available} permits available`);
```
-### Cancelling Operations
+### Cancelling operations
-You can cancel a semaphore operation if it takes too long or if you need to stop it for any reason:
+You can cancel a semaphore operation using an AbortSignal:
```typescript
import { withPermit } from 'commandkit/semaphore';
@@ -94,22 +99,29 @@ try {
}
```
-### Monitoring Semaphore State
+### Monitoring semaphore state
-You can check how many permits are being used and how many are available:
+You can check how many permits are being used and how many are
+available:
```typescript
-import { getAvailablePermits, getAcquiredPermits } from 'commandkit/semaphore';
+import {
+ getAvailablePermits,
+ getAcquiredPermits,
+} from 'commandkit/semaphore';
const available = await getAvailablePermits('database');
const acquired = await getAcquiredPermits('database');
-console.log(`Database connections: ${acquired} active, ${available} available`);
+console.log(
+ `Database connections: ${acquired} active, ${available} available`,
+);
```
-### Using External Storage
+### Using external storage
-By default, semaphores store permit information in memory. If you're running multiple servers, you'll want to use external storage like Redis:
+Semaphores store permit information in memory by default. For
+multi-server deployments, use external storage like Redis:
```typescript
import { Semaphore, SemaphoreStorage } from 'commandkit/semaphore';
@@ -140,26 +152,8 @@ const semaphore = createSemaphore({
});
```
-## Default Settings
+## Default settings
- **Permits**: 1 (sequential access)
- **Timeout**: 30 seconds (30000ms)
- **Storage**: In-memory (works for single-server applications)
-
-## Common Use Cases
-
-- **Database Connection Pooling**: Limit how many database connections are used at once
-- **API Rate Limiting with Concurrency**: Allow multiple API calls but not too many
-- **File Upload Throttling**: Control how many files can be uploaded simultaneously
-- **External Service Access**: Limit calls to third-party services
-- **Resource Pool Management**: Manage access to limited resources like memory or CPU
-
-## Tips for Beginners
-
-1. **Use `withPermit` When Possible**: It automatically handles cleanup, so you don't forget to release permits
-2. **Set Appropriate Limits**: Don't set too many permits (might overwhelm resources) or too few (might be too slow)
-3. **Monitor Usage**: Keep an eye on how many permits are being used to optimize performance
-4. **Use Descriptive Names**: Give your resources meaningful names like `database:main` or `api:external`
-5. **Handle Errors**: Always handle cases where permit acquisition fails
-6. **Consider Your Resources**: Set permit limits based on what your system can actually handle
-7. **Think About Your Setup**: Use external storage if you have multiple servers
diff --git a/apps/website/docs/guide/09-useful-utilities/07-commandkit-queue.mdx b/apps/website/docs/guide/09-useful-utilities/07-commandkit-queue.mdx
new file mode 100644
index 00000000..8c7b6782
--- /dev/null
+++ b/apps/website/docs/guide/09-useful-utilities/07-commandkit-queue.mdx
@@ -0,0 +1,450 @@
+---
+title: '@commandkit/queue'
+---
+
+The CommandKit Queue package provides a service-agnostic message queue
+API for inter-service communication. It allows you to send and receive
+messages between different parts of your application or across
+multiple services using a simple, unified interface.
+
+## Features
+
+- **Service Agnostic**: Works with any message queue implementation
+- **Simple API**: Easy-to-use `send` and `receive` functions
+- **Type Safety**: Full TypeScript support with strong typing
+- **Driver System**: Pluggable drivers for different queue backends
+- **Discord.js Integration**: Built-in support for Discord.js brokers
+- **Redis Support**: Ready-to-use Redis PubSub driver
+
+## Installation
+
+```bash
+npm install @commandkit/queue
+```
+
+For Discord.js integration with Redis:
+
+```bash
+npm install @commandkit/queue @discordjs/brokers ioredis
+```
+
+## Basic setup
+
+### Setting up the driver
+
+Before you can send or receive messages, you need to set up a driver.
+The driver handles the actual message queue backend.
+
+```typescript
+import { setDriver } from '@commandkit/queue';
+import { RedisPubSubDriver } from '@commandkit/queue/discordjs';
+import { PubSubRedisBroker } from '@discordjs/brokers';
+import Redis from 'ioredis';
+
+// Create a Redis connection
+const redis = new Redis();
+
+// Create a broker
+const broker = new PubSubRedisBroker(redis);
+
+// Create a driver
+const driver = new RedisPubSubDriver(broker);
+
+// Set the driver
+setDriver(driver);
+```
+
+### Redis configuration
+
+You can configure Redis for different environments:
+
+```typescript
+import Redis from 'ioredis';
+
+// Local Redis
+const redis = new Redis();
+
+// Or with configuration
+const redis = new Redis({
+ host: 'localhost',
+ port: 6379,
+ password: 'your-password',
+ db: 0,
+});
+
+// Cloud Redis (example with Redis Cloud)
+const redis = new Redis({
+ host: 'your-redis-host.redis.cloud.com',
+ port: 6379,
+ password: 'your-redis-password',
+ tls: {},
+});
+```
+
+## Core concepts
+
+### Topics
+
+Topics are named channels where messages are sent and received. They
+function as categories for different types of messages.
+
+### Messages
+
+Messages can be any JSON-serializable data. They're sent to topics and
+received by subscribers.
+
+### Drivers
+
+Drivers are implementations that handle the actual message queue
+backend. CommandKit provides a unified API that works with any driver.
+
+## Basic operations
+
+### Sending messages
+
+Use the `send` function to publish messages to a topic.
+
+```typescript
+import { send } from '@commandkit/queue';
+
+// Send a simple message
+await send('user-events', { userId: '123', action: 'login' });
+
+// Send different types of data
+await send('notifications', {
+ type: 'welcome',
+ userId: '123',
+ message: 'Welcome to our platform!',
+});
+
+await send('analytics', {
+ event: 'page_view',
+ page: '/dashboard',
+ timestamp: Date.now(),
+});
+```
+
+### Receiving messages
+
+Use the `receive` function to subscribe to messages from a topic.
+
+```typescript
+import { receive } from '@commandkit/queue';
+
+// Basic message handling
+await receive('user-events', (message) => {
+ console.log(`User ${message.userId} performed ${message.action}`);
+});
+
+// Handle different message types
+await receive('notifications', (message) => {
+ switch (message.type) {
+ case 'welcome':
+ console.log(`Welcome message for user ${message.userId}`);
+ break;
+ case 'reminder':
+ console.log(`Reminder: ${message.message}`);
+ break;
+ }
+});
+
+// Async message handling
+await receive('analytics', async (message) => {
+ await processAnalyticsEvent(message);
+});
+```
+
+## Type safety
+
+You can define types for your messages to get better TypeScript
+support.
+
+```typescript
+interface UserEvent {
+ userId: string;
+ action: 'login' | 'logout' | 'register';
+ timestamp?: number;
+}
+
+interface Notification {
+ type: 'welcome' | 'reminder' | 'alert';
+ userId: string;
+ message: string;
+}
+
+// Type-safe sending
+await send('user-events', {
+ userId: '123',
+ action: 'login',
+ timestamp: Date.now(),
+} as UserEvent);
+
+// Type-safe receiving
+await receive('user-events', (message: UserEvent) => {
+ console.log(`User ${message.userId} ${message.action}`);
+});
+```
+
+You can also define typed events for better TypeScript support:
+
+```typescript
+interface QueueEvents {
+ 'user-updates': {
+ userId: string;
+ action: 'login' | 'logout' | 'register';
+ timestamp: number;
+ };
+ 'guild-events': {
+ guildId: string;
+ event: 'member-join' | 'member-leave' | 'role-update';
+ data: any;
+ };
+ analytics: {
+ event: string;
+ data: Record;
+ timestamp: number;
+ };
+}
+
+// Create a typed driver
+const driver = new RedisPubSubDriver(broker);
+setDriver(driver);
+```
+
+## Discord.js integration
+
+CommandKit Queue provides seamless integration with Discord.js through
+the `@discordjs/brokers` package and Redis PubSub.
+
+### Cross-shard communication
+
+Send messages between different shards of your Discord.js application:
+
+```typescript
+// In shard 0
+await send('shard-communication', {
+ fromShard: 0,
+ toShard: 1,
+ type: 'user-status-update',
+ data: { userId: '123456789', status: 'online' },
+});
+
+// In shard 1 (receiving)
+await receive('shard-communication', (message) => {
+ if (message.toShard === 1) {
+ console.log(
+ `Received from shard ${message.fromShard}:`,
+ message.data,
+ );
+ }
+});
+```
+
+### Multi-bot communication
+
+Send messages between different Discord.js bots:
+
+```typescript
+// In bot A
+await send('bot-communication', {
+ fromBot: 'bot-a',
+ toBot: 'bot-b',
+ type: 'user-data-request',
+ data: { userId: '123456789' },
+});
+
+// In bot B
+await receive('bot-communication', async (message) => {
+ if (message.toBot === 'bot-b') {
+ const userData = await getUserData(message.data.userId);
+
+ await send('bot-communication', {
+ fromBot: 'bot-b',
+ toBot: 'bot-a',
+ type: 'user-data-response',
+ data: userData,
+ });
+ }
+});
+```
+
+### Real-time updates
+
+Send real-time updates to connected clients:
+
+```typescript
+// When a user joins a voice channel
+await send('voice-updates', {
+ userId: '123456789',
+ guildId: '987654321',
+ channelId: '111222333',
+ action: 'join',
+ timestamp: Date.now(),
+});
+
+// Handle voice updates
+await receive('voice-updates', (message) => {
+ // Update voice channel status
+ updateVoiceChannelStatus(
+ message.guildId,
+ message.channelId,
+ message.userId,
+ message.action,
+ );
+
+ // Notify other users
+ notifyVoiceChannelUsers(
+ message.guildId,
+ message.channelId,
+ message,
+ );
+});
+```
+
+## Error handling
+
+Always handle errors when sending or receiving messages.
+
+```typescript
+// Handle Redis connection errors
+redis.on('error', (error) => {
+ console.error('Redis connection error:', error);
+});
+
+redis.on('connect', () => {
+ console.log('Connected to Redis');
+});
+
+// Handle broker errors
+broker.on('error', (error) => {
+ console.error('Broker error:', error);
+});
+
+// Handle message processing errors
+await receive('user-updates', async (message) => {
+ try {
+ await processUserUpdate(message);
+ } catch (error) {
+ console.error('Failed to process user update:', error);
+
+ // Optionally retry or send to dead letter queue
+ await send('failed-messages', {
+ originalTopic: 'user-updates',
+ message,
+ error: error.message,
+ timestamp: Date.now(),
+ });
+ }
+});
+```
+
+## Best practices
+
+### Use descriptive topic names
+
+```typescript
+// Good
+await send('user-authentication-events', message);
+await send('order-processing-updates', message);
+
+// Avoid
+await send('events', message);
+await send('data', message);
+```
+
+### Structure your messages
+
+```typescript
+// Good - structured message
+await send('user-events', {
+ type: 'login',
+ userId: '123',
+ timestamp: Date.now(),
+});
+
+// Avoid - unstructured
+await send('user-events', 'user logged in');
+```
+
+### Handle message processing gracefully
+
+```typescript
+await receive('user-events', async (message) => {
+ try {
+ // Process the message
+ await processUserEvent(message);
+
+ // Acknowledge successful processing
+ console.log(`Processed event for user ${message.userId}`);
+ } catch (error) {
+ // Log error but don't crash
+ console.error(
+ `Failed to process event for user ${message.userId}:`,
+ error,
+ );
+
+ // Optionally retry or send to dead letter queue
+ await handleFailedMessage(message, error);
+ }
+});
+```
+
+### Use appropriate message sizes
+
+```typescript
+// Good - reasonable message size
+await send('user-profile-updates', {
+ userId: '123',
+ changes: {
+ displayName: 'New Name',
+ avatar: 'https://example.com/avatar.jpg',
+ },
+});
+
+// Avoid - very large messages
+await send('user-profile-updates', {
+ userId: '123',
+ fullProfile: {
+ /* massive object */
+ },
+});
+```
+
+## Use cases
+
+- **Inter-Service Communication**: Send messages between different
+ parts of your application
+- **Event Broadcasting**: Broadcast events to multiple subscribers
+- **Task Distribution**: Distribute work across multiple workers
+- **Real-time Updates**: Send real-time updates to connected clients
+- **Microservices**: Enable communication between microservices
+
+## Cleanup
+
+Always clean up resources when shutting down:
+
+```typescript
+import { setDriver } from '@commandkit/queue';
+
+// Cleanup function
+async function cleanup() {
+ try {
+ // Close the driver
+ const driver = getDriver(); // You'll need to implement this
+ if (driver && driver.close) {
+ await driver.close();
+ }
+
+ // Close Redis connection
+ await redis.quit();
+
+ console.log('Queue cleanup completed');
+ } catch (error) {
+ console.error('Error during cleanup:', error);
+ }
+}
+
+// Handle graceful shutdown
+process.on('SIGINT', cleanup);
+process.on('SIGTERM', cleanup);
+```
diff --git a/apps/website/docs/guide/14-useful-utilities/01-ratelimit.mdx b/apps/website/docs/guide/14-useful-utilities/01-ratelimit.mdx
deleted file mode 100644
index 4424da63..00000000
--- a/apps/website/docs/guide/14-useful-utilities/01-ratelimit.mdx
+++ /dev/null
@@ -1,119 +0,0 @@
----
-title: Rate Limiter
-description: Control request frequency with configurable limits and intervals
----
-
-# Rate Limiter
-
-Think of a rate limiter like a traffic light for your application. It controls how often something can happen - like how many times a user can click a button or make an API request. This prevents your system from being overwhelmed.
-
-## Basic Usage
-
-The simplest way to use rate limiting is with the default settings:
-
-```typescript
-import { ratelimit } from 'commandkit/ratelimit';
-
-// Check if this user can make another request
-const allowed = await ratelimit('user:123');
-if (allowed) {
- // Process the request
- console.log('Request processed successfully');
-} else {
- // User has made too many requests
- console.log('Please wait before making another request');
-}
-```
-
-## Custom Configuration
-
-Sometimes you need different limits for different situations. You can create a custom rate limiter with specific settings:
-
-```typescript
-import { createRateLimiter } from 'commandkit/ratelimit';
-
-// Create a stricter rate limiter for API endpoints
-const apiLimiter = createRateLimiter({
- maxRequests: 5, // Allow 5 requests
- interval: 30000, // Per 30 seconds
-});
-
-const allowed = await apiLimiter.limit('api:endpoint');
-```
-
-## Advanced Usage
-
-### Checking Remaining Requests
-
-You can check how many requests a user has left and when the limit resets:
-
-```typescript
-import { getRemainingRequests, getResetTime } from 'commandkit/ratelimit';
-
-const remaining = await getRemainingRequests('user:123');
-const resetTime = await getResetTime('user:123');
-
-console.log(`${remaining} requests remaining`);
-console.log(`Limit resets in ${Math.round(resetTime / 1000)} seconds`);
-```
-
-### Manual Reset
-
-In some cases, you might want to reset a user's rate limit (like after they upgrade their account):
-
-```typescript
-import { resetRateLimit } from 'commandkit/ratelimit';
-
-// Give the user a fresh start
-await resetRateLimit('user:123');
-```
-
-### Using External Storage
-
-By default, rate limiters store data in memory. If you're running multiple servers, you'll want to use external storage like Redis so all servers can share the same rate limit information:
-
-```typescript
-import { RateLimiter, RateLimitStorage } from 'commandkit/ratelimit';
-import { RedisRateLimitStorage } from '@commandkit/redis';
-import { Redis } from 'ioredis';
-
-// Create Redis client
-const redis = new Redis();
-
-// Use Redis-based rate limit storage
-const limiter = new RateLimiter(10, 60000, new RedisRateLimitStorage(redis));
-```
-
-You can also use the convenience function:
-
-```typescript
-import { createRateLimiter } from 'commandkit/ratelimit';
-import { RedisRateLimitStorage } from '@commandkit/redis';
-
-const limiter = createRateLimiter({
- maxRequests: 10,
- interval: 60000,
- storage: new RedisRateLimitStorage(redis),
-});
-```
-
-## Default Settings
-
-- **Max Requests**: 10 requests
-- **Time Window**: 60 seconds (1 minute)
-- **Storage**: In-memory (works for single-server applications)
-
-## Common Use Cases
-
-- **API Rate Limiting**: Prevent users from making too many API calls
-- **User Action Throttling**: Limit how often users can click buttons or submit forms
-- **Resource Access Control**: Control access to expensive operations
-- **Spam Prevention**: Stop automated bots from overwhelming your system
-
-## Tips for Beginners
-
-1. **Start Simple**: Use the default `ratelimit()` function for basic needs
-2. **Choose Good Keys**: Use descriptive keys like `user:123` or `api:endpoint` to make debugging easier
-3. **Set Reasonable Limits**: Don't make limits too strict or too loose - find the right balance
-4. **Handle Rejection**: Always check if the rate limit allows the action before proceeding
-5. **Consider Your Users**: Think about legitimate use cases when setting limits
diff --git a/apps/website/docs/guide/14-useful-utilities/index.mdx b/apps/website/docs/guide/14-useful-utilities/index.mdx
deleted file mode 100644
index bd6d78fa..00000000
--- a/apps/website/docs/guide/14-useful-utilities/index.mdx
+++ /dev/null
@@ -1,181 +0,0 @@
----
-title: Useful Utilities
-description: Essential utilities for async operations, concurrency control, and resource management
----
-
-# Useful Utilities
-
-CommandKit provides a collection of essential utilities that help you manage common programming challenges like controlling how many requests can be made, ensuring only one operation accesses a resource at a time, and managing multiple tasks efficiently.
-
-## Available Utilities
-
-### [Rate Limiter](./01-ratelimit.mdx)
-
-Think of rate limiting like a traffic light for your application. It controls how often something can happen - like how many times a user can click a button or make an API request. This prevents your system from being overwhelmed.
-
-```typescript
-import { ratelimit } from 'commandkit/ratelimit';
-
-const allowed = await ratelimit('user:123');
-if (allowed) {
- // Process the request
-}
-```
-
-### [Mutex](./02-mutex.mdx)
-
-A mutex is like a "do not disturb" sign for your data. It ensures that only one operation can access a shared resource at a time. Imagine multiple people trying to edit the same document - a mutex makes sure only one person can edit it at once.
-
-```typescript
-import { withMutex } from 'commandkit/mutex';
-
-const result = await withMutex('shared-resource', async () => {
- return await updateSharedResource();
-});
-```
-
-### [Semaphore](./03-semaphore.mdx)
-
-A semaphore is like a parking lot with a limited number of spaces. It allows a specific number of operations to happen at the same time, but no more. Perfect for controlling access to limited resources like database connections.
-
-```typescript
-import { withPermit } from 'commandkit/semaphore';
-
-const result = await withPermit('database-connection', async () => {
- return await executeDatabaseQuery();
-});
-```
-
-### [Async Queue](./04-async-queue.mdx)
-
-An async queue is like a line at a bank. Tasks wait in line and get processed one by one (or a few at a time). This helps you control how many things happen simultaneously and ensures everything gets done in an orderly way.
-
-```typescript
-import { createAsyncQueue } from 'commandkit/async-queue';
-
-const queue = createAsyncQueue({ concurrency: 3 });
-const result = await queue.add(async () => await processTask());
-```
-
-## Common Patterns
-
-### Cancelling Operations
-
-All utilities support cancellation, which is like having an emergency stop button. You can cancel operations if they take too long or if you need to stop them for any reason.
-
-```typescript
-// Create a timeout that cancels after 5 seconds
-const signal = AbortSignal.timeout(5000);
-
-// Rate limiting with timeout
-const allowed = await ratelimit('user:123');
-
-// Mutex with timeout
-const result = await withMutex(
- 'resource',
- async () => {
- return await criticalOperation();
- },
- 30000,
- signal,
-);
-
-// Semaphore with timeout
-const result = await withPermit(
- 'resource',
- async () => {
- return await limitedOperation();
- },
- 30000,
- signal,
-);
-
-// Queue with timeout
-const queue = createAsyncQueue({ signal });
-const result = await queue.add(async () => await task());
-```
-
-### Using External Storage
-
-All utilities support custom storage implementations for distributed environments:
-
-```typescript
-// Redis storage implementations are available from @commandkit/redis
-import {
- RedisRateLimitStorage,
- RedisMutexStorage,
- RedisSemaphoreStorage,
-} from '@commandkit/redis';
-import { Redis } from 'ioredis';
-
-const redis = new Redis();
-
-// Rate limiting with Redis
-const rateLimiter = createRateLimiter({
- storage: new RedisRateLimitStorage(redis),
-});
-
-// Mutex with Redis
-const mutex = createMutex({
- storage: new RedisMutexStorage(redis),
-});
-
-// Semaphore with Redis
-const semaphore = createSemaphore({
- storage: new RedisSemaphoreStorage(redis),
-});
-```
-
-### Handling Errors Gracefully
-
-When things go wrong, it's important to handle errors properly so your application doesn't crash and can recover gracefully.
-
-```typescript
-try {
- const result = await withMutex('resource', async () => {
- return await criticalOperation();
- });
-} catch (error) {
- if (error.message.includes('aborted')) {
- console.log('Operation was cancelled');
- } else if (error.message.includes('timeout')) {
- console.log('Operation took too long');
- }
-}
-```
-
-## When to Use Each Utility
-
-| Utility | What it does | When to use it |
-| ---------------- | -------------------------------------------- | ------------------------------------------------------ |
-| **Rate Limiter** | Controls how often something can happen | API endpoints, user actions, preventing spam |
-| **Mutex** | Ensures only one thing can access a resource | Database transactions, file operations, config updates |
-| **Semaphore** | Limits how many things can happen at once | Database connections, API concurrency, resource pools |
-| **Async Queue** | Processes tasks in an orderly way | Batch operations, file processing, email sending |
-
-## Best Practices
-
-1. **Choose the right tool**: Think about what you're trying to achieve:
- - Need to limit how often something happens? Use a rate limiter
- - Need to ensure only one thing accesses a resource? Use a mutex
- - Need to limit how many things happen at once? Use a semaphore
- - Need to process tasks in order? Use a queue
-
-2. **Set reasonable timeouts**: Always set timeouts to prevent your application from hanging forever if something goes wrong.
-
-3. **Use descriptive names**: Give your resources meaningful names so you can easily debug issues later.
-
-4. **Handle errors**: Always handle errors properly so your application can recover from problems.
-
-5. **Consider your setup**: If you're running multiple instances of your app, use external storage like Redis.
-
-6. **Monitor usage**: Keep an eye on how these utilities are being used to optimize performance.
-
-## Next Steps
-
-Ready to learn more? Check out the detailed guides for each utility:
-
-- [Rate Limiter Guide](./01-ratelimit.mdx)
-- [Mutex Guide](./02-mutex.mdx)
-- [Semaphore Guide](./03-semaphore.mdx)
-- [Async Queue Guide](./04-async-queue.mdx)
diff --git a/apps/website/docs/guide/15-key-value-store/01-introduction.mdx b/apps/website/docs/guide/15-key-value-store/01-introduction.mdx
deleted file mode 100644
index 897e6429..00000000
--- a/apps/website/docs/guide/15-key-value-store/01-introduction.mdx
+++ /dev/null
@@ -1,75 +0,0 @@
----
-title: Key-Value Store
-description: Learn how to use CommandKit's built-in key-value store for persistent data storage.
----
-
-# Key-Value Store
-
-The CommandKit Key-Value (KV) store provides a simple, persistent storage solution using SQLite. It supports storing any JSON-serializable data types directly, including objects, arrays, dates, maps, sets, and more.
-
-## Features
-
-- **JSON Serialization**: Store any JSON-serializable data types directly
-- **Dot Notation**: Access nested properties using dot notation (e.g., `user:123.settings.theme`)
-- **Namespaces**: Organize data into separate namespaces
-- **Expiration**: Set time-to-live (TTL) for automatic cleanup
-- **Transactions**: Execute multiple operations atomically
-- **Iteration**: Iterate over all key-value pairs
-- **Type Safety**: Full TypeScript support with strong typing
-
-## Quick Start
-
-```typescript
-import { KV } from 'commandkit/kv';
-
-// Create a new KV store
-const kv = new KV('data.db');
-
-// Store any data type directly
-kv.set('user:123', { name: 'John', age: 30 });
-kv.set('counter', 42);
-kv.set('active', true);
-kv.set('tags', ['javascript', 'typescript']);
-
-// Use dot notation for nested properties
-kv.set('user:123.settings.theme', 'dark');
-kv.set('user:123.settings.notifications', true);
-
-// Retrieve data
-const user = kv.get('user:123'); // { name: 'John', age: 30, settings: { theme: 'dark', notifications: true } }
-const theme = kv.get('user:123.settings.theme'); // 'dark'
-
-// Set expiration
-kv.setex('session:123', { userId: 123, token: 'abc123' }, 60 * 60 * 1000); // 1 hour
-
-// Use namespaces
-const userKv = kv.namespace('users');
-userKv.set('123', { name: 'John', age: 30 });
-```
-
-## Supported Data Types
-
-The KV store supports storing and retrieving the following data types:
-
-- **Primitives**: `string`, `number`, `boolean`, `bigint`, `null`, `undefined`
-- **Objects**: Plain objects, nested objects
-- **Arrays**: Any array of supported types
-- **Dates**: JavaScript Date objects
-- **Collections**: `Map`, `Set`
-- **Buffers**: Node.js Buffer objects
-- **Regular Expressions**: RegExp objects
-- **Functions**: Function objects (serialized as strings)
-
-## Installation
-
-The KV store is included with CommandKit. No additional installation is required.
-
-```bash
-npm install commandkit/kv
-```
-
-## Next Steps
-
-- [Basic Operations](./02-basic-operations.mdx) - Learn about core KV operations
-- [Namespaces](./03-namespaces.mdx) - Organize data with namespaces
-- [Advanced Features](./04-advanced-features.mdx) - Expiration, transactions, and more
diff --git a/apps/website/docs/guide/15-key-value-store/02-basic-operations.mdx b/apps/website/docs/guide/15-key-value-store/02-basic-operations.mdx
deleted file mode 100644
index af369105..00000000
--- a/apps/website/docs/guide/15-key-value-store/02-basic-operations.mdx
+++ /dev/null
@@ -1,263 +0,0 @@
----
-title: Basic Operations
-description: Learn the fundamental operations for working with the KV store.
----
-
-# Basic Operations
-
-This guide covers the fundamental operations of the CommandKit KV store, including storing and retrieving data with JSON serialization and dot notation support.
-
-## Creating a KV Store
-
-```typescript
-import { KV, openKV } from 'commandkit/kv';
-
-// Create with custom database file
-const kv = new KV('my-bot-data.db');
-
-// Or use the convenience function
-const kv = openKV('my-bot-data.db');
-
-// In-memory store for testing
-const kv = openKV(':memory:');
-```
-
-## Storing Data
-
-The KV store supports storing any JSON-serializable data type directly:
-
-### Primitive Values
-
-```typescript
-// Strings
-kv.set('bot_name', 'MyAwesomeBot');
-
-// Numbers
-kv.set('user_count', 1234);
-kv.set('pi', 3.14159);
-
-// Booleans
-kv.set('maintenance_mode', false);
-kv.set('feature_enabled', true);
-
-// BigInt
-kv.set('large_number', BigInt('12345678901234567890'));
-
-// Null and undefined
-kv.set('empty_value', null);
-kv.set('no_value', undefined);
-```
-
-### Objects and Arrays
-
-```typescript
-// Objects
-kv.set('user:123', {
- name: 'John Doe',
- age: 30,
- email: 'john@example.com',
- preferences: {
- theme: 'dark',
- notifications: true,
- },
-});
-
-// Arrays
-kv.set('tags', ['javascript', 'typescript', 'discord']);
-kv.set('numbers', [1, 2, 3, 4, 5]);
-
-// Mixed arrays
-kv.set('mixed_data', ['string', 42, true, { key: 'value' }, [1, 2, 3]]);
-```
-
-### Complex Data Types
-
-```typescript
-// Dates
-kv.set('created_at', new Date());
-kv.set('expires_at', new Date('2024-12-31'));
-
-// Maps
-kv.set(
- 'permissions',
- new Map([
- ['admin', true],
- ['moderator', false],
- ['user', true],
- ]),
-);
-
-// Sets
-kv.set('unique_ids', new Set([1, 2, 3, 4, 5]));
-
-// Buffers
-kv.set('binary_data', Buffer.from('Hello, World!'));
-
-// Regular Expressions
-kv.set('email_regex', /^[^\s@]+@[^\s@]+\.[^\s@]+$/);
-
-// Functions (serialized as strings)
-kv.set('validator', (value: string) => value.length > 0);
-```
-
-## Retrieving Data
-
-### Basic Retrieval
-
-```typescript
-// Get stored values
-const botName = kv.get('bot_name'); // 'MyAwesomeBot'
-const userCount = kv.get('user_count'); // 1234
-const isMaintenance = kv.get('maintenance_mode'); // false
-
-// Get objects
-const user = kv.get('user:123');
-// { name: 'John Doe', age: 30, email: 'john@example.com', preferences: { theme: 'dark', notifications: true } }
-
-// Get arrays
-const tags = kv.get('tags'); // ['javascript', 'typescript', 'discord']
-
-// Get complex types
-const createdAt = kv.get('created_at'); // Date object
-const permissions = kv.get('permissions'); // Map object
-const uniqueIds = kv.get('unique_ids'); // Set object
-```
-
-### Dot Notation for Nested Properties
-
-The KV store supports accessing nested properties using dot notation:
-
-```typescript
-// Store an object
-kv.set('user:123', {
- name: 'John Doe',
- age: 30,
- settings: {
- theme: 'dark',
- notifications: {
- email: true,
- push: false,
- },
- },
-});
-
-// Access nested properties
-const userName = kv.get('user:123.name'); // 'John Doe'
-const userAge = kv.get('user:123.age'); // 30
-const theme = kv.get('user:123.settings.theme'); // 'dark'
-const emailNotifications = kv.get('user:123.settings.notifications.email'); // true
-
-// Set nested properties
-kv.set('user:123.settings.theme', 'light');
-kv.set('user:123.settings.notifications.push', true);
-
-// The object is automatically updated
-const updatedUser = kv.get('user:123');
-// { name: 'John Doe', age: 30, settings: { theme: 'light', notifications: { email: true, push: true } } }
-```
-
-## Checking Existence
-
-```typescript
-// Check if a key exists
-if (kv.has('user:123')) {
- console.log('User exists');
-}
-
-// Check nested properties
-if (kv.has('user:123.settings.theme')) {
- console.log('User has theme setting');
-}
-```
-
-## Deleting Data
-
-```typescript
-// Delete a key
-kv.delete('user:123');
-
-// Delete nested properties (removes the entire object)
-kv.delete('user:123.settings.theme');
-```
-
-## Bulk Operations
-
-### Get All Data
-
-```typescript
-// Get all key-value pairs as an object
-const allData = kv.all();
-console.log('All stored data:', allData);
-// { 'bot_name': 'MyAwesomeBot', 'user_count': 1234, ... }
-
-// Get all keys
-const keys = kv.keys();
-console.log('All keys:', keys);
-// ['bot_name', 'user_count', 'user:123', ...]
-
-// Get all values
-const values = kv.values();
-console.log('All values:', values);
-// ['MyAwesomeBot', 1234, { name: 'John Doe', ... }, ...]
-
-// Count total entries
-const count = kv.count();
-console.log(`Total entries: ${count}`);
-```
-
-### Iteration
-
-```typescript
-// Iterate over all key-value pairs
-for (const [key, value] of kv) {
- console.log(`${key}:`, value);
-}
-
-// Convert to array
-const entries = [...kv];
-console.log('All entries:', entries);
-
-// Use with array methods
-const userEntries = [...kv].filter(([key]) => key.startsWith('user:'));
-```
-
-## Clearing Data
-
-```typescript
-// Remove all data from the current namespace
-kv.clear();
-```
-
-## Error Handling
-
-```typescript
-// Always check if data exists before using it
-const user = kv.get('user:123');
-if (user) {
- console.log('User found:', user.name);
-} else {
- console.log('User not found');
-}
-
-// Handle missing nested properties
-const theme = kv.get('user:123.settings.theme');
-if (theme !== undefined) {
- console.log('Theme:', theme);
-} else {
- console.log('No theme setting found');
-}
-```
-
-## Best Practices
-
-1. **Use Descriptive Keys**: Choose meaningful key names that clearly indicate what data they contain
-2. **Structure Your Data**: Use consistent object structures for related data
-3. **Handle Missing Data**: Always check if data exists before using it
-4. **Use Dot Notation Wisely**: Dot notation is convenient but can make data structure less explicit
-5. **Serialize Complex Data**: The KV store handles serialization automatically, but be aware of what can be serialized
-6. **Clean Up Resources**: Close the database connection when you're done
-
-## Next Steps
-
-- [Namespaces](./03-namespaces.mdx) - Organize data with namespaces
-- [Advanced Features](./04-advanced-features.mdx) - Expiration, transactions, and more
diff --git a/apps/website/docs/guide/15-key-value-store/03-namespaces.mdx b/apps/website/docs/guide/15-key-value-store/03-namespaces.mdx
deleted file mode 100644
index 5a075ffc..00000000
--- a/apps/website/docs/guide/15-key-value-store/03-namespaces.mdx
+++ /dev/null
@@ -1,339 +0,0 @@
----
-title: Namespaces
-description: Learn how to use namespaces to organize your KV store data logically.
----
-
-# Namespaces
-
-Namespaces are a powerful feature of the KV store that allows you to organize your data into logical groups. This helps keep your data organized, prevents key conflicts, and makes it easier to manage different types of data.
-
-## What are Namespaces?
-
-Namespaces are like separate tables within your SQLite database. Each namespace has its own set of key-value pairs, completely isolated from other namespaces. This means you can have the same key in different namespaces without conflicts.
-
-## Creating Namespaces
-
-You can create namespaces in two ways:
-
-### 1. During Initialization
-
-```ts
-import { openKV } from 'commandkit/kv';
-
-// Create KV store with a specific namespace
-const kv = openKV('data.db', {
- namespace: 'users',
-});
-
-// This will store data in the 'users' namespace
-kv.set('123', JSON.stringify({ name: 'John' }));
-```
-
-### 2. Using the namespace() Method
-
-```ts
-import { openKV } from 'commandkit/kv';
-
-// Create the main KV store
-const kv = openKV('data.db');
-
-// Create namespace instances
-const userKv = kv.namespace('users');
-const configKv = kv.namespace('config');
-const statsKv = kv.namespace('statistics');
-```
-
-## Working with Multiple Namespaces
-
-Here's how to work with different namespaces:
-
-```ts
-import { openKV } from 'commandkit/kv';
-
-const kv = openKV('bot-data.db');
-
-// Create namespace instances
-const userKv = kv.namespace('users');
-const configKv = kv.namespace('config');
-const guildKv = kv.namespace('guilds');
-
-// Store data in different namespaces
-userKv.set(
- '123',
- JSON.stringify({
- name: 'John Doe',
- level: 5,
- joinDate: new Date().toISOString(),
- }),
-);
-
-configKv.set('theme', 'dark');
-configKv.set('language', 'en');
-configKv.set('timezone', 'UTC');
-
-guildKv.set(
- '456',
- JSON.stringify({
- name: 'My Discord Server',
- memberCount: 1000,
- premium: true,
- }),
-);
-
-// Each namespace can have the same key without conflicts
-userKv.set('settings', JSON.stringify({ theme: 'light' }));
-configKv.set('settings', JSON.stringify({ maintenance: false }));
-```
-
-## Getting Current Namespace
-
-You can check which namespace you're currently working with:
-
-```ts
-const kv = openKV('data.db', { namespace: 'users' });
-console.log(kv.getCurrentNamespace()); // 'users'
-
-const configKv = kv.namespace('config');
-console.log(configKv.getCurrentNamespace()); // 'config'
-```
-
-## Listing All Namespaces
-
-You can get a list of all available namespaces in your database:
-
-```ts
-const kv = openKV('data.db');
-
-// Create some namespaces
-kv.namespace('users').set('123', 'user data');
-kv.namespace('config').set('theme', 'dark');
-kv.namespace('guilds').set('456', 'guild data');
-
-// Get all namespaces
-const namespaces = kv.namespaces();
-console.log('Available namespaces:', namespaces);
-// Output: ['users', 'config', 'guilds']
-```
-
-## Namespace-Specific Operations
-
-Each namespace instance has its own set of operations:
-
-```ts
-const kv = openKV('data.db');
-const userKv = kv.namespace('users');
-const configKv = kv.namespace('config');
-
-// Each namespace has its own count
-console.log(`Users: ${userKv.count()}`);
-console.log(`Config items: ${configKv.count()}`);
-
-// Each namespace has its own keys
-console.log('User keys:', userKv.keys());
-console.log('Config keys:', configKv.keys());
-
-// Each namespace has its own data
-console.log('All users:', userKv.all());
-console.log('All config:', configKv.all());
-```
-
-## Common Namespace Patterns
-
-### User Data Management
-
-```ts
-const kv = openKV('bot-data.db');
-
-// User profiles
-const userProfiles = kv.namespace('user_profiles');
-userProfiles.set(
- '123',
- JSON.stringify({
- username: 'john_doe',
- level: 5,
- experience: 1250,
- joinDate: new Date().toISOString(),
- }),
-);
-
-// User settings
-const userSettings = kv.namespace('user_settings');
-userSettings.set(
- '123',
- JSON.stringify({
- theme: 'dark',
- language: 'en',
- notifications: true,
- }),
-);
-
-// User statistics
-const userStats = kv.namespace('user_stats');
-userStats.set(
- '123',
- JSON.stringify({
- messagesSent: 150,
- commandsUsed: 45,
- timeSpent: 3600,
- }),
-);
-```
-
-### Guild/Server Management
-
-```ts
-const kv = openKV('bot-data.db');
-
-// Guild configurations
-const guildConfig = kv.namespace('guild_config');
-guildConfig.set(
- '456',
- JSON.stringify({
- prefix: '!',
- welcomeChannel: '123456789',
- modRole: '987654321',
- autoRole: '111222333',
- }),
-);
-
-// Guild statistics
-const guildStats = kv.namespace('guild_stats');
-guildStats.set(
- '456',
- JSON.stringify({
- memberCount: 1000,
- messageCount: 50000,
- commandUsage: 2500,
- }),
-);
-
-// Guild features
-const guildFeatures = kv.namespace('guild_features');
-guildFeatures.set(
- '456',
- JSON.stringify({
- welcomeMessages: true,
- autoModeration: false,
- leveling: true,
- music: false,
- }),
-);
-```
-
-### Bot Configuration
-
-```ts
-const kv = openKV('bot-data.db');
-
-// Global bot settings
-const botConfig = kv.namespace('bot_config');
-botConfig.set(
- 'global',
- JSON.stringify({
- defaultPrefix: '!',
- maintenanceMode: false,
- logLevel: 'info',
- backupInterval: 3600,
- }),
-);
-
-// Feature flags
-const featureFlags = kv.namespace('feature_flags');
-featureFlags.set('ai_commands', 'true');
-featureFlags.set('analytics', 'false');
-featureFlags.set('beta_features', 'true');
-
-// API keys and secrets
-const secrets = kv.namespace('secrets');
-secrets.set('openai_key', 'sk-...');
-secrets.set('weather_api', 'abc123...');
-```
-
-## Namespace Best Practices
-
-### 1. **Use Descriptive Names**
-
-```ts
-// Good
-const userProfiles = kv.namespace('user_profiles');
-const guildSettings = kv.namespace('guild_settings');
-const botConfiguration = kv.namespace('bot_config');
-
-// Avoid
-const up = kv.namespace('up');
-const gs = kv.namespace('gs');
-const bc = kv.namespace('bc');
-```
-
-### 2. **Group Related Data**
-
-```ts
-// Group all user-related data
-const userData = kv.namespace('users');
-const userProfiles = kv.namespace('user_profiles');
-const userSettings = kv.namespace('user_settings');
-const userStats = kv.namespace('user_statistics');
-
-// Group all guild-related data
-const guildData = kv.namespace('guilds');
-const guildConfig = kv.namespace('guild_configuration');
-const guildFeatures = kv.namespace('guild_features');
-```
-
-### 3. **Use Consistent Naming**
-
-```ts
-// Be consistent with naming conventions
-const userKv = kv.namespace('users');
-const guildKv = kv.namespace('guilds');
-const configKv = kv.namespace('config');
-
-// Or use descriptive suffixes
-const userProfiles = kv.namespace('user_profiles');
-const guildSettings = kv.namespace('guild_settings');
-const botConfiguration = kv.namespace('bot_configuration');
-```
-
-### 4. **Document Your Namespaces**
-
-```ts
-// Create a namespace registry
-const namespaces = {
- users: kv.namespace('users'),
- userProfiles: kv.namespace('user_profiles'),
- userSettings: kv.namespace('user_settings'),
- guilds: kv.namespace('guilds'),
- guildConfig: kv.namespace('guild_config'),
- botConfig: kv.namespace('bot_config'),
- featureFlags: kv.namespace('feature_flags'),
-};
-
-// Use the registry
-namespaces.users.set('123', 'user data');
-namespaces.guildConfig.set('456', 'guild config');
-```
-
-## Namespace Cleanup
-
-You can clear specific namespaces:
-
-```ts
-const kv = openKV('data.db');
-const userKv = kv.namespace('users');
-const configKv = kv.namespace('config');
-
-// Clear only the users namespace
-userKv.clear();
-
-// Clear only the config namespace
-configKv.clear();
-
-// Clear all data (all namespaces)
-kv.clear(); // This only clears the default namespace
-```
-
-## Next Steps
-
-Now that you understand namespaces, learn about:
-
-- [Advanced features](./04-advanced-features.mdx) like iteration and resource management
diff --git a/apps/website/docs/guide/15-key-value-store/04-advanced-features.mdx b/apps/website/docs/guide/15-key-value-store/04-advanced-features.mdx
deleted file mode 100644
index 68124b4f..00000000
--- a/apps/website/docs/guide/15-key-value-store/04-advanced-features.mdx
+++ /dev/null
@@ -1,398 +0,0 @@
----
-title: Advanced Features
-description: Learn about advanced KV store features like iteration, resource management, and database access.
----
-
-# Advanced Features
-
-This guide covers advanced features of the CommandKit KV store, including expiration, transactions, and best practices for complex use cases.
-
-## Expiration Support
-
-The KV store supports automatic expiration of data, similar to Redis. This is useful for caching, sessions, and temporary data.
-
-### Setting Data with Expiration
-
-```typescript
-// Set data with expiration (1 hour)
-kv.setex('session:123', { userId: 123, token: 'abc123' }, 60 * 60 * 1000);
-
-// Set data with 5 minutes expiration
-kv.setex('temp:data', { cached: true, timestamp: Date.now() }, 5 * 60 * 1000);
-
-// Set data with 1 day expiration
-kv.setex('daily:stats', { count: 100, date: new Date() }, 24 * 60 * 60 * 1000);
-
-// Use dot notation with expiration
-kv.setex('user:123.temp_settings', { theme: 'light' }, 30 * 60 * 1000);
-```
-
-### Setting Expiration for Existing Keys
-
-```typescript
-// First set the data
-kv.set('user:123', { name: 'John', age: 30 });
-
-// Then set expiration (30 minutes)
-if (kv.expire('user:123', 30 * 60 * 1000)) {
- console.log('Expiration set successfully');
-} else {
- console.log('Key does not exist');
-}
-```
-
-### Checking Time to Live
-
-```typescript
-const ttl = kv.ttl('user:123');
-
-if (ttl > 0) {
- console.log(`Key expires in ${ttl}ms (${Math.floor(ttl / 1000)}s)`);
-} else if (ttl === -2) {
- console.log('Key has no expiration');
-} else {
- console.log('Key does not exist or has expired');
-}
-```
-
-### Automatic Expiration Handling
-
-```typescript
-// Set data with expiration
-kv.setex('temp:key', 'value', 1000); // 1 second
-
-// Immediately check - should exist
-console.log(kv.has('temp:key')); // true
-
-// Wait for expiration
-setTimeout(() => {
- console.log(kv.has('temp:key')); // false
- console.log(kv.get('temp:key')); // undefined
-}, 1100);
-```
-
-## Transactions
-
-Transactions allow you to execute multiple operations atomically. If any operation fails, all changes are rolled back.
-
-### Basic Transaction Usage
-
-```typescript
-// Execute multiple operations atomically
-kv.transaction(() => {
- kv.set('user:123', { name: 'John', balance: 100 });
- kv.set('user:456', { name: 'Jane', balance: 200 });
-
- // If any operation fails, all changes are rolled back
-});
-```
-
-### Async Transactions
-
-```typescript
-// Async transactions are also supported
-await kv.transaction(async () => {
- kv.set('user:123', { name: 'John', balance: 100 });
-
- // You can perform async operations
- await someAsyncOperation();
-
- kv.set('user:456', { name: 'Jane', balance: 200 });
-
- // If any operation fails, all changes are rolled back
-});
-```
-
-### Transaction with Error Handling
-
-```typescript
-try {
- kv.transaction(() => {
- kv.set('user:123', { name: 'John', balance: 100 });
-
- // Simulate an error
- throw new Error('Database error');
-
- // This won't execute due to the error
- kv.set('user:456', { name: 'Jane', balance: 200 });
- });
-} catch (error) {
- console.error('Transaction failed:', error);
- // All changes were automatically rolled back
-}
-```
-
-### Complex Transaction Example
-
-```typescript
-// Transfer money between users
-async function transferMoney(
- fromUserId: string,
- toUserId: string,
- amount: number,
-) {
- return kv.transaction(async () => {
- // Get current balances
- const fromUser = kv.get(`user:${fromUserId}`) || { balance: 0 };
- const toUser = kv.get(`user:${toUserId}`) || { balance: 0 };
-
- // Validate transfer
- if (fromUser.balance < amount) {
- throw new Error('Insufficient funds');
- }
-
- // Update balances
- fromUser.balance -= amount;
- toUser.balance += amount;
-
- // Save updated users
- kv.set(`user:${fromUserId}`, fromUser);
- kv.set(`user:${toUserId}`, toUser);
-
- // Log the transaction
- kv.set(`transaction:${Date.now()}`, {
- from: fromUserId,
- to: toUserId,
- amount,
- timestamp: new Date(),
- });
-
- return { success: true, newBalance: fromUser.balance };
- });
-}
-
-// Usage
-try {
- const result = await transferMoney('123', '456', 50);
- console.log('Transfer successful:', result);
-} catch (error) {
- console.error('Transfer failed:', error);
-}
-```
-
-## Resource Management
-
-The KV store implements disposable patterns for automatic resource cleanup.
-
-### Using with Statement
-
-```typitten
-// Automatic cleanup with using statement
-{
- using kv = openKV('data.db');
- kv.set('key', 'value');
- // kv is automatically closed when the block ends
-}
-```
-
-### Using Async Disposal
-
-```typescript
-// Async disposal (fake promise wrapper)
-await using kv = openKV('data.db');
-kv.set('key', 'value');
-// kv is automatically closed when the block ends
-```
-
-:::note Async Disposal
-The `async using` statement is just a fake promise wrapper around the synchronous `using` statement. The disposal is still synchronous.
-:::
-
-### Manual Resource Management
-
-```typescript
-const kv = openKV('data.db');
-
-try {
- kv.set('key', 'value');
- // ... other operations
-} finally {
- kv.close(); // Always close the connection
-}
-```
-
-## Performance Optimization
-
-### Using Write-Ahead Logging (WAL)
-
-```typescript
-// Enable WAL for better performance (enabled by default)
-const kv = new KV('data.db', { enableWAL: true });
-```
-
-### Batch Operations
-
-```typescript
-// Use transactions for batch operations
-kv.transaction(() => {
- for (let i = 0; i < 1000; i++) {
- kv.set(`item:${i}`, { id: i, data: `data-${i}` });
- }
-});
-```
-
-### Efficient Iteration
-
-```typescript
-// Use iterator for large datasets
-for (const [key, value] of kv) {
- if (key.startsWith('user:')) {
- // Process user data
- console.log(`Processing ${key}:`, value);
- }
-}
-
-// Avoid loading all data into memory
-// Instead of: const all = kv.all(); // Loads everything
-// Use: for (const [key, value] of kv) { ... } // Processes one at a time
-```
-
-## Error Handling Best Practices
-
-### Comprehensive Error Handling
-
-```typescript
-class KVStoreManager {
- private kv: KV;
-
- constructor(dbPath: string) {
- this.kv = new KV(dbPath);
- }
-
- async setUser(userId: string, userData: any): Promise {
- try {
- if (!this.kv.isOpen()) {
- throw new Error('Database is not open');
- }
-
- this.kv.set(`user:${userId}`, userData);
- return true;
- } catch (error) {
- console.error(`Failed to set user ${userId}:`, error);
- return false;
- }
- }
-
- async getUser(userId: string): Promise {
- try {
- if (!this.kv.isOpen()) {
- throw new Error('Database is not open');
- }
-
- const user = this.kv.get(`user:${userId}`);
- return user || null;
- } catch (error) {
- console.error(`Failed to get user ${userId}:`, error);
- return null;
- }
- }
-
- close(): void {
- try {
- this.kv.close();
- } catch (error) {
- console.error('Failed to close database:', error);
- }
- }
-}
-```
-
-### Validation and Type Safety
-
-```typescript
-interface User {
- id: string;
- name: string;
- email: string;
- settings: {
- theme: 'light' | 'dark';
- notifications: boolean;
- };
-}
-
-function validateUser(data: any): data is User {
- return (
- typeof data === 'object' &&
- typeof data.id === 'string' &&
- typeof data.name === 'string' &&
- typeof data.email === 'string' &&
- typeof data.settings === 'object' &&
- (data.settings.theme === 'light' || data.settings.theme === 'dark') &&
- typeof data.settings.notifications === 'boolean'
- );
-}
-
-// Usage
-const userData = kv.get('user:123');
-if (userData && validateUser(userData)) {
- console.log('Valid user:', userData.name);
-} else {
- console.log('Invalid or missing user data');
-}
-```
-
-## Monitoring and Debugging
-
-### Database Health Checks
-
-```typescript
-function checkDatabaseHealth(kv: KV): boolean {
- try {
- // Check if database is open
- if (!kv.isOpen()) {
- console.error('Database is not open');
- return false;
- }
-
- // Test basic operations
- const testKey = '__health_check__';
- kv.set(testKey, { timestamp: Date.now() });
- const result = kv.get(testKey);
- kv.delete(testKey);
-
- if (!result) {
- console.error('Database read/write test failed');
- return false;
- }
-
- console.log('Database health check passed');
- return true;
- } catch (error) {
- console.error('Database health check failed:', error);
- return false;
- }
-}
-```
-
-### Performance Monitoring
-
-```typescript
-function measureOperationTime(operation: () => T): {
- result: T;
- duration: number;
-} {
- const start = performance.now();
- const result = operation();
- const duration = performance.now() - start;
-
- return { result, duration };
-}
-
-// Usage
-const { result, duration } = measureOperationTime(() => {
- return kv.get('user:123');
-});
-
-console.log(`Operation took ${duration.toFixed(2)}ms`);
-```
-
-## Best Practices Summary
-
-1. **Use Transactions for Related Operations**: Group related operations in transactions for consistency
-2. **Handle Expiration Gracefully**: Always check TTL and handle expired data appropriately
-3. **Validate Data**: Implement proper validation for complex data structures
-4. **Monitor Performance**: Use health checks and performance monitoring
-5. **Clean Up Resources**: Always close database connections properly
-6. **Use Meaningful Keys**: Choose descriptive key names and use consistent patterns
-7. **Handle Errors**: Implement comprehensive error handling for all operations
-8. **Optimize for Your Use Case**: Use appropriate features like WAL and batch operations
diff --git a/apps/website/docs/guide/16-queue/01-introduction.mdx b/apps/website/docs/guide/16-queue/01-introduction.mdx
deleted file mode 100644
index 2074cc4f..00000000
--- a/apps/website/docs/guide/16-queue/01-introduction.mdx
+++ /dev/null
@@ -1,81 +0,0 @@
----
-title: Message Queue
-description: Learn how to use CommandKit's message queue system for inter-service communication.
----
-
-# Message Queue
-
-The CommandKit Queue package provides a service-agnostic message queue API for inter-service communication. It allows you to send and receive messages between different parts of your application or across multiple services using a simple, unified interface.
-
-## Features
-
-- **Service Agnostic**: Works with any message queue implementation
-- **Simple API**: Easy-to-use `send` and `receive` functions
-- **Type Safety**: Full TypeScript support with strong typing
-- **Driver System**: Pluggable drivers for different queue backends
-- **Discord.js Integration**: Built-in support for Discord.js brokers
-- **Redis Support**: Ready-to-use Redis PubSub driver
-
-## Quick Start
-
-```typescript
-import { setDriver } from '@commandkit/queue';
-import { RedisPubSubDriver } from '@commandkit/queue/discordjs';
-import { PubSubRedisBroker } from '@discordjs/brokers';
-import Redis from 'ioredis';
-
-// Set up the driver
-const broker = new PubSubRedisBroker(new Redis());
-const driver = new RedisPubSubDriver(broker);
-setDriver(driver);
-
-// Send a message
-import { send } from '@commandkit/queue';
-await send('user-updates', { userId: '123', action: 'login' });
-
-// Receive messages
-import { receive } from '@commandkit/queue';
-await receive('user-updates', (message) => {
- console.log(`User ${message.userId} performed ${message.action}`);
-});
-```
-
-## Installation
-
-```bash
-npm install @commandkit/queue
-```
-
-For Discord.js integration with Redis:
-
-```bash
-npm install @commandkit/queue @discordjs/brokers ioredis
-```
-
-## Core Concepts
-
-### Topics
-
-Topics are named channels where messages are sent and received. Think of them as categories or channels for different types of messages.
-
-### Messages
-
-Messages can be any JSON-serializable data. They're sent to topics and received by subscribers.
-
-### Drivers
-
-Drivers are implementations that handle the actual message queue backend. CommandKit provides a unified API that works with any driver.
-
-## Use Cases
-
-- **Inter-Service Communication**: Send messages between different parts of your application
-- **Event Broadcasting**: Broadcast events to multiple subscribers
-- **Task Distribution**: Distribute work across multiple workers
-- **Real-time Updates**: Send real-time updates to connected clients
-- **Microservices**: Enable communication between microservices
-
-## Next Steps
-
-- [Basic Operations](./02-basic-operations.md) - Learn about sending and receiving messages
-- [Discord.js Integration](./03-discordjs-integration.md) - Set up Redis PubSub with Discord.js
-- [Advanced Features](./04-advanced-features.md) - Custom drivers, error handling, and more
diff --git a/apps/website/docs/guide/16-queue/02-basic-operations.mdx b/apps/website/docs/guide/16-queue/02-basic-operations.mdx
deleted file mode 100644
index 7a812c67..00000000
--- a/apps/website/docs/guide/16-queue/02-basic-operations.mdx
+++ /dev/null
@@ -1,234 +0,0 @@
----
-title: Basic Operations
-description: Learn how to send and receive messages using CommandKit's queue system.
----
-
-# Basic Operations
-
-This guide covers the fundamental operations of the CommandKit Queue system: sending and receiving messages.
-
-## Setting Up the Driver
-
-Before you can send or receive messages, you need to set up a driver. The driver handles the actual message queue backend.
-
-```typescript
-import { setDriver } from '@commandkit/queue';
-import { RedisPubSubDriver } from '@commandkit/queue/discordjs';
-import { PubSubRedisBroker } from '@discordjs/brokers';
-import Redis from 'ioredis';
-
-// Create a Redis connection
-const redis = new Redis();
-
-// Create a broker
-const broker = new PubSubRedisBroker(redis);
-
-// Create a driver
-const driver = new RedisPubSubDriver(broker);
-
-// Set the driver
-setDriver(driver);
-```
-
-## Sending Messages
-
-Use the `send` function to publish messages to a topic.
-
-```typescript
-import { send } from '@commandkit/queue';
-
-// Send a simple message
-await send('user-events', { userId: '123', action: 'login' });
-
-// Send different types of data
-await send('notifications', {
- type: 'welcome',
- userId: '123',
- message: 'Welcome to our platform!',
-});
-
-await send('analytics', {
- event: 'page_view',
- page: '/dashboard',
- timestamp: Date.now(),
-});
-```
-
-## Receiving Messages
-
-Use the `receive` function to subscribe to messages from a topic.
-
-```typescript
-import { receive } from '@commandkit/queue';
-
-// Basic message handling
-await receive('user-events', (message) => {
- console.log(`User ${message.userId} performed ${message.action}`);
-});
-
-// Handle different message types
-await receive('notifications', (message) => {
- switch (message.type) {
- case 'welcome':
- console.log(`Welcome message for user ${message.userId}`);
- break;
- case 'reminder':
- console.log(`Reminder: ${message.message}`);
- break;
- }
-});
-
-// Async message handling
-await receive('analytics', async (message) => {
- await processAnalyticsEvent(message);
-});
-```
-
-## Type Safety
-
-You can define types for your messages to get better TypeScript support.
-
-```typescript
-interface UserEvent {
- userId: string;
- action: 'login' | 'logout' | 'register';
- timestamp?: number;
-}
-
-interface Notification {
- type: 'welcome' | 'reminder' | 'alert';
- userId: string;
- message: string;
-}
-
-// Type-safe sending
-await send('user-events', {
- userId: '123',
- action: 'login',
- timestamp: Date.now(),
-} as UserEvent);
-
-// Type-safe receiving
-await receive('user-events', (message: UserEvent) => {
- console.log(`User ${message.userId} ${message.action}`);
-});
-```
-
-## Multiple Subscribers
-
-You can have multiple subscribers listening to the same topic.
-
-```typescript
-// Subscriber 1: Log all user events
-await receive('user-events', (message) => {
- console.log(`[LOG] User event: ${message.userId} - ${message.action}`);
-});
-
-// Subscriber 2: Update user analytics
-await receive('user-events', async (message) => {
- await updateUserAnalytics(message.userId, message.action);
-});
-
-// Subscriber 3: Send notifications
-await receive('user-events', (message) => {
- if (message.action === 'login') {
- sendNotification(message.userId, 'Welcome back!');
- }
-});
-```
-
-## Error Handling
-
-Always handle errors when sending or receiving messages.
-
-```typescript
-// Error handling for sending
-try {
- await send('user-events', { userId: '123', action: 'login' });
-} catch (error) {
- console.error('Failed to send message:', error);
-}
-
-// Error handling for receiving
-await receive('user-events', (message) => {
- try {
- processUserEvent(message);
- } catch (error) {
- console.error('Failed to process message:', error);
- }
-});
-```
-
-## Best Practices
-
-### 1. Use Descriptive Topic Names
-
-```typescript
-// Good
-await send('user-authentication-events', message);
-await send('order-processing-updates', message);
-
-// Avoid
-await send('events', message);
-await send('data', message);
-```
-
-### 2. Structure Your Messages
-
-```typescript
-// Good - structured message
-await send('user-events', {
- type: 'login',
- userId: '123',
- timestamp: Date.now(),
-});
-
-// Avoid - unstructured
-await send('user-events', 'user logged in');
-```
-
-### 3. Handle Message Processing Gracefully
-
-```typescript
-await receive('user-events', async (message) => {
- try {
- // Process the message
- await processUserEvent(message);
-
- // Acknowledge successful processing
- console.log(`Processed event for user ${message.userId}`);
- } catch (error) {
- // Log error but don't crash
- console.error(`Failed to process event for user ${message.userId}:`, error);
-
- // Optionally retry or send to dead letter queue
- await handleFailedMessage(message, error);
- }
-});
-```
-
-### 4. Use Appropriate Message Sizes
-
-```typescript
-// Good - reasonable message size
-await send('user-profile-updates', {
- userId: '123',
- changes: {
- displayName: 'New Name',
- avatar: 'https://example.com/avatar.jpg',
- },
-});
-
-// Avoid - very large messages
-await send('user-profile-updates', {
- userId: '123',
- fullProfile: {
- /* massive object */
- },
-});
-```
-
-## Next Steps
-
-- [Discord.js Integration](./03-discordjs-integration.md) - Learn how to set up Redis PubSub with Discord.js
-- [Advanced Features](./04-advanced-features.md) - Custom drivers, error handling, and more
diff --git a/apps/website/docs/guide/16-queue/03-discordjs-integration.mdx b/apps/website/docs/guide/16-queue/03-discordjs-integration.mdx
deleted file mode 100644
index a33e3d72..00000000
--- a/apps/website/docs/guide/16-queue/03-discordjs-integration.mdx
+++ /dev/null
@@ -1,359 +0,0 @@
----
-title: Discord.js Integration
-description: Learn how to integrate CommandKit Queue with Discord.js using Redis PubSub.
----
-
-# Discord.js Integration
-
-CommandKit Queue provides seamless integration with Discord.js through the `@discordjs/brokers` package and Redis PubSub. This allows you to send messages between different Discord.js shards or even different Discord.js applications.
-
-## Prerequisites
-
-Install the required dependencies:
-
-```bash
-npm install @commandkit/queue @discordjs/brokers ioredis
-```
-
-## Basic Setup
-
-### 1. Create a Redis Connection
-
-First, you need a Redis instance running. You can use a local Redis server or a cloud service like Redis Cloud.
-
-```typescript
-import Redis from 'ioredis';
-
-// Local Redis
-const redis = new Redis();
-
-// Or with configuration
-const redis = new Redis({
- host: 'localhost',
- port: 6379,
- password: 'your-password',
- db: 0,
-});
-
-// Cloud Redis (example with Redis Cloud)
-const redis = new Redis({
- host: 'your-redis-host.redis.cloud.com',
- port: 6379,
- password: 'your-redis-password',
- tls: {},
-});
-```
-
-### 2. Set Up the Broker and Driver
-
-```typescript
-import { setDriver } from '@commandkit/queue';
-import { RedisPubSubDriver } from '@commandkit/queue/discordjs';
-import { PubSubRedisBroker } from '@discordjs/brokers';
-import Redis from 'ioredis';
-
-// Create Redis connection
-const redis = new Redis();
-
-// Create the broker
-const broker = new PubSubRedisBroker(redis);
-
-// Create the driver
-const driver = new RedisPubSubDriver(broker);
-
-// Set the driver
-setDriver(driver);
-```
-
-## Type-Safe Events
-
-You can define typed events for better TypeScript support:
-
-```typescript
-interface QueueEvents {
- 'user-updates': {
- userId: string;
- action: 'login' | 'logout' | 'register';
- timestamp: number;
- };
- 'guild-events': {
- guildId: string;
- event: 'member-join' | 'member-leave' | 'role-update';
- data: any;
- };
- analytics: {
- event: string;
- data: Record;
- timestamp: number;
- };
-}
-
-// Create a typed driver
-const driver = new RedisPubSubDriver(broker);
-setDriver(driver);
-```
-
-## Sending Messages
-
-Send messages to specific topics:
-
-```typescript
-import { send } from '@commandkit/queue';
-
-// Send user events
-await send('user-updates', {
- userId: '123456789',
- action: 'login',
- timestamp: Date.now(),
-});
-
-// Send guild events
-await send('guild-events', {
- guildId: '987654321',
- event: 'member-join',
- data: { memberId: '123456789', username: 'JohnDoe' },
-});
-
-// Send analytics
-await send('analytics', {
- event: 'command-executed',
- data: { command: 'ping', userId: '123456789' },
- timestamp: Date.now(),
-});
-```
-
-## Receiving Messages
-
-Subscribe to messages from topics:
-
-```typescript
-import { receive } from '@commandkit/queue';
-
-// Handle user updates
-await receive('user-updates', (message) => {
- console.log(
- `User ${message.userId} ${message.action} at ${new Date(message.timestamp)}`,
- );
-
- // Update user status in your application
- updateUserStatus(message.userId, message.action);
-});
-
-// Handle guild events
-await receive('guild-events', (message) => {
- switch (message.event) {
- case 'member-join':
- console.log(
- `Member ${message.data.memberId} joined guild ${message.guildId}`,
- );
- break;
- case 'member-leave':
- console.log(
- `Member ${message.data.memberId} left guild ${message.guildId}`,
- );
- break;
- }
-});
-
-// Handle analytics
-await receive('analytics', async (message) => {
- await logAnalyticsEvent(message.event, message.data);
-});
-```
-
-## Discord.js Integration Examples
-
-### Cross-Shard Communication
-
-Send messages between different shards of your Discord.js application:
-
-```typescript
-// In shard 1
-await send('shard-communication', {
- fromShard: 0,
- toShard: 1,
- type: 'user-status-update',
- data: { userId: '123456789', status: 'online' },
-});
-
-// In shard 1 (receiving)
-await receive('shard-communication', (message) => {
- if (message.toShard === 0) {
- console.log(`Received from shard ${message.fromShard}:`, message.data);
- }
-});
-```
-
-### Multi-Bot Communication
-
-Send messages between different Discord.js bots:
-
-```typescript
-// In bot A
-await send('bot-communication', {
- fromBot: 'bot-a',
- toBot: 'bot-b',
- type: 'user-data-request',
- data: { userId: '123456789' },
-});
-
-// In bot B
-await receive('bot-communication', async (message) => {
- if (message.toBot === 'bot-b') {
- const userData = await getUserData(message.data.userId);
-
- await send('bot-communication', {
- fromBot: 'bot-b',
- toBot: 'bot-a',
- type: 'user-data-response',
- data: userData,
- });
- }
-});
-```
-
-### Real-time Updates
-
-Send real-time updates to connected clients:
-
-```typescript
-// When a user joins a voice channel
-await send('voice-updates', {
- userId: '123456789',
- guildId: '987654321',
- channelId: '111222333',
- action: 'join',
- timestamp: Date.now(),
-});
-
-// Handle voice updates
-await receive('voice-updates', (message) => {
- // Update voice channel status
- updateVoiceChannelStatus(
- message.guildId,
- message.channelId,
- message.userId,
- message.action,
- );
-
- // Notify other users
- notifyVoiceChannelUsers(message.guildId, message.channelId, message);
-});
-```
-
-## Error Handling
-
-Handle connection and message processing errors:
-
-```typescript
-// Handle Redis connection errors
-redis.on('error', (error) => {
- console.error('Redis connection error:', error);
-});
-
-redis.on('connect', () => {
- console.log('Connected to Redis');
-});
-
-// Handle broker errors
-broker.on('error', (error) => {
- console.error('Broker error:', error);
-});
-
-// Handle message processing errors
-await receive('user-updates', async (message) => {
- try {
- await processUserUpdate(message);
- } catch (error) {
- console.error('Failed to process user update:', error);
-
- // Optionally retry or send to dead letter queue
- await send('failed-messages', {
- originalTopic: 'user-updates',
- message,
- error: error.message,
- timestamp: Date.now(),
- });
- }
-});
-```
-
-## Performance Considerations
-
-### Connection Pooling
-
-For high-traffic applications, consider using connection pooling:
-
-```typescript
-import Redis from 'ioredis';
-
-// Create a connection pool
-const redis = new Redis({
- host: 'localhost',
- port: 6379,
- maxRetriesPerRequest: 3,
- retryDelayOnFailover: 100,
- enableReadyCheck: false,
- maxLoadingTimeout: 10000,
-});
-```
-
-### Message Batching
-
-For high-volume scenarios, consider batching messages:
-
-```typescript
-// Batch multiple updates
-const updates = [
- { userId: '123', action: 'login' },
- { userId: '456', action: 'logout' },
- { userId: '789', action: 'login' },
-];
-
-// Send as a batch
-await send('user-updates-batch', {
- updates,
- timestamp: Date.now(),
-});
-
-// Process batch
-await receive('user-updates-batch', async (message) => {
- for (const update of message.updates) {
- await processUserUpdate(update);
- }
-});
-```
-
-## Cleanup
-
-Always clean up resources when shutting down:
-
-```typescript
-import { setDriver } from '@commandkit/queue';
-
-// Cleanup function
-async function cleanup() {
- try {
- // Close the driver
- const driver = getDriver(); // You'll need to implement this
- if (driver && driver.close) {
- await driver.close();
- }
-
- // Close Redis connection
- await redis.quit();
-
- console.log('Queue cleanup completed');
- } catch (error) {
- console.error('Error during cleanup:', error);
- }
-}
-
-// Handle graceful shutdown
-process.on('SIGINT', cleanup);
-process.on('SIGTERM', cleanup);
-```
-
-## Next Steps
-
-- [Advanced Features](./04-advanced-features.md) - Custom drivers, error handling, and more
diff --git a/apps/website/docs/guide/16-queue/04-advanced-features.mdx b/apps/website/docs/guide/16-queue/04-advanced-features.mdx
deleted file mode 100644
index c76fd6e7..00000000
--- a/apps/website/docs/guide/16-queue/04-advanced-features.mdx
+++ /dev/null
@@ -1,649 +0,0 @@
----
-title: Advanced Features
-description: Learn about custom drivers, error handling, and advanced queue features.
----
-
-# Advanced Features
-
-This guide covers advanced features of the CommandKit Queue system, including custom drivers, error handling, and performance optimizations.
-
-## Custom Drivers
-
-You can create custom drivers to work with any message queue backend. A driver must implement the `MessageQueue` interface.
-
-### Creating a Custom Driver
-
-```typescript
-import type { MessageQueue, Awaitable } from '@commandkit/queue';
-
-class CustomQueueDriver implements MessageQueue {
- private subscribers = new Map<
- string,
- Set<(message: any) => Awaitable>
- >();
-
- async send(topic: string, message: any): Promise {
- // Implement your message sending logic here
- console.log(`Sending message to topic ${topic}:`, message);
-
- // Example: Send to your custom queue backend
- await this.sendToBackend(topic, message);
- }
-
- async receive(
- topic: string,
- handler: (message: any) => Awaitable,
- ): Promise {
- // Subscribe to the topic
- if (!this.subscribers.has(topic)) {
- this.subscribers.set(topic, new Set());
- }
-
- this.subscribers.get(topic)!.add(handler);
-
- // Start listening for messages on this topic
- await this.startListening(topic);
- }
-
- async close(): Promise {
- // Clean up resources
- this.subscribers.clear();
- await this.cleanup();
- }
-
- private async sendToBackend(topic: string, message: any): Promise {
- // Implement your backend communication
- // This could be RabbitMQ, Apache Kafka, AWS SQS, etc.
- }
-
- private async startListening(topic: string): Promise {
- // Start listening for messages from your backend
- // When a message arrives, notify all subscribers
- }
-
- private async cleanup(): Promise {
- // Clean up any connections or resources
- }
-}
-```
-
-### RabbitMQ Driver Example
-
-```typescript
-import amqp from 'amqplib';
-import type { MessageQueue, Awaitable } from '@commandkit/queue';
-
-class RabbitMQDriver implements MessageQueue {
- private connection?: amqp.Connection;
- private channel?: amqp.Channel;
- private subscribers = new Map<
- string,
- Set<(message: any) => Awaitable>
- >();
-
- constructor(private url: string) {}
-
- async send(topic: string, message: any): Promise {
- if (!this.channel) {
- throw new Error('Driver not initialized');
- }
-
- await this.channel.assertExchange(topic, 'fanout', { durable: false });
- this.channel.publish(topic, '', Buffer.from(JSON.stringify(message)));
- }
-
- async receive(
- topic: string,
- handler: (message: any) => Awaitable,
- ): Promise {
- if (!this.channel) {
- throw new Error('Driver not initialized');
- }
-
- await this.channel.assertExchange(topic, 'fanout', { durable: false });
- const queue = await this.channel.assertQueue('', { exclusive: true });
-
- await this.channel.bindQueue(queue.queue, topic, '');
-
- this.channel.consume(queue.queue, async (msg) => {
- if (msg) {
- const content = JSON.parse(msg.content.toString());
- await handler(content);
- this.channel!.ack(msg);
- }
- });
- }
-
- async connect(): Promise {
- this.connection = await amqp.connect(this.url);
- this.channel = await this.connection.createChannel();
- }
-
- async close(): Promise {
- if (this.channel) {
- await this.channel.close();
- }
- if (this.connection) {
- await this.connection.close();
- }
- }
-}
-
-// Usage
-const driver = new RabbitMQDriver('amqp://localhost');
-await driver.connect();
-setDriver(driver);
-```
-
-### AWS SQS Driver Example
-
-```typescript
-import {
- SQSClient,
- SendMessageCommand,
- ReceiveMessageCommand,
- DeleteMessageCommand,
-} from '@aws-sdk/client-sqs';
-import type { MessageQueue, Awaitable } from '@commandkit/queue';
-
-class SQSDriver implements MessageQueue {
- private client: SQSClient;
- private queueUrls = new Map();
- private polling = new Map();
-
- constructor(region: string) {
- this.client = new SQSClient({ region });
- }
-
- async send(topic: string, message: any): Promise {
- const queueUrl = await this.getQueueUrl(topic);
-
- await this.client.send(
- new SendMessageCommand({
- QueueUrl: queueUrl,
- MessageBody: JSON.stringify(message),
- }),
- );
- }
-
- async receive(
- topic: string,
- handler: (message: any) => Awaitable,
- ): Promise {
- const queueUrl = await this.getQueueUrl(topic);
-
- // Start polling for messages
- const pollMessages = async () => {
- try {
- const response = await this.client.send(
- new ReceiveMessageCommand({
- QueueUrl: queueUrl,
- MaxNumberOfMessages: 10,
- WaitTimeSeconds: 20,
- }),
- );
-
- for (const message of response.Messages || []) {
- try {
- const content = JSON.parse(message.Body!);
- await handler(content);
-
- // Delete the message after successful processing
- await this.client.send(
- new DeleteMessageCommand({
- QueueUrl: queueUrl,
- ReceiptHandle: message.ReceiptHandle!,
- }),
- );
- } catch (error) {
- console.error('Failed to process message:', error);
- }
- }
- } catch (error) {
- console.error('Error polling messages:', error);
- }
-
- // Continue polling
- this.polling.set(topic, setTimeout(pollMessages, 1000));
- };
-
- pollMessages();
- }
-
- async close(): Promise {
- // Stop all polling
- for (const timeout of this.polling.values()) {
- clearTimeout(timeout);
- }
- this.polling.clear();
- }
-
- private async getQueueUrl(topic: string): Promise {
- if (this.queueUrls.has(topic)) {
- return this.queueUrls.get(topic)!;
- }
-
- // Create queue if it doesn't exist
- // Implementation depends on your AWS setup
- const queueUrl = `https://sqs.${region}.amazonaws.com/123456789012/${topic}`;
- this.queueUrls.set(topic, queueUrl);
- return queueUrl;
- }
-}
-```
-
-## Error Handling Strategies
-
-### Retry Logic
-
-Implement retry logic for failed message processing:
-
-```typescript
-import { receive } from '@commandkit/queue';
-
-async function withRetry(
- fn: () => Promise,
- maxRetries: number = 3,
- delay: number = 1000,
-): Promise {
- let lastError: Error;
-
- for (let attempt = 1; attempt <= maxRetries; attempt++) {
- try {
- return await fn();
- } catch (error) {
- lastError = error as Error;
-
- if (attempt === maxRetries) {
- throw lastError;
- }
-
- // Exponential backoff
- await new Promise((resolve) => setTimeout(resolve, delay * attempt));
- }
- }
-
- throw lastError!;
-}
-
-// Usage with retry
-await receive('user-events', async (message) => {
- await withRetry(
- async () => {
- await processUserEvent(message);
- },
- 3,
- 1000,
- );
-});
-```
-
-### Dead Letter Queue
-
-Implement a dead letter queue for failed messages:
-
-```typescript
-import { send, receive } from '@commandkit/queue';
-
-// Process messages with dead letter queue
-await receive('user-events', async (message) => {
- try {
- await processUserEvent(message);
- } catch (error) {
- console.error('Failed to process message:', error);
-
- // Send to dead letter queue
- await send('dead-letter-queue', {
- originalTopic: 'user-events',
- message,
- error: error.message,
- timestamp: Date.now(),
- retryCount: 0,
- });
- }
-});
-
-// Process dead letter queue
-await receive('dead-letter-queue', async (message) => {
- const { originalTopic, message: originalMessage, retryCount } = message;
-
- if (retryCount < 3) {
- // Retry processing
- try {
- await processUserEvent(originalMessage);
- } catch (error) {
- // Increment retry count and send back to dead letter queue
- await send('dead-letter-queue', {
- ...message,
- retryCount: retryCount + 1,
- });
- }
- } else {
- // Log final failure
- console.error('Message permanently failed:', originalMessage);
- }
-});
-```
-
-### Circuit Breaker Pattern
-
-Implement a circuit breaker to prevent cascading failures:
-
-```typescript
-class CircuitBreaker {
- private failures = 0;
- private lastFailureTime = 0;
- private state: 'CLOSED' | 'OPEN' | 'HALF_OPEN' = 'CLOSED';
-
- constructor(
- private threshold: number = 5,
- private timeout: number = 60000,
- ) {}
-
- async execute(fn: () => Promise): Promise {
- if (this.state === 'OPEN') {
- if (Date.now() - this.lastFailureTime > this.timeout) {
- this.state = 'HALF_OPEN';
- } else {
- throw new Error('Circuit breaker is OPEN');
- }
- }
-
- try {
- const result = await fn();
- this.onSuccess();
- return result;
- } catch (error) {
- this.onFailure();
- throw error;
- }
- }
-
- private onSuccess(): void {
- this.failures = 0;
- this.state = 'CLOSED';
- }
-
- private onFailure(): void {
- this.failures++;
- this.lastFailureTime = Date.now();
-
- if (this.failures >= this.threshold) {
- this.state = 'OPEN';
- }
- }
-}
-
-// Usage
-const circuitBreaker = new CircuitBreaker(5, 60000);
-
-await receive('user-events', async (message) => {
- await circuitBreaker.execute(async () => {
- await processUserEvent(message);
- });
-});
-```
-
-## Performance Optimizations
-
-### Message Batching
-
-Batch multiple messages for better performance:
-
-```typescript
-class MessageBatcher {
- private batches = new Map();
- private timers = new Map();
-
- constructor(
- private batchSize: number = 100,
- private batchTimeout: number = 1000,
- ) {}
-
- async addToBatch(topic: string, message: any): Promise {
- if (!this.batches.has(topic)) {
- this.batches.set(topic, []);
- }
-
- this.batches.get(topic)!.push(message);
-
- // Flush batch if it's full
- if (this.batches.get(topic)!.length >= this.batchSize) {
- await this.flushBatch(topic);
- } else {
- // Set timeout to flush batch
- if (this.timers.has(topic)) {
- clearTimeout(this.timers.get(topic)!);
- }
-
- this.timers.set(
- topic,
- setTimeout(() => {
- this.flushBatch(topic);
- }, this.batchTimeout),
- );
- }
- }
-
- private async flushBatch(topic: string): Promise {
- const batch = this.batches.get(topic);
- if (!batch || batch.length === 0) return;
-
- // Send batched message
- await send(`${topic}-batch`, {
- messages: batch,
- timestamp: Date.now(),
- });
-
- // Clear batch and timer
- this.batches.set(topic, []);
- if (this.timers.has(topic)) {
- clearTimeout(this.timers.get(topic)!);
- this.timers.delete(topic);
- }
- }
-}
-
-// Usage
-const batcher = new MessageBatcher(50, 500);
-
-// Instead of sending individual messages
-await send('user-events', message);
-
-// Use batcher
-await batcher.addToBatch('user-events', message);
-```
-
-### Connection Pooling
-
-Implement connection pooling for better resource management:
-
-```typescript
-import Redis from 'ioredis';
-
-class RedisConnectionPool {
- private pool: Redis[] = [];
- private currentIndex = 0;
-
- constructor(
- private size: number = 10,
- private config: any = {},
- ) {}
-
- async initialize(): Promise {
- for (let i = 0; i < this.size; i++) {
- const redis = new Redis(this.config);
- this.pool.push(redis);
- }
- }
-
- getConnection(): Redis {
- const connection = this.pool[this.currentIndex];
- this.currentIndex = (this.currentIndex + 1) % this.size;
- return connection;
- }
-
- async close(): Promise {
- await Promise.all(this.pool.map((redis) => redis.quit()));
- this.pool = [];
- }
-}
-
-// Usage
-const pool = new RedisConnectionPool(10, {
- host: 'localhost',
- port: 6379,
-});
-
-await pool.initialize();
-
-// Use pooled connections
-const redis = pool.getConnection();
-const broker = new PubSubRedisBroker(redis);
-```
-
-## Monitoring and Metrics
-
-### Message Processing Metrics
-
-Track message processing performance:
-
-```typescript
-class QueueMetrics {
- private metrics = {
- messagesSent: 0,
- messagesReceived: 0,
- processingTimes: [] as number[],
- errors: 0,
- };
-
- recordMessageSent(): void {
- this.metrics.messagesSent++;
- }
-
- recordMessageReceived(): void {
- this.metrics.messagesReceived++;
- }
-
- recordProcessingTime(time: number): void {
- this.metrics.processingTimes.push(time);
-
- // Keep only last 1000 times
- if (this.metrics.processingTimes.length > 1000) {
- this.metrics.processingTimes.shift();
- }
- }
-
- recordError(): void {
- this.metrics.errors++;
- }
-
- getMetrics() {
- const avgProcessingTime =
- this.metrics.processingTimes.length > 0
- ? this.metrics.processingTimes.reduce((a, b) => a + b, 0) /
- this.metrics.processingTimes.length
- : 0;
-
- return {
- ...this.metrics,
- avgProcessingTime,
- errorRate:
- this.metrics.messagesReceived > 0
- ? this.metrics.errors / this.metrics.messagesReceived
- : 0,
- };
- }
-}
-
-// Usage
-const metrics = new QueueMetrics();
-
-await receive('user-events', async (message) => {
- const startTime = Date.now();
- metrics.recordMessageReceived();
-
- try {
- await processUserEvent(message);
- metrics.recordProcessingTime(Date.now() - startTime);
- } catch (error) {
- metrics.recordError();
- throw error;
- }
-});
-
-// Log metrics periodically
-setInterval(() => {
- console.log('Queue metrics:', metrics.getMetrics());
-}, 60000);
-```
-
-## Testing
-
-### Unit Testing
-
-Test your queue implementations:
-
-```typescript
-import { describe, it, expect, beforeEach, afterEach } from 'vitest';
-import { setDriver, send, receive } from '@commandkit/queue';
-
-class MockDriver implements MessageQueue {
- private messages: any[] = [];
- private handlers = new Map void>();
-
- async send(topic: string, message: any): Promise {
- this.messages.push({ topic, message });
-
- // Trigger handler if exists
- const handler = this.handlers.get(topic);
- if (handler) {
- handler(message);
- }
- }
-
- async receive(topic: string, handler: (message: any) => void): Promise {
- this.handlers.set(topic, handler);
- }
-
- getMessages() {
- return this.messages;
- }
-}
-
-describe('Queue', () => {
- let mockDriver: MockDriver;
-
- beforeEach(() => {
- mockDriver = new MockDriver();
- setDriver(mockDriver);
- });
-
- afterEach(() => {
- // Clean up
- });
-
- it('should send and receive messages', async () => {
- const receivedMessages: any[] = [];
-
- await receive('test-topic', (message) => {
- receivedMessages.push(message);
- });
-
- await send('test-topic', { test: 'data' });
-
- expect(receivedMessages).toHaveLength(1);
- expect(receivedMessages[0]).toEqual({ test: 'data' });
- });
-});
-```
-
-## Next Steps
-
-You now have a comprehensive understanding of CommandKit Queue's advanced features. You can:
-
-- Create custom drivers for your specific message queue backend
-- Implement robust error handling with retries and dead letter queues
-- Optimize performance with batching and connection pooling
-- Monitor your queue system with metrics
-- Test your queue implementations thoroughly
diff --git a/apps/website/docs/guide/17-tasks/01-getting-started.mdx b/apps/website/docs/guide/17-tasks/01-getting-started.mdx
deleted file mode 100644
index 689acf63..00000000
--- a/apps/website/docs/guide/17-tasks/01-getting-started.mdx
+++ /dev/null
@@ -1,164 +0,0 @@
----
-title: Getting Started with Tasks
-description: Learn how to set up and use the tasks plugin for scheduling background tasks in your CommandKit bot.
----
-
-# Getting Started with Tasks
-
-The tasks plugin provides a powerful way to schedule and manage background tasks in your CommandKit bot. Whether you need to run periodic maintenance, send scheduled reminders, or perform data cleanup, the tasks plugin has you covered.
-
-## Installation
-
-First, install the tasks plugin:
-
-```bash
-npm install @commandkit/tasks
-```
-
-## Basic Setup
-
-Add the tasks plugin to your CommandKit configuration:
-
-```ts
-import { defineConfig } from 'commandkit/config';
-import { tasks } from '@commandkit/tasks';
-
-export default defineConfig({
- plugins: [tasks()],
-});
-```
-
-## Setting Up a Driver (optional)
-
-By default, the plugin will initialize the sqlite driver. You can set up a different driver by calling `setDriver` function from the `@commandkit/tasks` package.
-If you want to disable the default driver initialization behavior, you can pass `initializeDefaultDriver: false` to the `tasks()` options in your commandkit config.
-
-```ts
-import { setDriver } from '@commandkit/tasks';
-import { SQLiteDriver } from '@commandkit/tasks/sqlite';
-
-// For development
-setDriver(new SQLiteDriver('./tasks.db'));
-
-// For production, use BullMQ with Redis
-// setDriver(new BullMQDriver({ host: 'localhost', port: 6379 }));
-```
-
-## Creating Your First Task
-
-Create a file in `src/app/tasks/` to define your tasks:
-
-```ts
-import { task } from '@commandkit/tasks';
-
-export default task({
- name: 'daily-backup',
- schedule: '0 0 * * *', // Daily at midnight (cron string)
- async execute(ctx) {
- // Your task logic here
- await performBackup();
- console.log('Daily backup completed!');
- },
-});
-```
-
-## Task Structure
-
-Every task has the following components:
-
-- **name**: A unique identifier for the task
-- **schedule**: When the task should run (optional for manual execution)
-- **execute**: The main function that performs the task work
-- **prepare**: Optional function to determine if the task should run
-
-## Schedule Types
-
-### Cron Schedules
-
-Use cron expressions for recurring tasks:
-
-```ts
-export default task({
- name: 'hourly-task',
- schedule: '0 * * * *', // Every hour
- async execute(ctx) {
- // Task logic
- },
-});
-```
-
-### Date Schedules
-
-Schedule tasks for specific times:
-
-```ts
-export default task({
- name: 'reminder',
- schedule: new Date('2024-01-01T12:00:00Z'), // Specific date
- async execute(ctx) {
- // Send reminder
- },
-});
-
-// Or use timestamps
-export default task({
- name: 'timestamp-task',
- schedule: Date.now() + 60000, // 1 minute from now
- async execute(ctx) {
- // Task logic
- },
-});
-```
-
-## Task Context
-
-The `execute` function receives a context object with useful properties:
-
-```ts
-export default task({
- name: 'context-example',
- schedule: '0 */6 * * *', // Every 6 hours
- async execute(ctx) {
- // Access the Discord.js client
- const client = ctx.commandkit.client;
-
- // Access custom data (for dynamic tasks)
- const { userId, message } = ctx.data;
-
- // Use the temporary store
- ctx.store.set('lastRun', Date.now());
-
- // Send a message to a channel
- const channel = client.channels.cache.get('channel-id');
- if (channel?.isTextBased()) {
- await channel.send('Task executed!');
- }
- },
-});
-```
-
-## Conditional Execution
-
-Use the `prepare` function to conditionally execute tasks:
-
-```ts
-export default task({
- name: 'conditional-task',
- schedule: '0 */2 * * *', // Every 2 hours
- async prepare(ctx) {
- // Only run if maintenance mode is not enabled
- return !ctx.commandkit.store.get('maintenance-mode');
- },
- async execute(ctx) {
- await performMaintenanceChecks();
- },
-});
-```
-
-## Next Steps
-
-Now that you have the basics, explore:
-
-- [Task Drivers](./02-task-drivers) - Learn about different persistence options
-- [Dynamic Tasks](./03-dynamic-tasks) - Create tasks on-demand from commands
-- [Advanced Patterns](./04-advanced-patterns) - Best practices and advanced usage
diff --git a/apps/website/docs/guide/17-tasks/02-task-drivers.mdx b/apps/website/docs/guide/17-tasks/02-task-drivers.mdx
deleted file mode 100644
index 43768d65..00000000
--- a/apps/website/docs/guide/17-tasks/02-task-drivers.mdx
+++ /dev/null
@@ -1,200 +0,0 @@
----
-title: Task Drivers
-description: Learn about different task drivers and how to configure them for your use case.
----
-
-# Task Drivers
-
-Task drivers handle the persistence and scheduling of your tasks. The tasks plugin supports multiple drivers to fit different deployment scenarios and requirements.
-
-## Available Drivers
-
-### SQLite Driver (Development)
-
-The SQLite driver provides persistent, file-based task scheduling. It's perfect for development environments and single-instance applications with job recovery on restart.
-
-**Pros:**
-
-- Persistent job storage
-- Jobs recoverable on restart
-- No external dependencies
-- Lightweight and reliable
-
-**Cons:**
-
-- Single instance only
-- No distributed scheduling support
-- Intended for development use
-
-**Usage:**
-
-```ts
-import { SQLiteDriver } from '@commandkit/tasks/sqlite';
-import { setDriver } from '@commandkit/tasks';
-
-setDriver(new SQLiteDriver('./tasks.db'));
-```
-
-### BullMQ Driver (Production)
-
-The BullMQ driver provides robust, distributed task scheduling using Redis as the backend. It's ideal for production environments with multiple bot instances.
-
-**Pros:**
-
-- Distributed scheduling across multiple instances
-- Persistent task storage
-- Built-in retry mechanisms
-- Production-ready with Redis
-
-**Cons:**
-
-- Requires Redis server
-- More complex setup
-- Additional dependency
-
-**Installation:**
-
-```bash
-npm install bullmq
-```
-
-**Usage:**
-
-```ts
-import { BullMQDriver } from '@commandkit/tasks/bullmq';
-import { setDriver } from '@commandkit/tasks';
-
-setDriver(
- new BullMQDriver({
- host: 'localhost',
- port: 6379,
- }),
-);
-```
-
-**Advanced Redis Configuration:**
-
-```ts
-const driver = new BullMQDriver({
- host: 'redis.example.com',
- port: 6379,
- password: 'your-password',
- tls: true,
- retryDelayOnFailover: 100,
- maxRetriesPerRequest: 3,
-});
-```
-
-## Setting Up Drivers
-
-### Basic Setup
-
-Configure your driver before the tasks plugin loads:
-
-```ts
-import { tasks } from '@commandkit/tasks';
-import { SQLiteDriver } from '@commandkit/tasks/sqlite';
-import { setDriver } from '@commandkit/tasks';
-
-// Set up the driver
-setDriver(new SQLiteDriver('./tasks.db'));
-
-export default {
- plugins: [tasks()],
-};
-```
-
-### Environment-Specific Configuration
-
-```ts
-import { tasks } from '@commandkit/tasks';
-import { SQLiteDriver } from '@commandkit/tasks/sqlite';
-import { BullMQDriver } from '@commandkit/tasks/bullmq';
-import { setDriver } from '@commandkit/tasks';
-import { COMMANDKIT_IS_DEV } from 'commandkit';
-
-// Choose driver based on environment
-if (COMMANDKIT_IS_DEV) {
- setDriver(new SQLiteDriver('./tasks.db'));
-} else {
- setDriver(
- new BullMQDriver({
- host: process.env.REDIS_HOST || 'localhost',
- port: parseInt(process.env.REDIS_PORT || '6379'),
- password: process.env.REDIS_PASSWORD,
- }),
- );
-}
-
-export default {
- plugins: [tasks()],
-};
-```
-
-## Driver Comparison
-
-| Feature | SQLite | BullMQ |
-| -------------------- | ----------- | --------------- |
-| **Setup Complexity** | Simple | Moderate |
-| **Dependencies** | None | `bullmq`, Redis |
-| **Persistence** | File-based | Redis |
-| **Distributed** | No | Yes |
-| **Job Recovery** | Yes | Yes |
-| **Production Ready** | Development | Yes |
-| **Memory Usage** | Low | Moderate |
-
-## Choosing the Right Driver
-
-### Use SQLite when:
-
-- You're developing locally
-- You want persistent job storage during development
-- You have a single bot instance
-- You need job recovery on restart
-
-### Use BullMQ when:
-
-- You have multiple bot instances
-- You need distributed task scheduling
-- You're deploying to production
-- You need advanced features like retries and monitoring
-
-## Custom Drivers
-
-You can create your own driver by implementing the `TaskDriver` interface:
-
-```ts
-import { TaskDriver, TaskRunner } from '@commandkit/tasks';
-import { TaskData } from '@commandkit/tasks';
-
-class CustomDriver implements TaskDriver {
- private runner: TaskRunner | null = null;
-
- async create(task: TaskData): Promise {
- // Implement your scheduling logic
- const id = await this.scheduler.schedule(task);
-
- // invoke the runner function to execute the task (normally, this would be invoked by the scheduler)
- await this.runner?.(task);
-
- return id;
- }
-
- async delete(task: string): Promise {
- // Implement your deletion logic
- await this.scheduler.cancel(task);
- }
-
- async setTaskRunner(runner: TaskRunner): Promise {
- this.runner = runner;
- }
-}
-
-// Use your custom driver
-setDriver(new CustomDriver());
-```
-
-## Next Steps
-
-- [Dynamic Tasks](./03-dynamic-tasks) - Learn how to create tasks on-demand
-- [Advanced Patterns](./04-advanced-patterns) - Best practices for task management
diff --git a/apps/website/docs/guide/17-tasks/03-dynamic-tasks.mdx b/apps/website/docs/guide/17-tasks/03-dynamic-tasks.mdx
deleted file mode 100644
index d7820580..00000000
--- a/apps/website/docs/guide/17-tasks/03-dynamic-tasks.mdx
+++ /dev/null
@@ -1,474 +0,0 @@
----
-title: Dynamic Tasks
-description: Learn how to create tasks on-demand from commands and events.
----
-
-# Dynamic Tasks
-
-While static tasks are great for recurring operations, you often need to create tasks dynamically based on user interactions or events. The tasks plugin provides utilities for creating tasks on-demand.
-
-## Creating Dynamic Tasks
-
-Use the `createTask` function to create tasks programmatically:
-
-```ts
-import { createTask } from '@commandkit/tasks';
-
-// Create a task that runs in 5 minutes
-const taskId = await createTask({
- name: 'reminder',
- data: { userId: '123', message: "Don't forget your meeting!" },
- schedule: Date.now() + 5 * 60 * 1000, // 5 minutes from now
-});
-```
-
-## Reminder System Example
-
-Here's a complete example of a reminder command that creates dynamic tasks:
-
-```ts
-import type { CommandData, ChatInputCommand } from 'commandkit';
-import { ApplicationCommandOptionType } from 'discord.js';
-import ms from 'ms';
-import { createTask } from '@commandkit/tasks';
-
-export const command: CommandData = {
- name: 'remind',
- description: 'remind command',
- options: [
- {
- name: 'time',
- description: 'The time to remind after. Eg: 6h, 10m, 1d',
- type: ApplicationCommandOptionType.String,
- required: true,
- },
- {
- name: 'message',
- description: 'The message to remind about.',
- type: ApplicationCommandOptionType.String,
- required: true,
- },
- ],
-};
-
-export const chatInput: ChatInputCommand = async (ctx) => {
- const time = ctx.options.getString('time', true);
- const message = ctx.options.getString('message', true);
- const timeMs = Date.now() + ms(time as `${number}`);
-
- await createTask({
- name: 'remind',
- data: {
- userId: ctx.interaction.user.id,
- message,
- channelId: ctx.interaction.channelId,
- setAt: Date.now(),
- },
- schedule: timeMs,
- });
-
- await ctx.interaction.reply(
- `I will remind you for \`${message}\``,
- );
-};
-```
-
-## The Reminder Task
-
-Create a static task definition that handles the actual reminder:
-
-```ts
-// src/app/tasks/remind.ts
-import { task } from '@commandkit/tasks';
-
-export interface RemindTaskData {
- userId: string;
- message: string;
- channelId: string;
- setAt: number;
-}
-
-export default task({
- name: 'remind',
- async execute(ctx) {
- const { userId, message, channelId, setAt } = ctx.data;
-
- const channel = await ctx.client.channels.fetch(channelId);
-
- if (!channel?.isTextBased() || !channel.isSendable()) return;
-
- await channel.send({
- content: `<@${userId}>`,
- embeds: [
- {
- title: `You asked me to remind you about:`,
- description: message,
- color: 0x0099ff,
- timestamp: new Date(setAt).toISOString(),
- },
- ],
- });
- },
-});
-```
-
-## Advanced Dynamic Task Patterns
-
-### Conditional Task Creation
-
-```ts
-import type { CommandData, ChatInputCommand } from 'commandkit';
-import { ApplicationCommandOptionType } from 'discord.js';
-import { createTask } from '@commandkit/tasks';
-
-export const command: CommandData = {
- name: 'schedule-maintenance',
- description: 'Schedule maintenance for a specific time',
- options: [
- {
- name: 'duration',
- description: 'Duration in minutes',
- type: ApplicationCommandOptionType.Integer,
- required: true,
- },
- ],
-};
-
-export const chatInput: ChatInputCommand = async (ctx) => {
- const maintenanceMode = ctx.commandkit.store.get('maintenance-mode');
-
- if (maintenanceMode) {
- await ctx.interaction.reply('Maintenance mode is already enabled.');
- return;
- }
-
- const duration = ctx.options.getInteger('duration', true);
-
- // Create maintenance task
- await createTask({
- name: 'maintenance',
- data: {
- requestedBy: ctx.interaction.user.id,
- duration: duration * 60 * 1000, // Convert to milliseconds
- },
- schedule: Date.now() + 5 * 60 * 1000, // 5 minutes from now
- });
-
- await ctx.interaction.reply('Maintenance scheduled for 5 minutes from now.');
-};
-```
-
-### Batch Task Creation
-
-```ts
-import type { CommandData, ChatInputCommand } from 'commandkit';
-import { ApplicationCommandOptionType } from 'discord.js';
-import { createTask } from '@commandkit/tasks';
-
-export const command: CommandData = {
- name: 'schedule-events',
- description: 'Schedule multiple events',
- options: [
- {
- name: 'events',
- description: 'Number of events to schedule',
- type: ApplicationCommandOptionType.Integer,
- required: true,
- },
- ],
-};
-
-export const chatInput: ChatInputCommand = async (ctx) => {
- const eventCount = ctx.options.getInteger('events', true);
-
- const events = Array.from({ length: eventCount }, (_, i) => ({
- name: `Event ${i + 1}`,
- time: Date.now() + (i + 1) * 60 * 60 * 1000, // 1 hour apart
- }));
-
- const taskIds = await Promise.all(
- events.map((event) =>
- createTask({
- name: 'event-notification',
- data: {
- eventName: event.name,
- channelId: ctx.interaction.channelId,
- },
- schedule: event.time,
- }),
- ),
- );
-
- await ctx.interaction.reply(`Scheduled ${events.length} events.`);
-};
-```
-
-## Managing Dynamic Tasks
-
-### Deleting Tasks
-
-```ts
-import type { CommandData, ChatInputCommand } from 'commandkit';
-import { ApplicationCommandOptionType } from 'discord.js';
-import { deleteTask } from '@commandkit/tasks';
-
-export const command: CommandData = {
- name: 'cancel-reminder',
- description: 'Cancel a scheduled reminder',
- options: [
- {
- name: 'task-id',
- description: 'The task ID to cancel',
- type: ApplicationCommandOptionType.String,
- required: true,
- },
- ],
-};
-
-export const chatInput: ChatInputCommand = async (ctx) => {
- const taskId = ctx.options.getString('task-id', true);
-
- try {
- await deleteTask(taskId);
- await ctx.interaction.reply('Reminder cancelled successfully.');
- } catch (error) {
- await ctx.interaction.reply(
- 'Failed to cancel reminder. It may have already been executed.',
- );
- }
-};
-```
-
-### Task Storage and Retrieval
-
-For more complex scenarios, you might want to store task IDs in your database:
-
-```ts
-import type { CommandData, ChatInputCommand } from 'commandkit';
-import { ApplicationCommandOptionType } from 'discord.js';
-import ms from 'ms';
-import { createTask } from '@commandkit/tasks';
-
-export const command: CommandData = {
- name: 'remind',
- description: 'Set a reminder',
- options: [
- {
- name: 'time',
- description: 'The time to remind after. Eg: 6h, 10m, 1d',
- type: ApplicationCommandOptionType.String,
- required: true,
- },
- {
- name: 'message',
- description: 'The message to remind about.',
- type: ApplicationCommandOptionType.String,
- required: true,
- },
- ],
-};
-
-export const chatInput: ChatInputCommand = async (ctx) => {
- const timeStr = ctx.options.getString('time', true);
- const message = ctx.options.getString('message', true);
-
- const delay = ms(timeStr as `${number}`);
- const taskId = await createTask({
- name: 'reminder',
- data: {
- userId: ctx.interaction.user.id,
- message,
- },
- schedule: Date.now() + delay,
- });
-
- // Store the task ID in your database
- await db.reminders.create({
- userId: ctx.interaction.user.id,
- taskId,
- message,
- scheduledFor: new Date(Date.now() + delay),
- });
-
- await ctx.interaction.reply(`Reminder set! Task ID: ${taskId}`);
-};
-```
-
-### Error Handling
-
-Always handle potential errors when creating dynamic tasks:
-
-```ts
-import type { CommandData, ChatInputCommand } from 'commandkit';
-import { ApplicationCommandOptionType } from 'discord.js';
-import { createTask } from '@commandkit/tasks';
-
-export const command: CommandData = {
- name: 'schedule-task',
- description: 'Schedule a custom task',
- options: [
- {
- name: 'delay',
- description: 'Delay in minutes',
- type: ApplicationCommandOptionType.Integer,
- required: true,
- },
- ],
-};
-
-export const chatInput: ChatInputCommand = async (ctx) => {
- try {
- const delay = ctx.options.getInteger('delay', true);
- const taskId = await createTask({
- name: 'custom-task',
- data: { userId: ctx.interaction.user.id },
- schedule: Date.now() + delay * 60 * 1000,
- });
-
- await ctx.interaction.reply(`Task scheduled successfully. ID: ${taskId}`);
- } catch (error) {
- console.error('Failed to schedule task:', error);
- await ctx.interaction.reply('Failed to schedule task. Please try again.');
- }
-};
-```
-
-## Best Practices
-
-### 1. Use Descriptive Task Names
-
-```ts
-// Good
-await createTask({
- name: 'user-reminder',
- data: { userId, message },
- schedule: reminderTime,
-});
-
-// Avoid
-await createTask({
- name: 'task',
- data: { userId, message },
- schedule: reminderTime,
-});
-```
-
-### 2. Validate Input Data
-
-```ts
-import type { CommandData, ChatInputCommand } from 'commandkit';
-import { ApplicationCommandOptionType } from 'discord.js';
-import ms from 'ms';
-import { createTask } from '@commandkit/tasks';
-
-export const command: CommandData = {
- name: 'schedule-reminder',
- description: 'Schedule a reminder',
- options: [
- {
- name: 'time',
- description: 'The time to remind after. Eg: 6h, 10m, 1d',
- type: ApplicationCommandOptionType.String,
- required: true,
- },
- {
- name: 'message',
- description: 'The message to remind about.',
- type: ApplicationCommandOptionType.String,
- required: true,
- },
- ],
-};
-
-export const chatInput: ChatInputCommand = async (ctx) => {
- const timeStr = ctx.options.getString('time', true);
- const message = ctx.options.getString('message', true);
-
- // Validate time format
- const delay = ms(timeStr as `${number}`);
- if (!delay || delay < 60000 || delay > 30 * 24 * 60 * 60 * 1000) {
- await ctx.interaction.reply(
- 'Please specify a time between 1 minute and 30 days.',
- );
- return;
- }
-
- // Validate message length
- if (message.length > 1000) {
- await ctx.interaction.reply(
- 'Message too long. Please keep it under 1000 characters.',
- );
- return;
- }
-
- // Create task
- await createTask({
- name: 'reminder',
- data: { userId: ctx.interaction.user.id, message },
- schedule: Date.now() + delay,
- });
-
- await ctx.interaction.reply('Reminder scheduled successfully!');
-};
-```
-
-### 3. Handle Task Limits
-
-```ts
-import type { CommandData, ChatInputCommand } from 'commandkit';
-import { ApplicationCommandOptionType } from 'discord.js';
-import { createTask } from '@commandkit/tasks';
-
-export const command: CommandData = {
- name: 'schedule-reminder',
- description: 'Schedule a reminder',
- options: [
- {
- name: 'time',
- description: 'The time to remind after. Eg: 6h, 10m, 1d',
- type: ApplicationCommandOptionType.String,
- required: true,
- },
- {
- name: 'message',
- description: 'The message to remind about.',
- type: ApplicationCommandOptionType.String,
- required: true,
- },
- ],
-};
-
-export const chatInput: ChatInputCommand = async (ctx) => {
- const userId = ctx.interaction.user.id;
-
- // Check existing reminders
- const existingReminders = await db.reminders.count({
- where: { userId, active: true },
- });
-
- if (existingReminders >= 10) {
- await ctx.interaction.reply(
- 'You already have 10 active reminders. Please cancel some first.',
- );
- return;
- }
-
- // Create new reminder
- const timeStr = ctx.options.getString('time', true);
- const message = ctx.options.getString('message', true);
- const delay = ms(timeStr as `${number}`);
-
- await createTask({
- name: 'reminder',
- data: { userId, message },
- schedule: Date.now() + delay,
- });
-
- await ctx.interaction.reply('Reminder scheduled successfully!');
-};
-```
-
-## Next Steps
-
-- [Advanced Patterns](./04-advanced-patterns) - Learn advanced task management techniques
-- [Task Drivers](./02-task-drivers) - Understand different persistence options
diff --git a/apps/website/docs/guide/17-tasks/04-advanced-patterns.mdx b/apps/website/docs/guide/17-tasks/04-advanced-patterns.mdx
deleted file mode 100644
index 31b233e3..00000000
--- a/apps/website/docs/guide/17-tasks/04-advanced-patterns.mdx
+++ /dev/null
@@ -1,365 +0,0 @@
----
-title: Advanced Patterns
-description: Learn advanced patterns and best practices for task management.
----
-
-# Advanced Patterns
-
-Once you're comfortable with the basics, you can implement more sophisticated task management patterns. This guide covers advanced techniques for complex use cases.
-
-## Task Workflows
-
-Organize related tasks to create complex workflows:
-
-```ts
-// src/app/tasks/data-processing.ts
-import { task } from '@commandkit/tasks';
-
-export default task({
- name: 'data-processing',
- schedule: '0 2 * * *', // Daily at 2 AM
- async execute(ctx) {
- // Step 1: Collect data
- const data = await collectData();
- ctx.store.set('collectedData', data);
-
- // Step 2: Process data immediately
- const processedData = await processData(data);
-
- // Step 3: Send notification
- const channel = ctx.commandkit.client.channels.cache.get('log-channel');
- if (channel?.isTextBased()) {
- await channel.send(`Data processing completed for ID: ${data.id}`);
- }
- },
-});
-```
-
-## Task Retry Logic
-
-Implement custom retry logic for failed tasks:
-
-```ts
-// src/app/tasks/retry-example.ts
-import { task } from '@commandkit/tasks';
-
-export default task({
- name: 'retry-task',
- async execute(ctx) {
- const { attempt = 1, maxAttempts = 3 } = ctx.data;
-
- try {
- // Attempt the operation
- await performRiskyOperation();
-
- // Success - no need to retry
- console.log('Operation completed successfully');
- } catch (error) {
- console.error(`Attempt ${attempt} failed:`, error);
-
- if (attempt < maxAttempts) {
- // Log retry attempt
- console.log(`Will retry attempt ${attempt + 1} of ${maxAttempts}`);
-
- // In a real implementation, you might use a different approach
- // such as storing retry state in a database
- } else {
- // Max attempts reached
- console.error('Max retry attempts reached');
-
- // Notify about failure
- const channel =
- ctx.commandkit.client.channels.cache.get('error-channel');
- if (channel?.isTextBased()) {
- await channel.send(`Task failed after ${maxAttempts} attempts`);
- }
- }
- }
- },
-});
-```
-
-## Task Dependencies
-
-Manage tasks that depend on other conditions:
-
-```ts
-// src/app/tasks/dependency-example.ts
-import { task } from '@commandkit/tasks';
-
-export default task({
- name: 'dependency-task',
- async execute(ctx) {
- const { requiredData } = ctx.data;
-
- // Check if required conditions are met
- const conditionsMet = await checkRequiredConditions();
-
- if (!conditionsMet) {
- console.log('Required conditions not met, skipping task execution');
- return;
- }
-
- // Conditions met, proceed with the task
- await processWithDependentData(requiredData);
- },
-});
-```
-
-## Task Batching
-
-Process multiple items in batches:
-
-```ts
-// src/app/tasks/batch-processing.ts
-import { task } from '@commandkit/tasks';
-
-export default task({
- name: 'batch-processor',
- schedule: '0 */6 * * *', // Every 6 hours
- async execute(ctx) {
- // Get items to process
- const items = await getItemsToProcess();
-
- if (items.length === 0) {
- console.log('No items to process');
- return;
- }
-
- // Process in batches of 10
- const batchSize = 10;
- let processedCount = 0;
-
- for (let i = 0; i < items.length; i += batchSize) {
- const batch = items.slice(i, i + batchSize);
- console.log(
- `Processing batch ${Math.floor(i / batchSize) + 1}/${Math.ceil(items.length / batchSize)}`,
- );
-
- // Process the batch
- for (const item of batch) {
- await processItem(item);
- processedCount++;
- }
- }
-
- console.log(`Completed processing ${processedCount} items`);
- },
-});
-```
-
-## Task Monitoring and Metrics
-
-Track task execution metrics:
-
-```ts
-// src/app/tasks/metrics.ts
-import { task } from '@commandkit/tasks';
-
-export default task({
- name: 'metrics-task',
- schedule: '0 * * * *', // Every hour
- async execute(ctx) {
- const startTime = Date.now();
-
- try {
- // Perform the task
- await performTask();
-
- // Record success metrics
- await recordMetrics({
- taskName: 'metrics-task',
- status: 'success',
- duration: Date.now() - startTime,
- timestamp: new Date(),
- });
- } catch (error) {
- // Record failure metrics
- await recordMetrics({
- taskName: 'metrics-task',
- status: 'error',
- duration: Date.now() - startTime,
- error: error.message,
- timestamp: new Date(),
- });
-
- throw error; // Re-throw to let the task system handle it
- }
- },
-});
-```
-
-## Task State Management
-
-Use the context store to manage state across task executions:
-
-```ts
-// src/app/tasks/state-management.ts
-import { task } from '@commandkit/tasks';
-
-export default task({
- name: 'stateful-task',
- schedule: '0 */2 * * *', // Every 2 hours
- async prepare(ctx) {
- // Check if we're in a cooldown period
- const lastRun = ctx.commandkit.store.get('last-run');
- const cooldown = 30 * 60 * 1000; // 30 minutes
-
- if (lastRun && Date.now() - lastRun < cooldown) {
- return false; // Skip execution
- }
-
- return true;
- },
- async execute(ctx) {
- // Update last run time
- ctx.commandkit.store.set('last-run', Date.now());
-
- // Get or initialize counter
- const counter = ctx.commandkit.store.get('execution-counter') || 0;
- ctx.commandkit.store.set('execution-counter', counter + 1);
-
- console.log(`Task executed ${counter + 1} times`);
-
- // Perform the actual work
- await performWork();
- },
-});
-```
-
-## Task Cleanup
-
-Implement cleanup tasks for resource management:
-
-```ts
-// src/app/tasks/cleanup.ts
-import { task } from '@commandkit/tasks';
-
-export default task({
- name: 'cleanup',
- schedule: '0 3 * * *', // Daily at 3 AM
- async execute(ctx) {
- const cutoffDate = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000); // 7 days ago
-
- // Clean up old data
- await cleanupOldRecords(cutoffDate);
-
- // Clean up expired tasks
- await cleanupExpiredTasks();
-
- // Clean up temporary files
- await cleanupTempFiles();
-
- console.log('Cleanup completed successfully');
- },
-});
-```
-
-## Task Error Recovery
-
-Implement robust error recovery mechanisms:
-
-```ts
-// src/app/tasks/error-recovery.ts
-import { task } from '@commandkit/tasks';
-
-export default task({
- name: 'resilient-task',
- async execute(ctx) {
- const { operation, fallbackOperation } = ctx.data;
-
- try {
- // Try the primary operation
- await performOperation(operation);
- } catch (error) {
- console.error('Primary operation failed:', error);
-
- // Try fallback operation
- try {
- await performOperation(fallbackOperation);
- console.log('Fallback operation succeeded');
- } catch (fallbackError) {
- console.error('Fallback operation also failed:', fallbackError);
-
- // Log the failure for manual intervention
- const channel =
- ctx.commandkit.client.channels.cache.get('error-channel');
- if (channel?.isTextBased()) {
- await channel.send(
- `Task failed: ${error.message}. Fallback also failed: ${fallbackError.message}`,
- );
- }
- }
- }
- },
-});
-```
-
-## Task Scheduling Patterns
-
-### Rolling Windows
-
-```ts
-// src/app/tasks/rolling-window.ts
-import { task } from '@commandkit/tasks';
-
-export default task({
- name: 'rolling-window',
- schedule: '*/15 * * * *', // Every 15 minutes
- async execute(ctx) {
- const windowSize = 60 * 60 * 1000; // 1 hour
- const now = Date.now();
-
- // Get data from the last hour
- const data = await getDataInWindow(now - windowSize, now);
-
- // Process the rolling window
- await processRollingWindow(data);
- },
-});
-```
-
-### Adaptive Processing
-
-```ts
-// src/app/tasks/adaptive-processing.ts
-import { task } from '@commandkit/tasks';
-
-export default task({
- name: 'adaptive-task',
- schedule: '*/5 * * * *', // Every 5 minutes
- async execute(ctx) {
- // Get current system load
- const load = await getCurrentLoad();
-
- // Adjust processing based on load
- if (load > 0.8) {
- // High load - process fewer items
- await performTask({ maxItems: 10 });
- } else if (load > 0.5) {
- // Medium load - process normal amount
- await performTask({ maxItems: 50 });
- } else {
- // Low load - process more items
- await performTask({ maxItems: 100 });
- }
- },
-});
-```
-
-## Best Practices Summary
-
-1. **Error Handling**: Always handle errors gracefully and implement fallback mechanisms
-2. **Resource Management**: Clean up resources and implement proper cleanup tasks
-3. **Monitoring**: Track task execution metrics and performance
-4. **State Management**: Use the context store for sharing data between task phases
-5. **Conditional Execution**: Check required conditions before proceeding with task work
-6. **Batching**: Process large datasets in manageable batches
-7. **Adaptive Processing**: Adjust processing based on system load
-8. **Logging**: Implement comprehensive logging for debugging and monitoring
-
-## Next Steps
-
-- [Getting Started](./01-getting-started) - Review the basics
-- [Task Drivers](./02-task-drivers) - Understand persistence options
-- [Dynamic Tasks](./03-dynamic-tasks) - Learn about on-demand task creation
diff --git a/apps/website/docs/guide/99-helper-functions/01-after.mdx b/apps/website/docs/guide/99-helper-functions/01-after.mdx
deleted file mode 100644
index 18c88f36..00000000
--- a/apps/website/docs/guide/99-helper-functions/01-after.mdx
+++ /dev/null
@@ -1,25 +0,0 @@
----
-title: after Function
-description: The after function is a utility function that allows you to execute a callback after current command has been executed. This is useful for performing actions that should occur after the command has completed, such as logging or cleanup tasks.
----
-
-The `after()` function is a utility function that allows you to execute a callback after the current command has been executed. This is useful for performing actions that should occur after the command has completed, such as logging or cleanup tasks.
-It is guaranteed to be called after the command has been executed, regardless of whether the command was successful or not.
-
-## Usage
-
-```ts
-import { after, ChatInputCommand } from 'commandkit';
-
-export const chatInput: ChatInputCommand = async (ctx) => {
- after(() => {
- // This code will be executed after the command has been executed
- console.log('Command has been executed');
- });
-
- await ctx.interaction.reply({
- content: 'Hello World',
- ephemeral: true,
- });
-};
-```
diff --git a/apps/website/docusaurus.config.ts b/apps/website/docusaurus.config.ts
index 3a8ccb7f..dfa98418 100644
--- a/apps/website/docusaurus.config.ts
+++ b/apps/website/docusaurus.config.ts
@@ -19,7 +19,7 @@ const maybeUpperCase = (str: string) => {
const config: Config = {
title: 'CommandKit',
- tagline: 'A Discord.js handler',
+ tagline: 'The discord.js meta-framework',
favicon: 'img/favicon.ico',
url: 'https://commandkit.dev',
baseUrl: '/',
@@ -34,6 +34,18 @@ const config: Config = {
'classic',
{
docs: {
+ lastVersion: '0.1.10',
+ versions: {
+ current: {
+ label: 'v1',
+ // path: 'v1',
+ badge: false,
+ },
+ '0.1.10': {
+ label: 'v0.1.10',
+ path: 'v0',
+ },
+ },
sidebarPath: './sidebars.ts',
showLastUpdateAuthor: true,
showLastUpdateTime: true,
@@ -127,6 +139,10 @@ const config: Config = {
{
type: 'docsVersionDropdown',
position: 'right',
+ versions: {
+ current: { label: 'v1' },
+ '0.1.10': { label: 'v0' },
+ },
},
{
href: 'https://github.com/underctrl-io/commandkit',
diff --git a/apps/website/ignore_build.js b/apps/website/ignore_build.js
index e3d22436..29ac96c9 100644
--- a/apps/website/ignore_build.js
+++ b/apps/website/ignore_build.js
@@ -16,7 +16,7 @@ function hasWebsiteChanges() {
if (process.env.CACHED_COMMIT_REF) {
return execSync(
`git diff --name-only ${process.env.CACHED_COMMIT_REF}...HEAD`,
- { encoding: 'utf8' }
+ { encoding: 'utf8' },
);
}
return null;
@@ -24,7 +24,9 @@ function hasWebsiteChanges() {
// Strategy 2: Use git diff with HEAD~1 (compare with previous commit)
() => {
- return execSync('git diff --name-only HEAD~1...HEAD', { encoding: 'utf8' });
+ return execSync('git diff --name-only HEAD~1...HEAD', {
+ encoding: 'utf8',
+ });
},
// Strategy 3: Check git status for uncommitted changes
@@ -35,8 +37,10 @@ function hasWebsiteChanges() {
// Strategy 4: Fetch and compare with origin/main
() => {
execSync('git fetch origin main --depth=1', { stdio: 'ignore' });
- return execSync('git diff --name-only origin/main...HEAD', { encoding: 'utf8' });
- }
+ return execSync('git diff --name-only origin/main...HEAD', {
+ encoding: 'utf8',
+ });
+ },
];
// Try each strategy until one works
@@ -54,21 +58,21 @@ function hasWebsiteChanges() {
}
if (!changedFiles) {
- console.log('Could not determine changes using any strategy, allowing build to proceed');
+ console.log(
+ 'Could not determine changes using any strategy, allowing build to proceed',
+ );
return true;
}
console.log('Changed files detected:', changedFiles.trim());
// Check if any of the changed files are in the website directory
- const websiteChanges = changedFiles
- .split('\n')
- .filter((file) => {
- const trimmedFile = file.trim();
- // Handle git status format (files may have status prefixes like M, A, D)
- const cleanFile = trimmedFile.replace(/^[MADRCU?!\s]+/, '');
- return cleanFile && cleanFile.startsWith('apps/website/');
- });
+ const websiteChanges = changedFiles.split('\n').filter((file) => {
+ const trimmedFile = file.trim();
+ // Handle git status format (files may have status prefixes like M, A, D)
+ const cleanFile = trimmedFile.replace(/^[MADRCU?!\s]+/, '');
+ return cleanFile && cleanFile.startsWith('apps/website/');
+ });
console.log('Website changes:', websiteChanges);
return websiteChanges.length > 0;
diff --git a/apps/website/package.json b/apps/website/package.json
index 3eff554b..e7ef46cc 100644
--- a/apps/website/package.json
+++ b/apps/website/package.json
@@ -12,8 +12,7 @@
"serve": "docusaurus serve",
"write-translations": "docusaurus write-translations",
"write-heading-ids": "docusaurus write-heading-ids",
- "typecheck": "tsc",
- "docgen": "tsx ./scripts/docgen.ts"
+ "check-types": "tsc"
},
"dependencies": {
"@docusaurus/core": "3.8.1",
diff --git a/apps/website/src/css/custom.css b/apps/website/src/css/custom.css
index 32e76f00..9bf96042 100644
--- a/apps/website/src/css/custom.css
+++ b/apps/website/src/css/custom.css
@@ -1,7 +1,6 @@
/* some of these styles were borrowed from https://github.com/vendure-ecommerce/vendure/blob/cfc0dd7c34fd070a15455508f32d37e94589e656/docs/src/css/custom.css */
-@import url('https://fonts.googleapis.com/css2?family=Geist:wght@100..900&display=swap');
-@import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:ital,wght@0,100..800;1,100..800&display=swap');
+@import url('https://fonts.googleapis.com/css2?family=Geist+Mono:wght@100..900&family=Geist:wght@100..900&display=swap');
@tailwind base;
@tailwind components;
@@ -38,7 +37,7 @@
--ifm-heading-font-family: 'Geist', var(--ifm-font-family-base);
--ifm-heading-font-weight: 700;
--ifm-font-family-monospace:
- 'JetBrains Mono', 'SF Mono', Monaco, 'Inconsolata', 'Roboto Mono',
+ 'Geist Mono', 'SF Mono', Monaco, 'Inconsolata', 'Roboto Mono',
'Source Code Pro', Menlo, Consolas, 'DejaVu Sans Mono', monospace;
}
@@ -89,15 +88,15 @@ h6 {
/* Responsive typography scaling */
h1 {
- @apply text-4xl md:text-5xl lg:text-6xl;
+ @apply text-2xl md:text-3xl lg:text-4xl;
}
h2 {
- @apply text-3xl md:text-4xl lg:text-5xl;
+ @apply text-xl md:text-2xl lg:text-3xl;
}
h3 {
- @apply text-2xl md:text-3xl lg:text-4xl;
+ @apply text-lg md:text-xl lg:text-2xl;
}
.markdown h2:not(:first-of-type) {
diff --git a/apps/website/src/pages/index.tsx b/apps/website/src/pages/index.tsx
index fe230872..9e71183c 100644
--- a/apps/website/src/pages/index.tsx
+++ b/apps/website/src/pages/index.tsx
@@ -160,20 +160,22 @@ export default function Home(): React.JSX.Element {
{' '}
handle it for you!
-
+
The discord.js meta-framework for building powerful, modular, and
extensible Discord bots with ease.