@laylzj777/my-apple-apple-eventkit 1.1.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.
- package/CHANGELOG.md +575 -0
- package/LICENSE +21 -0
- package/README.md +304 -0
- package/README.zh-CN.md +270 -0
- package/bin/event +0 -0
- package/bin/event-disclaim +0 -0
- package/dist/bin/apple-calendar.d.ts +6 -0
- package/dist/bin/apple-calendar.js +8 -0
- package/dist/bin/apple-calendar.js.map +1 -0
- package/dist/bin/apple-reminders.d.ts +7 -0
- package/dist/bin/apple-reminders.js +9 -0
- package/dist/bin/apple-reminders.js.map +1 -0
- package/dist/bootstrap.d.ts +11 -0
- package/dist/bootstrap.js +44 -0
- package/dist/bootstrap.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/server/handlers.d.ts +12 -0
- package/dist/server/handlers.js +58 -0
- package/dist/server/handlers.js.map +1 -0
- package/dist/server/mode.d.ts +19 -0
- package/dist/server/mode.js +47 -0
- package/dist/server/mode.js.map +1 -0
- package/dist/server/promptAbstractions.d.ts +61 -0
- package/dist/server/promptAbstractions.js +140 -0
- package/dist/server/promptAbstractions.js.map +1 -0
- package/dist/server/prompts.d.ts +13 -0
- package/dist/server/prompts.js +461 -0
- package/dist/server/prompts.js.map +1 -0
- package/dist/server/server.d.ts +61 -0
- package/dist/server/server.js +112 -0
- package/dist/server/server.js.map +1 -0
- package/dist/tools/definitions.d.ts +9 -0
- package/dist/tools/definitions.js +272 -0
- package/dist/tools/definitions.js.map +1 -0
- package/dist/tools/handlers/calendarHandlers.d.ts +11 -0
- package/dist/tools/handlers/calendarHandlers.js +151 -0
- package/dist/tools/handlers/calendarHandlers.js.map +1 -0
- package/dist/tools/handlers/formatters.d.ts +24 -0
- package/dist/tools/handlers/formatters.js +105 -0
- package/dist/tools/handlers/formatters.js.map +1 -0
- package/dist/tools/handlers/index.d.ts +8 -0
- package/dist/tools/handlers/index.js +9 -0
- package/dist/tools/handlers/index.js.map +1 -0
- package/dist/tools/handlers/listHandlers.d.ts +10 -0
- package/dist/tools/handlers/listHandlers.js +47 -0
- package/dist/tools/handlers/listHandlers.js.map +1 -0
- package/dist/tools/handlers/reminderHandlers.d.ts +10 -0
- package/dist/tools/handlers/reminderHandlers.js +219 -0
- package/dist/tools/handlers/reminderHandlers.js.map +1 -0
- package/dist/tools/handlers/shared.d.ts +28 -0
- package/dist/tools/handlers/shared.js +57 -0
- package/dist/tools/handlers/shared.js.map +1 -0
- package/dist/tools/handlers/subtaskHandlers.d.ts +12 -0
- package/dist/tools/handlers/subtaskHandlers.js +113 -0
- package/dist/tools/handlers/subtaskHandlers.js.map +1 -0
- package/dist/tools/index.d.ts +10 -0
- package/dist/tools/index.js +74 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/types/index.d.ts +263 -0
- package/dist/types/index.js +42 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/prompts.d.ts +88 -0
- package/dist/types/prompts.js +6 -0
- package/dist/types/prompts.js.map +1 -0
- package/dist/types/repository.d.ts +197 -0
- package/dist/types/repository.js +14 -0
- package/dist/types/repository.js.map +1 -0
- package/dist/utils/binaryValidator.d.ts +52 -0
- package/dist/utils/binaryValidator.js +185 -0
- package/dist/utils/binaryValidator.js.map +1 -0
- package/dist/utils/calendarRepository.d.ts +49 -0
- package/dist/utils/calendarRepository.js +226 -0
- package/dist/utils/calendarRepository.js.map +1 -0
- package/dist/utils/constants.d.ts +79 -0
- package/dist/utils/constants.js +80 -0
- package/dist/utils/constants.js.map +1 -0
- package/dist/utils/dateFiltering.d.ts +41 -0
- package/dist/utils/dateFiltering.js +129 -0
- package/dist/utils/dateFiltering.js.map +1 -0
- package/dist/utils/dateUtils.d.ts +27 -0
- package/dist/utils/dateUtils.js +71 -0
- package/dist/utils/dateUtils.js.map +1 -0
- package/dist/utils/errorHandling.d.ts +27 -0
- package/dist/utils/errorHandling.js +88 -0
- package/dist/utils/errorHandling.js.map +1 -0
- package/dist/utils/eventCli.d.ts +40 -0
- package/dist/utils/eventCli.js +294 -0
- package/dist/utils/eventCli.js.map +1 -0
- package/dist/utils/helpers.d.ts +35 -0
- package/dist/utils/helpers.js +61 -0
- package/dist/utils/helpers.js.map +1 -0
- package/dist/utils/looseDateParser.d.ts +19 -0
- package/dist/utils/looseDateParser.js +149 -0
- package/dist/utils/looseDateParser.js.map +1 -0
- package/dist/utils/projectUtils.d.ts +13 -0
- package/dist/utils/projectUtils.js +92 -0
- package/dist/utils/projectUtils.js.map +1 -0
- package/dist/utils/reminderDateParser.d.ts +8 -0
- package/dist/utils/reminderDateParser.js +89 -0
- package/dist/utils/reminderDateParser.js.map +1 -0
- package/dist/utils/reminderRepository.d.ts +33 -0
- package/dist/utils/reminderRepository.js +262 -0
- package/dist/utils/reminderRepository.js.map +1 -0
- package/dist/utils/subtaskUtils.d.ts +103 -0
- package/dist/utils/subtaskUtils.js +253 -0
- package/dist/utils/subtaskUtils.js.map +1 -0
- package/dist/utils/tagUtils.d.ts +58 -0
- package/dist/utils/tagUtils.js +177 -0
- package/dist/utils/tagUtils.js.map +1 -0
- package/dist/utils/timeHelpers.d.ts +40 -0
- package/dist/utils/timeHelpers.js +136 -0
- package/dist/utils/timeHelpers.js.map +1 -0
- package/dist/validation/schemas.d.ts +385 -0
- package/dist/validation/schemas.js +579 -0
- package/dist/validation/schemas.js.map +1 -0
- package/package.json +96 -0
- package/scripts/build-event.mjs +513 -0
- package/scripts/disclaim.c +69 -0
- package/scripts/event-Info.plist +29 -0
- package/scripts/event.entitlements +10 -0
- package/scripts/postinstall.mjs +91 -0
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* subtaskUtils.ts
|
|
3
|
+
* Utilities for handling subtasks stored in reminder notes
|
|
4
|
+
*
|
|
5
|
+
* Subtasks are stored in the notes field with a human-readable format:
|
|
6
|
+
* ---SUBTASKS---
|
|
7
|
+
* [ ] {id} Task title
|
|
8
|
+
* [x] {id} Completed task title
|
|
9
|
+
* ---END SUBTASKS---
|
|
10
|
+
*
|
|
11
|
+
* This format:
|
|
12
|
+
* - Is human-readable in the native Reminders app
|
|
13
|
+
* - Allows programmatic parsing and manipulation
|
|
14
|
+
* - Coexists with tags ([#tag] format) and user notes
|
|
15
|
+
*/
|
|
16
|
+
import { webcrypto } from 'node:crypto';
|
|
17
|
+
// Subtask section markers
|
|
18
|
+
const SUBTASK_START = '---SUBTASKS---';
|
|
19
|
+
const SUBTASK_END = '---END SUBTASKS---';
|
|
20
|
+
// Regex to match the subtask section. Both markers are anchored to line
|
|
21
|
+
// boundaries so that user-supplied content that happens to contain the marker
|
|
22
|
+
// text mid-line cannot forge or truncate the structured section. `\r?\n`
|
|
23
|
+
// keeps the matcher tolerant of CRLF line endings that some tools (or a
|
|
24
|
+
// round-trip through AppleScript) can introduce.
|
|
25
|
+
const SUBTASK_SECTION_REGEX = /(?:^|\r?\n)---SUBTASKS---\r?\n([\s\S]*?)\r?\n---END SUBTASKS---(?=\r?\n|$)/;
|
|
26
|
+
// Regex to match individual subtask lines: [ ] {id} title or [x] {id} title.
|
|
27
|
+
// `\s*` at the end absorbs a trailing `\r` left behind when a CRLF-terminated
|
|
28
|
+
// block is split on `\n`.
|
|
29
|
+
const SUBTASK_LINE_REGEX = /^\[([ x])\]\s*\{([a-f0-9]+)\}\s*(.+?)\s*$/;
|
|
30
|
+
/**
|
|
31
|
+
* Generates a short unique ID (8 hex characters)
|
|
32
|
+
*/
|
|
33
|
+
export function generateSubtaskId() {
|
|
34
|
+
const bytes = new Uint8Array(4);
|
|
35
|
+
webcrypto.getRandomValues(bytes);
|
|
36
|
+
return Array.from(bytes)
|
|
37
|
+
.map((b) => b.toString(16).padStart(2, '0'))
|
|
38
|
+
.join('');
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Parses subtasks from notes content
|
|
42
|
+
* @param notes - The notes string that may contain subtasks
|
|
43
|
+
* @returns Array of subtasks in order
|
|
44
|
+
*/
|
|
45
|
+
export function parseSubtasks(notes) {
|
|
46
|
+
if (!notes)
|
|
47
|
+
return [];
|
|
48
|
+
const match = notes.match(SUBTASK_SECTION_REGEX);
|
|
49
|
+
if (!match)
|
|
50
|
+
return [];
|
|
51
|
+
// Groups 1-3 are mandatory in SUBTASK_SECTION_REGEX / SUBTASK_LINE_REGEX,
|
|
52
|
+
// so non-null assertions on match indices are safe under noUncheckedIndexedAccess.
|
|
53
|
+
const subtaskContent = match[1];
|
|
54
|
+
// Split on either `\n` or `\r\n` so CRLF-terminated input round-trips.
|
|
55
|
+
const lines = subtaskContent.split(/\r?\n/).filter((line) => line.trim());
|
|
56
|
+
const subtasks = [];
|
|
57
|
+
for (const line of lines) {
|
|
58
|
+
const lineMatch = line.match(SUBTASK_LINE_REGEX);
|
|
59
|
+
if (lineMatch) {
|
|
60
|
+
subtasks.push({
|
|
61
|
+
id: lineMatch[2],
|
|
62
|
+
title: lineMatch[3].trim(),
|
|
63
|
+
isCompleted: lineMatch[1] === 'x',
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return subtasks;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Serializes subtasks into the notes format
|
|
71
|
+
* @param subtasks - Array of subtasks to serialize
|
|
72
|
+
* @returns Formatted subtask section string (empty string if no subtasks)
|
|
73
|
+
*/
|
|
74
|
+
export function serializeSubtasks(subtasks) {
|
|
75
|
+
if (!subtasks || subtasks.length === 0)
|
|
76
|
+
return '';
|
|
77
|
+
const lines = subtasks.map((subtask) => {
|
|
78
|
+
const checkbox = subtask.isCompleted ? '[x]' : '[ ]';
|
|
79
|
+
return `${checkbox} {${subtask.id}} ${subtask.title}`;
|
|
80
|
+
});
|
|
81
|
+
return `${SUBTASK_START}\n${lines.join('\n')}\n${SUBTASK_END}`;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Removes the subtask section from notes, returning clean content
|
|
85
|
+
* @param notes - The notes string with potential subtasks
|
|
86
|
+
* @returns Notes content without subtask section
|
|
87
|
+
*/
|
|
88
|
+
export function stripSubtasks(notes) {
|
|
89
|
+
if (!notes)
|
|
90
|
+
return '';
|
|
91
|
+
return notes
|
|
92
|
+
.replace(SUBTASK_SECTION_REGEX, '')
|
|
93
|
+
.replace(/\n{3,}/g, '\n\n') // Collapse multiple newlines
|
|
94
|
+
.trim();
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Combines subtasks with notes content
|
|
98
|
+
* Tags are preserved and subtasks are appended at the end
|
|
99
|
+
* @param subtasks - Array of subtasks
|
|
100
|
+
* @param notes - Existing notes content (may have tags and/or existing subtasks)
|
|
101
|
+
* @returns Combined notes with subtasks appended
|
|
102
|
+
*/
|
|
103
|
+
export function combineSubtasksAndNotes(subtasks, notes) {
|
|
104
|
+
// Remove existing subtasks section from notes
|
|
105
|
+
const cleanNotes = stripSubtasks(notes);
|
|
106
|
+
const subtaskSection = serializeSubtasks(subtasks);
|
|
107
|
+
if (cleanNotes && subtaskSection) {
|
|
108
|
+
return `${cleanNotes}\n\n${subtaskSection}`;
|
|
109
|
+
}
|
|
110
|
+
else if (subtaskSection) {
|
|
111
|
+
return subtaskSection;
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
return cleanNotes;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Adds a subtask to existing notes
|
|
119
|
+
* @param title - Title of the new subtask
|
|
120
|
+
* @param notes - Existing notes content
|
|
121
|
+
* @returns Updated notes with new subtask added
|
|
122
|
+
*/
|
|
123
|
+
export function addSubtask(title, notes) {
|
|
124
|
+
const existingSubtasks = parseSubtasks(notes);
|
|
125
|
+
const newSubtask = {
|
|
126
|
+
id: generateSubtaskId(),
|
|
127
|
+
title: title.trim(),
|
|
128
|
+
isCompleted: false,
|
|
129
|
+
};
|
|
130
|
+
existingSubtasks.push(newSubtask);
|
|
131
|
+
return {
|
|
132
|
+
notes: combineSubtasksAndNotes(existingSubtasks, notes),
|
|
133
|
+
subtask: newSubtask,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Updates a subtask in existing notes
|
|
138
|
+
* @param subtaskId - ID of the subtask to update
|
|
139
|
+
* @param updates - Partial subtask updates
|
|
140
|
+
* @param notes - Existing notes content
|
|
141
|
+
* @returns Updated notes with subtask modified
|
|
142
|
+
*/
|
|
143
|
+
export function updateSubtask(subtaskId, updates, notes) {
|
|
144
|
+
const subtasks = parseSubtasks(notes);
|
|
145
|
+
const index = subtasks.findIndex((s) => s.id === subtaskId);
|
|
146
|
+
if (index === -1) {
|
|
147
|
+
throw new Error(`Subtask with ID '${subtaskId}' not found.`);
|
|
148
|
+
}
|
|
149
|
+
// findIndex returned a valid index, so subtasks[index] is defined.
|
|
150
|
+
const subtask = subtasks[index];
|
|
151
|
+
if (updates.title !== undefined) {
|
|
152
|
+
subtask.title = updates.title.trim();
|
|
153
|
+
}
|
|
154
|
+
if (updates.isCompleted !== undefined) {
|
|
155
|
+
subtask.isCompleted = updates.isCompleted;
|
|
156
|
+
}
|
|
157
|
+
return combineSubtasksAndNotes(subtasks, notes);
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Removes a subtask from existing notes
|
|
161
|
+
* @param subtaskId - ID of the subtask to remove
|
|
162
|
+
* @param notes - Existing notes content
|
|
163
|
+
* @returns Updated notes with subtask removed
|
|
164
|
+
*/
|
|
165
|
+
export function removeSubtask(subtaskId, notes) {
|
|
166
|
+
const subtasks = parseSubtasks(notes);
|
|
167
|
+
const index = subtasks.findIndex((s) => s.id === subtaskId);
|
|
168
|
+
if (index === -1) {
|
|
169
|
+
throw new Error(`Subtask with ID '${subtaskId}' not found.`);
|
|
170
|
+
}
|
|
171
|
+
subtasks.splice(index, 1);
|
|
172
|
+
return combineSubtasksAndNotes(subtasks, notes);
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Toggles a subtask's completion status
|
|
176
|
+
* @param subtaskId - ID of the subtask to toggle
|
|
177
|
+
* @param notes - Existing notes content
|
|
178
|
+
* @returns Updated notes with subtask toggled
|
|
179
|
+
*/
|
|
180
|
+
export function toggleSubtask(subtaskId, notes) {
|
|
181
|
+
const subtasks = parseSubtasks(notes);
|
|
182
|
+
const index = subtasks.findIndex((s) => s.id === subtaskId);
|
|
183
|
+
if (index === -1) {
|
|
184
|
+
throw new Error(`Subtask with ID '${subtaskId}' not found.`);
|
|
185
|
+
}
|
|
186
|
+
// findIndex returned a valid index, so subtasks[index] is defined.
|
|
187
|
+
const subtask = subtasks[index];
|
|
188
|
+
subtask.isCompleted = !subtask.isCompleted;
|
|
189
|
+
return {
|
|
190
|
+
notes: combineSubtasksAndNotes(subtasks, notes),
|
|
191
|
+
subtask,
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Reorders subtasks based on provided ID order
|
|
196
|
+
* @param order - Array of subtask IDs in desired order
|
|
197
|
+
* @param notes - Existing notes content
|
|
198
|
+
* @returns Updated notes with subtasks reordered
|
|
199
|
+
*/
|
|
200
|
+
export function reorderSubtasks(order, notes) {
|
|
201
|
+
const subtasks = parseSubtasks(notes);
|
|
202
|
+
// Create a map for quick lookup
|
|
203
|
+
const subtaskMap = new Map(subtasks.map((s) => [s.id, s]));
|
|
204
|
+
// Validate all IDs exist
|
|
205
|
+
for (const id of order) {
|
|
206
|
+
if (!subtaskMap.has(id)) {
|
|
207
|
+
throw new Error(`Subtask with ID '${id}' not found.`);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
// Check for missing IDs (IDs in subtasks but not in order)
|
|
211
|
+
const orderSet = new Set(order);
|
|
212
|
+
for (const subtask of subtasks) {
|
|
213
|
+
if (!orderSet.has(subtask.id)) {
|
|
214
|
+
throw new Error(`Reorder array is missing subtask ID '${subtask.id}'. All subtask IDs must be included.`);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
// Reorder
|
|
218
|
+
const reorderedSubtasks = order.map((id) => {
|
|
219
|
+
const subtask = subtaskMap.get(id);
|
|
220
|
+
if (!subtask) {
|
|
221
|
+
throw new Error(`Subtask with ID '${id}' not found.`);
|
|
222
|
+
}
|
|
223
|
+
return subtask;
|
|
224
|
+
});
|
|
225
|
+
return combineSubtasksAndNotes(reorderedSubtasks, notes);
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Creates subtasks from an array of titles
|
|
229
|
+
* @param titles - Array of subtask titles
|
|
230
|
+
* @returns Array of new subtasks
|
|
231
|
+
*/
|
|
232
|
+
export function createSubtasksFromTitles(titles) {
|
|
233
|
+
return titles.map((title) => ({
|
|
234
|
+
id: generateSubtaskId(),
|
|
235
|
+
title: title.trim(),
|
|
236
|
+
isCompleted: false,
|
|
237
|
+
}));
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Calculates subtask completion progress
|
|
241
|
+
* @param subtasks - Array of subtasks
|
|
242
|
+
* @returns Progress object with completed count, total, and percentage
|
|
243
|
+
*/
|
|
244
|
+
export function getSubtaskProgress(subtasks) {
|
|
245
|
+
if (!subtasks || subtasks.length === 0) {
|
|
246
|
+
return { completed: 0, total: 0, percentage: 100 };
|
|
247
|
+
}
|
|
248
|
+
const completed = subtasks.filter((s) => s.isCompleted).length;
|
|
249
|
+
const total = subtasks.length;
|
|
250
|
+
const percentage = Math.round((completed / total) * 100);
|
|
251
|
+
return { completed, total, percentage };
|
|
252
|
+
}
|
|
253
|
+
//# sourceMappingURL=subtaskUtils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subtaskUtils.js","sourceRoot":"","sources":["../../src/utils/subtaskUtils.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* tagUtils.ts
|
|
3
|
+
* Utilities for handling tags stored in reminder notes.
|
|
4
|
+
*
|
|
5
|
+
* Supports two tag formats:
|
|
6
|
+
* - Bracket format: [#tagname] (written by this MCP server)
|
|
7
|
+
* - Bare format: #tagname (used by Apple Reminders native UI)
|
|
8
|
+
*
|
|
9
|
+
* Writing always uses bracket format for unambiguous round-tripping.
|
|
10
|
+
* Reading parses both formats so native Apple Reminders tags are recognized.
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Extracts tags from notes content, supporting both [#tag] and bare #tag formats
|
|
14
|
+
* @param notes - The notes string that may contain tags
|
|
15
|
+
* @returns Array of tag names (without # prefix), deduplicated and lowercased
|
|
16
|
+
*/
|
|
17
|
+
export declare function extractTags(notes: string | null | undefined): string[];
|
|
18
|
+
/**
|
|
19
|
+
* Removes tag markers from notes, returning clean content.
|
|
20
|
+
* Strips both [#tag] bracket format and bare #tag format.
|
|
21
|
+
* @param notes - The notes string with potential tags
|
|
22
|
+
* @returns Notes content without tag markers
|
|
23
|
+
*/
|
|
24
|
+
export declare function stripTags(notes: string | null | undefined): string;
|
|
25
|
+
/**
|
|
26
|
+
* Formats tags into the [#tag] format for storage
|
|
27
|
+
* @param tags - Array of tag names (with or without # prefix)
|
|
28
|
+
* @returns Formatted tag string
|
|
29
|
+
*/
|
|
30
|
+
export declare function formatTags(tags: string[]): string;
|
|
31
|
+
/**
|
|
32
|
+
* Combines tags with notes content
|
|
33
|
+
* @param tags - Array of tag names
|
|
34
|
+
* @param notes - Notes content (may already have tags)
|
|
35
|
+
* @returns Combined notes with tags prepended
|
|
36
|
+
*/
|
|
37
|
+
export declare function combineTagsAndNotes(tags: string[] | undefined, notes: string | undefined): string;
|
|
38
|
+
/**
|
|
39
|
+
* Adds tags to existing notes
|
|
40
|
+
* @param tagsToAdd - Tags to add
|
|
41
|
+
* @param notes - Existing notes content
|
|
42
|
+
* @returns Updated notes with added tags
|
|
43
|
+
*/
|
|
44
|
+
export declare function addTagsToNotes(tagsToAdd: string[], notes: string | undefined): string;
|
|
45
|
+
/**
|
|
46
|
+
* Removes tags from existing notes
|
|
47
|
+
* @param tagsToRemove - Tags to remove
|
|
48
|
+
* @param notes - Existing notes content
|
|
49
|
+
* @returns Updated notes with specified tags removed
|
|
50
|
+
*/
|
|
51
|
+
export declare function removeTagsFromNotes(tagsToRemove: string[], notes: string | undefined): string;
|
|
52
|
+
/**
|
|
53
|
+
* Checks if a reminder has all specified tags
|
|
54
|
+
* @param reminderTags - Tags the reminder has
|
|
55
|
+
* @param filterTags - Tags to check for
|
|
56
|
+
* @returns true if reminder has ALL filter tags
|
|
57
|
+
*/
|
|
58
|
+
export declare function hasAllTags(reminderTags: string[] | undefined, filterTags: string[]): boolean;
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* tagUtils.ts
|
|
3
|
+
* Utilities for handling tags stored in reminder notes.
|
|
4
|
+
*
|
|
5
|
+
* Supports two tag formats:
|
|
6
|
+
* - Bracket format: [#tagname] (written by this MCP server)
|
|
7
|
+
* - Bare format: #tagname (used by Apple Reminders native UI)
|
|
8
|
+
*
|
|
9
|
+
* Writing always uses bracket format for unambiguous round-tripping.
|
|
10
|
+
* Reading parses both formats so native Apple Reminders tags are recognized.
|
|
11
|
+
*/
|
|
12
|
+
// Regex to match tags in [#tag] bracket format
|
|
13
|
+
const BRACKET_TAG_REGEX = /\[#([^\]]+)\]/g;
|
|
14
|
+
// Regex to match bare #tag format (Apple Reminders native).
|
|
15
|
+
// Must be preceded by start-of-string or whitespace.
|
|
16
|
+
// Uses negative lookahead to exclude purely numeric tags (e.g. #42)
|
|
17
|
+
// while allowing digit-starting mixed tags (e.g. #1st, #2024q1, #2024年).
|
|
18
|
+
// Body uses Unicode property classes so CJK / Japanese / Korean / other
|
|
19
|
+
// non-Latin scripts work as tag characters (e.g. #雷蒙三十, #日本語, #한국어).
|
|
20
|
+
// The inner negative lookahead `(?![\p{L}\p{N}_-])` replaces the old `\b`,
|
|
21
|
+
// which broke for CJK because `\b` fires between ASCII digits and CJK letters
|
|
22
|
+
// (CJK chars count as `\W` in JS regex), causing `#2024年` to be rejected.
|
|
23
|
+
const BARE_TAG_REGEX = /(?:^|(?<=\s))#(?![0-9]+(?![\p{L}\p{N}_-]))([\p{L}\p{N}_-]+)/gmu;
|
|
24
|
+
/**
|
|
25
|
+
* Normalizes a tag by removing # prefix, trimming, and lowercasing
|
|
26
|
+
* @param tag - Tag string to normalize
|
|
27
|
+
* @returns Normalized tag string
|
|
28
|
+
*/
|
|
29
|
+
function normalizeTag(tag) {
|
|
30
|
+
return tag.replace(/^#/, '').trim().toLowerCase();
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Normalizes an array of tags
|
|
34
|
+
* @param tags - Array of tags to normalize
|
|
35
|
+
* @returns Array of normalized tags
|
|
36
|
+
*/
|
|
37
|
+
function normalizeTags(tags) {
|
|
38
|
+
return tags.map(normalizeTag);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Extracts tags from notes content, supporting both [#tag] and bare #tag formats
|
|
42
|
+
* @param notes - The notes string that may contain tags
|
|
43
|
+
* @returns Array of tag names (without # prefix), deduplicated and lowercased
|
|
44
|
+
*/
|
|
45
|
+
export function extractTags(notes) {
|
|
46
|
+
if (!notes)
|
|
47
|
+
return [];
|
|
48
|
+
const tags = new Set();
|
|
49
|
+
// Extract bracket-format tags: [#tagname] — group 1 is mandatory.
|
|
50
|
+
for (const match of notes.matchAll(BRACKET_TAG_REGEX)) {
|
|
51
|
+
const tag = match[1]?.trim().toLowerCase();
|
|
52
|
+
if (tag)
|
|
53
|
+
tags.add(tag);
|
|
54
|
+
}
|
|
55
|
+
// Extract bare-format tags: #tagname (Apple Reminders native) — group 1 is mandatory.
|
|
56
|
+
for (const match of notes.matchAll(BARE_TAG_REGEX)) {
|
|
57
|
+
const tag = match[1]?.trim().toLowerCase();
|
|
58
|
+
if (tag)
|
|
59
|
+
tags.add(tag);
|
|
60
|
+
}
|
|
61
|
+
return Array.from(tags);
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Removes tag markers from notes, returning clean content.
|
|
65
|
+
* Strips both [#tag] bracket format and bare #tag format.
|
|
66
|
+
* @param notes - The notes string with potential tags
|
|
67
|
+
* @returns Notes content without tag markers
|
|
68
|
+
*/
|
|
69
|
+
export function stripTags(notes) {
|
|
70
|
+
if (!notes)
|
|
71
|
+
return '';
|
|
72
|
+
return notes
|
|
73
|
+
.replace(BRACKET_TAG_REGEX, '')
|
|
74
|
+
.replace(BARE_TAG_REGEX, '')
|
|
75
|
+
.replace(/[ \t]{2,}/g, ' ') // Collapse multiple spaces
|
|
76
|
+
.replace(/^\s+/, '') // Trim leading whitespace
|
|
77
|
+
.replace(/\s+$/, '') // Trim trailing whitespace
|
|
78
|
+
.replace(/\n{3,}/g, '\n\n'); // Collapse multiple newlines
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Formats tags into the [#tag] format for storage
|
|
82
|
+
* @param tags - Array of tag names (with or without # prefix)
|
|
83
|
+
* @returns Formatted tag string
|
|
84
|
+
*/
|
|
85
|
+
export function formatTags(tags) {
|
|
86
|
+
if (!tags || tags.length === 0)
|
|
87
|
+
return '';
|
|
88
|
+
return tags
|
|
89
|
+
.map((tag) => {
|
|
90
|
+
const cleanTag = normalizeTag(tag);
|
|
91
|
+
return cleanTag ? `[#${cleanTag}]` : '';
|
|
92
|
+
})
|
|
93
|
+
.filter(Boolean)
|
|
94
|
+
.join(' ');
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Combines tags with notes content
|
|
98
|
+
* @param tags - Array of tag names
|
|
99
|
+
* @param notes - Notes content (may already have tags)
|
|
100
|
+
* @returns Combined notes with tags prepended
|
|
101
|
+
*/
|
|
102
|
+
export function combineTagsAndNotes(tags, notes) {
|
|
103
|
+
const existingTags = extractTags(notes);
|
|
104
|
+
const cleanNotes = stripTags(notes);
|
|
105
|
+
const mergedTags = tags ? [...tags, ...existingTags] : existingTags;
|
|
106
|
+
const allTags = [...new Set(normalizeTags(mergedTags))];
|
|
107
|
+
const formattedTags = formatTags(allTags);
|
|
108
|
+
if (formattedTags && cleanNotes) {
|
|
109
|
+
return `${formattedTags}\n${cleanNotes}`;
|
|
110
|
+
}
|
|
111
|
+
else if (formattedTags) {
|
|
112
|
+
return formattedTags;
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
return cleanNotes;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Adds tags to existing notes
|
|
120
|
+
* @param tagsToAdd - Tags to add
|
|
121
|
+
* @param notes - Existing notes content
|
|
122
|
+
* @returns Updated notes with added tags
|
|
123
|
+
*/
|
|
124
|
+
export function addTagsToNotes(tagsToAdd, notes) {
|
|
125
|
+
const existingTags = extractTags(notes);
|
|
126
|
+
const cleanNotes = stripTags(notes);
|
|
127
|
+
const normalizedNewTags = normalizeTags(tagsToAdd);
|
|
128
|
+
const allTags = [...new Set([...existingTags, ...normalizedNewTags])];
|
|
129
|
+
const formattedTags = formatTags(allTags);
|
|
130
|
+
if (formattedTags && cleanNotes) {
|
|
131
|
+
return `${formattedTags}\n${cleanNotes}`;
|
|
132
|
+
}
|
|
133
|
+
else if (formattedTags) {
|
|
134
|
+
return formattedTags;
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
return cleanNotes;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Removes tags from existing notes
|
|
142
|
+
* @param tagsToRemove - Tags to remove
|
|
143
|
+
* @param notes - Existing notes content
|
|
144
|
+
* @returns Updated notes with specified tags removed
|
|
145
|
+
*/
|
|
146
|
+
export function removeTagsFromNotes(tagsToRemove, notes) {
|
|
147
|
+
const existingTags = extractTags(notes);
|
|
148
|
+
const cleanNotes = stripTags(notes);
|
|
149
|
+
const normalizedRemove = normalizeTags(tagsToRemove);
|
|
150
|
+
const remainingTags = existingTags.filter((tag) => !normalizedRemove.includes(tag));
|
|
151
|
+
const formattedTags = formatTags(remainingTags);
|
|
152
|
+
if (formattedTags && cleanNotes) {
|
|
153
|
+
return `${formattedTags}\n${cleanNotes}`;
|
|
154
|
+
}
|
|
155
|
+
else if (formattedTags) {
|
|
156
|
+
return formattedTags;
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
return cleanNotes;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Checks if a reminder has all specified tags
|
|
164
|
+
* @param reminderTags - Tags the reminder has
|
|
165
|
+
* @param filterTags - Tags to check for
|
|
166
|
+
* @returns true if reminder has ALL filter tags
|
|
167
|
+
*/
|
|
168
|
+
export function hasAllTags(reminderTags, filterTags) {
|
|
169
|
+
if (!filterTags || filterTags.length === 0)
|
|
170
|
+
return true;
|
|
171
|
+
if (!reminderTags || reminderTags.length === 0)
|
|
172
|
+
return false;
|
|
173
|
+
const normalizedReminderTags = normalizeTags(reminderTags);
|
|
174
|
+
const normalizedFilterTags = normalizeTags(filterTags);
|
|
175
|
+
return normalizedFilterTags.every((tag) => normalizedReminderTags.includes(tag));
|
|
176
|
+
}
|
|
177
|
+
//# sourceMappingURL=tagUtils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tagUtils.js","sourceRoot":"","sources":["../../src/utils/tagUtils.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* timeHelpers.ts
|
|
3
|
+
* Time formatting and context utilities for prompt templates
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Time context information for prompts
|
|
7
|
+
*/
|
|
8
|
+
export interface TimeContext {
|
|
9
|
+
/** Current date and time in ISO format (UTC) */
|
|
10
|
+
currentDateTime: string;
|
|
11
|
+
/** Current date in YYYY-MM-DD format (local timezone) */
|
|
12
|
+
currentDate: string;
|
|
13
|
+
/** Current time in HH:MM format (local timezone) */
|
|
14
|
+
currentTime: string;
|
|
15
|
+
/** Day of the week (Monday, Tuesday, etc.) */
|
|
16
|
+
dayOfWeek: string;
|
|
17
|
+
/** Whether it's currently working hours (9am-6pm) */
|
|
18
|
+
isWorkingHours: boolean;
|
|
19
|
+
/** Time of day description */
|
|
20
|
+
timeOfDay: 'morning' | 'afternoon' | 'evening' | 'night';
|
|
21
|
+
/** Formatted time description for prompts */
|
|
22
|
+
timeDescription: string;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Get comprehensive time context for prompt templates
|
|
26
|
+
*/
|
|
27
|
+
export declare function getTimeContext(): TimeContext;
|
|
28
|
+
/**
|
|
29
|
+
* Format a relative time description for scheduling
|
|
30
|
+
*/
|
|
31
|
+
export declare function formatRelativeTime(targetDate: Date): string;
|
|
32
|
+
/**
|
|
33
|
+
* Get fuzzy time suggestions based on current time
|
|
34
|
+
*/
|
|
35
|
+
export declare function getFuzzyTimeSuggestions(): {
|
|
36
|
+
laterToday: string;
|
|
37
|
+
tomorrow: string;
|
|
38
|
+
endOfWeek: string;
|
|
39
|
+
nextWeek: string;
|
|
40
|
+
};
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* timeHelpers.ts
|
|
3
|
+
* Time formatting and context utilities for prompt templates
|
|
4
|
+
*/
|
|
5
|
+
import { TIME } from './constants.js';
|
|
6
|
+
import { getDateStart, getTodayStart, getTomorrowStart } from './dateUtils.js';
|
|
7
|
+
/**
|
|
8
|
+
* Get comprehensive time context for prompt templates
|
|
9
|
+
*/
|
|
10
|
+
export function getTimeContext() {
|
|
11
|
+
const now = new Date();
|
|
12
|
+
const today = getTodayStart();
|
|
13
|
+
// Use system local timezone for date formatting
|
|
14
|
+
// Format: YYYY-MM-DD in local timezone (not UTC)
|
|
15
|
+
const year = today.getFullYear();
|
|
16
|
+
const month = String(today.getMonth() + 1).padStart(2, '0');
|
|
17
|
+
const day = String(today.getDate()).padStart(2, '0');
|
|
18
|
+
const currentDate = `${year}-${month}-${day}`;
|
|
19
|
+
// Keep ISO format for full datetime
|
|
20
|
+
const currentDateTime = now.toISOString();
|
|
21
|
+
// Local time in HH:MM format
|
|
22
|
+
const currentTime = now.toTimeString().slice(0, 5); // HH:MM
|
|
23
|
+
// Day of week
|
|
24
|
+
const dayOfWeek = now.toLocaleDateString('en-US', { weekday: 'long' });
|
|
25
|
+
// Working hours check (9am-6pm)
|
|
26
|
+
const hour = now.getHours();
|
|
27
|
+
const isWorkingHours = hour >= TIME.WORKING_HOURS_START && hour < TIME.WORKING_HOURS_END;
|
|
28
|
+
// Time of day categorization
|
|
29
|
+
let timeOfDay;
|
|
30
|
+
if (hour >= TIME.MORNING_START && hour < TIME.NOON) {
|
|
31
|
+
timeOfDay = 'morning';
|
|
32
|
+
}
|
|
33
|
+
else if (hour >= TIME.NOON && hour < TIME.AFTERNOON_END) {
|
|
34
|
+
timeOfDay = 'afternoon';
|
|
35
|
+
}
|
|
36
|
+
else if (hour >= TIME.EVENING_START && hour < TIME.NIGHT_START) {
|
|
37
|
+
timeOfDay = 'evening';
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
timeOfDay = 'night';
|
|
41
|
+
}
|
|
42
|
+
// Human-readable time description
|
|
43
|
+
const timeDescription = formatTimeDescription(now, dayOfWeek, timeOfDay, isWorkingHours);
|
|
44
|
+
return {
|
|
45
|
+
currentDateTime,
|
|
46
|
+
currentDate,
|
|
47
|
+
currentTime,
|
|
48
|
+
dayOfWeek,
|
|
49
|
+
isWorkingHours,
|
|
50
|
+
timeOfDay,
|
|
51
|
+
timeDescription,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
const TIME_OF_DAY_LABELS = {
|
|
55
|
+
morning: ' (morning)',
|
|
56
|
+
afternoon: ' (afternoon)',
|
|
57
|
+
evening: ' (evening)',
|
|
58
|
+
night: ' (night)',
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* Create a human-readable time description for prompts
|
|
62
|
+
*/
|
|
63
|
+
function formatTimeDescription(now, dayOfWeek, timeOfDay, isWorkingHours) {
|
|
64
|
+
const timeStr = now.toLocaleTimeString('en-US', {
|
|
65
|
+
hour: 'numeric',
|
|
66
|
+
minute: '2-digit',
|
|
67
|
+
hour12: true,
|
|
68
|
+
});
|
|
69
|
+
return `Current time: ${dayOfWeek} at ${timeStr}${TIME_OF_DAY_LABELS[timeOfDay] || ''}${isWorkingHours ? ' - working hours' : ' - outside working hours'}`;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Format a relative time description for scheduling
|
|
73
|
+
*/
|
|
74
|
+
export function formatRelativeTime(targetDate) {
|
|
75
|
+
const today = getTodayStart();
|
|
76
|
+
const tomorrow = getTomorrowStart();
|
|
77
|
+
const targetDay = getDateStart(targetDate);
|
|
78
|
+
if (targetDay.getTime() === today.getTime()) {
|
|
79
|
+
return `today at ${targetDate.toLocaleTimeString('en-US', {
|
|
80
|
+
hour: 'numeric',
|
|
81
|
+
minute: '2-digit',
|
|
82
|
+
hour12: true,
|
|
83
|
+
})}`;
|
|
84
|
+
}
|
|
85
|
+
else if (targetDay.getTime() === tomorrow.getTime()) {
|
|
86
|
+
return `tomorrow at ${targetDate.toLocaleTimeString('en-US', {
|
|
87
|
+
hour: 'numeric',
|
|
88
|
+
minute: '2-digit',
|
|
89
|
+
hour12: true,
|
|
90
|
+
})}`;
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
return targetDate.toLocaleDateString('en-US', {
|
|
94
|
+
weekday: 'short',
|
|
95
|
+
month: 'short',
|
|
96
|
+
day: 'numeric',
|
|
97
|
+
hour: 'numeric',
|
|
98
|
+
minute: '2-digit',
|
|
99
|
+
hour12: true,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Get fuzzy time suggestions based on current time
|
|
105
|
+
*/
|
|
106
|
+
export function getFuzzyTimeSuggestions() {
|
|
107
|
+
const now = new Date();
|
|
108
|
+
const hour = now.getHours();
|
|
109
|
+
// Later today
|
|
110
|
+
const laterToday = new Date(now);
|
|
111
|
+
laterToday.setHours(Math.min(hour + TIME.LATER_TODAY_HOURS, TIME.END_OF_WEEK_HOUR), 0, 0, 0);
|
|
112
|
+
// Tomorrow morning
|
|
113
|
+
const tomorrow = getTomorrowStart();
|
|
114
|
+
tomorrow.setHours(TIME.DEFAULT_MORNING_HOUR, 0, 0, 0);
|
|
115
|
+
// End of week (Friday 5pm)
|
|
116
|
+
const endOfWeek = new Date(now);
|
|
117
|
+
const currentDay = now.getDay(); // 0 = Sunday, 6 = Saturday
|
|
118
|
+
const daysUntilFriday = currentDay === TIME.SUNDAY
|
|
119
|
+
? TIME.FRIDAY
|
|
120
|
+
: currentDay <= TIME.FRIDAY
|
|
121
|
+
? TIME.FRIDAY - currentDay
|
|
122
|
+
: 12 - currentDay;
|
|
123
|
+
endOfWeek.setDate(now.getDate() + daysUntilFriday);
|
|
124
|
+
endOfWeek.setHours(TIME.END_OF_WEEK_HOUR, 0, 0, 0);
|
|
125
|
+
// Next week
|
|
126
|
+
const nextWeek = new Date(now);
|
|
127
|
+
nextWeek.setDate(now.getDate() + 7);
|
|
128
|
+
nextWeek.setHours(TIME.DEFAULT_MORNING_HOUR, 0, 0, 0);
|
|
129
|
+
return {
|
|
130
|
+
laterToday: formatRelativeTime(laterToday),
|
|
131
|
+
tomorrow: formatRelativeTime(tomorrow),
|
|
132
|
+
endOfWeek: formatRelativeTime(endOfWeek),
|
|
133
|
+
nextWeek: formatRelativeTime(nextWeek),
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
//# sourceMappingURL=timeHelpers.js.map
|