Cursor Sound Rotator
A zero-dependency macOS LaunchAgent that rotates Cursor IDE's single completion-chime setting through a whole sound pack, without patching or modifying Cursor itself.
Project Brief
- Role
- Solo developer
- Scope
- A complete, tested macOS LaunchAgent utility with installer, uninstaller, and pack-generation tooling
- Timeline
- Built in 2026
- Result
- A zero-dependency, fully tested (10/10) utility shipped as a polished open-source project rather than a personal script
Evidence Included
Full test suite passing
10 of 10 automated tests pass, covering unit behavior, install/rotate/restore integration, generated-audio validity, and LaunchAgent plist validation.
Zero external dependencies
The rotator, installer, uninstaller, and pack generator run entirely on the Python 3 standard library — no pip packages required.
Zero (stdlib only)
Dependencies
MP3/WAV/OGG/M4A/AAC/FLAC
Formats supported
10 / 10 passing
Test suite
Cursor can play a custom sound when an agent finishes or needs attention, but its settings only accept one fixed file — there's no built-in way to rotate between multiple sounds. This project solves that without touching Cursor's application bundle, injecting code, or changing its signature: it keeps Cursor's one configured sound path in place and safely replaces that file's contents with a different clip from a pack each time playback is detected. Built entirely on the Python 3 standard library and shipped as a small, fully tested macOS LaunchAgent, it's a complete, polished utility rather than a personal script — install, uninstall, and configuration are all handled by dedicated scripts with real safety guarantees around a user's existing settings.
Highlights
- Rotates through any homogeneous pack of MP3, WAV, OGG, M4A, AAC, or FLAC clips, avoiding immediate repeats when the pack has two or more files
- Detects playback via file access-time polling rather than hooking or patching the Cursor application, so the app's code signature and behavior are never touched
- Atomic per-file replacement — stages a complete copy of the next clip before swapping it in, so a rotation can never leave a corrupted or partial file behind
- Rescans the active pack on every rotation, so new same-format clips can be added to the pack live without restarting the service
- Zero external dependencies — the entire rotator, installer, and pack generator run on the Python 3 standard library alone
- Runs as a proper user-level macOS LaunchAgent that starts at login, with an install/uninstall pair that can fully restore prior state (`--purge` on uninstall restores original Cursor settings from a backup)
- Generates an original, redistributable six-clip chiptune starter pack on first install, so it works out of the box without requiring the user to supply audio files
- Backs up Cursor's settings before any change and refuses to touch them at all if the existing settings file contains JSONC comments or otherwise can't be parsed safely — printing the target path for manual configuration instead of guessing
- 10 automated tests covering syntax, unit behavior, integration (install/rotate/restore), generated-audio validity, and macOS LaunchAgent plist validation — verified passing (10/10) via `python3 -m unittest discover`
Architecture & Infrastructure
Non-invasive detection via file access time
Rather than hooking into Cursor's process or patching its binary, the rotator polls the access time of Cursor's own configured sound file. A detected read means the sound just played, triggering the next rotation — Cursor's application code, signature, and behavior are never modified.
Atomic, crash-safe file replacement
Each rotation stages a complete copy of the next clip in the pack before performing an atomic file replace on both Cursor's configured target path and its internal settings cache copy, so an interrupted rotation can never leave a half-written or corrupted sound file in place.
Conservative settings-file handling
Before making any change, Cursor's settings file is backed up. If that file contains JSONC comments or otherwise fails to parse as strict JSON, the installer leaves it completely untouched and instead prints the exact sound path for the user to select manually — refusing to guess at a rewrite of a file it can't fully understand.