From 9505aa86b8e2d5b2ba01fb48d03ab9678cc98cd2 Mon Sep 17 00:00:00 2001 From: rameshbaskaran Date: Fri, 31 Jul 2026 15:43:11 +0530 Subject: [PATCH] fix: allow deleting a reminder by external id after it's been completed `delete` fetched only incomplete reminders before resolving the given index/id, so `reminders delete ` failed with "No reminder at index ... on ..." whenever that reminder had already been marked complete, even though the id is valid and stable. Widen the fetch to the full (`.all`) display set when the argument is an external id (non-numeric), since ids are unambiguous regardless of completion state. Numeric indexes are left untouched (still scoped to `.incomplete`, matching what `show`'s default output displays), so existing index-based delete behavior is unaffected. Manually verified against a live Reminders store: - create -> complete -> delete by external id -> now succeeds and the item is gone from `--include-completed` output (previously failed). - create -> delete by numeric index on an incomplete item -> still works exactly as before. No automated regression test added: `Reminders` talks directly to a real `EKEventStore()` with no injection seam, and CI has no granted Reminders permission, so this can't be exercised in `swift test` today (same constraint the existing `NaturalLanguageTests.swift` suite works around by only covering pure date-parsing logic). --- Sources/RemindersLibrary/Reminders.swift | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/Sources/RemindersLibrary/Reminders.swift b/Sources/RemindersLibrary/Reminders.swift index 2640811..fb04a20 100644 --- a/Sources/RemindersLibrary/Reminders.swift +++ b/Sources/RemindersLibrary/Reminders.swift @@ -288,7 +288,17 @@ public final class Reminders { let calendar = self.calendar(withName: name) let semaphore = DispatchSemaphore(value: 0) - self.reminders(on: [calendar], displayOptions: .incomplete) { reminders in + // Numeric indexes are only meaningful against the same display set that + // `show` uses by default (incomplete-only), so keep that scope when the + // caller passes a plain integer index — otherwise a numeric index would + // resolve against a differently-ordered/sized array than the one the + // user actually saw. External identifiers are stable regardless of + // completion state, so widen the fetch to `.all` in that case, so a + // reminder already marked complete can still be found and deleted by + // its id instead of failing with "No reminder at index ...". + let displayOptions: DisplayOptions = Int(index) == nil ? .all : .incomplete + + self.reminders(on: [calendar], displayOptions: displayOptions) { reminders in guard let reminder = self.getReminder(from: reminders, at: index) else { print("No reminder at index \(index) on \(name)") exit(1)