-
-
Notifications
You must be signed in to change notification settings - Fork 376
Description
Bug: macOS desktop notifications silently fail — osascript display notification swallowed when terminal app lacks notification permissions
GSD Version
2.40+ (affects all versions using notifications.js)
Affected area
Auto-mode / Notifications
What happened?
GSD auto-mode notifications (milestone complete, budget warnings, blockers, errors) never appear on macOS despite notifications.enabled: true in preferences.
The root cause: notifications.js uses osascript -e 'display notification ...' on macOS. This command exits 0 (no error) but the notification is silently dropped by macOS if the calling terminal app (e.g. Ghostty, iTerm2, Alacritty, Kitty, Warp) doesn't have notification permissions in System Settings → Notifications.
Most terminal apps don't appear in the Notifications settings panel until they've successfully delivered at least one notification — creating a chicken-and-egg problem where the permission entry never shows up because notifications are silently swallowed before they can register.
Steps to reproduce
- Use any third-party terminal (Ghostty, iTerm2, etc.) that doesn't have notification permissions enabled
- Set
notifications.enabled: truein GSD preferences - Run
/gsd autoon a milestone - Wait for milestone completion or budget threshold
- No notification appears — no error logged, no indication of failure
Verify manually:
# This exits 0 but produces no visible notification:
osascript -e 'display notification "test" with title "GSD" sound name "Glass"'
# This works because terminal-notifier registers as its own app:
brew install terminal-notifier
terminal-notifier -title "GSD" -message "test" -sound GlassExpected behavior
Desktop notifications should be delivered reliably when enabled in preferences.
Proposed fix
Prefer terminal-notifier when available on macOS, fall back to osascript:
// In buildDesktopNotificationCommand() for platform === "darwin":
// 1. Check for terminal-notifier first (registers as its own Notification Center app)
const tnPath = which("terminal-notifier"); // or execFileSync("which", ["terminal-notifier"])
if (tnPath) {
const args = ["-title", title, "-message", message];
if (level === "error") args.push("-sound", "Basso");
else args.push("-sound", "Glass");
return { file: "terminal-notifier", args };
}
// 2. Fall back to osascript (works if Terminal.app/Script Editor has notification perms)terminal-notifier (brew install terminal-notifier) registers itself as a standalone app in Notification Center, so it gets its own permission entry on first use — macOS prompts the user to allow/deny, which is the expected UX.
Workaround
Go to System Settings → Notifications and enable notifications for your terminal app (e.g. Ghostty). If your terminal doesn't appear in the list, it has never successfully delivered a notification — you'd need to use Terminal.app once to register "Script Editor", or install terminal-notifier.
Environment
- macOS Sequoia (15.x)
- Ghostty terminal
- GSD notifications.enabled: true, all sub-toggles true
- cmux.enabled: false (falls through to osascript path)