CLI tool
mohak34/opencode-notifier avatar
mohak34/opencode-notifier

opencode-notifier: desktop alerts and sounds for OpenCode permission and completion events

OpenCode plugin for desktop notifications and sounds on permission, completion, and error events.

859 stars86 forksTypeScriptMIT

At a glance

What is it?
opencode-notifier is an OpenCode plugin that plays a sound and raises a desktop notification when the agent needs permission, when a session finishes, when an error occurs or when the question tool pops up. It ships a terminal-side component for OpenCode 2 alongside a server-side one, with per-event sound, notification, bell and custom-command settings, and it works on macOS, Linux and Windows.
Who is it for?
Adopt opencode-notifier if you run OpenCode on a machine where you switch away from the terminal and want to be told when a permission prompt, a question or an error appears. Do not adopt it if you are running opencode run or the desktop and web clients, since those do not load the terminal component and get no sound or popup.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 5 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What the plugin does and which events fire

opencode-notifier is an OpenCode plugin that plays sounds and sends system notifications when permission is needed, when generation completes, when errors occur or when the question tool is invoked, working on macOS, Linux and Windows.

The event list in the README is slightly longer than that summary. You get notified when OpenCode needs permission to run something, when your session finishes, when an error happens, and when the question tool pops up. There is also subagent_complete for when subagents finish, and user_cancelled for when you press ESC to abort, and both of those are silent by default so you are not spammed.

That default is the design decision worth noticing. The events that need attention are the ones that block you: a permission prompt you have not answered, a question you have not answered. The events that are merely informative, a subagent finishing, you cancelling, are available but muted, because an agent session produces a lot of completion events and a notification for each is noise.

The configuration exposes more events still. The default configuration lists session_started and user_message with sounds but notifications off, plan_exit with both on, and client_connected with a sound and no notification. So the plugin can tell you when a session begins, when you send a message, when a plan is ready for review, and when a client connects, in addition to the four headline events.

The plugin is published to npm as @mohak34/opencode-notifier, is MIT licensed, and the last push was on 2026-10-17 with v1.1.0 tagged the same day.

Installing it, and the difference between OpenCode 1 and 2

For OpenCode 1, you add the package to the plugin list in opencode.json:

json
{
  "plugin": ["@mohak34/opencode-notifier@latest"]
}

For OpenCode 2, the key is plugins rather than plugin:

json
{
  "plugins": ["@mohak34/opencode-notifier@latest"]
}

Then you restart OpenCode. The package contains both implementations and you do not select a version manually, and V2 loads the package's terminal component automatically in the interactive CLI.

That split into two implementations is the part of the design that needs explaining, and the README explains it under the heading of OpenCode 2 delivery.

In V2, sounds, desktop popups, terminal bells, Ghostty notifications and focus detection all run in the terminal client. Custom commands run once per event on the server, including when no terminal is open. This uses the existing command configuration, and no external notification service is bundled.

The reason is the deployment model. If you run OpenCode on a remote server with a terminal client attached on your laptop, the notification has to appear on the laptop and the custom command has to run on the server. Putting the sound and popup logic in the terminal client and the command logic on the server makes both work in that arrangement.

The README is explicit that with a remote server you put the sound and popup settings on your local computer and the custom-command settings on the server, that event-command script paths refer to the server's filesystem while click-command paths refer to the computer displaying the notification, and that focus suppression applies to local alerts rather than to server commands.

It also warns about duplicates. V2 already includes a notification plugin, so to avoid duplicate built-in alerts you add the exclusion to the plugins list in the CLI configuration:

json
{
  "plugins": ["*", "-opencode.notifications"]
}

Keep any other entries in that list, with the exclusion after matching enable entries such as * or opencode.*, since a later enable entry can turn the built-in alerts back on.

Which terminal gets the alert, and the presence registry behind it

When several OpenCode terminals are open, only one should alert you, and the mechanism for deciding which is the most intricate part of the README.

Each attached terminal receives alerts for its project. On one computer, clients with the session's family open in a tab or current route take priority over unrelated clients in the same project. If several eligible clients have it open, only the first to claim the event alerts. If none have it open, an eligible client still alerts after a brief fallback grace, and terminals on other computers each alert.

The claim mechanism is described in terms of what happens when the registry is unavailable: if the registry or the client-local UI APIs are unavailable, alerts fail open through the existing event claim. Failing open here means an alert is delivered rather than swallowed, which is the right default for a notification system.

