Every Morning, an AI Reads My Watch and Tells Me What to Do

2026-08-06

I wear a Garmin watch. It knows a lot about me: sleep, heart rate variability, stress, training load, body battery. And for years, all that data did... nothing. It sat in an app I opened twice a week, looked at a graph, and closed.

So I built myself a personal trainer out of three boring pieces: a git repository, a cron job, and Claude running headless on a small server.

Now every morning, before I've had coffee, there's a push notification on my phone with a short report: how I slept, whether my body is ready to train, what my weight trend looks like - and a concrete plan for today. Not "move more". Something like: "30 min easy cardio, zone 2. Yesterday's load was high and your HRV is below baseline - keep it light."

Here's how it works, and why I think the pattern is more interesting than the fitness part.

The problem: data with no consequences

Fitness trackers are great at collecting and terrible at concluding. The Garmin app will happily show me twenty charts, but it won't say:

  • "Your weight trend this week is flat, but you've also slept badly for three nights - don't panic-diet, fix the sleep."
  • "Your resting heart rate has been creeping up all week and today's readiness is low. This is not the day for intervals - go for a walk."

That kind of conclusion needs context across days and my personal goals. An app doesn't have them. A language model with access to my history does.

The idea: a repo as memory, an LLM as analyst, cron as discipline

The whole system is one small git repository. I've published it as a reusable template: torm-fitness-template. The structure:

goals.md               # my goals and training rules - the report writes advice *against* these
data/
  activities.csv       # 1 row = 1 workout
  days.csv             # 1 row = 1 day (sleep, HRV, steps, stress, readiness...)
  weight.csv           # 1 row = 1 weigh-in (smart scale)
  journal/
    2026-08-03.md      # journal: 1 file = 1 day, written by AI, annotated by me
prompts/
  daily_report.md      # the prompt for the headless run
scripts/
  daily_report.sh      # cron entry point: fetch -> write -> commit -> notify
docker-compose.yml     # two containers: Garmin MCP server + Claude-on-cron

Three roles, cleanly separated:

  • 📁 The repo is the memory. Every day appends rows to CSV files and one Markdown journal entry. Append-only, never rewritten. Git history is the audit log for free.
  • 🤖 Claude is the analyst. Every morning a cron job runs Claude Code in headless mode. It pulls yesterday's data from Garmin (via an MCP server), appends the CSV rows, writes the journal entry, and produces a short report with advice for today.
  • Cron is the discipline. I don't have to remember anything. The report exists whether I feel motivated or not. That's the entire point - my motivation is no longer a dependency.

The morning flow:

Garmin watch  ->  Garmin Connect  ->  MCP server  ->  headless Claude
                                                          |
                          appends CSV rows + writes journal entry
                                                          |
                              script commits & pushes to git
                                                          |
                       HTML report on my server  +  ntfy push with a TLDR

One day, two formats

Every day lives in two places, on purpose:

  • 📊 CSV - for the machine. Numbers only. Easy to chart, easy to compute trends over, easy for the next AI run to load as context.
  • 📝 Markdown - for the human. The journal entry has the story: what happened, what the numbers mean, and advice for today. At the bottom there's a "My notes" section where I add my own observations - how I felt, what hurt, what worked.

This split turned out to be the most important design decision. CSV keeps the data queryable. Markdown keeps it readable and annotatable. And my handwritten notes become context for the next morning's analysis - the AI reads the last few journal entries before writing a new one. It's a feedback loop: the trainer remembers what I told it.

Goals live in a file, not in a prompt

There's one file, goals.md, that the daily run always reads. It could say, for example:

  • 🏁 First half marathon in October. Goal: finish it, no time target.
  • 📅 Three runs a week, one of them long. Missed run = don't cram it into the next day.
  • 💪 Two short strength sessions a week, for the knees.
  • 🛌 Bad night of sleep = swap the run for a walk. No heroics.
  • 🧘 One full rest day a week, no exceptions.

The daily advice is generated against this file. When my goals change, I edit one Markdown file - not a prompt buried in a script, not a fine-tune, not an app setting. The system's "personality" is a text file under version control.

The unglamorous parts that make it actually work

A few rules learned the hard way:

  • Append-only. The AI never edits old rows or old journal entries. If a row for a date already exists, it fills gaps instead of duplicating. Idempotency means a re-run can't corrupt history.
  • 🕳️ No data = empty field. The prompt explicitly forbids inventing numbers. An LLM will happily hallucinate a plausible resting heart rate; an empty CSV cell is infinitely more valuable than a fake one.
  • Wait for the morning sync. The report is only useful if it includes last night's sleep - which requires the watch to have synced. The script runs every 10 minutes in a morning window, waits for the sync plus a short grace period (time to step on the scale), and generates the report exactly once per day. After a cutoff hour it gives up waiting and reports with yesterday's data, clearly labeled.
  • 📲 Push the TLDR, not the report. The notification is 6-8 short lines. The full report is one tap away. Respect the morning brain.

Steal the recipe

The pattern is not really about fitness. The general recipe:

  • 📁 Pick an area where data exists but conclusions don't.
  • 📊 Define a tiny, boring data format. CSV + Markdown is plenty.
  • 🎯 Write your goals into a file the AI must read every run.
  • 🤖 Write one prompt that turns "fetch, append, conclude, advise" into a daily routine. Make it idempotent and forbid invented data.
  • ⏰ Put it on cron. Send yourself the TLDR.

No app, no database, no dashboard framework. A repo, a prompt, a schedule.

Or skip the setup and start from my template: torm89/torm-fitness-template - the full skeleton with empty data files, the daily prompt, the cron script, and a docker-compose.yml that runs the Garmin MCP server and Claude-on-cron as two containers.

Two warnings

  • The hard part is not the AI - it's writing down goals you'll actually stand behind. Honest ones, not aspirational ones. The AI part took an evening.
  • Keep raw health data private. My data repo is private; what's public is the template - the pattern with empty data files. Publish the template, not your medical history.

Built with: a Garmin watch, Claude Code running headless, the Taxuspt/garmin_mcp MCP server, cron on a Raspberry Pi, git, ntfy for notifications, and dufs for serving the HTML report. Template repo: torm89/torm-fitness-template.