A minimal and self-managing iMessage bot β powered by pi.
- Minimal: No BlueBubble, no webhooks, no extra dependencies
- Self-managing: Turn the agent into whatever you need. He builds his own tools without pre-built assumptions
- Transparent: tool calls and reasoning are sent to your iMessage chat, so you can see exactly what it's doing and why
- iMessage Integration: Responds to DMs, SMS, and group chats; identifies who sent each message; understands quoted/reply-to messages
- Web UI: browse chat history, toggle replies on/off per chat, live updates β disable with WEB_ENABLED=false and let the agent build your own web UI
β οΈ Security note
- Replies are off for all chats by default (
blacklist: ["*"]) β only explicitly whitelisted chats get a response- The agent runs with Full Disk Access and can read/write your filesystem as part of its tool use
- The web UI has no authentication and is accessible to anyone on your local network; set
WEB_ENABLED=falseif that's a concern
Prerequisites: macOS with Messages.app, Full Disk Access for the terminal, Pi Coding Agent authenticated
npm install -g @kingcrab/pi-imessage
pi-imessage # run in foreground
pi-imessage install # install as launchd service (auto-start on boot, restart on crash)Available at http://localhost:7750 (configurable via WEB_PORT).
- Displays chat logs from the last 7 days, sorted by most recent activity
- Toggle reply on/off per chat
- Live updates via SSE when new messages arrive
P.S. Disable with WEB_ENABLED=false and let the agent build your own web UI
Send these as iMessage to interact with the bot:
| Command | Description | Example Reply |
|---|---|---|
/new |
Reset the session, starting a fresh conversation | β New session started |
/status |
Show session stats: tokens, context, model | π¬ 3 msgs - β7.2k β505 1.1%/128kπ€ anthropic/claude-sonnet-4 β’ π minimal |
/reload |
Reload models and clear all sessions | β Models reloaded |
All fields are optional.
{
"chatAllowlist": {
"whitelist": ["iMessage;-;+11234567890"],
"blacklist": ["*"]
}
}Chat allowlist controls which chats receive replies (messages are always logged). By default, replies are off for all chats (blacklist: ["*"]) β opt in specific chats via the web UI or by adding their guid to whitelist. Resolution priority: blacklist[guid] > whitelist[guid] > blacklist["*"] > whitelist["*"].
| Variable | Required | Default | Description |
|---|---|---|---|
WEB_ENABLED |
no | true |
Set to false to disable the built-in web UI |
WEB_HOST |
no | localhost |
Web UI host |
WEB_PORT |
no | 7750 |
Web UI port |
WORKING_DIR |
no | ~/.pi/imessage |
Workspace directory |
npm run check # typecheck + lint (run after code changes)
npm test # run tests ~/Library/Messages/chat.db
β
(poll every 2s for new rows)
β
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββββ
β pi-imessage β
β β
β Watcher (chat.db polling) β
β β β
β ββ Filter: is_from_me=0, no reactions β
β ββ Deduplicate via seenRowIds β
β ββ Read attachments from local disk β
β β β
β βΌ β
β AsyncQueue<IncomingMessage> β
β β β
β βΌ β
β SessionManager (pi-coding-agent) β
β β per chatGuid, persistent on disk β
β β ββ data/<chatGuid>/ β
β β ββ log.jsonl (full history) β
β β ββ context.jsonl (LLM context) β
β β β
β βΌ β
β Agent loop (pi-agent-core) β
β β β
β β ββ outer: follow-up messages βββββ β
β β β ββ inner: tool calls + β β β
β β β β steering messages β β β
β β β βββββββββββββββββββββββββββββ β β
β β ββββββββββββββββββββββββββββββββββ β
β β β
β βΌ β
β Collect assistant reply text β
β β β
β ββ sendMessage (AppleScript β Messages.app) β
β ββ save logs (messages, digests) β
β β
ββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
iMessage (user receives reply via Messages.app)
