Blog · · Dimitris Kyriakakis

Get a notification on your Mac when Claude Code needs you or finishes

The whole point of handing a task to a coding agent is that you can look at something else. Then you look back and find it has been sitting on a permission prompt the whole time.

So you want a ping when the agent needs you, and another one when it's done. Claude Code and Codex can both do that on macOS, with a bit of setup.

The built-in setting

Claude Code sends a desktop notification on its own, but by default only in Ghostty, Kitty and iTerm2. The setting is Local notifications in /config, and in settings.json it's called preferredNotifChannel:

{
  "preferredNotifChannel": "terminal_bell"
}

The values are auto (the default), terminal_bell, iterm2, iterm2_with_bell, kitty, ghostty and notifications_disabled. In the macOS Terminal the bell is the one that works.

A Notification hook for a real banner

If you want a proper macOS banner in any terminal, a hook does it. The Notification hook runs whenever Claude Code sends a notification, and we'll point it at osascript, in ~/.claude/settings.json:

{
  "hooks": {
    "Notification": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "osascript -e 'display notification \"Claude Code needs your attention\" with title \"Claude Code\"'"
          }
        ]
      }
    ]
  }
}
  1. An empty matcher runs it for every notification type. permission_prompt and idle_prompt are the 2 you'll see most.
  2. The hook receives the notification as JSON on stdin, with message, notification_type and an optional title, so a small script can put the real message on the banner instead of a fixed line.
  3. If your settings already have a hooks key, add Notification next to the events that are there rather than replacing the object.

⚠️ osascript notifications are posted as Script Editor. If Script Editor isn't allowed to notify in System Settings → Notifications, the hook fails silently and you never see a thing.

Keep in mind when these fire. A permission_prompt notification comes after the prompt has waited about 6 seconds, and idle_prompt comes about 60 seconds after Claude finished and you haven't typed. For "it's done", the Stop hook runs as soon as Claude finishes responding, and you can hang the same osascript line on it.

Codex

Codex has a notify setting in ~/.codex/config.toml that runs a command of yours with a JSON payload:

notify = ["python3", "/path/to/notify.py"]

Today it fires on agent-turn-complete only, so it tells you a turn finished and nothing about approvals. Codex's own terminal notifications can cover approval requests as well, through its tui.notifications setting.

Where a notification stops being enough

Set up like this, you know when something is waiting. Then you still go and find the window, read the prompt and answer it there, and with a few sessions open that is most of the effort. A banner that only says "needs your attention" doesn't tell you whether it's a harmless npm test or a git push --force.

That gap is what AgentNod fills on my Mac. Its notifications name the session and what it wants, for example "Wants to run a command · read-only", and Allow, Deny and Deny with a note are buttons on the banner, so a lot of prompts never need the terminal at all. A destructive command gets no Allow on the banner, so you read it on the card first. A finished session posts a banner with the first sentence of the agent's last message, and Focus and quiet hours hold all of them back.

If you are on one agent in iTerm2 or Ghostty, the built-in setting is probably all you need. Start there, add the hook if your terminal stays quiet, and see how many banners you actually act on in a day.