ical CLI — macOS Calendar
Binary: /Users/fredchu/bin/ical — always use full path.
Gotchas (Read First)
deleteneeds--force— without it, blocks on interactive confirmation and hangsupdatehas NO--forceflag — changes apply immediately, just run it--idand positional arg are mutually exclusive — never pass both- Calendar JSON field is
title, notname - Never use
-i(interactive mode) — it requires terminal input, use flags instead - Row numbers reset on every list/today/upcoming command
Commands
Querying
/Users/fredchu/bin/ical today # Today's events
/Users/fredchu/bin/ical today -o json # JSON for parsing
/Users/fredchu/bin/ical list --from today --to "next friday" # Date range
/Users/fredchu/bin/ical upcoming --days 7 # Next N days
/Users/fredchu/bin/ical show 2 # Details for row #2
/Users/fredchu/bin/ical search "meeting" --from "jan 1" --to "dec 31"
/Users/fredchu/bin/ical calendars -o json # List calendars
Creating
/Users/fredchu/bin/ical add "會議" \
--start "tomorrow at 2pm" --end "tomorrow at 3pm" \
--calendar Work --alert 15m --location "Office"
# Recurring
/Users/fredchu/bin/ical add "Standup" \
--start "next monday at 9am" --end "next monday at 9:30am" \
--repeat weekly --repeat-days mon,wed,fri
Updating
# By row number (NO --force needed, changes apply immediately)
/Users/fredchu/bin/ical update 1 --title "新標題"
/Users/fredchu/bin/ical update 2 --start "tomorrow at 3pm" --end "tomorrow at 4pm"
/Users/fredchu/bin/ical update 1 --location "" # Clear a field
/Users/fredchu/bin/ical update 1 --alert none # Clear alerts
/Users/fredchu/bin/ical update 1 --repeat none # Remove recurrence
# By exact ID (for scripts)
/Users/fredchu/bin/ical update --id "FULL_EVENT_ID" --title "New"
# Recurring: update this + future occurrences
/Users/fredchu/bin/ical update 2 --span future --start "next monday at 9am"
Deleting
# ALWAYS pass --force to avoid interactive prompt
/Users/fredchu/bin/ical delete 1 --force
/Users/fredchu/bin/ical delete --id "FULL_EVENT_ID" --force
# Recurring: delete this + future
/Users/fredchu/bin/ical delete 3 --span future --force
Key Flags Reference
Filtering (list / today / upcoming / search):
--calendar-c— filter by calendar name--from-f,--to-t— date range--exclude-calendar— exclude by name (repeatable)-o json— JSON output
Creating (add):
--start-s(required),--end-e(default: start + 1h)--calendar-c,--location-l,--notes-n,--url-u--alert(e.g.,15m,1h,1d) — repeatable--all-day-a--repeat(daily/weekly/monthly/yearly)--repeat-interval,--repeat-days(e.g.,mon,wed),--repeat-count,--repeat-until
Event selection (show / update / delete):
- Number → row from last listing (e.g.,
2) --id "FULL_ID"→ exact match (preferred for scripting)- Never combine both
Date Syntax
Natural language: today, tomorrow, now, eod, eow, next monday, in 3 hours, 2 days ago, mar 15 at 2pm, 5pm, next friday at 3:30pm, end of week
ISO: 2026-03-15, 2026-03-15T14:30:00
JSON Fields
Event: id, title, start_date, end_date, calendar, calendar_id, location, notes, url, all_day, recurrence, alerts
Calendar: id, title, type, color, source, readOnly