Claude Code on a Mac

Get a Mac notification when Claude Code is done or needs you

Add two hooks to ~/.claude/settings.json. A Notification hook runs when Claude Code is waiting on a permission prompt, and a Stop hook runs the moment it finishes a turn. Point both at osascript and macOS shows a banner. Claude Code’s own alert covers only the waiting, only in some terminals, so the hooks are the dependable route.

Last updated: October 2026

Written by the developer of Crest, 6 October 2026. Every step was run that day on a 16-inch MacBook Pro with macOS 27.0.1 and Claude Code 2.1.288; the times below come from the hooks’ own log. Crest comes last, and you do not need it for any of this.

The two hooks

Open ~/.claude/settings.json (create it if it is not there) and add this. If the file already has a hooks key, add the two events inside it rather than a second hooks.

{
  "hooks": {
    "Notification": [
      {
        "matcher": "permission_prompt",
        "hooks": [
          { "type": "command", "command": "osascript -e 'display notification \"Claude Code needs you\" with title \"Claude Code\"'" }
        ]
      }
    ],
    "Stop": [
      {
        "hooks": [
          { "type": "command", "command": "osascript -e 'display notification \"Claude Code is done\" with title \"Claude Code\" sound name \"Glass\"'" }
        ]
      }
    ]
  }
}

Restart Claude Code, or type /hooks to see both listed. The first banner appears when Claude stops to ask you something and you have not typed for six seconds. The second appears every time Claude finishes, with a sound.

Put the same block in a project’s .claude/settings.json instead if you only want it there.

What fires when, measured

We ran a real session that asked for permission once and then finished, and logged every hook with its time.

What happensHook that firesWhen it fired in our testClaude Code’s own alert
Claude asks to run a commandNotification, type permission_prompt6 seconds after the prompt appeared, as long as nobody typedYes, in Ghostty, iTerm2 and Kitty
Claude finishes its turnStopAbout 2 seconds after its last action, as the turn endedNo
You have not come backNotification, type idle_prompt60 seconds after the turn endedYes, in Ghostty, iTerm2 and Kitty

Two things that table explains. A notification that shows up a minute late is idle_prompt: it is a reminder, not a finish signal, which is why the config above uses Stop for finishing and leaves idle_prompt out with "matcher": "permission_prompt". In our run that matcher kept the 60-second reminder away. And any keystroke resets the six seconds, so you get no banner while you are typing in that terminal.

Stop also fires in claude -p runs, so a script that runs Claude Code headless gets its done banner too.

A banner that names the project

With three sessions open, “Claude Code needs you” does not say which. The hook gets the session’s details as JSON on standard input, including the folder (cwd) and, for Stop, Claude’s last message. Save this as ~/.claude/hooks/notify.sh:

#!/bin/sh
# ~/.claude/hooks/notify.sh: a macOS banner that names the project.
# Claude Code sends the hook's details as JSON on stdin.
# jq and osascript both ship with macOS.
input=$(cat)
project=$(basename "$(printf '%s' "$input" | jq -r '.cwd // empty')")
case "$(printf '%s' "$input" | jq -r '.hook_event_name')" in
  Stop) title="Claude Code is done: $project"
        text=$(printf '%s' "$input" | jq -r '.last_assistant_message // "Finished" | .[0:120]') ;;
  *)    title="Claude Code needs you: $project"
        text=$(printf '%s' "$input" | jq -r '.message // "Needs your attention"') ;;
esac
osascript -e 'on run argv' \
  -e 'display notification (item 2 of argv) with title (item 1 of argv) sound name "Glass"' \
  -e 'end run' "$title" "$text"

Then point both hooks at it:

{
  "hooks": {
    "Notification": [
      { "matcher": "permission_prompt",
        "hooks": [{ "type": "command", "command": "sh ~/.claude/hooks/notify.sh" }] }
    ],
    "Stop": [
      { "hooks": [{ "type": "command", "command": "sh ~/.claude/hooks/notify.sh" }] }
    ]
  }
}

