GitHub - siisee11/discode: messenger bridge for coding agents

GitHub

10 min read Original article ↗

Discode

English

Bridge AI agent CLIs to Discord for remote monitoring and control.

Derived from DoBuDevel/discord-agent-bridge. This project preserves original authorship and builds on top of the upstream work.

TypeScript Bun License: MIT Tests

Overview

Discoding - run AI coding CLIs locally and relay them to Discord.

I built this after experimenting with OpenClaw. Even with full system permissions, I realized I preferred conversational control over full autonomy.

Instead of building another dashboard, I wired my AI CLI to Discord.

Discode runs your AI agent in tmux and simply relays output to Discord - no wrappers, no hidden execution layers, no cloud dependency.

  • Local-first
  • Relay-only architecture
  • Persistent tmux sessions
  • Single daemon managing multiple projects

Discode Demo

Features

  • Multi-Agent Support: Works with Claude Code, Gemini CLI, and OpenCode
  • Auto-Discovery: Automatically detects installed AI agents on your system
  • Real-Time Streaming: Sends agent outputs to Discord/Slack through event hooks
  • Project Isolation: Each project gets a dedicated Discord channel
  • Single Daemon: One Discord bot connection manages all projects
  • Session Management: Persistent tmux sessions survive disconnections
  • Rich CLI: Intuitive commands for setup, control, and monitoring
  • Type-Safe: Written in TypeScript with dependency injection pattern
  • Well-Tested: 129 unit tests with Vitest

Supported Platforms

Platform Supported Notes
macOS Yes Developed and tested
Linux Expected Should work (tmux-based), not yet tested
Windows (WSL) Expected Should work with tmux installed in WSL, not yet tested
Windows (native) No tmux is not available natively

Prerequisites

  • Bun: Version 1.3 or higher
  • tmux: Version 3.0 or higher
    • Basic tmux proficiency (session/window/pane navigation, attach/detach) is recommended
  • Discord Bot: Create a bot following the Discord Bot Setup Guide
    • Required permissions: Send Messages, Manage Channels, Read Message History, Embed Links, Add Reactions
    • Required intents: Guilds, GuildMessages, MessageContent, GuildMessageReactions
  • Slack (optional): Use Slack instead of Discord by following the Slack Setup Guide
  • AI Agent: At least one of:

Installation

Global install (npm or Bun)

npm install -g @siisee11/discode
bun add -g @siisee11/discode

Binary install (no Bun/Node runtime required)

curl -fsSL https://discode.chat/install | bash

Fallback:

curl -fsSL https://raw.githubusercontent.com/siisee11/discode/main/install | bash

From source

git clone https://github.com/siisee11/discode.git
cd discode
bun install
bun run build

For local runtime switching and development workflows, see DEVELOPMENT.md.

Uninstall

Full cleanup (remove config/state/logs and installed bridge plugins too):

discode uninstall --purge --yes

Quick Start

1. Setup Discord Bot

# One-time onboarding
discode onboard

The onboard command prompts for your bot token, auto-detects the Discord server ID, lets you choose a default AI CLI, asks whether to enable OpenCode allow permission mode, and asks for telemetry opt-in. You can verify or change settings later:

discode config --show              # View current configuration
discode config --server SERVER_ID  # Change server ID manually

Note: onboard is required for initial configuration — it auto-detects the server ID by connecting to Discord. The config command only updates individual values without auto-detection.

2. Start Working

cd ~/projects/my-app

# Just run new — that's it!
discode new

new handles everything automatically: detects installed agents, starts the daemon, creates a Discord channel, launches the agent in tmux, and attaches you to the session.

discode new claude        # Specify an agent explicitly

Your AI agent is now running in tmux, with output delivered to Discord/Slack in real time through hooks.

CLI Reference

Global Commands

onboard

One-time onboarding: prompts for bot token, connects to Discord to auto-detect your server, lets you choose your default AI CLI, configures OpenCode permission mode, and asks telemetry opt-in.

discode onboard
# Optional for non-interactive shells
discode onboard --token YOUR_BOT_TOKEN

