Major changes: - Add StyleConfig dataclass with independent color, width, opacity, duration, pulse_speed, sound, and volume per mode (running/completed) - Replace flat flash_*/sound_*/play_on config with running: and completed: YAML sections - Replace CGEventTap (silently fails in forked daemon) with CGEventSourceSecondsSinceLastEventType polling for reliable input-based pulse dismissal when Cursor is already frontmost - Update overlay, sound, and daemon to pass StyleConfig per call - Rewrite tests for new config shape and dismiss mechanism Made-with: Cursor
34 lines
1.9 KiB
Markdown
34 lines
1.9 KiB
Markdown
# Rewrite: Hook-based detection architecture
|
|
|
|
## Task
|
|
Replaced the unreliable macOS accessibility tree polling approach with Cursor's native hooks API for detecting when the agent needs user attention.
|
|
|
|
## Problem
|
|
The a11y-based approach had fundamental issues:
|
|
- Cursor's "Stop" button during agent generation is NOT exposed in the accessibility tree
|
|
- Approval button text persists in chat history, causing false positives
|
|
- Required complex baseline tracking and state machine to mitigate, still unreliable
|
|
|
|
## Solution
|
|
Switched to Cursor's lifecycle hooks (`preToolUse`, `stop`) which fire directly from Cursor when:
|
|
- The agent wants to use a tool (shell command, file write, etc.) that may need approval
|
|
- The agent loop completes (task finished, waiting for next prompt)
|
|
|
|
Architecture: Hook script → Unix domain socket → Daemon → Window flash overlay
|
|
|
|
## Changes
|
|
- **New**: `hooks/notify.py` — hook script that sends workspace/event info to daemon socket
|
|
- **New**: `src/cursor_flasher/windows.py` — window discovery and geometry (a11y used only for positions)
|
|
- **Rewritten**: `src/cursor_flasher/daemon.py` — Unix socket listener instead of a11y polling
|
|
- **Rewritten**: `src/cursor_flasher/overlay.py` — single brief flash instead of continuous pulse
|
|
- **Rewritten**: `src/cursor_flasher/cli.py` — added `install`/`uninstall` commands for hook management
|
|
- **Rewritten**: `src/cursor_flasher/config.py` — simplified config (no sound, no polling settings)
|
|
- **Deleted**: `detector.py`, `state.py`, `sound.py`, `scripts/` directory
|
|
- **Rewritten**: All tests for new architecture (13 tests passing)
|
|
- **Updated**: `README.md`, `.gitignore`, `pyproject.toml` (bumped to 0.2.0)
|
|
|
|
## Follow-up
|
|
- The daemon is running and hooks are installed globally at `~/.cursor/hooks.json`
|
|
- Hooks fire on every `preToolUse` — could add matcher filtering if too noisy
|
|
- No sound from cursor-flasher; relies on Cursor's built-in sound
|