The text goes to osascript as arguments, not pasted into the script, so a message with quotes in it cannot break the command.

A macOS notification banner from Script Editor titled Claude Code needs you: web-app, reading Claude needs your permission
A macOS notification banner titled Claude Code is done: web-app, reading Done. Created notes.txt in the current directory

The two banners from a real Claude Code 2.1.288 session on macOS 27. The first came from the Notification hook six seconds after the permission prompt appeared; the second from the Stop hook when the turn ended. web-app is the test folder's name. The icon is Script Editor's, because osascript posts through it.

Claude Code’s own alert, terminal by terminal

Claude Code also alerts by itself when it is waiting, through your terminal. We recorded exactly what it sent under each terminal type:

TerminalWhat Claude Code sent itWhat to do
GhosttyA desktop notification escape (OSC 777) with the messageNothing, if Ghostty may post notifications
iTerm2A notification escape (OSC 9) with the messageProfiles > Terminal: Notification Center Alerts, then Filter Alerts: escape sequence alerts
KittyA desktop notification, per Anthropic’s docs (not tested here)Nothing
Terminal.appNothing, with the audible bell on (the default)Set preferredNotifChannel to terminal_bell for a bell, or use the hooks
Warp, VS Code and Cursor terminalsNothingUse the hooks

Two limits. It never alerts when Claude finishes; it alerts at the permission prompt and again 60 seconds after the finish. And it only becomes a banner if the terminal turns it into one: in our test, Ghostty received the escape but no banner appeared, so check that the terminal is allowed in System Settings > Notifications. The hooks do not depend on the terminal at all, which is why this page leads with them. Hooks and the built-in alert run side by side.

Inside tmux, add set -g allow-passthrough on to ~/.tmux.conf or the built-in alert never reaches the outer terminal.

If no banner shows up

  • Script Editor is not allowed to notify. osascript posts as Script Editor, and without permission it fails silently. Run osascript -e 'display notification "test"' once in Terminal, then open System Settings > Notifications > Script Editor and turn on Allow notifications.
  • A Focus is on. Do Not Disturb hides banners; they wait in Notification Center.
  • The JSON is broken. jq . ~/.claude/settings.json prints it back if it parses, and an error with a line number if not. /hooks in Claude Code shows what it loaded.
  • You were typing. The permission_prompt notice waits for six quiet seconds. Stop does not wait.
  • You are on SSH. Hooks run where Claude Code runs, so osascript on a Linux server does nothing. The built-in terminal alert travels back over SSH to Ghostty and Kitty, and to iTerm2 with its alert setting on.

Or answer it where you are

A banner tells you to go back to the terminal. Crest, a Mac notch app, puts the request itself on the notch: which agent, the command, and Deny and Allow, on every display. It arrives the moment Claude asks, not six seconds later, because Crest’s hook runs before the prompt, and Claude’s questions come with their answers to tap.

Crest's closed notch with Claude waiting: it wants to run npm test.

The same moment on Crest’s notch. The pill names the agent and the command the moment Claude asks.

  • A chime and a macOS banner, “Claude is waiting on you”, come with it. Both are on by default, each with a switch in Settings > Coding agents.
  • Which session: with Crest Pro, the Agents widget lists every session as Waiting for you, working, or Finished 4 min ago, and one click takes you to its terminal.
  • What it does not do: Crest posts no banner when Claude finishes. Keep the Stop hook above for that; the two work side by side.
  • Permission prompts reach the notch from Manual mode sessions. In auto mode, Claude Code’s default since 2.1.283, a classifier answers most commands for you, and the notch still gets Claude’s questions.
  1. Download Crest. Signed and notarized.Free tier, no account · 7-day Pro trial, no card · macOS 14 or later
  2. Open Crest’s Settings, go to Coding agents and turn on “Allow or deny commands here” and “Answer Claude’s questions here”.

