Skip to content

Commit e1fbebe

Browse files
committed
refactor: extract inline OmniJS to external script files
Move OmniJS code from Python string templates to separate .js files for better maintainability and editor support. Add execute_omnijs_with_params function that loads scripts from files and passes parameters. Affected tools: query, tree, perspectives, dump_database.
1 parent c088e3d commit e1fbebe

15 files changed

Lines changed: 1580 additions & 1679 deletions

File tree

src/omnifocus_mcp/mcp_tools/debug/dump_database.py

Lines changed: 9 additions & 227 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
"""Database dump tool for OmniFocus."""
22

33
import json
4-
from ...omnijs import execute_omnijs
4+
5+
from ...omnijs import execute_omnijs_with_params
56

67

78
async def dump_database(
@@ -21,233 +22,14 @@ async def dump_database(
2122
Returns:
2223
Formatted database dump with hierarchical structure
2324
"""
24-
script = f'''
25-
(() => {{
26-
try {{
27-
const hideCompleted = {str(hide_completed).lower()};
28-
const hideRecurringDuplicates = {str(hide_recurring_duplicates).lower()};
29-
30-
// Status mappings
31-
const taskStatusMap = {{}};
32-
taskStatusMap[Task.Status.Available] = "avail";
33-
taskStatusMap[Task.Status.Blocked] = "block";
34-
taskStatusMap[Task.Status.Completed] = "compl";
35-
taskStatusMap[Task.Status.Dropped] = "drop";
36-
taskStatusMap[Task.Status.DueSoon] = "due";
37-
taskStatusMap[Task.Status.Next] = "next";
38-
taskStatusMap[Task.Status.Overdue] = "over";
39-
40-
const projectStatusMap = {{}};
41-
projectStatusMap[Project.Status.Active] = "active";
42-
projectStatusMap[Project.Status.Done] = "done";
43-
projectStatusMap[Project.Status.Dropped] = "dropped";
44-
projectStatusMap[Project.Status.OnHold] = "onHold";
45-
46-
// Format date as M/D
47-
function formatDate(date) {{
48-
if (!date) return null;
49-
return (date.getMonth() + 1) + "/" + date.getDate();
50-
}}
51-
52-
// Format duration
53-
function formatDuration(minutes) {{
54-
if (!minutes) return null;
55-
if (minutes >= 60) {{
56-
return Math.floor(minutes / 60) + "h";
57-
}}
58-
return minutes + "m";
59-
}}
60-
61-
// Track seen recurring task names to filter duplicates
62-
const seenRecurring = new Set();
63-
64-
// Build output
65-
let output = [];
66-
67-
// Legend
68-
output.push("Legend: F:Folder P:Project \\u2022:Task \\uD83D\\uDEA9:Flagged [M/D]:Date <tag>:Tags");
69-
output.push("Status: #next #avail #block #due #over #compl #drop");
70-
output.push("");
71-
72-
// Process folders
73-
flattenedFolders.forEach(folder => {{
74-
output.push("F: " + folder.name);
75-
76-
// Get projects in this folder
77-
folder.projects.forEach(project => {{
78-
if (hideCompleted && (project.status === Project.Status.Done ||
79-
project.status === Project.Status.Dropped)) {{
80-
return;
81-
}}
82-
83-
let projectLine = " P: " + project.name;
84-
if (project.status !== Project.Status.Active) {{
85-
projectLine += " #" + projectStatusMap[project.status];
86-
}}
87-
if (project.dueDate) {{
88-
projectLine += " [" + formatDate(project.dueDate) + "]";
89-
}}
90-
output.push(projectLine);
91-
92-
// Get tasks in this project
93-
project.flattenedTasks.forEach(task => {{
94-
if (hideCompleted && (task.taskStatus === Task.Status.Completed ||
95-
task.taskStatus === Task.Status.Dropped)) {{
96-
return;
97-
}}
98-
99-
// Filter recurring duplicates
100-
if (hideRecurringDuplicates && task.repetitionRule) {{
101-
const key = task.name + "|" + (task.containingProject ? task.containingProject.name : "");
102-
if (seenRecurring.has(key)) {{
103-
return;
104-
}}
105-
seenRecurring.add(key);
106-
}}
107-
108-
let taskLine = " \\u2022 " + task.name;
109-
110-
// Add status
111-
const status = taskStatusMap[task.taskStatus];
112-
if (status && status !== "avail") {{
113-
taskLine += " #" + status;
114-
}}
115-
116-
// Add flag
117-
if (task.flagged) {{
118-
taskLine += " \\uD83D\\uDEA9";
119-
}}
120-
121-
// Add due date
122-
if (task.dueDate) {{
123-
taskLine += " [" + formatDate(task.dueDate) + "]";
124-
}}
125-
126-
// Add duration
127-
if (task.estimatedMinutes) {{
128-
taskLine += " " + formatDuration(task.estimatedMinutes);
129-
}}
130-
131-
// Add tags
132-
if (task.tags && task.tags.length > 0) {{
133-
taskLine += " <" + task.tags.map(t => t.name).join(",") + ">";
134-
}}
135-
136-
output.push(taskLine);
137-
}});
138-
}});
139-
140-
output.push("");
141-
}});
142-
143-
// Also show projects not in any folder
144-
flattenedProjects.forEach(project => {{
145-
if (project.folder) return; // Already shown
146-
147-
if (hideCompleted && (project.status === Project.Status.Done ||
148-
project.status === Project.Status.Dropped)) {{
149-
return;
150-
}}
151-
152-
let projectLine = "P: " + project.name;
153-
if (project.status !== Project.Status.Active) {{
154-
projectLine += " #" + projectStatusMap[project.status];
155-
}}
156-
if (project.dueDate) {{
157-
projectLine += " [" + formatDate(project.dueDate) + "]";
158-
}}
159-
output.push(projectLine);
160-
161-
// Get tasks in this project
162-
project.flattenedTasks.forEach(task => {{
163-
if (hideCompleted && (task.taskStatus === Task.Status.Completed ||
164-
task.taskStatus === Task.Status.Dropped)) {{
165-
return;
166-
}}
167-
168-
if (hideRecurringDuplicates && task.repetitionRule) {{
169-
const key = task.name + "|" + project.name;
170-
if (seenRecurring.has(key)) {{
171-
return;
172-
}}
173-
seenRecurring.add(key);
174-
}}
175-
176-
let taskLine = " \\u2022 " + task.name;
177-
178-
const status = taskStatusMap[task.taskStatus];
179-
if (status && status !== "avail") {{
180-
taskLine += " #" + status;
181-
}}
182-
183-
if (task.flagged) {{
184-
taskLine += " \\uD83D\\uDEA9";
185-
}}
186-
187-
if (task.dueDate) {{
188-
taskLine += " [" + formatDate(task.dueDate) + "]";
189-
}}
190-
191-
if (task.estimatedMinutes) {{
192-
taskLine += " " + formatDuration(task.estimatedMinutes);
193-
}}
194-
195-
if (task.tags && task.tags.length > 0) {{
196-
taskLine += " <" + task.tags.map(t => t.name).join(",") + ">";
197-
}}
198-
199-
output.push(taskLine);
200-
}});
201-
202-
output.push("");
203-
}});
204-
205-
// Show inbox tasks
206-
if (inbox.length > 0) {{
207-
output.push("INBOX:");
208-
inbox.forEach(task => {{
209-
if (hideCompleted && (task.taskStatus === Task.Status.Completed ||
210-
task.taskStatus === Task.Status.Dropped)) {{
211-
return;
212-
}}
213-
214-
let taskLine = " \\u2022 " + task.name;
215-
216-
const status = taskStatusMap[task.taskStatus];
217-
if (status && status !== "avail") {{
218-
taskLine += " #" + status;
219-
}}
220-
221-
if (task.flagged) {{
222-
taskLine += " \\uD83D\\uDEA9";
223-
}}
224-
225-
if (task.dueDate) {{
226-
taskLine += " [" + formatDate(task.dueDate) + "]";
227-
}}
228-
229-
if (task.estimatedMinutes) {{
230-
taskLine += " " + formatDuration(task.estimatedMinutes);
231-
}}
232-
233-
if (task.tags && task.tags.length > 0) {{
234-
taskLine += " <" + task.tags.map(t => t.name).join(",") + ">";
235-
}}
236-
237-
output.push(taskLine);
238-
}});
239-
}}
240-
241-
return output.join("\\n");
242-
243-
}} catch (error) {{
244-
return "Error dumping database: " + error.toString();
245-
}}
246-
}})();
247-
'''
248-
24925
try:
250-
result = await execute_omnijs(script)
26+
result = await execute_omnijs_with_params(
27+
"dump_database",
28+
{
29+
"hide_completed": hide_completed,
30+
"hide_recurring_duplicates": hide_recurring_duplicates,
31+
},
32+
)
25133
# The result might be wrapped in quotes from JSON, handle both cases
25234
if isinstance(result, dict):
25335
if "error" in result:
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"""Perspectives tools for OmniFocus."""
22

3-
from .list_perspectives import list_perspectives
43
from .get_perspective_view import get_perspective_view
4+
from .list_perspectives import list_perspectives
55

66
__all__ = ["list_perspectives", "get_perspective_view"]

0 commit comments

Comments
 (0)