The presence registry itself is a small filesystem protocol. Clients publish their open-session IDs in private temporary files, refreshed once per second and removed on disposal. Dead processes and presence older than five seconds are ignored. The one-second refresh and the five-second staleness window are the two numbers that determine how quickly a closed terminal stops being considered a candidate.

Two limitations are stated plainly. opencode run and the Desktop and Web clients do not load this terminal component, so they receive no plugin sound, popup or bell, though server commands still run. And V1 delivery and its enableOnDesktop behaviour are unchanged, which is the kind of statement that tells you the older path was deliberately left alone rather than unified.

One event does not translate between versions. plan_exit remains a V1 event, and V2 has no equivalent plan-ready signal, so that setting is inactive there. client_connected is best-effort: terminal startup for local alerts and server plugin startup for custom commands.

Per-platform requirements and the WSL wrinkle

The setup differs enough by platform that the README gives each one its own subsection.

On macOS there is nothing to do, it works out of the box, and it shows the Script Editor icon. That is a consequence of using osascript for the notification system, which is the default value of notificationSystem in the configuration.

On Linux it should work if you already have a notification system set up, and if not you install libnotify:

bash
sudo apt install libnotify-bin  # Ubuntu/Debian
sudo dnf install libnotify       # Fedora  
sudo pacman -S libnotify         # Arch

For sounds on Linux you need one of paplay, aplay, mpv or ffplay.

On Windows it works out of the box, with two caveats. Only .wav files work, not mp3, and you must use full paths like C:/Users/You/sounds/alert.wav rather than a tilde.

WSL has its own note. You should set customIconPath to a file on the Windows filesystem to avoid icon-path translation issues, and you can copy an icon from the repository's logos folder. The path is passed to snoretoast executables.

json
{
  "showIcon": true,
  "customIconPath": "C:\\Users\\YourName\\Documents\\opencode-logo-dark.png"
}

That is in the opencode-notifier.json configuration file, and the README also links to a note on missing WSL notifications for the cases where nothing appears at all.

The dependency list explains the platform spread. node-notifier handles the desktop notifications on macOS and Windows, dbus-next handles the Linux path, and is-wsl detects the WSL case so the right behaviour can be selected.

The configuration file, and the shape of the event settings

The configuration lives at ~/.config/opencode/opencode-notifier.json, and omitted settings fall back to their defaults.

The top level is a set of global switches. sound and notification are the two booleans that turn the behaviours on, bell adds the terminal bell, and timeout is the number of seconds the notification stays. The notificationTitle can be null, which presumably means the message text is used. showProjectName, showFullPath and showSessionTitle control what context appears in the notification body.

The showIcon and customIconPath pair controls the icon, and the icon path is what the WSL guidance is about. suppressWhenFocused suppresses alerts while your terminal has focus, and focusOnClick brings the terminal to the front when you click the notification. enableOnDesktop is the V1 behaviour and is left unchanged. notificationSystem selects how the notification is raised, with osascript as the default. suppressGhosttySound covers the case where the Ghostty terminal plays its own sound. The linux object has a grouping setting, and minDuration is a minimum duration before an alert is shown.

The command object configures a custom command run on an event, with enabled, path, args and its own minDuration. The arguments are a template, with --event and --message placeholders, so the script receives the event name and the message text.

Then the events object, where each of the eleven events carries its own sound, notification, command and bell booleans. That is the core of the configuration: the global switches set the default behaviour, and each event can override it. The default configuration turns sound, notification and command on for permission, complete, error, question and plan_exit, turns the bell off everywhere, and leaves subagent_complete and user_cancelled silent apart from the command.

The messages object gives the text template per event, all built around a {sessionTitle} placeholder, and the sounds object maps each event to a sound file, which is null in the default configuration so the plugin falls back to whatever built-in sound it uses. The volumes object maps each event to a volume level.

Limits: terminal-only delivery, one maintainer and a fast-moving dependency surface

The limits are visible in the README and in the packaging.

The first is that delivery is tied to the terminal client. opencode run and the Desktop and Web clients do not load the terminal component, so they get no sound, no popup and no bell, though server commands still run. If your workflow is headless, most of this plugin does nothing for you and only the custom command half applies.

The second is the OpenCode version surface. The package has to satisfy three peer dependency ranges at once: @opencode-ai/plugin at 1.17 or newer below 2, and @opencode/client and @opencode/plugin at 2.0.18 or newer below 3, and all three are marked optional in peerDependenciesMeta. Supporting two major versions of the host application in one package, with a terminal component for one and a server component for the other, is a lot of surface for a plugin that raises notifications. The README's mitigation is the release notes and the CHANGELOG.md at the root.

