| name | apollo-calendar-ingest |
| description | Sync, query, and ingest calendar events from Google Calendar (work and personal) to drive daily schedule planning and automated work logs. |
| version | 1.0.0 |
| author | Apollo |
| license | MIT |
| metadata | {"hermes":{"tags":["calendar","google-workspace","ingest","logs"],"related_skills":["google-workspace","work-log","morning-briefing","wind-down"]}} |
Apollo Calendar Ingest
Handles Google Calendar syncs across all of your accounts (work, personal-main, personal-junk) to drive schedule-planning, morning briefings, and automated work log accomplishments.
Credentials & Tokens
- Tokens are located at:
~/.hermes/google_tokens/{work,personal-main,personal-junk}.json
- Wrapped in the cross-account script:
~/.hermes/skills/productivity/google-workspace/scripts/gws_multi.py
Querying Schedule (Read-Only)
To retrieve schedule events for a specific timeframe (e.g. today or tomorrow):
python3 ~/.hermes/skills/productivity/google-workspace/scripts/gws_multi.py --account all calendar list --start <YYYY-MM-DD>T00:00:00<OFFSET> --end <YYYY-MM-DD>T23:59:59<OFFSET> --max 50
This schedule feeds directly into:
- Today's Daily Note: Populates
## 📅 Schedule & Events.
- Morning Briefing: Drives the active schedule briefing.
- Daily Work Log: Compiles and reconciles meeting attendances.
Scheduling Events (Write Access)
Apollo has write access to Google Calendar. You can directly schedule events on Justin's behalf (e.g., during briefings or from forwarded email instructions) rather than creating tasks in Todoist:
python3 ~/.hermes/skills/productivity/google-workspace/scripts/gws_multi.py --account work|personal-main calendar create --summary "Event Title" --start "2026-06-09T14:00:00" --end "2026-06-09T15:00:00" --description "Context..."
Note: Specify the single correct target account; do not use --account all for write operations.
Common Pitfalls
- Declined Invites showing as Confirmed: When a user declines a calendar event, the top-level
status of the event in the API response remains "confirmed". The declined status is nested inside the attendees list under the owner's email as responseStatus: "declined". Always verify the owner's response status before compiling schedules; otherwise, declined/cancelled meetings will mistakenly be shown as active/confirmed.
- Deleting Declined Events: When the user explicitly requests to delete or clean up declined meetings from the calendar, use
calendar delete EVENT_ID under the correct account (e.g. work) to completely purge the event from their calendar views.
Handling Vacation & Meeting Cancellations
When Justin informs you that he is taking a day off, has cancelled work meetings, or is on vacation through a specific date, you must actively update the environment:
- Calculate Weekdays: Determine the list of weekdays (Monday–Friday) between today and the specified target date.
- Update Days-Off Config: Append these dates (in
YYYY-MM-DD format, one per line) to ~/.hermes/days-off.txt. This ensures the work-day checking system (work_day.py) evaluates them as non-work days, setting is_work_day: False and skipping work log reviewer warnings.
- Patch Daily Notes:
- Locate the daily notes in
~/Developer/obsidian-vault/Daily Notes/ for the affected dates.
- Update the top
Preview Summary blockquote to note the cancellation/vacation.
- Under
## 🗓 Schedule & Events, cross out the cancelled work meetings using strike-through syntax (e.g., ~~Meeting Name~~) and append — *Cancelled*.
- Regenerate Briefing Cache: Execute the morning cache compilation script:
python3 ~/.hermes/scripts/generate_morning_cache.py
Verify that the output confirms Is Work Day: False, Work Log Status: skipped.
- Audit Live Calendar Discrepancies: Run a live Google Calendar query for the cancellation range. If meetings remain with
status: "confirmed", report the discrepancy clearly to Justin and ask if he wants you to delete them on his behalf.