cottongin ba656291ab feat: switch to uv, add check command, fix silent a11y failures
- Added pyobjc-framework-ApplicationServices to dependencies (was
  implicitly available via pyenv's system packages but missing in
  clean venvs)
- Added `cursor-flasher check` command that verifies Cursor is running
  and accessibility permissions are working
- Detector now logs a warning when a11y tree reads fail (previously
  failed silently, making permission issues invisible)
- Switched to uv for dependency management: `uv sync` + `uv run`
- Updated README with uv-based workflow and accessibility
  troubleshooting guide

Made-with: Cursor
2026-03-10 03:12:10 -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%