# Aetheel — Discord-Claude Gateway An event-driven AI agent runtime that connects Discord to Claude Code CLI. Inspired by [OpenClaw](https://github.com/nichochar/open-claw)'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](https://discord.com/developers/applications)) with Message Content Intent enabled ## Quick Start ```bash git clone 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 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 `/claude` slash command - `/claude-reset` to 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`: ```markdown ## check-email Interval: 1800 Instruction: Check my inbox for anything urgent. ``` ### Cron Jobs (Scheduled Events) Define in `config/agents.md`: ```markdown ## 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`: ```markdown ## 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/`: ```json {"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) ```bash sudo bash scripts/setup.sh # Creates .env, config/, and systemd service ``` Or manually: ```bash 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 ```bash npm run build pm2 start dist/index.js --name aetheel pm2 save ``` ### Dev mode ```bash 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 ```bash 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