diff --git a/kitty/files/kitty.conf b/kitty/files/kitty.conf index 11cb198..58226eb 100644 --- a/kitty/files/kitty.conf +++ b/kitty/files/kitty.conf @@ -6,7 +6,7 @@ include colors.conf # END_KITTY_THEME #: Startup {{{ -shell /usr/bin/fish -l -c "zellij; exec fish -l" +shell /bin/sh -c 'FISH=/usr/bin/fish; [ -x /opt/homebrew/bin/fish ] && FISH=/opt/homebrew/bin/fish; exec $FISH -l -c "zellij; exec $FISH -l"' #: }} #: Keybindings (disable kitty tabs for Zellij) {{{ @@ -1838,7 +1838,7 @@ background_opacity 0.9 #: background_opacity. If you want to use both, you are probably #: better off just hiding the titlebar with hide_window_decorations. -# macos_option_as_alt no +macos_option_as_alt yes #: Use the Option key as an Alt key on macOS. With this set to no, #: kitty will use the macOS native Option+Key to enter Unicode diff --git a/pi/files/agent/extensions/pi-done-notify.ts b/pi/files/agent/extensions/pi-done-notify.ts index 15bc67a..8f88a01 100644 --- a/pi/files/agent/extensions/pi-done-notify.ts +++ b/pi/files/agent/extensions/pi-done-notify.ts @@ -2,7 +2,7 @@ * Pi Done Notify Extension * * Sends a native terminal notification when Pi finishes a prompt - * and is waiting for input. + * and is waiting for input. Optionally plays a sound. */ import type { ExtensionAPI } from "@mariozechner/pi-coding-agent"; @@ -74,9 +74,106 @@ function notify(title: string, body: string): void { } } +// Sound playback +function playSound(soundPath?: string): void { + const { execFile } = require("child_process"); + + if (process.platform === "darwin") { + // macOS: use afplay with system sound or custom path + const sound = soundPath || "/System/Library/Sounds/Glass.aiff"; + execFile("afplay", [sound], (error: Error | null) => { + if (error) console.error("Failed to play sound:", error); + }); + } else if (process.platform === "linux") { + // Linux: try paplay (PulseAudio), then pw-play (PipeWire), then aplay (ALSA) + const sound = + soundPath || "/usr/share/sounds/freedesktop/stereo/complete.oga"; + execFile("paplay", [sound], (error: Error | null) => { + if (error) { + execFile("pw-play", [sound], (error2: Error | null) => { + if (error2) { + execFile("aplay", [sound]); + } + }); + } + }); + } else if (process.platform === "win32") { + // Windows: use PowerShell to play system sound + const sound = + soundPath || "C:\\Windows\\Media\\Windows Notify System Generic.wav"; + execFile("powershell.exe", [ + "-NoProfile", + "-Command", + `(New-Object Media.SoundPlayer '${sound}').PlaySync()`, + ]); + } +} + +// Settings state +interface NotifySettings { + soundEnabled: boolean; + soundPath?: string; +} + +const CUSTOM_TYPE = "pi-done-notify-settings"; + export default function (pi: ExtensionAPI) { + let settings: NotifySettings = { + soundEnabled: true, + }; + + // Restore settings from session + pi.on("session_start", async (_event, ctx) => { + for (const entry of ctx.sessionManager.getEntries()) { + if (entry.type === "custom" && entry.customType === CUSTOM_TYPE) { + settings = entry.data as NotifySettings; + } + } + }); + + // Register /notify command + pi.registerCommand("notify", { + description: "Configure notification settings (sound on/off, or set sound path)", + handler: async (args, ctx) => { + const arg = args?.trim().toLowerCase(); + + if (!arg || arg === "status") { + ctx.ui.notify( + `Sound: ${settings.soundEnabled ? "on" : "off"}${settings.soundPath ? ` (${settings.soundPath})` : ""}`, + "info" + ); + return; + } + + if (arg === "on") { + settings.soundEnabled = true; + pi.appendEntry(CUSTOM_TYPE, settings); + ctx.ui.notify("Notification sound enabled", "success"); + return; + } + + if (arg === "off") { + settings.soundEnabled = false; + pi.appendEntry(CUSTOM_TYPE, settings); + ctx.ui.notify("Notification sound disabled", "info"); + return; + } + + // Treat as a sound path + settings.soundPath = args?.trim() || undefined; + pi.appendEntry(CUSTOM_TYPE, settings); + ctx.ui.notify(`Sound path set to: ${settings.soundPath}`, "success"); + }, + }); + + // Notify on agent end pi.on("agent_end", async (_event, ctx) => { if (!ctx.hasUI) return; + notify("Pi", "Done. Ready for input."); + + if (settings.soundEnabled) { + playSound(settings.soundPath); + } }); }