/** * Pi Done Notify Extension * * Sends a native terminal notification when Pi finishes a prompt * and is waiting for input. */ import type { ExtensionAPI } from "@mariozechner/pi-coding-agent"; function windowsToastScript(title: string, body: string): string { const type = "Windows.UI.Notifications"; const mgr = `[${type}.ToastNotificationManager, ${type}, ContentType = WindowsRuntime]`; const template = `[${type}.ToastTemplateType]::ToastText01`; const toast = `[${type}.ToastNotification]::new($xml)`; return [ `${mgr} > $null`, `$xml = [${type}.ToastNotificationManager]::GetTemplateContent(${template})`, `$xml.GetElementsByTagName('text')[0].AppendChild($xml.CreateTextNode('${body}')) > $null`, `[${type}.ToastNotificationManager]::CreateToastNotifier('${title}').Show(${toast})`, ].join("; "); } function notifyOSC777(title: string, body: string): void { process.stdout.write(`\x1b]777;notify;${title};${body}\x07`); } function notifyOSC99(title: string, body: string): void { process.stdout.write(`\x1b]99;i=1:d=1;${title}\x1b\\`); process.stdout.write(`\x1b]99;i=1:p=body;${body}\x1b\\`); } const DEFAULT_DURATION_MS = 5000; function notifyWindows(title: string, body: string): void { const { execFile } = require("child_process"); execFile("powershell.exe", [ "-NoProfile", "-Command", windowsToastScript(title, body), ]); } function notifyDesktop( title: string, body: string, iconPath: string, durationMs = DEFAULT_DURATION_MS ): void { const { execFile } = require("child_process"); execFile( "notify-send", ["-t", `${durationMs}`, "-i", iconPath, title, body], (error: Error | null) => { if (error) { notifyOSC777(title, body); } } ); } const ICON_PATH = "/home/thomasgl/.pi/agent/extensions/assets/pi-logo.svg"; function notify(title: string, body: string): void { if (process.platform === "linux") { notifyDesktop(title, body, ICON_PATH); return; } if (process.env.WT_SESSION) { notifyWindows(title, body); } else if (process.env.KITTY_WINDOW_ID) { notifyOSC99(title, body); } else { notifyOSC777(title, body); } } export default function (pi: ExtensionAPI) { pi.on("agent_end", async (_event, ctx) => { if (!ctx.hasUI) return; notify("Pi", "Done. Ready for input."); }); }