Aetheel — Discord-Claude Gateway
An event-driven AI agent runtime that connects Discord to Claude Code CLI. Inspired by OpenClaw's architecture — a gateway in front of an agent runtime with markdown-based personality, memory, scheduled behaviors, and proactive messaging.
How It Works
Discord Users ──► Discord Bot ──► Event Queue ──► Agent Runtime ──► Claude Code CLI
▲ │
Heartbeats ──────────┤ │
Cron Jobs ───────────┤ ┌─────────────────────┘
Hooks ───────────────┘ ▼
IPC Watcher ───────────── Markdown Config Files
(CLAUDE.md, memory.md, etc.)
All inputs — Discord messages, heartbeat timers, cron jobs, lifecycle hooks — enter a unified event queue. The agent runtime reads your markdown config files fresh on each event, assembles a dynamic system prompt, and calls the Claude Code CLI. The agent can write back to memory.md to persist facts across sessions, and send proactive messages via the IPC system.
Uses your existing Claude Code subscription — no API key needed.
Prerequisites
- Node.js 18+
- Claude Code CLI installed and signed in (
npm install -g @anthropic-ai/claude-code && claude) - A Discord bot token (create one here) with Message Content Intent enabled
Quick Start
git clone <your-repo-url>
cd aetheel-2
npm install
cp .env.example .env # Edit with your Discord bot token
mkdir -p config
# Create config/CLAUDE.md with your persona (see Setup section)
npm run dev
Or run the interactive setup:
bash scripts/setup.sh
Configuration
Environment Variables
Create a .env file in the project root (auto-loaded via dotenv):
| Variable | Required | Default | Description |
|---|---|---|---|
DISCORD_BOT_TOKEN |
Yes | — | Discord bot token |
OUTPUT_CHANNEL_ID |
No | — | Discord channel for heartbeat/cron/hook output |
CLAUDE_CLI_PATH |
No | claude |
Path to the Claude Code CLI binary |
CONFIG_DIR |
No | ./config |
Path to markdown config directory |
ALLOWED_TOOLS |
No | Read,Write,Edit,Glob,Grep,WebSearch,WebFetch |
Comma-separated tools the agent can use |
PERMISSION_MODE |
No | bypassPermissions |
Claude Code permission mode |
QUERY_TIMEOUT_MS |
No | 120000 |
Max time per query (ms) |
MAX_CONCURRENT_QUERIES |
No | 5 |
Max simultaneous queries |
MAX_QUEUE_DEPTH |
No | 100 |
Max events in the queue |
IDLE_SESSION_TIMEOUT_MS |
No | 1800000 |
Session idle timeout (30 min) |
LOG_LEVEL |
No | info |
Log level: debug, info, warn, error |
Markdown Config Files
Place these in CONFIG_DIR (default: ./config/):
| File | Purpose | Required |
|---|---|---|
CLAUDE.md |
Persona: identity, personality, user context, tools | Yes |
agents.md |
Operating rules, cron jobs, hooks (parsed at startup) | No |
memory.md |
Long-term memory (agent-writable) | No (auto-created) |
heartbeat.md |
Proactive check definitions (parsed at startup) | No |
The gateway reads CLAUDE.md and memory.md fresh on every event — edit them anytime. agents.md and heartbeat.md are parsed at startup for timers, so restart after editing those.
Skills
Drop skill files into config/skills/{name}/SKILL.md and they're automatically loaded into the system prompt:
config/skills/
├── web-research/
│ └── SKILL.md → "When asked to research, use WebSearch..."
└── code-review/
└── SKILL.md → "When reviewing code, focus on..."
Features
Discord Integration
- Mention the bot (
@Aetheel hi) or use/claudeslash command /claude-resetto start a fresh conversation- Responses auto-split at 2000 chars with code block preservation
- Typing indicators while processing
Session Management
- Per-channel conversation sessions with Claude Code CLI
--resume - Sessions persist to
config/sessions.json(survive restarts) - Auto-cleanup of idle sessions after 30 minutes (configurable)
Heartbeats (Timer Events)
Define in config/heartbeat.md:
## check-email
Interval: 1800
Instruction: Check my inbox for anything urgent.
Cron Jobs (Scheduled Events)
Define in config/agents.md:
## Cron Jobs
### morning-briefing
Cron: 0 8 * * *
Instruction: Good morning! Search for the latest AI news and post a summary.
Lifecycle Hooks
Define in config/agents.md:
## Hooks
### startup
Instruction: Say hello, you just came online.
### shutdown
Instruction: Save important context to memory.md.
Proactive Messaging (IPC)
The agent can send messages to any Discord channel by writing JSON files to config/ipc/outbound/:
{"channelId": "123456789", "text": "Hey, found something interesting!"}
The gateway polls every 2 seconds and delivers the message.
Message History
All inbound/outbound messages stored per channel in config/messages/{channelId}.json. Max 100 messages per channel, auto-trimmed.
Conversation Archiving
Every exchange saved as readable markdown in config/conversations/{channelId}/{YYYY-MM-DD}.md.
Retry with Backoff
Claude CLI calls retry 3 times with exponential backoff (5s, 10s, 20s) on transient errors. Session corruption errors fail immediately.
Structured Logging
Pino-based structured JSON logging. Set LOG_LEVEL=debug for verbose output. Pretty-printed in dev, JSON in production (NODE_ENV=production).
Deployment
systemd (recommended for Linux servers)
sudo bash scripts/setup.sh # Creates .env, config/, and systemd service
Or manually:
sudo cp scripts/aetheel.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable aetheel
sudo systemctl start aetheel
# View logs
sudo journalctl -u aetheel -f
PM2
npm run build
pm2 start dist/index.js --name aetheel
pm2 save
Dev mode
npm run dev
Project Structure
src/
├── index.ts # Entry point (loads .env)
├── gateway-core.ts # Main orchestrator
├── config.ts # Environment variable loader
├── logger.ts # Pino structured logger
├── discord-bot.ts # Discord.js wrapper
├── event-queue.ts # Unified FIFO event queue
├── agent-runtime.ts # Core engine: reads configs, spawns CLI, streams output
├── markdown-config-loader.ts # Reads CLAUDE.md, agents.md, memory.md
├── system-prompt-assembler.ts # Assembles system prompt with sections
├── skills-loader.ts # Loads skills from config/skills/*/SKILL.md
├── session-manager.ts # Channel → session ID (persisted, idle cleanup)
├── message-history.ts # Per-channel message storage
├── conversation-archiver.ts # Markdown conversation logs
├── ipc-watcher.ts # Polls ipc/outbound/ for proactive messages
├── response-formatter.ts # Splits long text for Discord's 2000 char limit
├── error-formatter.ts # Sanitizes errors (strips keys, paths, stacks)
├── heartbeat-scheduler.ts # Recurring timer events
├── cron-scheduler.ts # node-cron scheduled events
├── hook-manager.ts # Lifecycle hooks
├── bootstrap-manager.ts # First-run file validation/creation
├── channel-queue.ts # Per-channel sequential processing
└── shutdown-handler.ts # Graceful SIGTERM/SIGINT handling
config/ # Agent workspace (gitignored)
├── CLAUDE.md # Persona
├── agents.md # Rules, cron, hooks
├── memory.md # Long-term memory (agent-writable)
├── heartbeat.md # Heartbeat checks
├── sessions.json # Session persistence (auto)
├── messages/ # Message history (auto)
├── conversations/ # Conversation archives (auto)
├── ipc/outbound/ # Proactive message queue (auto)
├── skills/ # Skill definitions
└── news/ # Example: agent-created content
Development
npm test # Run tests (85 passing)
npm run dev # Dev mode with tsx
npm run build # Compile TypeScript
npm start # Run compiled JS
License
MIT