Sync and query CalDAV calendars using vdirsyncer + khal. Works on macOS and Linux. USE WHEN: iCloud calendar, Fastmail, Nextcloud, any CalDAV server, creating/viewing local calendar events. DON'T USE: Gmail/Google Calendar (use gog skill), Outlook/Microsoft 365 calendars. NOTE: On this machine, Clawdbot-Calendar syncs to Google Calendar via CalDAV.
vdirsyncer syncs CalDAV calendars to local .ics files. khal reads and writes them.
❌ "Search my Gmail for calendar invites" → Use gog skill (Gmail) ❌ "What's on my Google Calendar?" → Use gog skill (gog calendar events) ❌ "Add event to Outlook/365" → Not supported here ❌ "Set a reminder" → Use cron for time-based reminders, or Apple Reminders
Always sync before querying or after making changes:
vdirsyncer sync
khal list # Today
khal list today 7d # Next 7 days
khal list tomorrow # Tomorrow
khal list 2026-01-15 2026-01-20 # Date range
khal list -a Work today # Specific calendar
khal search "meeting"
khal search "dentist" --format "{start-date} {title}"
khal new 2026-01-15 10:00 11:00 "Meeting title"
khal new 2026-01-15 "All day event"
khal new tomorrow 14:00 15:30 "Call" -a Work
khal new 2026-01-15 10:00 11:00 "With notes" :: Description goes here
After creating, sync to push changes:
vdirsyncer sync
khal edit is interactive — requires a TTY. Use tmux if automating:
khal edit "search term"
khal edit -a CalendarName "search term"
khal edit --show-past "old event"
Menu options:
s → edit summaryd → edit descriptiont → edit datetime rangel → edit locationD → delete eventn → skip (save changes, next match)q → quitAfter editing, sync:
vdirsyncer sync
Use khal edit, then press D to delete.
Problem: khal new with overnight times (e.g., 10pm-2am) creates broken all-day events.
Solution: Write ICS files directly for overnight/multi-day timed events:
cat > /tmp/event.ics << 'EOF'
BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Clawdbot//Calendar//EN
BEGIN:VEVENT
UID:unique-id-$(date +%s)@clawdbot
DTSTAMP:20260101T000000Z
DTSTART;TZID=America/New_York:20260211T220000
DTEND;TZID=America/New_York:20260212T020000
SUMMARY:Event Title
LOCATION:Location
DESCRIPTION:Description
END:VEVENT
END:VCALENDAR
EOF
cp /tmp/event.ics ~/.local/share/vdirsyncer/calendars/Clawdbot-Calendar/
vdirsyncer sync clawdbot_calendar
For same-day events: khal new works fine.
For scripting:
khal list --format "{start-date} {start-time}-{end-time} {title}" today 7d
khal list --format "{uid} | {title} | {calendar}" today
Placeholders: {title}, {description}, {start}, {end}, {start-date}, {start-time}, {end-date}, {end-time}, {location}, {calendar}, {uid}
khal caches events in ~/.local/share/khal/khal.db. If data looks stale after syncing:
rm ~/.local/share/khal/khal.db
~/.config/vdirsyncer/config)Example for iCloud:
[general]
status_path = "~/.local/share/vdirsyncer/status/"
[pair icloud_calendar]
a = "icloud_remote"
b = "icloud_local"
collections = ["from a", "from b"]
conflict_resolution = "a wins"
[storage icloud_remote]
type = "caldav"
url = "https://caldav.icloud.com/"
username = "[email protected]"
password.fetch = ["command", "cat", "~/.config/vdirsyncer/icloud_password"]
[storage icloud_local]
type = "filesystem"
path = "~/.local/share/vdirsyncer/calendars/"
fileext = ".ics"
Provider URLs:
https://caldav.icloud.com/google_calendar storage typehttps://caldav.fastmail.com/dav/calendars/user/EMAIL/https://YOUR.CLOUD/remote.php/dav/calendars/USERNAME/~/.config/khal/config)[calendars]
[[my_calendars]]
path = ~/.local/share/vdirsyncer/calendars/*
type = discover
[default]
default_calendar = Home
highlight_event_days = True
[locale]
timeformat = %H:%M
dateformat = %Y-%m-%d
vdirsyncer discover # First time only
vdirsyncer sync