The onboarding flow will:

  1. Save your bot token to ~/.discode/config.json
  2. Connect to Discord and detect which server(s) your bot is in
  3. If the bot is in multiple servers, prompt you to select one
  4. Let you choose a default AI CLI for discode new
  5. Ask whether to set OpenCode permission mode to allow
  6. Warn that non-allow mode may cause inconvenient approval prompts in Discord
  7. Ask whether to enable anonymous CLI telemetry (opt-in)

daemon <action>

Control the global daemon process.

discode daemon start    # Start daemon
discode daemon stop     # Stop daemon
discode daemon status   # Check daemon status

list

List all registered projects.

agents

List available AI agents detected on your system.

tui

Open interactive terminal UI. Use /new inside the TUI to create a new agent session.

config [options]

View or update global configuration.

discode config --show              # Show current configuration
discode config --token NEW_TOKEN   # Update bot token
discode config --server SERVER_ID  # Set Discord server ID manually
discode config --port 18470        # Set hook server port
discode config --telemetry on      # Enable anonymous CLI telemetry (opt-in)
discode config --telemetry-endpoint https://telemetry.discode.chat/v1/events

Project Commands

Run these commands from your project directory.

start [options]

Start the bridge server for registered projects.

discode start                        # Start all projects
discode start -p my-app             # Start a specific project
discode start -p my-app --attach    # Start and attach to tmux

stop [project]

Stop a project: kills tmux session, deletes Discord channel, and removes project state. Defaults to current directory name if project is not specified.

discode stop                # Stop current directory's project
discode stop my-app         # Stop a specific project
discode stop --keep-channel # Keep Discord channel (only kill tmux)

status

Show project status.

attach [project]

Attach to a project's tmux session. Defaults to current directory name if project is not specified.

discode attach              # Attach to current directory's project
discode attach my-app       # Attach to a specific project

Press Ctrl-b d to detach from tmux without stopping the agent.

new [agent] [options]

Quick start: start daemon, set up project if needed, and attach to tmux. Auto-detects installed agents and creates the Discord channel automatically.

discode new              # Auto-detect agent, setup & attach
discode new claude       # Use a specific agent
discode new --no-attach  # Start without attaching to tmux

Supported Agents

Agent Binary Auto-Detect Notes
Claude Code claude Yes Official Anthropic CLI
Gemini CLI gemini Yes Google Gemini CLI
OpenCode opencode Yes Open-source alternative

Note: Codex support is temporarily removed and will be restored once Codex provides hook support. Tracking discussion: openai/codex#2150

Agent Detection

The CLI automatically detects installed agents using command -v <binary>. Run discode agents to see available agents on your system.

Configuration

Global Config

Stored in ~/.discode/config.json:

{
  "token": "YOUR_BOT_TOKEN",
  "serverId": "YOUR_SERVER_ID",
  "hookServerPort": 18470,
  "telemetryEnabled": false
}
Key Required Description Default
token Yes Discord bot token. Set via discode onboard or config --token -
serverId Yes Discord server (guild) ID. Auto-detected by onboard, or set via config --server -
hookServerPort No Port for the hook server 18470
defaultAgentCli No Default AI CLI used by discode new when agent is omitted First installed CLI
telemetryEnabled No Opt-in flag for anonymous CLI telemetry false
telemetryEndpoint No HTTP endpoint for telemetry proxy (recommended: Cloudflare Worker) https://telemetry.discode.chat/v1/events
telemetryInstallId No Anonymous per-install random ID used as GA client ID Auto-generated on opt-in
discode config --show               # View current config
discode config --token NEW_TOKEN     # Update bot token
discode config --server SERVER_ID    # Set server ID manually
discode config --port 18470          # Set hook server port
discode config --telemetry on        # Enable anonymous telemetry
discode config --telemetry-endpoint https://telemetry.discode.chat/v1/events

Project State

Project state is stored in ~/.discode/state.json and managed automatically.

Environment Variables

Config values can be overridden with environment variables:

Variable Required Description Default
DISCORD_BOT_TOKEN Yes (if not in config.json) Discord bot token -
DISCORD_GUILD_ID Yes (if not in config.json) Discord server ID -
DISCORD_CHANNEL_ID No Override default channel Auto-created per project
TMUX_SESSION_PREFIX No Prefix for tmux session names ``
TMUX_SHARED_SESSION_NAME No Shared tmux session name (without prefix) bridge
DISCODE_DEFAULT_AGENT_CLI No Default AI CLI used by discode new when agent is omitted First installed CLI
HOOK_SERVER_PORT No Port for the hook server 18470
DISCODE_TELEMETRY_ENABLED No Enable telemetry without writing config (true/false) false
DISCODE_TELEMETRY_ENDPOINT No Telemetry proxy endpoint URL -
DISCODE_TELEMETRY_INSTALL_ID No Override anonymous install ID -
DISCORD_BOT_TOKEN=token discode daemon start
DISCORD_GUILD_ID=server_id discode new

