- 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
112 lines
3.2 KiB
Markdown
112 lines
3.2 KiB
Markdown
# 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](https://docs.astral.sh/uv/) (recommended)
|
|
- **Accessibility permission** granted to your terminal (System Settings → Privacy & Security → Accessibility)
|
|
|
|
## Installation
|
|
|
|
```bash
|
|
git clone <repo-url> && cd cursor-flasher
|
|
uv sync
|
|
```
|
|
|
|
## Usage
|
|
|
|
```bash
|
|
# 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:
|
|
|
|
```yaml
|
|
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:
|
|
|
|
```bash
|
|
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
|
|
|
|
```bash
|
|
# 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
|
|
```
|