@scardis/omnifocus-mcp 0.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.
Files changed (387) hide show
  1. package/.claude/commands/opsx/apply.md +152 -0
  2. package/.claude/commands/opsx/archive.md +157 -0
  3. package/.claude/commands/opsx/bulk-archive.md +242 -0
  4. package/.claude/commands/opsx/continue.md +114 -0
  5. package/.claude/commands/opsx/explore.md +173 -0
  6. package/.claude/commands/opsx/ff.md +97 -0
  7. package/.claude/commands/opsx/new.md +69 -0
  8. package/.claude/commands/opsx/onboard.md +550 -0
  9. package/.claude/commands/opsx/propose.md +106 -0
  10. package/.claude/commands/opsx/sync.md +134 -0
  11. package/.claude/commands/opsx/verify.md +164 -0
  12. package/.claude/skills/openspec-apply-change/SKILL.md +156 -0
  13. package/.claude/skills/openspec-archive-change/SKILL.md +114 -0
  14. package/.claude/skills/openspec-bulk-archive-change/SKILL.md +246 -0
  15. package/.claude/skills/openspec-continue-change/SKILL.md +118 -0
  16. package/.claude/skills/openspec-explore/SKILL.md +288 -0
  17. package/.claude/skills/openspec-ff-change/SKILL.md +101 -0
  18. package/.claude/skills/openspec-new-change/SKILL.md +74 -0
  19. package/.claude/skills/openspec-onboard/SKILL.md +554 -0
  20. package/.claude/skills/openspec-propose/SKILL.md +110 -0
  21. package/.claude/skills/openspec-sync-specs/SKILL.md +138 -0
  22. package/.claude/skills/openspec-verify-change/SKILL.md +168 -0
  23. package/CONTRIBUTING.md +83 -0
  24. package/LICENSE +21 -0
  25. package/README.md +198 -0
  26. package/dist/runtime/bridge.d.ts +16 -0
  27. package/dist/runtime/bridge.d.ts.map +1 -0
  28. package/dist/runtime/bridge.js +76 -0
  29. package/dist/runtime/bridge.js.map +1 -0
  30. package/dist/runtime/index.d.ts +5 -0
  31. package/dist/runtime/index.d.ts.map +1 -0
  32. package/dist/runtime/index.js +5 -0
  33. package/dist/runtime/index.js.map +1 -0
  34. package/dist/runtime/jxaShim.d.ts +21 -0
  35. package/dist/runtime/jxaShim.d.ts.map +1 -0
  36. package/dist/runtime/jxaShim.js +55 -0
  37. package/dist/runtime/jxaShim.js.map +1 -0
  38. package/dist/runtime/resultProtocol.d.ts +66 -0
  39. package/dist/runtime/resultProtocol.d.ts.map +1 -0
  40. package/dist/runtime/resultProtocol.js +52 -0
  41. package/dist/runtime/resultProtocol.js.map +1 -0
  42. package/dist/runtime/snippetLoader.d.ts +4 -0
  43. package/dist/runtime/snippetLoader.d.ts.map +1 -0
  44. package/dist/runtime/snippetLoader.js +68 -0
  45. package/dist/runtime/snippetLoader.js.map +1 -0
  46. package/dist/schemas/enums.d.ts +9 -0
  47. package/dist/schemas/enums.d.ts.map +1 -0
  48. package/dist/schemas/enums.js +26 -0
  49. package/dist/schemas/enums.js.map +1 -0
  50. package/dist/schemas/index.d.ts +3 -0
  51. package/dist/schemas/index.d.ts.map +1 -0
  52. package/dist/schemas/index.js +3 -0
  53. package/dist/schemas/index.js.map +1 -0
  54. package/dist/schemas/shapes.d.ts +726 -0
  55. package/dist/schemas/shapes.d.ts.map +1 -0
  56. package/dist/schemas/shapes.js +221 -0
  57. package/dist/schemas/shapes.js.map +1 -0
  58. package/dist/server.d.ts +2 -0
  59. package/dist/server.d.ts.map +1 -0
  60. package/dist/server.js +50 -0
  61. package/dist/server.js.map +1 -0
  62. package/dist/tools/completeProject.d.ts +24 -0
  63. package/dist/tools/completeProject.d.ts.map +1 -0
  64. package/dist/tools/completeProject.js +17 -0
  65. package/dist/tools/completeProject.js.map +1 -0
  66. package/dist/tools/completeTask.d.ts +25 -0
  67. package/dist/tools/completeTask.d.ts.map +1 -0
  68. package/dist/tools/completeTask.js +17 -0
  69. package/dist/tools/completeTask.js.map +1 -0
  70. package/dist/tools/createFolder.d.ts +20 -0
  71. package/dist/tools/createFolder.d.ts.map +1 -0
  72. package/dist/tools/createFolder.js +13 -0
  73. package/dist/tools/createFolder.js.map +1 -0
  74. package/dist/tools/createProject.d.ts +59 -0
  75. package/dist/tools/createProject.d.ts.map +1 -0
  76. package/dist/tools/createProject.js +13 -0
  77. package/dist/tools/createProject.js.map +1 -0
  78. package/dist/tools/createTag.d.ts +20 -0
  79. package/dist/tools/createTag.d.ts.map +1 -0
  80. package/dist/tools/createTag.js +13 -0
  81. package/dist/tools/createTag.js.map +1 -0
  82. package/dist/tools/createTask.d.ts +116 -0
  83. package/dist/tools/createTask.d.ts.map +1 -0
  84. package/dist/tools/createTask.js +13 -0
  85. package/dist/tools/createTask.js.map +1 -0
  86. package/dist/tools/deleteFolder.d.ts +30 -0
  87. package/dist/tools/deleteFolder.d.ts.map +1 -0
  88. package/dist/tools/deleteFolder.js +18 -0
  89. package/dist/tools/deleteFolder.js.map +1 -0
  90. package/dist/tools/deleteProject.d.ts +30 -0
  91. package/dist/tools/deleteProject.d.ts.map +1 -0
  92. package/dist/tools/deleteProject.js +18 -0
  93. package/dist/tools/deleteProject.js.map +1 -0
  94. package/dist/tools/deleteTag.d.ts +30 -0
  95. package/dist/tools/deleteTag.d.ts.map +1 -0
  96. package/dist/tools/deleteTag.js +18 -0
  97. package/dist/tools/deleteTag.js.map +1 -0
  98. package/dist/tools/deleteTask.d.ts +31 -0
  99. package/dist/tools/deleteTask.d.ts.map +1 -0
  100. package/dist/tools/deleteTask.js +18 -0
  101. package/dist/tools/deleteTask.js.map +1 -0
  102. package/dist/tools/dropProject.d.ts +24 -0
  103. package/dist/tools/dropProject.d.ts.map +1 -0
  104. package/dist/tools/dropProject.js +17 -0
  105. package/dist/tools/dropProject.js.map +1 -0
  106. package/dist/tools/dropTask.d.ts +25 -0
  107. package/dist/tools/dropTask.d.ts.map +1 -0
  108. package/dist/tools/dropTask.js +17 -0
  109. package/dist/tools/dropTask.js.map +1 -0
  110. package/dist/tools/editFolder.d.ts +20 -0
  111. package/dist/tools/editFolder.d.ts.map +1 -0
  112. package/dist/tools/editFolder.js +13 -0
  113. package/dist/tools/editFolder.js.map +1 -0
  114. package/dist/tools/editProject.d.ts +59 -0
  115. package/dist/tools/editProject.d.ts.map +1 -0
  116. package/dist/tools/editProject.js +13 -0
  117. package/dist/tools/editProject.js.map +1 -0
  118. package/dist/tools/editTag.d.ts +31 -0
  119. package/dist/tools/editTag.d.ts.map +1 -0
  120. package/dist/tools/editTag.js +13 -0
  121. package/dist/tools/editTag.js.map +1 -0
  122. package/dist/tools/editTask.d.ts +79 -0
  123. package/dist/tools/editTask.d.ts.map +1 -0
  124. package/dist/tools/editTask.js +13 -0
  125. package/dist/tools/editTask.js.map +1 -0
  126. package/dist/tools/getFolder.d.ts +24 -0
  127. package/dist/tools/getFolder.d.ts.map +1 -0
  128. package/dist/tools/getFolder.js +17 -0
  129. package/dist/tools/getFolder.js.map +1 -0
  130. package/dist/tools/getProject.d.ts +24 -0
  131. package/dist/tools/getProject.d.ts.map +1 -0
  132. package/dist/tools/getProject.js +17 -0
  133. package/dist/tools/getProject.js.map +1 -0
  134. package/dist/tools/getTag.d.ts +24 -0
  135. package/dist/tools/getTag.d.ts.map +1 -0
  136. package/dist/tools/getTag.js +17 -0
  137. package/dist/tools/getTag.js.map +1 -0
  138. package/dist/tools/getTask.d.ts +24 -0
  139. package/dist/tools/getTask.d.ts.map +1 -0
  140. package/dist/tools/getTask.js +17 -0
  141. package/dist/tools/getTask.js.map +1 -0
  142. package/dist/tools/index.d.ts +732 -0
  143. package/dist/tools/index.d.ts.map +1 -0
  144. package/dist/tools/index.js +84 -0
  145. package/dist/tools/index.js.map +1 -0
  146. package/dist/tools/listFolders.d.ts +50 -0
  147. package/dist/tools/listFolders.d.ts.map +1 -0
  148. package/dist/tools/listFolders.js +21 -0
  149. package/dist/tools/listFolders.js.map +1 -0
  150. package/dist/tools/listProjects.d.ts +70 -0
  151. package/dist/tools/listProjects.d.ts.map +1 -0
  152. package/dist/tools/listProjects.js +21 -0
  153. package/dist/tools/listProjects.js.map +1 -0
  154. package/dist/tools/listTags.d.ts +50 -0
  155. package/dist/tools/listTags.d.ts.map +1 -0
  156. package/dist/tools/listTags.js +21 -0
  157. package/dist/tools/listTags.js.map +1 -0
  158. package/dist/tools/listTasks.d.ts +156 -0
  159. package/dist/tools/listTasks.d.ts.map +1 -0
  160. package/dist/tools/listTasks.js +36 -0
  161. package/dist/tools/listTasks.js.map +1 -0
  162. package/dist/tools/moveProject.d.ts +20 -0
  163. package/dist/tools/moveProject.d.ts.map +1 -0
  164. package/dist/tools/moveProject.js +13 -0
  165. package/dist/tools/moveProject.js.map +1 -0
  166. package/dist/tools/moveTask.d.ts +31 -0
  167. package/dist/tools/moveTask.d.ts.map +1 -0
  168. package/dist/tools/moveTask.js +13 -0
  169. package/dist/tools/moveTask.js.map +1 -0
  170. package/dist/tools/resolveName.d.ts +36 -0
  171. package/dist/tools/resolveName.d.ts.map +1 -0
  172. package/dist/tools/resolveName.js +26 -0
  173. package/dist/tools/resolveName.js.map +1 -0
  174. package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/.openspec.yaml +2 -0
  175. package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/design.md +162 -0
  176. package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/proposal.md +49 -0
  177. package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/specs/attachments/spec.md +9 -0
  178. package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/specs/batch-operations/spec.md +9 -0
  179. package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/specs/database-inspection/spec.md +9 -0
  180. package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/specs/execution-runtime/spec.md +69 -0
  181. package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/specs/folder-management/spec.md +25 -0
  182. package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/specs/forecast/spec.md +9 -0
  183. package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/specs/identity-resolution/spec.md +45 -0
  184. package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/specs/perspective-management/spec.md +9 -0
  185. package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/specs/project-management/spec.md +25 -0
  186. package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/specs/recurrence/spec.md +9 -0
  187. package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/specs/settings/spec.md +9 -0
  188. package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/specs/tag-management/spec.md +25 -0
  189. package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/specs/task-management/spec.md +29 -0
  190. package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/specs/url-automation/spec.md +9 -0
  191. package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/specs/window-state/spec.md +9 -0
  192. package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/tasks.md +84 -0
  193. package/openspec/changes/archive/2026-04-09-folder-crud/.openspec.yaml +2 -0
  194. package/openspec/changes/archive/2026-04-09-folder-crud/design.md +58 -0
  195. package/openspec/changes/archive/2026-04-09-folder-crud/proposal.md +28 -0
  196. package/openspec/changes/archive/2026-04-09-folder-crud/specs/folder-write/spec.md +45 -0
  197. package/openspec/changes/archive/2026-04-09-folder-crud/tasks.md +41 -0
  198. package/openspec/changes/archive/2026-04-09-folder-tag-list-filtering/.openspec.yaml +2 -0
  199. package/openspec/changes/archive/2026-04-09-folder-tag-list-filtering/design.md +38 -0
  200. package/openspec/changes/archive/2026-04-09-folder-tag-list-filtering/proposal.md +30 -0
  201. package/openspec/changes/archive/2026-04-09-folder-tag-list-filtering/specs/folder-management/spec.md +21 -0
  202. package/openspec/changes/archive/2026-04-09-folder-tag-list-filtering/specs/tag-management/spec.md +21 -0
  203. package/openspec/changes/archive/2026-04-09-folder-tag-list-filtering/tasks.md +35 -0
  204. package/openspec/changes/archive/2026-04-09-move-operations/.openspec.yaml +2 -0
  205. package/openspec/changes/archive/2026-04-09-move-operations/design.md +43 -0
  206. package/openspec/changes/archive/2026-04-09-move-operations/proposal.md +25 -0
  207. package/openspec/changes/archive/2026-04-09-move-operations/specs/move-operations/spec.md +41 -0
  208. package/openspec/changes/archive/2026-04-09-move-operations/tasks.md +40 -0
  209. package/openspec/changes/archive/2026-04-09-project-crud/.openspec.yaml +2 -0
  210. package/openspec/changes/archive/2026-04-09-project-crud/design.md +60 -0
  211. package/openspec/changes/archive/2026-04-09-project-crud/proposal.md +29 -0
  212. package/openspec/changes/archive/2026-04-09-project-crud/specs/project-write/spec.md +74 -0
  213. package/openspec/changes/archive/2026-04-09-project-crud/tasks.md +48 -0
  214. package/openspec/changes/archive/2026-04-09-project-filtering/.openspec.yaml +2 -0
  215. package/openspec/changes/archive/2026-04-09-project-filtering/design.md +52 -0
  216. package/openspec/changes/archive/2026-04-09-project-filtering/proposal.md +26 -0
  217. package/openspec/changes/archive/2026-04-09-project-filtering/specs/project-filtering/spec.md +66 -0
  218. package/openspec/changes/archive/2026-04-09-project-filtering/specs/project-management/spec.md +13 -0
  219. package/openspec/changes/archive/2026-04-09-project-filtering/tasks.md +41 -0
  220. package/openspec/changes/archive/2026-04-09-tag-crud/.openspec.yaml +2 -0
  221. package/openspec/changes/archive/2026-04-09-tag-crud/design.md +45 -0
  222. package/openspec/changes/archive/2026-04-09-tag-crud/proposal.md +28 -0
  223. package/openspec/changes/archive/2026-04-09-tag-crud/specs/tag-write/spec.md +49 -0
  224. package/openspec/changes/archive/2026-04-09-tag-crud/tasks.md +41 -0
  225. package/openspec/changes/archive/2026-04-09-task-crud/.openspec.yaml +2 -0
  226. package/openspec/changes/archive/2026-04-09-task-crud/design.md +62 -0
  227. package/openspec/changes/archive/2026-04-09-task-crud/proposal.md +29 -0
  228. package/openspec/changes/archive/2026-04-09-task-crud/specs/task-management/spec.md +17 -0
  229. package/openspec/changes/archive/2026-04-09-task-crud/specs/task-write/spec.md +89 -0
  230. package/openspec/changes/archive/2026-04-09-task-crud/tasks.md +55 -0
  231. package/openspec/changes/archive/2026-04-09-task-filtering/.openspec.yaml +2 -0
  232. package/openspec/changes/archive/2026-04-09-task-filtering/design.md +61 -0
  233. package/openspec/changes/archive/2026-04-09-task-filtering/proposal.md +26 -0
  234. package/openspec/changes/archive/2026-04-09-task-filtering/specs/task-filtering/spec.md +63 -0
  235. package/openspec/changes/archive/2026-04-09-task-filtering/specs/task-management/spec.md +17 -0
  236. package/openspec/changes/archive/2026-04-09-task-filtering/tasks.md +42 -0
  237. package/openspec/changes/archive/2026-04-10-planned-date/.openspec.yaml +2 -0
  238. package/openspec/changes/archive/2026-04-10-planned-date/design.md +27 -0
  239. package/openspec/changes/archive/2026-04-10-planned-date/proposal.md +29 -0
  240. package/openspec/changes/archive/2026-04-10-planned-date/specs/task-management/spec.md +29 -0
  241. package/openspec/changes/archive/2026-04-10-planned-date/specs/task-write/spec.md +69 -0
  242. package/openspec/changes/archive/2026-04-10-planned-date/tasks.md +26 -0
  243. package/openspec/changes/archive/2026-04-10-task-recurrence/.openspec.yaml +2 -0
  244. package/openspec/changes/archive/2026-04-10-task-recurrence/design.md +81 -0
  245. package/openspec/changes/archive/2026-04-10-task-recurrence/proposal.md +28 -0
  246. package/openspec/changes/archive/2026-04-10-task-recurrence/specs/recurrence/spec.md +47 -0
  247. package/openspec/changes/archive/2026-04-10-task-recurrence/specs/task-management/spec.md +25 -0
  248. package/openspec/changes/archive/2026-04-10-task-recurrence/specs/task-write/spec.md +61 -0
  249. package/openspec/changes/archive/2026-04-10-task-recurrence/tasks.md +39 -0
  250. package/openspec/config.yaml +20 -0
  251. package/openspec/specs/attachments/spec.md +15 -0
  252. package/openspec/specs/batch-operations/spec.md +15 -0
  253. package/openspec/specs/database-inspection/spec.md +15 -0
  254. package/openspec/specs/execution-runtime/spec.md +75 -0
  255. package/openspec/specs/folder-management/spec.md +39 -0
  256. package/openspec/specs/folder-write/spec.md +45 -0
  257. package/openspec/specs/forecast/spec.md +15 -0
  258. package/openspec/specs/identity-resolution/spec.md +51 -0
  259. package/openspec/specs/move-operations/spec.md +41 -0
  260. package/openspec/specs/perspective-management/spec.md +15 -0
  261. package/openspec/specs/project-filtering/spec.md +72 -0
  262. package/openspec/specs/project-management/spec.md +31 -0
  263. package/openspec/specs/project-write/spec.md +79 -0
  264. package/openspec/specs/recurrence/spec.md +51 -0
  265. package/openspec/specs/settings/spec.md +15 -0
  266. package/openspec/specs/tag-management/spec.md +39 -0
  267. package/openspec/specs/tag-write/spec.md +49 -0
  268. package/openspec/specs/task-filtering/spec.md +63 -0
  269. package/openspec/specs/task-management/spec.md +51 -0
  270. package/openspec/specs/task-write/spec.md +115 -0
  271. package/openspec/specs/url-automation/spec.md +15 -0
  272. package/openspec/specs/window-state/spec.md +15 -0
  273. package/package.json +32 -0
  274. package/scripts/cleanup-fixtures.ts +89 -0
  275. package/server.json +21 -0
  276. package/src/runtime/bridge.ts +97 -0
  277. package/src/runtime/index.ts +4 -0
  278. package/src/runtime/jxaShim.ts +55 -0
  279. package/src/runtime/resultProtocol.ts +62 -0
  280. package/src/runtime/snippetLoader.ts +79 -0
  281. package/src/schemas/enums.ts +32 -0
  282. package/src/schemas/index.ts +38 -0
  283. package/src/schemas/shapes.ts +267 -0
  284. package/src/server.ts +58 -0
  285. package/src/snippets/complete_project.js +73 -0
  286. package/src/snippets/complete_task.js +85 -0
  287. package/src/snippets/create_folder.js +52 -0
  288. package/src/snippets/create_project.js +107 -0
  289. package/src/snippets/create_tag.js +55 -0
  290. package/src/snippets/create_task.js +159 -0
  291. package/src/snippets/delete_folder.js +26 -0
  292. package/src/snippets/delete_project.js +20 -0
  293. package/src/snippets/delete_tag.js +20 -0
  294. package/src/snippets/delete_task.js +20 -0
  295. package/src/snippets/drop_project.js +73 -0
  296. package/src/snippets/drop_task.js +85 -0
  297. package/src/snippets/edit_folder.js +46 -0
  298. package/src/snippets/edit_project.js +106 -0
  299. package/src/snippets/edit_tag.js +56 -0
  300. package/src/snippets/edit_task.js +146 -0
  301. package/src/snippets/get_folder.js +48 -0
  302. package/src/snippets/get_project.js +77 -0
  303. package/src/snippets/get_tag.js +51 -0
  304. package/src/snippets/get_task.js +96 -0
  305. package/src/snippets/list_folders.js +50 -0
  306. package/src/snippets/list_projects.js +98 -0
  307. package/src/snippets/list_tags.js +54 -0
  308. package/src/snippets/list_tasks.js +127 -0
  309. package/src/snippets/move_project.js +79 -0
  310. package/src/snippets/move_task.js +113 -0
  311. package/src/snippets/resolve_name.js +83 -0
  312. package/src/tools/completeProject.ts +21 -0
  313. package/src/tools/completeTask.ts +23 -0
  314. package/src/tools/createFolder.ts +20 -0
  315. package/src/tools/createProject.ts +20 -0
  316. package/src/tools/createTag.ts +20 -0
  317. package/src/tools/createTask.ts +20 -0
  318. package/src/tools/deleteFolder.ts +24 -0
  319. package/src/tools/deleteProject.ts +24 -0
  320. package/src/tools/deleteTag.ts +24 -0
  321. package/src/tools/deleteTask.ts +26 -0
  322. package/src/tools/dropProject.ts +21 -0
  323. package/src/tools/dropTask.ts +23 -0
  324. package/src/tools/editFolder.ts +19 -0
  325. package/src/tools/editProject.ts +20 -0
  326. package/src/tools/editTag.ts +20 -0
  327. package/src/tools/editTask.ts +20 -0
  328. package/src/tools/getFolder.ts +24 -0
  329. package/src/tools/getProject.ts +24 -0
  330. package/src/tools/getTag.ts +24 -0
  331. package/src/tools/getTask.ts +24 -0
  332. package/src/tools/index.ts +85 -0
  333. package/src/tools/listFolders.ts +32 -0
  334. package/src/tools/listProjects.ts +32 -0
  335. package/src/tools/listTags.ts +32 -0
  336. package/src/tools/listTasks.ts +56 -0
  337. package/src/tools/moveProject.ts +20 -0
  338. package/src/tools/moveTask.ts +20 -0
  339. package/src/tools/resolveName.ts +37 -0
  340. package/test/integration/.gitkeep +0 -0
  341. package/test/integration/completeProject.int.test.ts +25 -0
  342. package/test/integration/completeTask.int.test.ts +30 -0
  343. package/test/integration/createFolder.int.test.ts +50 -0
  344. package/test/integration/createProject.int.test.ts +49 -0
  345. package/test/integration/createTag.int.test.ts +52 -0
  346. package/test/integration/createTask.int.test.ts +55 -0
  347. package/test/integration/deleteFolder.int.test.ts +64 -0
  348. package/test/integration/deleteProject.int.test.ts +31 -0
  349. package/test/integration/deleteTag.int.test.ts +61 -0
  350. package/test/integration/deleteTask.int.test.ts +36 -0
  351. package/test/integration/dropProject.int.test.ts +24 -0
  352. package/test/integration/dropTask.int.test.ts +29 -0
  353. package/test/integration/editFolder.int.test.ts +43 -0
  354. package/test/integration/editProject.int.test.ts +39 -0
  355. package/test/integration/editTag.int.test.ts +43 -0
  356. package/test/integration/editTask.int.test.ts +56 -0
  357. package/test/integration/fixtures.ts +219 -0
  358. package/test/integration/getTask.int.test.ts +64 -0
  359. package/test/integration/listFoldersFiltered.int.test.ts +98 -0
  360. package/test/integration/listProjects.int.test.ts +73 -0
  361. package/test/integration/listProjectsFiltered.int.test.ts +96 -0
  362. package/test/integration/listTagsFiltered.int.test.ts +54 -0
  363. package/test/integration/listTasksFiltered.int.test.ts +141 -0
  364. package/test/integration/moveProject.int.test.ts +57 -0
  365. package/test/integration/moveTask.int.test.ts +61 -0
  366. package/test/integration/plannedDate.int.test.ts +72 -0
  367. package/test/integration/preflight.ts +60 -0
  368. package/test/integration/resolveName.int.test.ts +86 -0
  369. package/test/integration/taskRecurrence.int.test.ts +106 -0
  370. package/test/unit/.gitkeep +0 -0
  371. package/test/unit/bridge.injection.test.ts +66 -0
  372. package/test/unit/resultProtocol.test.ts +71 -0
  373. package/test/unit/schemas.createFolder.test.ts +38 -0
  374. package/test/unit/schemas.createProject.test.ts +115 -0
  375. package/test/unit/schemas.createTask.test.ts +87 -0
  376. package/test/unit/schemas.editTag.test.ts +64 -0
  377. package/test/unit/schemas.folderTagFiltering.test.ts +42 -0
  378. package/test/unit/schemas.listProjects.test.ts +44 -0
  379. package/test/unit/schemas.moveOperations.test.ts +60 -0
  380. package/test/unit/schemas.recurrence.test.ts +120 -0
  381. package/test/unit/schemas.test.ts +126 -0
  382. package/test/unit/snippetLoader.test.ts +56 -0
  383. package/test/unit/tools.deleteTask.test.ts +19 -0
  384. package/test/unit/tools.listTasks.test.ts +126 -0
  385. package/tsconfig.json +19 -0
  386. package/vitest.config.ts +8 -0
  387. package/vitest.integration.config.ts +18 -0
