- Sync reminders: new "Sync reminders when mirroring" option (global and per-route) copies source event alarms into mirrored placeholders. CLI flag --sync-reminders. - CLI polish: --help, --list-calendars, --status (all support --json). Scheduled/headless runs record last-run time/result/summary. Failure paths now exit with distinct nonzero codes (2 = no calendar access, 3 = no saved routes) instead of always exiting 0. - Fix settings silently wiped on upgrade: SettingsPayload's auto-synthesized Codable threw on any settings blob missing a field added since (e.g. the new syncReminders key), failing the whole decode and letting the next autosave persist an empty state over real routes/filters. Custom decode now falls back per-field like Route already did, and loadSettingsFromDefaults recovers routes from the legacy routes.v1 key if settings.v2 comes back empty, repairing installs already hit by this. - Roadmap: event-driven background sync (SMAppService + EKEventStoreChanged + wake notification) and an external MCP wrapper around the CLI queued next; decided against direct Google/CalDAV API integration since EventKit already covers it via System Settings accounts. All 47 unit tests pass (45 existing + 2 new SettingsPayloadTests). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
3.9 KiB
3.9 KiB
BusyMirror Roadmap
Shipped (highlights)
- Route-driven mirroring (multi-source)
- Accepted-only filter (mirror your accepted meetings)
- Persistent settings with autosave/restore; Import/Export JSON
- Overlap modes (allow, skipCovered, fillGaps) and merge-gap
- Work Hours filter and title-based skip filters
- Privacy: placeholders with prefix + customizable title
- 1.3.0: Mark Private option (global + per-route)
- 1.3.4: persistent file logging, stale-calendar pruning on refresh, clickable top-bar mode toggle
- 1.3.6: in-app scheduling via
launchdwith hourly/daily/weekday modes - 1.3.6: generated macOS app icon set and packaged release assets
- 1.4.0: unit-test suite (45 tests), Cancel button, progress indicator, sandbox LaunchAgent fix, mirror URL fix, engine refactor into
MirrorConfig - Calendar list already auto-refreshes on
EKEventStoreChanged(does not yet trigger an auto-sync — see Next) - CLI diagnostics:
--help,--list-calendars [--json],--status [--json], real exit codes (2 = no access, 3 = no saved routes), last-run tracking (time/ok/summary)
Next — reliability (V2 groundwork)
- Event-driven background sync. The existing hourly
launchd StartIntervalonly fires while the Mac happens to be awake at that instant and gets throttled/coalesced by macOS, so it's not reliable. Replace polling with reacting:- Keep the app running as a login item via
SMAppService.agentinstead of relying on launchd to relaunch it. - Extend the existing
EKEventStoreChangedobserver (today it only reloads the calendar list) to also trigger a debounced (~2-5s) auto-sync of saved routes. - Add an
NSWorkspace.didWakeNotificationhandler to resync after sleep. - Keep one coarse fallback timer (e.g. every 30 min) purely as a safety net for a missed notification — not the primary mechanism.
launchd's remaining job shrinks to "make sure the app is running," whichSMAppServicelikely covers, so the plist-generation code may become unnecessary.
- Keep the app running as a login item via
- MCP server (thin external wrapper, not embedded in the app). So agents driving BusyMirror don't have to shell out to the CLI and regex-parse log lines. A small standalone stdio-transport script (Node/Python) maps MCP tools 1:1 onto the CLI's
--jsonoutput:list_calendars,list_routes,run_route,run_saved_routes,get_status. Deliberately kept out of the Swift app itself — no MCP SDK dependency in the signed binary (AGENTS.md's zero-external-packages rule stays intact), and MCP hosts spawn server processes on demand anyway, so there's no need for the app to run one persistently.
Then — V2 UI polish
- Split
ContentView.swift(~1800 lines doing UI + settings + CLI + scheduling) into per-section view models (Routes, Schedule, Privacy, Log) so views are small and native-feeling. - Real
Settings { }scene instead of the main window doubling as preferences. - Menu bar icon reflects state (idle / syncing / error) instead of a static icon.
- Surface last-sync-time / last-error / next-check in the menu bar UI — the data now exists (
--status'slastRunAtISO/lastRunOK/lastRunSummary), this is just wiring it into the menu. - Better server-side privacy mapping (per-provider heuristics).
Later
- Signed/notarized binaries and release pipeline.
- Smarter cleanup & conflict resolution.
- iOS/iPadOS helper (Shortcuts integration).
- Profiles & MDM/Managed Config support.
Decided against
- Direct Google/CalDAV/Exchange API integration (OAuth flows, token storage, per-provider clients so BusyMirror can mirror a non-local calendar without it being added to macOS). EventKit already surfaces any calendar added via System Settings → Internet Accounts — macOS does the sync, auth, and refresh. Building a parallel integration would duplicate the OS for the narrow case of an account that can't or won't be added system-wide (e.g. MDM-restricted work accounts). Not worth the OAuth/Keychain/per-provider-quirk surface for that.