Restructure config for per-mode style/sound and fix pulse dismiss
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
This commit is contained in:
33
chat-summaries/2026-03-10_03-59-summary.md
Normal file
33
chat-summaries/2026-03-10_03-59-summary.md
Normal file
@@ -0,0 +1,33 @@
|
||||
# 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
|
||||
19
chat-summaries/2026-03-10_04-08-summary.md
Normal file
19
chat-summaries/2026-03-10_04-08-summary.md
Normal file
@@ -0,0 +1,19 @@
|
||||
# Tweaks: approval filtering + two flash modes
|
||||
|
||||
## Task
|
||||
Added tool filtering and differentiated flash behavior for approval vs. completion events.
|
||||
|
||||
## Changes
|
||||
- **`config.py`**: Added `approval_tools` list (default: Shell, Write, Delete), `sound_name`/`sound_volume`, `pulse_speed`
|
||||
- **`overlay.py`**: Two modes — `pulse()` with sine-wave animation + auto-dismiss when Cursor is focused, `flash()` for brief single flash
|
||||
- **`daemon.py`**: Routes `preToolUse` to pulse+sound (filtered by approval_tools), `stop` to brief flash; stop dismisses active pulse
|
||||
- **`sound.py`**: Re-added for approval pulse events
|
||||
- **Tests**: 19 tests covering tool filtering, both flash modes, cooldown, fallback behavior
|
||||
- **`README.md`**: Documented both behaviors and config options
|
||||
|
||||
## Behavior
|
||||
| Event | Tool in approval_tools? | Action |
|
||||
|-------|------------------------|--------|
|
||||
| preToolUse | Yes (Shell, Write, Delete) | Pulse border + play sound until user clicks window |
|
||||
| preToolUse | No (Read, Grep, etc.) | Ignored |
|
||||
| stop | N/A | Brief single flash |
|
||||
18
chat-summaries/2026-03-10_06-33-summary.md
Normal file
18
chat-summaries/2026-03-10_06-33-summary.md
Normal file
@@ -0,0 +1,18 @@
|
||||
# Per-mode Style and Sound Config
|
||||
|
||||
## Task
|
||||
Restructure the cursor-flasher config to have independent visual style and sound settings for "running" (approval pulse) and "completed" (agent stop flash), replacing the shared flat config and `play_on` field.
|
||||
|
||||
## Changes
|
||||
|
||||
- **`src/cursor_flasher/config.py`** — Added `StyleConfig` dataclass (`color`, `width`, `opacity`, `duration`, `pulse_speed`, `sound`, `volume`). `Config` now has `running: StyleConfig` and `completed: StyleConfig` with sensible defaults. Removed flat `flash_*`, `sound_*`, and `play_on` fields. Updated `load_config` to parse `running:` and `completed:` YAML sections.
|
||||
- **`src/cursor_flasher/overlay.py`** — `flash()` and `pulse()` accept a `StyleConfig` parameter. `OverlayManager.__init__` no longer takes `Config`.
|
||||
- **`src/cursor_flasher/sound.py`** — `play_alert()` accepts `StyleConfig` instead of `Config`. Skips playback when `sound` is empty.
|
||||
- **`src/cursor_flasher/daemon.py`** — Passes `config.running` to pulse/play_alert, `config.completed` to flash/play_alert. Removed `play_on` conditionals. `OverlayManager()` instantiated without args.
|
||||
- **`tests/test_config.py`** — Rewritten for new config structure (13 tests).
|
||||
- **`tests/test_daemon.py`** — Updated overlay call assertions to include StyleConfig arg. Replaced `play_on` tests with per-style sound tests. Added `test_custom_colors_per_mode`. 40/40 pass.
|
||||
- **`README.md`** — Updated config docs to show `running:` / `completed:` YAML structure.
|
||||
|
||||
## Follow-up
|
||||
- Restart daemon to pick up new config structure
|
||||
- Update user's `~/.cursor-flasher/config.yaml` if it uses the old `flash:`/`sound:` format
|
||||
Reference in New Issue
Block a user