
Apple Calendar
- 689 installs
- 638 repo stars
- Updated March 7, 2026
- sundial-org/awesome-openclaw-skills
apple-calendar is an AI agent skill that exposes macOS Calendar.app CRUD and search operations through seven AppleScript shell scripts for developers who need coding agents to manage calendar events from the terminal.
About
apple-calendar is an AI agent skill from sundial-org/awesome-openclaw-skills that bridges macOS Calendar.app to coding agents through seven AppleScript shell scripts. Developers install it with `npx skills add sundial-org/awesome-openclaw-skills --skill apple-calendar`, then agents run commands like cal-list.sh, cal-create.sh, and cal-search.sh from the skill base directory to list calendars, create timed or all-day events, update summaries and locations, delete entries, and search across multiple calendars. Output uses pipe-delimited UID, summary, start, end, and calendar fields for easy parsing. The skill documents YYYY-MM-DD date formats, case-sensitive calendar names, and three RRULE recurrence patterns for daily, weekly, and monthly repeats. It targets darwin-only environments and has recorded 414 catalog installs. Reach for apple-calendar when building agent workflows that schedule standups, sync deployment windows, or query upcoming meetings without leaving the terminal.
- apple-calendar
Apple Calendar by the numbers
- 689 all-time installs (skills.sh)
- +7 installs in the week ending Aug 5, 2026 (Skillselion tracking)
- Ranked #531 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/sundial-org/awesome-openclaw-skills --skill apple-calendarAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 689 |
|---|---|
| repo stars | ★ 638 |
| Last updated | March 7, 2026 |
| Repository | sundial-org/awesome-openclaw-skills ↗ |
How do you automate macOS Calendar from an AI agent?
Use apple-calendar for development tasks
Who is it for?
macOS developers building AI agent workflows that need native Calendar.app CRUD, search, and RRULE recurrence without a cloud calendar API.
Skip if: Developers on Linux or Windows, teams standardized on Google Calendar or Outlook APIs, or anyone who cannot grant Calendar.app automation permissions on macOS.
When should I use this skill?
The user asks an agent to list, create, update, delete, or search events in macOS Calendar.app from the command line.
What you get
Created or updated Calendar.app events, pipe-delimited event listings, full event detail dumps with recurrence and URL fields, and multi-calendar search results.
- Calendar.app events created or updated
- Pipe-delimited event listings
- Full event detail dumps with recurrence
By the numbers
- Bundles 7 AppleScript shell scripts for Calendar.app CRUD and search
- 414 catalog installs recorded in Skillselion
- Documents 3 RRULE recurrence pattern examples in SKILL.md
Files
Apple Calendar
Interact with Calendar.app via AppleScript. Run scripts from: cd {baseDir}
Commands
| Command | Usage |
|---|---|
| List calendars | scripts/cal-list.sh |
| List events | scripts/cal-events.sh [days_ahead] [calendar_name] |
| Read event | scripts/cal-read.sh <event-uid> [calendar_name] |
| Create event | scripts/cal-create.sh <calendar> <summary> <start> <end> [location] [description] [allday] [recurrence] |
| Update event | scripts/cal-update.sh <event-uid> [--summary X] [--start X] [--end X] [--location X] [--description X] |
| Delete event | scripts/cal-delete.sh <event-uid> [calendar_name] |
| Search events | scripts/cal-search.sh <query> [days_ahead] [calendar_name] |
Date Format
- Timed:
YYYY-MM-DD HH:MM - All-day:
YYYY-MM-DD
Recurrence
| Pattern | RRULE |
|---|---|
| Daily 10x | FREQ=DAILY;COUNT=10 |
| Weekly M/W/F | FREQ=WEEKLY;BYDAY=MO,WE,FR |
| Monthly 15th | FREQ=MONTHLY;BYMONTHDAY=15 |
Output
- Events/search:
UID | Summary | Start | End | AllDay | Location | Calendar - Read: Full details with description, URL, recurrence
Notes
- Read-only calendars (Birthdays, Holidays) can't be modified
- Calendar names are case-sensitive
- Deleting recurring events removes entire series
#!/bin/bash
# Create a new calendar event
# Usage: cal-create.sh <calendar> <summary> <start_date> <end_date> [location] [description] [allday] [recurrence]
# Date format: "YYYY-MM-DD HH:MM" or "YYYY-MM-DD" for all-day events
# Recurrence format: iCalendar RRULE (e.g., "FREQ=WEEKLY;COUNT=4" or "FREQ=DAILY;UNTIL=20260201")
# Examples:
# cal-create.sh Personal "Meeting" "2026-01-15 10:00" "2026-01-15 11:00"
# cal-create.sh Personal "Vacation" "2026-02-01" "2026-02-05" "" "Beach trip" true
# cal-create.sh Personal "Weekly Standup" "2026-01-20 09:00" "2026-01-20 09:30" "Zoom" "" false "FREQ=WEEKLY;COUNT=10"
CALENDAR="${1:-}"
SUMMARY="${2:-}"
START_DATE="${3:-}"
END_DATE="${4:-}"
LOCATION="${5:-}"
DESCRIPTION="${6:-}"
ALL_DAY="${7:-false}"
RECURRENCE="${8:-}"
if [ -z "$CALENDAR" ] || [ -z "$SUMMARY" ] || [ -z "$START_DATE" ] || [ -z "$END_DATE" ]; then
echo "Usage: cal-create.sh <calendar> <summary> <start_date> <end_date> [location] [description] [allday] [recurrence]"
echo "Date format: 'YYYY-MM-DD HH:MM' or 'YYYY-MM-DD' for all-day"
exit 1
fi
osascript - "$CALENDAR" "$SUMMARY" "$START_DATE" "$END_DATE" "$LOCATION" "$DESCRIPTION" "$ALL_DAY" "$RECURRENCE" <<'EOF'
on splitString(theString, theDelimiter)
set oldDelimiters to AppleScript's text item delimiters
set AppleScript's text item delimiters to theDelimiter
set theArray to every text item of theString
set AppleScript's text item delimiters to oldDelimiters
return theArray
end splitString
on parseDate(dateStr)
set dateParts to my splitString(dateStr, " ")
set ymdParts to my splitString(item 1 of dateParts, "-")
set theDate to current date
set year of theDate to (item 1 of ymdParts) as integer
set month of theDate to (item 2 of ymdParts) as integer
set day of theDate to (item 3 of ymdParts) as integer
if (count of dateParts) > 1 then
set timeParts to my splitString(item 2 of dateParts, ":")
set hours of theDate to (item 1 of timeParts) as integer
set minutes of theDate to (item 2 of timeParts) as integer
set seconds of theDate to 0
else
set hours of theDate to 0
set minutes of theDate to 0
set seconds of theDate to 0
end if
return theDate
end parseDate
on run argv
set calendarName to item 1 of argv as string
set eventSummary to item 2 of argv as string
set startDateStr to item 3 of argv as string
set endDateStr to item 4 of argv as string
set eventLocation to item 5 of argv as string
set eventDescription to item 6 of argv as string
set isAllDay to item 7 of argv as string
set eventRecurrence to item 8 of argv as string
set startDate to my parseDate(startDateStr)
set endDate to my parseDate(endDateStr)
tell application "Calendar"
try
set cal to calendar calendarName
on error
return "Error: Calendar '" & calendarName & "' not found"
end try
if not (writable of cal) then
return "Error: Calendar '" & calendarName & "' is read-only"
end if
set eventProps to {summary:eventSummary, start date:startDate, end date:endDate}
if isAllDay is "true" then
set eventProps to eventProps & {allday event:true}
end if
set newEvent to make new event at end of events of cal with properties eventProps
if eventLocation is not "" then
set location of newEvent to eventLocation
end if
if eventDescription is not "" then
set description of newEvent to eventDescription
end if
if eventRecurrence is not "" then
set recurrence of newEvent to eventRecurrence
end if
return "Created event: " & (uid of newEvent)
end tell
end run
EOF
#!/bin/bash
# Delete a calendar event by UID
# Usage: cal-delete.sh <event-uid> [calendar_name]
# If calendar not specified, searches all calendars
EVENT_UID="${1:-}"
CALENDAR_NAME="${2:-}"
if [ -z "$EVENT_UID" ]; then
echo "Usage: cal-delete.sh <event-uid> [calendar_name]"
exit 1
fi
osascript - "$EVENT_UID" "$CALENDAR_NAME" <<'EOF'
on run argv
set eventUID to item 1 of argv as string
set calendarName to item 2 of argv as string
tell application "Calendar"
if calendarName is not "" then
try
set cals to {calendar calendarName}
on error
return "Error: Calendar '" & calendarName & "' not found"
end try
else
set cals to calendars
end if
repeat with cal in cals
try
set matchingEvents to (every event of cal whose uid is eventUID)
if (count of matchingEvents) > 0 then
set e to item 1 of matchingEvents
set eventName to summary of e
if not (writable of cal) then
return "Error: Calendar '" & (name of cal) & "' is read-only"
end if
delete e
return "Deleted event: " & eventName & " (" & eventUID & ")"
end if
end try
end repeat
return "Error: Event with UID '" & eventUID & "' not found"
end tell
end run
EOF
#!/bin/bash
# List events in a date range
# Usage: cal-events.sh [days_ahead] [calendar_name]
# Examples:
# cal-events.sh # Today's events from all calendars
# cal-events.sh 7 # Next 7 days from all calendars
# cal-events.sh 7 Personal # Next 7 days from Personal calendar only
DAYS_AHEAD="${1:-0}"
CALENDAR_NAME="${2:-}"
osascript - "$DAYS_AHEAD" "$CALENDAR_NAME" <<'EOF'
on run argv
set daysAhead to item 1 of argv as integer
set calendarName to item 2 of argv as string
tell application "Calendar"
set today to current date
set startOfDay to today - (time of today)
if daysAhead = 0 then
set endDate to startOfDay + (24 * 60 * 60)
else
set endDate to startOfDay + ((daysAhead + 1) * 24 * 60 * 60)
end if
set results to {}
if calendarName is not "" then
try
set cals to {calendar calendarName}
on error
return "Error: Calendar '" & calendarName & "' not found"
end try
else
set cals to calendars
end if
repeat with cal in cals
try
set calEvents to (every event of cal whose start date ≥ startOfDay and start date < endDate)
repeat with e in calEvents
set eventStart to start date of e
set eventEnd to end date of e
set isAllDay to allday event of e
set eventLoc to location of e
if eventLoc is missing value then set eventLoc to ""
set eventLine to (uid of e) & " | " & (summary of e) & " | " & (eventStart as string) & " | " & (eventEnd as string) & " | " & (isAllDay as string) & " | " & eventLoc & " | " & (name of cal)
set end of results to eventLine
end repeat
end try
end repeat
if (count of results) = 0 then
return "No events found"
end if
set output to ""
repeat with r in results
set output to output & r & linefeed
end repeat
return output
end tell
end run
EOF
#!/bin/bash
# List all calendars with their properties
# Usage: cal-list.sh
osascript <<'EOF'
tell application "Calendar"
set calNames to name of every calendar
set calWritable to writable of every calendar
set output to ""
repeat with i from 1 to count of calNames
set calName to item i of calNames
set isWritable to item i of calWritable
if isWritable then
set writeStatus to "writable"
else
set writeStatus to "read-only"
end if
set output to output & calName & " | " & writeStatus & linefeed
end repeat
return output
end tell
EOF
#!/bin/bash
# Read a single event by UID
# Usage: cal-read.sh <event-uid> [calendar_name]
# If calendar not specified, searches all calendars
EVENT_UID="${1:-}"
CALENDAR_NAME="${2:-}"
if [ -z "$EVENT_UID" ]; then
echo "Usage: cal-read.sh <event-uid> [calendar_name]"
exit 1
fi
osascript - "$EVENT_UID" "$CALENDAR_NAME" <<'EOF'
on run argv
set eventUID to item 1 of argv as string
set calendarName to item 2 of argv as string
tell application "Calendar"
if calendarName is not "" then
try
set cals to {calendar calendarName}
on error
return "Error: Calendar '" & calendarName & "' not found"
end try
else
set cals to calendars
end if
repeat with cal in cals
try
set matchingEvents to (every event of cal whose uid is eventUID)
if (count of matchingEvents) > 0 then
set e to item 1 of matchingEvents
set eventSummary to summary of e
set eventStart to start date of e
set eventEnd to end date of e
set isAllDay to allday event of e
set eventLoc to location of e
set eventDesc to description of e
set eventURL to url of e
set eventRecur to recurrence of e
if eventLoc is missing value then set eventLoc to ""
if eventDesc is missing value then set eventDesc to ""
if eventURL is missing value then set eventURL to ""
if eventRecur is missing value then set eventRecur to ""
set output to "UID: " & eventUID & linefeed
set output to output & "Calendar: " & (name of cal) & linefeed
set output to output & "Summary: " & eventSummary & linefeed
set output to output & "Start: " & (eventStart as string) & linefeed
set output to output & "End: " & (eventEnd as string) & linefeed
set output to output & "All Day: " & (isAllDay as string) & linefeed
set output to output & "Location: " & eventLoc & linefeed
set output to output & "Description: " & eventDesc & linefeed
set output to output & "URL: " & eventURL & linefeed
set output to output & "Recurrence: " & eventRecur
return output
end if
end try
end repeat
return "Error: Event with UID '" & eventUID & "' not found"
end tell
end run
EOF
#!/bin/bash
# Search events by text (summary, location, or description)
# Usage: cal-search.sh <query> [days_ahead] [calendar_name]
# Examples:
# cal-search.sh "meeting" # Search all calendars, next 30 days
# cal-search.sh "dentist" 90 # Search next 90 days
# cal-search.sh "standup" 14 Work # Search Work calendar, next 14 days
QUERY="${1:-}"
DAYS_AHEAD="${2:-30}"
CALENDAR_NAME="${3:-}"
if [ -z "$QUERY" ]; then
echo "Usage: cal-search.sh <query> [days_ahead] [calendar_name]"
exit 1
fi
osascript - "$QUERY" "$DAYS_AHEAD" "$CALENDAR_NAME" <<'EOF'
on run argv
set searchQuery to item 1 of argv as string
set daysAhead to item 2 of argv as integer
set calendarName to item 3 of argv as string
tell application "Calendar"
set today to current date
set startOfDay to today - (time of today)
set endDate to startOfDay + (daysAhead * 24 * 60 * 60)
set results to {}
if calendarName is not "" then
try
set cals to {calendar calendarName}
on error
return "Error: Calendar '" & calendarName & "' not found"
end try
else
set cals to calendars
end if
repeat with cal in cals
try
set calEvents to (every event of cal whose start date ≥ startOfDay and start date < endDate)
repeat with e in calEvents
set eventSummary to summary of e
set eventLoc to location of e
set eventDesc to description of e
if eventLoc is missing value then set eventLoc to ""
if eventDesc is missing value then set eventDesc to ""
-- Case-insensitive search in summary, location, or description
set lowerQuery to my toLowerCase(searchQuery)
set matchFound to false
if my toLowerCase(eventSummary) contains lowerQuery then
set matchFound to true
else if my toLowerCase(eventLoc) contains lowerQuery then
set matchFound to true
else if my toLowerCase(eventDesc) contains lowerQuery then
set matchFound to true
end if
if matchFound then
set eventStart to start date of e
set isAllDay to allday event of e
set eventLine to (uid of e) & " | " & eventSummary & " | " & (eventStart as string) & " | " & (isAllDay as string) & " | " & eventLoc & " | " & (name of cal)
set end of results to eventLine
end if
end repeat
end try
end repeat
if (count of results) = 0 then
return "No events found matching: " & searchQuery
end if
set output to ""
repeat with r in results
set output to output & r & linefeed
end repeat
return output
end tell
end run
on toLowerCase(theString)
set lowercaseChars to "abcdefghijklmnopqrstuvwxyz"
set uppercaseChars to "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
set resultString to ""
repeat with c in theString
set charIndex to offset of c in uppercaseChars
if charIndex > 0 then
set resultString to resultString & character charIndex of lowercaseChars
else
set resultString to resultString & c
end if
end repeat
return resultString
end toLowerCase
EOF
#!/bin/bash
# Update an existing calendar event
# Usage: cal-update.sh <event-uid> [--calendar <name>] [--summary <text>] [--start <date>] [--end <date>] [--location <text>] [--description <text>] [--allday <true/false>] [--recurrence <rrule>]
# Date format: "YYYY-MM-DD HH:MM" or "YYYY-MM-DD" for all-day events
# Examples:
# cal-update.sh ABC123 --summary "Updated Meeting"
# cal-update.sh ABC123 --calendar Personal --start "2026-01-16 14:00" --end "2026-01-16 15:00"
# cal-update.sh ABC123 --location "Room 101" --description "Bring laptop"
EVENT_UID=""
CALENDAR_NAME=""
SUMMARY=""
START_DATE=""
END_DATE=""
LOCATION=""
DESCRIPTION=""
ALL_DAY=""
RECURRENCE=""
# Parse arguments
while [[ $# -gt 0 ]]; do
case $1 in
--calendar) CALENDAR_NAME="$2"; shift 2 ;;
--summary) SUMMARY="$2"; shift 2 ;;
--start) START_DATE="$2"; shift 2 ;;
--end) END_DATE="$2"; shift 2 ;;
--location) LOCATION="$2"; shift 2 ;;
--description) DESCRIPTION="$2"; shift 2 ;;
--allday) ALL_DAY="$2"; shift 2 ;;
--recurrence) RECURRENCE="$2"; shift 2 ;;
*)
if [ -z "$EVENT_UID" ]; then
EVENT_UID="$1"
fi
shift
;;
esac
done
if [ -z "$EVENT_UID" ]; then
echo "Usage: cal-update.sh <event-uid> [--calendar <name>] [--summary <text>] [--start <date>] [--end <date>] [--location <text>] [--description <text>] [--allday <true/false>] [--recurrence <rrule>]"
exit 1
fi
osascript - "$EVENT_UID" "$CALENDAR_NAME" "$SUMMARY" "$START_DATE" "$END_DATE" "$LOCATION" "$DESCRIPTION" "$ALL_DAY" "$RECURRENCE" <<'EOF'
on splitString(theString, theDelimiter)
set oldDelimiters to AppleScript's text item delimiters
set AppleScript's text item delimiters to theDelimiter
set theArray to every text item of theString
set AppleScript's text item delimiters to oldDelimiters
return theArray
end splitString
on parseDate(dateStr)
if dateStr is "" then return missing value
set dateParts to my splitString(dateStr, " ")
set ymdParts to my splitString(item 1 of dateParts, "-")
set theDate to current date
set year of theDate to (item 1 of ymdParts) as integer
set month of theDate to (item 2 of ymdParts) as integer
set day of theDate to (item 3 of ymdParts) as integer
if (count of dateParts) > 1 then
set timeParts to my splitString(item 2 of dateParts, ":")
set hours of theDate to (item 1 of timeParts) as integer
set minutes of theDate to (item 2 of timeParts) as integer
set seconds of theDate to 0
else
set hours of theDate to 0
set minutes of theDate to 0
set seconds of theDate to 0
end if
return theDate
end parseDate
on run argv
set eventUID to item 1 of argv as string
set calendarName to item 2 of argv as string
set newSummary to item 3 of argv as string
set newStartStr to item 4 of argv as string
set newEndStr to item 5 of argv as string
set newLocation to item 6 of argv as string
set newDescription to item 7 of argv as string
set newAllDay to item 8 of argv as string
set newRecurrence to item 9 of argv as string
tell application "Calendar"
if calendarName is not "" then
try
set cals to {calendar calendarName}
on error
return "Error: Calendar '" & calendarName & "' not found"
end try
else
set cals to calendars
end if
repeat with cal in cals
try
set matchingEvents to (every event of cal whose uid is eventUID)
if (count of matchingEvents) > 0 then
set e to item 1 of matchingEvents
if not (writable of cal) then
return "Error: Calendar '" & (name of cal) & "' is read-only"
end if
if newSummary is not "" then
set summary of e to newSummary
end if
if newStartStr is not "" then
set start date of e to my parseDate(newStartStr)
end if
if newEndStr is not "" then
set end date of e to my parseDate(newEndStr)
end if
if newLocation is not "" then
set location of e to newLocation
end if
if newDescription is not "" then
set description of e to newDescription
end if
if newAllDay is "true" then
set allday event of e to true
else if newAllDay is "false" then
set allday event of e to false
end if
if newRecurrence is not "" then
set recurrence of e to newRecurrence
end if
return "Updated event: " & eventUID
end if
end try
end repeat
return "Error: Event with UID '" & eventUID & "' not found"
end tell
end run
EOF
Related skills
How it compares
Choose apple-calendar when you need zero-API native macOS Calendar.app control from an agent; pick a cloud calendar API skill when agents must run cross-platform or outside darwin.
FAQ
How do you install apple-calendar for Cursor or Claude Code?
apple-calendar installs with `npx skills add sundial-org/awesome-openclaw-skills --skill apple-calendar` from a project root. The skill downloads seven AppleScript shell scripts into `.cursor/skills/apple-calendar` or the agent's skills directory on macOS darwin hosts.
What calendar operations does apple-calendar support?
apple-calendar bundles seven scripts: cal-list.sh, cal-events.sh, cal-read.sh, cal-create.sh, cal-update.sh, cal-delete.sh, and cal-search.sh. Together they provide full CRUD plus search across multiple macOS Calendar.app calendars with pipe-delimited terminal output.
Does apple-calendar support recurring events?
apple-calendar accepts RRULE strings on cal-create.sh for daily, weekly, and monthly recurrence patterns documented in SKILL.md. Deleting a recurring event removes the entire series, and read-only calendars like Birthdays cannot be modified.