@mattheworiordan/remi 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/README.md +101 -136
  2. package/dist/cli/commands/add.js +9 -6
  3. package/dist/cli/commands/add.js.map +1 -1
  4. package/dist/cli/commands/complete.js +4 -2
  5. package/dist/cli/commands/complete.js.map +1 -1
  6. package/dist/cli/commands/create-section.js +4 -2
  7. package/dist/cli/commands/create-section.js.map +1 -1
  8. package/dist/cli/commands/delete-list.js +4 -2
  9. package/dist/cli/commands/delete-list.js.map +1 -1
  10. package/dist/cli/commands/delete-section.js +5 -2
  11. package/dist/cli/commands/delete-section.js.map +1 -1
  12. package/dist/cli/commands/delete.js +4 -3
  13. package/dist/cli/commands/delete.js.map +1 -1
  14. package/dist/cli/commands/demo.d.ts +3 -0
  15. package/dist/cli/commands/demo.js +134 -0
  16. package/dist/cli/commands/demo.js.map +1 -0
  17. package/dist/cli/commands/list.js +48 -2
  18. package/dist/cli/commands/list.js.map +1 -1
  19. package/dist/cli/commands/move.js +6 -4
  20. package/dist/cli/commands/move.js.map +1 -1
  21. package/dist/cli/commands/sections.js +4 -2
  22. package/dist/cli/commands/sections.js.map +1 -1
  23. package/dist/cli/commands/update.js +4 -2
  24. package/dist/cli/commands/update.js.map +1 -1
  25. package/dist/cli/index.js +234 -215
  26. package/dist/cli/index.js.map +1 -1
  27. package/dist/cli/output.d.ts +2 -0
  28. package/dist/cli/output.js +33 -3
  29. package/dist/cli/output.js.map +1 -1
  30. package/dist/core/fuzzy.d.ts +16 -0
  31. package/dist/core/fuzzy.js +39 -0
  32. package/dist/core/fuzzy.js.map +1 -0
  33. package/dist/core/lookup.d.ts +1 -1
  34. package/dist/core/lookup.js +19 -9
  35. package/dist/core/lookup.js.map +1 -1
  36. package/dist/core/resolve.d.ts +13 -0
  37. package/dist/core/resolve.js +25 -0
  38. package/dist/core/resolve.js.map +1 -0
  39. package/dist/mcp/server.d.ts +23 -0
  40. package/dist/mcp/server.js +217 -0
  41. package/dist/mcp/server.js.map +1 -0
  42. package/package.json +2 -1
package/README.md CHANGED
@@ -1,207 +1,172 @@
1
- # remi
1
+ <p align="center">
2
+ <img src="docs/logo.svg" alt="remi" width="80">
3
+ </p>
2
4
 
3
- **The missing CLI for Apple Reminders — with section support and iCloud sync.**
5
+ <h1 align="center">remi</h1>
4
6
 
5
- remi is the only Apple Reminders CLI that supports **sections** (the organizational unit Apple added in macOS 13) with full **iCloud sync**. Create lists, add reminders, organize them into sections, and have everything sync across all your Apple devices.
7
+ <p align="center"><strong>The CLI that Apple should have built for Reminders.</strong></p>
6
8
 
