Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings
/ ekctl Public

A native macOS CLI tool for managing Calendar events and Reminders via EventKit with JSON output

Notifications You must be signed in to change notification settings

schappim/ekctl

Folders and files

NameName
Last commit message
Last commit date

Latest commit

History

6 Commits

Repository files navigation

ekctl

A native macOS command-line tool for managing Calendar events and Reminders using the EventKit framework. All output is JSON, making it perfect for scripting and automation.

Features

  • List, create, and delete calendar events
  • List, create, complete, and delete reminders
  • Calendar aliases - Use friendly names instead of long IDs
  • JSON output for easy parsing and scripting
  • Full EventKit integration with proper permission handling
  • Support for all calendar and reminder list types (iCloud, Exchange, local, etc.)

Requirements

  • macOS 13.0 (Ventura) or later
  • Xcode Command Line Tools or Xcode
  • Swift 5.9+

Installation

Homebrew (Recommended)

brew tap schappim/ekctl
brew install ekctl

Build from Source

# Clone the repository
git clone https://github.com/schappim/ekctl.git
cd ekctl
# Build release version
swift build -c release
# Optional: Sign with entitlements for better permission handling
codesign --force --sign - --entitlements ekctl.entitlements .build/release/ekctl
# Install to /usr/local/bin
sudo cp .build/release/ekctl /usr/local/bin/

First Run

On first run, macOS will prompt you to grant access to Calendars and Reminders. You can manage these permissions later in:

System Settings → Privacy & Security → Calendars / Reminders

Usage

List Calendars

List all calendars (event calendars and reminder lists):

ekctl list calendars

Output:

{
 "calendars": [
 {
 "id": "CA513B39-1659-4359-8FE9-0C2A3DCEF153",
 "title": "Work",
 "type": "event",
 "source": "iCloud",
 "color": "#0088FF",
 "allowsModifications": true
 },
 {
 "id": "4E367C6F-354B-4811-935E-7F25A1BB7D39",
 "title": "Reminders",
 "type": "reminder",
 "source": "iCloud",
 "color": "#1BADF8",
 "allowsModifications": true
 }
 ],
 "status": "success"
}

Calendar Aliases

Instead of using long calendar IDs, you can create friendly aliases:

# Set an alias for a calendar
ekctl alias set work "CA513B39-1659-4359-8FE9-0C2A3DCEF153"
ekctl alias set personal "4E367C6F-354B-4811-935E-7F25A1BB7D39"
ekctl alias set groceries "E30AE972-8F29-40AF-BFB9-E984B98B08AB"
# List all aliases
ekctl alias list
# Remove an alias
ekctl alias remove work

Output for ekctl alias list:

{
 "aliases": [
 { "name": "groceries", "id": "E30AE972-8F29-40AF-BFB9-E984B98B08AB" },
 { "name": "personal", "id": "4E367C6F-354B-4811-935E-7F25A1BB7D39" },
 { "name": "work", "id": "CA513B39-1659-4359-8FE9-0C2A3DCEF153" }
 ],
 "count": 3,
 "configPath": "/Users/you/.ekctl/config.json",
 "status": "success"
}

Once set, use aliases anywhere you would use a calendar ID:

# These are equivalent:
ekctl list events --calendar "CA513B39-1659-4359-8FE9-0C2A3DCEF153" --from ...
ekctl list events --calendar work --from ...
# Works with all commands
ekctl add event --calendar work --title "Meeting" --start ...
ekctl list reminders --list groceries
ekctl add reminder --list personal --title "Call mom"

Aliases are stored in ~/.ekctl/config.json.

List Events

List events in a calendar within a date range:

# Using calendar ID
ekctl list events \
 --calendar "CA513B39-1659-4359-8FE9-0C2A3DCEF153" \
 --from "2026年01月01日T00:00:00Z" \
 --to "2026年01月31日T23:59:59Z"
# Or using an alias (after setting one)
ekctl list events \
 --calendar work \
 --from "2026年01月01日T00:00:00Z" \
 --to "2026年01月31日T23:59:59Z"

Output:

{
 "count": 2,
 "events": [
 {
 "id": "ABC123:DEF456",
 "title": "Team Meeting",
 "calendar": {
 "id": "CA513B39-1659-4359-8FE9-0C2A3DCEF153",
 "title": "Work"
 },
 "startDate": "2026年01月15日T09:00:00Z",
 "endDate": "2026年01月15日T10:00:00Z",
 "location": "Conference Room A",
 "notes": null,
 "allDay": false,
 "hasAlarms": true,
 "hasRecurrenceRules": false
 }
 ],
 "status": "success"
}

Show Event Details

ekctl show event "ABC123:DEF456"

Add Event

Create a new calendar event:

# Basic event (using alias)
ekctl add event \
 --calendar work \
 --title "Lunch with Client" \
 --start "2026年02月10日T12:30:00Z" \
 --end "2026年02月10日T13:30:00Z"
# Event with location and notes
ekctl add event \
 --calendar work \
 --title "Project Review" \
 --start "2026年02月15日T14:00:00Z" \
 --end "2026年02月15日T15:30:00Z" \
 --location "Building 2, Room 301" \
 --notes "Bring Q1 reports"
# All-day event (using full ID also works)
ekctl add event \
 --calendar "CA513B39-1659-4359-8FE9-0C2A3DCEF153" \
 --title "Company Holiday" \
 --start "2026年03月01日T00:00:00Z" \
 --end "2026年03月02日T00:00:00Z" \
 --all-day

