@iflow-mcp/omnifocus-mcp 1.2.3

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 (69) hide show
  1. package/QUERY_TOOL_EXAMPLES.md +298 -0
  2. package/QUERY_TOOL_REFERENCE.md +228 -0
  3. package/README.md +250 -0
  4. package/assets/omnifocus-mcp-logo.png +0 -0
  5. package/cli.cjs +9 -0
  6. package/dist/omnifocustypes.js +48 -0
  7. package/dist/server.js +44 -0
  8. package/dist/tools/definitions/addOmniFocusTask.js +76 -0
  9. package/dist/tools/definitions/addProject.js +61 -0
  10. package/dist/tools/definitions/batchAddItems.js +89 -0
  11. package/dist/tools/definitions/batchRemoveItems.js +74 -0
  12. package/dist/tools/definitions/dumpDatabase.js +259 -0
  13. package/dist/tools/definitions/editItem.js +88 -0
  14. package/dist/tools/definitions/getPerspectiveView.js +107 -0
  15. package/dist/tools/definitions/listPerspectives.js +65 -0
  16. package/dist/tools/definitions/queryOmnifocus.js +190 -0
  17. package/dist/tools/definitions/removeItem.js +80 -0
  18. package/dist/tools/dumpDatabase.js +121 -0
  19. package/dist/tools/dumpDatabaseOptimized.js +192 -0
  20. package/dist/tools/primitives/addOmniFocusTask.js +227 -0
  21. package/dist/tools/primitives/addProject.js +132 -0
  22. package/dist/tools/primitives/batchAddItems.js +166 -0
  23. package/dist/tools/primitives/batchRemoveItems.js +44 -0
  24. package/dist/tools/primitives/editItem.js +443 -0
  25. package/dist/tools/primitives/getPerspectiveView.js +50 -0
  26. package/dist/tools/primitives/listPerspectives.js +34 -0
  27. package/dist/tools/primitives/queryOmnifocus.js +365 -0
  28. package/dist/tools/primitives/queryOmnifocusDebug.js +135 -0
  29. package/dist/tools/primitives/removeItem.js +177 -0
  30. package/dist/types.js +1 -0
  31. package/dist/utils/cacheManager.js +187 -0
  32. package/dist/utils/dateFormatting.js +58 -0
  33. package/dist/utils/omnifocusScripts/getPerspectiveView.js +169 -0
  34. package/dist/utils/omnifocusScripts/listPerspectives.js +59 -0
  35. package/dist/utils/omnifocusScripts/omnifocusDump.js +223 -0
  36. package/dist/utils/scriptExecution.js +113 -0
  37. package/package.json +37 -0
  38. package/src/omnifocustypes.ts +89 -0
  39. package/src/server.ts +109 -0
  40. package/src/tools/definitions/addOmniFocusTask.ts +80 -0
  41. package/src/tools/definitions/addProject.ts +67 -0
  42. package/src/tools/definitions/batchAddItems.ts +98 -0
  43. package/src/tools/definitions/batchRemoveItems.ts +80 -0
  44. package/src/tools/definitions/dumpDatabase.ts +311 -0
  45. package/src/tools/definitions/editItem.ts +96 -0
  46. package/src/tools/definitions/getPerspectiveView.ts +125 -0
  47. package/src/tools/definitions/listPerspectives.ts +72 -0
  48. package/src/tools/definitions/queryOmnifocus.ts +212 -0
  49. package/src/tools/definitions/removeItem.ts +86 -0
  50. package/src/tools/dumpDatabase.ts +196 -0
  51. package/src/tools/dumpDatabaseOptimized.ts +231 -0
  52. package/src/tools/primitives/addOmniFocusTask.ts +252 -0
  53. package/src/tools/primitives/addProject.ts +156 -0
  54. package/src/tools/primitives/batchAddItems.ts +207 -0
  55. package/src/tools/primitives/batchRemoveItems.ts +64 -0
  56. package/src/tools/primitives/editItem.ts +507 -0
  57. package/src/tools/primitives/getPerspectiveView.ts +71 -0
  58. package/src/tools/primitives/listPerspectives.ts +53 -0
  59. package/src/tools/primitives/queryOmnifocus.ts +394 -0
  60. package/src/tools/primitives/queryOmnifocusDebug.ts +139 -0
  61. package/src/tools/primitives/removeItem.ts +195 -0
  62. package/src/types.ts +107 -0
  63. package/src/utils/cacheManager.ts +234 -0
  64. package/src/utils/dateFormatting.ts +81 -0
  65. package/src/utils/omnifocusScripts/getPerspectiveView.js +169 -0
  66. package/src/utils/omnifocusScripts/listPerspectives.js +59 -0
  67. package/src/utils/omnifocusScripts/omnifocusDump.js +223 -0
  68. package/src/utils/scriptExecution.ts +128 -0
  69. package/tsconfig.json +15 -0
