Baike.dev
All toolsOnline toolsAI codingTrendingOpen sourceNewsSubmit
Log in
< Back to tools
O

opencode-notifier

> 编程语言
Open source

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

747 stars0 likes1 views
WebsiteGitHub

About

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

opencode-notifier

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

Quick Start

Install the plugin via the CLI: opencode plug -g @mohak34/opencode-notifier.

Or add manually to your opencode.json:

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

Restart OpenCode. Done.

What it does

You'll get notified when:

  • OpenCode needs permission to run something
  • Your session finishes
  • An error happens
  • The question tool pops up

There's also subagent_complete for when subagents finish, and user_cancelled for when you press ESC to abort -- both are silent by default so you don't get spammed.

Setup by platform

macOS: Nothing to do, works out of the box. Shows the Script Editor icon.

Linux: Should work if you already have a notification system setup. If not install libnotify:

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

For sounds, you need one of: paplay, aplay, mpv, or ffplay

Windows: Works out of the box. But heads up:

  • Only .wav files work (not mp3)
  • Use full paths like C:/Users/You/sounds/alert.wav not ~/

WSL: It's recommeneded to set customIconPath pointing to a file on Windows filesystem due to issues with path translation (can be copied from logos folder from this repository). This path will be passed down to snoretoast-*.exe

In opencode-notifier.json config:

json
  "showIcon": true,
  "customIconPath": "C:\\Users\\jhon\\Documents\\opencode-logo-dark.png",
  • If notifications are not showing up, check out: missing WSL notification

Config file

Create ~/.config/opencode/opencode-notifier.json with the defaults:

…

All options

Global options

json
{
  "sound": true,
  "notification": true,
  "bell": false,
  "timeout": 5,
  "showProjectName": true,
  "showFullPath": false,
  "showSessionTitle": false,
  "showIcon": true,
  "suppressWhenFocused": true,
  "enableOnDesktop": false,
  "notificationSystem": "osascript",
  "suppressGhosttySound": false
}
  • sound - Turn sounds on/off (default: true)
  • notification - Turn notifications on/off (default: true)
  • bell - Emit terminal BEL (\x07) on events (default: false). Behavior depends on your terminal/WM settings
  • timeout - How long notifications show in seconds, Linux only (default: 5)
  • showProjectName - Show folder name in notification title (default: true)
  • showFullPath - Show full absolute path instead of folder name in notification title and {projectName} token (default: false). When true, shows OpenCode (/home/user/projects/myapp) instead of OpenCode (myapp)
  • showSessionTitle - Include the session title in notification messages via {sessionTitle} placeholder (default: false)
  • showIcon - Show OpenCode icon, Windows/Linux only (default: true)
  • customIconPath - Path to a custom icon for notifications. Useful on WSL where Windows paths are needed (default: null)
  • suppressWhenFocused - Skip notifications and sounds when the terminal is the active window (default: true). See Focus detection for platform details
  • enableOnDesktop - Run the plugin on Desktop and Web clients (default: false). When false, the plugin only runs on CLI. Set to true if you want notifications/sounds/commands on Desktop/Web — useful if you want custom commands (Telegram, webhooks) but don't care about built-in notifications
  • notificationSystem - macOS only: "osascript", "node-notifier", or "ghostty" (default: "osascript"). Use "ghostty" if you're running Ghostty terminal for native OSC 9 notifications
  • suppressGhosttySound - macOS only: when true with notificationSystem: "ghostty", skips the plugin's sound to avoid duplicating macOS Notification Center's default sound (default: false)
  • minDuration - Suppress complete and subagent_complete notifications when session finishes faster than this many seconds (default: 0). See Minimum duration threshold
  • linux.grouping - Linux only: replace notifications in-place instead of stacking (default: false). Requires notify-send 0.8+

Events

Control each event separately:

…

user_cancelled fires when you press ESC to abort a session. It's silent by default so intentional cancellations don't trigger error alerts. Set sound or notification to true if you want confirmation when cancelling.

session_started fires when a new top-level session is created. user_message fires when a user message is submitted in a top-level session. client_connected fires shortly after the plugin initializes and is best-effort (there is no dedicated SDK connection event from plugin context).

The command property controls whether the custom command (see Custom commands) runs for that event. Defaults to true for all events. Set it to false to suppress the command for specific events without disabling it globally.

bell is terminal-driven and may be audible, visual, both, or ignored depending on your terminal setup. Quick check: printf '\a'.

Or use true/false for both:

json
{
  "events": {
    "complete": false
  }
}

Messages

Customize the notification text:

…

Messages support placeholder tokens that get replaced with actual values:

  • {sessionTitle} - The title/summary of the current session (e.g. "Fix login bug")
  • {agentName} - Subagent name extracted from session titles with (@name subagent) suffix (e.g. builder, codebase-researcher), empty for non-subagent sessions
  • {projectName} - The project folder name
  • {timestamp} - Current time in HH:MM:SS format (e.g. "14:30:05")
  • {turn} - Global notification counter that persists across restarts (e.g. 1, 2, 3). Stored in ~/.config/opencode/opencode-notifier-state.json

When showSessionTitle is false, {sessionTitle} is replaced with an empty string. Any trailing separators (: , -, |) are automatically cleaned up when a placeholder resolves to empty.

