My weeks have three things fighting over the same hours: a full-time job, community college classes, and the side projects I'm hoping will replace the job. None of those is hard on its own. What kept going wrong was the gaps between them. A due date would move and I wouldn't see it until the night before. A grade would post and I'd find out a week later. I'd get home from work, open my laptop and spend twenty minutes working out what I was supposed to do tonight.
I didn't need another app to check. I needed something that checked for me and told me, in the place I already look all day, which is my messages.
So I built Pai. It's a small orange owl that talks to me over Telegram.
The rules came first
Before any code, I wrote a short plan, and the top of it was four rules:
- Read-only by default. Anything touching school, money or accounts only reads. It never submits, pays or trades.
- I decide. Suggestions and summaries come to me. Every action that matters stays with me.
- Cheap to run. It has to fit inside my monthly API credits with plenty of room.
- One tool per feature. Each feature is a function plus a JSON schema. Adding one means adding a row to a table and one function.
Those rules have done more to shape Pai than any single feature.
How it works
Pai is a Python bot running a Claude tool loop. I send it a message, it sends that and the list of tools to Claude, runs whichever tools Claude asks for, sends the results back, and repeats until there's an answer. A scheduler starts the same loop on its own for the things that should happen without me asking.
It uses long polling, so there's no open port and no public address. It only answers my Telegram user ID, and it keeps only the last few turns of chat in each request to keep costs down. Everything it remembers lives in one SQLite file.
What it did on day one
The first version came together over a weekend, in phases that each worked on their own before the next one started.
School. Every Sunday night Pai pulls what's due from Canvas, fits it around work and class, and
sends me a plan for each day. Every couple of hours it checks for new grades and due dates.
/due, /grades, /plan and /replan are there when I want them. School got enough of its own
work that it has its own post.
Daily life. Reminders in plain language ("remind me Thursday at 6 to call the bank") that fire on time even after a restart. Quick notes I can search later. A morning briefing with the weather, what's due today and my reminders.
Keeping an eye on things. Uptime checks on my sites every five minutes. GitHub activity summarized. A weekly standup on my side projects: what shipped, what's stalled, one next step for each. And the health of the machine Pai itself runs on.
Research. Ask a question and get a short answer with sources. Send a link or a PDF and get a summary. Send a photo of a syllabus or a flyer and get the dates turned into reminders.
Study. A /focus timer, flashcards and quizzes from material I send, an evening recap and a
habit check-in at night.
The odd jobs. Send a YouTube link and get an MP3 back. Watch a product's price and tell me when it drops. And a lead finder that looks for local businesses with weak or no websites and drafts an outreach note, which I read and send myself, or don't.
Keeping it cheap
The rule about cost turned into a handful of habits:
- The right model for the job. Haiku for routing, reminders and short replies. Sonnet for study plans and research. Opus only when something really needs it.
- Prompt caching on the system prompt and the tool list, since they're the same on every call.
- Code before models. Anything code can work out, code works out. The planner computes the free time slots itself and only asks the model to place work into them.
- A daily spending cap inside Pai, so a buggy loop can't burn through a month of credits
overnight.
/spendshows where it's at.
Where it lives
Pai was meant to live on an Orange Pi Zero 2W. The model runs on Anthropic's servers, so the board
only has to make HTTP requests, and a 1 GB board is plenty. It runs as a systemd service that
restarts itself, and /update pulls the latest code and restarts it from Telegram. For now it's on
Railway while I wait on a microSD card, and it goes back to the Pi after that.
Since that first weekend it's grown a lot: long-term memory, a check-in that only speaks up when
something is actually new, a /fix command that diagnoses its own errors, and a
read-only connection to my bank accounts. But the reason it exists hasn't
changed. I don't want to go looking for what I'm supposed to do next. I want it to come to me.