Output:

{
 "status": "success",
 "message": "Event created successfully",
 "event": {
 "id": "NEW123:EVENT456",
 "title": "Lunch with Client",
 "calendar": {
 "id": "CA513B39-1659-4359-8FE9-0C2A3DCEF153",
 "title": "Work"
 },
 "startDate": "2026年02月10日T12:30:00Z",
 "endDate": "2026年02月10日T13:30:00Z",
 "location": null,
 "notes": null,
 "allDay": false
 }
}

Delete Event

ekctl delete event "ABC123:DEF456"

Output:

{
 "status": "success",
 "message": "Event 'Team Meeting' deleted successfully",
 "deletedEventID": "ABC123:DEF456"
}

List Reminders

List reminders in a reminder list:

# List all reminders (using alias)
ekctl list reminders --list personal
# List only incomplete reminders
ekctl list reminders --list personal --completed false
# List only completed reminders (using full ID also works)
ekctl list reminders --list "4E367C6F-354B-4811-935E-7F25A1BB7D39" --completed true

Output:

{
 "count": 2,
 "reminders": [
 {
 "id": "REM123-456-789",
 "title": "Buy groceries",
 "list": {
 "id": "4E367C6F-354B-4811-935E-7F25A1BB7D39",
 "title": "Reminders"
 },
 "dueDate": "2026年01月20日T17:00:00Z",
 "completed": false,
 "priority": 0,
 "notes": null
 }
 ],
 "status": "success"
}

Show Reminder Details

ekctl show reminder "REM123-456-789"

Add Reminder

Create a new reminder:

# Simple reminder (using alias)
ekctl add reminder \
 --list personal \
 --title "Call the dentist"
# Reminder with due date
ekctl add reminder \
 --list personal \
 --title "Submit expense report" \
 --due "2026年01月25日T09:00:00Z"
# Reminder with priority and notes
# Priority: 0=none, 1=high, 5=medium, 9=low
ekctl add reminder \
 --list groceries \
 --title "Buy milk" \
 --due "2026年02月01日T12:00:00Z" \
 --priority 1 \
 --notes "Check expiration date first"

Output:

{
 "status": "success",
 "message": "Reminder created successfully",
 "reminder": {
 "id": "NEWREM-123-456",
 "title": "Submit expense report",
 "list": {
 "id": "4E367C6F-354B-4811-935E-7F25A1BB7D39",
 "title": "Reminders"
 },
 "dueDate": "2026年01月25日T09:00:00Z",
 "completed": false,
 "priority": 0,
 "notes": null
 }
}

Complete Reminder

Mark a reminder as completed:

ekctl complete reminder "REM123-456-789"

Output:

{
 "status": "success",
 "message": "Reminder 'Buy groceries' marked as completed",
 "reminder": {
 "id": "REM123-456-789",
 "title": "Buy groceries",
 "completed": true,
 "completionDate": "2026年01月21日T10:30:00Z"
 }
}

Delete Reminder

ekctl delete reminder "REM123-456-789"

Date Format

All dates use ISO 8601 format with timezone. Examples:

Format Example Description
UTC 2026年01月15日T09:00:00Z 9:00 AM UTC
With offset 2026年01月15日T09:00:00+10:00 9:00 AM AEST
Midnight 2026年01月15日T00:00:00Z Start of day
End of day 2026年01月15日T23:59:59Z End of day

Scripting Examples

Get calendar ID by name

# Using jq to find a calendar by name
CALENDAR_ID=$(ekctl list calendars | jq -r '.calendars[] | select(.title == "Work") | .id')
echo $CALENDAR_ID

List today's events

TODAY=$(date -u +"%Y-%m-%dT00:00:00Z")
TOMORROW=$(date -u -v+1d +"%Y-%m-%dT00:00:00Z")
ekctl list events \
 --calendar "$CALENDAR_ID" \
 --from "$TODAY" \
 --to "$TOMORROW"

Create event from variables

TITLE="Sprint Planning"
START="2026年01月20日T10:00:00Z"
END="2026年01月20日T11:00:00Z"
ekctl add event \
 --calendar "$CALENDAR_ID" \
 --title "$TITLE" \
 --start "$START" \
 --end "$END"

Count incomplete reminders

ekctl list reminders --list "$LIST_ID" --completed false | jq '.count'

Export events to CSV

ekctl list events \
 --calendar "$CALENDAR_ID" \
 --from "2026年01月01日T00:00:00Z" \
 --to "2026年12月31日T23:59:59Z" \
 | jq -r '.events[] | [.title, .startDate, .endDate, .location // ""] | @csv'

Error Handling

When an error occurs, the output includes an error message:

{
 "status": "error",
 "error": "Calendar not found with ID: invalid-id"
}

Common errors:

  • Permission denied - Grant access in System Settings
  • Calendar not found - Check the calendar ID with list calendars
  • Invalid date format - Use ISO 8601 format (see examples above)

Help

Get help for any command:

ekctl --help
ekctl list --help
ekctl add event --help
ekctl list reminders --help

License

MIT License

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

About

A native macOS CLI tool for managing Calendar events and Reminders via EventKit with JSON output

Topics

Resources

Stars

Watchers

Forks

Packages

No packages published

AltStyle によって変換されたページ (->オリジナル) /