Version: 0.3.0
Control Claude Code, Gemini CLI, and Tmux terminals through Discord with interactive approvals, rich embeds, and action tracking.
- Remote CLI Control: Interact with Claude Code and Gemini CLI from Discord
- Interactive Approvals: Approve or deny tool usage requests through Discord buttons
- Terminal Watching: Monitor and interact with tmux sessions in real-time
- Multi-Runner Support: Manage multiple machines from a single Discord server
- Agent Skills: Enable CLI agents to control Discord (send messages, rename channels)
- Private Sessions: Each CLI session gets its own private thread
- Secure Authentication: Token-based runner registration
For an easy way to host the Discord bot and Runner Agent, we recommend using TaskServer.
TaskServer provides a simple web dashboard to manage long-running scripts (like the bot and runner), view logs, and handle auto-restarts. It also integrates with Cloudflare Tunnel for secure remote access without port forwarding.
┌─────────────────────────────────────────────────────────────┐
│ Discord Bot (Central Server) │
│ - Slash commands (/generate-token, /create-session) │
│ - WebSocket server for runners │
│ - Discord buttons for approvals │
│ - YAML-based storage │
└────────────────────┬────────────────────────────────────────┘
│ WebSocket
▼
┌─────────────────────────────────────────────────────────────┐
│ Runner Agent (on user machine) │
│ - WebSocket client to Discord bot │
│ - HTTP server for CLI plugins │
│ - Manages CLI processes │
└────────────────────┬────────────────────────────────────────┘
│ HTTP
▼
┌─────────────────────────────────────────────────────────────┐
│ CLI Plugin (Claude Code or Gemini CLI) │
│ - Hooks to intercept tool usage │
│ - Sends approval requests to Runner Agent │
│ - Respects allow/deny decisions │
└─────────────────────────────────────────────────────────────┘
DisCode/
├── discord-bot/ # Discord bot source
├── runner-agent/ # Runner Agent source
├── shared/ # Shared types and utilities
├── plugins/ # CLI plugin installation scripts
└── hooks/ # Plugin hooks
- Create a Discord Application at https://discord.com/developers/applications
- Enable bot with required scopes:
botapplications.commands
- Get your bot token and client ID
- Invite bot to your server with these scopes:
botapplications.commands
Discord Bot:
cd discord-bot
cp .env.example .envEdit .env:
DISCODE_DISCORD_TOKEN=your_bot_token_here
DISCODE_DISCORD_CLIENT_ID=your_client_id_here
DISCODE_WS_PORT=8080
DISCODE_STORAGE_PATH=./dataRunner Agent:
cd runner-agent
cp .env.example .envEdit .env:
DISCODE_TOKEN=token_from_discord_bot
DISCODE_BOT_URL=ws://localhost:8080
DISCODE_RUNNER_NAME=my-machine
DISCODE_CLI_TYPE=claude # or 'gemini'
DISCODE_HTTP_PORT=3000cd discord-bot
bun install
bun run src/index.tsExpected output:
✅ Discord bot logged in as DisCodeBot#1234
✅ Successfully reloaded application (/) commands.
✅ WebSocket server listening on port 8080
In your Discord server:
/generate-token
Copy the token and add it to your Runner Agent's .env file.
Install Claude Code Plugin:
bun run install-plugins.shOr manually install plugins from the plugins/ directory.
Install Gemini CLI Plugin:
cp hooks/gemini/before-tool-handler.sh ~/.gemini/discode/
cp hooks/gemini/session-handler.sh ~/.gemini/discode/Then add to ~/.gemini/settings.json:
{
"hooks": {
"BeforeTool": "~/.gemini/discode/before-tool-handler.sh",
"OnSessionStart": "~/.gemini/discode/session-handler.sh"
}
}cd runner-agent
bun install
bun run src/index.tsExpected output:
╔════════════════════════════════════════════════════════════╗
║ DisCode Runner Agent v0.1.0 ║
╠════════════════════════════════════════════════════════════╣
║ Runner ID: runner_my-machine_1234567890 ║
║ Runner Name: my-machine ║
║ CLI Type: claude ║
║ HTTP Server: http://localhost:3000 ║
║ Bot WebSocket: ws://localhost:8080 ║
╚════════════════════════════════════════════════════════════╝
✅ Connected to Discord bot
Available commands:
/list-runners # List all connected runners
/create-session # Start a new CLI session
/share-runner @user <id> # Share a runner with another user
/terminals # List active tmux terminals
/watch <session_id> # Watch a specific terminal
/unwatch <session_id> # Stop watching a terminal
/interrupt <session_id> # Send Ctrl+C to a terminal
-
Create Session:
/create-session→ Creates a private thread
-
Use Claude Code:
claude --print→ Send prompt: "Read package.json"
-
Approval Request: Discord shows:
🔔 Tool Use Approval Required Runner: my-machine Tool: Read Input: { "file_path": "package.json" } [Allow] [Deny] -
Click Button: → Runner Agent receives decision → Claude Code continues or stops
The Terminal Watch feature (/terminals and /watch) requires tmux to be running on your machine.
# macOS
brew install tmux
# Ubuntu/Debian
sudo apt-get install tmuxtmux new -s my-work-sessionThis session will appear in DisCode via /terminals.
Add to your ~/.zshrc or ~/.bashrc:
# Start tmux if it's not running, or attach to the last session
if command -v tmux &> /dev/null && [ -n "$PS1" ] && [[ ! "$TERM" =~ screen ]] && [[ ! "$TERM" =~ tmux ]] && [ -z "$TMUX" ]; then
tmux attach -t default || tmux new -s default
fi- Slash commands for all operations
- WebSocket server for runner connections
- Discord button components for approvals
- Private thread management per session
- Token-based authentication
- YAML-based storage
- WebSocket client to Discord bot
- HTTP server for CLI plugin communication
- Auto-reconnect on disconnect
- Heartbeat monitoring (30s interval)
- Graceful shutdown
- Tmux integration for terminal monitoring
- Automatic session discovery
-
Claude Code hooks (PreToolUse, SessionStart, SessionEnd)
-
Gemini CLI hooks (BeforeTool, SessionStart, SessionEnd)
-
HTTP communication with Runner Agent
-
Safe defaults (deny if unreachable)
-
Safe defaults (deny if unreachable)
DisCode automatically injects skills into your CLI sessions to enable advanced features:
- Discord Integration: ALlows the agent to send messages to the thread and update the channel name/topic.
- Actions:
send_message,update_channel - Installation: Automatic when a session starts (injected into
.claude/skillsor.gemini/skills)
- Actions:
| Variable | Required | Default | Description |
|---|---|---|---|
DISCODE_DISCORD_TOKEN |
✅ | - | Discord bot token |
DISCODE_DISCORD_CLIENT_ID |
✅ | - | Discord application client ID |
DISCODE_WS_PORT |
❌ | 8080 | WebSocket server port |
DISCODE_STORAGE_PATH |
❌ | ./data | YAML storage directory |
| Variable | Required | Default | Description |
|---|---|---|---|
DISCODE_TOKEN |
✅ | - | Token from Discord bot |
DISCODE_BOT_URL |
❌ | ws://localhost:8080 | Discord bot WebSocket URL |
DISCODE_RUNNER_NAME |
❌ | local-runner | Human-readable runner name |
DISCODE_CLI_TYPE |
❌ | claude | CLI type (claude/gemini) |
DISCODE_HTTP_PORT |
❌ | 3000 | HTTP server port for plugin |
DISCODE_TMUX_POLLING |
❌ | true | Enable polling for new tmux sessions |
YAML files are stored in ./data (or DISCODE_STORAGE_PATH):
users.yaml- User tokens and runner associationsrunners.yaml- Runner registration datasessions.yaml- Active session data
Bot not responding:
- Check token is correct
- Verify bot has
botandapplications.commandsscopes - Check console for errors
Commands not showing:
- Run:
bun run src/register-commands.ts - Wait 1-5 minutes for Discord to update
- Try restarting Discord
Can't connect to bot:
- Verify bot WebSocket server is running
- Check
DISCODE_BOT_URLis correct - Check token is valid
Approvals timing out:
- Check Runner Agent is still connected
- Verify Discord bot WebSocket is running
- Check network connectivity
Approvals not triggering:
- Verify Runner Agent is running on port 3000
- Check plugin is installed correctly
- Test with:
curl http://localhost:3000/
Claude Code ignores decisions:
- Check hooks.json is correct
- Verify plugin is in right directory:
~/.claude/plugins/discode/ - Check approval-handler.js is executable
Contributions are welcome! Please feel free to submit issues or pull requests.
MIT License - See LICENSE file for details
For issues or questions:
- Check the troubleshooting section above
- Examine logs in both Discord bot and Runner Agent
- Review configuration files
Version: 0.3.0 Status: ✅ Stable