-
Notifications
You must be signed in to change notification settings - Fork 0
Architecture
claude-tmux-discord bridges Discord and Claude Code using tmux as the process manager. The key insight: Claude sends replies directly to Discord — the bot never scrapes tmux output.
┌─────────────────────────────────────────────────────────┐
│ Discord Server │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ #claude-room1│ │ #claude-room2│ │ #claude-room3│ │
│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │
└─────────┼─────────────────┼─────────────────┼───────────┘
│ │ │
▼ ▼ ▼
┌─────────────────────────────────────────────────────────┐
│ Bot Process (Node.js) │
│ ┌─────────────┐ ┌──────────┐ ┌────────────────────┐ │
│ │ MessageBuffer│ │ Auth/ACL │ │ SessionManager │ │
│ │ (per channel)│ │ │ │ (tmux lifecycle) │ │
│ └──────┬──────┘ └──────────┘ └─────────┬──────────┘ │
└─────────┼─────────────────────────────────┼─────────────┘
│ /enter flushes buffer │
▼ ▼
┌─────────────────────────────────────────────────────────┐
│ tmux Sessions │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ claude-room1 │ │ claude-room2 │ │ claude-room3 │ │
│ │ Claude Code │ │ Claude Code │ │ Claude Code │ │
│ │ ↓ │ │ ↓ │ │ ↓ │ │
│ │ discord-send │ │ discord-send │ │ discord-send │ │
│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │
└─────────┼─────────────────┼─────────────────┼───────────┘
│ │ │
▼ ▼ ▼
Discord REST API (messages appear in channels)
The bot does not read Claude's output from tmux. Instead, Claude's system prompt instructs it to use the discord-send CLI to send messages. The only capturePane() call is in waitForClaudeReady() — polling for the ❯ prompt to confirm Claude has booted.
Each channel has a SessionState.busy promise chain. All prompts and interactive choices are serialized via .then() callbacks. This prevents concurrent tmux input to the same session, which would corrupt the prompt.
Messages accumulate in a MessageBuffer until /enter flushes them. This enables multi-message composition — users can send text across multiple messages, attach files, and then submit everything as one prompt.
ensure() creates tmux sessions on demand. If the bot restarts, in-memory state is lost but sessions re-create lazily on the next /enter. Registered channels are repopulated from the database on startup.
exportEnvToSession() uses both:
-
tmux set-environment— for future child processes spawned by tmux - Shell
exportkeystrokes — for the current shell session
Both are needed because Claude's working directory may differ from the project root, so discord-send can't find the .env file.
index.ts
├── config.ts (Zod env validation)
├── logger.ts (Pino)
├── db.ts (Prisma singleton)
├── acl.ts (in-memory cache + DB write-through)
├── commands.ts
│ ├── db.ts
│ ├── acl.ts
│ ├── owner.ts (bot owner detection)
│ ├── session.ts (SessionManager)
│ ├── buffer.ts (MessageBuffer)
│ └── config.ts
└── bot.ts
├── auth.ts (authorization check)
├── session.ts
│ ├── tmux.ts (shell wrapper via execa)
│ ├── db.ts
│ └── config.ts
├── buffer.ts
│ └── attachments.ts
├── commands.ts
├── acl.ts
└── config.ts
-
Discord message received →
Events.MessageCreateinbot.ts -
Authorization →
authorize()checks: not a bot, not DM, registered channel OR ACL match -
Attachment download → files saved to
<workspace>/.uploads/<timestamp36>-<name> -
Buffer append →
buffer.append(channelId, { text, attachments, authorTag }) - Acknowledge → bot reacts with emoji
-
/entercommand →buffer.flush()→sessionMgr.sendPrompt() -
Session ensure → create tmux session if needed, inject env, start Claude, wait for
❯ -
Send text →
tmux send-keys -l <text>+tmux send-keys Enter -
Claude processes → runs
discord-send "response"as a shell command -
Response appears →
discord-sendhits Discord REST API directly
workspace/
└── <channelId>/ # Per-channel workspace
├── .claude-tmux-discord/
│ └── started # Resume marker file
└── .uploads/
├── k3x7f-photo.png # Downloaded attachments
└── k3x8a-document.pdf
claude-tmux-discord — Discord bot for per-channel Claude Code sessions via tmux
Setup
Usage
Technical
Help