I often leave Cursor to do some work and go off to play with my kids or something. But then I often miss the moment when it's done working, and it just hangs there waiting for me, wasting time.
The "done" chime sometimes doesn't work, and definitely doesn't work when there's a network interruption or an error, so I needed a better method.
Here's how to make Cursor send you a push notification when it's done working.
Step 1: NTFY
Register for an NTFY.sh account (optional, actually, but lets you subscribe on web too so you get desktop notifications as well).
Install NTFY onto your phone and do nothing for now.
Step 2: Set up the Cursor hooks
Create ~/.config/cursor-notify/config.json:
{
"server": "https://ntfy.sh",
"topic": "cursor_done_SOME_RANDOM_STRING"
}
Replace SOME_RANDOM_STRING with an actual, unguessable random string. This is your unique hook identifier. If anyone gets it, they can just spam your hook and blow your plan.
Then, in the NTFY app, go to Subscribe, and input this topic.
Then make a hook in ~/.cursor/hooks/notify-ntfy.py.
#!/usr/bin/env python3
import json
import os
import subprocess
from pathlib import Path
from datetime import datetime
CFG_PATH = Path.home() / ".config" / "cursor-notify" / "config.json"
def main() -> None:
# Read hook payload from Cursor
try:
payload = json.load(os.sys.stdin)
except Exception:
payload = {}
status = payload.get("status", "unknown")
conversation_id = payload.get("conversation_id", "")
loop_count = payload.get("loop_count", None)
# Load ntfy config
cfg = json.loads(CFG_PATH.read_text())
server = cfg.get("server", "https://ntfy.sh").rstrip("/")
topic = cfg["topic"]
repo = os.path.basename(os.getcwd())
now = datetime.now().strftime("%H:%M:%S")
# Optional: reduce noise (only notify on "completed")
# Cursor docs/examples commonly show status as completed/aborted/error. :contentReference[oaicite:6]{index=6}
if status != "completed":
# Still must output JSON to Cursor
print("{}")
return
msg = f"Cursor finished in {repo} at {now} (status: {status})"
if loop_count is not None:
msg += f" | loop={loop_count}"
if conversation_id:
msg += f" | convo={conversation_id}"
# Send push
# ntfy supports headers like Title/Priority/Tags and Click actions. :contentReference[oaicite:7]{index=7}
url = f"{server}/{topic}"
subprocess.run(
[
"curl", "-sS", "--max-time", "2", "--retry", "1",
"-H", "Title: Cursor Agent",
"-H", "Priority: high",
"-H", "Tags: robot_face,white_check_mark",
"-d", msg,
url,
],
check=False,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
)
# Hooks talk JSON back to Cursor
print("{}")
if __name__ == "__main__":
main()
Make it executable with chmod +x ~/.cursor/hooks/notify-ntfy.py.
Restart Cursor.
Now you will be notified whenever it's done working, and you can also find all past notifications in the app, retained for ~12 hours.