Category: Personal

  • I Built a Menu Bar App That Turns My Wife’s Texts Into Calendar Events

    I Built a Menu Bar App That Turns My Wife’s Texts Into Calendar Events

    My day is a wall of notifications.

    IRC, Slack, Discord, P2s, Adium jabber alerts, ntfy.sh pings, Telegram, WhatsApp, iMessage. I have thousands of streams of text coming at me every single day. It’s just ping ping ding ding ding from the moment I open my laptop until I close it.

    And somewhere in that noise, my wife texts me that the kids have a baseball game Tuesday at 5pm at Riverside Park.

    She’s incredibly organized. She texts me about dates, plans, appointments, school events, family stuff. All the time. And she’s great about it. The problem is me. I’m neck deep in a Kubernetes migration or chasing a kernel bug, and by the time I come up for air, that text is buried under 47 Slack threads and a Discord ping about someone’s homelab.

    So I built something to fix it.

    iMessageWatcher

    Screenshot of iMessageWatcher app showing a conversation about a kids' baseball game scheduled for February 24, 2026, with event details added to the calendar.

    iMessageWatcher is a macOS menu bar app that watches my iMessages specifically from my wife and uses a local LLM to figure out if a message contains an event, a date, a reminder, or something I need to act on. If it does, the app automatically creates a calendar event with the name, location, time, and all that. No input from me. No copy-pasting. No “I’ll add that later” (which means never).

    The whole thing runs locally. The LLM runs on my machine through Ollama. My wife’s messages never leave my device, never hit a cloud API, never get sent to OpenAI or anyone else. That was non-negotiable for me.

    How It Works

    The app sits in your menu bar and polls the iMessage database (~/Library/Messages/chat.db) every 60 seconds. It watches for new messages from whichever contact you configure (in my case, my wife’s number).

    Flowchart illustrating how iMessageWatcher processes text messages into calendar events on a device, showing steps from iMessage database to classification and extraction by Olama LLM, leading to integration with Calendar, Reminders, Due App, and ntfy.sh.

    When it finds new messages, it grabs the recent conversation context and sends it to a local Ollama instance running deepseek-r1 (or whatever model you prefer). The LLM gets a prompt that basically says: “Look at this conversation. Is there an event, appointment, or task mentioned? If so, extract the name, date, time, and location. Return JSON.”

    The app parses the response and creates the event in Apple Calendar. Done. My wife texts “Don’t forget about the kids’ baseball game Tuesday at 5pm, it’s at Riverside Park” and a few seconds later, “Kids’ Baseball Game” shows up on my calendar for Tuesday at 5:00 PM at Riverside Park. I don’t touch anything.

    It also works with Apple Reminders, the Due app (a personal favorite reminder app), and ntfy.sh for push notifications. You can toggle each one on or off depending on your setup.

    The Entire App Is 4 Files!!!1

    This is probably my favorite part. The whole thing is 4 files with no Xcode project and no external dependencies:

    Overview of the app structure, featuring four key files: main.swift as the entry point, AppDelegate.swift for application logic, Info.plist for metadata, and build.sh for compilation. Each file includes brief descriptions of its functionality.
    • main.swift : App entry point. 5 lines.
    • AppDelegate.swift : All the logic. Menu bar, SQLite scanning, LLM classification, EventKit integration, preferences window. Everything.
    • Info.plist : Bundle metadata and permission descriptions.
    • build.sh : Compiles the app, generates the icon programmatically, bundles everything into a proper .app.

    No CocoaPods. No Swift Package Manager. No Xcode project file. You clone the repo, run ./build.sh, and you have a working macOS app. The build script even generates the app icon using Core Graphics in an inline Swift script. I love that kind of simplicity.

    The compilation is just a single swiftc call:

    swiftc -O main.swift AppDelegate.swift \
    -framework Cocoa \
    -framework EventKit \
    -lsqlite3

    That’s it. Two Swift files, three frameworks, one binary.

    Why Local LLM

    I thought about this a lot. I could have used OpenAI’s API or Claude’s API and gotten better classification accuracy out of the box. But these are my wife’s text messages. They contain personal details about my kids, our schedules, where we’ll be and when. I’m not sending that to a third party.

    Ollama makes this easy. You install it, pull a model, and you have a local inference server running on localhost. The app just makes HTTP requests to http://localhost:11434. Everything stays on my machine.

    The classification accuracy with deepseek-r1 is honestly great for this use case. It’s not trying to write poetry. It’s looking at a text message and deciding “is this an event or not” and pulling out structured data. Local models handle that just fine.

    The SQLite Trick

    iMessage on macOS stores everything in a SQLite database at ~/Library/Messages/chat.db. The app reads it directly using the SQLite3 C API (no ORMs, no wrappers, just raw queries). It tracks which messages it’s already processed using ROWIDs so it never creates duplicate events.

    You do need Full Disk Access enabled for the app since chat.db is in a protected directory. The app checks for this on launch and walks you through enabling it if needed.

    What It Actually Catches

    Here’s the kind of stuff that used to slip through the cracks and doesn’t anymore:

    • “Soccer practice moved to Thursday at 4:30”
    • “Dentist appointment for the kids next Wednesday at 2”
    • “My mom is coming over Saturday around noon”
    • “Can you take and pick up the kids from school tomorrow?”
    • “I’m traveling for a work event the second week of April to Houston” (Yes it will do a multi day entry accurately”
    • “Don’t forget we have that dinner thing Friday at 7, it’s at that Italian place downtown”

    The LLM is good at parsing casual language. My wife doesn’t text in calendar-event format. She texts like a normal person. And the model handles it.

    Try It

    The repo is at github.com/rfaile313/iMessageWatcher. Clone it, run ./build.sh, configure your contact, and you’re done.

    You’ll need:

    • macOS 14+
    • Ollama installed and running
    • Full Disk Access for the app
    • Calendar and Reminders permissions

    It’s free, it’s open source, and your data never leaves your machine. If you’re someone who drowns in notifications and occasionally misses the important stuff from the people who matter most, this might help.

    It definitely helped me stop being the guy who forgets about Tuesday at 5pm.

  • Journaling, Blogging, and Embracing Imperfection

    Journaling, Blogging, and Embracing Imperfection

    Hello, hello! I know it’s been a long time since I’ve made a post on here. My last proper blog post was on February 1st, 2021 😱. I wrote about the Global Game Jam Contest that I participated in that year. The only other thing I posted was my “Life at Automattic” post on August 8, 2023, which wasn’t a true post from me—it was just a repost of an article I had been featured in at work. I wanted to blog more, but every time I thought about it, I considered the work that goes into it. I have to really think through and refine everything, consider the potential reader, and how it’s going to read years into the future, etc. My colleague, Alex Kirk, summed this up perfectly:

    When you have a blog, it can feel like it pressures you into publishing. The longer you let it linger, the more abandoned it looks. Thus, it can feel like as soon as you start a blog, you put yourself into a position where publishing becomes a chore rather than just a powerful means of expression.

    But I’m starting to think that all of that isn’t really that important. Alex is Right. No matter how much I think something over, proofread it, or go back through it, there are going to be mistakes and things I regret. It’s just like when you write code—every time you write code, it becomes legacy code. So, I’m going to try to worry less about how long it’s been since I’ve posted or if what I’m posting here is perfect or not, because there are a lot of other benefits to blogging. With the advancements in AI and LLM and stuff, maybe one day, people will be able to train an LLM against my blog and essentially be able to “talk to me” based on what I’ve written there over the years. It’s not a new idea or anything, but I think it is pretty cool:

    I think it’s also helped that I started journaling recently, I’ve been doing it for about a month now. Automattic has had this really great product called Day One, which is a journaling app. It’s super easy to use, it’s intuitive and has all the features you would expect to make journaling an easy breazy experience. I especially like the sync between all my devices, the daily prompts, and the comprehensive metadata that contains everything from geolocation, what the weather was, even what phase the moon was in 😆. This isn’t even an ad, it’s just a good product. I mainly started so that I would have something to look back on over the past several years and see what I was doing or thinking or what was going on during that time. I did this because when I started thinking about the last 10 years, I really couldn’t drill down granularly to a day-to-day level and could only really think about larger events and happenings, which was kind of shocking. Then, when I thought about like the last 20 or 30 years, oh boy, I felt like there was so much lost there.

    So I’m going to continue journaling and start blogging more and getting all of these ideas and words in my head out there on paper somehow. Not just for future me, but for my friends, family, and kids. I will say that one thing I’m always concerned about is security and privacy. I already put a lot of personal information out there on the Internet, and while I would love it if everybody could just read stuff and move about their day, I would be remiss in my duties if I didn’t recognize that there are some crazy people out there, and we all have to be kind of cautious. So, I’m still going to try to keep my family’s security and privacy at the forefront of everything, still trying to focus on thoughts and ideas that might be beneficial to me or anybody else reading. I do still smile when I receive emails from people telling me that they found value in something that I wrote, or a new comment on one of my YouTube videos telling me how nothing worked or clicked for them until they watched my video. Here’s one, for example:

    This video is over three years old, and I still get nice comments like that every week ☺️

    So anyway, to wrap this up, I guess long story short—I’m not going to commit to anything. It’s possible, although unlikely, that this will be the last post for the year, but what I am saying is that I’m not going to worry about it. It doesn’t matter if the blog looks abandoned, it doesn’t matter if it takes an extra amount of time between each post, it doesn’t matter if I do five posts in a week and then nothing at all. I’m just going to blog when I feel like blogging and not worry so much about how it appears to anybody else because, at the end of the day, while it is one of my goals and I do hope that people enjoy reading it and find value in it, it’s really for me. 🙂


    [Update] I wrote:

    With the advancements in AI and LLM and stuff, maybe one day, people will be able to train an LLM against my blog and essentially be able to “talk to me” based on what I’ve written there over the years. It’s not a new idea or anything, but I think it is pretty cool:

    Less than 10 minutes after I posted this. My colleague Jeremy Herve told me that we already have this 😳 — so um, I guess enjoy!