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
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
- Polls Cursor's macOS accessibility tree every 500ms
- Detects agent state by looking for specific UI elements (Stop/Accept/Reject buttons)
- When the agent finishes and is waiting for input, shows a pulsing amber border around only the window(s) that need attention
- Plays a system sound (default: "Glass")
- 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:
- Open System Settings → Privacy & Security → Accessibility
- Click the + button and add your terminal app (Terminal.app, Ghostty, iTerm2, etc.)
- 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