v1.8.2
Standard app instead of menu-bar-only: removed LSUIElement from Info.plist. Dock icon, Cmd+Tab, and the standard app menu (Cmd+Q) are back. The accessory-app design was making the app hard to quit reliably -- no Dock icon and no accessible app menu meant the only way to quit was finding the menu bar dropdown's "Quit BusyMirror" item, which was blocking updates (old process holds the bundle open, "app in use" on replace). Menu bar extra stays as a secondary quick-access point. Verified via `lsappinfo` (type="Foreground", was "UIElement") since this is an app-type change a screenshot wouldn't show either way. All unit tests pass. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
BusyMirror
BusyMirror mirrors meetings between your calendars so your availability stays consistent across accounts/devices.
On macOS, BusyMirror runs as a standard app (Dock icon, ⌘Q to quit) and also has a menu bar icon for quick sync/status without opening the main window.
What it does (current)
- Route-driven mirroring (multi-source): define Source → Targets routes and run them in one go.
- Manual selection mirroring: pick a source and targets in the UI and run.
- Two privacy modes:
- Private (hide details): mirrors placeholders with prefix + placeholder title (e.g., "🪞 Busy").
- Mark Private: mirrors prefix + real title, but marks events Private on supported servers (best-effort).
- DRY-RUN mode: see what would be created/updated/deleted without writing.
- Activity Log in the app plus persistent file logging on disk.
- In-app scheduling: install or remove a
launchdLaunchAgent from theScheduled runssection. - Menu bar controls: trigger
Sync Now, open the main window, open Preferences, or quit. - Overlap modes:
allow,skipCovered,fillGaps. - Merge adjacent events with a configurable gap.
- Time window controls (days back/forward) and Work Hours filter.
- Accepted-only filter (mirror your accepted meetings only).
- Cleanup of placeholders, including auto-delete of mirrors whose source disappeared.
- Refresh Calendars prunes stale saved calendars and routes when calendars are removed from the system.
- Prefix-based tagging and loop guards to prevent re-mirroring mirrors.
- Settings: autosave/restore, Import/Export JSON, saved routes for scheduled/headless runs.
Why
Use one calendar’s confirmed meetings to block time in other calendars (e.g., corporate iPad vs. personal devices).
Build (macOS)
Option A — Xcode
- Open
BusyMirror.xcodeprojin Xcode. - Select the BusyMirror scheme → My Mac.
- Product → Build.
- Product → Archive → Distribute App → Copy App (no notarization) to export a
.app(or ZIP it for sharing).
Option B — Makefile (reproducible)
- Build Release:
make build-release - Package ZIP:
make package(createsBusyMirror-<version>-macOS.zip+.sha256) - Built app:
build/DerivedData/Build/Products/Release/BusyMirror.app
See CHANGELOG.md for notable changes.
CLI (optional)
- Run from Terminal with
--routesto mirror without the UI. Example:BusyMirror.app/Contents/MacOS/BusyMirror --routes "1->2,3; 4->5" --write 1 --days-forward 7 --mode allow --exit
- Run the routes already saved in the app settings:
BusyMirror.app/Contents/MacOS/BusyMirror --run-saved-routes --write 1 --exit
--helpprints full flag documentation.--list-calendars [--json]prints available calendars (index, id, title, source/type) and exits.--status [--json]prints last-run time/result, schedule state, and saved-route count and exits.- Flags exist for privacy, all-day, merge gap, days window, overlap mode, cleanup, and filters.
- Filters:
--exclude-titles "token1, token2"--exclude-organizers "alice@example.com, Example Org"- Tokens are comma or newline separated; matching is case-insensitive.
- Exit codes:
0success,2no calendar access,3no saved routes (with--run-saved-routes).
Logs
- BusyMirror now writes a persistent log file to
~/Library/Logs/BusyMirror/BusyMirror.log. - When the file grows large, the previous file is rotated to
~/Library/Logs/BusyMirror/BusyMirror.previous.log. launchdstdout/stderr for scheduled runs are also written in the same folder.- In the UI, use
Reveal Log Fileto open the current log directly in Finder.
Scheduling
- BusyMirror can create its own schedule from the app UI in
Scheduled runs. - Choose
Hourly,Daily, orWeekdays, then clickInstall Schedule. - The installed LaunchAgent runs:
/Applications/BusyMirror.app/Contents/MacOS/BusyMirror --run-saved-routes --write 1 --exit
- This is more stable than index-based
--routes, because it uses the routes and per-route options you already configured in the UI. - Hourly schedules use
launchdStartInterval; daily and weekday schedules useStartCalendarInterval. - You can remove the job from the same UI with
Remove Schedule, and inspect the generated plist withReveal LaunchAgent. - Note: scheduled headless runs depend on Calendar permission being granted to the installed app. Because these local builds are unsigned, macOS may require re-granting permission after replacing the app bundle with a new build.
Roadmap
See ROADMAP.md
License
MIT — see LICENSE.