Files
cursor-flasher/chat-summaries/2026-03-10_02-57-summary.md
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

2.3 KiB

cursor-flasher: Full Implementation

Date: 2026-03-10 Branch: feat/cursor-flasher → merged to master (14 commits)

Task

Build a macOS daemon that monitors Cursor IDE's accessibility tree and shows a pulsing border overlay + plays a sound when the AI agent is waiting for user input.

Changes Made

Core Modules (src/cursor_flasher/)

  • config.py — Dataclass-based config with YAML loading from ~/.cursor-flasher/config.yaml
  • state.py — State machine: IDLE → AGENT_WORKING → WAITING_FOR_USER with cooldown logic
  • detector.py — Per-window accessibility tree scanning; detects approval prompts (Accept, Reject, Run, etc.) and agent-working indicators (Stop, Cancel) via AXStaticText values and AXButton titles
  • overlay.py — Native macOS overlay using NSWindow + PulseBorderView with sine-wave opacity animation; reads position directly from AXWindow elements
  • sound.py — System sound playback via NSSound
  • daemon.py — Main loop wiring detection → state machine → overlay + sound; NSRunLoop-based event loop
  • cli.pycursor-flasher start/stop/status CLI with PID file management and fork-based daemonization

Dev Tools (scripts/)

  • dump_a11y_tree.py — Dumps Cursor's full accessibility tree for debugging detection patterns
  • test_overlay.py — Manual overlay test with --per-window flag

Tests (31 passing)

  • test_config.py (7), test_state.py (10), test_detector.py (12), test_sound.py (2)

Key Decisions & Discoveries

  • Cursor's bundle ID is com.todesktop.230313mzl4w4u92 (not matchable by app name alone)
  • Electron web content exposes UI as AXStaticText values, not AXButton titles
  • CGWindowList returns empty names for Electron windows; position tracking uses AXWindow AXPosition/AXSize via AXValueGetValue instead
  • Per-window detection: only the specific window(s) with active approval prompts get flashed
  • pyproject.toml build-backend corrected from plan's setuptools.backends._legacy:_Backend to setuptools.build_meta
  • NSRect.insetBy doesn't exist in pyobjc; used NSInsetRect instead

Follow-up Items

  • Detection patterns may need tuning as Cursor UI evolves
  • No menu bar icon (out of MVP scope)
  • No auto-start on login
  • No multi-monitor awareness beyond what CGWindow/AXWindow coordinates provide