28 Commits

Author SHA1 Message Date
cottongin
392183692e Disable shell execution hooks to fix broken notifications
beforeShellExecution in hooks.json required a JSON response we never
provided, likely causing Cursor to silently break the entire hook
pipeline. Commenting out those entries (and afterShellExecution) from
HOOKS_CONFIG restores reliable preToolUse/postToolUse delivery. All
Python handler code is retained as dead code for reference.

Also reverts the is_cursor_frontmost() gate in _check_pending — pulses
should fire unconditionally when the approval delay expires.

Made-with: Cursor
2026-03-11 15:24:43 -04:00
cottongin
6610919a58 Show overlay on all Spaces and alongside fullscreen apps
Set canJoinAllSpaces + fullScreenAuxiliary on overlay windows so the
border renders regardless of which Space or fullscreen app is active.
In window mode, fall back to screen-edge border when Cursor isn't
frontmost to avoid a floating rectangle on other Spaces.

Made-with: Cursor
2026-03-11 03:07:12 -04:00
cottongin
23fe6ac101 Keep NSScreen list current across sleep/wake and display changes
Register for NSApplicationDidChangeScreenParametersNotification and
NSWorkspaceDidWakeNotification so the daemon refreshes NSScreen.screens()
after external monitors connect/disconnect or the system wakes from sleep.

Made-with: Cursor
2026-03-11 03:06:47 -04:00
cottongin
730f6ec1cf Add AI-assistance disclaimer to README
Made-with: Cursor
2026-03-10 14:30:31 -04:00
cottongin
5fc378e558 Fix per-workspace pulse dismiss for multi-window setups
Interacting with one Cursor window no longer dismisses overlays from
other windows. The overlay manager now tracks panels by tag (workspace),
the daemon maintains per-workspace pending/active/cooldown state, and
dismiss logic identifies the focused window via AXFocusedWindow to
target only that workspace's pulse.

Made-with: Cursor
2026-03-10 09:33:42 -04:00
cottongin
a3203e2970 Add dark/light theme support with real-time OS appearance detection
Config now uses top-level dark/light sections (each with running/completed
styles) and a theme option ("dark", "light", "auto"). The daemon resolves
the active theme at flash time via NSApp.effectiveAppearance().

Made-with: Cursor
2026-03-10 09:06:09 -04:00
cottongin
49c03e4b71 Add design doc for dark/light theme support
Defines the config format, data model, and daemon integration
approach for theme-aware styling.

Made-with: Cursor
2026-03-10 08:34:43 -04:00
cottongin
eefb908268 Tidy repo for public release
- Add MIT LICENSE
- Polish README: tagline, permissions docs, clone URL
- Add license, authors, readme, and repository URL to pyproject.toml
- Remove stale docs/plans/ (relocated to .cursor/) and requirements.txt
- Deduplicate and clean up .gitignore

Made-with: Cursor
2026-03-10 08:03:22 -04:00
cottongin
d71bac7b93 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
2026-03-10 07:40:21 -04:00
cottongin
5b71b2275b 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
2026-03-10 07:01:52 -04:00
cottongin
c0477d2f40 fix: prevent false positives from stale approval buttons in chat history
State machine no longer transitions directly from IDLE to WAITING_FOR_USER
on approval signals. Must see AGENT_WORKING first — this prevents stale
buttons like "Run this time only" persisting in chat history from
triggering the flash when no agent task is active.

Also removed "Continue" and "Resume" from approval keywords (too generic,
appear in normal chat text).

Made-with: Cursor
2026-03-10 03:17:34 -04:00
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
cottongin
1a5de8cf8a docs: add README with installation and usage instructions
Made-with: Cursor
2026-03-10 02:57:31 -04:00
cottongin
a5ca7f5d33 fix: tune detection patterns and add state transition logging
- Added "Resume" and "Continue" to approval keywords
- Added state transition logging to daemon for observability
- Guarded signal handler against duplicate SIGTERM delivery
- Verified end-to-end: daemon detects approval prompt, transitions
  to waiting_for_user, overlays 1 window, plays sound

