@leo000001/opencode-notify-native
v1.4.1
Published
OpenCode plugin for direct native notification center alerts
Maintainers
Readme
opencode-notify-native
Direct native notification plugin for OpenCode.
What this repository ships
- npm package:
@leo000001/opencode-notify-native
Repository layout
src/: plugin source codedist/: build output used by npm package entrypoints- repository root
package.json: publishable package@leo000001/opencode-notify-native - repository root also contains docs and CI workflows
Features
- Native notifications on Windows, macOS, Linux
- Automatic event hooks:
completeerrorattention
- Defensive event payload parsing (raw + wrapped hook envelopes)
- Per-event sound profile
- Notification anti-spam controls (collapse + cooldown)
- Basic text sanitization and truncation
Notification signal policy
- Notify only terminal, user-actionable events.
- Do not notify non-terminal progress states; specifically,
session.statuswithbusy/retryis always ignored. - Treat user-initiated interrupt/cancel/abort flows as no-notify outcomes.
session.status idleis considered complete only after a recent active status for the same session.- Legacy
session.idleduplicates are suppressed whensession.status idlehas already been seen. - Complete notifications are held briefly and canceled if a
session.errorarrives right after idle, preventing false "Completed" after failures/aborts. - Idle transitions right after non-abort errors are suppressed to avoid "Error + Completed" double noise.
- Attention notifications are emitted only for unresolved prompts (
permission.asked, unresolved legacypermission.updated, andquestion.asked). - Permission and question prompts are briefly delayed and canceled if the same request is resolved immediately, so auto-approved or instantly answered flows stay quiet by default.
- Acknowledgement events (
permission.replied,question.replied,question.rejected) and legacyquestion.updatedare ignored. - When event semantics are unclear, the plugin prefers no-notify and relies on debug logs for observation.
Runtime compatibility
- This package is ESM-only (
"type": "module").
Notification content
- Title format:
OpenCode · <Session Title or Project> - Body first line:
<Completed|Error|Attention> · <summary> - Optional body lines:
Project Dir: <shortened worktree path>(controlled byshowDirectory)Session ID: <first 8 chars>(controlled byshowSessionId, default off)
Install
From npm
Add to opencode.json:
{
"$schema": "https://opencode.ai/config.json",
"plugin": ["@leo000001/opencode-notify-native"]
}opencode.json vs tui.json
- Put plugin registration in
opencode.json(plugin: [...]). - Use
tui.jsononly for terminal UI preferences/keymaps. - This plugin is loaded from OpenCode runtime config, not from TUI-only config.
Local development install
{
"$schema": "https://opencode.ai/config.json",
"plugin": [
"file:///ABSOLUTE/PATH/TO/plugins/opencode-notify-native/dist/index.js"
]
}Use your own absolute path, but always point to this entry file:
.../opencode-notify-native/dist/index.js
Notes:
dist/is generated. Runnpm ciandnpm run buildbefore using the file URL (and re-run after making changes).
Optional config
Recommended global config:
~/.config/opencode/notify-native.config.json- Windows fallback:
%APPDATA%\\opencode\\notify-native.config.json
Optional project overrides:
<worktree>/notify-native.config.json<directory>/notify-native.config.json(when different fromworktree)<worktree>/.opencode/notify-native.config.json<directory>/.opencode/notify-native.config.json(when different fromworktree)
Note: OpenCode provides both worktree (project root) and directory (current working directory). In monorepos they can differ; this plugin checks both locations.
Compatibility names still supported:
opencode-native-notify.config.jsonopencode-notify.config.json
Resolution order (low -> high):
- Global config (
~/.config/opencode/...) <worktree>/...<directory>/...<worktree>/.opencode/...<directory>/.opencode/...OPENCODE_NOTIFY_NATIVE_CONFIG(if set)
Values are layered; later sources override earlier ones.
{
"enabled": true,
"events": {
"complete": true,
"error": true,
"attention": true
},
"soundByEvent": {
"complete": true,
"error": "error",
"attention": "attention"
},
"collapseWindowMs": 3000,
"cooldownMs": 30000,
"sanitize": true,
"maxBodyLength": 200,
"showDirectory": false,
"showSessionId": false
}Notes:
sanitize: trueenables best-effort redaction of token-like substrings (for exampleBearer ...).- Regardless of
sanitize, the plugin normalizes whitespace, strips control characters, and clamps lengths to keep notification backends stable. - If you set
sanitize: false, notifications may include secrets from tool output or error messages. showDirectorydefaults tofalseto reduce lock-screen/path leakage. Enable it only if directory context is worth the privacy tradeoff.
Data files
- This plugin does not persist state files.
- No queue/status bridge is used.
Platform notes
- Windows: notifications depend on system notification settings and Focus Assist.
- Windows sender label: defaults to Explorer to keep click behavior stable. Set
OPENCODE_NOTIFY_NATIVE_WINDOWS_SENDER=terminalto prefer Windows Terminal app IDs. - Windows command launch is hardened for
.cmd/shim-heavy environments (including CI) with retry and shell fallbacks. - macOS: uses
osascript(display notification) only. This backend cannot replace/group notifications at the OS level. - Linux: requires
notify-send(for examplelibnotify-binon Debian/Ubuntu). Backend delivery falls back throughlong -> short -> plain -> minimalargument modes for compatibility. - Linux sound:
notify-sendhas no standard sound support; this plugin can only best-effort play sounds whencanberra-gtk-playis available. - Sender identity is platform-defined: macOS
osascriptdoes not support setting the sender to the current terminal, Windows sender is tied to the selected AUMID, and Linux can only provide a best-effort app name (opencode).
Debugging
If a config file exists but is ignored (for example due to invalid JSON), this plugin falls back to the last successfully loaded config (built-in defaults if none).
- Non-ENOENT config load failures emit a one-time warning to stderr.
- Unknown config keys emit a one-time warning and are ignored.
- Backend command spawn failures also emit one-time warnings to stderr.
- Set
OPENCODE_NOTIFY_NATIVE_DEBUG=1for detailed debug logs (including full error messages and ignored event traces). - Config is loaded once at plugin initialization (no hot-reload during a running session).
If you are testing and expect a banner for every completion, note the defaults:
collapseWindowMscollapses bursts into one notification.cooldownMssuppresses repeats for the same session/event.- Auto-resolved permission/question requests are suppressed by default, so
attentionnotifications usually surface only for prompts that still need user action.
Implementation note:
collapseWindowMsis a fixed window starting at the first event for a given key (it does not extend on each subsequent event).- Collapse timers are
unref()'d, so a last pending collapsed notification can be dropped if OpenCode exits before the window fires.
Click behavior
- The plugin keeps click behavior as best-effort no-op.
- The plugin does not register any custom click action.
- On macOS
osascript, custom click actions are not supported; click behavior is controlled by Notification Center and varies by system settings. - On Linux and Windows, click handling still depends on the OS notification service and cannot be guaranteed as strict no-op in every environment.
- It is intentionally not designed to jump/focus the originating terminal/editor window.
- If you opt into
OPENCODE_NOTIFY_NATIVE_WINDOWS_SENDER=terminal, click behavior is still best-effort no-op but depends on Windows Terminal activation handling.
Build and test
npm install
npm run build
npm run typecheck
npm testOptional local integration check on macOS (sends a real notification):
OC_NOTIFY_NATIVE_INTEGRATION=1 npm testRelease
- CI and release workflows publish only the npm plugin.
- Tag push (
v*) runs build/typecheck/test/pack and optionally publishes to npm whenNPM_TOKENis configured. - Before release, ensure your local worktree is clean (
git status) so unpublished local edits do not skew manual verification.
Design and maintenance docs
- Runtime design:
DESIGN.md - Maintainer guardrails:
AGENTS.md
