@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,207 @@
1
+ import { addOmniFocusTask, AddOmniFocusTaskParams } from './addOmniFocusTask.js';
2
+ import { addProject, AddProjectParams } from './addProject.js';
3
+
4
+ // Define the parameters for the batch operation
5
+ export type BatchAddItemsParams = {
6
+ type: 'task' | 'project';
7
+ name: string;
8
+ note?: string;
9
+ dueDate?: string;
10
+ deferDate?: string;
11
+ flagged?: boolean;
12
+ estimatedMinutes?: number;
13
+ tags?: string[];
14
+ projectName?: string; // For tasks
15
+ // Hierarchy for tasks
16
+ parentTaskId?: string;
17
+ parentTaskName?: string;
18
+ tempId?: string;
19
+ parentTempId?: string;
20
+ hierarchyLevel?: number;
21
+ folderName?: string; // For projects
22
+ sequential?: boolean; // For projects
23
+ };
24
+
25
+ // Define the result type for individual operations
26
+ type ItemResult = {
27
+ success: boolean;
28
+ id?: string;
29
+ error?: string;
30
+ };
31
+
32
+ // Define the result type for the batch operation
33
+ type BatchResult = {
34
+ success: boolean;
35
+ results: ItemResult[];
36
+ error?: string;
37
+ };
38
+
39
+ /**
40
+ * Add multiple items (tasks or projects) to OmniFocus
41
+ */
42
+ export async function batchAddItems(items: BatchAddItemsParams[]): Promise<BatchResult> {
43
+ try {
44
+ const results: ItemResult[] = new Array(items.length);
45
+ const processed: boolean[] = new Array(items.length).fill(false);
46
+ const tempToRealId = new Map<string, string>();
47
+
48
+ // Pre-validate cycles in tempId -> parentTempId references
49
+ const tempIndex = new Map<string, number>();
50
+ items.forEach((it, idx) => { if (it.tempId) tempIndex.set(it.tempId, idx); });
51
+
52
+ // Detect cycles using DFS and capture cycle paths
53
+ const visiting = new Set<string>();
54
+ const visited = new Set<string>();
55
+ const inCycle = new Set<string>();
56
+ const cycleMessageByTempId = new Map<string, string>();
57
+ const stack: string[] = [];
58
+
59
+ function dfs(tempId: string) {
60
+ if (visited.has(tempId) || inCycle.has(tempId)) return;
61
+ if (visiting.has(tempId)) return; // already on stack, handled by caller
62
+ visiting.add(tempId);
63
+ stack.push(tempId);
64
+ const idx = tempIndex.get(tempId)!;
65
+ const parentTemp = items[idx].parentTempId;
66
+ if (parentTemp && tempIndex.has(parentTemp)) {
67
+ if (visiting.has(parentTemp)) {
68
+ // Found a cycle; construct path
69
+ const startIdx = stack.indexOf(parentTemp);
70
+ const cycleIds = stack.slice(startIdx).concat(parentTemp);
71
+ const cycleNames = cycleIds.map(tid => {
72
+ const i = tempIndex.get(tid)!;
73
+ return items[i].name || tid;
74
+ });
75
+ const pathText = `${cycleNames.join(' -> ')}`;
76
+ for (const tid of cycleIds) {
77
+ inCycle.add(tid);
78
+ cycleMessageByTempId.set(tid, `Cycle detected: ${pathText}`);
79
+ }
80
+ } else {
81
+ dfs(parentTemp);
82
+ }
83
+ }
84
+ stack.pop();
85
+ visiting.delete(tempId);
86
+ visited.add(tempId);
87
+ }
88
+
89
+ for (const tid of tempIndex.keys()) dfs(tid);
90
+
91
+ // Mark items that participate in cycles as failed early
92
+ for (const tid of inCycle) {
93
+ const idx = tempIndex.get(tid)!;
94
+ const msg = cycleMessageByTempId.get(tid) || `Cycle detected involving tempId: ${tid}`;
95
+ results[idx] = { success: false, error: msg };
96
+ processed[idx] = true;
97
+ }
98
+
99
+ // Mark items with unknown parentTempId (and no explicit parentTaskId) as invalid early
100
+ items.forEach((it, idx) => {
101
+ if (processed[idx]) return;
102
+ if (it.parentTempId && !tempIndex.has(it.parentTempId) && !it.parentTaskId) {
103
+ results[idx] = { success: false, error: `Unknown parentTempId: ${it.parentTempId}` };
104
+ processed[idx] = true;
105
+ }
106
+ });
107
+
108
+ // Stable order: sort by hierarchyLevel (undefined -> 0), then original index
109
+ const indexed = items.map((it, idx) => ({ ...it, __index: idx }));
110
+ indexed.sort((a, b) => (a.hierarchyLevel ?? 0) - (b.hierarchyLevel ?? 0) || a.__index - b.__index);
111
+
112
+ let madeProgress = true;
113
+ while (processed.some(p => !p) && madeProgress) {
114
+ madeProgress = false;
115
+ for (const item of indexed) {
116
+ const i = item.__index;
117
+ if (processed[i]) continue;
118
+ try {
119
+ if (item.type === 'project') {
120
+ const projectParams: AddProjectParams = {
121
+ name: item.name,
122
+ note: item.note,
123
+ dueDate: item.dueDate,
124
+ deferDate: item.deferDate,
125
+ flagged: item.flagged,
126
+ estimatedMinutes: item.estimatedMinutes,
127
+ tags: item.tags,
128
+ folderName: item.folderName,
129
+ sequential: item.sequential
130
+ };
131
+
132
+ const projectResult = await addProject(projectParams);
133
+ results[i] = {
134
+ success: projectResult.success,
135
+ id: projectResult.projectId,
136
+ error: projectResult.error
137
+ };
138
+ processed[i] = true;
139
+ madeProgress = true;
140
+ continue;
141
+ }
142
+
143
+ // task
144
+ let parentTaskId = item.parentTaskId;
145
+ if (!parentTaskId && item.parentTempId) {
146
+ parentTaskId = tempToRealId.get(item.parentTempId);
147
+ if (!parentTaskId) {
148
+ // Parent not created yet; skip this round
149
+ continue;
150
+ }
151
+ }
152
+
153
+ const taskParams: AddOmniFocusTaskParams = {
154
+ name: item.name,
155
+ note: item.note,
156
+ dueDate: item.dueDate,
157
+ deferDate: item.deferDate,
158
+ flagged: item.flagged,
159
+ estimatedMinutes: item.estimatedMinutes,
160
+ tags: item.tags,
161
+ projectName: item.projectName,
162
+ parentTaskId,
163
+ parentTaskName: item.parentTaskName,
164
+ hierarchyLevel: item.hierarchyLevel
165
+ };
166
+
167
+ const taskResult = await addOmniFocusTask(taskParams);
168
+ results[i] = {
169
+ success: taskResult.success,
170
+ id: taskResult.taskId,
171
+ error: taskResult.error
172
+ };
173
+ if (item.tempId && taskResult.taskId && taskResult.success) {
174
+ tempToRealId.set(item.tempId, taskResult.taskId);
175
+ }
176
+ processed[i] = true;
177
+ madeProgress = true;
178
+ } catch (itemError: any) {
179
+ results[i] = {
180
+ success: false,
181
+ error: itemError?.message || 'Unknown error processing item'
182
+ };
183
+ processed[i] = true; // avoid infinite loop on thrown errors
184
+ madeProgress = true;
185
+ }
186
+ }
187
+ }
188
+
189
+ // Any unprocessed due to dependencies/cycles -> fail with message
190
+ for (const item of indexed) {
191
+ const i = item.__index;
192
+ if (!processed[i]) {
193
+ const reason = item.parentTempId && !tempToRealId.has(item.parentTempId)
194
+ ? `Unresolved parentTempId: ${item.parentTempId}`
195
+ : 'Unresolved dependency or cycle';
196
+ results[i] = { success: false, error: reason };
197
+ processed[i] = true;
198
+ }
199
+ }
200
+
201
+ const overallSuccess = results.some(r => r?.success);
202
+ return { success: overallSuccess, results };
203
+ } catch (error: any) {
204
+ console.error('Error in batchAddItems:', error);
205
+ return { success: false, results: [], error: error?.message || 'Unknown error in batchAddItems' };
206
+ }
207
+ }
@@ -0,0 +1,64 @@
1
+ import { removeItem, RemoveItemParams } from './removeItem.js';
2
+
3
+ // Define the parameters for the batch removal operation
4
+ export type BatchRemoveItemsParams = RemoveItemParams;
5
+
6
+ // Define the result type for individual operations
7
+ type ItemResult = {
8
+ success: boolean;
9
+ id?: string;
10
+ name?: string;
11
+ error?: string;
12
+ };
13
+
14
+ // Define the result type for the batch operation
15
+ type BatchResult = {
16
+ success: boolean;
17
+ results: ItemResult[];
18
+ error?: string;
19
+ };
20
+
21
+ /**
22
+ * Remove multiple items (tasks or projects) from OmniFocus
23
+ */
24
+ export async function batchRemoveItems(items: BatchRemoveItemsParams[]): Promise<BatchResult> {
25
+ try {
26
+ // Results array to track individual operation outcomes
27
+ const results: ItemResult[] = [];
28
+
29
+ // Process each item in sequence
30
+ for (const item of items) {
31
+ try {
32
+ // Remove item
33
+ const itemResult = await removeItem(item);
34
+ results.push({
35
+ success: itemResult.success,
36
+ id: itemResult.id,
37
+ name: itemResult.name,
38
+ error: itemResult.error
39
+ });
40
+ } catch (itemError: any) {
41
+ // Handle individual item errors
42
+ results.push({
43
+ success: false,
44
+ error: itemError.message || "Unknown error processing item"
45
+ });
46
+ }
47
+ }
48
+
49
+ // Determine overall success (true if at least one item was removed successfully)
50
+ const overallSuccess = results.some(result => result.success);
51
+
52
+ return {
53
+ success: overallSuccess,
54
+ results: results
55
+ };
56
+ } catch (error: any) {
57
+ console.error("Error in batchRemoveItems:", error);
58
+ return {
59
+ success: false,
60
+ results: [],
61
+ error: error.message || "Unknown error in batchRemoveItems"
62
+ };
63
+ }
64
+ }