anotifier / guides / cursor-agent-notifications
[ GUIDE · CURSOR ]
Cursor logo

How to get notified when the Cursor agent finishes

To get a notification when Cursor is done, try Cursor's own completion sound and OS notification first (both in Cursor's Settings), or add a stop hook to ~/.cursor/hooks.json, which Cursor runs when the agent loop ends. The sound and OS notification only reach the computer running Cursor; a hook can show your own text, push to your phone or post to Slack, and npx anotifier@latest setup writes one for you. This guide covers each option, the exact hook format, and what to check when nothing arrives.

Rather skip the manual setup? One command wires every agent you have installed to desktop toasts, phone push, and Slack, Discord or Telegram:

$ npx anotifier@latest setup

Updated 2026-10-29 · also see the Cursor overview and the full docs

Cursor's built-in completion sound and notifications

Cursor has two settings of its own, both in the app's Settings:

  • A sound. Version 0.48 added a beta option to play a sound when a chat is ready for review. The 0.48 changelog gave the path as Settings → Features → Chat → Play sound on finish. Cursor's settings have changed since, so if you don't find it there, search the settings for sound.
  • An OS notification. The 1.5 changelog says: "Get native OS notifications when an agent run finishes or when input is required." The example it gives is approving a command that is not allowlisted. It says to enable them from Settings, without naming the setting.

Both happen on the computer running Cursor and use Cursor's fixed wording. Neither reaches your phone, Slack or a second machine, and you can't change the text, so for those you need a hook (next sections). They are still the cheapest answer to "ding when it's done": try them first.

The Cursor CLI (cursor-agent) is separate. Its CLI changelog lists desktop notifications from April 2026 for a finished turn and for the agent being blocked on approvals, questions or sudo, in iTerm2, Ghostty, Warp, Kitty and Terminal.app. It doesn't say how to turn them on.

Cursor hooks by hand: the stop hook in hooks.json

Cursor hooks run a command of yours at points in the agent loop. Put them in ~/.cursor/hooks.json for every project (%USERPROFILE%\.cursor\hooks.json on Windows) or in .cursor/hooks.json inside one project. Cursor runs the matching hooks from every file and reloads the files when they change. This is the shape of the file. The command can be any shell string or script; the next section has working ones:

{
  "version": 1,
  "hooks": {
    "stop": [
      {
        "command": "./hooks/notify.sh"
      }
    ]
  }
}

What Cursor's hooks docs say about it:

  • version is 1. Each key under hooks is an event name holding a list of entries. An entry needs command; timeout (seconds), matcher and failClosed are optional.
  • stop fires when the agent loop ends. It gets JSON on stdin with status (completed, aborted or error) and loop_count, plus the fields every hook gets, among them conversation_id, generation_id, hook_event_name and workspace_roots.
  • Exit code 0 means success, 2 blocks the action, and any other code is a failure that lets the agent carry on. A notification hook that fails therefore fails silently.
  • A stop hook can print {"followup_message": "..."}, and Cursor sends that as the next message, up to loop_limit times (5 by default). A notification hook should print nothing or {}; a followup_message field would be sent as the next prompt.
  • For user hooks, relative command paths resolve from ~/.cursor/, so ./hooks/notify.sh means ~/.cursor/hooks/notify.sh. Project hooks run from the project root.
  • To debug, open the Hooks tab under Customize or the Hooks output channel. If a new hook still doesn't load, restart Cursor.
  • User-level hooks in ~/.cursor/hooks.json are not available in Cursor's cloud agents, so a stop hook there (or npx anotifier@latest setup) sends nothing for a cloud run; those agents run a project's .cursor/hooks.json (and, on Enterprise plans, team and enterprise-managed hooks), on Cursor's machine rather than yours. Hooks don't run during a cloud agent's early read-only turns.

Cursor's documented events cover prompts, tool and shell calls, file edits, subagents and sessions, but none for "waiting for your approval". For that, Cursor's own OS notification above is the option.