To disable session titles in messages without changing showSessionTitle, just remove the {sessionTitle} placeholder from your custom messages.

The {timestamp} and {turn} placeholders also work in custom command args.

Sounds

Use your own sound files:

json
{
  "sounds": {
    "permission": "/path/to/alert.wav",
    "complete": "/path/to/done.wav",
    "subagent_complete": "/path/to/subagent-done.wav",
    "error": "/path/to/error.wav",
    "question": "/path/to/question.wav",
    "user_cancelled": "/path/to/cancelled.wav",
    "plan_exit": "/path/to/plan-ready.wav",
    "session_started": "/path/to/session-started.wav",
    "user_message": "/path/to/user-message.wav",
    "client_connected": "/path/to/client-connected.wav"
  }
}

Platform notes:

  • macOS/Linux: .wav or .mp3 files work
  • Windows: Only .wav files work
  • If file doesn't exist, falls back to bundled sound

Volumes

Set per-event volume from 0 to 1:

json
{
  "volumes": {
    "permission": 0.6,
    "complete": 0.3,
    "subagent_complete": 0.15,
    "error": 1,
    "question": 0.7,
    "user_cancelled": 0.5,
    "plan_exit": 0.6,
    "session_started": 0.35,
    "user_message": 0.2,
    "client_connected": 0.45
  }
}
  • 0 = mute, 1 = full volume
  • Values outside 0..1 are clamped automatically
  • On Windows, playback still works but custom volume may not be honored by the default player

Custom commands

Run your own script when something happens. Use {event}, {message}, {sessionTitle}, {agentName}, {projectName}, {timestamp}, and {turn} as placeholders:

json
{
  "command": {
    "enabled": true,
    "path": "/path/to/your/script",
    "args": ["{event}", "{message}"],
    "minDuration": 10
  }
}
  • enabled - Turn command on/off
  • path - Path to your script/executable
  • args - Arguments to pass, can use {event}, {message}, {sessionTitle}, {agentName}, {projectName}, {timestamp}, and {turn} tokens
  • minDuration - Skip if response was quick, avoids spam (seconds)

Token values are passed as argv values and are not shell-escaped for use inside script source. Do not put {message}, {sessionTitle}, or other dynamic tokens inside a sh -c, bash -c, powershell -Command, or similar script string. Use a wrapper script and pass the tokens as separate arguments instead. Custom commands run with the same user permissions as OpenCode, so only enable scripts you trust.

Example: Log events to a file

json
{
  "command": {
    "enabled": true,
    "path": "/bin/bash",
    "args": [
      "-c",
      "printf '[%s] %s\\n' \"$1\" \"$2\" >> /tmp/opencode.log",
      "opencode-notifier",
      "{event}",
      "{message}"
    ]
  }
}

macOS: Pick your notification style

osascript (default): Reliable but shows Script Editor icon

json
{ 
  "notificationSystem": "osascript" 
}

node-notifier: Shows OpenCode icon but might miss notifications sometimes

json
{ 
  "notificationSystem": "node-notifier" 
}

NOTE: If you go with node-notifier and start missing notifications, just switch back or remove the option from the config. Users have reported issues with using node-notifier for receiving only sounds and no notification popups.

Ghostty notifications

If you're using Ghostty terminal, you can use its native notification system via OSC 9 escape sequences:

json
{
  "notificationSystem": "ghostty"
}

This sends notifications directly through the terminal instead of using system notification tools. Works on any platform where Ghostty is running.

macOS: Ghostty delivers notifications through macOS Notification Center, which plays its own default sound. This can result in duplicate audio with the plugin's sound effects. Set suppressGhosttySound to true to skip the plugin's sound:

json
{
  "notificationSystem": "ghostty",
  "suppressGhosttySound": true
}

Note: custom sounds configured via the sounds section still play — only default (bundled) sounds are suppressed.

If you're using Ghostty inside tmux, enable passthrough in your tmux config so OSC 9 notifications can pass through:

tmux
set -g allow-passthrough on

Then reload tmux config:

bash
tmux source-file ~/.tmux.conf

Focus detection

When suppressWhenFocused is true (the default), notifications and sounds are skipped if the terminal running OpenCode is the active/focused window. The idea is simple: if you're already looking at it, you don't need an alert.

To disable this and always get notified:

json
{
  "suppressWhenFocused": false
}

Minimum duration threshold

You can suppress complete and subagent_complete notifications for short-lived sessions. Set minDuration to the number of seconds a session must exceed to trigger a done notification:

json
{
  "minDuration": 10
}

With the above, if OpenCode finishes in under 10 seconds, no notification, sound, bell, or command is fired. Default is 0 (no threshold).

This is independent of command.minDuration, which only controls whether the custom command runs.

Platform support

Platform Method Requirements Status
macOS AppleScript (System Events) None Untested
Linux X11 `xdo

Issues· 0 open

View all issuesOpen on GitHub

No open issues yet, or sync has not completed.

> Tags

TypeScript

No comments yet. Be the first to share.

> Details

PublishedAug 1, 2026
UpdatedSep 17, 2026
Category编程语言
PricingOpen source

> Related tools

T
TypeScript
JavaScript 的超集,为前端与全栈提供静态类型
P
Python
通用编程语言,广泛用于 Web、数据与 AI
G
Go
Google 推出的简洁高效系统语言