Skip to content

Add --repeat flag for recurring reminders - #107

Open
rameshbaskaran wants to merge 2 commits into
keith:mainfrom
rameshbaskaran:add-repeat-flag
Open

Add --repeat flag for recurring reminders#107
rameshbaskaran wants to merge 2 commits into
keith:mainfrom
rameshbaskaran:add-repeat-flag

Conversation

@rameshbaskaran

Copy link
Copy Markdown
Contributor

Problem

There's currently no way to create or edit a recurring reminder through this CLI, even though:

  • EKRecurrenceRule is public EventKit API (not a private/undocumented framework)
  • Reminders.app itself fully supports repeat rules on reminders

Users migrating recurring tasks from other tools (Todoist, etc.) into Reminders via this CLI have had to flatten everything into one-shot due dates.

Changes

  • New Recurrence: ExpressibleByArgument (Sources/RemindersLibrary/Recurrence.swift) parsing daily / weekly / monthly / yearly, plus interval forms like 2-weeks or 3-months.
  • reminders add ... --repeat <freq> — requires --due-date to also be set (a recurrence rule has no meaning without a due date on EKReminder).
  • reminders edit ... --repeat <freq> [--due-date <date>] — adds/replaces recurrence on an existing reminder; if the reminder has no due date yet, --due-date must be passed alongside --repeat.
  • Recurrence rules are attached via addRecurrenceRule() before the initial EKEventStore.save() — adding them after save silently fails to persist (undocumented but consistently reported behavior across EventKit).
  • Recurrence surfaced in both plain ((repeats: weekly)) and JSON ("recurrence": "weekly") output for show / show-all, so pre-existing recurring reminders (created via Reminders.app) now also report their recurrence.
  • Unit tests for the Recurrence parser (Tests/RemindersTests/RecurrenceTests.swift).
  • README section with usage examples.

Testing

Built and manually verified end-to-end against a real local Reminders list:

$ reminders add Test "Recurrence smoke test" --due-date "2026-08-10 09:00" --repeat weekly --format json
{
  ...
  "recurrence" : "weekly",
  ...
}
$ reminders show Test
6: Recurrence smoke test (in 1 week) (repeats: weekly)

Also verified edit --repeat adds recurrence to an existing non-recurring reminder, and that pre-existing recurring reminders created via Reminders.app UI (e.g. daily/weekly ones already on my list) correctly show (repeats: ...) with the new encoder logic — confirming the JSON/plain output changes don't regress anything for reminders this tool didn't create.

swift build -Xswiftc -warnings-as-errors passes clean locally. swift test requires a full Xcode toolchain (not available in my sandboxed environment, XCTest module missing from Command Line Tools only) — added tests should run fine under the macos-14 + Xcode 16.2 CI config already in .github/workflows/swift.yml.

Scope

This intentionally covers frequency + interval only (not day-of-week sets, end conditions/count, etc.) to keep the CLI surface simple — matches the level of expressiveness most task-migration use cases need. Happy to extend if there's interest.

reminders-cli previously had no way to create or edit recurring
reminders, even though EKRecurrenceRule is public EventKit API and
Reminders.app natively supports repeat rules.

Adds:
- Recurrence: ExpressibleByArgument parsing daily/weekly/monthly/yearly,
  optionally with an interval like '2-weeks' or '3-months'
- 'reminders add ... --repeat <freq>' (requires --due-date, since
  EKReminder recurrence has no meaning without a due date)
- 'reminders edit ... --repeat <freq> [--due-date <date>]' to add/replace
  recurrence on an existing reminder
- recurrence rules are attached before the initial EKEventStore.save(),
  matching Apple's documented (if under-documented) requirement that
  addRecurrenceRule() calls after save() are silently dropped
- recurrence surfaced in both plain and JSON output (show/show-all)
- unit tests for the Recurrence parser
Swift's split(separator:) omits empty subsequences by default, so
"-1-weeks".split(separator: "-") silently drops the leading empty
segment and parses as interval=1 instead of being rejected. Guard
against a leading '-' up front so negative intervals correctly return
nil (invalid) instead of parsing as their positive counterpart.

CI caught this: build succeeded, 21/22 tests passed, only
testInvalidInterval failed on '-1-weeks' — this is the fix.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant