Discord bots are a classic first agent project and a classic way to leak a token to GitHub. This loadout sets up a clean discord.js v14 structure (one file per slash command, a loader, a deploy script), teaches the agent the three-second interaction rule and the intents model, and gives it a skill for adding new commands end to end.
What's in it
- One-file-per-command structure with a separate command deploy script
- The three-second rule: defer, then edit the reply
- Minimal intents; no privileged Message Content intent unless you really need it
- Skill that adds, registers and documents a new slash command
You'll need
- Tools:
node - Environment variables:
DISCORD_TOKEN,DISCORD_CLIENT_ID,DISCORD_GUILD_ID
Verification
| Check | Result | What we found |
|---|---|---|
| Listinglisting.yaml matches the schema and declares every file in the folder. | Pass | Schema valid; 5 files declared, none missing or extra
|
| Files parseJSON parses; SKILL.md and .mdc front matter is valid YAML with the fields each agent needs; code fences are closed. | Pass | All 5 files parse; front matter valid |
| AdaptersCLAUDE.md imports @AGENTS.md, GEMINI.md imports @./AGENTS.md, and both match the generator. | Pass | CLAUDE.md and GEMINI.md import AGENTS.md and are up to date
|
| Install pathsEvery file lands where each agent documents it, with no collisions, and the laid-out project is re-discovered correctly. | Pass | Correct paths for all 5 agents; layouts re-discovered
|
| MCP configmcp.json is structurally valid, converts to each agent's format, declares no literal secrets, and referenced packages exist on npm/PyPI. | Not applicable | No MCP servers in this loadout |
| Safety scanThe intake scanner finds no credentials, risky shell, hidden Unicode or prompt-injection wording, and the listing's network flag is honest. | Pass | Clean: no credentials, risky shell or hidden text
|
| ReferencesLinks and file paths between the loadout's files resolve, including for each agent's installed layout. | Pass | No cross-file references to check |
| Size limitsFile count and sizes are within the submission limits; SKILL.md bodies stay under 500 lines. | Pass | 5 files, 7.0 KB total; largest AGENTS.md 3.5 KB
|
| LicenceA recognised SPDX licence, with the licence text available. | Pass | MIT, by Agent Loadout; text in the repository LICENSE |
| Starter buildA minimal fixture project with the loadout applied installs and builds or type-checks, and the loadout's own code examples compile. | Pass | discord.js v14 bot in the loadout's layout type-checks (tsc strict) and the loadout's example command builds a valid slash command
|
What this means: a script checked the files themselves: they parse, they install where each agent looks, any MCP config is sound, the safety scan is clean, references between files resolve, and, where a free and repeatable fixture exists, a small starter project builds with the loadout applied. No AI agent was run. Per-agent results ("tested with agent") are separate and stay untested until an agent actually runs.
Install
Choose your agent. Run the commands from your project root, or download a zip already laid out in the right folders.
Claude Code reads CLAUDE.md (imports AGENTS.md with @AGENTS.md) and skills from .claude/skills/.
curl -fsSL --create-dirs -o AGENTS.md https://loadout.jnw-it.com/raw/discord-bot-ts/1.1.0/AGENTS.md curl -fsSL --create-dirs -o CLAUDE.md https://loadout.jnw-it.com/raw/discord-bot-ts/1.1.0/CLAUDE.md curl -fsSL --create-dirs -o .claude/skills/add-slash-command/SKILL.md https://loadout.jnw-it.com/raw/discord-bot-ts/1.1.0/skills/add-slash-command/SKILL.md
| File | Goes to |
|---|---|
AGENTS.md | AGENTS.md |
CLAUDE.md | CLAUDE.md |
skills/add-slash-command/SKILL.md | .claude/skills/add-slash-command/SKILL.md |
Already have an AGENTS.md or CLAUDE.md? The commands above overwrite it — download the file instead and merge the sections you want.
Codex CLI reads AGENTS.md and skills from .agents/skills/.
curl -fsSL --create-dirs -o AGENTS.md https://loadout.jnw-it.com/raw/discord-bot-ts/1.1.0/AGENTS.md curl -fsSL --create-dirs -o .agents/skills/add-slash-command/SKILL.md https://loadout.jnw-it.com/raw/discord-bot-ts/1.1.0/skills/add-slash-command/SKILL.md
| File | Goes to |
|---|---|
AGENTS.md | AGENTS.md |
skills/add-slash-command/SKILL.md | .agents/skills/add-slash-command/SKILL.md |
Already have an AGENTS.md or CLAUDE.md? The commands above overwrite it — download the file instead and merge the sections you want.
Cursor reads AGENTS.md + .cursor/rules/*.mdc and skills from .agents/skills/.
curl -fsSL --create-dirs -o AGENTS.md https://loadout.jnw-it.com/raw/discord-bot-ts/1.1.0/AGENTS.md curl -fsSL --create-dirs -o .cursor/rules/discord-commands.mdc https://loadout.jnw-it.com/raw/discord-bot-ts/1.1.0/.cursor/rules/discord-commands.mdc curl -fsSL --create-dirs -o .agents/skills/add-slash-command/SKILL.md https://loadout.jnw-it.com/raw/discord-bot-ts/1.1.0/skills/add-slash-command/SKILL.md
| File | Goes to |
|---|---|
AGENTS.md | AGENTS.md |
.cursor/rules/discord-commands.mdc | .cursor/rules/discord-commands.mdc |
skills/add-slash-command/SKILL.md | .agents/skills/add-slash-command/SKILL.md |
Already have an AGENTS.md or CLAUDE.md? The commands above overwrite it — download the file instead and merge the sections you want.
Gemini CLI reads GEMINI.md (imports AGENTS.md with @./AGENTS.md) and skills from .agents/skills/.
curl -fsSL --create-dirs -o AGENTS.md https://loadout.jnw-it.com/raw/discord-bot-ts/1.1.0/AGENTS.md curl -fsSL --create-dirs -o GEMINI.md https://loadout.jnw-it.com/raw/discord-bot-ts/1.1.0/GEMINI.md curl -fsSL --create-dirs -o .agents/skills/add-slash-command/SKILL.md https://loadout.jnw-it.com/raw/discord-bot-ts/1.1.0/skills/add-slash-command/SKILL.md
| File | Goes to |
|---|---|
AGENTS.md | AGENTS.md |
GEMINI.md | GEMINI.md |
skills/add-slash-command/SKILL.md | .agents/skills/add-slash-command/SKILL.md |
Already have an AGENTS.md or CLAUDE.md? The commands above overwrite it — download the file instead and merge the sections you want.
GitHub Copilot reads AGENTS.md and skills from .agents/skills/.
curl -fsSL --create-dirs -o AGENTS.md https://loadout.jnw-it.com/raw/discord-bot-ts/1.1.0/AGENTS.md curl -fsSL --create-dirs -o .agents/skills/add-slash-command/SKILL.md https://loadout.jnw-it.com/raw/discord-bot-ts/1.1.0/skills/add-slash-command/SKILL.md
| File | Goes to |
|---|---|
AGENTS.md | AGENTS.md |
skills/add-slash-command/SKILL.md | .agents/skills/add-slash-command/SKILL.md |
Already have an AGENTS.md or CLAUDE.md? The commands above overwrite it — download the file instead and merge the sections you want.
Files
Rules AGENTS.md Discord bot project rules. 63 lines
# AGENTS.md: Discord bot (TypeScript, discord.js v14)
## Layout
```
src/
index.ts creates the Client, loads commands and events, logs in
deploy-commands.ts registers slash commands with Discord (run by hand, not on every start)
commands/<name>.ts one slash command per file: exports `data` (SlashCommandBuilder) and `execute(interaction)`
events/<name>.ts one gateway event per file: exports `name`, `once?`, `execute(...)`
lib/ shared helpers (embeds, db, permissions)
```
Run with `npm run dev` (tsx watch) locally. `npm run deploy` runs `deploy-commands.ts`.
## Command file shape
Each file in `src/commands/` exports `data` and `execute`, nothing else:
```ts
import { SlashCommandBuilder, type ChatInputCommandInteraction } from "discord.js";
export const data = new SlashCommandBuilder()
.setName("roll") // lowercase, 1–32 chars, no spaces
.setDescription("Roll a dice") // required, 1–100 chars
.addIntegerOption((o) => o.setName("sides").setDescription("Number of sides").setMinValue(2).setMaxValue(1000));
export async function execute(interaction: ChatInputCommandInteraction) {
const sides = interaction.options.getInteger("sides") ?? 6;
const result = 1 + Math.floor(Math.random() * sides);
await interaction.reply({ content: `🎲 ${result} (d${sides})` });
}
```
## Token and secrets
- `DISCORD_TOKEN`, `DISCORD_CLIENT_ID`, `DISCORD_GUILD_ID` come from `.env` (git-ignored). `.env.example` lists them with dummy values.
- Never print the token, commit it, or paste it into an embed or error message. If it leaks, the human must reset it in the
Developer Portal → Bot → Reset Token. Discord also auto-revokes tokens it finds in public repos.
## Intents
- Request the minimum: `GatewayIntentBits.Guilds` covers slash commands.
- `GuildMembers`, `GuildPresences` and `MessageContent` are **privileged**: they must be switched on in the Developer
Portal and are reviewed once a bot is in 100+ servers. Prefer slash commands over reading message content.
## Interactions
- Discord gives you **3 seconds** to respond. If work might take longer (API calls, database, AI), call
`await interaction.deferReply()` first, then `await interaction.editReply(...)`.
- Private replies: `interaction.reply({ content, flags: MessageFlags.Ephemeral })`. (`ephemeral: true` is deprecated.)
- Every `execute` is wrapped by the loader in try/catch. On error: log it, then reply or follow up with a short
ephemeral "Something went wrong" — never the stack trace.
- Check permissions for moderation commands with `.setDefaultMemberPermissions(...)` on the builder *and* re-check in code.
- Validate options: string lengths, number ranges (`.setMinValue/.setMaxValue`), and never pass user text into shell commands or eval.
## Registering commands
- During development register to one guild (`Routes.applicationGuildCommands(clientId, guildId)`) — updates are instant.
- Global registration (`Routes.applicationCommands(clientId)`) is for release. Do not register on every bot start; there are rate limits.
- After changing a command's `data`, remind the human to run `npm run deploy`.
## Style
- TypeScript strict, ES modules, `async/await`. No `any` on interaction types: use `ChatInputCommandInteraction`.
- Keep command files small; move logic into `lib/` when two commands share it.
- Rate-limit costly commands per user (a simple in-memory cooldown map is fine to start).
## Before you say "done"
`npm run build` (tsc) passes, the new command is in `commands/`, and you have told the human whether to re-run deploy. Adapter CLAUDE.md Imports AGENTS.md for Claude Code. 10 lines
# CLAUDE.md
@AGENTS.md
## Claude Code notes
- Skills from this loadout live in `.claude/skills/`: `add-slash-command`. Use them when their description matches the task.
- Follow AGENTS.md above; where this file and AGENTS.md disagree, AGENTS.md wins.
<!-- From the Agent Loadout "Discord bot (TypeScript)" v1.1.0 — https://loadout.jnw-it.com/loadouts/discord-bot-ts/ --> Adapter GEMINI.md Imports AGENTS.md for Gemini CLI. 10 lines
# GEMINI.md
@./AGENTS.md
## Gemini CLI notes
- Skills from this loadout live in `.agents/skills/`: `add-slash-command`. Use them when their description matches the task.
- Follow AGENTS.md above; where this file and AGENTS.md disagree, AGENTS.md wins.
<!-- From the Agent Loadout "Discord bot (TypeScript)" v1.1.0 — https://loadout.jnw-it.com/loadouts/discord-bot-ts/ --> Cursor rule .cursor/rules/discord-commands.mdc Rule for files in src/commands/. 28 lines
---
description: Shape of a discord.js v14 slash command file
globs: src/commands/**/*.ts
alwaysApply: false
---
Each file exports `data` and `execute`, nothing else:
```ts
import { SlashCommandBuilder, MessageFlags, type ChatInputCommandInteraction } from "discord.js";
export const data = new SlashCommandBuilder()
.setName("roll") // lowercase, 1–32 chars, no spaces
.setDescription("Roll a dice") // required, 1–100 chars
.addIntegerOption((o) => o.setName("sides").setDescription("Number of sides").setMinValue(2).setMaxValue(1000));
export async function execute(interaction: ChatInputCommandInteraction) {
const sides = interaction.options.getInteger("sides") ?? 6;
const result = 1 + Math.floor(Math.random() * sides);
await interaction.reply({ content: `🎲 ${result} (d${sides})` });
}
```
- If `execute` awaits anything slow, `deferReply()` first and `editReply()` after.
- Use `flags: MessageFlags.Ephemeral` for private replies, not `ephemeral: true`.
- Required options must be added before optional ones.
- Do not register commands from here; that is `src/deploy-commands.ts`. Skill skills/add-slash-command/SKILL.md Adds and registers a new slash command. 24 lines
---
name: add-slash-command
description: Add a new slash command to a discord.js v14 bot, register it and document it. Use when the user asks for a new bot command, e.g. "add a /poll command".
---
# Add a slash command
1. **Clarify in one line** if needed: command name, options (name, type, required?), who can use it, public or private reply.
Otherwise choose sensible defaults and state them.
2. **Check the name is free:** look in `src/commands/` and in the `data.setName(...)` calls.
3. **Create `src/commands/<name>.ts`** following the command file shape in AGENTS.md:
- `SlashCommandBuilder` with name, description, typed options with limits.
- Moderation or admin commands: `.setDefaultMemberPermissions(PermissionFlagsBits.<Perm>)` and `.setContexts(InteractionContextType.Guild)`
so they cannot run in DMs.
- `execute`: validate, `deferReply()` if anything is slow, do the work, reply.
- Handle the expected failures (missing permission, bad input, external API down) with a friendly ephemeral message.
4. **Check the loader** in `src/index.ts` picks up new files automatically. If commands are listed by hand, add it there and in
`deploy-commands.ts`.
5. **Type-check:** `npm run build` or `npx tsc --noEmit`.
6. **Register:** tell the human to run `npm run deploy` (guild registration is instant). Only run it yourself if they ask and
`.env` is already set up — it uses the bot token.
7. **Document:** add the command to the commands table in `README.md` (name, options, who can use it).
8. **Report:** the file created, the command signature (`/poll question:<text> duration:<minutes>`), and how to try it. All files: source zip · Licence MIT · By Agent Loadout · First listed
Installing and testing
How do I install Discord bot (TypeScript) in Claude Code?
From your project root, put AGENTS.md, CLAUDE.md, skills/add-slash-command/SKILL.md at .claude/skills/add-slash-command/SKILL.md. The "Claude Code" tab on this page has the curl commands, or download https://loadout.jnw-it.com/download/discord-bot-ts/claude-code.zip, which is already laid out. Do not overwrite an existing AGENTS.md or CLAUDE.md; merge it.
How do I install Discord bot (TypeScript) in Codex CLI?
From your project root, put AGENTS.md, skills/add-slash-command/SKILL.md at .agents/skills/add-slash-command/SKILL.md. The "Codex CLI" tab on this page has the curl commands, or download https://loadout.jnw-it.com/download/discord-bot-ts/codex.zip, which is already laid out. Do not overwrite an existing AGENTS.md or CLAUDE.md; merge it.
How do I install Discord bot (TypeScript) in Cursor?
From your project root, put AGENTS.md, .cursor/rules/discord-commands.mdc, skills/add-slash-command/SKILL.md at .agents/skills/add-slash-command/SKILL.md. The "Cursor" tab on this page has the curl commands, or download https://loadout.jnw-it.com/download/discord-bot-ts/cursor.zip, which is already laid out. Do not overwrite an existing AGENTS.md or CLAUDE.md; merge it.
How do I install Discord bot (TypeScript) in Gemini CLI?
From your project root, put AGENTS.md, GEMINI.md, skills/add-slash-command/SKILL.md at .agents/skills/add-slash-command/SKILL.md. The "Gemini CLI" tab on this page has the curl commands, or download https://loadout.jnw-it.com/download/discord-bot-ts/gemini-cli.zip, which is already laid out. Do not overwrite an existing AGENTS.md or CLAUDE.md; merge it.
How do I install Discord bot (TypeScript) in GitHub Copilot?
From your project root, put AGENTS.md, skills/add-slash-command/SKILL.md at .agents/skills/add-slash-command/SKILL.md. The "GitHub Copilot" tab on this page has the curl commands, or download https://loadout.jnw-it.com/download/discord-bot-ts/copilot.zip, which is already laid out. Do not overwrite an existing AGENTS.md or CLAUDE.md; merge it.
Has Discord bot (TypeScript) been tested?
It passed the Level 1 automated install checks on 2026-10-16: the files parse, install to each agent's documented paths, and pass the safety scan. No AI agent has run it yet, so no agent pass or fail is claimed.
More for Claude Code, Codex, Cursor, Gemini CLI and GitHub Copilot.