@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,49 @@
1
+ import { describe, it, expect, beforeAll, afterAll } from "vitest";
2
+ import { runSnippet } from "../../src/runtime/bridge.js";
3
+ import { createTestFolder, cleanupTestFolder, type TestFixture } from "./fixtures.js";
4
+ import { ProjectDetail } from "../../src/schemas/index.js";
5
+
6
+ describe("create_project (integration)", () => {
7
+ let fixture: TestFixture;
8
+
9
+ beforeAll(async () => {
10
+ fixture = await createTestFolder();
11
+ });
12
+
13
+ afterAll(async () => {
14
+ await cleanupTestFolder(fixture.folderId);
15
+ });
16
+
17
+ it("creates a top-level project and returns stable id", async () => {
18
+ const name = `__mcp_test_proj_${Date.now()}__`;
19
+ const raw = await runSnippet("create_project", { name });
20
+ const project = ProjectDetail.parse(raw);
21
+ expect(project.id).toBeTruthy();
22
+ expect(project.name).toBe(name);
23
+ expect(project.folderPath).toBe("");
24
+ expect(project.type).toBe("parallel");
25
+ // Clean up the top-level project
26
+ await runSnippet("delete_project", { id: project.id });
27
+ });
28
+
29
+ it("creates project inside a folder and sets folderPath", async () => {
30
+ const raw = await runSnippet("create_project", {
31
+ name: "Folder Project Test",
32
+ folderId: fixture.folderId,
33
+ });
34
+ const project = ProjectDetail.parse(raw);
35
+ expect(project.id).toBeTruthy();
36
+ expect(project.folderPath).toBeTruthy();
37
+ expect(project.type).toBe("parallel");
38
+ });
39
+
40
+ it("creates sequential project and returns type sequential", async () => {
41
+ const raw = await runSnippet("create_project", {
42
+ name: "Sequential Project Test",
43
+ folderId: fixture.folderId,
44
+ type: "sequential",
45
+ });
46
+ const project = ProjectDetail.parse(raw);
47
+ expect(project.type).toBe("sequential");
48
+ });
49
+ });
@@ -0,0 +1,52 @@
1
+ import { describe, it, expect, afterAll } from "vitest";
2
+ import { runSnippet } from "../../src/runtime/bridge.js";
3
+ import { deleteTestTag } from "./fixtures.js";
4
+ import { TagDetail } from "../../src/schemas/index.js";
5
+
6
+ describe("create_tag (integration)", () => {
7
+ const createdTagIds: string[] = [];
8
+
9
+ afterAll(async () => {
10
+ for (const id of createdTagIds) {
11
+ await deleteTestTag(id);
12
+ }
13
+ });
14
+
15
+ it("creates a top-level tag and returns stable id", async () => {
16
+ const name = `__mcp_test_tag_${Date.now()}__`;
17
+ const raw = await runSnippet("create_tag", { name });
18
+ const tag = TagDetail.parse(raw);
19
+ expect(tag.id).toBeTruthy();
20
+ expect(tag.name).toBe(name);
21
+ expect(tag.parentId).toBeNull();
22
+ expect(tag.path).toBe(name);
23
+ createdTagIds.push(tag.id);
24
+ });
25
+
26
+ it("creates a child tag with correct path and parentId", async () => {
27
+ const parentName = `__mcp_test_parent_tag_${Date.now()}__`;
28
+ const parentRaw = await runSnippet("create_tag", { name: parentName });
29
+ const parent = TagDetail.parse(parentRaw);
30
+ createdTagIds.push(parent.id);
31
+
32
+ const childRaw = await runSnippet("create_tag", {
33
+ name: "Child",
34
+ parentTagId: parent.id,
35
+ });
36
+ const child = TagDetail.parse(childRaw);
37
+ expect(child.parentId).toBe(parent.id);
38
+ expect(child.path).toContain(parentName);
39
+ expect(child.path).toContain("Child");
40
+ // Parent should now list child
41
+ expect(parent.childTagIds).not.toContain(child.id); // stale parent object; child is real
42
+ });
43
+
44
+ it("returns not-found error for invalid parentTagId", async () => {
45
+ await expect(
46
+ runSnippet("create_tag", { name: "X", parentTagId: "nonexistent-id-xyz" })
47
+ ).rejects.toSatisfy((e: unknown) => {
48
+ const err = e as Record<string, unknown>;
49
+ return err.name === "ExecutionError" && err.errorName === "NotFoundError";
50
+ });
51
+ });
52
+ });
@@ -0,0 +1,55 @@
1
+ import { describe, it, expect, beforeAll, afterAll } from "vitest";
2
+ import { runSnippet } from "../../src/runtime/bridge.js";
3
+ import { createTestFolder, cleanupTestFolder, createTestProject, type TestFixture } from "./fixtures.js";
4
+ import { TaskDetail } from "../../src/schemas/index.js";
5
+
6
+ describe("create_task (integration)", () => {
7
+ let fixture: TestFixture;
8
+ let projectId: string;
9
+
10
+ beforeAll(async () => {
11
+ fixture = await createTestFolder();
12
+ projectId = await createTestProject(fixture.folderId, "CreateTask Test Project");
13
+ });
14
+
15
+ afterAll(async () => {
16
+ await cleanupTestFolder(fixture.folderId);
17
+ });
18
+
19
+ it("creates an inbox task and returns a stable id", async () => {
20
+ const raw = await runSnippet("create_task", { name: "Inbox task test" });
21
+ const task = TaskDetail.parse(raw);
22
+ expect(task.id).toBeTruthy();
23
+ expect(task.name).toBe("Inbox task test");
24
+ expect(task.containerType).toBe("inbox");
25
+ // Clean up inbox task
26
+ await runSnippet("delete_task", { id: task.id });
27
+ });
28
+
29
+ it("creates a task in a project and sets containerId", async () => {
30
+ const raw = await runSnippet("create_task", {
31
+ name: "Project task test",
32
+ projectId,
33
+ });
34
+ const task = TaskDetail.parse(raw);
35
+ expect(task.id).toBeTruthy();
36
+ expect(task.name).toBe("Project task test");
37
+ expect(task.containerId).toBe(projectId);
38
+ expect(task.containerType).toBe("project");
39
+ });
40
+
41
+ it("creates a subtask and sets parentTaskId", async () => {
42
+ const parentRaw = await runSnippet("create_task", {
43
+ name: "Parent task",
44
+ projectId,
45
+ });
46
+ const parent = TaskDetail.parse(parentRaw);
47
+
48
+ const childRaw = await runSnippet("create_task", {
49
+ name: "Child task",
50
+ parentTaskId: parent.id,
51
+ });
52
+ const child = TaskDetail.parse(childRaw);
53
+ expect(child.parentTaskId).toBe(parent.id);
54
+ });
55
+ });
@@ -0,0 +1,64 @@
1
+ import { describe, it, expect, beforeAll, afterAll } from "vitest";
2
+ import { runSnippet } from "../../src/runtime/bridge.js";
3
+ import { createTestFolder, cleanupTestFolder, createTestProject, type TestFixture } from "./fixtures.js";
4
+ import { FolderDetail } from "../../src/schemas/index.js";
5
+
6
+ describe("delete_folder (integration)", () => {
7
+ let fixture: TestFixture;
8
+
9
+ beforeAll(async () => {
10
+ fixture = await createTestFolder();
11
+ });
12
+
13
+ afterAll(async () => {
14
+ // Best-effort cleanup — the folder may already be deleted by the test
15
+ try { await cleanupTestFolder(fixture.folderId); } catch (_) {}
16
+ });
17
+
18
+ it("deletes folder with child project and get_folder returns not-found afterwards", async () => {
19
+ // Create a nested folder with a project inside the fixture
20
+ const childRaw = await runSnippet("create_folder", {
21
+ name: "ToDelete",
22
+ parentFolderId: fixture.folderId,
23
+ });
24
+ const childFolder = FolderDetail.parse(childRaw);
25
+ await createTestProject(childFolder.id, "Project inside folder");
26
+
27
+ // Delete the child folder
28
+ await runSnippet("delete_folder", { id: childFolder.id });
29
+
30
+ // get_folder should return not-found
31
+ await expect(
32
+ runSnippet("get_folder", { id: childFolder.id })
33
+ ).rejects.toSatisfy((e: unknown) => {
34
+ const err = e as Record<string, unknown>;
35
+ return err.name === "ExecutionError" && err.errorName === "NotFoundError";
36
+ });
37
+ });
38
+
39
+ it("deletes an empty folder", async () => {
40
+ const raw = await runSnippet("create_folder", {
41
+ name: "EmptyToDelete",
42
+ parentFolderId: fixture.folderId,
43
+ });
44
+ const folder = FolderDetail.parse(raw);
45
+
46
+ await runSnippet("delete_folder", { id: folder.id });
47
+
48
+ await expect(
49
+ runSnippet("get_folder", { id: folder.id })
50
+ ).rejects.toSatisfy((e: unknown) => {
51
+ const err = e as Record<string, unknown>;
52
+ return err.name === "ExecutionError" && err.errorName === "NotFoundError";
53
+ });
54
+ });
55
+
56
+ it("returns not-found error for invalid id", async () => {
57
+ await expect(
58
+ runSnippet("delete_folder", { id: "nonexistent-id-xyz" })
59
+ ).rejects.toSatisfy((e: unknown) => {
60
+ const err = e as Record<string, unknown>;
61
+ return err.name === "ExecutionError" && err.errorName === "NotFoundError";
62
+ });
63
+ });
64
+ });
@@ -0,0 +1,31 @@
1
+ import { describe, it, expect, beforeAll, afterAll } from "vitest";
2
+ import { runSnippet } from "../../src/runtime/bridge.js";
3
+ import { createTestFolder, cleanupTestFolder, createTestProject, type TestFixture } from "./fixtures.js";
4
+ import { ProjectDetail } from "../../src/schemas/index.js";
5
+
6
+ describe("delete_project (integration)", () => {
7
+ let fixture: TestFixture;
8
+ let projectId: string;
9
+
10
+ beforeAll(async () => {
11
+ fixture = await createTestFolder();
12
+ projectId = await createTestProject(fixture.folderId, "DeleteProject Test Project");
13
+ });
14
+
15
+ afterAll(async () => {
16
+ await cleanupTestFolder(fixture.folderId);
17
+ });
18
+
19
+ it("deletes the project and get_project returns not-found afterwards", async () => {
20
+ await runSnippet("delete_project", { id: projectId });
21
+
22
+ await expect(
23
+ runSnippet("get_project", { id: projectId })
24
+ ).rejects.toSatisfy(
25
+ (e: unknown) => {
26
+ const err = e as Record<string, unknown>;
27
+ return err.name === "ExecutionError" && err.errorName === "NotFoundError";
28
+ }
29
+ );
30
+ });
31
+ });
@@ -0,0 +1,61 @@
1
+ import { describe, it, expect, beforeAll } from "vitest";
2
+ import { runSnippet } from "../../src/runtime/bridge.js";
3
+ import { createTestTag } from "./fixtures.js";
4
+ import { TagDetail } from "../../src/schemas/index.js";
5
+
6
+ describe("delete_tag (integration)", () => {
7
+ let tagId: string;
8
+ let parentTagId: string;
9
+
10
+ beforeAll(async () => {
11
+ tagId = await createTestTag(`__mcp_test_del_tag_${Date.now()}__`);
12
+ parentTagId = await createTestTag(`__mcp_test_del_parent_${Date.now()}__`);
13
+ });
14
+
15
+ it("deletes a tag and get_tag returns not-found afterwards", async () => {
16
+ await runSnippet("delete_tag", { id: tagId });
17
+
18
+ await expect(
19
+ runSnippet("get_tag", { id: tagId })
20
+ ).rejects.toSatisfy((e: unknown) => {
21
+ const err = e as Record<string, unknown>;
22
+ return err.name === "ExecutionError" && err.errorName === "NotFoundError";
23
+ });
24
+ });
25
+
26
+ it("deletes parent tag and child tags are also gone", async () => {
27
+ // Create a child tag under parentTagId
28
+ const childRaw = await runSnippet("create_tag", {
29
+ name: "ChildToDelete",
30
+ parentTagId,
31
+ });
32
+ const child = TagDetail.parse(childRaw);
33
+
34
+ // Delete the parent
35
+ await runSnippet("delete_tag", { id: parentTagId });
36
+
37
+ // Both parent and child should be gone
38
+ await expect(
39
+ runSnippet("get_tag", { id: parentTagId })
40
+ ).rejects.toSatisfy((e: unknown) => {
41
+ const err = e as Record<string, unknown>;
42
+ return err.name === "ExecutionError" && err.errorName === "NotFoundError";
43
+ });
44
+
45
+ await expect(
46
+ runSnippet("get_tag", { id: child.id })
47
+ ).rejects.toSatisfy((e: unknown) => {
48
+ const err = e as Record<string, unknown>;
49
+ return err.name === "ExecutionError" && err.errorName === "NotFoundError";
50
+ });
51
+ });
52
+
53
+ it("returns not-found error for invalid id", async () => {
54
+ await expect(
55
+ runSnippet("delete_tag", { id: "nonexistent-id-xyz" })
56
+ ).rejects.toSatisfy((e: unknown) => {
57
+ const err = e as Record<string, unknown>;
58
+ return err.name === "ExecutionError" && err.errorName === "NotFoundError";
59
+ });
60
+ });
61
+ });
@@ -0,0 +1,36 @@
1
+ import { describe, it, expect, beforeAll, afterAll } from "vitest";
2
+ import { runSnippet } from "../../src/runtime/bridge.js";
3
+ import { createTestFolder, cleanupTestFolder, createTestProject, type TestFixture } from "./fixtures.js";
4
+ import { TaskDetail } from "../../src/schemas/index.js";
5
+
6
+ describe("delete_task (integration)", () => {
7
+ let fixture: TestFixture;
8
+ let taskId: string;
9
+
10
+ beforeAll(async () => {
11
+ fixture = await createTestFolder();
12
+ const projectId = await createTestProject(fixture.folderId, "DeleteTask Test Project");
13
+ const taskRaw = await runSnippet("create_task", {
14
+ name: "Task to delete",
15
+ projectId,
16
+ });
17
+ taskId = TaskDetail.parse(taskRaw).id;
18
+ });
19
+
20
+ afterAll(async () => {
21
+ await cleanupTestFolder(fixture.folderId);
22
+ });
23
+
24
+ it("deletes the task and get_task returns not-found afterwards", async () => {
25
+ await runSnippet("delete_task", { id: taskId });
26
+
27
+ await expect(
28
+ runSnippet("get_task", { id: taskId })
29
+ ).rejects.toSatisfy(
30
+ (e: unknown) => {
31
+ const err = e as Record<string, unknown>;
32
+ return err.name === "ExecutionError" && err.errorName === "NotFoundError";
33
+ }
34
+ );
35
+ });
36
+ });
@@ -0,0 +1,24 @@
1
+ import { describe, it, expect, beforeAll, afterAll } from "vitest";
2
+ import { runSnippet } from "../../src/runtime/bridge.js";
3
+ import { createTestFolder, cleanupTestFolder, createTestProject, type TestFixture } from "./fixtures.js";
4
+ import { ProjectDetail } from "../../src/schemas/index.js";
5
+
6
+ describe("drop_project (integration)", () => {
7
+ let fixture: TestFixture;
8
+ let projectId: string;
9
+
10
+ beforeAll(async () => {
11
+ fixture = await createTestFolder();
12
+ projectId = await createTestProject(fixture.folderId, "DropProject Test Project");
13
+ });
14
+
15
+ afterAll(async () => {
16
+ await cleanupTestFolder(fixture.folderId);
17
+ });
18
+
19
+ it("marks project dropped and returns status dropped", async () => {
20
+ const raw = await runSnippet("drop_project", { id: projectId });
21
+ const project = ProjectDetail.parse(raw);
22
+ expect(project.status).toBe("dropped");
23
+ });
24
+ });
@@ -0,0 +1,29 @@
1
+ import { describe, it, expect, beforeAll, afterAll } from "vitest";
2
+ import { runSnippet } from "../../src/runtime/bridge.js";
3
+ import { createTestFolder, cleanupTestFolder, createTestProject, type TestFixture } from "./fixtures.js";
4
+ import { TaskDetail } from "../../src/schemas/index.js";
5
+
6
+ describe("drop_task (integration)", () => {
7
+ let fixture: TestFixture;
8
+ let taskId: string;
9
+
10
+ beforeAll(async () => {
11
+ fixture = await createTestFolder();
12
+ const projectId = await createTestProject(fixture.folderId, "DropTask Test Project");
13
+ const taskRaw = await runSnippet("create_task", {
14
+ name: "Task to drop",
15
+ projectId,
16
+ });
17
+ taskId = TaskDetail.parse(taskRaw).id;
18
+ });
19
+
20
+ afterAll(async () => {
21
+ await cleanupTestFolder(fixture.folderId);
22
+ });
23
+
24
+ it("marks task dropped and returns status dropped", async () => {
25
+ const raw = await runSnippet("drop_task", { id: taskId });
26
+ const task = TaskDetail.parse(raw);
27
+ expect(task.status).toBe("dropped");
28
+ });
29
+ });
@@ -0,0 +1,43 @@
1
+ import { describe, it, expect, beforeAll, afterAll } from "vitest";
2
+ import { runSnippet } from "../../src/runtime/bridge.js";
3
+ import { createTestFolder, cleanupTestFolder, type TestFixture } from "./fixtures.js";
4
+ import { FolderDetail } from "../../src/schemas/index.js";
5
+
6
+ describe("edit_folder (integration)", () => {
7
+ let fixture: TestFixture;
8
+ let childFolderId: string;
9
+
10
+ beforeAll(async () => {
11
+ fixture = await createTestFolder();
12
+ // Create a child folder to rename
13
+ const raw = await runSnippet("create_folder", {
14
+ name: "Original Name",
15
+ parentFolderId: fixture.folderId,
16
+ });
17
+ const folder = FolderDetail.parse(raw);
18
+ childFolderId = folder.id;
19
+ });
20
+
21
+ afterAll(async () => {
22
+ await cleanupTestFolder(fixture.folderId);
23
+ });
24
+
25
+ it("renames a folder and returns updated name and path", async () => {
26
+ const raw = await runSnippet("edit_folder", {
27
+ id: childFolderId,
28
+ name: "Renamed Folder",
29
+ });
30
+ const folder = FolderDetail.parse(raw);
31
+ expect(folder.name).toBe("Renamed Folder");
32
+ expect(folder.path).toContain("Renamed Folder");
33
+ });
34
+
35
+ it("returns not-found error for invalid id", async () => {
36
+ await expect(
37
+ runSnippet("edit_folder", { id: "nonexistent-id-xyz", name: "X" })
38
+ ).rejects.toSatisfy((e: unknown) => {
39
+ const err = e as Record<string, unknown>;
40
+ return err.name === "ExecutionError" && err.errorName === "NotFoundError";
41
+ });
42
+ });
43
+ });
@@ -0,0 +1,39 @@
1
+ import { describe, it, expect, beforeAll, afterAll } from "vitest";
2
+ import { runSnippet } from "../../src/runtime/bridge.js";
3
+ import { createTestFolder, cleanupTestFolder, createTestProject, type TestFixture } from "./fixtures.js";
4
+ import { ProjectDetail } from "../../src/schemas/index.js";
5
+
6
+ describe("edit_project (integration)", () => {
7
+ let fixture: TestFixture;
8
+ let projectId: string;
9
+
10
+ beforeAll(async () => {
11
+ fixture = await createTestFolder();
12
+ projectId = await createTestProject(fixture.folderId, "EditProject Test Project");
13
+ });
14
+
15
+ afterAll(async () => {
16
+ await cleanupTestFolder(fixture.folderId);
17
+ });
18
+
19
+ it("edits name only; other fields unchanged", async () => {
20
+ const raw = await runSnippet("edit_project", { id: projectId, name: "Updated name" });
21
+ const project = ProjectDetail.parse(raw);
22
+ expect(project.name).toBe("Updated name");
23
+ });
24
+
25
+ it("sets status to onHold", async () => {
26
+ const raw = await runSnippet("edit_project", { id: projectId, status: "onHold" });
27
+ const project = ProjectDetail.parse(raw);
28
+ expect(project.status).toBe("onHold");
29
+ });
30
+
31
+ it("sets review interval", async () => {
32
+ const raw = await runSnippet("edit_project", {
33
+ id: projectId,
34
+ reviewInterval: { steps: 2, unit: "weeks" },
35
+ });
36
+ const project = ProjectDetail.parse(raw);
37
+ expect(project.reviewInterval).toBeTruthy();
38
+ });
39
+ });
@@ -0,0 +1,43 @@
1
+ import { describe, it, expect, beforeAll, afterAll } from "vitest";
2
+ import { runSnippet } from "../../src/runtime/bridge.js";
3
+ import { createTestTag, deleteTestTag } from "./fixtures.js";
4
+ import { TagDetail } from "../../src/schemas/index.js";
5
+
6
+ describe("edit_tag (integration)", () => {
7
+ let tagId: string;
8
+
9
+ beforeAll(async () => {
10
+ tagId = await createTestTag(`__mcp_test_edit_tag_${Date.now()}__`);
11
+ });
12
+
13
+ afterAll(async () => {
14
+ await deleteTestTag(tagId);
15
+ });
16
+
17
+ it("renames a tag", async () => {
18
+ const raw = await runSnippet("edit_tag", { id: tagId, name: "Renamed Tag" });
19
+ const tag = TagDetail.parse(raw);
20
+ expect(tag.name).toBe("Renamed Tag");
21
+ });
22
+
23
+ it("puts tag on hold", async () => {
24
+ const raw = await runSnippet("edit_tag", { id: tagId, status: "onHold" });
25
+ const tag = TagDetail.parse(raw);
26
+ expect(tag.status).toBe("onHold");
27
+ });
28
+
29
+ it("restores tag to active", async () => {
30
+ const raw = await runSnippet("edit_tag", { id: tagId, status: "active" });
31
+ const tag = TagDetail.parse(raw);
32
+ expect(tag.status).toBe("active");
33
+ });
34
+
35
+ it("returns not-found error for invalid id", async () => {
36
+ await expect(
37
+ runSnippet("edit_tag", { id: "nonexistent-id-xyz", name: "X" })
38
+ ).rejects.toSatisfy((e: unknown) => {
39
+ const err = e as Record<string, unknown>;
40
+ return err.name === "ExecutionError" && err.errorName === "NotFoundError";
41
+ });
42
+ });
43
+ });
@@ -0,0 +1,56 @@
1
+ import { describe, it, expect, beforeAll, afterAll } from "vitest";
2
+ import { runSnippet } from "../../src/runtime/bridge.js";
3
+ import { createTestFolder, cleanupTestFolder, createTestProject, createTestTag, deleteTestTag, type TestFixture } from "./fixtures.js";
4
+ import { TaskDetail } from "../../src/schemas/index.js";
5
+
6
+ describe("edit_task (integration)", () => {
7
+ let fixture: TestFixture;
8
+ let taskId: string;
9
+
10
+ beforeAll(async () => {
11
+ fixture = await createTestFolder();
12
+ const projectId = await createTestProject(fixture.folderId, "EditTask Test Project");
13
+ const taskRaw = await runSnippet("create_task", {
14
+ name: "Original name",
15
+ projectId,
16
+ dueDate: "2026-12-31T00:00:00.000Z",
17
+ });
18
+ taskId = TaskDetail.parse(taskRaw).id;
19
+ });
20
+
21
+ afterAll(async () => {
22
+ await cleanupTestFolder(fixture.folderId);
23
+ });
24
+
25
+ it("edits name only; other fields unchanged", async () => {
26
+ const raw = await runSnippet("edit_task", { id: taskId, name: "Updated name" });
27
+ const task = TaskDetail.parse(raw);
28
+ expect(task.name).toBe("Updated name");
29
+ expect(task.dueDate).not.toBeNull();
30
+ });
31
+
32
+ it("sets flagged", async () => {
33
+ const raw = await runSnippet("edit_task", { id: taskId, flagged: true });
34
+ const task = TaskDetail.parse(raw);
35
+ expect(task.flagged).toBe(true);
36
+ });
37
+
38
+ it("clears due date with null", async () => {
39
+ const raw = await runSnippet("edit_task", { id: taskId, dueDate: null });
40
+ const task = TaskDetail.parse(raw);
41
+ expect(task.dueDate).toBeNull();
42
+ });
43
+
44
+ it("replaces tag set", async () => {
45
+ const tagId = await createTestTag(`__mcp_test_tag_${Date.now()}__`);
46
+ try {
47
+ const raw = await runSnippet("edit_task", { id: taskId, tagIds: [tagId] });
48
+ const task = TaskDetail.parse(raw);
49
+ expect(task.tagIds).toContain(tagId);
50
+ // Clear tags before deleting the tag
51
+ await runSnippet("edit_task", { id: taskId, tagIds: [] });
52
+ } finally {
53
+ await deleteTestTag(tagId);
54
+ }
55
+ });
56
+ });