A stop hook for macOS, Linux and Windows

Each hooks.json below shows a banner with a fixed message and ignores the event JSON. Linux (needs notify-send, from libnotify):

{
  "version": 1,
  "hooks": {
    "stop": [
      {
        "command": "notify-send \"Cursor\" \"Agent finished\""
      }
    ]
  }
}

macOS:

{
  "version": 1,
  "hooks": {
    "stop": [
      {
        "command": "osascript -e 'display notification \"Agent finished\" with title \"Cursor\"'"
      }
    ]
  }
}

Windows. Cursor's docs don't say which shell runs a hook command, so call PowerShell explicitly and use an absolute path with forward slashes. Save the toast script from the Codex guide (it takes a title and a message, and needs no module) as C:/Users/you/.cursor/win-toast.ps1, then:

{
  "version": 1,
  "hooks": {
    "stop": [
      {
        "command": "powershell.exe -NoProfile -ExecutionPolicy Bypass -File \"C:/Users/you/.cursor/win-toast.ps1\" -Title Cursor -Message \"Agent finished\""
      }
    ]
  }
}

To put the status and project name in the message, read the event JSON in a script. This one needs jq; save it as ~/.cursor/hooks/notify.sh, run chmod +x on it, and point the hook's command at ./hooks/notify.sh as in the first example:

#!/bin/bash
input=$(cat)
status=$(echo "$input" | jq -r '.status')
project=$(basename "$(echo "$input" | jq -r '.workspace_roots[0]')")
notify-send "Cursor · $project" "Agent $status"

On macOS swap the last line for osascript -e "display notification \"Agent $status\" with title \"Cursor · $project\"". workspace_roots is the list of folders open in the window, so [0] is the first. Test it before trusting it: echo '{"status":"completed","loop_count":0,"workspace_roots":["/home/you/my-app"]}' | ~/.cursor/hooks/notify.sh.

anotifier setup for Cursor

npx anotifier@latest setup

Setup wires Cursor when a ~/.cursor folder exists. It copies hooks.json to ~/.anotifier/backups/, sets version to 1, and adds one entry under stop that runs anotifier's notify.mjs with --source cursor --event stop. Your other hooks stay; running setup again replaces anotifier's own entry instead of adding a second. Cursor reloads the file by itself.

What you get, and what you don't:

  • One alert per finished run, titled Cursor and saying Task complete, as a toast, a phone push and a webhook message (whichever you enabled). A second stop within 1.5 seconds is dropped, because Cursor can fire the hook twice in quick succession.
  • No project name. Cursor's docs list workspace_roots in the hook input, but anotifier reads cwd and workspacePaths and not workspace_roots, so every Cursor alert looks the same. With several windows open, the hand-written script above is the way to name the project.
  • No status. anotifier doesn't read status, so a run you stopped or one that ended in an error says Task complete too.
  • No click-to-focus. The Windows toast jumps to a window by project folder, and a Cursor alert has none, so there is no click action.
  • Only `stop`. There is no alert for "waiting for approval". Leave Cursor's own OS notification on for that.
  • Not driven by a live Cursor. CI checks that setup writes and removes the hook correctly; it doesn't run a real Cursor agent end to end.
FeatureBuilt-inDIY hookanotifier
Desktop toastYes, Cursor's OS notification settingYes, fixed textYes, Windows · macOS · Linux · WSL
Phone pushNoWith a curl to ntfyYes, ntfy, generic by default
Slack / Discord / TelegramNoWrite the JSON yourselfYes, pick a format
Shows what the agent saidNoNoYes for Claude Code (toast + webhook)
Names the projectNoHard-code itNo, the title is Cursor
Urgent for approvals, calm for doneNoTwo hooks, two scriptsNo, finished runs only
Click to focus the windowNoNoNo
Every agent, one configNoRepeat per agentYes
Snooze / quiet hoursNoNoYes
Setup time1 setting10–20 min per agentOne command