The third is the event mapping gap. plan_exit works in V1 and has no equivalent in V2, so a configuration that depends on being told when a plan is ready will behave differently depending on the host version, and the setting is simply inactive rather than reporting an error.

The fourth is that presence is tracked through temporary files refreshed once a second. That works on a single machine and is what makes the multi-machine arrangement possible, but it is a filesystem convention rather than a documented API, and it depends on the temporary directory being writable and shared appropriately. The five-second staleness window is also a hard-coded value in the design rather than a setting.

The fifth is the project size. The releases are v1.1.0 on 2026-10-17, its beta on the same day, and v1.0.0 on 2026-10-14, so the project is a few days old. The repository is small, with src/, test/, sounds/, logos/ and a gnome-shell-extension/ directory, but a plugin this tightly coupled to a fast-moving host application will need frequent updates.

The last point is about the dependency choices. node-notifier and dbus-next are the two libraries doing the platform work, and the repository references node-notifier's own README for WSL troubleshooting, which means the platform edge cases are inherited from those libraries rather than solved here.

opencode-notifier against the built-in alerts and against running the agent in the foreground

Two comparisons make the choice clearer.

The first is against the notifications OpenCode 2 ships with, which is why the README tells you to disable them. The difference in approach is that the built-in plugin is one implementation of one behaviour, while this package is a configurable layer over it with per-event control, custom commands, per-platform sounds, focus suppression, click-to-focus, volume control and the multi-terminal claiming behaviour. If you want the built-in popup and nothing else, the built-in one is simpler and there is no reason to install this.

The second comparison is against just watching the terminal. The plugin exists because agents run long and you switch away: you start a job, go and do something else, and come back to find it has been blocked on a permission prompt for twenty minutes. A notification solves that. The cost is that every event that fires is an interruption, which is why the plugin lets you mute the informative ones, and why the focus suppression setting exists.

The middle option is the custom command half. You can point the command configuration at a script that does something other than a desktop notification, a terminal bell, a message to a chat system, a file that something else watches. Because the command runs once per event on the server with the event name and message passed as arguments, it works even when no terminal is attached, which makes it the part of this plugin that fits a headless setup.

So the honest summary is that opencode-notifier earns its place for the terminal-attached case, where you want to be interrupted only for the events that block you. For a headless or already-notified workflow, the custom command configuration is the part to take, and the notification machinery is decoration you can leave off.

Editorial conclusion

Adopt opencode-notifier if you run OpenCode on a machine where you switch away from the terminal and want to be told when a permission prompt, a question or an error appears. Do not adopt it if you are running opencode run or the desktop and web clients, since those do not load the terminal component and get no sound or popup. Verify first that your platform has what it needs, libnotify on Linux and a wav file on Windows, and that you disable the built-in alerts in the OpenCode 2 CLI configuration so you do not get every event twice.

Frequently asked questions

Which OpenCode events does opencode-notifier fire on?

It notifies when OpenCode needs permission to run something, when your session finishes, when an error happens and when the question tool pops up. It also handles subagent_complete for when subagents finish and user_cancelled for when you press ESC to abort, both of which are silent by default so you are not spammed. The configuration can also enable session_started, user_message, plan_exit and client_connected.

How do I install opencode-notifier in OpenCode 2?

Add the package to the plugins list in the configuration, for example "plugins": ["@mohak34/opencode-notifier@latest"], then restart OpenCode. The package contains both the V1 and V2 implementations and you do not choose a version. Because OpenCode 2 already ships a notification plugin, you should also add "-opencode.notifications" to the plugins list in ~/.config/opencode/cli.json to avoid duplicate alerts.

What does opencode-notifier need on Linux and Windows?

On Linux you need libnotify installed, which is libnotify-bin on Ubuntu and Debian, libnotify on Fedora and libnotify on Arch, plus one of paplay, aplay, mpv or ffplay for sounds. On Windows it works out of the box, but only .wav files work rather than mp3, and you must use full paths rather than a tilde. Under WSL you should point customIconPath at a file on the Windows filesystem.

Official sources

  1. License: MIT
  2. mohak34/opencode-notifier on GitHub
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/mohak34-opencode-notifier.svg)](https://hysenlabs.com/projects/mohak34-opencode-notifier)

Was this useful?