npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

discordance

v0.4.1

Published

opencode plugin: a bi-directional bridge that streams session turns and tool calls to Discord and drives the session from Discord.

Readme

discordance

An opencode plugin: a bi-directional bridge between an opencode coding-agent session and Discord.

  • Outbound streaming. Assistant turns and tool calls are streamed to a Discord channel using hermes-style progressive message edits (send on first token, edit as tokens arrive, roll to a new message at the 2000-char limit).
  • Inbound queueing. Messages you send from Discord are forwarded via opencode's async prompt endpoint, so they land in opencode's own prompt queue: they appear in the TUI's queued-message list and run when the current turn finishes (never force-injected mid-turn).
  • Interactive gates. Permission requests and agent questions surface as Discord buttons (Allow once / Always / Reject, or one button per answer) and reply straight back into the session.
  • Command mirroring. In on mode your opencode commands are exposed as Discord slash commands and run in the active session.
  • Runtime toggle. Everything is driven by the /remote enable|on|off command from the opencode TUI (no restart needed to switch modes).

The plugin talks to Discord directly via discord.js over a live gateway connection and needs its own bot.

Modes: /remote

The bridge has three modes, toggled at runtime from the opencode TUI with the /remote command (the plugin installs this command into your global opencode config on first run, so restart opencode once after installing):

| Command | Mode | Behaviour | | --- | --- | --- | | /remote enable | enable | Outbound only: assistant turns and tool calls stream to Discord. | | /remote on | on | Full two-way: outbound streaming plus inbound messages queued into the session, permission/question button gates, and your opencode commands mirrored as Discord slash commands. | | /remote off | off | Bridge silent; slash commands deregistered. |

Running /remote with no (or an unknown) argument shows the current mode as a toast.

In on mode the plugin enumerates your opencode commands (GET /command) and registers each as a Discord slash command. Invoking one from Discord runs it in the active session via POST /session/:id/command and streams the result back into the channel. The /remote command itself is never mirrored.

Install

Add the package to the plugin array of your opencode config (opencode.json in your project, or ~/.config/opencode/opencode.json globally):

{
  "$schema": "https://opencode.ai/config.json",
  "plugin": ["discordance"]
}

opencode auto-installs listed npm plugins with bun at startup (cached under ~/.cache/opencode/node_modules/). No manual npm install and no .opencode/plugins/ wrapper is needed for the published package.

On first run the plugin writes ~/.config/opencode/commands/remote.md (respecting OPENCODE_CONFIG_DIR/XDG_CONFIG_HOME) so the /remote command is available. Restart opencode once for it to appear.

Configuration

All configuration is via environment variables. The bridge starts in off mode unless told otherwise, and is toggled live with /remote.

Required for the Discord bridge

| Variable | Description | | --- | --- | | DISCORD_BOT_TOKEN | Your Discord bot token. Never commit this. | | DISCORDANCE_STREAM_CHANNEL_ID | Target channel (or DM channel) ID for the stream. |

Optional tuning

