From d71bac7b93b04439ab0c79b4924d94e23d65096e Mon Sep 17 00:00:00 2001 From: cottongin Date: Tue, 10 Mar 2026 07:40:21 -0400 Subject: [PATCH] Remove tracked files now covered by .gitignore Add .pytest_cache/, .cursor/, and chat-summaries/ to .gitignore and untrack the previously committed chat-summaries. Made-with: Cursor --- .gitignore | 4 +++ chat-summaries/2026-03-10_02-57-summary.md | 40 ---------------------- chat-summaries/2026-03-10_03-59-summary.md | 33 ------------------ chat-summaries/2026-03-10_04-08-summary.md | 19 ---------- chat-summaries/2026-03-10_06-33-summary.md | 18 ---------- 5 files changed, 4 insertions(+), 110 deletions(-) delete mode 100644 chat-summaries/2026-03-10_02-57-summary.md delete mode 100644 chat-summaries/2026-03-10_03-59-summary.md delete mode 100644 chat-summaries/2026-03-10_04-08-summary.md delete mode 100644 chat-summaries/2026-03-10_06-33-summary.md diff --git a/.gitignore b/.gitignore index c6c1681..03e2b99 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,7 @@ build/ .venv/ a11y_dump.txt agent-tools/ +.pytest_cache/ +.cursor/ +chat-summaries/ + diff --git a/chat-summaries/2026-03-10_02-57-summary.md b/chat-summaries/2026-03-10_02-57-summary.md deleted file mode 100644 index 1098a89..0000000 --- a/chat-summaries/2026-03-10_02-57-summary.md +++ /dev/null @@ -1,40 +0,0 @@ -# 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.py** — `cursor-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 diff --git a/chat-summaries/2026-03-10_03-59-summary.md b/chat-summaries/2026-03-10_03-59-summary.md deleted file mode 100644 index b3cd9bf..0000000 --- a/chat-summaries/2026-03-10_03-59-summary.md +++ /dev/null @@ -1,33 +0,0 @@ -# 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 diff --git a/chat-summaries/2026-03-10_04-08-summary.md b/chat-summaries/2026-03-10_04-08-summary.md deleted file mode 100644 index 87b82ca..0000000 --- a/chat-summaries/2026-03-10_04-08-summary.md +++ /dev/null @@ -1,19 +0,0 @@ -# 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 | diff --git a/chat-summaries/2026-03-10_06-33-summary.md b/chat-summaries/2026-03-10_06-33-summary.md deleted file mode 100644 index 8b912f5..0000000 --- a/chat-summaries/2026-03-10_06-33-summary.md +++ /dev/null @@ -1,18 +0,0 @@ -# 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