Made-with: Cursor
2026-03-10 02:57:01 -04:00
cottongin
b31f39268e feat: per-window detection — only flash windows needing attention
Detector now walks each AXWindow subtree independently and returns
both aggregate signals (for state machine) and a list of AXWindow
element refs for windows with active approval signals.

Overlay reads position/size directly from AXWindow elements via
AXValueGetValue, eliminating the CGWindowList dependency (which
returned empty names for Electron windows anyway).

Daemon passes only the active AXWindow refs to the overlay, so
only the specific window(s) waiting for user input get flashed.

Made-with: Cursor
2026-03-10 02:54:15 -04:00
cottongin
bce6ec39f8 feat: add CLI with start/stop/status commands
Made-with: Cursor
2026-03-10 02:46:38 -04:00
cottongin
bcd8d4da1a feat: add main daemon loop wiring detection, overlay, and sound
Made-with: Cursor
2026-03-10 02:45:17 -04:00
cottongin
3cbe529b7a feat: add system sound alert module
Made-with: Cursor
2026-03-10 02:44:45 -04:00
cottongin
c1d10efe7b feat: support multiple Cursor windows in overlay
Renamed OverlayWindow to OverlayManager. Now discovers all on-screen
windows for the Cursor PID and creates/reuses/hides overlay windows
dynamically to match. Filters out tiny windows (<100px).
Verified: detects 3 windows across monitors.

Made-with: Cursor
2026-03-10 02:44:04 -04:00
cottongin
f967575ebc feat: add native macOS overlay window with pulsing border
Uses NSWindow + PulseBorderView with Core Animation-style pulse.
Fixed NSRect handling from plan: uses NSInsetRect instead of
non-existent .insetBy method on pyobjc tuples.

Made-with: Cursor
2026-03-10 02:39:30 -04:00
cottongin
f4cbfb997e feat: add accessibility-based agent state detector
Adapted from plan to match real a11y tree structure: Electron web
content exposes in-app buttons as AXStaticText values, not AXButton
titles. Detects via exact matches and regex patterns. Uses correct
Cursor bundle ID for process lookup.

Made-with: Cursor
2026-03-10 02:37:59 -04:00
cottongin
2cd48e03f8 feat: add accessibility tree explorer script for development
Findings: Cursor uses bundle ID com.todesktop.230313mzl4w4u92.
Electron web content exposed as AXStaticText values within AXWebArea,
not AXButton titles. Detector must search AXStaticText elements.

Made-with: Cursor
2026-03-10 02:36:51 -04:00
cottongin
ec9f434cb3 feat: add state machine for agent activity detection
Made-with: Cursor
2026-03-10 02:28:16 -04:00
cottongin
38fdab9594 chore: add .gitignore and remove cached files from tracking
Made-with: Cursor
2026-03-10 02:26:40 -04:00
cottongin
db94c25b44 feat: add configuration module with YAML loading and defaults
Made-with: Cursor
2026-03-10 02:26:11 -04:00
cottongin
0c48edb5f7 feat: project scaffolding with pyproject.toml and package structure
Made-with: Cursor
2026-03-10 02:23:24 -04:00
cottongin
25a775ff4e Add cursor-flasher implementation plan
11 bite-sized tasks covering scaffolding, config, state machine,
accessibility detection, overlay, sound, daemon, CLI, and testing.

Made-with: Cursor
2026-03-10 02:13:26 -04:00
cottongin
6b3390f89d Add cursor-flasher design document
Defines the approach for a macOS daemon that monitors Cursor's
accessibility tree and shows a pulsing border overlay when the
agent is waiting for user input.

Made-with: Cursor
2026-03-10 02:03:53 -04:00