Jatayu is the pen name of Lalmohan Ganguly — the devoted, ever-enthusiastic sidekick to Feluda, Satyajit Ray's fictional detective. He tags along on every case, means well, occasionally fumbles, but is completely loyal and always shows up. A personal assistant that handles your calendar, email, and messages felt like exactly his energy.
Inspired by OpenClaw: Jatayu is inspired by OpenClaw. I ran it for a while and then removed it - too many things to validate/check for problems. I wanted to do a smaller one, that does what I need it to and I can incrementally build on. I borrowed the core concepts of heartbeat and plugins, obviously. I also did not want to add a platform API cost on top of my Claude max cost so really wanted the agent to comply to Claude's first party usage rules.
macOS only: Jatayu reads iMessage's local chat.db and sends via
AppleScript to Messages.app, so it runs on the Mac where you're signed
into iMessage.
iMessage is the only channel today: You talk to Jatayu from an
iPhone (or any Apple device signed into the same iMessage account) via
iMessage — there's no SMS, WhatsApp, web, or voice interface yet. The
architecture is channel-agnostic (see docs/channels.md),
but the only channel currently shipped is the vendored iMessage MCP
server in plugins/imessage-local/.
- Claude Code installed and authenticated
- macOS with iMessage configured
tmux(brew install tmux)bun(brew install oven-sh/bun/bun) — runs the vendored iMessage MCP server
./start.sh # auto-runs setup.sh if prerequisites are missingIf this is a fresh clone, setup.sh will create PERSONAL.yaml from the
template and then exit with a warning. Fill it in, then run start.sh again:
# Edit PERSONAL.yaml — name, phone, email, family, service names
./start.sh # now starts the tmux sessionOnce the session is running, attach and approve iMessage access:
tmux attach -t jatayu
# Inside the session, run:
/imessage:access # approve your phone number and any group chatsYou can also run setup.sh directly any time you need to re-apply it
(e.g. after changing the heartbeat interval in PERSONAL.yaml):
bash setup.sh # idempotent — installs deps, reloads launchd agentAttach to the live session any time:
tmux attach -t jatayustart.sh launches Claude Code with --dangerously-load-development-channels.
Channels are Claude Code's inbound-message transport — they push iMessage
messages into the session as <channel source="..."> notifications so the
model can reply through a tool. The flag is currently required because
channels are in preview; locally-developed channels (like our vendored
iMessage MCP server) are gated behind it until the feature ships. The
one-time "I am using this for local development" prompt is auto-confirmed
by start.sh.
You'll see this on the first second of boot:
server:imessage · no MCP server configured with that name
It's cosmetic. Claude Code resolves the channel list before the MCP
server (plugins/imessage-local/server.ts, booted via --mcp-config)
finishes its stdio handshake. Once the server connects, the channel
binds to it and messages start flowing — which is why the bot still
works despite the warning.
Plugins live under plugins/<name>/ and are auto-discovered on launch.
Each one ships its own README.md with setup (API keys, env vars,
prerequisites) and disable instructions — read those before first use.
| Plugin | What it does | Setup notes |
|---|---|---|
imessage-local |
Vendored MCP server that makes iMessage the bot's channel | Required. No extra keys. |
directions |
Google Maps driving directions + live traffic | Needs GOOGLE_MAPS_API env var |
weather |
Current conditions + short forecast via Open-Meteo | No key, no signup |
Writing your own plugin: see docs/plugins.md.
The full design is in docs/. Start with
docs/architecture.md for the big picture,
then branch out:
| Topic | Doc |
|---|---|
| System tour, data flow, directory layout | architecture.md |
| Adding or modifying a plugin | plugins.md |
| Adding a messaging channel | channels.md |
| Skills vs plugins, writing a skill | skills.md |
| Trust tiers, unknown-sender pairing | trust.md |
| Conversation memory, summarization, daily logs | memory.md |
| Heartbeat, scheduled tasks, triggers | scheduling.md |
Operational rules the bot itself follows live in
CLAUDE.md and PERSONALITY.md.
Identity and contacts live in PERSONAL.yaml (gitignored — see
PERSONAL.example.yaml).
Set reminders via iMessage:
"Remind me at 3pm to call the dentist"— one-shot"Remind me every morning to check my tasks"— recurring"What are my reminders?"— list pending"Cancel the dentist reminder"— delete by fuzzy match
Stored in tasks/pending.json; fired by scripts/heartbeat.py every
5 minutes. See scheduling.md for details.
- Jatayu runs with your iMessage ID, which means you need to text yourself to talk (which duplicates messages)
- To avoid duplicates, use imessage groups (but that really only works if you add someone else)
CLAUDE.md operational rules for the bot (read first)
PERSONALITY.md tone and identity
PERSONAL.example.yaml template — copy to PERSONAL.yaml (gitignored)
start.sh launch tmux + live chat session
setup.sh one-time setup (deps, launchd agent)
docs/ design documentation (see table above)
framework/ plugin + channel loader, dispatcher
plugins/<name>/ individual plugin implementations
channels/<name>/ individual channel configs + rules
scripts/ runtime helpers (heartbeat, trust, memory, …)
.claude/ Claude Code hooks + on-demand skills
launchd/ launchd plist templates
tasks/ runtime state (gitignored)
memory/ daily logs (gitignored)
Stop the heartbeat launchd agent with:
launchctl unload ~/Library/LaunchAgents/com.jatayu.heartbeat.plist