| Variable | Default | Description | | --- | --- | --- | | DISCORDANCE_REMOTE_MODE | off | Initial mode on load: off, enable, or on. | | DISCORDANCE_STREAM | off | Legacy alias — if set, starts in on mode. | | DISCORDANCE_REQUIRE_MENTION | off | Set to 1 to only act on guild-channel messages that @-mention the bot (DMs always bypass). Recommended when the target is a shared channel so member-directed chatter isn't captured. The mention is stripped before the message is queued. | | DISCORDANCE_LISTEN_USER_IDS | unset (allow-all) | Comma-separated Discord user-ID allow-list for inbound messages, slash commands, and button gates. Empty allows everyone (back-compat). Strongly recommended whenever the target is a shared channel — inbound messages are queued as prompts that can trigger tool calls, so this is the deny-by-default control over who can drive your session. | | DISCORD_GUILD_ID | unset | If set, slash commands are also registered to this guild for instant updates (global registration always happens too; guild-scoped commands appear only inside that guild, not in DMs). | | DISCORDANCE_STREAM_EDIT_INTERVAL_MS | 1200 | Minimum ms between message edits (rate-limit friendly). | | DISCORDANCE_STREAM_BUFFER_CHARS | 60 | Chars buffered before a forced flush/edit. | | DISCORDANCE_STREAM_CURSOR | | Cursor glyph appended while a message is still streaming. | | DISCORDANCE_STREAM_QUIET | follows DISCORDANCE_REQUIRE_MENTION | Stream tool calls as a header only (🔧 **tool** ✓), dropping the command/path detail, to keep a shared channel legible. Defaults on when DISCORDANCE_REQUIRE_MENTION is set; force with 1/0. | | DISCORDANCE_GATE_TTL_MS | 900000 (15m) | How long a permission/question button gate stays live before expiring. | | DISCORDANCE_DEBUG | off | Set to 1 to write verbose inbound/gateway debug logs. | | DISCORDANCE_DEBUG_FILE | /tmp/dd-inbound-debug.log | Debug log path (when DISCORDANCE_DEBUG=1). |

Channel choice (important on a shared channel)

Outbound streaming always fills whatever channel it targets, so it should not live in a shared channel like a team #general. Prefer one of:

  • a dedicated guild channel (e.g. #opencode), or
  • a DM with the bot — a DM is inherently 1:1, so no mention-gate is needed.

If you must share a channel, protect it: set DISCORDANCE_REQUIRE_MENTION=1 (so only messages that @-mention the bot are captured) and DISCORDANCE_LISTEN_USER_IDS to your own user ID (so only you can drive the session). Without an allow-list, anyone who can post in the channel can queue prompts that run tools in your live opencode session.

Event trace plugin (diagnostics only)

The package also exports a DiscordanceTrace plugin that dumps every opencode event to JSONL for debugging. It is fully independent of the Discord bridge.

| Variable | Default | Description | | --- | --- | --- | | DISCORDANCE_TRACE | off | Set to 1 to enable event tracing. | | DISCORDANCE_TRACE_FILE | <project>/.opencode/discordance-trace/events.jsonl | Output path for the JSONL trace. |

Discord bot setup

  1. Create an application + bot at the Discord Developer Portal and copy the bot token into DISCORD_BOT_TOKEN.
  2. Under Bot > Privileged Gateway Intents, enable Message Content Intent.
  3. Invite the bot to your server, or open a DM with it. The plugin requests Guilds, GuildMessages, MessageContent and DirectMessages intents with Channel/Message partials, so both guild channels and DMs work. The standard bot invite scope already includes applications.commands, so no extra scope is needed for slash commands.
  4. Get the channel ID (Discord Settings > Advanced > Developer Mode, then right-click the channel/DM > Copy ID) and set DISCORDANCE_STREAM_CHANNEL_ID.
  5. Slash commands (mode on) are registered globally by default, so they show up in DMs and every guild (allow up to ~1h to propagate). Set DISCORD_GUILD_ID to also register them in one guild for instant iteration.

Local development (from this repo)

The published package needs no wrapper, but to exercise the plugin in-repo before publishing there is a thin dev wrapper at .opencode/plugins/discordance.ts that re-exports from this package's built output:

cd opencode-plugin
npm install
npm run build          # emits dist/ that the wrapper imports

Then start opencode from the repo root with the env vars set. Rebuild (npm run build) after editing anything under src/. npm run typecheck runs a no-emit strict type check.

Publishing

cd opencode-plugin
npm run typecheck                 # strict type check
npm publish --dry-run             # inspect the tarball (should contain only dist/, package.json, README, LICENSE)
npm login                         # first time only
npm publish --access public

prepublishOnly runs the build automatically, so dist/ is always fresh in the published tarball. Only dist/ is shipped (see files in package.json); src/ and node_modules/ are excluded.

License

MIT (c) Quintin De Kok