The first ten minutes at the desk go the same way most mornings. Open Calendar, count the meetings, notice the 10:00 that moved. Open Reminders, scroll past the things that were due last Thursday, find the one that is due today. Then try to build a morning brief in your head from both lists while the inbox loads.
None of it is hard. It is two apps and some arithmetic about gaps, every weekday, and the reminder that matters is usually below the fold.
This flow does the reading for you. At 8:30 on weekdays it collects today's events and your open reminders, has a model turn them into a plan of under 120 words, and opens it as one page: today on a timeline, the model's suggestions sitting in the free slots, and your reminders marked overdue, today or later. Click Got it and it is gone until tomorrow.
The flow
Eight nodes in three Sections: gather, think, show. Sections change nothing about how it runs; they make the canvas read as its three jobs.
Gather
Schedule is in calendar mode: Days set to Weekdays, Time 8:30 AM.
Today's date is a Run Script node with one line: date '+%A, %B %-d, %Y'. The model needs to know what "today" and "overdue" mean, and the ISO stamps the other nodes emit (timestamp, rangeStartISO) are in UTC. In Tokyo or Sydney, 8:30 in the morning is still yesterday in UTC, so a model reading those stamps gets the day wrong. The script prints the local date, which arrives downstream as {{output}}.
List Calendar Events has Range set to Today and every calendar included. It emits events as a real array, eventCount, and eventsText, which is already one readable line per event (start, end, title, calendar name).
Apple Reminders runs the List Reminders operation on one list, with Show Completed off and Max Results at 25. Each reminder comes back in results with title, dueDate and priority.
Think
The AI Prompt node is renamed Plan the day. The prompt drops in all three things the Gather section produced:
Today is {{output}}.
My calendar for today:
{{eventsText}}
My open reminders, as JSON (dueDate is empty when a reminder has no due date):
{{results}}
and asks for the first fixed commitment, the reminders due today or overdue, and any gap of an hour or more with a reminder that fits it. Temperature is 0.3, and the system prompt forbids inventing a meeting, task or time.
Provider is blank in the download, which means your default provider. Pick deliberately, because privacy follows the provider: a cloud model receives your event titles, times, calendar names and reminder titles every weekday. Apple Intelligence runs on the Mac at no cost, and this prompt is small enough for its short context window. Ollama or LM Studio also keep it local.
Show
HTML Template builds the page. {{payload}} puts the whole payload into it as JSON, so the page has the events, the reminders and the plan as real data, and a short script in the page draws them: the day and date, three counts (events, reminders, free time), a timeline from 9 to 6 (wider when your day is) with each event where it falls, and the model's Gap … lines placed in those free slots. The rest of the plan is a list with its labels as tags, and each reminder is marked Overdue, Today or Someday against today's date. If the script cannot read the data, the plan still shows as plain text. Got it calls watchflows.emit(). Everything is inline: a page shown by HTML View cannot load anything from the network.
HTML View shows it with Window set to Window and Chrome set to None. The page's body is transparent and the page is one rounded element, so the window is the page's own edges. A window brings Watchflows forward, which is the point at 8:30: it is the first thing you read.
Permissions you will be asked for
Press Run on the canvas once after importing, so the prompts come up while you are watching and not at 8:30 tomorrow.
| Node | What macOS asks | If you said no |
|---|---|---|
| List Calendar Events | Full access to Calendars | System Settings, Privacy & Security, Calendars |
| Apple Reminders | Whether Watchflows may control Reminders (it is driven through AppleScript) | System Settings, Privacy & Security, Automation |
| Brief failed (Notification) | Whether Watchflows may send notifications, if you have not allowed it already | System Settings, Notifications |
The Calendars prompt only appears once. Deny it and the node cannot ask again; you switch it on in System Settings yourself.
What bites in practice
The Mac was asleep at 8:30. The Schedule node makes up a missed morning once, at the next wake, with catchUp set to true. Five slept-through mornings still produce one brief, not five.
Nobody clicks Got it. While the page is open, the flow cannot run again. Give up after is set to 240 minutes so a page left on a desk over a long weekend cannot block Monday. When the wait runs out the node fails and Execution History records a failed run. That is the price of not blocking.
Reminders reads one list. List Reminders takes a list by name, and the default here is Reminders. Change the List field to where your tasks live; a list that does not exist fails the node. The Today and Scheduled views in the Reminders app are smart lists and cannot be read this way. A second Apple Reminders node for another list would overwrite results, so pick the list that matters.
The node does not filter by date. It returns open reminders in the order the list holds them and stops at Max Results. That is why the model gets the date and picks what is due. Raise Max Results for a long list. dueDate is the text Reminders gives AppleScript, in your Mac's language and date format; models read it fine.
The provider is down. The Failure outlet on Plan the day is wired to a notification that says the plan could not be written and why, instead of the run failing where nobody is looking.
Variations
- Skip late briefs. Put a Condition on
{{catchUp}}right after Schedule and only continue when it is false. - Swap the page for a Notification with
{{result}}as the body. Less room, no window to dismiss. - Get pinged about new meetings. The Calendar Event (Apple) trigger fires when an event is created, edited or deleted, not before it starts, so it cannot do "ten minutes before". What it can do: fire on Created, check
{{calendarEvent.location}}containszoom.uswith a Condition, and post a Notification whose Action URL is{{calendarEvent.location}}. When the location is the link itself, the new meeting is one click away.
To build it from a sentence instead of importing it, paste this into the Flow Builder:
Every weekday at 8:30 AM, get today's date with a bash script, list today's events from Apple Calendar, and list my open reminders from the Reminders list in Apple Reminders. Have AI write a plan for today in under 120 words: my first meeting, the reminders due today or overdue, and any free hour. Show it in a window: today's events on a timeline with the free gaps and the plan's suggestions in them, and my reminders marked overdue, today or later, with a Got it button that gives up after 240 minutes. If the AI step fails, send me a notification with the error.
Node reference: Schedule, List Calendar Events, Apple Reminders, AI Prompt, HTML View, Sections.