I wanted to share my long process to get something done. So most of the prompts and Claude responses are included here to show the entire process.
I have had a long standing bug in Apple calendars app. Starting in March of 2023 I was no longer able to print my monthly calendar as a list format pdf document.
My procedure before then was at the end of a month I would go in to Calendar and print as a list, choose all days of the month, save as PDF. This is so I could keep a record of older calendars. I adopted this tactic because Apple Calendar starts to fail once you have a lot of individual events. Eventually at about 3-4 years I have to clean out by deleting all older events tor Apple calendar crashes consistently. A typical month for me has about 600 separate events. A searchable PDF is the best, most compact way to save the data. I have calendar entries like this going back to 1996 that I frequently reference.
When this started failing I would call in and report it to Apple, I’ve spent untold hours with Apple tech support trying to get them to fix it. Their “solutions” all involved iCloud which is a non-starter for me. Eventually they would finally tell me to upgrade to the latest and greatest system version when I ran off the end of the current one. I would and the cycle started all over again.
I searched back through my old calendars until I found a month where Apple had been able to create the report and that didn’t have any private information I was not willing to let Anthropic see.
I uploaded that file and gave Claude this prompt
Check Skills and the Skill Index for relevant context.
There is a bug in printing of calendars in Apple calendars. where it crashes after a few days of info. I want to create a searchable pdf file in color of an entire month of calendar entries in the Apple calendar list format. Here is an example from when this last worked. How would you create this type of document and what privileges do you need?
Here is the Claude response:
Great example to work from — I can see the full format clearly across all 8 pages. Let me break down what's needed and how to build it.
What the format requires
Looking at your February 2023 PDF, the document has these elements on every page:
Header (repeated each page):
Large "Events" title + date range
Color legend — calendar names with their colored squares, 3-column layout
Two mini month-grid calendars (current + next month), with the current week highlighted in black
Body — two-column layout, one section per day:
Day heading in large bold ("Wednesday, February 1")
"All-Day Events" subsection — colored dot + event name in bold
Dashed separator line
"Events" subsection — colored rectangle + time range + description
Horizontal rule between days
Page N/N footer
How to build it
The cleanest approach is a two-step Python script you run on your Mac:
Step 1 — Extract data from Apple Calendar using AppleScript called from Python via osascript. This pulls event titles, start/end times, all-day flag, and — critically — each calendar's assigned color and name.
Step 2 — Generate the PDF using reportlab, replicating the layout with the two-column design, colored squares, mini calendars, and searchable text throughout.
This produces a native searchable PDF (not a screenshot), exactly like what Apple Calendar used to generate.
Privileges needed
PermissionWhy it's neededWhere to grant itCalendar accessSo the script can read your eventsSystem Settings → Privacy & Security → Calendars → enable Terminal (or whichever app runs the script)No Full Disk Access neededWe use AppleScript/Calendar API, not direct SQLite reads—No special PDF permissionsreportlab generates PDFs freely—
The script runs entirely on your Mac. I would write it here and you'd run it in Terminal. The output PDF would save wherever you specify.
Before I build it — a few questions
Would you like me to go ahead and write the script? If so, I need to confirm a few things:
The questions involved whether the month should be configurable and which way to get data out of Apple.
The script was generated along with what I needed to do to run it from terminal. I then asked Claude to save the instructions and the script in my tools folder (a place in the Robot Assistance Obsidian vault)
I ran the script and hit errors. There followed a back and forth where I ran the script, encountered errors, fed the error report back to Claude, got the script rewritten, tested again. Wash, Rinse repeat. It took 5 tries to get it to run completely. I can provide all the back and forth if anyone really wants to see it.
I then tried to run it for several of the missing months and quickly discovered that there were duplicate entries in the output. The issue is that part way through 2023 I moved all my calendars to a calDAV server on our in-house Synology NAS so they were accessible to my husband. For a while both versions were being kept on my mac and the script got both. I’ve since eliminated the on my mac calendar use so I started a new task to get the script to only handle the calDAV calendars. iCloud is a calDAV account and the script was looking at both. We fine tuned it to only strict calDAV accounts.
Several syntax errors later that got fixed and then this response from Claude
The root problem is deeper — Calendar.app's AppleScript dictionary doesn't actually have an account class at all. every account, calendar accounts — none of it exists in the dictionary. The only iterable is calendars (the flat list), which is what the original script uses. There's no way to get account type through AppleScript.
The right tool for this is EventKit, which explicitly knows each calendar's source type (CalDAV, iCloud, local, etc.). I'll use a small inline Swift script to get the CalDAV calendar names, then filter the AppleScript results in Python. If Swift fails for any reason, it falls back gracefully to all calendars.
This is one HUGE reason I do not trust Claude to write decent code in general.
However, i felt it was worth attempting to get past this and continued on.
Ran into the Claude lots it’s memory issue and had to explicitly direct it to read the 2 different task descriptions and results all over again. Also had it apply my naming conventions to the resulting output. Tried to run and it went forever and after 40 minutes it still hadn’t finished. Killed it on the terminal and back to Claude and explained the problem. Had again to reference the scripts it had already written but finally got this output.
Got it — here's exactly why it's slow, and then the fix.
The cause: February 2023 is 3 years old. When AppleScript tells Calendar.app every event of c whose start date >= ..., Calendar.app has to reach out to iCloud to fetch that historical month's data — once per calendar, sequentially. With 16 CalDAV calendars, that's 16 separate iCloud round-trips, each potentially taking several minutes. The bulk property fetching helps once data is retrieved, but it doesn't help the initial sync cost.
The fix: Use EventKit/Swift for the event fetch too. EventKit queries the local EKEventStore SQLite cache directly — no Calendar.app GUI involvement, no iCloud round-trip per-calendar. We already know Swift/EventKit works on your machine (it found all 17 calendars in seconds). We just need to extend it to fetch events as well.
Now implementing:
Some more back and forth
Root cause of the 40-minute hang: AppleScript's every event of c whose start date >= ... tells Calendar.app to evaluate all events for a calendar. For a 3-year-old month like February 2023, Calendar.app doesn't have that data cached in its active display state, so it makes a full iCloud sync call for each calendar — 16 calendars × 2-4 minutes each = your 40+ minutes. The bulk property fetching optimization doesn't help here because the bottleneck is the initial data retrieval, not the property reads.
What changed in the CalDAV script:
Added _SWIFT_CALDAV_CALENDARS — gets calendar names and colours via EventKit in one call
Added _SWIFT_CALDAV_EVENTS — fetches all CalDAV events for the target month via EventKit; reads the local EKEventStore SQLite cache directly so historical months are just as fast as the current one
Added run_swift() — shared helper for both Swift scripts
get_calendars() and get_events() now use Swift as the primary path, with AppleScript as a named fallback (_get_events_applescript()) that prints a warning about expected slowness
generate_pdf() simplified — no more caldav_names plumbing through every function call
The startup output will now show two EventKit steps completing in seconds before the PDF render begins.
Now I started adding additional features: My prompt “Can you modify the place it puts the resulting pdf to be my Downloads folder?”
Claude added and fixed that.
Looked at the resulting file and noticed some formatting issues so sent this one
“The key at the top of the page with the calendar name and the colors is printing out over the top of the 2 month calendar displays on the top. I don’t need the previous month view at all and it can be deleted and leave less space in the columns so it all fits.”
I can see exactly what's happening. The legend uses full CONTENT_W (540pt), but the two mini calendars sit in the top-right starting at x=324 — so legend column 2 (starting at x=396) runs straight into the second calendar.
Two changes needed in both scripts:
_draw_mini_calendars — drop the next-month calendar, draw only the current month
paint_header — constrain legend width to the space left of the single remaining mini calendar
Verified that this was fixed and added this prompt “When I run the scripts I’d like to enter in the month as YYYY-mm rather than a space in between the year and month. Is that a simple change to make?”
Got 2 ways to run the script back
python3 generate_calendar_pdf_caldav.py 2023-02
python3 generate_calendar_pdf_caldav.py 2023-02 ~/Documents/custom-name.pdf
Next feature “Change the naming convention for the output file to be YYYY-mm_Oogie_Calendar_List”
That all got added.
My next feature was to define which of the calendars it found that I wanted in my report. “If I want todrop a calendar from the final result how can I best decide that? Perhaps a list with a checkbox to include or not? or do you have better ideas?”
Option A — Config file (recommended for your workflow)
A small JSON file alongside the script (or in ~/.config/) holds a list of excluded calendar names. You edit it once; every subsequent run respects it automatically. No prompts, no extra packages.
Option B — Interactive checklist at runtime
Each time the script runs, it fetches the calendar list first, then presents a numbered menu with [x] / [ ] markers. You type numbers to toggle, then press Enter to proceed. No extra packages needed (just input() loop). Works well but adds a step to every run — annoying when you just want to regenerate the same month.
Option C — Config file + one-time setup flag
Best of both: defaults to a saved config (like A), but running with --configure drops into the interactive checklist (like B) and saves your selections for next time. Slightly more code but genuinely the most convenient long-term.
Option D — Command-line exclude flag
--exclude "Birthdays" --exclude "US Holidays" on the command line. Fine if you always exclude the same set and are comfortable with CLI flags, but fragile if calendar names have spaces or change.
My recommendation is Option C — the config file with a --configure mode. Your exclusion list is stable month-to-month (you always want Birthdays and Holidays out, for example), so you don't want to re-pick every run. But having a clean interactive setup mode means you never have to hand-edit JSON.
Want me to implement Option C in both scripts?
I said to implement option C and got a long list of instructions on how to run the scripts. Then I entered this “Update the instructions on how to run these scripts in the xport_guide file”
I got back a list of things that were no stale or incomplete which Claude fixed.
Then I added this after all that work (trust but verify) “Update the skills index, the documentation and tell me any leftover files I need to delete . All the items for running this script should be in the tools folder.”
I will abbreviate what it did but but wanted to show something at the end.
All documentation is updated. Now here are the files to delete — two Python cache directories that should never have been committed to the workspace:
Delete these:
Oogie_Robot_Assistant/__pycache__/generate_calendar_pdf.cpython-310.pyc
Oogie_Robot_Assistant/__pycache__/ ← the folder itself, once the .pyc is gone
Oogie_Robot_Assistant/Tools/calendar-pdf-export/__pycache__/generate_calendar_pdf.cpython-310.pyc
Oogie_Robot_Assistant/Tools/calendar-pdf-export/__pycache__/ ← same
These are auto-generated Python bytecode cache files. The one at the workspace root (Oogie_Robot_Assistant/__pycache__/) got there because the script was run from the wrong directory at some point — it belongs nowhere near the workspace root. The one inside Tools/calendar-pdf-export/__pycache__/ is also auto-generated and has no place in your workspace folder.
Python recreates them automatically when needed, so deleting them is safe. You can trash both __pycache__ folders entirely.
Everything else is in the right place — the two scripts and the guide are all inside Tools/calendar-pdf-export/, and the skill routing docs (Skills/calendar-pdf-export.md and Skills/_skill-index.md) are correctly in the Skills/ folder where they belong.
I asked “Where is the config file stored?”
The config file is stored on your Mac at:
~/.config/calendar-pdf-export/config.json
That expands to /Users/yourname/.config/calendar-pdf-export/ — it's a hidden folder in your home directory, outside the workspace folder entirely. The scripts create it automatically the first time you run --configure.
This location was chosen deliberately so the config is personal to your Mac user account and not tied to the workspace folder. If you ever move or share the workspace, your calendar preferences don't travel with it (which is appropriate since calendar names are specific to your iCloud account).
If you'd prefer it to live inside the workspace folder instead — so it's visible alongside the scripts and backed up with everything else — I can change the CONFIG_PATH in both scripts to something like Tools/calendar-pdf-export/calendar_pdf_config.json. Just say the word.
I sent “move it to the Tools directory please”
I'll anchor it to the script's own directory using __file__ so it works regardless of where you run the script from.Edited 4 filesEdited 4 filesNow update the three doc files to reflect the new path:Edited 3 filesEdited 3 filesDone. The config will now be created at:
Tools/calendar-pdf-export/calendar_pdf_config.json
Using __file__ to locate it means the path resolves correctly no matter what directory you run the script from — it always lands right alongside the scripts. If you had already run --configure before this change, you'd need to run it once more to write the new file (the old one at ~/.config/calendar-pdf-export/config.json can be deleted).
I sent this “Is everything regarding this now clean and no extra files hanging on anywhere?”
and got this
Interesting — calendar_pdf_config.json already exists in the tools folder, and the __pycache__ directories are gone. Let me quickly verify the config file looks correct and spot-check the key references across all four doc/script files.Searched 2 patterns, ran a commandSearched 2 patterns, ran a commandTwo small things to fix — both in the guide:
The comment inside both scripts still says (~/.config/calendar-pdf-export/config.json) — stale, but it's a code comment so harmless. I'll clean it up anyway.
The guide's sample terminal output still shows the old ~/.config/... path in the example session.
Edited 3 filesEdited 3 filesEverything is now clean. Here's the full picture:
Tools/calendar-pdf-export/ — 4 files, all correct:
generate_calendar_pdf.py
generate_calendar_pdf_caldav.py
calendar_pdf_export_guide.md
calendar_pdf_config.json ← already exists with your saved exclusions (Grazecart Times, Planning)
Skills/ — 2 files, both correct:
calendar-pdf-export.md
_skill-index.md
No orphaned files, no __pycache__ directories, no stale path references anywhere. Everything points to Tools/calendar-pdf-export/calendar_pdf_config.json consistently across both scripts, the guide, the skill doc, and the index.
All told I spent 4 hours getting it all working properly and about 15 minutes generating the calendar files for all months since February 2023. Since this is something I wanted and could not do at all, the time, although a bit excessive, was worth it for me.