import { settings } from "../../config/settings"; export type TerminalBellEvent = "complete" | "approval" | "ask"; const BEL = "\x07"; function getBellSetting(key: Parameters[0]): boolean { try { return Boolean(settings.get(key)); } catch { // Some component-level tests exercise UI helpers before Settings.init(). // Terminal bells are purely best-effort, so an unavailable settings store // must not make the UI path throw. return false; } } function terminalBellEnabled(): boolean { try { // macOS terminals commonly use OSC notification protocols for background // status, while an interactive completion does not send that notification. // Make the local BEL audible by default on macOS; an explicit setting still // wins so users can disable it. return settings.has("notifications.terminalBell") ? Boolean(settings.get("notifications.terminalBell")) : process.platform === "darwin"; } catch { return false; } } function enabledForEvent(event: TerminalBellEvent): boolean { if (!terminalBellEnabled()) return false; switch (event) { case "complete": return getBellSetting("notifications.bellOnComplete"); case "approval": return getBellSetting("notifications.bellOnApproval"); case "ask": return getBellSetting("notifications.bellOnAsk"); } } export function ringTerminalBell( event: TerminalBellEvent, output: Pick = process.stdout, ): void { if (!enabledForEvent(event)) return; try { output.write(BEL); } catch { // Best-effort local notification only. } } export function classifyHookSelectorBellEvent(title: string): TerminalBellEvent { const normalized = title.toLowerCase(); if (normalized.includes("approval") || normalized.includes("approve") || normalized.includes("plan ready")) { return "approval"; } return "ask"; }