Claude
Skills
Sign in
Back

ekctl

Included with Lifetime
$97 forever

Manage macOS Calendar events and Reminders using the ekctl CLI tool

Productivity

What this skill does


# ekctl - macOS Calendar & Reminders CLI

Use the `ekctl` command-line tool to manage Calendar events and Reminders on macOS. Output defaults to JSON; CSV and plain-text are available on every command via `--format csv` and `--format text`.

## Prerequisites

Ensure ekctl is installed:
```bash
brew tap schappim/ekctl && brew install ekctl
```

If not installed, guide the user to install it first.

Verify version (this skill targets `ekctl >= 1.4.0` — earlier versions don't have `today`/`tomorrow`/`next`, `--search`, `--availability`, or `--format`):
```bash
ekctl --version
```

## Workflow

### 1. Check for aliases first

Before any calendar/reminder operation, check if the user has aliases configured:
```bash
ekctl alias list
```

If aliases exist, use them instead of raw IDs. If no aliases exist, help the user set them up after listing calendars.

### 2. List available calendars

```bash
ekctl list calendars
```

This returns both event calendars (`type: "event"`) and reminder lists (`type: "reminder"`).

### 3. Set up aliases for convenience

Help users create aliases for frequently used calendars:
```bash
ekctl alias set work "CALENDAR_ID"
ekctl alias set personal "CALENDAR_ID"
ekctl alias set groceries "REMINDER_LIST_ID"
```

### 4. Perform requested operations

Use the appropriate command based on what the user wants to do. See `command-reference.md` for all commands and `scripting-examples.md` for jq patterns and integration recipes.

## Quick date ranges

Three top-level subcommands cover the most common queries with no date arithmetic required. Prefer these over computing `--from`/`--to` yourself:

```bash
ekctl today --calendar work          # events occurring today (local time)
ekctl tomorrow --calendar work       # events occurring tomorrow
ekctl next --calendar work           # the single next upcoming event
ekctl next --calendar work --count 5 # the next 5 upcoming events
ekctl next --calendar work --days 7  # only look 7 days ahead
```

`next` returns events sorted by start time and includes events currently in progress (their `endDate` is still in the future).

All three accept the same filter and format flags as `list events`, so they compose:
```bash
ekctl today --calendar work,personal --availability busy --format csv
ekctl next --calendar work --search standup --count 3 --format text
```

## Filtering

Two filters narrow `list events` and (`--search` only) `list reminders`. They compose with everything else:

- **`--search <term>`** — case-insensitive substring match across an event's title, location, and notes (or a reminder's title and notes). Use this instead of post-filtering through jq.
- **`--availability busy|free|tentative|unavailable|notSupported`** — events only. Filters to events whose EKEventAvailability matches.

```bash
# All standup-related events for the week
ekctl next --calendar work --search standup --days 7

# Only "busy" events today — actual blocked-out time
ekctl today --calendar work --availability busy

# Reminders that mention milk
ekctl list reminders --list groceries --search milk
```

## Output formats

Every output-producing command takes `--format`:

- **`json`** (default) — full structure, pretty-printed.
- **`csv`** — RFC 4180. Header is the union of every field across the returned items, alphabetised. Nested objects flatten to dot-notated columns (`calendar.id`, `calendar.title`); nested arrays (like `attendees`) become a JSON-encoded cell.
- **`text`** — `key: value` lines per item, blank line between items. Greppable.

CSV and text are auto-discovered from the same dictionary that produces JSON, so new fields appear in all three formats simultaneously — no drift.

```bash
ekctl today --calendar work --format csv > today.csv
ekctl list calendars --format text
```

## Multi-calendar

`--calendar` accepts a comma-separated list. Each returned event keeps its `calendar.id` and `calendar.title`, so the merged stream is still distinguishable:

```bash
ekctl today --calendar work,personal,family
ekctl list events --calendar work,personal --from "2026-02-01T00:00:00Z" --to "2026-02-28T23:59:59Z"
```

## Date format

For commands that take explicit `--from` / `--to` / `--start` / `--end` / `--due`, use **ISO 8601** with timezone:

| Example | Description |
|---------|-------------|
| `2026-01-15T09:00:00Z` | 9:00 AM UTC |
| `2026-01-15T09:00:00+10:00` | 9:00 AM AEST |
| `2026-01-15T00:00:00Z` | Start of day (midnight UTC) |
| `2026-01-15T23:59:59Z` | End of day |

For "today" / "tomorrow" / "this week", **use the subcommands above instead of computing dates** — `today`/`tomorrow`/`next` are local-timezone-aware and don't rely on the BSD-only `date -v+1d` flag (which breaks on Linux).

If you genuinely need a custom range outside today/tomorrow/next, generate dates like this:
```bash
# An explicit absolute date
START="2026-01-15T09:00:00Z"

# A relative date in the user's local zone via Swift / Python is more portable
# than `date -v+1d`. For most cases, `ekctl next --days N` is simpler.
```

## Common patterns

### Get today's busy events
```bash
ekctl today --calendar work --availability busy
```

### Get the next 3 meetings
```bash
ekctl next --calendar work --count 3
```

### Search across all my calendars
```bash
ekctl today --calendar work,personal --search "1:1"
```

### Create a meeting
```bash
ekctl add event \
  --calendar work \
  --title "Team Standup" \
  --start "2026-01-15T09:00:00Z" \
  --end "2026-01-15T09:30:00Z" \
  --location "Conference Room A" \
  --notes "Weekly sync"
```

### Create a recurring meeting
```bash
ekctl add event \
  --calendar work \
  --title "Weekly 1:1" \
  --start "2026-01-15T14:00:00Z" \
  --end "2026-01-15T14:30:00Z" \
  --recurrence-frequency weekly \
  --recurrence-end-count 12
```

### Create an all-day event
```bash
ekctl add event \
  --calendar personal \
  --title "Vacation Day" \
  --start "2026-01-20T00:00:00Z" \
  --end "2026-01-21T00:00:00Z" \
  --all-day
```

### Add a reminder with due date
```bash
ekctl add reminder \
  --list personal \
  --title "Call the dentist" \
  --due "2026-01-16T10:00:00Z" \
  --priority 1
```

### List incomplete reminders
```bash
ekctl list reminders --list personal --completed false
```

### Complete a reminder
```bash
ekctl complete reminder "REMINDER_ID"
```

### Update an event
```bash
ekctl update event "EVENT_ID" --title "Renamed meeting" --location "New room"
```

### Update a reminder
```bash
ekctl update reminder "REMINDER_ID" --due "2026-01-17T10:00:00Z" --priority 5
```

## Output JSON shape

Events include these fields (since 1.4.0):

```json
{
  "id": "ABC123:DEF456",
  "title": "Team Meeting",
  "calendar": { "id": "...", "title": "Work" },
  "startDate": "2026-01-15T09:00:00+11:00",
  "endDate": "2026-01-15T10:00:00+11:00",
  "location": "Conference Room A",
  "notes": null,
  "url": null,
  "allDay": false,
  "hasAlarms": true,
  "hasRecurrenceRules": false,
  "availability": "busy",
  "attendees": [
    { "name": "Jane Doe", "email": "[email protected]", "status": "accepted", "role": "required" }
  ]
}
```

Reminders include:

```json
{
  "id": "REM123-456-789",
  "title": "Buy groceries",
  "list": { "id": "...", "title": "Personal" },
  "dueDate": "2026-01-20T17:00:00+11:00",
  "completed": false,
  "priority": 0,
  "notes": null,
  "url": null
}
```

## JSON output handling

ekctl returns JSON by default. For most filtering needs, prefer the built-in flags over `jq` — they're typically clearer and don't require a date / substring round-trip:

| Need | Old way (jq) | New way (built-in) |
|---|---|---|
| Events for today | `--from $TODAY --to $TOMORROW` | `ekctl today --calendar work` |
| Title contains "standup" | `... \| jq '.events[] \| select(.title \| contains("standup"))'` | `--search standup` |
| Only busy events | `... \| jq '.events[] \| select(.availability == "busy")'` | `--availability busy` |
| Multiple calendars | run twice + merge | `--calendar work,personal` |
| CSV export | `... \| jq
Files: 3
Size: 33.5 KB
Complexity: 34/100
Category: Productivity

Related in Productivity