@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.
- package/QUERY_TOOL_EXAMPLES.md +298 -0
- package/QUERY_TOOL_REFERENCE.md +228 -0
- package/README.md +250 -0
- package/assets/omnifocus-mcp-logo.png +0 -0
- package/cli.cjs +9 -0
- package/dist/omnifocustypes.js +48 -0
- package/dist/server.js +44 -0
- package/dist/tools/definitions/addOmniFocusTask.js +76 -0
- package/dist/tools/definitions/addProject.js +61 -0
- package/dist/tools/definitions/batchAddItems.js +89 -0
- package/dist/tools/definitions/batchRemoveItems.js +74 -0
- package/dist/tools/definitions/dumpDatabase.js +259 -0
- package/dist/tools/definitions/editItem.js +88 -0
- package/dist/tools/definitions/getPerspectiveView.js +107 -0
- package/dist/tools/definitions/listPerspectives.js +65 -0
- package/dist/tools/definitions/queryOmnifocus.js +190 -0
- package/dist/tools/definitions/removeItem.js +80 -0
- package/dist/tools/dumpDatabase.js +121 -0
- package/dist/tools/dumpDatabaseOptimized.js +192 -0
- package/dist/tools/primitives/addOmniFocusTask.js +227 -0
- package/dist/tools/primitives/addProject.js +132 -0
- package/dist/tools/primitives/batchAddItems.js +166 -0
- package/dist/tools/primitives/batchRemoveItems.js +44 -0
- package/dist/tools/primitives/editItem.js +443 -0
- package/dist/tools/primitives/getPerspectiveView.js +50 -0
- package/dist/tools/primitives/listPerspectives.js +34 -0
- package/dist/tools/primitives/queryOmnifocus.js +365 -0
- package/dist/tools/primitives/queryOmnifocusDebug.js +135 -0
- package/dist/tools/primitives/removeItem.js +177 -0
- package/dist/types.js +1 -0
- package/dist/utils/cacheManager.js +187 -0
- package/dist/utils/dateFormatting.js +58 -0
- package/dist/utils/omnifocusScripts/getPerspectiveView.js +169 -0
- package/dist/utils/omnifocusScripts/listPerspectives.js +59 -0
- package/dist/utils/omnifocusScripts/omnifocusDump.js +223 -0
- package/dist/utils/scriptExecution.js +113 -0
- package/package.json +37 -0
- package/src/omnifocustypes.ts +89 -0
- package/src/server.ts +109 -0
- package/src/tools/definitions/addOmniFocusTask.ts +80 -0
- package/src/tools/definitions/addProject.ts +67 -0
- package/src/tools/definitions/batchAddItems.ts +98 -0
- package/src/tools/definitions/batchRemoveItems.ts +80 -0
- package/src/tools/definitions/dumpDatabase.ts +311 -0
- package/src/tools/definitions/editItem.ts +96 -0
- package/src/tools/definitions/getPerspectiveView.ts +125 -0
- package/src/tools/definitions/listPerspectives.ts +72 -0
- package/src/tools/definitions/queryOmnifocus.ts +212 -0
- package/src/tools/definitions/removeItem.ts +86 -0
- package/src/tools/dumpDatabase.ts +196 -0
- package/src/tools/dumpDatabaseOptimized.ts +231 -0
- package/src/tools/primitives/addOmniFocusTask.ts +252 -0
- package/src/tools/primitives/addProject.ts +156 -0
- package/src/tools/primitives/batchAddItems.ts +207 -0
- package/src/tools/primitives/batchRemoveItems.ts +64 -0
- package/src/tools/primitives/editItem.ts +507 -0
- package/src/tools/primitives/getPerspectiveView.ts +71 -0
- package/src/tools/primitives/listPerspectives.ts +53 -0
- package/src/tools/primitives/queryOmnifocus.ts +394 -0
- package/src/tools/primitives/queryOmnifocusDebug.ts +139 -0
- package/src/tools/primitives/removeItem.ts +195 -0
- package/src/types.ts +107 -0
- package/src/utils/cacheManager.ts +234 -0
- package/src/utils/dateFormatting.ts +81 -0
- package/src/utils/omnifocusScripts/getPerspectiveView.js +169 -0
- package/src/utils/omnifocusScripts/listPerspectives.js +59 -0
- package/src/utils/omnifocusScripts/omnifocusDump.js +223 -0
- package/src/utils/scriptExecution.ts +128 -0
- 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
|
+
}
|