Claude-skill-registry automating-messages
Automates macOS Messages (iMessage/SMS) via JXA with reliable service→buddy resolution. Use when asked to "automate iMessage", "send Messages via script", "JXA Messages automation", or "read Messages history". Covers send-bug workarounds, UI scripting for attachments, chat.db forensics, and launchd polling bots.
install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/automating-messages" ~/.claude/skills/majiayu000-claude-skill-registry-automating-messages && rm -rf "$T"
manifest:
skills/data/automating-messages/SKILL.mdsource content
Automating Messages (JXA-first with UI/DB fallbacks)
Contents
- Permissions and scope
- Default workflow
- Quick recipe
- Attachments and UI fallback
- Data access and forensics
- Validation Checklist
- When Not to Use
- What to load
Permissions and scope
- Grants needed: Automation + Accessibility; Full Disk Access for any
reads.chat.db - Keep automation scoped and auditable; avoid unsolicited sends and DB writes.
- Pairs with
for common setup (permissions, osascript invocation, UI scripting basics).automating-mac-apps
Default workflow (happy path)
- Resolve transport: pick
(serviceType
oriMessage
) before targeting a buddy.SMS - Identify recipient: filter buddies by
(phone/email). Avoid ambiguous names.handle - Send via app-level
: pass Buddy object tosend
.Messages.send() - Verify window context: activate Messages when mixing with UI steps.
- Fallbacks: if send/attachments fail, use UI scripting; for history, use SQL.
Quick recipe (defensive send)
const Messages = Application('Messages'); Messages.includeStandardAdditions = true; function safeSend(text, handle, svcType = 'iMessage') { const svc = Messages.services.whose({ serviceType: svcType })[0]; if (!svc) throw new Error(`Service ${svcType} missing`); const buddy = svc.buddies.whose({ handle })[0]; if (!buddy) throw new Error(`Buddy ${handle} missing on ${svcType}`); Messages.send(text, { to: buddy }); }
- Wrap with
and log; add small delays when activating UI.try/catch - For groups, target an existing chat by GUID or fall back to UI scripting; array sends are unreliable.
Attachments and UI fallback
- Messages lacks a stable JXA attachment API; use clipboard + System Events paste/send.
- Ensure Accessibility permission, bring app forward, paste file, press Enter.
- See
for the full flow and ObjC pasteboard snippet.references/ui-scripting-attachments.md
Data access and forensics
Reading messages limitation: The AppleScript/JXA API for Messages is effectively write-only. While
send() works reliably, reading messages via chat.messages() or similar methods is broken/unsupported in modern macOS. The only reliable way to read message history is via direct SQLite access to ~/Library/Messages/chat.db.
Security consideration: Reading
chat.db requires Full Disk Access permission, which grants broad filesystem access beyond just Messages. This is a significant security trade-off - granting Full Disk Access to scripts or applications exposes all user data. Consider whether reading message history is truly necessary before enabling this permission.
- Use SQL against
for history; JXAchat.db
is unreliable/non-functional.chat.messages() - Requires: System Settings > Privacy & Security > Full Disk Access for your terminal/script.
- Remember Cocoa epoch conversion (nanoseconds since 2001-01-01); use
for structured results.sqlite3 -json - See
for schema notes, typedstream handling, and export tooling.references/database-forensics.md
Example read query (requires Full Disk Access):
sqlite3 ~/Library/Messages/chat.db "SELECT CASE WHEN m.is_from_me = 1 THEN 'Me' ELSE 'Them' END as sender, m.text, datetime(m.date/1000000000 + 978307200, 'unixepoch', 'localtime') as date FROM message m JOIN handle h ON m.handle_id = h.rowid WHERE h.id LIKE '%PHONE_NUMBER%' ORDER BY m.date DESC LIMIT 10;"
Bots and monitoring
- Implement polling daemons with
now that on-receive handlers are gone.launchd - Track
, query diffs, dispatch actions, and persist state.rowid - See
for the polling pattern and plist notes.references/monitoring-daemons.md
Validation Checklist
- Automation + Accessibility permissions granted
- Service resolves:
returns objectMessages.services.whose({ serviceType: 'iMessage' })[0] - Buddy lookup works:
returns targetsvc.buddies.whose({ handle })[0] - Test send completes without errors
- Full Disk Access granted if using
readschat.db
When Not to Use
- For reading message history without Full Disk Access (AppleScript/JXA cannot read messages)
- For cross-platform messaging (use platform APIs or third-party services)
- For business SMS automation (use Twilio or similar APIs)
- When iMessage/SMS features are not available on the target system
- For bulk messaging (rate limits and security restrictions apply)
- When security policy prohibits Full Disk Access grants (required for any read operations)
What to load
- Control plane and send reliability:
references/control-plane.md - UI scripting + attachments fallback:
references/ui-scripting-attachments.md - SQL/history access:
references/database-forensics.md - Polling bots/launchd:
references/monitoring-daemons.md