DeepDojo.AIPublished 2026-11-12中文

Get a Desktop Alert When Claude Code Needs You

Claude CodeAI Coding

You hand Claude Code a task and switch to your browser to look something up. Twenty minutes later you come back and find it stopped almost right away at a permission prompt, waiting for you to click "allow," with nothing else done.

When Claude Code needs you, the terminal shows a prompt, but you won't see it if you're not looking at the terminal. A desktop notification set up through a Hook fixes that: when Claude is waiting for permission, or has finished replying and is waiting for your next message, an alert pops up in the corner of your screen.

The configuration is only a dozen lines of JSON. A few details are where it tends to go wrong:

  • notifications are a personal habit, so they belong in user settings, not the team repository;
  • listen only for "waiting for permission" and "waiting for the next message," so it doesn't pop up for everything;
  • call the notification program directly with args to avoid shell quoting trouble;
  • idle_prompt takes a while to fire, so right after setup, not seeing it may just mean the time hasn't come yet.

Below is a complete macOS configuration, followed by how to confirm it actually fires.

01 Put It in User Settings

Notifications are a personal work habit, so they suit ~/.claude/settings.json. Team-shared .claude/settings.json handles project automation, and personal experiments go in .claude/settings.local.json. Something like a desktop notification, which is only about how you work, belongs in user settings, where it applies to every project and doesn't bother your teammates.

The Claude Code hooks guide opens with exactly this kind of desktop notification: it goes in ~/.claude/settings.json, uses osascript to show the alert, and the same page has the Linux and Windows commands.

02 A Complete macOS Configuration

This macOS configuration listens only for permission prompts and waits for the next message:

{
  "hooks": {
    "Notification": [
      {
        "matcher": "permission_prompt|idle_prompt",
        "hooks": [
          {
            "type": "command",
            "command": "osascript",
            "args": [
              "-e",
              "display notification \"Claude Code needs your attention\" with title \"Claude Code\""
            ],
            "timeout": 10
          }
        ]
      }
    ]
  }
}

The notification tool depends on the operating system and desktop permissions. Test it in a regular terminal first, then connect it to the Hook.

The two values in the Matcher cover two situations. The Claude Code hooks reference describes them like this:

Notification TypeWhen It Fires
permission_promptClaude needs you to approve a tool use, and the prompt has waited about six seconds
idle_promptClaude finished responding about 60 seconds ago and you haven't typed since

The Notification event has other types too, such as successful authentication and form requests. With only these two in the Matcher, the alert appears only in these two cases.

03 Why Call It Directly with args

This notification only needs to start osascript with arguments, so it uses exec form with args. Each string goes straight to the program without the shell splitting words, which makes paths and quotes easy to check. If you later need pipes, redirects, or &&, switch to shell form without args. Decide what the command needs before choosing how to call it.

The hooks reference draws the line this way: with args set, the command runs in exec form; without it, in shell form. The docs recommend setting args whenever a Hook references a path placeholder, because each element is passed as one argument with no quoting needed, and omitting it when you need shell features like pipes or &&.

timeout is in seconds, set to 10 here. The notification program starts far faster than that; it just leaves some margin.

04 How to Confirm It Actually Fires

After saving, check the event, Matcher, source, and command in /hooks, then trigger a permission request and see whether the alert appears. Valid JSON is one thing; you only know the chain works once it actually fires.

One thing makes testing easy to misjudge. The docs explain that these notification types share their timing with desktop notifications, so in terminal sessions you only see them when you appear to be away from the terminal:

  • permission_prompt waits until you haven't typed for about six seconds, and each keystroke pushes it back. To run a Hook the instant Claude asks for permission, use the PermissionRequest event instead.
  • idle_prompt fires about 60 seconds after Claude finishes responding, only if you haven't typed and no background subagent is still running.

So when testing, trigger a permission request and take your hands off the keyboard for a bit. If idle_prompt doesn't pop up right away, that doesn't mean it's broken. If it still doesn't appear after waiting long enough, work through registration, triggering, input, and result, as described in Claude Code Hook not firing? Check four layers.

05 Notifications Can Run in the Background

Command handlers like notifications and logging, which don't affect the current decision, can add "async": true so the foreground keeps going:

{
  "type": "command",
  "command": "osascript",
  "args": ["-e", "display notification \"Claude Code needs your attention\" with title \"Claude Code\""],
  "async": true
}

The hooks reference explains async this way: by default, hooks block Claude until they complete; with async, the hook runs in the background while Claude keeps working, but its response fields have no effect, so it can't block or control Claude. A notification never needs to block anything, so the background suits it well. A Hook that intercepts operations has to stay synchronous.

On Linux or Windows, swap osascript for your system's notification tool, and again confirm it shows an alert in a regular terminal before wiring it into the Hook. Once it's set up, you can hand off a task and go do something else, and the corner of your screen will tell you when Claude needs you.