cottongin c0477d2f40 fix: prevent false positives from stale approval buttons in chat history
State machine no longer transitions directly from IDLE to WAITING_FOR_USER
on approval signals. Must see AGENT_WORKING first — this prevents stale
buttons like "Run this time only" persisting in chat history from
triggering the flash when no agent task is active.

Also removed "Continue" and "Resume" from approval keywords (too generic,
appear in normal chat text).

Made-with: Cursor
2026-03-10 03:17:34 -04:00

cursor-flasher

A macOS daemon that flashes a pulsing border around Cursor IDE windows when the AI agent is waiting for your input. Optionally plays a system sound.

Prerequisites

  • macOS
  • Python 3.10+
  • uv (recommended)
  • Accessibility permission granted to your terminal (System Settings → Privacy & Security → Accessibility)

Installation

git clone <repo-url> && cd cursor-flasher
uv sync

Usage

# Verify accessibility permissions work from your terminal
uv run cursor-flasher check

# Start the daemon (backgrounds automatically)
uv run cursor-flasher start

# Start in foreground (useful for debugging)
uv run cursor-flasher start --foreground

# Check if the daemon is running
uv run cursor-flasher status

# Stop the daemon
uv run cursor-flasher stop

How It Works

  1. Polls Cursor's macOS accessibility tree every 500ms
  2. Detects agent state by looking for specific UI elements (Stop/Accept/Reject buttons)
  3. When the agent finishes and is waiting for input, shows a pulsing amber border around only the window(s) that need attention
  4. Plays a system sound (default: "Glass")
  5. Dismisses automatically when the agent starts working again, or after a timeout

Configuration

Create ~/.cursor-flasher/config.yaml to customize:

pulse:
  color: "#FF9500"    # Border color (hex)
  width: 4            # Border thickness (px)
  speed: 1.5          # Pulse cycle duration (seconds)
  opacity_min: 0.3    # Minimum pulse opacity
  opacity_max: 1.0    # Maximum pulse opacity

sound:
  enabled: true       # Play sound on trigger
  name: "Glass"       # macOS system sound name
  volume: 0.5         # Volume (0.0 - 1.0)

detection:
  poll_interval: 0.5  # Seconds between accessibility tree polls
  cooldown: 3.0       # Seconds before re-triggering after dismissal

timeout:
  auto_dismiss: 300   # Auto-hide overlay after N seconds

All values are optional — defaults are used for anything not specified.

Troubleshooting

"Cannot read accessibility tree" / no detection

This is almost always an Accessibility permission issue. Run:

uv run cursor-flasher check

If it reports a failure, your terminal app needs Accessibility permission:

  1. Open System Settings → Privacy & Security → Accessibility
  2. Click the + button and add your terminal app (Terminal.app, Ghostty, iTerm2, etc.)
  3. Restart the terminal after granting permission

Cursor not detected

Make sure Cursor is running. The daemon identifies it by bundle ID (com.todesktop.230313mzl4w4u92).

Overlay appears on wrong windows

Detection is per-window — only windows with active approval prompts (Accept, Reject, Run, etc.) should flash. If you see false positives, the detection patterns may need tuning for your Cursor version. Use scripts/dump_a11y_tree.py to inspect what the accessibility tree looks like.

Development

# Run tests
uv run pytest tests/ -v

# Dump Cursor's accessibility tree (for debugging detection)
uv run python scripts/dump_a11y_tree.py --depth 8

# Manual overlay test (flashes all windows for 10 seconds)
uv run python scripts/test_overlay.py

# Manual overlay test (only windows needing attention)
uv run python scripts/test_overlay.py --per-window
Description
A macOS daemon that flashes a pulsing border and plays a sound when your Cursor AI agent needs attention.
Readme MIT 188 KiB
Languages
Python 100%