Skip to content

Instantly share code, notes, and snippets.

@sahmett
Created July 4, 2026 22:21
Show Gist options
  • Select an option

  • Save sahmett/1b19f803ed261defdf709455a816df9a to your computer and use it in GitHub Desktop.

Select an option

Save sahmett/1b19f803ed261defdf709455a816df9a to your computer and use it in GitHub Desktop.
Claude Code → native macOS notifications with sound (zero tokens)

Claude Code → macOS Notifications (with sound, zero tokens)

A small set of hooks that post native macOS Notification Center alerts — with sound — when Claude Code finishes a task, waits for your approval, hits an error, or compacts its context. Works everywhere, including the VSCode integrated terminal.

Why a hook? Claude Code's built-in preferredNotifChannel setting only shows desktop notifications in iTerm2 / Ghostty / Kitty — VSCode and most other terminals aren't on that list. A hook bypasses the terminal entirely and posts straight to macOS.

Zero tokens / zero cost. These are "type": "command" hooks — they run a plain shell command and send nothing to the model (LLM). No API call → no tokens → no cost. (Only prompt and agent hook types consume tokens; none are used here.)


Setup

1. Add the hooks

Open ~/.claude/settings.json and add the hooks block below.

⚠️ Do NOT replace the whole file — MERGE with your existing settings. If you already have a hooks field, add these events inside your existing hooks object.

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "osascript -e 'display notification \"Task complete\" with title \"Claude Code\" sound name \"Glass\"'"
          }
        ]
      }
    ],
    "Notification": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "osascript -e 'display notification \"Waiting for your input\" with title \"Claude Code\" sound name \"Ping\"'"
          }
        ]
      }
    ],
    "StopFailure": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "osascript -e 'display notification \"Something went wrong\" with title \"Claude Code\" sound name \"Basso\"'"
          }
        ]
      }
    ],
    "PreCompact": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "osascript -e 'display notification \"Compacting context\" with title \"Claude Code\" sound name \"Purr\"'"
          }
        ]
      }
    ]
  }
}

2. Grant macOS notification permission (IMPORTANT — this is where most people get stuck)

osascript notifications are attributed to the "Script Editor" app. If that app has never delivered a notification, macOS won't list it under Notifications and the alerts get silently dropped (especially on macOS 15 / 26). Classic chicken-and-egg.

Fix — run this once in your terminal:

open -a "Script Editor"
osascript -e 'display notification "test" with title "Claude Code"'

Then: System Settings → Notifications → Script Editor → "Allow Notifications". It works permanently after that; you can close the Script Editor window.

3. Reload the hooks

After editing the settings file, run /hooks once in Claude Code (reloads the config) or restart.


Events and sounds

Event Fires when Message Sound
Stop Claude finishes a turn Task complete Glass
Notification Waiting for approval / attention Waiting for your input Ping
StopFailure A turn ends with an error Something went wrong Basso
PreCompact Context is auto-compacted Compacting context Purr

Distinct sounds → you can tell which one fired without looking.

Change the sound: edit the sound name "..." part. Built-in system sounds live in /System/Library/Sounds/: Basso, Blow, Bottle, Frog, Funk, Glass, Hero, Morse, Ping, Pop, Purr, Sosumi, Submarine, Tink

Change the message: edit the text inside the first pair of quotes.


Security notes

  • These hooks only show a local notification — they read no files, make no network calls, and require no admin (sudo) privileges. The messages are static strings, so there's no command-injection surface (no external data is interpolated into the command).
  • command hooks run a shell command. Always read what a hook does before pasting it from any source — this is general Claude Code hygiene, not specific to this snippet.
  • Don't share your whole settings.json; share only the hooks block. The file may later contain permission rules, env vars, or credential helpers.

Requirements

  • macOS (osascript is built in; nothing to install)
  • Claude Code
  • Troubleshooting: if nothing appears, check Focus / Do Not Disturb first — it suppresses both the banner and the sound while active.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment