Skip to main content

apple-events

Use this skill whenever the user wants to manage their Apple Reminders or Calendars using the `event` CLI tool. It covers creating, viewing, searching, updating, and deleting reminders and calendar events, and syncing them to a Cloudflare backend. Works on macOS (via EventKit) and on Linux (via a local SQLite database synced from the Cloudflare backend).

Source facts

Repository
FradSer/event
Last source activity
September 4, 2026 at 10:18
Detected SKILL.md language
English
Stars
52
Forks
9

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

File Explorer
8 files

Showing SKILL.md

SKILL.md
Source instructions ยท Read-only preview
name
apple-events
description
Use this skill whenever the user wants to manage their Apple Reminders or Calendars using the `event` CLI tool. It covers creating, viewing, searching, updating, and deleting reminders and calendar events, and syncing them to a Cloudflare backend. Works on macOS (via EventKit) and on Linux (via a local SQLite database synced from the Cloudflare backend).
argument-hint
[what to create or look up โ€” empty to capture from context]
# Apple Reminders and Calendar CLI (`event`) Use the `event` CLI to manage Apple Reminders and Calendars directly from the terminal. You can create, view, search, update, and delete reminders and calendar events, and sync data across devices. ## Invocation & Arguments Treat `$ARGUMENTS` as a free-form natural-language instruction and map it to the appropriate `event` command(s) documented below. Resolve relative dates and times ("tomorrow", "next Monday", "in 2 hours", "this Friday afternoon") against the current date/time before building the command. Examples: - `/apple-events remind me to call the dentist tomorrow at 3pm` -> `event reminders create ...` - `/apple-events what's on my calendar this week?` -> `event calendar list ...` - `/apple-events block 2-4pm Friday for deep work` -> `event calendar create ...` ### When no arguments are given If `$ARGUMENTS` is empty, do **not** default to listing. Instead, infer intent from the current conversation: 1. **Scan the recent conversation** for an actionable item the user might want captured โ€” a commitment, deadline, follow-up, meeting, appointment, or TODO. 2. **Classify it:** - A **task** (something to do, optionally with a due date) -> a reminder (`event reminders create`). - A **scheduled event** (something happening at a specific time, with a start/end) -> a calendar event (`event calendar create`). 3. **Confirm with the `AskUserQuestion` tool before creating anything.** Present the inferred type and details (title, date/time, target list/calendar) so the user can confirm, switch task<->event, adjust fields, or decline. Never create in the no-args path without explicit confirmation. 4. If **nothing actionable** is found in the conversation, use `AskUserQuestion` to ask what the user would like to create (task or event) rather than guessing. ## Setup & Constraints `event` runs on macOS and Linux, with platform-specific storage backends. All reminder/calendar/list commands below behave identically on both; only the underlying store differs. - **macOS** โ€” reads and writes Apple Reminders and Calendar directly via EventKit. - Requires Reminders.app and Calendar.app to be accessible. - If prompted, the user must grant Full Access permissions in System Settings > Privacy & Security > Reminders / Calendars. - Some advanced reminder fields (`tags`, `flagged`, `url`, `parentTitle`) require the `AdvancedReminderEdit` Shortcut to be installed (https://www.icloud.com/shortcuts/b578334075754da9ba6e50b501515808). Without it โ€” or with the global `--no-shortcuts` flag โ€” the basic reminder is still created and those fields are skipped with a printed note. - **Linux** (and other non-Apple platforms) โ€” there is no EventKit, so `event` reads and writes a local SQLite database at `~/.local/share/event-sync/local.db`. Run `event sync` first to populate it from the Cloudflare D1 backend (see [Cloud Sync](#cloud-sync)), then use the same commands to manage that local data. Advanced fields and the `AdvancedReminderEdit` Shortcut are macOS-only. ## General Usage All commands support the `--json` flag to output results in JSON format, which is easier to parse. **Parsing completion state โ€” never grep the text markers on JSON.** The default text output marks completed reminders with `- [x]` and incomplete with `- [ ]`. The `--json` output has no such markers: it encodes each reminder as an object with a boolean `isCompleted` field. So to count or filter completed/incomplete from `--json`, parse `isCompleted` (e.g. `jq '[.[] | select(.isCompleted)] | length'`, or a Python `json` pass) โ€” never `grep "[x]"` against JSON, which always returns 0 and falsely reads every reminder as incomplete. ## Reminders Management ### List & Search Reminders - List incomplete reminders: `event reminders list` โ€” **completed reminders are hidden by default** (matches Reminders.app). A task you marked done disappears from this view; that is expected, not a deletion or a sync failure โ€” the task still exists and its completion state syncs. - List including completed: `event reminders list --completed` โ€” always use this when you need to confirm a task's completion state, including verifying that a completion synced to another device. - Filter by specific list: `event reminders list --list "List Name"` - Filter by a due-date window (both bounds required, day-based, end exclusive): `event reminders list --start "2026-08-01" --end "2026-09-01"` โ€” the start day is inclusive and the end day exclusive, matching `calendar list`. To include reminders due on a given day, set `--end` to the day AFTER it (e.g. `--end "2026-09-01"` includes reminders through August 31). Bounds must be date-only `yyyy-MM-dd`; a time component is rejected with an invalid-date error. No `--json` is needed for human-readable output; the default markdown output works without the flag. - To programmatically check completion, add `--json` and read the `isCompleted` boolean per item โ€” do not `grep "[x]"` on JSON (see [General Usage](#general-usage)). - Filter by a due-date window (both bounds required, day-based, end exclusive): `event reminders list --start "2026-08-01" --end "2026-09-01"` โ€” the start day is inclusive and the end day exclusive, matching `calendar list`. To include reminders due on a given day, set `--end` to the day AFTER it (e.g. `--end "2026-09-01"` includes reminders through August 31). Bounds must be date-only `yyyy-MM-dd`; a time component is rejected with an invalid-date error. No `--json` is needed for human-readable output; the default markdown output works without the flag. - Search by keyword in title and notes: `event reminders search --keyword "groceries"` (also accepts `--list` and `--completed`) ### Create Reminders - Basic: `event reminders create --title "Buy groceries"` - With details: `event reminders create --title "Project meeting" --list "Work" --due "2026-03-10 14:00:00" --priority 1 --notes "Discuss Q3 goals"` - With multiline notes: `event reminders create --title "Shopping list" --notes $'Milk\nBread\nEggs'` (use `$'...'` with `\n` for newlines in bash/zsh) - Advanced fields (require Shortcut): `event reminders create --title "Urgent bug" --tags "bug,urgent" --flagged true --url "https://github.com/issues/1"` - Location trigger: `event reminders create --title "Pick up keys" --location "Home" --latitude 37.3349 --longitude -122.0090 --proximity enter` (`--radius` defaults to 100 meters; `--proximity` is `enter` or `leave`) - With a URL: `event reminders create --title "Fix login bug" --url "https://example.com/issues/42"`. When a task is associated with an external link, always pass it via `--url` โ€” never put URLs in `--notes` as a substitute. If the Shortcut isn't installed, `--url` is skipped gracefully with a printed note. - Continuing coding/agent work: when a reminder tracks an in-progress coding or agent task tied to a specific project and Claude Code session, append a continuation command to `--notes` on its own line: `Continue:\ncd <project-dir> && claude -r <session-id>` (use `$'...\n...'` for the newline). Resolve `<session-id>` by finding the Claude Code session whose transcript actually holds the task's context โ€” transcripts live at `~/.claude/projects/<encoded-cwd>/<session-id>.jsonl`, so grep them by keyword rather than assuming the current session's id. Don't guess `<project-dir>` or `<session-id>`; confirm with `AskUserQuestion` if either is unclear. ### Update Reminders - Mark as completed: `event reminders update --id <UUID> --completed true` (or `--completed false` to uncheck) - Change title and priority: `event reminders update --id <UUID> --title "New Title" --priority 5` - Add/remove flag (requires Shortcut): `event reminders update --id <UUID> --flagged true` or `--flagged false` - Clear a date: `event reminders update --id <UUID> --clear-due` (or `--clear-start`) - Remove location alarms: `event reminders update --id <UUID> --clear-location` ### Delete Reminders - Delete by ID: `event reminders delete --id <UUID>` ### List Management - List all reminder lists: `event reminders lists list` - Create a list: `event reminders lists create --name "New List Name"` - Rename a list: `event reminders lists update --id <LIST-ID> --name "New Name"` - Delete a list: `event reminders lists delete --id <LIST-ID>` ### Subtasks - There is no dedicated `subtasks` subcommand. Create a subtask by giving a parent's title (requires the Shortcut): `event reminders create --title "Subtask" --parent-title "Parent Task Title"` - Convert an existing reminder into a subtask: `event reminders update --id <UUID> --parent-title "Parent Task Title"` ## Calendar Management ### List Events - List upcoming events (default 1 month from today): `event calendar list` - List for a date range: `event calendar list --start "2026-03-01" --end "2026-03-31"` - Filter by calendar: `event calendar list --calendar "Work"` ### Create Events - Timed event: `event calendar create --title "Standup" --start "2026-03-10 09:00:00" --end "2026-03-10 09:30:00" --calendar "Work" --location "Office" --notes "Daily sync"` - With multiline notes: `event calendar create --title "Conference" --start "2026-03-10 09:00:00" --end "2026-03-10 17:00:00" --notes $'Agenda:\nKeynote\nWorkshop\nBreakout sessions'` - All-day event: use `yyyy-MM-dd` for `--start` / `--end`. ### Update & Delete Events - Update: `event calendar update --id <ID> --title "New Title" --start "2026-03-10 10:00:00" --end "2026-03-10 11:00:00"` - Delete: `event calendar delete --id <ID>` (for recurring events, `--span` controls scope) ## Cloud Sync Sync reminders, calendar events, and lists across devices through a Cloudflare D1 backend. - Run a full bidirectional sync (pull, then push): `event sync` (equivalently `event sync run`) - Check configuration and sync state: `event sync status` - Configure the backend connection: `event sync config --api-url <URL> --api-token <TOKEN> [--device-id <ID>]` (writes `~/.config/event-sync/config.json`; env vars take precedence when set) - Advanced one-directional sync: `event sync push` / `event sync pull` (both accept `--type reminders|calendar|lists|all`) On macOS, sync bridges EventKit and D1. On Linux, sync bridges the local SQLite database and D1 โ€” so on a fresh Linux machine, `event sync` (or `event sync pull`) is the first step before any data is available to the other commands. **What syncs.** Basic fields โ€” including each reminder's completion state (`isCompleted` / `completionDate`) โ€” plus `url`, `location`, `alarms`, `recurrenceRules`, and calendar `attendees` travel in the sync payload and are restored on pull, so they survive a cross-device sync. Tags, flagged status, and subtask relationships (`parentTitle`) are macOS/Shortcut-only and are **not** part of the sync payload; they must be set locally on each device. **Verifying a completion synced.** After marking a task done on one device, confirm it reached another device with `event reminders list --completed` on that device (or `event sync reminders list` to read the cloud store directly). Because `event reminders list` hides completed tasks by default, a task's absence from that default view does **not** mean the completion failed to sync. **D1-direct subcommands** bypass local storage and read/write the cloud backend directly: `event sync reminders list` / `event sync reminders create`, and `event sync calendar list`. Use these to inspect or seed the cloud store without touching EventKit or the local SQLite DB. Sync requires a configured Cloudflare D1 backend: set the `EVENT_SYNC_API_URL` and `EVENT_SYNC_API_TOKEN` environment variables (the device id defaults to the hostname). Reminders and calendar events are also end-to-end encrypted, so sync requires `EVENT_ENCRYPTION_KEY` โ€” a base64-encoded 32-byte key (`openssl rand -base64 32`) that must be **identical on every device**; reminder/calendar push and pull fail if it is unset or mismatched (lists are not encrypted). For one-time Worker deployment, key generation, and per-device environment setup, see [`references/cloud-sync.md`](references/cloud-sync.md). The canonical Worker is shared with the `note` CLI: deploy it once serving all five tables (`migrations/all`) and point both CLIs at the same URL and token. The Worker source is not bundled here โ€” `./references/fetch-worker.sh` fetches it on demand into a gitignored `references/worker/`. ## Limitations & Notes - **Dates**: timed values use `yyyy-MM-dd HH:mm:ss` (e.g. "2026-03-10 14:00:00"); all-day calendar events use `yyyy-MM-dd`. - **Priority**: 1 = High, 5 = Medium, 9 = Low, 0 = None. - **Advanced fields**: `tags`, `flagged`, `url`, and subtask relationships (`parentTitle`) are handled via the `AdvancedReminderEdit` Shortcut. Without it (or with `--no-shortcuts`) they are skipped. The reminder `notes` field holds plain user notes only โ€” no metadata block. - **Notes formatting**: newlines in `--notes` are preserved as plain text line breaks; markdown syntax (bold, italic, lists, etc.) is not interpreted and will appear literally. Bullet characters (`โ€ข`) must be written as `-` instead. - **Calendar sync window**: only events from one year in the past to two years ahead are pushed and pulled. Events moved outside this window remain in the cloud until explicitly deleted locally. - **Sync conflicts**: timed entities use last-write-wins using modification timestamps (falling back to creation time). Local copies without any timestamp are not overwritten on pull. - **Reminder lists**: no per-list modification timestamp โ€” concurrent renames last-write-wins on pull. - **Advanced fields on sync**: `tags`, `flagged`, and subtask relationships (`parentTitle`) are macOS/Shortcut-only and are not synced; use local Shortcut-backed commands for those fields. In contrast, `url`, `location`, `alarms`, `recurrenceRules`, and calendar `attendees` travel in the sync payload and are restored on pull. - **Encryption**: reminders and calendar events are end-to-end encrypted with AES-GCM before upload; the cloud only ever stores ciphertext for those fields (title, list, and dates stay plaintext for search). This requires `EVENT_ENCRYPTION_KEY` to be set and identical across devices โ€” see [Cloud Sync](#cloud-sync). Lists carry no sensitive data and are not encrypted.
View on GitHub