7
- ## Install
8
-
9
- ```bash
10
- # Homebrew (recommended)
11
- brew tap mattheworiordan/remi
12
- brew install remi
13
-
14
- # npm
15
- npm install -g @mattheworiordan/remi
16
-
17
- # Or run without installing
18
- npx @mattheworiordan/remi lists
19
- ```
20
-
21
- ## Quick start
22
-
23
- ```bash
24
- # See all your lists
25
- remi lists
26
-
27
- # View reminders in a list
28
- remi list "Groceries"
9
+ Create lists, add reminders, organize them into sections, and have everything sync across all your Apple devices — from the terminal.
29
10
 
30
- # Add a reminder
31
- remi add "Groceries" "Buy milk"
11
+ <p align="center">
12
+ <img src="docs/demo.gif" alt="remi demo" width="500">
13
+ </p>
32
14
 
33
- # Add with section, due date, and priority
34
- remi add "Groceries" "Fresh basil" --section "Produce" --due "next tuesday" --priority high
15
+ ## Why remi?
35
16
 
36
- # Add a recurring reminder
37
- remi add "Dogs" "Flea treatment" --due 2026-04-28 --repeat monthly
17
+ - **Sections with iCloud sync** — the only CLI that supports Apple Reminders sections. Create them, assign reminders, move between them, and it all syncs.
18
+ - **Natural language** — `--due "next tuesday"`, `--repeat "every 2 weeks on monday,friday"`
19
+ - **Fuzzy matching** — type `remi list shopping` instead of `remi list "Groceries / Shopping List"`
20
+ - **Agent-first** — structured JSON output, Claude Code plugin, skills.sh skill, OpenClaw compatible
21
+ - **Fast** — compiled Swift helpers, no Electron, no GUI
38
22
 
39
- # What's due today?
40
- remi today
23
+ ### vs other tools
41
24
 
42
- # What's overdue?
43
- remi overdue
44
- ```
45
-
46
- ## Commands
25
+ | | remi | [remindctl](https://github.com/steipete/remindctl) | [reminders-cli](https://github.com/keith/reminders-cli) |
26
+ |---|---|---|---|
27
+ | Sections | **Yes** | No | No |
28
+ | Section sync (iCloud) | **Yes** | N/A | N/A |
29
+ | Recurrence | **Yes** | Yes | No |
30
+ | Natural language dates | **Yes** | Yes | No |
31
+ | JSON output | **Yes** | Yes | No |
32
+ | AI agent integration | **Yes** | Partial | No |
47
33
 
48
- ### Queries — what's due?
49
-
50
- ```bash
51
- remi today # Due today
52
- remi overdue # Past due
53
- remi upcoming --days 7 # Due in the next 7 days
54
- ```
55
-
56
- ### Browse
34
+ ## Install
57
35
 
58
36
  ```bash
59
- remi list "Groceries" # Show reminders in a list
60
- remi list "Groceries" --include-completed
61
- remi lists # List all reminder lists
62
- remi search "milk" # Search across all lists
37
+ brew tap mattheworiordan/tap && brew install remi
63
38
  ```
64
39
 
65
- ### Task actions
40
+ Or via npm:
66
41
 
67
42
  ```bash
68
- remi add "Work" "Review report" --due "next friday" --priority high --notes "Q1 numbers"
69
- remi add "Work" "Team standup" --due tomorrow --repeat daily
70
- remi add "Work" "Sprint review" --repeat "every 2 weeks"
71
- remi complete "Work" "Review report"
72
- remi update "Work" "Review report" --due "in 3 days"
73
- remi delete "Work" "Review report" --confirm
43
+ npm install -g @mattheworiordan/remi
44
+ # or run without installing
45
+ npx @mattheworiordan/remi lists
74
46
  ```
75
47
 
76
- Dates accept YYYY-MM-DD or natural language: `tomorrow`, `next tuesday`, `in 3 days`.
77
-
78
- Recurrence supports: `daily`, `weekly`, `monthly`, `yearly`, `every N days/weeks/months`, `every 2 weeks on monday,friday`.
79
-
80
- ### Sections
81
-
82
- This is what makes remi unique. No other CLI supports Apple Reminders sections with iCloud sync.
48
+ ## Quick start
83
49
 
84
50
  ```bash
85
- remi sections "Groceries" # List sections
86
- remi move "Groceries" "Bananas" --to-section "Dairy" # Move between sections
87
- remi create-section "Groceries" "Produce" # Create a section
88
- remi add "Groceries" "Bananas" --section "Produce" # Add to a section
89
- remi delete-section "Groceries" "Produce" # Delete a section
51
+ remi lists # See all lists
52
+ remi list "Groceries" # View a list (fuzzy: remi list groceries)
53
+ remi add "Groceries" "Buy milk" --section "Dairy" # Add to a section
54
+ remi today # What's due today?
55
+ remi complete "Groceries" "milk" # Fuzzy complete
90
56
  ```
91
57
 
92
- Sections sync to iCloud via resolution token maps (CRDT-style vector clocks). See the [technical design](docs/TECHNICAL_DESIGN.md) for details.
58
+ ## Usage
93
59
 
94
- ### List management
60
+ ### Check what's due
95
61
 
96
62
  ```bash
97
- remi create-list "Home Projects"
98
- remi delete-list "Home Projects" --confirm
63
+ remi today # Due today
64
+ remi overdue # Past due
65
+ remi upcoming --days 7 # Coming up
66
+ remi search "dentist" # Search across all lists
99
67
  ```
100
68
 
101
- ### System
69
+ ### Manage reminders
102
70
 
103
71
  ```bash
104
- remi doctor # Check system health
105
- remi doctor --db # Show database stats
72
+ remi add "Work" "Review PR" --due "next friday" --priority high
73
+ remi add "Work" "Standup" --due tomorrow --repeat daily
74
+ remi complete "Work" "standup"
75
+ remi update "Work" "Review PR" --due "in 3 days"
76
+ remi delete "Work" "Review PR" --confirm
106
77
  ```
107
78
 
108
- ### Shell completions
79
+ Dates: `tomorrow`, `next tuesday`, `in 3 days`, or `YYYY-MM-DD`
80
+ Recurrence: `daily`, `weekly`, `monthly`, `every 2 weeks`, `every 3 months on monday,friday`
109
81
 
110
- Homebrew installs completions automatically. For manual setup:
82
+ ### Organize with sections
111
83
 
112
84
  ```bash
113
- # zsh (uses Homebrew's completions dir if available, otherwise system dir)
114
- remi completions zsh > $(brew --prefix 2>/dev/null || echo /usr/local)/share/zsh/site-functions/_remi
115
-
116
- # bash
117
- remi completions bash > /usr/local/etc/bash_completion.d/remi
118
-
119
- # fish
120
- remi completions fish > ~/.config/fish/completions/remi.fish
85
+ remi sections "Groceries" # List sections
86
+ remi create-section "Groceries" "Produce" # Create a section
87
+ remi add "Groceries" "Bananas" --section "Produce" # Add to a section
88
+ remi move "Groceries" "Bananas" --to-section "Dairy" # Move between sections
121
89
  ```
122
90
 
123
- ## JSON output
91
+ Sections sync to iCloud via CRDT vector clocks. See [how it works](docs/APPLE_REMINDERS_INTERNALS.md).
92
+
93
+ ### JSON output
124
94
 
125
95
  Every command supports `--json` for machine-readable output:
126
96
 
127
97
  ```bash
128
- remi lists --json
129
98
  remi today --json
130
- remi add "Work" "Task" --json
99
+ # {"success": true, "data": [...]}
131
100
  ```
132
101
 
133
- Returns `{"success": true, "data": ...}` on success or `{"success": false, "error": {"code": "...", "message": "...", "suggestion": "..."}}` on failure.
134
-
135
102
  ## AI agent integration
136
103
 
137
- remi is designed for AI agents. Install as a [Claude Code plugin](https://github.com/mattheworiordan/remi):
104
+ remi is designed for AI agents. Use it as an MCP server, Claude Code plugin, or skill:
138
105
 
139
- ```bash
140
- claude plugin marketplace add mattheworiordan/remi
141
- claude plugin install remi
106
+ ### MCP server
107
+
108
+ ```json
109
+ {
110
+ "mcpServers": {
111
+ "remi": {
112
+ "command": "remi",
113
+ "args": ["--mcp"]
114
+ }
115
+ }
116
+ }
142
117
  ```
143
118
 
144
- Or use as a [skill](https://skills.sh):
119
+ 16 tools: `remi_lists`, `remi_add`, `remi_complete`, `remi_move`, `remi_today`, `remi_search`, and more — all with fuzzy matching and structured responses.
120
+
121
+ ### Other integrations
145
122
 
146
123
  ```bash
124
+ # Claude Code plugin
125
+ claude plugin marketplace add mattheworiordan/remi
126
+
127
+ # skills.sh
147
128
  npx skills add mattheworiordan/remi
148
- ```
149
129
 
150
- Then agents can manage reminders via `/remi` or by using the CLI directly with `--json`.
130
+ # OpenClaw
131
+ clawhub install remi
132
+ ```
151
133
 
152
134
  ## Permissions
153
135
 
154
- macOS grants permissions to your **terminal app** (Terminal, iTerm, Cursor, VS Code, etc.), not to remi directly. You only need to do this once per terminal app.
136
+ On first run, macOS will ask you to grant Reminders access (click Allow). Section features also need Full Disk Access for your terminal app.
155
137
 
156
138
  ```bash
157
- remi authorize # Guides you through granting permissions
158
- remi doctor # Shows what's granted and what's missing
139
+ remi authorize # Guides you through both
140
+ remi doctor # Shows what's granted
159
141
  ```
160
142
 
161
- | Permission | What it enables | How to grant |
162
- |------------|----------------|--------------|
163
- | **Reminders access** | All reminder operations | System dialog on first run — click Allow |
164
- | **Full Disk Access** | Section features (create-section, move, etc.) | System Settings > Privacy & Security > Full Disk Access — add your terminal app |
143
+ ## Requirements
165
144
 
166
- Most developer terminals (iTerm, Ghostty, VS Code) already have Full Disk Access. If you only need basic reminder operations (no sections), Reminders access alone is sufficient.
145
+ - macOS 13+ (Ventura or later)
146
+ - Node.js 18+
147
+ - Xcode Command Line Tools (`xcode-select --install`)
167
148
 
168
149
  ## How it works
169
150
 
170
- remi uses a three-layer architecture to interact with Apple Reminders:
171
-
172
- | Layer | API | Used for |
173
- |-------|-----|----------|
174
- | 1 | **EventKit** (public) | Standard CRUD — lists, reminders, queries |
175
- | 2 | **ReminderKit** (private framework) | Section CRUD — create, list, delete sections |
176
- | 3 | **SQLite + Resolution Token Maps** | Section membership sync via CRDT vector clocks |
177
-
178
- The key innovation is Layer 3: directly writing to the Reminders SQLite database with proper resolution token map updates so that `remindd` (Apple's sync daemon) pushes section membership changes to CloudKit. This is the only known way to assign reminders to sections and have it sync across devices.
179
-
180
- See [docs/TECHNICAL_DESIGN.md](docs/TECHNICAL_DESIGN.md) for the full reverse-engineering story.
151
+ remi uses three layers because Apple never exposed sections in their public API:
181
152
 
182
- ## Requirements
153
+ | Layer | What | Why |
154
+ |-------|------|-----|
155
+ | **EventKit** | Reminder CRUD, queries, recurrence | Stable public API |
156
+ | **ReminderKit** | Section CRUD | Private framework — only way to create sections that sync |
157
+ | **SQLite + Token Maps** | Section membership | Direct database writes with CRDT vector clocks for iCloud sync |
183
158
 
184
- - macOS 13+ (Ventura or later)
185
- - Node.js 18+
186
- - Xcode Command Line Tools (`xcode-select --install`)
187
- - Apple Reminders access (granted on first use)
159
+ The [full reverse-engineering story](docs/APPLE_REMINDERS_INTERNALS.md) explains what we discovered about Apple's undocumented sync architecture.
188
160
 
189
- ## Development
161
+ ## Contributing
190
162
 
191
163
  ```bash
192
- git clone https://github.com/mattheworiordan/remi.git
193
- cd remi
194
- npm install
195
- npm run build:swift # Compile the section-helper binary
196
- npm run build # Compile TypeScript
197
- npm run dev -- lists # Run in dev mode
198
- npm test # Run unit tests
164
+ git clone https://github.com/mattheworiordan/remi.git && cd remi
165
+ npm install && npm run build:swift && npm run build
166
+ npm test # Unit tests
167
+ npm run test:integration # Integration tests (needs Reminders access)
199
168
  ```
200
169
 
201
170
  ## License
202
171
 
203
- MIT
204
-
205
- ## Author
206
-
207
- [Matthew O'Riordan](https://github.com/mattheworiordan)
172
+ MIT — [Matthew O'Riordan](https://github.com/mattheworiordan)
@@ -2,11 +2,14 @@ import { parseDate } from "../../core/dateparse.js";
2
2
  import { createReminder } from "../../core/eventkit.js";
3
3
  import { assignToSection } from "../../core/membership.js";
4
4
  import { parseRepeat } from "../../core/recurrence.js";
5
+ import { resolveListName, resolveSectionName } from "../../core/resolve.js";
5
6
  import { outputMessage } from "../output.js";
6
7
  export async function addCommand(list, title, opts) {
8
+ const listName = await resolveListName(list);
9
+ const sectionName = opts.section ? await resolveSectionName(listName, opts.section) : undefined;
7
10
  const createOpts = {
8
11
  title,
9
- listName: list,
12
+ listName,
10
13
  due: opts.due ? parseDate(opts.due) : undefined,
11
14
  priority: opts.priority,
12
15
  notes: opts.notes,
@@ -20,15 +23,15 @@ export async function addCommand(list, title, opts) {
20
23
  }
21
24
  const id = await createReminder(createOpts);
22
25
  let warning;
23
- if (opts.section) {
24
- const result = await assignToSection(list, title, opts.section);
26
+ if (sectionName) {
27
+ const result = await assignToSection(listName, title, sectionName);
25
28
  warning = result.warning;
26
29
  }
27
- let msg = `Added "${title}" to "${list}"`;
30
+ let msg = `Added "${title}" to "${listName}"`;
28
31
  if (opts.repeat)
29
32
  msg += ` (repeats ${opts.repeat})`;
30
- if (opts.section)
31
- msg += ` in section "${opts.section}"`;
33
+ if (sectionName)
34
+ msg += ` in section "${sectionName}"`;
32
35
  if (warning)
33
36
  msg += ` (note: ${warning})`;
34
37
  outputMessage(msg, { id });
@@ -1 +1 @@
1
- {"version":3,"file":"add.js","sourceRoot":"","sources":["../../../src/cli/commands/add.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE7C,MAAM,CAAC,KAAK,UAAU,UAAU,CAC/B,IAAY,EACZ,KAAa,EACb,IAMC;IAED,MAAM,UAAU,GAAyC;QACxD,KAAK;QACL,QAAQ,EAAE,IAAI;QACd,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS;QAC/C,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,KAAK,EAAE,IAAI,CAAC,KAAK;KACjB,CAAC;IAEF,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;QACjB,MAAM,GAAG,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACrC,UAAU,CAAC,SAAS,GAAG,GAAG,CAAC,SAAS,CAAC;QACrC,UAAU,CAAC,aAAa,GAAG,GAAG,CAAC,aAAa,CAAC;QAC7C,IAAI,GAAG,CAAC,SAAS;YAAE,UAAU,CAAC,SAAS,GAAG,GAAG,CAAC,SAAS,CAAC;IACzD,CAAC;IAED,MAAM,EAAE,GAAG,MAAM,cAAc,CAAC,UAAU,CAAC,CAAC;IAE5C,IAAI,OAA2B,CAAC;IAChC,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;QAClB,MAAM,MAAM,GAAG,MAAM,eAAe,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QAChE,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC;IAC1B,CAAC;IAED,IAAI,GAAG,GAAG,UAAU,KAAK,SAAS,IAAI,GAAG,CAAC;IAC1C,IAAI,IAAI,CAAC,MAAM;QAAE,GAAG,IAAI,aAAa,IAAI,CAAC,MAAM,GAAG,CAAC;IACpD,IAAI,IAAI,CAAC,OAAO;QAAE,GAAG,IAAI,gBAAgB,IAAI,CAAC,OAAO,GAAG,CAAC;IACzD,IAAI,OAAO;QAAE,GAAG,IAAI,WAAW,OAAO,GAAG,CAAC;IAC1C,aAAa,CAAC,GAAG,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;AAC5B,CAAC"}
1
+ {"version":3,"file":"add.js","sourceRoot":"","sources":["../../../src/cli/commands/add.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAC5E,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE7C,MAAM,CAAC,KAAK,UAAU,UAAU,CAC/B,IAAY,EACZ,KAAa,EACb,IAMC;IAED,MAAM,QAAQ,GAAG,MAAM,eAAe,CAAC,IAAI,CAAC,CAAC;IAC7C,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,kBAAkB,CAAC,QAAQ,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAChG,MAAM,UAAU,GAAyC;QACxD,KAAK;QACL,QAAQ;QACR,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS;QAC/C,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,KAAK,EAAE,IAAI,CAAC,KAAK;KACjB,CAAC;IAEF,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;QACjB,MAAM,GAAG,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACrC,UAAU,CAAC,SAAS,GAAG,GAAG,CAAC,SAAS,CAAC;QACrC,UAAU,CAAC,aAAa,GAAG,GAAG,CAAC,aAAa,CAAC;QAC7C,IAAI,GAAG,CAAC,SAAS;YAAE,UAAU,CAAC,SAAS,GAAG,GAAG,CAAC,SAAS,CAAC;IACzD,CAAC;IAED,MAAM,EAAE,GAAG,MAAM,cAAc,CAAC,UAAU,CAAC,CAAC;IAE5C,IAAI,OAA2B,CAAC;IAChC,IAAI,WAAW,EAAE,CAAC;QACjB,MAAM,MAAM,GAAG,MAAM,eAAe,CAAC,QAAQ,EAAE,KAAK,EAAE,WAAW,CAAC,CAAC;QACnE,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC;IAC1B,CAAC;IAED,IAAI,GAAG,GAAG,UAAU,KAAK,SAAS,QAAQ,GAAG,CAAC;IAC9C,IAAI,IAAI,CAAC,MAAM;QAAE,GAAG,IAAI,aAAa,IAAI,CAAC,MAAM,GAAG,CAAC;IACpD,IAAI,WAAW;QAAE,GAAG,IAAI,gBAAgB,WAAW,GAAG,CAAC;IACvD,IAAI,OAAO;QAAE,GAAG,IAAI,WAAW,OAAO,GAAG,CAAC;IAC1C,aAAa,CAAC,GAAG,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;AAC5B,CAAC"}
@@ -1,9 +1,11 @@
1
1
  import * as eventkit from "../../core/eventkit.js";
2
2
  import { findReminderByTitle } from "../../core/lookup.js";
3
+ import { resolveListName } from "../../core/resolve.js";
3
4
  import { outputMessage } from "../output.js";
4
5
  export async function completeCommand(list, title, opts) {
5
- const reminder = await findReminderByTitle(list, title, opts);
6
+ const listName = await resolveListName(list);
7
+ const reminder = await findReminderByTitle(listName, title, opts);
6
8
  await eventkit.completeReminder(reminder.id);
7
- outputMessage(`Completed "${reminder.title}" in "${list}"`);
9
+ outputMessage(`Completed "${reminder.title}" in "${listName}"`);
8
10
  }
9
11
  //# sourceMappingURL=complete.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"complete.js","sourceRoot":"","sources":["../../../src/cli/commands/complete.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,QAAQ,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE7C,MAAM,CAAC,KAAK,UAAU,eAAe,CACpC,IAAY,EACZ,KAAa,EACb,IAAqB;IAErB,MAAM,QAAQ,GAAG,MAAM,mBAAmB,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;IAC9D,MAAM,QAAQ,CAAC,gBAAgB,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;IAC7C,aAAa,CAAC,cAAc,QAAQ,CAAC,KAAK,SAAS,IAAI,GAAG,CAAC,CAAC;AAC7D,CAAC"}
1
+ {"version":3,"file":"complete.js","sourceRoot":"","sources":["../../../src/cli/commands/complete.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,QAAQ,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE7C,MAAM,CAAC,KAAK,UAAU,eAAe,CACpC,IAAY,EACZ,KAAa,EACb,IAAqB;IAErB,MAAM,QAAQ,GAAG,MAAM,eAAe,CAAC,IAAI,CAAC,CAAC;IAC7C,MAAM,QAAQ,GAAG,MAAM,mBAAmB,CAAC,QAAQ,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;IAClE,MAAM,QAAQ,CAAC,gBAAgB,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;IAC7C,aAAa,CAAC,cAAc,QAAQ,CAAC,KAAK,SAAS,QAAQ,GAAG,CAAC,CAAC;AACjE,CAAC"}
@@ -1,7 +1,9 @@
1
1
  import { createSection } from "../../core/reminderkit.js";
2
+ import { resolveListName } from "../../core/resolve.js";
2
3
  import { outputMessage } from "../output.js";
3
4
  export async function createSectionCommand(list, name) {
4
- await createSection(list, name);
5
- outputMessage(`Created section "${name}" in "${list}"`);
5
+ const listName = await resolveListName(list);
6
+ await createSection(listName, name);
7
+ outputMessage(`Created section "${name}" in "${listName}"`);
6
8
  }
7
9
  //# sourceMappingURL=create-section.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"create-section.js","sourceRoot":"","sources":["../../../src/cli/commands/create-section.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE7C,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,IAAY,EAAE,IAAY;IACpE,MAAM,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IAChC,aAAa,CAAC,oBAAoB,IAAI,SAAS,IAAI,GAAG,CAAC,CAAC;AACzD,CAAC"}
1
+ {"version":3,"file":"create-section.js","sourceRoot":"","sources":["../../../src/cli/commands/create-section.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE7C,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,IAAY,EAAE,IAAY;IACpE,MAAM,QAAQ,GAAG,MAAM,eAAe,CAAC,IAAI,CAAC,CAAC;IAC7C,MAAM,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IACpC,aAAa,CAAC,oBAAoB,IAAI,SAAS,QAAQ,GAAG,CAAC,CAAC;AAC7D,CAAC"}
@@ -1,11 +1,13 @@
1
1
  import { ErrorCode, RemiCommandError } from "../../core/errors.js";
2
2
  import { deleteList } from "../../core/eventkit.js";
3
+ import { resolveListName } from "../../core/resolve.js";
3
4
  import { isJsonMode, outputMessage } from "../output.js";
4
5
  export async function deleteListCommand(name, opts) {
5
6
  if (!opts.confirm && !isJsonMode()) {
6
7
  throw new RemiCommandError(ErrorCode.INVALID_ARGUMENT, "List deletion requires --confirm flag", `Run: remi delete-list "${name}" --confirm`);
7
8
  }
8
- await deleteList(name);
9
- outputMessage(`Deleted list "${name}"`);
9
+ const listName = await resolveListName(name);
10
+ await deleteList(listName);
11
+ outputMessage(`Deleted list "${listName}"`);
10
12
  }
11
13
  //# sourceMappingURL=delete-list.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"delete-list.js","sourceRoot":"","sources":["../../../src/cli/commands/delete-list.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACnE,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACpD,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAEzD,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,IAAY,EAAE,IAA2B;IAChF,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,UAAU,EAAE,EAAE,CAAC;QACpC,MAAM,IAAI,gBAAgB,CACzB,SAAS,CAAC,gBAAgB,EAC1B,uCAAuC,EACvC,0BAA0B,IAAI,aAAa,CAC3C,CAAC;IACH,CAAC;IAED,MAAM,UAAU,CAAC,IAAI,CAAC,CAAC;IACvB,aAAa,CAAC,iBAAiB,IAAI,GAAG,CAAC,CAAC;AACzC,CAAC"}
1
+ {"version":3,"file":"delete-list.js","sourceRoot":"","sources":["../../../src/cli/commands/delete-list.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACnE,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACpD,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAEzD,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,IAAY,EAAE,IAA2B;IAChF,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,UAAU,EAAE,EAAE,CAAC;QACpC,MAAM,IAAI,gBAAgB,CACzB,SAAS,CAAC,gBAAgB,EAC1B,uCAAuC,EACvC,0BAA0B,IAAI,aAAa,CAC3C,CAAC;IACH,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,eAAe,CAAC,IAAI,CAAC,CAAC;IAC7C,MAAM,UAAU,CAAC,QAAQ,CAAC,CAAC;IAC3B,aAAa,CAAC,iBAAiB,QAAQ,GAAG,CAAC,CAAC;AAC7C,CAAC"}
@@ -1,7 +1,10 @@
1
1
  import { deleteSection } from "../../core/reminderkit.js";
2
+ import { resolveListName, resolveSectionName } from "../../core/resolve.js";
2
3
  import { outputMessage } from "../output.js";
3
4
  export async function deleteSectionCommand(list, name) {
4
- await deleteSection(list, name);
5
- outputMessage(`Deleted section "${name}" from "${list}"`);
5
+ const listName = await resolveListName(list);
6
+ const sectionName = await resolveSectionName(listName, name);
7
+ await deleteSection(listName, sectionName);
8
+ outputMessage(`Deleted section "${sectionName}" from "${listName}"`);
6
9
  }
7
10
  //# sourceMappingURL=delete-section.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"delete-section.js","sourceRoot":"","sources":["../../../src/cli/commands/delete-section.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE7C,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,IAAY,EAAE,IAAY;IACpE,MAAM,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IAChC,aAAa,CAAC,oBAAoB,IAAI,WAAW,IAAI,GAAG,CAAC,CAAC;AAC3D,CAAC"}
1
+ {"version":3,"file":"delete-section.js","sourceRoot":"","sources":["../../../src/cli/commands/delete-section.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAC5E,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE7C,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,IAAY,EAAE,IAAY;IACpE,MAAM,QAAQ,GAAG,MAAM,eAAe,CAAC,IAAI,CAAC,CAAC;IAC7C,MAAM,WAAW,GAAG,MAAM,kBAAkB,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IAC7D,MAAM,aAAa,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;IAC3C,aAAa,CAAC,oBAAoB,WAAW,WAAW,QAAQ,GAAG,CAAC,CAAC;AACtE,CAAC"}
@@ -1,14 +1,15 @@
1
1
  import { ErrorCode, RemiCommandError } from "../../core/errors.js";
2
2
  import * as eventkit from "../../core/eventkit.js";
3
3
  import { findReminderByTitle } from "../../core/lookup.js";
4
+ import { resolveListName } from "../../core/resolve.js";
4
5
  import { isJsonMode, outputMessage } from "../output.js";
5
6
  export async function deleteCommand(list, title, opts) {
6
- // Require --confirm in interactive mode (JSON mode skips confirmation for agents)
7
7
  if (!opts.confirm && !isJsonMode()) {
8
8
  throw new RemiCommandError(ErrorCode.INVALID_ARGUMENT, "Deletion requires --confirm flag", `Run: remi delete "${list}" "${title}" --confirm`);
9
9
  }
10
- const reminder = await findReminderByTitle(list, title, opts);
10
+ const listName = await resolveListName(list);
11
+ const reminder = await findReminderByTitle(listName, title, opts);
11
12
  await eventkit.deleteReminder(reminder.id);
12
- outputMessage(`Deleted "${reminder.title}" from "${list}"`);
13
+ outputMessage(`Deleted "${reminder.title}" from "${listName}"`);
13
14
  }
14
15
  //# sourceMappingURL=delete.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"delete.js","sourceRoot":"","sources":["../../../src/cli/commands/delete.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACnE,OAAO,KAAK,QAAQ,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAEzD,MAAM,CAAC,KAAK,UAAU,aAAa,CAClC,IAAY,EACZ,KAAa,EACb,IAAwC;IAExC,kFAAkF;IAClF,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,UAAU,EAAE,EAAE,CAAC;QACpC,MAAM,IAAI,gBAAgB,CACzB,SAAS,CAAC,gBAAgB,EAC1B,kCAAkC,EAClC,qBAAqB,IAAI,MAAM,KAAK,aAAa,CACjD,CAAC;IACH,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,mBAAmB,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;IAC9D,MAAM,QAAQ,CAAC,cAAc,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;IAC3C,aAAa,CAAC,YAAY,QAAQ,CAAC,KAAK,WAAW,IAAI,GAAG,CAAC,CAAC;AAC7D,CAAC"}
1
+ {"version":3,"file":"delete.js","sourceRoot":"","sources":["../../../src/cli/commands/delete.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACnE,OAAO,KAAK,QAAQ,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAEzD,MAAM,CAAC,KAAK,UAAU,aAAa,CAClC,IAAY,EACZ,KAAa,EACb,IAAwC;IAExC,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,UAAU,EAAE,EAAE,CAAC;QACpC,MAAM,IAAI,gBAAgB,CACzB,SAAS,CAAC,gBAAgB,EAC1B,kCAAkC,EAClC,qBAAqB,IAAI,MAAM,KAAK,aAAa,CACjD,CAAC;IACH,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,eAAe,CAAC,IAAI,CAAC,CAAC;IAC7C,MAAM,QAAQ,GAAG,MAAM,mBAAmB,CAAC,QAAQ,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;IAClE,MAAM,QAAQ,CAAC,cAAc,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;IAC3C,aAAa,CAAC,YAAY,QAAQ,CAAC,KAAK,WAAW,QAAQ,GAAG,CAAC,CAAC;AACjE,CAAC"}
@@ -0,0 +1,3 @@
1
+ export declare function demoCommand(opts: {
2
+ cleanup?: boolean;
3
+ }): Promise<void>;
@@ -0,0 +1,134 @@
1
+ import chalk from "chalk";
2
+ import { createList, createReminder, deleteList, listLists } from "../../core/eventkit.js";
3
+ import { assignToSection } from "../../core/membership.js";
4
+ import { createSection } from "../../core/reminderkit.js";
5
+ import { isJsonMode, outputMessage } from "../output.js";
6
+ const DEMO_LIST = "remi Demo";
7
+ function daysFromNow(days) {
8
+ const d = new Date();
9
+ d.setDate(d.getDate() + days);
10
+ return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
11
+ }
12
+ export async function demoCommand(opts) {
13
+ if (opts.cleanup) {
14
+ try {
15
+ await deleteList(DEMO_LIST);
16
+ outputMessage(`Deleted "${DEMO_LIST}"`);
17
+ }
18
+ catch {
19
+ outputMessage(`"${DEMO_LIST}" not found — nothing to clean up`);
20
+ }
21
+ return;
22
+ }
23
+ // Check if demo list already exists
24
+ const lists = await listLists();
25
+ if (lists.some((l) => l.title === DEMO_LIST)) {
26
+ if (!isJsonMode()) {
27
+ process.stdout.write(chalk.yellow(`"${DEMO_LIST}" already exists. Run ${chalk.bold("remi demo --cleanup")} first.\n`));
28
+ }
29
+ return;
30
+ }
31
+ if (!isJsonMode()) {
32
+ process.stdout.write(chalk.bold(`\nCreating "${DEMO_LIST}"...\n\n`));
33
+ }
34
+ // Create list
35
+ await createList(DEMO_LIST);
36
+ // Create sections
37
+ const sections = ["This Week", "Upcoming", "Ideas"];
38
+ for (const s of sections) {
39
+ await createSection(DEMO_LIST, s);
40
+ }
41
+ // Add reminders with various features
42
+ const items = [
43
+ // This Week
44
+ {
45
+ title: "Review pull requests",
46
+ section: "This Week",
47
+ due: daysFromNow(0), // today
48
+ priority: "high",
49
+ },
50
+ {
51
+ title: "Book dentist appointment",
52
+ section: "This Week",
53
+ due: daysFromNow(2),
54
+ },
55
+ {
56
+ title: "Team standup",
57
+ section: "This Week",
58
+ due: daysFromNow(0), // today
59
+ repeat: "DAILY",
60
+ notes: "Zoom link in calendar",
61
+ },
62
+ {
63
+ title: "Weekly grocery run",
64
+ section: "This Week",
65
+ due: daysFromNow(1),
66
+ repeat: "WEEKLY",
67
+ },
68
+ // Upcoming
69
+ {
70
+ title: "Renew passport",
71
+ section: "Upcoming",
72
+ due: daysFromNow(16),
73
+ priority: "high",
74
+ notes: "Check gov.uk for processing times",
75
+ },
76
+ {
77
+ title: "Plan birthday party",
78
+ section: "Upcoming",
79
+ due: daysFromNow(36),
80
+ },
81
+ {
82
+ title: "Car service",
83
+ section: "Upcoming",
84
+ due: daysFromNow(24),
85
+ repeat: "YEARLY",
86
+ },
87
+ // Ideas
88
+ {
89
+ title: "Learn to make sourdough bread",
90
+ section: "Ideas",
91
+ },
92
+ {
93
+ title: "Set up a home weather station",
94
+ section: "Ideas",
95
+ },
96
+ {
97
+ title: "Try the new ramen place on High Street",
98
+ section: "Ideas",
99
+ notes: "Heard it's great from Sarah",
100
+ },
101
+ ];
102
+ for (const item of items) {
103
+ const createOpts = {
104
+ title: item.title,
105
+ listName: DEMO_LIST,
106
+ due: item.due,
107
+ priority: item.priority,
108
+ notes: item.notes,
109
+ };
110
+ if (item.repeat) {
111
+ createOpts.rruleFreq = item.repeat;
112
+ if (item.repeatInterval)
113
+ createOpts.rruleInterval = item.repeatInterval;
114
+ }
115
+ await createReminder(createOpts);
116
+ await assignToSection(DEMO_LIST, item.title, item.section);
117
+ if (!isJsonMode()) {
118
+ process.stdout.write(chalk.dim(` + ${item.title}\n`));
119
+ }
120
+ }
121
+ if (!isJsonMode()) {
122
+ process.stdout.write(`\n${chalk.green("✓")} Demo list created with ${items.length} reminders and ${sections.length} sections.\n`);
123
+ process.stdout.write(chalk.dim(`\nTry these:\n`));
124
+ process.stdout.write(chalk.dim(` remi list demo\n`));
125
+ process.stdout.write(chalk.dim(` remi sections demo\n`));
126
+ process.stdout.write(chalk.dim(` remi complete demo "Book dentist appointment"\n`));
127
+ process.stdout.write(chalk.dim(` remi move demo "Learn to make sourdough bread" --to-section "This Week"\n`));
128
+ process.stdout.write(chalk.dim(` remi demo --cleanup (when done)\n\n`));
129
+ }
130
+ else {
131
+ outputMessage(`Demo list created with ${items.length} reminders and ${sections.length} sections`);
132
+ }
133
+ }
134
+ //# sourceMappingURL=demo.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"demo.js","sourceRoot":"","sources":["../../../src/cli/commands/demo.ts"],"names":[],"mappings":"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"}