Telemetry Proxy (GA4)

Deploy Cloudflare Worker proxy:

npm run telemetry:deploy
npm run telemetry:secret

CLI telemetry defaults to https://telemetry.discode.chat/v1/events. To override it with your own deployed Worker URL:

discode config --telemetry-endpoint https://discode-telemetry-proxy.<your-subdomain>.workers.dev
discode config --telemetry on

Worker source: workers/telemetry-proxy

Development

Architecture overview: ARCHITECTURE.md Module boundaries: docs/MODULE_BOUNDARIES.md Documentation maps: docs/DESIGN.md, docs/PRODUCT_SENSE.md, docs/PLANS.md

Building

bun install
bun run build          # Compile TypeScript
bun run dev            # Dev mode

Release Packaging (prebuilt binaries)

npm run build:release              # Build CLI/daemon/sidecar/runtime-client binaries + npm meta package
npm run build:release:binaries:single  # Build only current OS/arch binary
npm run pack:release               # Create npm tarballs in dist/release

Testing

bun test              # Run all tests
bun run test:watch    # Watch mode
bun run test:coverage # Coverage report

Test suite includes 129 tests covering:

  • Agent adapters
  • State management
  • Discord client
  • Hook-based event delivery
  • CLI commands
  • Storage and execution mocks

Project Structure

discode/
├── bin/                  # CLI entry point (discode)
├── src/
│   ├── agents/           # Agent adapters (Claude, Gemini, OpenCode)
│   ├── capture/          # shared message parsing utilities
│   ├── config/           # Configuration management
│   ├── discord/          # Discord client and message handlers
│   ├── infra/            # Infrastructure (storage, shell, environment)
│   ├── state/            # Project state management
│   ├── tmux/             # tmux session management
│   └── types/            # TypeScript interfaces
├── tests/                # Vitest test suite
├── package.json
└── tsconfig.json

Dependency Injection

The codebase uses constructor injection with interfaces for testability:

// Interfaces
interface IStorage { readFile, writeFile, exists, unlink, mkdirp, chmod }
interface ICommandExecutor { exec, execVoid }
interface IEnvironment { get, homedir, platform }

// Usage
class DaemonManager {
  constructor(
    private storage: IStorage = new FileStorage(),
    private executor: ICommandExecutor = new ShellCommandExecutor()
  ) {}
}

// Testing
const mockStorage = new MockStorage();
const daemon = new DaemonManager(mockStorage);

Code Quality

  • TypeScript strict mode enabled
  • ESM modules with .js extensions in imports
  • Vitest with 129 passing tests
  • No unused locals/parameters (enforced by tsconfig.json)

Troubleshooting

Bot not connecting

  1. Verify token: discode config --show
  2. Check bot permissions in Discord Developer Portal
  3. Ensure MessageContent intent is enabled
  4. Restart daemon: discode daemon stop && discode daemon start

Agent not detected

  1. Run discode agents to see available agents
  2. Verify agent binary is in PATH: which claude
  3. Install missing agent and retry

tmux session issues

  1. Check session exists: tmux ls
  2. Kill stale session: tmux kill-session -t <session-name>
  3. Restart project: discode stop && discode start

No messages in Discord

  1. Check daemon status: discode daemon status
  2. Check daemon logs
  3. Check Discord channel permissions (bot needs Send Messages)

Tip: Keep running with lid closed (macOS)

If you want Discode to keep working when the laptop lid is closed on battery power, run:

sudo pmset -b disablesleep 1

To revert to normal sleep behavior:

sudo pmset -b disablesleep 0

Contributing

Contributions are welcome! Please:

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

Guidelines

  • Add tests for new features
  • Maintain TypeScript strict mode compliance
  • Follow existing code style
  • Update documentation as needed

License

MIT License - see LICENSE file for details.

Acknowledgments

Support