That's it. Calendar, contacts, notes, and todos included — no API required. Every incoming iMessage gets piped through an AI, and the reply goes back automatically. No Python. No Node. No daemon. No cloud function. Just Unix pipes and toast.
We started with a 150-line Python script. JSON-RPC over stdin/stdout. Named fifos. Subscription management. A notification loop. It worked, but it felt wrong. Too much ceremony to do something simple: read a message, think about it, send a reply.
Tried to use imsg by OpenClaw as the iMessage interface, piped through toast, and sent replies back. Six lines of bash replaced the Python script, not terrible:
jq --unbuffered -r 'select(.is_from_me==false) | "\(.chat_id)\t\(.text)"' |
while IFS=$'\t' read -r cid text; do
reply=$(echo "$text" | toast)
imsg send --chat-id "$cid" --text "$reply"
done
But it still felt clumsy. Too many flags. --json, --unbuffered, --chat-id, --text. Every flag is a decision someone has to make. So we built our own imessage CLI in C that figures out what you want from context:
| CID set? | stdin? | behavior |
|---|---|---|
| no | no | list chats |
| yes | no | show history |
| yes | yes | send message |
One binary, three behaviors. No subcommands. -e starts watch mode — for each incoming message, set CID in the environment and run the command. The child processes inherit it.
The pipeline imessage | toast | imessage does exactly the right thing at each step:
imessage
# 2. reads conversation, generates reply
toast
# 3. CID is set, stdin has reply → send it
imessage
The AI gets full conversation context every time. It remembers everything. Different chats run in parallel, same chat runs serial so replies stay in order.
The whole thing is a single C binary, it uses kqueue to watch the SQLite WAL file for new messages. History outputs them: and you: prefixes so it works directly as LLM context. Sending uses AppleScript via popen().
The interesting part isn't the AI. It's the interface. imessage doesn't know about AI. toast doesn't know about iMessage. They meet in a pipe. Replace toast with anything:
imessage -e 'imessage | tail -1 | imessage'
# Fortune cookie bot
imessage -e 'fortune | imessage'
# Translator bot
imessage -e 'imessage | toast "translate to Spanish" | imessage'
One line each. That's what composability gets you.
What about Calendar, Notes, and Todo?
Toast automatically reads a .crumbs file as context.
# Calendar
2026-02-08 12:00-13:00 Lunch with Bob
2026-02-08 15:00-16:00 Dentist
2026-02-10 09:00-10:00 Team standup
2026-02-12 18:00-19:30 Dinner with Sarah
# Contacts
Bob: +16505551234, coworker, prefers morning meetings
Sarah: +14155559876, friend, vegetarian
Mom: +16505554321, call on Sundays
# Todo
- Fix the garage door
- Renew passport (expires March)
- Buy birthday gift for Sarah (Feb 20)
# Notes
Allergic to shellfish
Gym: Mon/Wed/Fri 7am
Car in shop until Feb 10, taking the bus
Someone texts "are you free Thursday?" — toast checks .crumbs, sees the dentist, suggests another time. "What's Bob's number?" — it knows. "Remind him about my birthday" — it appends to the todo list.
Toast can also write to .crumbs. So when someone texts "let's do lunch Friday at noon," the assistant adds it to the calendar. No API. No integration. Just a text file.
The persona is another file:
You are the CEO's executive assistant. You respond to texts.
Tone: brief, direct, helpful. Text message style.
The file .crumbs contains the schedule, contacts, notes, and todos.
When asked to schedule something, add it to .crumbs.
If a time conflicts, suggest the nearest open slot.
If unsure about anything, say "Let me check."
The entire AI assistant — calendar, contacts, notes, todos, scheduling — is two text files and one line of bash:
No database. No app. No subscription. Edit your schedule with vim.