|
|
||
|---|---|---|
| .claude | ||
| .forgejo/workflows | ||
| .playwright-mcp | ||
| .project | ||
| @packages | ||
| deploy | ||
| docs | ||
| handoffs | ||
| mcp | ||
| scripts | ||
| src | ||
| web | ||
| .gitignore | ||
| .infra.yaml | ||
| .mcp.json | ||
| app.manifest.yaml | ||
| CLAUDE.md | ||
| deploy-to-plum-dist-from-src | ||
| docker-compose.dev.yml | ||
| m-quinn-messages-after-port.png | ||
| m-quinn-messages-search-after-load.png | ||
| m-quinn-messages-search-verify.png | ||
| m-quinn-thread-focus.png | ||
| mac-sync-dashboard-deployed.png | ||
| mac-sync-search-cin-2.png | ||
| mac-sync-search-cin.png | ||
| Makefile | ||
| no-params.png | ||
| Package.resolved | ||
| Package.swift | ||
| README.md | ||
| run | ||
| search-cin-params.png | ||
| search-cin.png | ||
| search-click-result.png | ||
| search-final.png | ||
| search-q-param.png | ||
| search-results-count.png | ||
| search-settled.png | ||
| thread-dates-highlight.png | ||
| thread-focus-message.png | ||
| VERSION.json | ||
@mac-sync
Unified macOS sync agent. One menu-bar app on the Mac, one Bun/Hono server on the LAN, one React SPA in the browser — six personal data domains kept in step: Messages, Photos, Mail, Calendar, Reminders, Notes.
@mac-sync collapses what used to be three separate agents (mac-imessage-sync,
mac-iphoto-sync, mac-imail-sync) into a single Swift package with one
BaseSyncManager lifecycle, one SendQueueClient outbound poller, one
AppleScriptEscape helper, and one Postgres schema (icloud.*).
At a glance
| Module | Direction | OS surface | Read interval | Outbound interval |
|---|---|---|---|---|
| iMessage | bidirectional | ~/Library/Messages/chat.db |
30s | 30s |
| iPhoto | read-only | Photos.framework | 300s | — |
| iMail | read-only (*) | Mail.app via AppleScript | 300s | — |
| iCal | bidirectional | EventKit | 300s | 60s |
| iReminders | bidirectional | EventKit | 300s | 60s |
| iNotes | bidirectional | Notes.app via AppleScript | 600s | 60s |
| iCalls | read-only | CallHistory.storedata (GRDB) |
120s | — |
(*) IMailSync.SyncManager.sendEmail exists but is unwired; see
known limitations.
Intervals verified in BaseSyncManager super.init(...) calls and
SendQueueClient(label:..., interval:) constructions; see
architecture for citations.
Repository layout
@packages/
shared/ MacSyncShared SwiftPM target
Sync/ BaseSyncManager, SendQueueClient, SyncConnectionError
Storage/ ActivityLog, ConfigFile
Transport/ Shared (server URL resolution), DeviceRegistration
Util/ AppleScriptEscape, ContentTypeMapping, PhoneUtils
WebServer/ LocalWebServer (localhost:8765, settings + SPA)
imessage/ IMessageSync (bidirectional, legacy icloud.send_queue table)
iphoto/ IPhotoSync (read-only Mac to server, with binary uploads)
imail/ IMailSync (Mail.app AppleScript reader; Sender unwired)
ical/ ICalSync (bidirectional, EventKit + calendar_send_queue)
ireminders/ IReminderSync (bidirectional, EventKit + reminder_send_queue)
inotes/ INoteSync (bidirectional, AppleScript + note_send_queue)
icalls/ ICallsSync (read-only, CallHistory.storedata via GRDB)
src/client/ MacSyncApp (executable menu-bar app)
src/server/ Bun + Hono + PostgreSQL (TypeScript)
web/ React SPA dashboard (Vite, react-query)
deploy/ install.sh, deploy-remote.sh, systemd units, nginx config
(See Package.swift:21-157 for the canonical target list.)
Get started
- Set up a dev environment: docs/dev-setup.md
- Tour the system: docs/architecture.md
- Trace a sync end-to-end: docs/data-flow.md
- Add a 7th module: docs/adding-a-module.md
- Ship a release: docs/release.md
- Known gotchas: docs/known-limitations.md
Per-module deep dives:
- Messages (iMessage)
- Photos (iPhoto)
- Mail (iMail)
- Calendar (iCal)
- Reminders (iReminders)
- Notes (iNotes)
- Calls (iCalls)
Build & test
make build # swift build --product MacSyncApp (Makefile:60-61)
make test # swift test (Makefile:71-73)
make run # build then run .build/debug/MacSyncApp
Server:
cd src/server && bun run dev # src/server/package.json:7
Web:
cd web && bun run dev # vite --port 5200 (web/package.json:7)
Safety
- NEVER commit — an external service handles commits (
CLAUDE.md:46). - NEVER
pkill node/killall node— kills Claude Code sessions. - NEVER use
file:orlink:inpackage.json— edit source, publish, bump.