From 164b01ad6e324ab674b8ad41026a12692d978b75 Mon Sep 17 00:00:00 2001 From: Glauber Brennon Date: Mon, 22 Jun 2026 22:51:25 -0300 Subject: [PATCH 1/3] feat(notifier): add package.json with node-notifier dependency --- plugins/notifier/package.json | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 plugins/notifier/package.json diff --git a/plugins/notifier/package.json b/plugins/notifier/package.json new file mode 100644 index 00000000..484047e9 --- /dev/null +++ b/plugins/notifier/package.json @@ -0,0 +1,18 @@ +{ + "name": "notifier", + "version": "0.0.0", + "private": true, + "type": "module", + "description": "Cross-platform OS notifications when a Cline run completes, using a strategy pattern for different notification systems.", + "dependencies": { + "node-notifier": "^10.0.1" + }, + "cline": { + "plugins": [ + { + "paths": ["./index.ts"], + "capabilities": ["hooks"] + } + ] + } +} From a32d11171c71b5d01e576f89a4fe1c96d4ed62d3 Mon Sep 17 00:00:00 2001 From: Glauber Brennon Date: Mon, 22 Jun 2026 22:51:32 -0300 Subject: [PATCH 2/3] feat(notifier): implement cross-platform OS notification plugin using strategy pattern --- plugins/notifier/index.ts | 176 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 176 insertions(+) create mode 100644 plugins/notifier/index.ts diff --git a/plugins/notifier/index.ts b/plugins/notifier/index.ts new file mode 100644 index 00000000..0f395e54 --- /dev/null +++ b/plugins/notifier/index.ts @@ -0,0 +1,176 @@ +/** + * Notifier Plugin + * + * Cross-platform OS notifications when a Cline run completes. + * Uses a strategy pattern where notification strategies are named after + * their respective OS notification systems: + * - NotificationCenter (macOS) + * - NotifySend (Linux) + * - WindowsToaster (Windows) + * + * CLI usage: + * cline plugin install notifier + * cline -i "Run the test suite" + */ + +import type { AgentPlugin, AgentRunResult } from "@cline/core"; + +// --------------------------------------------------------------------------- +// NotifierStrategy interface -- strategy names correspond to OS notification +// systems supported by the node-notifier library. +// --------------------------------------------------------------------------- + +interface NotifierStrategy { + /** Human-readable name of the notification system (e.g. "NotificationCenter"). */ + readonly systemName: string; + + /** Whether this notifier is supported on the current platform. */ + isSupported(): boolean; + + /** Send a notification. No-op if unsupported. */ + notify(title: string, message: string): void; +} + +// --------------------------------------------------------------------------- +// Concrete strategies -- one per supported OS notification system. +// --------------------------------------------------------------------------- + +/** macOS Notification Center (native alerts on darwin). */ +class NotificationCenterNotifier implements NotifierStrategy { + readonly systemName = "NotificationCenter"; + + isSupported(): boolean { + return process.platform === "darwin"; + } + + notify(title: string, message: string): void { + if (!this.isSupported()) { + return; + } + // Use dynamic import so node-notifier is only required at runtime on macOS. + import("node-notifier") + .then((notifier) => { + notifier.notify({ title, message, sound: true }, (err: Error | null) => { + if (err) { + console.error(`[notifier] NotificationCenter error: ${err.message}`); + } + }); + }) + .catch((err: unknown) => { + console.error(`[notifier] Failed to load node-notifier: ${String(err)}`); + }); + } +} + +/** Linux desktop notifications via notify-send / libnotify. */ +class NotifySendNotifier implements NotifierStrategy { + readonly systemName = "NotifySend"; + + isSupported(): boolean { + return process.platform === "linux"; + } + + notify(title: string, message: string): void { + if (!this.isSupported()) { + return; + } + import("node-notifier") + .then((notifier) => { + const n = + typeof notifier.NotificationCenter === "function" + ? new notifier.NotificationCenter() + : notifier; + n.notify({ title, message, sound: true }, (err: Error | null) => { + if (err) { + console.error(`[notifier] NotifySend error: ${err.message}`); + } + }); + }) + .catch((err: unknown) => { + console.error(`[notifier] Failed to load node-notifier: ${String(err)}`); + }); + } +} + +/** Windows toast notifications (Windows 8+ / Windows 10 / Windows 11). */ +class WindowsToasterNotifier implements NotifierStrategy { + readonly systemName = "WindowsToaster"; + + isSupported(): boolean { + return process.platform === "win32"; + } + + notify(title: string, message: string): void { + if (!this.isSupported()) { + return; + } + import("node-notifier") + .then((notifier) => { + const n = + typeof notifier.WindowsToaster === "function" + ? new notifier.WindowsToaster() + : notifier; + n.notify({ title, message, sound: true }, (err: Error | null) => { + if (err) { + console.error(`[notifier] WindowsToaster error: ${err.message}`); + } + }); + }) + .catch((err: unknown) => { + console.error(`[notifier] Failed to load node-notifier: ${String(err)}`); + }); + } +} + +// --------------------------------------------------------------------------- +// Strategy resolver -- picks the right notifier for the current platform. +// --------------------------------------------------------------------------- + +const strategies: NotifierStrategy[] = [ + new NotificationCenterNotifier(), + new NotifySendNotifier(), + new WindowsToasterNotifier(), +]; + +function resolveNotifier(): NotifierStrategy | undefined { + return strategies.find((s) => s.isSupported()); +} + +// --------------------------------------------------------------------------- +// Helpers +// --------------------------------------------------------------------------- + +function summarizeResult(result: AgentRunResult): string { + const summary = result.outputText.trim(); + if (summary.length > 0) { + return summary; + } + return `Completed in ${result.iterations} iteration(s).`; +} + +// --------------------------------------------------------------------------- +// Plugin definition +// --------------------------------------------------------------------------- + +const plugin: AgentPlugin = { + name: "notifier-on-complete", + manifest: { + capabilities: ["hooks"], + }, + + hooks: { + afterRun({ result }) { + if (result.status !== "completed") { + return; + } + const notifier = resolveNotifier(); + if (!notifier) { + return; + } + notifier.notify("Cline session completed", summarizeResult(result)); + }, + }, +}; + +export { plugin }; +export default plugin; From 589fefcb71994c8a4e3873c805eb62fbee82476a Mon Sep 17 00:00:00 2001 From: Glauber Brennon Date: Mon, 22 Jun 2026 22:51:36 -0300 Subject: [PATCH 3/3] docs(notifier): add README with install and usage instructions --- plugins/notifier/README.md | 45 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 plugins/notifier/README.md diff --git a/plugins/notifier/README.md b/plugins/notifier/README.md new file mode 100644 index 00000000..f1e63c22 --- /dev/null +++ b/plugins/notifier/README.md @@ -0,0 +1,45 @@ +# notifier + +Sends a cross-platform OS notification when a Cline run completes. + +## What It Does + +Registers an `afterRun` hook that sends a native desktop notification on run completion. Uses a strategy pattern with notification systems: + +- `NotificationCenter` -- macOS native alerts +- `NotifySend` -- Linux desktop notifications (libnotify) +- `WindowsToaster` -- Windows toast notifications (Windows 8+) + +The appropriate strategy is automatically selected based on the current platform. + +## Install + +```bash +cline plugin install notifier +``` + +For local development from this repository: + +```bash +cline plugin install ./plugins/notifier --cwd . +``` + +## Example Usage + +After installation, ask Cline: + +```text +Run the test suite and let me know when the task is complete. +``` + +When the Cline run completes, `notifier` sends a native desktop notification with a short completion summary. + +## Requirements + +- macOS, Linux, or Windows. +- Linux requires `libnotify-bin` or equivalent (`notify-send` command). +- No API keys or external services. + +## Security Notes + +Notification text can include task output. Avoid enabling it if completion summaries may contain sensitive information.