Control your AI coding assistant from anywhere β your phone, tablet, or another computer.
π¦ Used by developers worldwide β 2,000+ weekly downloads on npm
π New in v1.6! Hub with buttons, one Discord channel per session named after the session, sticky action buttons (Plan/Build, Interrupt, Diff, Model, Archive), resume your existing OpenCode sessions directly in Discord, and voice transcription powered by the local Handy app.
π§΅ v1.5: Session management β browse, attach, and manage OpenCode CLI sessions from Discord with
/session.π€ v1.4: Voice message support β send voice messages that are automatically transcribed and processed.
remote-opencode is a Discord bot that bridges your local OpenCode CLI to Discord, enabling you to interact with your AI coding assistant remotely. Perfect for developers who want to:
- π± Code from mobile β Send coding tasks from your phone while away from your desk
- π» Access from any device β Use your powerful dev machine from a laptop or tablet
- π Work remotely β Control your home/office workstation from anywhere
- ποΈ Console-like UX β A hub to launch sessions, one channel per session, and buttons for everything (no slash-command typing on your phone)
- π₯ Collaborate β Share AI coding sessions with team members in Discord
- π€ Automated Workflows β Queue up multiple tasks and let the bot process them sequentially
- π€ Voice Messages β Send voice messages that are transcribed locally by Handy and processed as text
flowchart LR
A["π± Your Phone / Other Device"] <-->|"Discord API"| B["π€ Discord Bot"]
subgraph Workflow
direction TB
B --> H["π₯οΈ Hub (launcher)"]
H --> S["π§΅ Session channels (one per session)"]
S --> C["π» OpenCode CLI"]
C --> D["π Your Codebase"]
end
The bot runs on your development machine alongside OpenCode. You launch sessions from a hub channel, each session gets its own text channel named after the session, and the output streams back in real-time.
remote-opencode.mp4
remote-opencode_voice-mode.mp4
- Installation
- Quick Start
- Hub & Session Channels
- Discord Bot Setup
- Voice Transcription (Handy)
- CLI Commands
- Discord Slash Commands
- Usage Workflow
- Access Control
- Configuration
- Run as a systemd service
- Proxy Support
- Troubleshooting
- Development
- Changelog
- License
- Node.js 22+ β Download
- Build tools β
node-ptycompiles from source on install (build-essential+python3on Debian/Ubuntu) - OpenCode CLI β Must be installed and working on your machine
- Discord Account β With a server where you have admin permissions
- Handy (optional, for voice) β Local speech-to-text app, see Voice Transcription (Handy)
git clone https://github.com/Felipe-258/remote-opencode.git
cd remote-opencode
npm install
npm run build
npm link # Makes 'remote-opencode' available globally# Global installation
npm install -g remote-opencode
# Or run directly with npx
npx remote-opencodeNote: The npm package is the upstream build and does not include the v1.6 hub / session-channel features. Use the source install above (or the fork) for the latest features.
# Step 1: Run the interactive setup wizard
remote-opencode setup
# Step 2: Start the Discord bot
remote-opencode startThen, in your Discord server:
- Run
/hubin any text channel β this becomes your launcher. - Press π Nueva sesiΓ³n, type a name (e.g.
24 august), and a session channelπ€ 24 augustis created. - Type your prompt directly in that channel β it goes straight to OpenCode.
The v1.6 UX is built around a hub and one Discord channel per session, so you almost never need to type slash commands from your phone.
Run /hub in any text channel. The bot pins a launcher panel with buttons:
| Button | Action |
|---|---|
| π Nueva sesiΓ³n | Opens a modal where you name the session, then creates a session channel |
| π Sesiones | Lists your sessions (including existing OpenCode sessions) and lets you resume or reopen them |
| π§ Modelo | Sets the default model used for new sessions |
| π Proyecto | Sets the default project used for new sessions |
The hub remembers its project alias, model, and category. By default the model is deepseek/deepseek-flash (set per project with the π§ Modelo button).
When you create a session:
- A text channel
π€ <name>is created inside a Sesiones category (created automatically). - The channel is bound to the hub's project and model.
- A matching OpenCode session is created with that title.
- Passthrough is ON: any message you type in the channel is sent directly to OpenCode.
- A pinned message shows sticky buttons:
| Button | Action |
|---|---|
| π― Plan / π¨ Build | Toggle agent mode (like Tab in the TUI) |
| βΉοΈ Interrupt | Abort the current task |
| π Diff | Show git diff of the project |
| π§ Modelo | Change the model for this channel |
| π Archivar | Archive the session (channel becomes π <name> and is hidden) |
| β©οΈ Undo | Revert the last user message |
| βΉοΈ Status | Project, branch, model, mode, session and busy state |
| π§Ή Compactar | Summarize/compact the context |
| β‘ Init | Generate/update AGENTS.md for the project |
If OpenCode renames the session (it auto-titles sessions after the first reply), the channel is renamed to match.
π Sesiones shows, sorted by most recent activity:
- Sessions already mapped to Discord channels (π’ active / βͺ idle, π archived).
- Other sessions from OpenCode β including ones you created earlier in the terminal. Use the "Resumir sesiΓ³n en Discord" select to create a channel attached to an existing OpenCode session and continue the conversation.
- Archived sessions β reopen any archived session (channel becomes visible again and passthrough is restored).
The setup wizard (remote-opencode setup) guides you through the entire process interactively:
- Opens Discord Developer Portal in your browser
- Walks you through creating an application, enabling intents, and getting your bot token
- Generates the invite link automatically and opens it in your browser
- Deploys slash commands to your server
Just run remote-opencode setup and follow the prompts β no manual URL copying needed!
π Manual setup reference (click to expand)
- Create Application: Go to Discord Developer Portal, create a new application
- Enable Intents: In "Bot" section, enable SERVER MEMBERS INTENT and MESSAGE CONTENT INTENT
- Get Bot Token: In "Bot" section, reset/view token and copy it
- Get Guild ID: Enable Developer Mode in Discord settings, right-click your server β Copy Server ID
- Invite Bot: The bot needs to create channels, manage categories and hide channels. Use this URL (permissions
275901456):https://discord.com/api/oauth2/authorize?client_id=YOUR_CLIENT_ID&permissions=275901456&scope=bot+applications.commands - Check Channel Access: For private or restricted channels, make sure the bot user or bot role can access the channel.
| Permission | Why |
|---|---|
| Manage Channels | Create the Sesiones category and session channels, hide archived ones |
| View Channel / Send Messages | Interact in channels and threads |
| Manage Messages | Edit streamed messages and pin the sticky panels |
| Embed Links / Read Message History | Render embeds and read the conversation |
| Create Public Threads / Send Messages in Threads | Legacy thread-based flows (/work, /opencode in a channel) |
| Attach Files | Voice/download flows |
Voice messages in session channels are transcribed locally using Handy β a speech-to-text desktop app (Whisper-family models via transcribe-cpp). No API keys, no cloud, audio never leaves your machine.
- You send a voice message (π€) in a session channel.
- The bot downloads it, converts it to 16 kHz mono WAV with a bundled
ffmpeg-staticbinary (no system ffmpeg needed). - It runs
handy --transcribe-file <wav> --jsonand sends the text as a prompt to OpenCode (queued if the agent is busy).
sudo apt install handyOpen the Handy app, pick a model (e.g. Parakeet V3 or Nemotron Streaming) that handles your language well, and leave it running (system tray). The bot uses the same model you selected in the app.
| Variable | Default | Description |
|---|---|---|
HANDY_BIN |
/usr/bin/handy |
Path to the Handy binary |
HANDY_MODEL |
(Handy's selected model) | Optional model id to force for transcription |
HANDY_TIMEOUT_MS |
120000 |
Max time for one transcription |
/voice status reports whether Handy was found.
Running under systemd: Handy initializes GTK even in headless mode, so the service needs the graphical session environment:
DISPLAY,WAYLAND_DISPLAYandXDG_RUNTIME_DIR. See Run as a systemd service.Docker note: Handy is a desktop app, so voice transcription is only supported when the bot runs natively on the same host. The bundled
ffmpeg-staticbinary also targets glibc β the alpine-basedDockerfileis fine for the rest of the bot but not for voice.
| Command | Description |
|---|---|
remote-opencode |
Start the bot (shows setup guide if not configured) |
remote-opencode setup |
Interactive setup wizard β configures bot token, IDs |
remote-opencode start |
Start the Discord bot |
remote-opencode deploy |
Deploy/update slash commands to Discord |
remote-opencode undeploy |
Remove slash commands from Discord |
remote-opencode config |
Display current configuration info |
remote-opencode allow add <userId> |
Add a Discord user ID to the allowlist |
remote-opencode allow remove <userId> |
Remove a Discord user ID from the allowlist |
remote-opencode allow list |
List all user IDs in the allowlist |
remote-opencode allow reset |
Clear the entire allowlist (removes access control) |
remote-opencode voice status |
Show voice transcription status (Handy) |
Once the bot is running, use these commands in your Discord server:
Make the current channel your hub. Pins the launcher panel (new session, sessions list, model, project).
/hub
Register a local project path with an alias for easy reference.
/setpath alias:myapp path:/Users/you/projects/my-app
| Parameter | Description |
|---|---|
alias |
Short name for the project (e.g., myapp, backend) |
path |
Absolute path to the project on your machine |
View all registered project paths and their aliases.
/projects
Set which project a Discord channel should interact with.
/use alias:myapp
After binding, all /opencode commands in that channel will work on the specified project.
Sends a prompt to OpenCode and streams the response.
/opencode prompt:Add a dark mode toggle to the settings page
Behavior in v1.6:
- In a session channel β runs the prompt in that session.
- In the hub (or any non-session channel) β creates a session channel named after the prompt and runs it there.
- Legacy: in a thread, it continues the thread's conversation.
Features:
- β‘ Real-time streaming β see output as it's generated (1-second updates)
- βΈοΈ Interrupt button β stop the current task if needed
- π Session persistence β continue conversations in the same channel/thread
Start isolated work on a new branch with its own worktree.
/work branch:feature/dark-mode description:Implement dark mode toggle
/work branch:feature/dark-mode # description defaults to the branch name
| Parameter | Description |
|---|---|
branch |
Git branch name (will be sanitized) |
description |
Optional. Defaults to the branch name when omitted. |
Features:
- π³ Creates a new git worktree for isolated work
- π§΅ Opens a dedicated thread for the task
- ποΈ Delete button β removes worktree and archives thread
- π Create PR button β automatically creates a pull request
Enable passthrough mode to send messages directly to OpenCode without slash commands.
/code
How it works:
- Run
/codein a session channel (or a thread) to enable passthrough mode - Type messages naturally β they're sent directly to OpenCode
- Run
/codeagain to disable
Session channels created from the hub have passthrough already enabled β no need to run
/code.
Features:
- π± Mobile-friendly β no more typing slash commands on phone
- β³ Busy indicator β shows π₯ reaction and queues if the previous task is still running
- π Safe β ignores bot messages (no infinite loops)
Enable automatic worktree creation for a project. When enabled, new /opencode sessions will automatically create isolated git worktrees.
/autowork
Enable automatic passthrough mode for a project. When enabled, every new thread the bot creates will already have passthrough mode on.
/autocode
Control the automated job queue for the current channel/thread.
/queue list
/queue clear
/queue pause
/queue resume
/queue settings continue_on_failure:True fresh_context:False
Settings:
continue_on_failure: IfTrue, the bot moves to the next task even if the current one fails.fresh_context: IfTrue, the AI forgets previous chat history for each new queued task. Default:False.
Show git diffs for the current project directly in Discord β perfect for reviewing AI-made changes from your phone.
/diff
/diff target:staged
/diff target:branch base:develop
/diff stat:true
| Parameter | Description |
|---|---|
target |
unstaged (default), staged, or branch |
stat |
Show --stat summary only instead of full diff (default: false) |
base |
Base branch for target:branch diff (default: main) |
Manage the user allowlist directly from Discord. This command is only available when the allowlist has already been initialized (at least one user exists).
/allow action:add user:@username
/allow action:remove user:@username
/allow action:list
/voice status
Shows whether Handy is available and transcription is enabled.
View available AI models or set the model for the current channel.
/model list
/model set name:anthropic/claude-sonnet-4-20250514
| Subcommand | Description |
|---|---|
list |
Show all available models grouped by provider |
set |
Set the AI model for the current channel/thread |
Features:
- π Autocomplete β start typing a model name and get instant suggestions
- πΎ Per-channel persistence β model preferences are saved per channel/thread
Browse OpenCode CLI sessions and manage session-channel mappings. Useful for resuming previous conversations or sharing sessions.
/session list
/session attach
/session detach
/session info
| Subcommand | Description |
|---|---|
list |
List all sessions for the current project (active + mapped) |
attach |
Attach an existing session to this thread (interactive menu) |
detach |
Disconnect the session from this thread |
info |
Show detailed status of the attached session |
-
Set up the hub:
/hub -
Register and bind your project (once):
/setpath alias:webapp path:/home/user/my-webappThen press π Proyecto in the hub and pick
webapp. -
Create a session β press π Nueva sesiΓ³n, type a name.
-
Code β type prompts directly in the new
π€ <name>channel. Use the sticky buttons for Interrupt, Diff, Model, Undo, Archive. -
Resume later β press π Sesiones in the hub: pick an existing OpenCode session to resume it in Discord, or reopen an archived one.
- π± Open Discord on your phone
- Open the hub channel and press π Nueva sesiΓ³n
- Type prompts directly in the session channel (passthrough is on)
- Use buttons to Interrupt / Diff / Archive β no slash commands needed
- Send voice messages (π€) β they're transcribed locally by Handy and sent as prompts
Share AI coding sessions with your team:
- Create a dedicated Discord channel for your project
- Bind the project:
/use alias:team-project - Team members can watch sessions in real-time
- Discuss in channels while AI works
β οΈ If the server has people you don't trust, configure the allowlist first.
Perfect for "setting and forgetting" several tasks:
- Send multiple instructions β if the bot is busy, tasks are queued (π₯).
- The bot finishes the current task, then processes the queue in order.
- Monitor progress:
/queue list.
remote-opencode supports an optional user allowlist to restrict who can interact with the bot. This is essential when your bot runs in a shared Discord server where untrusted users could otherwise execute commands on your machine.
- No allowlist configured (default): All Discord users in the server can use the bot. This preserves backward compatibility for existing installations.
- Allowlist configured (1+ user IDs): Only users whose Discord IDs are in the allowlist can use slash commands, buttons, and passthrough messages. Unauthorized users receive a rejection message.
β οΈ SECURITY WARNING: If your bot operates in a Discord channel accessible to untrusted users, you MUST configure the allowlist before starting the bot. The initial allowlist setup can ONLY be done via the CLI or the setup wizard β NOT from Discord. This prevents unauthorized users from adding themselves to an empty allowlist.
remote-opencode setupStep 5 of the wizard prompts you to enter your Discord user ID. This becomes the first entry in the allowlist.
# Add your Discord user ID
remote-opencode allow add 123456789012345678
# Verify
remote-opencode allow list- Cannot remove the last user β prevents accidental lockout
allow resetis the only way to fully clear the allowlist (intentional action)- Discord
/allowis disabled when allowlist is empty β prevents bootstrap attacks - Config file permissions are set to
0o600(owner-read/write only)
If you run opencode serve with HTTP Basic auth enabled via OPENCODE_SERVER_PASSWORD (and optionally OPENCODE_SERVER_USERNAME), the bot picks up the same credentials and applies them to all internal communication β session HTTP calls, the SSE /event stream, and readiness probes.
OPENCODE_SERVER_PASSWORD='your-password' remote-opencode startAll configuration is stored in ~/.remote-opencode/:
| File | Purpose |
|---|---|
config.json |
Bot credentials (token, client ID, guild ID) |
data.json |
Projects, bindings, sessions, hub, archives |
{
"discordToken": "your-bot-token",
"clientId": "your-application-id",
"guildId": "your-server-id",
"allowedUserIds": ["123456789012345678"]
}
allowedUserIdsis optional. When omitted or empty, access control is disabled.
{
"projects": [
{ "alias": "myapp", "path": "/Users/you/projects/my-app" }
],
"bindings": [
{ "channelId": "channel-id", "projectAlias": "myapp", "model": "deepseek/deepseek-flash" }
],
"threadSessions": [ ... ],
"worktreeMappings": [ ... ],
"hub": {
"hubChannelId": "hub-channel-id",
"categoryId": "sesiones-category-id",
"projectAlias": "myapp",
"model": "deepseek/deepseek-flash",
"agent": "build"
},
"channelAgents": [ { "channelId": "channel-id", "agent": "build" } ],
"archivedSessions": [ ... ]
}| Field | Description |
|---|---|
hub |
Hub channel, category, default project alias, model and agent mode |
channelAgents |
Per-channel agent mode (build / plan) |
archivedSessions |
Archived session channels, ready to be reopened |
| Variable | Description |
|---|---|
HANDY_BIN |
Path to the Handy binary (default /usr/bin/handy) |
HANDY_MODEL |
Force a Handy model id for transcription |
HANDY_TIMEOUT_MS |
Handy transcription timeout in ms (default 120000) |
OPENCODE_SERVER_PASSWORD |
Optional HTTP Basic auth password for opencode serve |
OPENCODE_SERVER_USERNAME |
Optional auth username (default opencode) |
HTTP_PROXY / HTTPS_PROXY / ALL_PROXY / NO_PROXY |
Proxy support for outbound requests |
Run the bot in the background and start it on boot:
/etc/systemd/system/remote-opencode.service:
[Unit]
Description=remote-opencode Discord bot
After=network.target
[Service]
Type=simple
User=felipe
WorkingDirectory=/home/felipe/Documents/Github/remote-opencode
Environment=PATH=/home/felipe/.nvm/versions/node/v24.15.0/bin:/home/felipe/.opencode/bin:/usr/local/bin:/usr/bin:/bin
# Required so Handy can initialize GTK for local transcription
Environment=DISPLAY=:0
Environment=WAYLAND_DISPLAY=wayland-0
Environment=XDG_RUNTIME_DIR=/run/user/1000
ExecStart=/home/felipe/.nvm/versions/node/v24.15.0/bin/node --no-deprecation /home/felipe/Documents/Github/remote-opencode/dist/src/cli.js start
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.targetsudo systemctl daemon-reload
sudo systemctl enable --now remote-opencode
sudo journalctl -u remote-opencode -fAdjust the paths, user and display variables to your environment. If you don't use voice, the DISPLAY/WAYLAND_DISPLAY/XDG_RUNTIME_DIR lines can be omitted.
remote-opencode supports HTTP proxy environments for Discord and other external API requests via HTTP_PROXY, HTTPS_PROXY, ALL_PROXY, and NO_PROXY. Local OpenCode traffic is always kept direct (localhost, 127.0.0.1, ::1 are excluded automatically).
- Check bot is online: Look for the bot in your server's member list
- Verify permissions: the bot needs Manage Channels, Send Messages, Manage Messages, Embed Links, Read Message History, and (for legacy flows) Create Public Threads
- Redeploy commands:
remote-opencode deploy
Handy needs a display server even in headless mode. When running under systemd, add the graphical session environment to the unit:
Environment=DISPLAY=:0
Environment=WAYLAND_DISPLAY=wayland-0
Environment=XDG_RUNTIME_DIR=/run/user/1000Then restart the service.
Bind a project first:
/setpath alias:myproject path:/path/to/project
/use alias:myproject
Or set the default project from the hub with π Proyecto.
Make sure the bot has Manage Channels and can access the channel/category.
- Kick the bot from your server
- Re-invite it with the invite URL above
- Run
remote-opencode deploy
- Verify OpenCode is installed:
opencode --version
- Check if another process is using the port
- Ensure the project path exists and is accessible
- Check Node.js version:
node --version # Should be 22+ - Verify configuration:
remote-opencode config - Re-run setup:
remote-opencode setup
git clone https://github.com/Felipe-258/remote-opencode.git
cd remote-opencode
npm install
# Development mode (with ts-node)
npm run dev setup # Run setup
npm run dev start # Start bot
# Build and run production
npm run build
npm startnpm testsrc/
βββ cli.ts # CLI entry point
βββ bot.ts # Discord client initialization
βββ commands/ # Slash command definitions
β βββ hub.ts # Hub / launcher panel with buttons
β βββ opencode.ts # Main AI interaction command
β βββ code.ts # Passthrough mode toggle
β βββ work.ts # Worktree management
β βββ diff.ts # Git diff viewer
β βββ model.ts # AI model list/set with autocomplete
β βββ session.ts # Session browsing and management
β βββ allow.ts # Allowlist management
β βββ voice.ts # Voice transcription status
β βββ setpath.ts # Project registration
β βββ projects.ts # List projects
β βββ use.ts # Channel binding
βββ handlers/ # Interaction handlers
β βββ interactionHandler.ts # Commands, modals, select menus
β βββ buttonHandler.ts # Buttons (sticky panels, hub)
β βββ messageHandler.ts # Passthrough + voice message handling
βββ services/ # Core business logic
β βββ serveManager.ts # OpenCode process management
β βββ sessionManager.ts # Session state management
β βββ sessionFlow.ts # Session channels, sticky messages, archive/reopen
β βββ queueManager.ts # Automated job queuing (incl. voice)
β βββ executionService.ts # Core prompt execution logic
β βββ voiceService.ts # Voice transcription (Handy local)
β βββ sseClient.ts # Real-time event streaming
β βββ dataStore.ts # Persistent storage
β βββ configStore.ts # Bot configuration
β βββ worktreeManager.ts # Git worktree operations
βββ setup/ # Setup wizard
β βββ wizard.ts
β βββ deploy.ts
βββ utils/ # Utilities
βββ messageFormatter.ts
βββ threadHelper.ts
See CHANGELOG.md for a full history of changes.
MIT
Contributions are welcome! Please read our Contributing Guide before submitting a Pull Request.