anotifier is free, open source (AGPL-3.0), has zero dependencies, and uninstalls cleanly with anotifier uninstall. Full reference in the docs.

More on the Cursor page, and what setup writes for every agent in Agent hooks explained.

Phone push (ntfy) and Slack when Cursor finishes

By hand, a hook can post to ntfy. Install the ntfy app, subscribe to a topic nobody could guess (anyone who knows a public topic name can read it), and use it here:

{
  "version": 1,
  "hooks": {
    "stop": [
      {
        "command": "curl -s -d \"Cursor agent finished\" ntfy.sh/your-secret-topic"
      }
    ]
  }
}

With anotifier, answer yes to the ntfy question during setup. It creates a random topic, you subscribe to it in the app, and anotifier test ntfy should buzz your phone. The push is titled Cursor with the body Task complete. The full walkthrough is in the phone push guide.

For Slack, create an incoming webhook and put it in ~/.anotifier/config.json. Every Cursor finish then posts one message to the channel:

{
  "webhook": {
    "enabled": true,
    "url": "https://hooks.slack.com/services/...",
    "format": "slack"
  }
}

See the Slack page for the webhook steps. Discord and Telegram use a different format; the Discord page has the webhook steps for a Discord channel.

Cursor notifications on Windows and WSL

  • Windows. The user hooks file is %USERPROFILE%\.cursor\hooks.json. Run setup in PowerShell, not in WSL. Setup also checks for PowerShell 7 (pwsh) and installs the BurntToast module for your user, so a toast arrives with the Cursor icon and a Windows notification sound. If pwsh is missing it prints winget install --id Microsoft.PowerShell --source winget. anotifier test toast confirms the toast works.
  • Hand-written hooks on Windows can't use notify-send. Use the PowerShell toast above, and if nothing appears check that Do not disturb (Focus Assist) is off.
  • WSL. Setup only looks in the home folder of the shell it runs in. If you run Cursor on Windows, run setup in PowerShell on Windows. If you run Cursor's agent from inside WSL, run it inside WSL, where the toast goes out through Windows interop. Cursor's hooks docs don't say which side runs a hook when a window is connected to WSL, so check with a real run: if the alert doesn't arrive, run anotifier status on both sides to see which one has the hook.

Details for both are in Claude Code and Codex notifications on Windows and WSL and Windows toast from WSL. Cursor is a VS Code fork, and the VS Code page covers what changes when the agent runs inside the editor, including Remote-SSH and dev containers.

Cursor notification not showing? Fixes by symptom

SymptomLikely causeWhat to do
Nothing at all after a runThe hook isn't in the file Cursor reads, or didn't loadRun anotifier status and look for Cursor IDE wired. Open the Hooks tab under Customize and the Hooks output channel for errors; restart Cursor if the hook still doesn't load
No alert from a cloud agentCloud agents don't read your ~/.cursor/hooks.json, where anotifier setup writes its hookAdd a stop hook to the project's .cursor/hooks.json that sends something off the cloud machine, such as the ntfy curl hook above. A toast or sound plays on the cloud machine, not on yours, and anotifier's own hook doesn't run there. The file is committed, so anyone who can read the repo can read the topic or webhook URL in it. Or use the push notification the Cursor mobile app sends when an agent finishes a turn
anotifier test toast works, real runs are silentThe problem is between Cursor and anotifierCheck ~/.cursor/hooks.json has a stop entry, then check anotifier status for snooze and quiet hours, which silence every channel on purpose
The Hooks output channel says node was not foundanotifier's hook command starts with node, so Cursor's hook environment has to find NodeStart Cursor from a terminal where node -v works, or put Node on the system PATH, and restart Cursor
My own hook never shows a bannerThe command exits with a code other than 0, and Cursor lets the agent carry on without telling youRun it by hand from ~/.cursor with a sample payload: echo '{"status":"completed","loop_count":0,"workspace_roots":["/home/you/my-app"]}' | ./hooks/notify.sh, and fix whatever it prints
Two notifications for one runThe same hook is in ~/.cursor/hooks.json and in a project's .cursor/hooks.json; Cursor runs bothKeep it in one file. anotifier also drops a second stop within 1.5 seconds, but a hand-written hook doesn't
Task complete after I stopped the agent or it erroredanotifier doesn't read the event's statusUse the script above, which prints completed, aborted or error
Toast works, phone doesn'tntfy skipped in setup, or a wrong topic in the appanotifier test ntfy, then the phone push troubleshooting

