Imported from eiiot/imsg-plus (
AGENTS.md). Install upstream withnpx skills add eiiot/imsg-plus. Copyright stays with the author.
Repository Guidelines
Project Structure & Module Organization
Sources/imsgholds the CLI entrypoint and command wiring.Sources/IMsgCorecontains SQLite access, watchers, AppleScript send logic, and helpers.bin/is created bymake buildfor local artifacts.
Build, Test, and Development Commands
make imsg— clean rebuild + run debug CLI (useARGS=...).make build— universal release build intobin/(includes dylib).make build-dylib— build only the injectable dylib for advanced features.make lint— runswift formatlint +swiftlint.make test— runswift testafter syncing version + patching deps.
Advanced Features Architecture (imsg-plus)
Dylib Injection Approach
Advanced features (typing indicators, read receipts, tapbacks) require access to the private IMCore framework, which is only available inside Messages.app's process. We use DYLD_INSERT_LIBRARIES to inject imsg-plus-helper.dylib into Messages.app at launch.
Key files:
Sources/IMsgHelper/IMsgInjected.m— Objective-C dylib that loads into Messages.appSources/IMsgCore/IMCoreBridge.swift— Swift side of IPC bridgeSources/IMsgCore/MessagesLauncher.swift— Manages dylib injection lifecycleMakefile—build-dylibtarget compiles the injectable dylib (arm64e)
IPC Mechanism
File-based IPC is used for communication between the CLI and the injected dylib:
- Command file:
~/Library/Containers/com.apple.MobileSMS/Data/.imsg-plus-command.json - Response file:
~/Library/Containers/com.apple.MobileSMS/Data/.imsg-plus-response.json - Lock file:
~/Library/Containers/com.apple.MobileSMS/Data/.imsg-plus-ready(contains Messages.app PID)
The dylib watches the command file using dispatch_source_t and writes responses to the response file. The Swift side polls for responses with timeout.
IMCore Framework Access
The dylib uses Objective-C runtime to access IMCore classes:
IMChatRegistry— Find chats by handle/identifierIMChat— Chat objects with methods likesetLocalUserIsTyping:,markAllMessagesAsReadIMMessageItem/IMChatItem— Message objects (note: these are different classes!)
Runtime compatibility: Some methods may not exist on all macOS versions. The dylib injects missing methods at runtime (e.g., isEditedMessageHistory for macOS 15.6).
Implementation Status
- ✅ Typing indicators: Working via
IMChat.setLocalUserIsTyping: - ✅ Read receipts: Working via
IMChat.markAllMessagesAsRead - ❌ Tapbacks: In progress - GUID-to-chat-item lookup needs work
- Issue:
chatItemsarray search doesn't find messages by GUID - May need alternative approach or different IMCore method
- Issue:
Testing Notes
- Must launch Messages.app with dylib:
DYLD_INSERT_LIBRARIES=.build/release/imsg-plus-helper.dylib /System/Applications/Messages.app/Contents/MacOS/Messages & - Requires SIP disabled (
csrutil disablefrom Recovery Mode) - Check dylib loaded:
lsof -p $(pgrep Messages) | grep imsgor check for IPC files - Console.app shows
[imsg-plus]logs from the dylib - Typing indicators appear on recipient's device, not sender's
Coding Style & Naming Conventions
- Swift 6 module; prefer concrete types, early returns, and minimal globals.
- Formatting is enforced by
swift formatandswiftlint. - CLI flags use long-form, kebab-case (
--chat-id,--attachments).
Testing Guidelines
- Unit tests live in
Tests/as*Tests.swift. - Prefer deterministic fixtures over touching the live Messages DB.
- Add regression tests for fixes touching parsing, filtering, or attachment metadata.
Commit & Pull Request Guidelines
- Follow the existing short, lowercase prefixes seen in history (
ci:,chore:,fix:,feat:) with an imperative summary (e.g.,fix: handle missing attachments). - PRs should include: brief description, steps to repro/verify, and outputs of
make lintandmake test. For CLI changes, include sample commands and before/after snippets. - Keep changeset focused; avoid drive-by refactors unless they reduce risk or remove duplication in touched areas.
Security & macOS Permissions
- The tool needs read-only access to
~/Library/Messages/chat.db; ensure the terminal has Full Disk Access before running tests that touch the DB. - Sending requires Automation permission for Messages.app and SMS relay configured in macOS/iOS; document any manual steps needed for reviewers.