@@ -0,0 +1,138 @@
1
+ ---
2
+ name: openspec-sync-specs
3
+ description: Sync delta specs from a change to main specs. Use when the user wants to update main specs with changes from a delta spec, without archiving the change.
4
+ license: MIT
5
+ compatibility: Requires openspec CLI.
6
+ metadata:
7
+ author: openspec
8
+ version: "1.0"
9
+ generatedBy: "1.2.0"
10
+ ---
11
+
12
+ Sync delta specs from a change to main specs.
13
+
14
+ This is an **agent-driven** operation - you will read delta specs and directly edit main specs to apply the changes. This allows intelligent merging (e.g., adding a scenario without copying the entire requirement).
15
+
16
+ **Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.
17
+
18
+ **Steps**
19
+
20
+ 1. **If no change name provided, prompt for selection**
21
+
22
+ Run `openspec list --json` to get available changes. Use the **AskUserQuestion tool** to let the user select.
23
+
24
+ Show changes that have delta specs (under `specs/` directory).
25
+
26
+ **IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.
27
+
28
+ 2. **Find delta specs**
29
+
30
+ Look for delta spec files in `openspec/changes/<name>/specs/*/spec.md`.
31
+
32
+ Each delta spec file contains sections like:
33
+ - `## ADDED Requirements` - New requirements to add
34
+ - `## MODIFIED Requirements` - Changes to existing requirements
35
+ - `## REMOVED Requirements` - Requirements to remove
36
+ - `## RENAMED Requirements` - Requirements to rename (FROM:/TO: format)
37
+
38
+ If no delta specs found, inform user and stop.
39
+
40
+ 3. **For each delta spec, apply changes to main specs**
41
+
42
+ For each capability with a delta spec at `openspec/changes/<name>/specs/<capability>/spec.md`:
43
+
44
+ a. **Read the delta spec** to understand the intended changes
45
+
46
+ b. **Read the main spec** at `openspec/specs/<capability>/spec.md` (may not exist yet)
47
+
48
+ c. **Apply changes intelligently**:
49
+
50
+ **ADDED Requirements:**
51
+ - If requirement doesn't exist in main spec → add it
52
+ - If requirement already exists → update it to match (treat as implicit MODIFIED)
53
+
54
+ **MODIFIED Requirements:**
55
+ - Find the requirement in main spec
56
+ - Apply the changes - this can be:
57
+ - Adding new scenarios (don't need to copy existing ones)
58
+ - Modifying existing scenarios
59
+ - Changing the requirement description
60
+ - Preserve scenarios/content not mentioned in the delta
61
+
62
+ **REMOVED Requirements:**
63
+ - Remove the entire requirement block from main spec
64
+
65
+ **RENAMED Requirements:**
66
+ - Find the FROM requirement, rename to TO
67
+
68
+ d. **Create new main spec** if capability doesn't exist yet:
69
+ - Create `openspec/specs/<capability>/spec.md`
70
+ - Add Purpose section (can be brief, mark as TBD)
71
+ - Add Requirements section with the ADDED requirements
72
+
73
+ 4. **Show summary**
74
+
75
+ After applying all changes, summarize:
76
+ - Which capabilities were updated
77
+ - What changes were made (requirements added/modified/removed/renamed)
78
+
79
+ **Delta Spec Format Reference**
80
+
81
+ ```markdown
82
+ ## ADDED Requirements
83
+
84
+ ### Requirement: New Feature
85
+ The system SHALL do something new.
86
+
87
+ #### Scenario: Basic case
88
+ - **WHEN** user does X
89
+ - **THEN** system does Y
90
+
91
+ ## MODIFIED Requirements
92
+
93
+ ### Requirement: Existing Feature
94
+ #### Scenario: New scenario to add
95
+ - **WHEN** user does A
96
+ - **THEN** system does B
97
+
98
+ ## REMOVED Requirements
99
+
100
+ ### Requirement: Deprecated Feature
101
+
102
+ ## RENAMED Requirements
103
+
104
+ - FROM: `### Requirement: Old Name`
105
+ - TO: `### Requirement: New Name`
106
+ ```
107
+
108
+ **Key Principle: Intelligent Merging**
109
+
110
+ Unlike programmatic merging, you can apply **partial updates**:
111
+ - To add a scenario, just include that scenario under MODIFIED - don't copy existing scenarios
112
+ - The delta represents *intent*, not a wholesale replacement
113
+ - Use your judgment to merge changes sensibly
114
+
115
+ **Output On Success**
116
+
117
+ ```
118
+ ## Specs Synced: <change-name>
119
+
120
+ Updated main specs:
121
+
122
+ **<capability-1>**:
123
+ - Added requirement: "New Feature"
124
+ - Modified requirement: "Existing Feature" (added 1 scenario)
125
+
126
+ **<capability-2>**:
127
+ - Created new spec file
128
+ - Added requirement: "Another Feature"
129
+
130
+ Main specs are now updated. The change remains active - archive when implementation is complete.
131
+ ```
132
+
133
+ **Guardrails**
134
+ - Read both delta and main specs before making changes
135
+ - Preserve existing content not mentioned in delta
136
+ - If something is unclear, ask for clarification
137
+ - Show what you're changing as you go
138
+ - The operation should be idempotent - running twice should give same result
@@ -0,0 +1,168 @@
1
+ ---
2
+ name: openspec-verify-change
3
+ description: Verify implementation matches change artifacts. Use when the user wants to validate that implementation is complete, correct, and coherent before archiving.
4
+ license: MIT
5
+ compatibility: Requires openspec CLI.
6
+ metadata:
7
+ author: openspec
8
+ version: "1.0"
9
+ generatedBy: "1.2.0"
10
+ ---
11
+
12
+ Verify that an implementation matches the change artifacts (specs, tasks, design).
13
+
14
+ **Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.
15
+
16
+ **Steps**
17
+
18
+ 1. **If no change name provided, prompt for selection**
19
+
20
+ Run `openspec list --json` to get available changes. Use the **AskUserQuestion tool** to let the user select.
21
+
22
+ Show changes that have implementation tasks (tasks artifact exists).
23
+ Include the schema used for each change if available.
24
+ Mark changes with incomplete tasks as "(In Progress)".
25
+
26
+ **IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.
27
+
28
+ 2. **Check status to understand the schema**
29
+ ```bash
30
+ openspec status --change "<name>" --json
31
+ ```
32
+ Parse the JSON to understand:
33
+ - `schemaName`: The workflow being used (e.g., "spec-driven")
34
+ - Which artifacts exist for this change
35
+
36
+ 3. **Get the change directory and load artifacts**
37
+
38
+ ```bash
39
+ openspec instructions apply --change "<name>" --json
40
+ ```
41
+
42
+ This returns the change directory and context files. Read all available artifacts from `contextFiles`.
43
+
44
+ 4. **Initialize verification report structure**
45
+
46
+ Create a report structure with three dimensions:
47
+ - **Completeness**: Track tasks and spec coverage
48
+ - **Correctness**: Track requirement implementation and scenario coverage
49
+ - **Coherence**: Track design adherence and pattern consistency
50
+
51
+ Each dimension can have CRITICAL, WARNING, or SUGGESTION issues.
52
+
53
+ 5. **Verify Completeness**
54
+
55
+ **Task Completion**:
56
+ - If tasks.md exists in contextFiles, read it
57
+ - Parse checkboxes: `- [ ]` (incomplete) vs `- [x]` (complete)
58
+ - Count complete vs total tasks
59
+ - If incomplete tasks exist:
60
+ - Add CRITICAL issue for each incomplete task
61
+ - Recommendation: "Complete task: <description>" or "Mark as done if already implemented"
62
+
63
+ **Spec Coverage**:
64
+ - If delta specs exist in `openspec/changes/<name>/specs/`:
65
+ - Extract all requirements (marked with "### Requirement:")
66
+ - For each requirement:
67
+ - Search codebase for keywords related to the requirement
68
+ - Assess if implementation likely exists
69
+ - If requirements appear unimplemented:
70
+ - Add CRITICAL issue: "Requirement not found: <requirement name>"
71
+ - Recommendation: "Implement requirement X: <description>"
72
+
73
+ 6. **Verify Correctness**
74
+
75
+ **Requirement Implementation Mapping**:
76
+ - For each requirement from delta specs:
77
+ - Search codebase for implementation evidence
78
+ - If found, note file paths and line ranges
79
+ - Assess if implementation matches requirement intent
80
+ - If divergence detected:
81
+ - Add WARNING: "Implementation may diverge from spec: <details>"
82
+ - Recommendation: "Review <file>:<lines> against requirement X"
83
+
84
+ **Scenario Coverage**:
85
+ - For each scenario in delta specs (marked with "#### Scenario:"):
86
+ - Check if conditions are handled in code
87
+ - Check if tests exist covering the scenario
88
+ - If scenario appears uncovered:
89
+ - Add WARNING: "Scenario not covered: <scenario name>"
90
+ - Recommendation: "Add test or implementation for scenario: <description>"
91
+
92
+ 7. **Verify Coherence**
93
+
94
+ **Design Adherence**:
95
+ - If design.md exists in contextFiles:
96
+ - Extract key decisions (look for sections like "Decision:", "Approach:", "Architecture:")
97
+ - Verify implementation follows those decisions
98
+ - If contradiction detected:
99
+ - Add WARNING: "Design decision not followed: <decision>"
100
+ - Recommendation: "Update implementation or revise design.md to match reality"
101
+ - If no design.md: Skip design adherence check, note "No design.md to verify against"
102
+
103
+ **Code Pattern Consistency**:
104
+ - Review new code for consistency with project patterns
105
+ - Check file naming, directory structure, coding style
106
+ - If significant deviations found:
107
+ - Add SUGGESTION: "Code pattern deviation: <details>"
108
+ - Recommendation: "Consider following project pattern: <example>"
109
+
110
+ 8. **Generate Verification Report**
111
+
112
+ **Summary Scorecard**:
113
+ ```
114
+ ## Verification Report: <change-name>
115
+
116
+ ### Summary
117
+ | Dimension | Status |
118
+ |--------------|------------------|
119
+ | Completeness | X/Y tasks, N reqs|
120
+ | Correctness | M/N reqs covered |
121
+ | Coherence | Followed/Issues |
122
+ ```
123
+
124
+ **Issues by Priority**:
125
+
126
+ 1. **CRITICAL** (Must fix before archive):
127
+ - Incomplete tasks
128
+ - Missing requirement implementations
129
+ - Each with specific, actionable recommendation
130
+
131
+ 2. **WARNING** (Should fix):
132
+ - Spec/design divergences
133
+ - Missing scenario coverage
134
+ - Each with specific recommendation
135
+
136
+ 3. **SUGGESTION** (Nice to fix):
137
+ - Pattern inconsistencies
138
+ - Minor improvements
139
+ - Each with specific recommendation
140
+
141
+ **Final Assessment**:
142
+ - If CRITICAL issues: "X critical issue(s) found. Fix before archiving."
143
+ - If only warnings: "No critical issues. Y warning(s) to consider. Ready for archive (with noted improvements)."
144
+ - If all clear: "All checks passed. Ready for archive."
145
+
146
+ **Verification Heuristics**
147
+
148
+ - **Completeness**: Focus on objective checklist items (checkboxes, requirements list)
149
+ - **Correctness**: Use keyword search, file path analysis, reasonable inference - don't require perfect certainty
150
+ - **Coherence**: Look for glaring inconsistencies, don't nitpick style
151
+ - **False Positives**: When uncertain, prefer SUGGESTION over WARNING, WARNING over CRITICAL
152
+ - **Actionability**: Every issue must have a specific recommendation with file/line references where applicable
153
+
154
+ **Graceful Degradation**
155
+
156
+ - If only tasks.md exists: verify task completion only, skip spec/design checks
157
+ - If tasks + specs exist: verify completeness and correctness, skip design
158
+ - If full artifacts: verify all three dimensions
159
+ - Always note which checks were skipped and why
160
+
161
+ **Output Format**
162
+
163
+ Use clear markdown with:
164
+ - Table for summary scorecard
165
+ - Grouped lists for issues (CRITICAL/WARNING/SUGGESTION)
166
+ - Code references in format: `file.ts:123`
167
+ - Specific, actionable recommendations
168
+ - No vague suggestions like "consider reviewing"
@@ -0,0 +1,83 @@
1
+ # Contributing
2
+
3
+ ## Snippet authoring rules
4
+
5
+ Every OmniFocus operation in this server is implemented as a standalone `.js` file under `src/snippets/`. These rules are invariants — violating them causes the same class of bugs the server was built to eliminate.
6
+
7
+ ### Rule 1: One `__ARGS__` placeholder per snippet, no other interpolation
8
+
9
+ Every snippet file must contain exactly one occurrence of the token `__ARGS__`. The runtime injects arguments by doing:
10
+
11
+ ```ts
12
+ template.replace("__ARGS__", JSON.stringify(args))
13
+ ```
14
+
15
+ This is the **only** way user-supplied data enters a snippet. No string concatenation, no template literals constructing JS source, no manual escaping. If you find yourself writing `"'"+someValue+"'"` or `` `const name = "${value}"` `` inside a snippet template, stop — that is the apostrophe/injection bug pattern this architecture was built to eliminate.
16
+
17
+ Because `JSON.stringify` produces output that is a syntactic subset of JavaScript, the injected args literal is safe for all inputs including apostrophes, double quotes, backslashes, newlines, emoji, and arbitrary unicode.
18
+
19
+ ### Rule 2: Snippets must work standalone
20
+
21
+ Every snippet must be paste-able into the OmniFocus Automation Console for manual testing. Replace `__ARGS__` with a hand-written object literal:
22
+
23
+ ```js
24
+ // Paste this into OmniFocus Automation Console:
25
+ const args = { id: "jMBMptE7rJ1" };
26
+ // ... rest of the snippet
27
+ ```
28
+
29
+ If your snippet cannot be tested this way, it's not a self-contained snippet — refactor until it is. This is what makes the snippets independently verifiable and debuggable.
30
+
31
+ ### Rule 3: Snippets return a JSON string in the result envelope format
32
+
33
+ Every snippet's last expression must be a JSON string matching:
34
+
35
+ ```js
36
+ JSON.stringify({ ok: true, data: <your result> })
37
+ ```
38
+
39
+ On error, throw (don't return an error envelope) — the JXA shim catches the exception and constructs the error envelope automatically:
40
+
41
+ ```js
42
+ // Good
43
+ throw new Error("Project not found: " + args.id);
44
+
45
+ // Don't do this — the shim handles it
46
+ // return JSON.stringify({ ok: false, error: { ... } })
47
+ ```
48
+
49
+ The one exception is inline error returns used to avoid the overhead of an exception for expected cases (like "not found" in a list operation). If you do this, use the same envelope shape:
50
+
51
+ ```js
52
+ return JSON.stringify({ ok: false, error: { name: "NotFoundError", message: "..." } });
53
+ ```
54
+
55
+ ### Rule 4: No JXA scripting-dictionary domain methods
56
+
57
+ The JXA shim uses `Application('OmniFocus').evaluateJavascript()` and nothing else. Inside snippets (which run in OmniJS), you have the full Omni Automation API: `flattenedProjects`, `flattenedTasks`, `flattenedFolders`, `flattenedTags`, `new Project(...)`, `new Folder(...)`, `moveTasks(...)`, etc.
58
+
59
+ Do **not** use the JXA scripting dictionary for domain operations. Concretely, in the `src/runtime/jxaShim.ts` JXA wrapper, the only call to the OmniFocus application object is `evaluateJavascript`. In snippets themselves, there is no JXA application object — you're inside OmniJS already.
60
+
61
+ This rule is why the server can do things the prior AppleScript-based server could not: folder creation, Single Actions lists, task moves, stable IDs, and recurrence rules all live in the OmniJS API, not the scripting dictionary.
62
+
63
+ ## Adding a new tool
64
+
65
+ 1. Write the OmniJS snippet as `src/snippets/<tool_name>.js`. Verify it works standalone in the Automation Console.
66
+ 2. Add the zod input schema and handler to `src/tools/<ToolName>.ts`.
67
+ 3. Export from `src/tools/index.ts` and add to `allTools`.
68
+ 4. Add the corresponding spec requirement and scenario to the relevant capability spec file under `openspec/changes/<change-name>/specs/<capability>/spec.md`.
69
+ 5. Write a unit test for schema validation and a unit test for injection safety if the tool has new parameter types.
70
+ 6. Write an integration test that exercises the tool against a real OmniFocus instance, scoped to a `withTestFolder` fixture.
71
+
72
+ ## Running tests
73
+
74
+ ```bash
75
+ # Unit tests (no OmniFocus required)
76
+ npm test
77
+
78
+ # Integration tests (requires OmniFocus running on macOS)
79
+ npm run test:integration
80
+
81
+ # Clean up stale test fixtures from interrupted runs
82
+ npm run test:cleanup-fixtures
83
+ ```
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Steve Ardis
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,198 @@
1
+ # omnifocus-mcp
2
+
3
+ An MCP server for OmniFocus that exposes the full Omni Automation JavaScript API to LLM callers.
4
+
5
+ **macOS only.** Requires OmniFocus running on the same machine. The entire implementation runs OmniJS snippets inside OmniFocus via `osascript -l JavaScript` — no AppleScript string generation, no scripting dictionary limitations.
6
+
7
+ ## Prerequisites
8
+
9
+ - macOS (Omni Automation is macOS-only; the server will not start on other platforms)
10
+ - OmniFocus installed and **running**
11
+ - Node.js ≥ 20
12
+
13
+ ## Install
14
+
15
+ ```bash
16
+ npm install
17
+ npm run build
18
+ ```
19
+
20
+ ## MCP Client Configuration
21
+
22
+ Add to your MCP client config (e.g. Claude Desktop `claude_desktop_config.json`):
23
+
24
+ ```json
25
+ {
26
+ "mcpServers": {
27
+ "omnifocus": {
28
+ "command": "node",
29
+ "args": ["/absolute/path/to/omnifocus-mcp/dist/server.js"]
30
+ }
31
+ }
32
+ }
33
+ ```
34
+
35
+ For development (no build step):
36
+
37
+ ```json
38
+ {
39
+ "mcpServers": {
40
+ "omnifocus": {
41
+ "command": "npx",
42
+ "args": ["tsx", "/absolute/path/to/omnifocus-mcp/src/server.ts"]
43
+ }
44
+ }
45
+ }
46
+ ```
47
+
48
+ ## Available Tools
49
+
50
+ ### Read
51
+
52
+ | Tool | Description |
53
+ |---|---|
54
+ | `list_projects` | Projects with optional filtering by status, folderId, flagged. Default excludes done/dropped. Limit (default 100). |
55
+ | `get_project` | Full project detail by stable ID |
56
+ | `list_tasks` | Tasks scoped by `projectId`, `folderId`, `inbox: true`, or `all: true` with optional status/tag/due/flagged filters. Limit (default 200). |
57
+ | `get_task` | Full task detail by stable ID — includes defer/planned/due dates, tags, repetition rule, parentTaskId |
58
+ | `list_folders` | Folders with optional status filter. Limit (default 200). |
59
+ | `get_folder` | Full folder detail by stable ID, including child folder and project IDs |
60
+ | `list_tags` | Tags with optional status filter. Limit (default 200). |
61
+ | `get_tag` | Full tag detail by stable ID, including child tag IDs |
62
+ | `resolve_name` | Resolve a name to stable ID candidates — **never silently disambiguates**; returns all matches |
63
+
64
+ ### Write
65
+
66
+ | Tool | Description |
67
+ |---|---|
68
+ | `create_task` | Create a task in inbox, project, or as subtask. Supports defer/planned/due dates, tags, flagged, estimated minutes, and repetition rules. |
69
+ | `edit_task` | Edit any task field. Pass `null` to clear dates or repetition. Omitted fields are unchanged. |
70
+ | `complete_task` | Mark a task complete |
71
+ | `drop_task` | Mark a task dropped |
72
+ | `delete_task` | Permanently delete a task and all subtasks |
73
+ | `create_project` | Create a project, optionally in a folder. Supports type, status, review interval, tags. |
74
+ | `edit_project` | Edit project fields |
75
+ | `complete_project` | Mark a project complete |
76
+ | `drop_project` | Mark a project dropped |
77
+ | `delete_project` | Permanently delete a project and all its tasks |
78
+ | `create_folder` | Create a folder, optionally nested |
79
+ | `edit_folder` | Rename a folder |
80
+ | `delete_folder` | Permanently delete a folder and entire subtree |
81
+ | `create_tag` | Create a tag, optionally nested |
82
+ | `edit_tag` | Edit tag name or status |
83
+ | `delete_tag` | Permanently delete a tag and child tags |
84
+ | `move_task` | Move a task to a project or make it a subtask of another task |
85
+ | `move_project` | Move a project to a folder or to top level |
86
+
87
+ ### Addressing model
88
+
89
+ Every entity returned by this server includes a stable `id` field (`id.primaryKey` from OmniFocus). Use this ID in subsequent calls rather than names. Names can be ambiguous; IDs are not.
90
+
91
+ If you have a name but not an ID, use `resolve_name`. It returns a list — if multiple candidates are returned, inspect the `path` field and ask the user to disambiguate before proceeding with any write operation.
92
+
93
+ ## Comparison with other OmniFocus MCP servers
94
+
95
+ Two notable alternatives exist:
96
+ [themotionmachine/OmniFocus-MCP](https://github.com/themotionmachine/OmniFocus-MCP) and
97
+ [jqlts1/omnifocus-mcp-enhanced](https://github.com/jqlts1/omnifocus-mcp-enhanced) (a fork of the above with additional tools).
98
+
99
+ **Scripting API.** The alternatives use the JXA scripting dictionary or AppleScript to drive OmniFocus. This server makes a single JXA call — `Application('OmniFocus').evaluateJavascript()` — and runs all logic as OmniJS (Omni Automation) inside OmniFocus. This gives access to the full Omni Automation API surface (recurrence rules, review intervals, perspectives, forecast, attachments, URL automation, etc.) rather than the more limited scripting dictionary.
100
+
101
+ **Argument injection.** The alternatives construct osascript commands via string interpolation, which can break on apostrophes, quotes, backslashes, and unicode in names. This server serializes all arguments with `JSON.stringify` into a JS literal.
102
+
103
+ **Entity addressing.** The alternatives address entities primarily by name. This server returns a stable `id` (`id.primaryKey`) for every entity and provides `resolve_name` to map a name to ID candidates — returning all matches with full paths rather than silently picking one when names are ambiguous.
104
+
105
+ **Full CRUD.** This server supports creating, editing, completing, dropping, deleting, and moving tasks, projects, folders, and tags — plus repetition rules and OmniFocus 4's planned date.
106
+
107
+ ## Development
108
+
109
+ ```bash
110
+ # Type-check without building
111
+ npm run typecheck
112
+
113
+ # Run unit tests (no OmniFocus required)
114
+ npm test
115
+
116
+ # Build
117
+ npm run build
118
+ ```
119
+
120
+ ## Testing
121
+
122
+ ### Unit tests (no OmniFocus required)
123
+
124
+ ```bash
125
+ npm test
126
+ ```
127
+
128
+ ### Integration tests
129
+
130
+ > ⚠️ **Integration tests run against your real OmniFocus database.**
131
+ >
132
+ > Each test run creates a temporary top-level folder named `__MCP_TEST_<uuid>__` and deletes it on teardown. If a test run is interrupted before teardown, run the cleanup script:
133
+ >
134
+ > ```bash
135
+ > npm run test:cleanup-fixtures
136
+ > ```
137
+
138
+ > ⚠️ **Sync warning:** By default, integration tests refuse to run if OmniFocus sync is enabled, to prevent test fixtures from propagating to your other devices. Disable OmniFocus sync first, or set `MCP_TEST_ALLOW_SYNC=1` to opt in (fixtures will sync):
139
+
140
+ ```bash
141
+ # Default (refuses if sync enabled)
142
+ npm run test:integration
143
+
144
+ # With sync enabled (use carefully)
145
+ MCP_TEST_ALLOW_SYNC=1 npm run test:integration
146
+ ```
147
+
148
+ ### Clean up stale test fixtures
149
+
150
+ ```bash
151
+ npm run test:cleanup-fixtures
152
+ ```
153
+
154
+ This removes any `__MCP_TEST_*__` folders and orphaned `__mcp_*__` projects/tags left in OmniFocus from interrupted test runs.
155
+
156
+ ## Contributing
157
+
158
+ Contributions are welcome! Here's how to get started:
159
+
160
+ 1. **Fork and clone** the repo
161
+ 2. **Install dependencies:** `npm install`
162
+ 3. **Run unit tests** (no OmniFocus needed): `npm test`
163
+ 4. **Run integration tests** (requires macOS + OmniFocus): `npm run test:integration`
164
+
165
+ ### Before submitting a PR
166
+
167
+ - `npm run typecheck` — must pass with no errors
168
+ - `npm test` — all unit tests must pass
169
+ - `npm run test:integration` — all integration tests must pass (macOS only)
170
+ - Keep changes focused — one feature or fix per PR
171
+
172
+ ### Architecture overview
173
+
174
+ The server runs OmniJS snippets inside OmniFocus via `osascript -l JavaScript`. Each tool has three layers:
175
+
176
+ - **Schema** (`src/schemas/shapes.ts`) — Zod schemas for input validation and output parsing
177
+ - **Snippet** (`src/snippets/*.js`) — OmniJS code that runs inside OmniFocus. Plain ES5 JavaScript (no imports, no TypeScript). Arguments are injected via `__ARGS__` placeholder.
178
+ - **Tool handler** (`src/tools/*.ts`) — Validates input, calls `runSnippet()`, parses the result
179
+
180
+ When adding a new tool:
181
+ 1. Define input/output schemas in `src/schemas/shapes.ts` and export from `src/schemas/index.ts`
182
+ 2. Create the OmniJS snippet in `src/snippets/`
183
+ 3. Add the snippet name to `ALLOWED_SNIPPETS` in `src/runtime/snippetLoader.ts`
184
+ 4. Create the tool handler in `src/tools/` and register it in `src/tools/index.ts`
185
+ 5. Add unit tests for schemas and integration tests that run against OmniFocus
186
+
187
+ ### Writing OmniJS snippets
188
+
189
+ Snippets run inside OmniFocus's JavaScript runtime, not Node.js. Key constraints:
190
+
191
+ - **ES5-style JavaScript** — use `var`, `function(){}`, no arrow functions in older OmniFocus versions
192
+ - **No imports** — all OmniJS globals (`flattenedTasks`, `flattenedProjects`, `moveTasks`, etc.) are available directly
193
+ - **Return JSON** — always `return JSON.stringify({ ok: true, data: ... })`
194
+ - **Error pattern** — throw named errors (`NotFoundError`, `ValidationError`) which the bridge catches and wraps
195
+
196
+ ## License
197
+
198
+ [MIT](LICENSE)
@@ -0,0 +1,16 @@
1
+ export interface RunOptions {
2
+ timeoutMs?: number;
3
+ }
4
+ /**
5
+ * Execute an OmniJS snippet inside OmniFocus via the JXA bridge.
6
+ *
7
+ * @param name - Snippet filename (without .js extension) under src/snippets/
8
+ * @param args - Arguments to inject. Embedded as a JSON literal; safe for
9
+ * all unicode, apostrophes, quotes, etc. (Design Decision 2).
10
+ * @param opts - Optional timeout override (default 30s)
11
+ * @returns The `data` field from the success envelope
12
+ * @throws ExecutionError if the snippet throws inside OmniJS
13
+ * @throws Error on timeout, process errors, or protocol violations
14
+ */
15
+ export declare function runSnippet(name: string, args: unknown, opts?: RunOptions): Promise<unknown>;
16
+ //# sourceMappingURL=bridge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bridge.d.ts","sourceRoot":"","sources":["../../src/runtime/bridge.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,UAAU;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,UAAU,CAC9B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,OAAO,EACb,IAAI,GAAE,UAAe,GACpB,OAAO,CAAC,OAAO,CAAC,CAsElB"}