← all projects
shipped 2026

Morning Brief Agent

A friendly daily email — workout, weather, one calm signal.

Morning Brief Agent — workflow diagram

Why this exists

I wanted the first thing read every morning to be a single calm signal — not the news, not group chats, not Slack. Something that answers two questions cleanly: what’s the workout today, and what is it going to feel like outside.

Off-the-shelf “habit” apps drown that in dashboards and streaks. The brief is the opposite: one short email. No app to open.

What it does

Every day at 06:30, an inbox gets a short, warm note that includes:

It’s intentionally one screen tall on a phone. If you have to scroll, the brief failed.

How it works

The flow is simple — that’s the point.

  1. Trigger — A scheduled GitHub Action wakes a Python script at 06:30 local time
  2. Fetch — Two parallel API calls: OpenWeatherMap for the day’s forecast, and a lookup against a local table of workouts keyed by day-of-week
  3. Compose — Claude is given the raw signals and a tight prompt that fixes tone, length, and structure. It returns a short, friendly note
  4. Send — A plain SMTP send to the recipient’s inbox. No service in the middle, no third-party form

End-to-end runtime is under five seconds, which means cost is effectively the Claude token spend (low single-digit cents per day) plus the free tier of OpenWeatherMap.

Choices that mattered

Email, not an app. Apps demand attention; email respects that you already have an inbox open.

One LLM call, templated. Early versions used the LLM for routing and decisions. That added latency and made failures hard to read. Now the script makes the decisions deterministically and the LLM only does the writing.

Hard length cap in the prompt. The model would otherwise drift toward over-helpful — “here are seven tips for your push day”. The prompt caps it at a known number of lines, with examples of the right length right above the call.

Light vs. normal mode. Some mornings call for a 30-minute version of the day’s split. The day’s metadata supports both, and the script picks based on a simple toggle.

What I’d do differently next time

Status

Shipped. Running daily. The next iteration is mostly about tone and resilience, not new features.

← all projects