@@ -0,0 +1,365 @@
1
+ import { executeOmniFocusScript } from '../../utils/scriptExecution.js';
2
+ export async function queryOmnifocus(params) {
3
+ try {
4
+ // Create JXA script for the query
5
+ const jxaScript = generateQueryScript(params);
6
+ // Write script to temp file and execute
7
+ const tempFile = `/tmp/omnifocus_query_${Date.now()}.js`;
8
+ const fs = await import('fs');
9
+ fs.writeFileSync(tempFile, jxaScript);
10
+ // Execute the script
11
+ const result = await executeOmniFocusScript(tempFile);
12
+ // Clean up temp file
13
+ fs.unlinkSync(tempFile);
14
+ if (result.error) {
15
+ return {
16
+ success: false,
17
+ error: result.error
18
+ };
19
+ }
20
+ return {
21
+ success: true,
22
+ items: params.summary ? undefined : result.items,
23
+ count: result.count
24
+ };
25
+ }
26
+ catch (error) {
27
+ console.error('Error querying OmniFocus:', error);
28
+ return {
29
+ success: false,
30
+ error: error instanceof Error ? error.message : 'Unknown error occurred'
31
+ };
32
+ }
33
+ }
34
+ function generateQueryScript(params) {
35
+ const { entity, filters = {}, fields, limit, sortBy, sortOrder, includeCompleted = false, summary = false } = params;
36
+ // Build the JXA script based on the entity type and filters
37
+ return `(() => {
38
+ try {
39
+ const startTime = new Date();
40
+
41
+ // Helper function to format dates
42
+ function formatDate(date) {
43
+ if (!date) return null;
44
+ return date.toISOString();
45
+ }
46
+
47
+ // Helper to check date filters
48
+ function checkDateFilter(itemDate, daysFromNow) {
49
+ if (!itemDate) return false;
50
+ const futureDate = new Date();
51
+ futureDate.setDate(futureDate.getDate() + daysFromNow);
52
+ return itemDate <= futureDate;
53
+ }
54
+
55
+ // Status mappings
56
+ const taskStatusMap = {
57
+ [Task.Status.Available]: "Available",
58
+ [Task.Status.Blocked]: "Blocked",
59
+ [Task.Status.Completed]: "Completed",
60
+ [Task.Status.Dropped]: "Dropped",
61
+ [Task.Status.DueSoon]: "DueSoon",
62
+ [Task.Status.Next]: "Next",
63
+ [Task.Status.Overdue]: "Overdue"
64
+ };
65
+
66
+ const projectStatusMap = {
67
+ [Project.Status.Active]: "Active",
68
+ [Project.Status.Done]: "Done",
69
+ [Project.Status.Dropped]: "Dropped",
70
+ [Project.Status.OnHold]: "OnHold"
71
+ };
72
+
73
+ // Get the appropriate collection based on entity type
74
+ let items = [];
75
+ const entityType = "${entity}";
76
+
77
+ if (entityType === "tasks") {
78
+ items = flattenedTasks;
79
+ } else if (entityType === "projects") {
80
+ items = flattenedProjects;
81
+ } else if (entityType === "folders") {
82
+ items = flattenedFolders;
83
+ }
84
+
85
+ // Apply filters
86
+ let filtered = items.filter(item => {
87
+ // Skip completed/dropped unless explicitly requested
88
+ if (!${includeCompleted}) {
89
+ if (entityType === "tasks") {
90
+ if (item.taskStatus === Task.Status.Completed ||
91
+ item.taskStatus === Task.Status.Dropped) {
92
+ return false;
93
+ }
94
+ } else if (entityType === "projects") {
95
+ if (item.status === Project.Status.Done ||
96
+ item.status === Project.Status.Dropped) {
97
+ return false;
98
+ }
99
+ }
100
+ }
101
+
102
+ // Apply specific filters
103
+ ${generateFilterConditions(entity, filters)}
104
+
105
+ return true;
106
+ });
107
+
108
+ // Apply sorting if specified
109
+ ${sortBy ? generateSortLogic(sortBy, sortOrder) : ''}
110
+
111
+ // Apply limit if specified
112
+ ${limit ? `filtered = filtered.slice(0, ${limit});` : ''}
113
+
114
+ // If summary mode, just return count
115
+ if (${summary}) {
116
+ return JSON.stringify({
117
+ count: filtered.length,
118
+ error: null
119
+ });
120
+ }
121
+
122
+ // Transform items to return only requested fields
123
+ const results = filtered.map(item => {
124
+ ${generateFieldMapping(entity, fields)}
125
+ });
126
+
127
+ return JSON.stringify({
128
+ items: results,
129
+ count: results.length,
130
+ error: null
131
+ });
132
+
133
+ } catch (error) {
134
+ return JSON.stringify({
135
+ error: "Script execution error: " + error.toString(),
136
+ items: [],
137
+ count: 0
138
+ });
139
+ }
140
+ })();`;
141
+ }
142
+ function generateFilterConditions(entity, filters) {
143
+ const conditions = [];
144
+ if (entity === 'tasks') {
145
+ if (filters.projectName) {
146
+ conditions.push(`
147
+ if (item.containingProject) {
148
+ const projectName = item.containingProject.name.toLowerCase();
149
+ if (!projectName.includes("${filters.projectName.toLowerCase()}")) return false;
150
+ } else if ("${filters.projectName.toLowerCase()}" !== "inbox") {
151
+ return false;
152
+ }
153
+ `);
154
+ }
155
+ if (filters.projectId) {
156
+ conditions.push(`
157
+ if (!item.containingProject ||
158
+ item.containingProject.id.primaryKey !== "${filters.projectId}") {
159
+ return false;
160
+ }
161
+ `);
162
+ }
163
+ if (filters.tags && filters.tags.length > 0) {
164
+ const tagCondition = filters.tags.map((tag) => `item.tags.some(t => t.name === "${tag}")`).join(' || ');
165
+ conditions.push(`if (!(${tagCondition})) return false;`);
166
+ }
167
+ if (filters.status && filters.status.length > 0) {
168
+ const statusCondition = filters.status.map((status) => `taskStatusMap[item.taskStatus] === "${status}"`).join(' || ');
169
+ conditions.push(`if (!(${statusCondition})) return false;`);
170
+ }
171
+ if (filters.flagged !== undefined) {
172
+ conditions.push(`if (item.flagged !== ${filters.flagged}) return false;`);
173
+ }
174
+ if (filters.dueWithin !== undefined) {
175
+ conditions.push(`
176
+ if (!item.dueDate || !checkDateFilter(item.dueDate, ${filters.dueWithin})) {
177
+ return false;
178
+ }
179
+ `);
180
+ }
181
+ if (filters.hasNote !== undefined) {
182
+ conditions.push(`
183
+ const hasNote = item.note && item.note.trim().length > 0;
184
+ if (hasNote !== ${filters.hasNote}) return false;
185
+ `);
186
+ }
187
+ }
188
+ if (entity === 'projects') {
189
+ if (filters.folderId) {
190
+ conditions.push(`
191
+ if (!item.parentFolder ||
192
+ item.parentFolder.id.primaryKey !== "${filters.folderId}") {
193
+ return false;
194
+ }
195
+ `);
196
+ }
197
+ if (filters.status && filters.status.length > 0) {
198
+ const statusCondition = filters.status.map((status) => `projectStatusMap[item.status] === "${status}"`).join(' || ');
199
+ conditions.push(`if (!(${statusCondition})) return false;`);
200
+ }
201
+ }
202
+ return conditions.join('\n');
203
+ }
204
+ function generateSortLogic(sortBy, sortOrder) {
205
+ const order = sortOrder === 'desc' ? -1 : 1;
206
+ return `
207
+ filtered.sort((a, b) => {
208
+ let aVal = a.${sortBy};
209
+ let bVal = b.${sortBy};
210
+
211
+ // Handle null/undefined values
212
+ if (aVal == null && bVal == null) return 0;
213
+ if (aVal == null) return 1;
214
+ if (bVal == null) return -1;
215
+
216
+ // Compare based on type
217
+ if (typeof aVal === 'string') {
218
+ return aVal.localeCompare(bVal) * ${order};
219
+ } else if (aVal instanceof Date) {
220
+ return (aVal.getTime() - bVal.getTime()) * ${order};
221
+ } else {
222
+ return (aVal - bVal) * ${order};
223
+ }
224
+ });
225
+ `;
226
+ }
227
+ function generateFieldMapping(entity, fields) {
228
+ // If no specific fields requested, return common fields based on entity
229
+ if (!fields || fields.length === 0) {
230
+ if (entity === 'tasks') {
231
+ return `
232
+ const obj = {
233
+ id: item.id.primaryKey,
234
+ name: item.name || "",
235
+ flagged: item.flagged || false,
236
+ taskStatus: taskStatusMap[item.taskStatus] || "Unknown",
237
+ dueDate: formatDate(item.dueDate),
238
+ deferDate: formatDate(item.deferDate),
239
+ tagNames: item.tags ? item.tags.map(t => t.name) : [],
240
+ projectName: item.containingProject ? item.containingProject.name : (item.inInbox ? "Inbox" : null),
241
+ estimatedMinutes: item.estimatedMinutes || null
242
+ };
243
+ if (item.note && item.note.trim()) obj.note = item.note;
244
+ return obj;
245
+ `;
246
+ }
247
+ else if (entity === 'projects') {
248
+ return `
249
+ const taskArray = item.tasks || [];
250
+ return {
251
+ id: item.id.primaryKey,
252
+ name: item.name || "",
253
+ status: projectStatusMap[item.status] || "Unknown",
254
+ folderName: item.parentFolder ? item.parentFolder.name : null,
255
+ taskCount: taskArray.length,
256
+ flagged: item.flagged || false,
257
+ dueDate: formatDate(item.dueDate),
258
+ deferDate: formatDate(item.deferDate)
259
+ };
260
+ `;
261
+ }
262
+ else if (entity === 'folders') {
263
+ return `
264
+ const projectArray = item.projects || [];
265
+ return {
266
+ id: item.id.primaryKey,
267
+ name: item.name || "",
268
+ projectCount: projectArray.length,
269
+ path: item.container ? item.container.name + "/" + item.name : item.name
270
+ };
271
+ `;
272
+ }
273
+ }
274
+ // Generate mapping for specific fields
275
+ const mappings = fields.map(field => {
276
+ // Handle special field mappings based on entity type
277
+ if (field === 'id') {
278
+ return `id: item.id.primaryKey`;
279
+ }
280
+ else if (field === 'taskStatus') {
281
+ return `taskStatus: taskStatusMap[item.taskStatus]`;
282
+ }
283
+ else if (field === 'status') {
284
+ return `status: projectStatusMap[item.status]`;
285
+ }
286
+ else if (field === 'modificationDate' || field === 'modified') {
287
+ return `modificationDate: formatDate(item.modified)`;
288
+ }
289
+ else if (field === 'creationDate' || field === 'added') {
290
+ return `creationDate: formatDate(item.added)`;
291
+ }
292
+ else if (field === 'completionDate') {
293
+ return `completionDate: item.completionDate ? formatDate(item.completionDate) : null`;
294
+ }
295
+ else if (field === 'dueDate') {
296
+ return `dueDate: formatDate(item.dueDate)`;
297
+ }
298
+ else if (field === 'deferDate') {
299
+ return `deferDate: formatDate(item.deferDate)`;
300
+ }
301
+ else if (field === 'effectiveDueDate') {
302
+ return `effectiveDueDate: formatDate(item.effectiveDueDate)`;
303
+ }
304
+ else if (field === 'effectiveDeferDate') {
305
+ return `effectiveDeferDate: formatDate(item.effectiveDeferDate)`;
306
+ }
307
+ else if (field === 'tagNames') {
308
+ return `tagNames: item.tags ? item.tags.map(t => t.name) : []`;
309
+ }
310
+ else if (field === 'tags') {
311
+ return `tags: item.tags ? item.tags.map(t => t.id.primaryKey) : []`;
312
+ }
313
+ else if (field === 'projectName') {
314
+ return `projectName: item.containingProject ? item.containingProject.name : (item.inInbox ? "Inbox" : null)`;
315
+ }
316
+ else if (field === 'projectId') {
317
+ return `projectId: item.containingProject ? item.containingProject.id.primaryKey : null`;
318
+ }
319
+ else if (field === 'parentId') {
320
+ return `parentId: item.parent ? item.parent.id.primaryKey : null`;
321
+ }
322
+ else if (field === 'childIds') {
323
+ return `childIds: item.children ? item.children.map(c => c.id.primaryKey) : []`;
324
+ }
325
+ else if (field === 'hasChildren') {
326
+ return `hasChildren: item.children ? item.children.length > 0 : false`;
327
+ }
328
+ else if (field === 'folderName') {
329
+ return `folderName: item.parentFolder ? item.parentFolder.name : null`;
330
+ }
331
+ else if (field === 'folderID') {
332
+ return `folderID: item.parentFolder ? item.parentFolder.id.primaryKey : null`;
333
+ }
334
+ else if (field === 'taskCount') {
335
+ return `taskCount: item.tasks ? item.tasks.length : 0`;
336
+ }
337
+ else if (field === 'tasks') {
338
+ return `tasks: item.tasks ? item.tasks.map(t => t.id.primaryKey) : []`;
339
+ }
340
+ else if (field === 'projectCount') {
341
+ return `projectCount: item.projects ? item.projects.length : 0`;
342
+ }
343
+ else if (field === 'projects') {
344
+ return `projects: item.projects ? item.projects.map(p => p.id.primaryKey) : []`;
345
+ }
346
+ else if (field === 'subfolders') {
347
+ return `subfolders: item.folders ? item.folders.map(f => f.id.primaryKey) : []`;
348
+ }
349
+ else if (field === 'path') {
350
+ return `path: item.container ? item.container.name + "/" + item.name : item.name`;
351
+ }
352
+ else if (field === 'estimatedMinutes') {
353
+ return `estimatedMinutes: item.estimatedMinutes || null`;
354
+ }
355
+ else {
356
+ // Default: try to access the field directly
357
+ return `${field}: item.${field} !== undefined ? item.${field} : null`;
358
+ }
359
+ }).join(',\n ');
360
+ return `
361
+ return {
362
+ ${mappings}
363
+ };
364
+ `;
365
+ }
@@ -0,0 +1,135 @@
1
+ import { executeOmniFocusScript } from '../../utils/scriptExecution.js';
2
+ /**
3
+ * Debug version of queryOmnifocus that returns raw field information
4
+ * Useful for understanding what fields are available in OmniFocus
5
+ */
6
+ export async function queryOmnifocusDebug(entity) {
7
+ const script = `
8
+ (() => {
9
+ try {
10
+ // Get first item of the requested type
11
+ let item;
12
+ const entityType = "${entity}";
13
+
14
+ if (entityType === "task") {
15
+ item = flattenedTasks[0];
16
+ } else if (entityType === "project") {
17
+ item = flattenedProjects[0];
18
+ } else if (entityType === "folder") {
19
+ item = flattenedFolders[0];
20
+ }
21
+
22
+ if (!item) {
23
+ return JSON.stringify({ error: "No items found" });
24
+ }
25
+
26
+ // Get all properties of the item
27
+ const properties = {};
28
+ const skipProps = ['constructor', 'toString', 'valueOf'];
29
+
30
+ for (let prop in item) {
31
+ if (skipProps.includes(prop)) continue;
32
+
33
+ try {
34
+ const value = item[prop];
35
+ const valueType = typeof value;
36
+
37
+ if (value === null) {
38
+ properties[prop] = { type: 'null', value: null };
39
+ } else if (value === undefined) {
40
+ properties[prop] = { type: 'undefined', value: undefined };
41
+ } else if (valueType === 'function') {
42
+ properties[prop] = { type: 'function', value: '[Function]' };
43
+ } else if (value instanceof Date) {
44
+ properties[prop] = { type: 'Date', value: value.toISOString() };
45
+ } else if (Array.isArray(value)) {
46
+ properties[prop] = {
47
+ type: 'Array',
48
+ length: value.length,
49
+ sample: value.length > 0 ? value[0] : null
50
+ };
51
+ } else if (valueType === 'object') {
52
+ // Try to get ID if it's an OmniFocus object
53
+ if (value.id && value.id.primaryKey) {
54
+ properties[prop] = {
55
+ type: 'OFObject',
56
+ id: value.id.primaryKey,
57
+ name: value.name || null
58
+ };
59
+ } else {
60
+ properties[prop] = { type: 'object', keys: Object.keys(value) };
61
+ }
62
+ } else {
63
+ properties[prop] = { type: valueType, value: value };
64
+ }
65
+ } catch (e) {
66
+ properties[prop] = { type: 'error', error: e.toString() };
67
+ }
68
+ }
69
+
70
+ // Also check specific expected properties
71
+ const checkProps = [
72
+ 'id', 'name', 'note', 'flagged', 'dueDate', 'deferDate',
73
+ 'estimatedMinutes', 'modificationDate', 'creationDate',
74
+ 'completionDate', 'taskStatus', 'status', 'tasks', 'projects',
75
+ 'containingProject', 'parentFolder', 'parent', 'children'
76
+ ];
77
+
78
+ const expectedProps = {};
79
+ checkProps.forEach(prop => {
80
+ try {
81
+ const value = item[prop];
82
+ if (value !== undefined) {
83
+ if (value && value.id && value.id.primaryKey) {
84
+ expectedProps[prop] = {
85
+ exists: true,
86
+ type: 'OFObject',
87
+ id: value.id.primaryKey
88
+ };
89
+ } else if (value instanceof Date) {
90
+ expectedProps[prop] = {
91
+ exists: true,
92
+ type: 'Date',
93
+ value: value.toISOString()
94
+ };
95
+ } else if (Array.isArray(value)) {
96
+ expectedProps[prop] = {
97
+ exists: true,
98
+ type: 'Array',
99
+ length: value.length
100
+ };
101
+ } else {
102
+ expectedProps[prop] = {
103
+ exists: true,
104
+ type: typeof value,
105
+ value: value
106
+ };
107
+ }
108
+ } else {
109
+ expectedProps[prop] = { exists: false };
110
+ }
111
+ } catch (e) {
112
+ expectedProps[prop] = { exists: false, error: e.toString() };
113
+ }
114
+ });
115
+
116
+ return JSON.stringify({
117
+ entityType: entityType,
118
+ itemName: item.name || 'Unnamed',
119
+ allProperties: properties,
120
+ expectedProperties: expectedProps
121
+ }, null, 2);
122
+
123
+ } catch (error) {
124
+ return JSON.stringify({ error: error.toString() });
125
+ }
126
+ })();
127
+ `;
128
+ // Write script to temp file and execute
129
+ const fs = await import('fs');
130
+ const tempFile = `/tmp/omnifocus_debug_${Date.now()}.js`;
131
+ fs.writeFileSync(tempFile, script);
132
+ const result = await executeOmniFocusScript(tempFile);
133
+ fs.unlinkSync(tempFile);
134
+ return result;
135
+ }
@@ -0,0 +1,177 @@
1
+ import { exec } from 'child_process';
2
+ import { promisify } from 'util';
3
+ const execAsync = promisify(exec);
4
+ /**
5
+ * Generate pure AppleScript for item removal
6
+ */
7
+ function generateAppleScript(params) {
8
+ // Sanitize and prepare parameters for AppleScript
9
+ const id = params.id?.replace(/['"\\]/g, '\\$&') || ''; // Escape quotes and backslashes
10
+ const name = params.name?.replace(/['"\\]/g, '\\$&') || '';
11
+ const itemType = params.itemType;
12
+ // Verify we have at least one identifier
13
+ if (!id && !name) {
14
+ return `return "{\\\"success\\\":false,\\\"error\\\":\\\"Either id or name must be provided\\\"}"`;
15
+ }
16
+ // Construct AppleScript with error handling
17
+ let script = `
18
+ try
19
+ tell application "OmniFocus"
20
+ tell front document
21
+ -- Find the item to remove
22
+ set foundItem to missing value
23
+ `;
24
+ // Add ID search if provided
25
+ if (id) {
26
+ if (itemType === 'task') {
27
+ script += `
28
+ -- Try to find task by ID (search in projects first, then inbox)
29
+ try
30
+ set foundItem to first flattened task where id = "${id}"
31
+ end try
32
+
33
+ -- If not found in projects, search in inbox
34
+ if foundItem is missing value then
35
+ try
36
+ set foundItem to first inbox task where id = "${id}"
37
+ end try
38
+ end if
39
+ `;
40
+ }
41
+ else {
42
+ script += `
43
+ -- Try to find project by ID
44
+ try
45
+ set foundItem to first flattened project where id = "${id}"
46
+ end try
47
+ `;
48
+ }
49
+ }
50
+ // Add name search if provided (and no ID or as fallback)
51
+ if (!id && name) {
52
+ if (itemType === 'task') {
53
+ script += `
54
+ -- Find task by name (search in projects first, then inbox)
55
+ try
56
+ set foundItem to first flattened task where name = "${name}"
57
+ end try
58
+
59
+ -- If not found in projects, search in inbox
60
+ if foundItem is missing value then
61
+ try
62
+ set foundItem to first inbox task where name = "${name}"
63
+ end try
64
+ end if
65
+ `;
66
+ }
67
+ else {
68
+ script += `
69
+ -- Find project by name
70
+ try
71
+ set foundItem to first flattened project where name = "${name}"
72
+ end try
73
+ `;
74
+ }
75
+ }
76
+ else if (id && name) {
77
+ if (itemType === 'task') {
78
+ script += `
79
+ -- If ID search failed, try to find by name as fallback
80
+ if foundItem is missing value then
81
+ try
82
+ set foundItem to first flattened task where name = "${name}"
83
+ end try
84
+ end if
85
+
86
+ -- If still not found, search in inbox
87
+ if foundItem is missing value then
88
+ try
89
+ set foundItem to first inbox task where name = "${name}"
90
+ end try
91
+ end if
92
+ `;
93
+ }
94
+ else {
95
+ script += `
96
+ -- If ID search failed, try to find project by name as fallback
97
+ if foundItem is missing value then
98
+ try
99
+ set foundItem to first flattened project where name = "${name}"
100
+ end try
101
+ end if
102
+ `;
103
+ }
104
+ }
105
+ // Add the rest of the script
106
+ script += `
107
+ -- If we found the item, remove it
108
+ if foundItem is not missing value then
109
+ set itemName to name of foundItem
110
+ set itemId to id of foundItem as string
111
+
112
+ -- Delete the item
113
+ delete foundItem
114
+
115
+ -- Return success
116
+ return "{\\\"success\\\":true,\\\"id\\\":\\"" & itemId & "\\",\\\"name\\\":\\"" & itemName & "\\"}"
117
+ else
118
+ -- Item not found
119
+ return "{\\\"success\\\":false,\\\"error\\\":\\\"Item not found\\\"}"
120
+ end if
121
+ end tell
122
+ end tell
123
+ on error errorMessage
124
+ return "{\\\"success\\\":false,\\\"error\\\":\\"" & errorMessage & "\\"}"
125
+ end try
126
+ `;
127
+ return script;
128
+ }
129
+ /**
130
+ * Remove a task or project from OmniFocus
131
+ */
132
+ export async function removeItem(params) {
133
+ try {
134
+ // Generate AppleScript
135
+ const script = generateAppleScript(params);
136
+ console.error("Executing AppleScript for removal...");
137
+ console.error(`Item type: ${params.itemType}, ID: ${params.id || 'not provided'}, Name: ${params.name || 'not provided'}`);
138
+ // Log a preview of the script for debugging (first few lines)
139
+ const scriptPreview = script.split('\n').slice(0, 10).join('\n') + '\n...';
140
+ console.error("AppleScript preview:\n", scriptPreview);
141
+ // Execute AppleScript directly
142
+ const { stdout, stderr } = await execAsync(`osascript -e '${script}'`);
143
+ if (stderr) {
144
+ console.error("AppleScript stderr:", stderr);
145
+ }
146
+ console.error("AppleScript stdout:", stdout);
147
+ // Parse the result
148
+ try {
149
+ const result = JSON.parse(stdout);
150
+ // Return the result
151
+ return {
152
+ success: result.success,
153
+ id: result.id,
154
+ name: result.name,
155
+ error: result.error
156
+ };
157
+ }
158
+ catch (parseError) {
159
+ console.error("Error parsing AppleScript result:", parseError);
160
+ return {
161
+ success: false,
162
+ error: `Failed to parse result: ${stdout}`
163
+ };
164
+ }
165
+ }
166
+ catch (error) {
167
+ console.error("Error in removeItem execution:", error);
168
+ // Include more detailed error information
169
+ if (error.message && error.message.includes('syntax error')) {
170
+ console.error("This appears to be an AppleScript syntax error. Review the script generation logic.");
171
+ }
172
+ return {
173
+ success: false,
174
+ error: error?.message || "Unknown error in removeItem"
175
+ };
176
+ }
177
+ }
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};