Terminal access to your own iMessage / SMS history by reading the local
Messages database (~/Library/Messages/chat.db) read-only:
imsg chats # 1 row per conversation, most recent first
imsg read "alice" # show a thread
imsg search "dinner" # full-text search across all messages
imsg send "alice" "hi" --yes # send a 1:1 message (dry-run without --yes)Recent macOS removed message-text reading from the Messages scripting
dictionary (it only exposes send), so the SQLite store is the only way to read
history — the
CLI snapshots chat.db (plus its WAL/SHM sidecars) to a temp dir and queries
the copy, so it never locks or mutates the live database.
Contact names are resolved best-effort via the Contacts framework; without that permission, raw phone/email handles are shown instead (commands still work).
Requires Python 3.11+ and uv — it runs the
script and resolves its dependencies on demand:
# macOS / Linux
curl -LsSf https://astral.sh/uv/install.sh | sh
# or
brew install uvThe npx skills install path additionally requires Node.js (for npx).
chat.db is protected by macOS privacy (TCC). Grant the terminal app that
runs the command Full Disk Access, or every command fails with
authorization denied:
- System Settings → Privacy & Security → Full Disk Access
- Enable (or add with +) your terminal — e.g. iTerm or Terminal
- Quit and relaunch that terminal app (required for it to take effect)
A — Bundled launcher. No install step. Clone the repo and invoke bin/imsg
directly; the #!/usr/bin/env -S uv run --script shebang and
PEP 723 inline metadata make uv pull
deps on the first run.
git clone <repo> imessage-cli
cd imessage-cli
./bin/imsg chatsB — As an agent skill. The repo ships a SKILL.md and the self-contained
bin/imsg launcher at the project root, in the
Vercel Labs skills format:
npx skills add ClementWalter/imessage-cliThis drops the skill under ~/.agents/skills/imessage-cli/ and symlinks it into
every supported agent runtime installed on your machine (Claude Code, Cursor,
Windsurf, Codex, Gemini CLI, …). Agents then drive the CLI by invoking the
bundled bin/imsg script directly.
To install locally for development instead, symlink the checkout so the skill picks up live edits:
mkdir -p ~/.claude/skills
ln -s "$(pwd)" ~/.claude/skills/imessage-cliimsg chats # 30 most-recent conversations
imsg chats --limit 200 --json # everything, machine-readable
imsg read "alice" # alice's thread; auto-resolves contact names
imsg read "alice" --match 2 # disambiguate when multiple chats match
imsg read "+15555550123" # by raw handle
imsg read "Family" --limit 200 # a group chat
imsg search "dinner" # substring search across all messages
imsg send "alice" "running late" # dry-run: prints resolved recipient + text
imsg send "alice" "running late" --yes # actually sends (via Messages)
imsg send "alice" "hi" --sms # over SMS instead of iMessage
imsg send "alice" "hi" --handle +15555550123 # target an exact handleimsg --help lists every subcommand; imsg <cmd> --help for per-command
options including --json, --limit, --match, --handle, --sms, --yes.
Every read command supports --json for structured output. Sending is an
outbound action: it defaults to a dry-run and only sends with --yes.
- Read-only by construction. Reads happen against a temp-dir snapshot of
chat.db(+ WAL/SHM), so the live Messages database is never locked or mutated. Onlysend --yeswrites anything (an outgoing message, via the Messages app's AppleScriptsend). - Body-text decoding. When a message's
textcolumn is empty (newer macOS), the body is decoded from theattributedBodyblob; undecodable attachment-only messages show as[attachment]. - Timestamps are converted from Apple absolute time to local time.
Declared inline via PEP 723 in bin/imsg:
click— CLI frameworkpyobjc-framework-Contacts— macOS-only, resolves handles to contact names
Everything else is Python stdlib (sqlite3, shutil, subprocess, …).
Single-user personal tooling for your own account on your own Mac. It reads a local, TCC-protected database you already have access to — there is no network service, no account registration, and no way to read anyone else's messages. macOS only.
Same idea — your own messages, from the terminal, for other platforms:
- whatsapp-cli — your personal WhatsApp chats (pairs as a linked device)
- slack-user-cli — Slack via your existing browser session credentials