The order of checks that works for any agent is in the troubleshooting guide.

Turn off Cursor notifications

To stopDo this
Cursor's own sound or OS notificationTurn off the matching option in Cursor's Settings (see the first section)
A hand-written hookDelete its entry from hooks.json; Cursor reloads the file
anotifier for a whileanotifier snooze 2h silences every channel until it expires; anotifier snooze off cancels it. Quiet hours (quietHours.enabled in ~/.anotifier/config.json, off by default) do the same every night
anotifier's Cursor alert onlyDelete the stop entry whose command contains --source cursor. Running setup again puts it back
anotifier for goodnpx anotifier@latest uninstall

Cursor notification questions

How do I get a notification when Cursor is done?

Two routes. First, Cursor's own settings: Cursor 0.48 added a beta option to play a sound when a chat is ready for review (its changelog gave the path as Settings > Features > Chat > Play sound on finish; if it has moved, search Cursor's settings for sound), and Cursor 1.5 added native OS notifications for a finished agent run, enabled from Settings. Second, a stop hook in ~/.cursor/hooks.json, which Cursor runs when the agent loop ends. npx anotifier@latest setup writes that hook for you and sends a desktop toast, phone push or webhook message titled Cursor that says Task complete.

Does Cursor have a built-in notification when the agent finishes?

Yes, two. Cursor 0.48 added an option to play a sound when a chat is ready for review, and Cursor 1.5 added native OS notifications for a finished agent run or when input is required. Both stay on the computer running Cursor and use Cursor's own wording; for a phone push, Slack or your own text, add a stop hook.

How do I get a Cursor notification on my phone when the agent is done?

Add a stop hook to ~/.cursor/hooks.json that posts to ntfy, for example curl -s -d "Cursor agent finished" ntfy.sh/your-secret-topic, and subscribe to that topic in the ntfy app. npx anotifier@latest setup does the same with a random private topic and also covers Slack, Discord and Telegram.

What is the Cursor stop hook and what does it receive?

stop is the hooks.json event that fires when the agent loop ends. Its stdin JSON carries status (completed, aborted or error) and loop_count, plus common fields such as conversation_id, generation_id and workspace_roots. Exit 0 and print nothing or {} for a plain notification; a followup_message field would be sent as the next prompt.

Why did I get two Cursor notifications for one run?

Cursor runs the matching hooks from every hooks.json, so the same hook in ~/.cursor and in a project's .cursor folder fires twice. Cursor can also fire a hook twice in quick succession for one run; anotifier de-duplicates a repeat of the same event within 1.5 seconds, but a hand-written hook needs its own guard.

Does the Cursor CLI (cursor-agent) notify when it finishes?

Cursor's CLI changelog lists desktop notifications from April 2026 for a finished turn and for approvals, questions and sudo prompts, in iTerm2, Ghostty, Warp, Kitty and Terminal.app, and it added hooks in January 2026. anotifier's tests don't run the Cursor CLI, so whether its stop hook fires there is untested.

How do I turn Cursor notifications off?

Turn off the sound and notification options in Cursor's Settings for the built-in ones, delete a hand-written entry from hooks.json, run anotifier snooze 2h to pause anotifier, or run npx anotifier@latest uninstall to remove it from every agent.

Try it

One command wires Cursor and every other agent you have installed.

npx anotifier@latest setup

Other guides