Answering from the notch is free and needs no account. Crest Pro adds the Agents widget; Pro, with Take and Minutes, is $24.99 once or $2.99 a month. More on the notch side: Claude Code in the notch. Running agents beyond Claude Code and Codex? Crest vs Vibe Island compares an app that follows thirty of them. Codex has its own notification switches: Codex CLI notifications on Mac. And if the problem is how often Claude asks, see how to make the agents ask less.

Sources

  • Anthropic, Claude Code docs, Automate actions with hooks. Read 6 October 2026. The macOS Notification hook with osascript, and the note that a missing Script Editor permission makes it fail silently.
  • Anthropic, Claude Code docs, Hooks reference. Read 6 October 2026. When permission_prompt (about six seconds) and idle_prompt (about 60 seconds) fire, that each keystroke defers them, and the Stop event with last_assistant_message.
  • Anthropic, Claude Code docs, Configure your terminal and the settings reference. Read 6 October 2026. Which terminals get a desktop notification, preferredNotifChannel and its values, iTerm2’s alert settings, tmux passthrough.
  • Our own runs, 6 October 2026, on a 16-inch MacBook Pro with macOS 27.0.1 and Claude Code 2.1.288, in throwaway folders: hook timings from the hooks’ own log, the built-in alert recorded byte for byte per terminal type, and the two banners above.
  • Crest source, read 6 October 2026: ApprovalHook.swift, ApprovalNotifier.swift, SettingsCatalog.swift, AgentSessionsReading.swift.

FAQ

How do I get a notification when Claude Code finishes?

Add a Stop hook to ~/.claude/settings.json that runs osascript -e 'display notification "Claude Code is done" with title "Claude Code"'. Stop runs every time Claude finishes a turn; in our test it fired about two seconds after Claude's last action. Claude Code's own terminal alert does not fire on finishing.

Why does my Claude Code notification arrive a minute late?

Because it is the idle_prompt notification, which Claude Code sends about 60 seconds after it finishes if you have not typed since. For an alert at the moment it finishes, use the Stop hook, and set the Notification hook's matcher to permission_prompt so the late reminder stays away.

Why does no notification appear on my Mac?

Most often Script Editor is not allowed to post notifications: osascript posts as Script Editor and fails silently without permission. Run osascript -e 'display notification "test"' once, then turn on Allow notifications for Script Editor in System Settings > Notifications. Also check Focus, and that ~/.claude/settings.json is valid JSON (jq . prints it back).

Can Claude Code play a sound when it needs input?

Yes. Add sound name "Glass" to the osascript display notification line, or make the hook command afplay /System/Library/Sounds/Glass.aiff for a sound with no banner.

How do I see which Claude Code session needs me?

The hook receives the session's folder as cwd in its JSON input, so a small script can put the folder name in the banner's title, as the notify.sh on this page does. Crest Pro's Agents widget also lists every session with its state and jumps to its terminal.

Does Claude Code notify on its own without hooks?

Yes, in Ghostty, iTerm2 and Kitty: it sends a terminal notification when a permission prompt waits and when it has been idle for about a minute. In Terminal.app it rings the bell only if you set preferredNotifChannel to terminal_bell. It never sends one when it finishes.

Do I need Crest for this?

No. The hooks on this page are all Claude Code and macOS. Crest is for answering the prompt from the notch instead of switching to the terminal, which is free.

Try Crest free

Download it with no account. Now Playing, the Shelf, the Clipboard and window snapping are free for good, and signing in gives you 7 days of Pro with no card. Pro, with Take and Minutes, is $24.99 once, on 2 Macs, or $2.99 a month.

Download Crest for macOS Get Crest Pro, $24.99

macOS 14 Sonoma or later · signed and notarized by Apple · 7-day refund

Keep reading: Codex CLI notifications on Mac · Make the agents ask less · Claude Code in the notch · Notch apps for AI agents · Facts and prices · Crest overview