EveryNotify is a lightweight, zero-dependency notification plugin for opencode designed to keep you informed about your AI-driven development sessions. It dispatches notifications to multiple services simultaneously, ensuring you stay updated on task progress, errors, and interaction requests, even when you're not actively monitoring your terminal.
In long-running development tasks or deep research sessions, it's common to switch focus while opencode works in the background. EveryNotify bridges the gap between your terminal and your preferred notification device, allowing you to react quickly when opencode requires input or has finished its work.
- Multitasking Efficiency: Walk away from your desk while opencode processes complex requests.
- Immediate Awareness: Get notified the instant an error occurs or a permission is requested.
- Centralized Logs: Use Slack, Discord, or Telegram as a secondary history of your opencode sessions.
- Universal Reach: Works across desktop and mobile through native service apps.
- ✅ 4 Notification Services: Native support for Pushover, Telegram, Slack, and Discord.
- ✅ Automatic Event Detection: Notifies on session completion, idle states, errors, and questions.
- ✅ Rich Session Meta: Notifications include the project name, session ID, and total elapsed time.
- ✅ Intelligent Delay-and-Replace: Holds notifications for a configurable delay (default: 120s), replacing duplicates with the latest. Only the final notification sends after activity settles.
- ✅ Fault Tolerance: Isolated service calls ensure that a failure in one provider doesn't block others.
- ✅ Zero Runtime Dependencies: Built entirely on standard Node.js APIs and native
fetch(). - ✅ Privacy & Control: Completely opt-in; no notifications are sent until you enable and configure a service.
- ✅ Event Filtering: Selectively enable or disable notifications for specific event types.
- ✅ Rich Content: Notifications include the actual assistant response text for better context.
- ✅ Truncation Direction: Choose whether to keep the beginning or end of long messages when they exceed service limits.
Install EveryNotify into your opencode environment using npm:
npm install @sillybit/opencode-plugin-everynotifyopencode does not auto-load npm packages; you must register the plugin in your opencode config so it is loaded at startup.
Add EveryNotify to the plugin array in your opencode config. opencode will install and load it automatically (using Bun) when you run opencode.
Global config (all projects): edit ~/.config/opencode/opencode.json:
{
"plugin": ["@sillybit/opencode-plugin-everynotify"]
}Project config (single project): add or edit opencode.json in your project root:
{
"plugin": ["@sillybit/opencode-plugin-everynotify"]
}You can list multiple plugins in the same array, e.g. ["@sillybit/opencode-plugin-everynotify", "opencode-wakatime"].
Alternatively, place the plugin in opencode’s plugin directory so it is loaded as a local plugin:
- Project-only:
.opencode/plugins/(e.g. symlink or copy fromnode_modules/@sillybit/opencode-plugin-everynotify) - All projects:
~/.config/opencode/plugins/
Files in these directories are loaded automatically; no plugin entry in opencode config is required for them.
After the plugin is loaded, configure your notification services (see Configuration) and optionally add a .everynotify.json for per-project overrides.
EveryNotify utilizes a simple JSON configuration file named .everynotify.json. The plugin aggregates configuration from two potential scopes:
- Global Configuration:
~/.config/opencode/.everynotify.jsonUse this for your default tokens and webhook URLs across all projects. - Project Configuration:
.opencode/.everynotify.json(inside your project directory) Use this to override settings or redirect notifications for a specific repository.
Create your .everynotify.json with the tokens for the services you want to use. You can enable multiple services at once.
{
"pushover": {
"enabled": true,
"token": "KzG789...your_app_token",
"userKey": "uQi678...your_user_key",
"priority": 0
},
"telegram": {
"enabled": true,
"botToken": "123456:ABC-DEF...your_bot_token",
"chatId": "987654321"
},
"slack": {
"enabled": false,
"webhookUrl": "https://hooks.slack.com/services/T000/B000/XXXX"
},
"discord": {
"enabled": false,
"webhookUrl": "https://discord.com/api/webhooks/0000/XXXX"
},
"log": {
"enabled": true,
"level": "warn"
},
"events": {
"complete": true,
"subagent_complete": true,
"error": true,
"permission": false,
"question": true
},
"truncateFrom": "end",
"delay": 120
}You can control which events trigger notifications by adding an events block to your .everynotify.json. All events are enabled (true) by default to ensure backward compatibility.
| Event | Description |
|---|---|
complete |
The main opencode session has finished its task and is now idle. |
subagent_complete |
A subagent has completed its specific assigned task. |
error |
A fatal error or crash occurred during the session. |
permission |
opencode is waiting for you to grant permission for a tool or file access. |
question |
The question tool was used to ask you for clarification. |
Each notification service has a maximum message length (e.g., Pushover: 1024 chars, Discord: 2000 chars). When a message exceeds the limit, EveryNotify truncates it and adds ... as an indicator.
The truncateFrom option controls which part of the message is kept:
| Value | Behavior | Best For |
|---|---|---|
"end" |
Keep beginning, trim end (default) | When the start of the message has the context |
"start" |
Keep end, trim beginning | When the conclusion/result at the end matters |
Global setting applies to all services:
{
"truncateFrom": "start"
}Per-service override — for example, keep message endings on Pushover but beginnings on Slack:
{
"truncateFrom": "end",
"pushover": {
"enabled": true,
"token": "...",
"userKey": "...",
"truncateFrom": "start"
}
}Service-level truncateFrom takes priority over the global setting.
EveryNotify includes an intelligent delay-and-replace system that consolidates rapid notifications into a single, final message. When a notification is triggered, it's held for a configurable delay period (default: 120 seconds). If another notification of the same event type arrives during this window, the old one is cancelled and replaced with the new one, and the timer resets. Only the final notification sends after activity settles.
This feature prevents notification floods when opencode sends multiple messages in quick succession while still working.
How it works:
- Delayed events:
complete,subagent_complete, andquestionevents are held for the configured delay - Immediate events:
errorandpermissionevents bypass the delay and send immediately (they're time-sensitive) - Replacement logic: Each event type has its own independent timer. New events of the same type replace pending ones
Configuration:
| Setting | Description | Default |
|---|---|---|
"delay": 120 |
Delay in seconds before sending | 120 |
"delay": 0 |
Disable feature (send immediately) | - |
Example:
{
"delay": 120,
"pushover": {
"enabled": true,
"token": "...",
"userKey": "..."
}
}Known Limitation:
If the opencode process exits (terminal closed, crash, etc.) while notifications are pending, they will not be sent. This is an acknowledged tradeoff — the delay feature prioritizes reducing noise over delivery guarantees. A future version may add process exit flushing when opencode provides a shutdown hook.
EveryNotify provides rich context in its notifications by extracting the assistant's last response. Instead of generic "Task completed" messages, you receive the actual summary or answer provided by opencode.
Notification Priority:
- Errors: The specific error message always takes top priority.
- Assistant Text: The actual text from the assistant's final response.
- Fallback: If no text is found, it defaults to a generic "Task completed" message.
Message Format Example:
[complete] my-project
I've successfully implemented the authentication system with JWT tokens and refresh token rotation. The API endpoints are secured and tests are passing. (elapsed: 2m 18s)
EveryNotify includes an optional file-based logging system to help you troubleshoot service delivery or configuration issues without cluttering your terminal.
{
"log": {
"enabled": true,
"level": "warn"
}
}Options:
enabled: Set totrueto activate file-based logging (default:false).level: The minimum severity to log."error": Only records failed service dispatches and critical system errors."warn": Records errors plus non-critical warnings like missing configuration files (default).
Log File Details:
- Location:
~/.config/opencode/.everynotify.log - Format:
[ISO-8601] [LEVEL] [EveryNotify] Message - Rotation: Logs are automatically rotated every 7 days (based on file modification time).
- Cleanup: EveryNotify maintains a maximum of 4 rotated files (e.g.,
.everynotify.log.2026-02-01), automatically deleting the oldest.
Example Entry:
[2026-02-08T14:30:45.123Z] [ERROR] [EveryNotify] Service dispatch failed: Pushover: Network timeout
[2026-02-08T14:30:45.456Z] [WARN] [EveryNotify] Config file not found at ~/.config/opencode/.everynotify.json
EveryNotify is a "fire-and-forget" plugin. Once you have registered it with opencode and configured at least one service with "enabled": true, it runs automatically. opencode loads the plugin at startup; EveryNotify then listens for events and sends notifications in the background with no further action needed.
When an event is triggered, EveryNotify builds a rich message containing the event type, project name, the actual assistant response, and the total elapsed time.
Example:
[complete] my-project: I've finished the refactor. (elapsed: 12m 30s)
Notifications are dispatched to all services marked as "enabled": true.
| Service | Requirements | Recommended For | Setup Link |
|---|---|---|---|
| Pushover | App Token, User Key | High-priority mobile alerts | Pushover API |
| Telegram | Bot Token, Chat ID | Instant messaging & groups | Telegram Bots |
| Slack | Webhook URL | Team collaboration & logging | Slack Webhooks |
| Discord | Webhook URL | Community & server-based alerts | Discord Webhooks |
The plugin monitors several key lifecycle hooks within opencode:
complete: Dispatched when a task is finished and the session becomes idle. Also triggers when a subagent completes its assigned task.error: Dispatched if the session crashes or encounters a fatal execution error. Includes the error message in the notification.permission: Dispatched when opencode pauses to ask for tool execution permissions or file access.question: Dispatched when opencode uses thequestiontool to seek clarification from the user.
For Pushover users, you can customize the priority level:
-2: Lowest priority (no notification)-1: Quiet notification0: Normal priority (default)1: High priority (bypasses quiet hours)2: Emergency priority (requires acknowledgment)
See Message Truncation under Configuration Options for details on the truncateFrom setting. This is especially useful for services with tight message limits like Pushover (1024 chars), where the end of a message often contains the most important information (the result or conclusion).
EveryNotify automatically calculates the time elapsed since the first user message in a session. This duration is included in the notification text to give you context on how long the task took to complete.
If you are working on a sensitive or client-specific project, you can place a .everynotify.json file in the project's .opencode/ directory to send notifications to a specific Slack channel or Telegram group, bypassing your global configuration.
Developers looking to extend EveryNotify or add new service providers should consult AGENTS.md.
The project uses Bun for lightning-fast development:
- Install Dependencies:
bun install - Build Plugin:
bun run build - Run Tests:
bun test - Type Check:
bun run typecheck
We welcome contributions of all kinds!
- Bug Reports: Open an issue describing the bug and your environment.
- Feature Requests: Propose new services or event hooks via issues.
- Pull Requests: Follow the existing code style (2-space indent, strict TypeScript) and ensure all tests pass.
Distributed under the MIT License. See LICENSE for more information.
Created by Sillybit — Pixel Perfect Innovation.
Built with ❤️ for the opencode community.