RemCTL: A Reminders CLI That Splits Reads From Writes
An Apple Reminders CLI for power users and AI agents. RemCTL supports all the latest Reminders features such as sections, subtasks, tags, rich links, groceries lists, templates, smart lists, and image attachments. (Not affiliated with Apple.)
At a glance
- What is it?
- RemCTL is a macOS Reminders command line tool for power users and AI agents, built around a signed capability host that reads SQLite directly and writes through EventKit. Its private ReminderKit path is explicitly opt-in and experimental.
- Who is it for?
- Adopt RemCTL if you script Reminders on macOS 14 or later, you can supply an Apple Development signing identity, and you are willing to grant Full Disk Access to one signed host app. Do not adopt it if you need a portable tool, a headless Linux or CI runner, or a no-signing setup, because the README states a Mac without that identity is not eligible for a live Capability Host install and there is no ad-hoc fallback.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 11 days ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.
DEEP OPEN-SOURCE ANALYSIS
The gap RemCTL fills in Apple's Reminders data model
Apple ships Reminders on every Mac, and it ships no first-class command line interface for it. Scripting it generally means either AppleScript, which is slow and exposes a limited slice of the data model, or reading the local SQLite store directly, which is fast and gets you everything but risks writing into a sync database. RemCTL is aimed at the people who hit that wall: power users who want Reminders in shell pipelines, and AI agents that need to read and mutate task data programmatically. The README describes the target audience directly as power users and AI agents, and the feature list it claims covers sections, subtasks, tags, rich links, groceries lists, templates, smart lists, and image attachments. Those are the parts of the modern Reminders schema that AppleScript never exposed. The project is MIT licensed and written in Python, and it installs on macOS 14 or later.
Why reads and writes take different paths
The architecture is the most interesting decision in the project. Reads go straight at the local iCloud Reminders database with SQLite under Full Disk Access. Writes go through Apple's public EventKit APIs, so Reminders and iCloud stay in charge of mutations. RemCTL never writes to the sync database itself. The README explains the payoff of the SQLite read path: it exposes sections, subtasks, tags, attachments with sha512-verified local file paths, deep links, list colors and badges, recurrence metadata, normal alarms, location alarms, and Early Reminder metadata, and it does so in tens of milliseconds. That combination is the reason the tool exists. A pure EventKit client would be slower and would not see the metadata; a pure SQLite client would be fast and would risk corrupting sync state. Splitting the two paths gets the detail without the write hazard. Everything routes through one signed app called RemCTL Capability Host.app, which means Reminders, Automation, and Full Disk Access grants attach to a single target rather than to Terminal, Hermes, Codex, or whatever Python process happens to be calling.
The capability host, the broker, and the six commands that stay local
The README gives a flow diagram worth reading closely. A client (Python 3.10+) connects over an owner-only Unix socket using broker protocol v2 to the signed persistent host, which runs a protected Python 3.13 or newer. Inside the host, reads use SQLite with Full Disk Access, writes go through a sealed remctl-bridge to EventKit with Reminders access, flags go through AppleScript with Automation access, and the private path goes through a sealed remctl-private helper to ReminderKit. Of the 49 permission-bearing commands, all route through the broker. Six stay in the caller: completion, doctor, list-symbols, onboard, permissions, and setup. Execution mode is controlled by REMCTL_CAPABILITY_HOST, which accepts auto, force, or direct. auto is the default and uses the host when installed; force requires it; direct bypasses it for diagnostics and degraded recovery, which means the caller must hold its own access. Setting REMCTL_STORE_DIR forces direct execution under auto or direct and is an error under force. REMCTL_BRIDGE_PATH and REMCTL_PRIVATE_PATH only affect direct execution, since hosted commands use the sealed helpers fixed by the signed generation. There is also a --via-eventkit flag on show, search, today, and upcoming for limited EventKit reads. The README is explicit that this flag never changes the execution route and never returns RemCTL numeric IDs.
Getting it running, and the signing identity you probably do not have
The quick start is three commands: git clone https://github.com/viticci/remctl.git, then cd remctl, then ./install.sh --bootstrap, then ~/bin/remctl onboard. The installer builds, signs, and strictly verifies a complete capability-host generation before transactionally replacing and starting the LaunchAgent service. It also creates two aliases in your bin directory, rctl and reminders, that behave identically to remctl. The setup requirements are where this gets narrow. You need Xcode Command Line Tools, an official python.org Framework installation of Python 3.13 or newer, and an Apple Development signing identity with a TeamIdentifier. The installer preserves an existing identity, accepts a valid explicit REMCTL_CODESIGN_IDENTITY, or auto-detects an Apple Development identity. The README states plainly that a Mac without that identity is not currently eligible for a live Capability Host install, and that RemCTL does not fall back to ad-hoc signing. That is a real constraint, not a footnote. Onboarding asks the signed host to present the native Reminders and Automation consent flows. macOS has no native Full Disk Access prompt, so onboarding opens a guided helper when that grant is missing, and you must add only the exact signed host shown there. If you change its Full Disk Access authorization, restart it with launchctl kickstart -k "gui/$(id -u)/net.macstories.remctl.capability-host". Then verify with ~/bin/remctl doctor and try ~/bin/remctl today. Run remctl permissions full-disk-access only to reopen the guide or repair the grant. When no onboarding state exists, the first interactive, non-JSON permission-bearing invocation runs onboarding before the requested command; RemCTL skips that for local setup and display commands, non-TTY or JSON calls, host-internal execution, and REMCTL_SKIP_ONBOARD=1. Warnings do not block the command, and unresolved permission failures surface from the command itself.
The private ReminderKit path is where the risk sits
Sections, shared-list assignments, subtasks, tags, image attachments, urgent state, Early Reminders, display ordering, list appearance metadata, Groceries list metadata, list groups, custom smart lists, and Reminders templates are not available through public APIs. RemCTL reaches them through an optional integration with Reminders' private API on macOS, gated behind --private and using the native ReminderKit framework rather than direct SQLite mutation. Location-based alarms are guarded by the same --private command surface but are saved through the public EventKit bridge, because the README says that path materializes reliably on current macOS. The project's own framing is that private metadata writes are unsupported and explicitly opt-in, and should be treated as experimental power-user functionality. It also warns that private ReminderKit behavior can change between macOS releases. Read that as the main failure mode: a macOS update can break the private path without breaking the rest of the tool. The release notes add a second caveat. The 1.8.0 signed-host release is verified on the current early macOS 27 Golden Gate build, and the underlying command and private-API paths have historical test coverage on macOS 26 Tahoe, but this exact host release has not been rerun there. So the tested surface is narrower than the supported surface. If your workflow depends on templates or smart lists, you are on the fragile edge of the tool, and you should verify that specific command against your macOS build before building anything on top of it.
How it compares to AppleScript and to a direct SQLite client
The obvious alternative for a Mac user is AppleScript through osascript. It needs no signing identity, no Full Disk Access grant, and no LaunchAgent. It also cannot see sections, subtasks, attachments, or list appearance metadata, and it is slow enough that looping over a few hundred reminders in a shell script becomes painful. RemCTL trades that simplicity for coverage and speed, and it pays for it with a setup procedure that includes a code signing identity and a restartable background service. The other alternative is a script that reads the Reminders SQLite store directly and writes back to it. That approach gets the same rich reads with none of the host machinery, and it is genuinely simpler to reason about. It also means you own the consequences of writing into a database that iCloud syncs, which is exactly what RemCTL's split design avoids. If your use case is read-only reporting, a direct SQLite query is a reasonable choice and RemCTL's host is overhead you do not need. The moment you need to mutate data that syncs to an iPhone, the EventKit write path is the safer position, and that is the design argument the README is making.
Maintenance surface and what the MIT licence does not cover
The maintenance cost here is not the Python client. It is the capability host generation. The installer builds, signs, and verifies a generation before replacing the running LaunchAgent, so upgrades are transactional, but they are also tied to your signing identity and to the protected Python 3.13 runtime the installer selects. The README notes that the installer preserves an existing identity or accepts REMCTL_CODESIGN_IDENTITY, which matters if you rotate certificates or move between machines. The 1.7.1 release landed on 2026-08-13, and the repository's last push is 2026-09-04, so the project is active, though the README's own caveat about macOS 26 Tahoe not being rerun for this host release suggests that the maintainer's verification is build-specific rather than broad. On licensing: RemCTL is MIT, which covers the code in this repository. It does not grant you anything with respect to Apple's private ReminderKit APIs, and the README's own language about unsupported private metadata writes is the relevant signal there. Nothing in the MIT grant changes what Apple may do to those interfaces in a future macOS release. Treat the licence as permission to use and modify the Python code, not as a guarantee about the private path's longevity.
Editorial conclusion
Adopt RemCTL if you script Reminders on macOS 14 or later, you can supply an Apple Development signing identity, and you are willing to grant Full Disk Access to one signed host app. Do not adopt it if you need a portable tool, a headless Linux or CI runner, or a no-signing setup, because the README states a Mac without that identity is not eligible for a live Capability Host install and there is no ad-hoc fallback. Before trusting it, run ~/bin/remctl doctor and ~/bin/remctl permissions full-disk-access to confirm the host generation is intact and the grants are attached to the exact signed app.
Community notes