@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.
- package/.claude/commands/opsx/apply.md +152 -0
- package/.claude/commands/opsx/archive.md +157 -0
- package/.claude/commands/opsx/bulk-archive.md +242 -0
- package/.claude/commands/opsx/continue.md +114 -0
- package/.claude/commands/opsx/explore.md +173 -0
- package/.claude/commands/opsx/ff.md +97 -0
- package/.claude/commands/opsx/new.md +69 -0
- package/.claude/commands/opsx/onboard.md +550 -0
- package/.claude/commands/opsx/propose.md +106 -0
- package/.claude/commands/opsx/sync.md +134 -0
- package/.claude/commands/opsx/verify.md +164 -0
- package/.claude/skills/openspec-apply-change/SKILL.md +156 -0
- package/.claude/skills/openspec-archive-change/SKILL.md +114 -0
- package/.claude/skills/openspec-bulk-archive-change/SKILL.md +246 -0
- package/.claude/skills/openspec-continue-change/SKILL.md +118 -0
- package/.claude/skills/openspec-explore/SKILL.md +288 -0
- package/.claude/skills/openspec-ff-change/SKILL.md +101 -0
- package/.claude/skills/openspec-new-change/SKILL.md +74 -0
- package/.claude/skills/openspec-onboard/SKILL.md +554 -0
- package/.claude/skills/openspec-propose/SKILL.md +110 -0
- package/.claude/skills/openspec-sync-specs/SKILL.md +138 -0
- package/.claude/skills/openspec-verify-change/SKILL.md +168 -0
- package/CONTRIBUTING.md +83 -0
- package/LICENSE +21 -0
- package/README.md +198 -0
- package/dist/runtime/bridge.d.ts +16 -0
- package/dist/runtime/bridge.d.ts.map +1 -0
- package/dist/runtime/bridge.js +76 -0
- package/dist/runtime/bridge.js.map +1 -0
- package/dist/runtime/index.d.ts +5 -0
- package/dist/runtime/index.d.ts.map +1 -0
- package/dist/runtime/index.js +5 -0
- package/dist/runtime/index.js.map +1 -0
- package/dist/runtime/jxaShim.d.ts +21 -0
- package/dist/runtime/jxaShim.d.ts.map +1 -0
- package/dist/runtime/jxaShim.js +55 -0
- package/dist/runtime/jxaShim.js.map +1 -0
- package/dist/runtime/resultProtocol.d.ts +66 -0
- package/dist/runtime/resultProtocol.d.ts.map +1 -0
- package/dist/runtime/resultProtocol.js +52 -0
- package/dist/runtime/resultProtocol.js.map +1 -0
- package/dist/runtime/snippetLoader.d.ts +4 -0
- package/dist/runtime/snippetLoader.d.ts.map +1 -0
- package/dist/runtime/snippetLoader.js +68 -0
- package/dist/runtime/snippetLoader.js.map +1 -0
- package/dist/schemas/enums.d.ts +9 -0
- package/dist/schemas/enums.d.ts.map +1 -0
- package/dist/schemas/enums.js +26 -0
- package/dist/schemas/enums.js.map +1 -0
- package/dist/schemas/index.d.ts +3 -0
- package/dist/schemas/index.d.ts.map +1 -0
- package/dist/schemas/index.js +3 -0
- package/dist/schemas/index.js.map +1 -0
- package/dist/schemas/shapes.d.ts +726 -0
- package/dist/schemas/shapes.d.ts.map +1 -0
- package/dist/schemas/shapes.js +221 -0
- package/dist/schemas/shapes.js.map +1 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +50 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/completeProject.d.ts +24 -0
- package/dist/tools/completeProject.d.ts.map +1 -0
- package/dist/tools/completeProject.js +17 -0
- package/dist/tools/completeProject.js.map +1 -0
- package/dist/tools/completeTask.d.ts +25 -0
- package/dist/tools/completeTask.d.ts.map +1 -0
- package/dist/tools/completeTask.js +17 -0
- package/dist/tools/completeTask.js.map +1 -0
- package/dist/tools/createFolder.d.ts +20 -0
- package/dist/tools/createFolder.d.ts.map +1 -0
- package/dist/tools/createFolder.js +13 -0
- package/dist/tools/createFolder.js.map +1 -0
- package/dist/tools/createProject.d.ts +59 -0
- package/dist/tools/createProject.d.ts.map +1 -0
- package/dist/tools/createProject.js +13 -0
- package/dist/tools/createProject.js.map +1 -0
- package/dist/tools/createTag.d.ts +20 -0
- package/dist/tools/createTag.d.ts.map +1 -0
- package/dist/tools/createTag.js +13 -0
- package/dist/tools/createTag.js.map +1 -0
- package/dist/tools/createTask.d.ts +116 -0
- package/dist/tools/createTask.d.ts.map +1 -0
- package/dist/tools/createTask.js +13 -0
- package/dist/tools/createTask.js.map +1 -0
- package/dist/tools/deleteFolder.d.ts +30 -0
- package/dist/tools/deleteFolder.d.ts.map +1 -0
- package/dist/tools/deleteFolder.js +18 -0
- package/dist/tools/deleteFolder.js.map +1 -0
- package/dist/tools/deleteProject.d.ts +30 -0
- package/dist/tools/deleteProject.d.ts.map +1 -0
- package/dist/tools/deleteProject.js +18 -0
- package/dist/tools/deleteProject.js.map +1 -0
- package/dist/tools/deleteTag.d.ts +30 -0
- package/dist/tools/deleteTag.d.ts.map +1 -0
- package/dist/tools/deleteTag.js +18 -0
- package/dist/tools/deleteTag.js.map +1 -0
- package/dist/tools/deleteTask.d.ts +31 -0
- package/dist/tools/deleteTask.d.ts.map +1 -0
- package/dist/tools/deleteTask.js +18 -0
- package/dist/tools/deleteTask.js.map +1 -0
- package/dist/tools/dropProject.d.ts +24 -0
- package/dist/tools/dropProject.d.ts.map +1 -0
- package/dist/tools/dropProject.js +17 -0
- package/dist/tools/dropProject.js.map +1 -0
- package/dist/tools/dropTask.d.ts +25 -0
- package/dist/tools/dropTask.d.ts.map +1 -0
- package/dist/tools/dropTask.js +17 -0
- package/dist/tools/dropTask.js.map +1 -0
- package/dist/tools/editFolder.d.ts +20 -0
- package/dist/tools/editFolder.d.ts.map +1 -0
- package/dist/tools/editFolder.js +13 -0
- package/dist/tools/editFolder.js.map +1 -0
- package/dist/tools/editProject.d.ts +59 -0
- package/dist/tools/editProject.d.ts.map +1 -0
- package/dist/tools/editProject.js +13 -0
- package/dist/tools/editProject.js.map +1 -0
- package/dist/tools/editTag.d.ts +31 -0
- package/dist/tools/editTag.d.ts.map +1 -0
- package/dist/tools/editTag.js +13 -0
- package/dist/tools/editTag.js.map +1 -0
- package/dist/tools/editTask.d.ts +79 -0
- package/dist/tools/editTask.d.ts.map +1 -0
- package/dist/tools/editTask.js +13 -0
- package/dist/tools/editTask.js.map +1 -0
- package/dist/tools/getFolder.d.ts +24 -0
- package/dist/tools/getFolder.d.ts.map +1 -0
- package/dist/tools/getFolder.js +17 -0
- package/dist/tools/getFolder.js.map +1 -0
- package/dist/tools/getProject.d.ts +24 -0
- package/dist/tools/getProject.d.ts.map +1 -0
- package/dist/tools/getProject.js +17 -0
- package/dist/tools/getProject.js.map +1 -0
- package/dist/tools/getTag.d.ts +24 -0
- package/dist/tools/getTag.d.ts.map +1 -0
- package/dist/tools/getTag.js +17 -0
- package/dist/tools/getTag.js.map +1 -0
- package/dist/tools/getTask.d.ts +24 -0
- package/dist/tools/getTask.d.ts.map +1 -0
- package/dist/tools/getTask.js +17 -0
- package/dist/tools/getTask.js.map +1 -0
- package/dist/tools/index.d.ts +732 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +84 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/listFolders.d.ts +50 -0
- package/dist/tools/listFolders.d.ts.map +1 -0
- package/dist/tools/listFolders.js +21 -0
- package/dist/tools/listFolders.js.map +1 -0
- package/dist/tools/listProjects.d.ts +70 -0
- package/dist/tools/listProjects.d.ts.map +1 -0
- package/dist/tools/listProjects.js +21 -0
- package/dist/tools/listProjects.js.map +1 -0
- package/dist/tools/listTags.d.ts +50 -0
- package/dist/tools/listTags.d.ts.map +1 -0
- package/dist/tools/listTags.js +21 -0
- package/dist/tools/listTags.js.map +1 -0
- package/dist/tools/listTasks.d.ts +156 -0
- package/dist/tools/listTasks.d.ts.map +1 -0
- package/dist/tools/listTasks.js +36 -0
- package/dist/tools/listTasks.js.map +1 -0
- package/dist/tools/moveProject.d.ts +20 -0
- package/dist/tools/moveProject.d.ts.map +1 -0
- package/dist/tools/moveProject.js +13 -0
- package/dist/tools/moveProject.js.map +1 -0
- package/dist/tools/moveTask.d.ts +31 -0
- package/dist/tools/moveTask.d.ts.map +1 -0
- package/dist/tools/moveTask.js +13 -0
- package/dist/tools/moveTask.js.map +1 -0
- package/dist/tools/resolveName.d.ts +36 -0
- package/dist/tools/resolveName.d.ts.map +1 -0
- package/dist/tools/resolveName.js +26 -0
- package/dist/tools/resolveName.js.map +1 -0
- package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/.openspec.yaml +2 -0
- package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/design.md +162 -0
- package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/proposal.md +49 -0
- package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/specs/attachments/spec.md +9 -0
- package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/specs/batch-operations/spec.md +9 -0
- package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/specs/database-inspection/spec.md +9 -0
- package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/specs/execution-runtime/spec.md +69 -0
- package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/specs/folder-management/spec.md +25 -0
- package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/specs/forecast/spec.md +9 -0
- package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/specs/identity-resolution/spec.md +45 -0
- package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/specs/perspective-management/spec.md +9 -0
- package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/specs/project-management/spec.md +25 -0
- package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/specs/recurrence/spec.md +9 -0
- package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/specs/settings/spec.md +9 -0
- package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/specs/tag-management/spec.md +25 -0
- package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/specs/task-management/spec.md +29 -0
- package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/specs/url-automation/spec.md +9 -0
- package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/specs/window-state/spec.md +9 -0
- package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/tasks.md +84 -0
- package/openspec/changes/archive/2026-04-09-folder-crud/.openspec.yaml +2 -0
- package/openspec/changes/archive/2026-04-09-folder-crud/design.md +58 -0
- package/openspec/changes/archive/2026-04-09-folder-crud/proposal.md +28 -0
- package/openspec/changes/archive/2026-04-09-folder-crud/specs/folder-write/spec.md +45 -0
- package/openspec/changes/archive/2026-04-09-folder-crud/tasks.md +41 -0
- package/openspec/changes/archive/2026-04-09-folder-tag-list-filtering/.openspec.yaml +2 -0
- package/openspec/changes/archive/2026-04-09-folder-tag-list-filtering/design.md +38 -0
- package/openspec/changes/archive/2026-04-09-folder-tag-list-filtering/proposal.md +30 -0
- package/openspec/changes/archive/2026-04-09-folder-tag-list-filtering/specs/folder-management/spec.md +21 -0
- package/openspec/changes/archive/2026-04-09-folder-tag-list-filtering/specs/tag-management/spec.md +21 -0
- package/openspec/changes/archive/2026-04-09-folder-tag-list-filtering/tasks.md +35 -0
- package/openspec/changes/archive/2026-04-09-move-operations/.openspec.yaml +2 -0
- package/openspec/changes/archive/2026-04-09-move-operations/design.md +43 -0
- package/openspec/changes/archive/2026-04-09-move-operations/proposal.md +25 -0
- package/openspec/changes/archive/2026-04-09-move-operations/specs/move-operations/spec.md +41 -0
- package/openspec/changes/archive/2026-04-09-move-operations/tasks.md +40 -0
- package/openspec/changes/archive/2026-04-09-project-crud/.openspec.yaml +2 -0
- package/openspec/changes/archive/2026-04-09-project-crud/design.md +60 -0
- package/openspec/changes/archive/2026-04-09-project-crud/proposal.md +29 -0
- package/openspec/changes/archive/2026-04-09-project-crud/specs/project-write/spec.md +74 -0
- package/openspec/changes/archive/2026-04-09-project-crud/tasks.md +48 -0
- package/openspec/changes/archive/2026-04-09-project-filtering/.openspec.yaml +2 -0
- package/openspec/changes/archive/2026-04-09-project-filtering/design.md +52 -0
- package/openspec/changes/archive/2026-04-09-project-filtering/proposal.md +26 -0
- package/openspec/changes/archive/2026-04-09-project-filtering/specs/project-filtering/spec.md +66 -0
- package/openspec/changes/archive/2026-04-09-project-filtering/specs/project-management/spec.md +13 -0
- package/openspec/changes/archive/2026-04-09-project-filtering/tasks.md +41 -0
- package/openspec/changes/archive/2026-04-09-tag-crud/.openspec.yaml +2 -0
- package/openspec/changes/archive/2026-04-09-tag-crud/design.md +45 -0
- package/openspec/changes/archive/2026-04-09-tag-crud/proposal.md +28 -0
- package/openspec/changes/archive/2026-04-09-tag-crud/specs/tag-write/spec.md +49 -0
- package/openspec/changes/archive/2026-04-09-tag-crud/tasks.md +41 -0
- package/openspec/changes/archive/2026-04-09-task-crud/.openspec.yaml +2 -0
- package/openspec/changes/archive/2026-04-09-task-crud/design.md +62 -0
- package/openspec/changes/archive/2026-04-09-task-crud/proposal.md +29 -0
- package/openspec/changes/archive/2026-04-09-task-crud/specs/task-management/spec.md +17 -0
- package/openspec/changes/archive/2026-04-09-task-crud/specs/task-write/spec.md +89 -0
- package/openspec/changes/archive/2026-04-09-task-crud/tasks.md +55 -0
- package/openspec/changes/archive/2026-04-09-task-filtering/.openspec.yaml +2 -0
- package/openspec/changes/archive/2026-04-09-task-filtering/design.md +61 -0
- package/openspec/changes/archive/2026-04-09-task-filtering/proposal.md +26 -0
- package/openspec/changes/archive/2026-04-09-task-filtering/specs/task-filtering/spec.md +63 -0
- package/openspec/changes/archive/2026-04-09-task-filtering/specs/task-management/spec.md +17 -0
- package/openspec/changes/archive/2026-04-09-task-filtering/tasks.md +42 -0
- package/openspec/changes/archive/2026-04-10-planned-date/.openspec.yaml +2 -0
- package/openspec/changes/archive/2026-04-10-planned-date/design.md +27 -0
- package/openspec/changes/archive/2026-04-10-planned-date/proposal.md +29 -0
- package/openspec/changes/archive/2026-04-10-planned-date/specs/task-management/spec.md +29 -0
- package/openspec/changes/archive/2026-04-10-planned-date/specs/task-write/spec.md +69 -0
- package/openspec/changes/archive/2026-04-10-planned-date/tasks.md +26 -0
- package/openspec/changes/archive/2026-04-10-task-recurrence/.openspec.yaml +2 -0
- package/openspec/changes/archive/2026-04-10-task-recurrence/design.md +81 -0
- package/openspec/changes/archive/2026-04-10-task-recurrence/proposal.md +28 -0
- package/openspec/changes/archive/2026-04-10-task-recurrence/specs/recurrence/spec.md +47 -0
- package/openspec/changes/archive/2026-04-10-task-recurrence/specs/task-management/spec.md +25 -0
- package/openspec/changes/archive/2026-04-10-task-recurrence/specs/task-write/spec.md +61 -0
- package/openspec/changes/archive/2026-04-10-task-recurrence/tasks.md +39 -0
- package/openspec/config.yaml +20 -0
- package/openspec/specs/attachments/spec.md +15 -0
- package/openspec/specs/batch-operations/spec.md +15 -0
- package/openspec/specs/database-inspection/spec.md +15 -0
- package/openspec/specs/execution-runtime/spec.md +75 -0
- package/openspec/specs/folder-management/spec.md +39 -0
- package/openspec/specs/folder-write/spec.md +45 -0
- package/openspec/specs/forecast/spec.md +15 -0
- package/openspec/specs/identity-resolution/spec.md +51 -0
- package/openspec/specs/move-operations/spec.md +41 -0
- package/openspec/specs/perspective-management/spec.md +15 -0
- package/openspec/specs/project-filtering/spec.md +72 -0
- package/openspec/specs/project-management/spec.md +31 -0
- package/openspec/specs/project-write/spec.md +79 -0
- package/openspec/specs/recurrence/spec.md +51 -0
- package/openspec/specs/settings/spec.md +15 -0
- package/openspec/specs/tag-management/spec.md +39 -0
- package/openspec/specs/tag-write/spec.md +49 -0
- package/openspec/specs/task-filtering/spec.md +63 -0
- package/openspec/specs/task-management/spec.md +51 -0
- package/openspec/specs/task-write/spec.md +115 -0
- package/openspec/specs/url-automation/spec.md +15 -0
- package/openspec/specs/window-state/spec.md +15 -0
- package/package.json +32 -0
- package/scripts/cleanup-fixtures.ts +89 -0
- package/server.json +21 -0
- package/src/runtime/bridge.ts +97 -0
- package/src/runtime/index.ts +4 -0
- package/src/runtime/jxaShim.ts +55 -0
- package/src/runtime/resultProtocol.ts +62 -0
- package/src/runtime/snippetLoader.ts +79 -0
- package/src/schemas/enums.ts +32 -0
- package/src/schemas/index.ts +38 -0
- package/src/schemas/shapes.ts +267 -0
- package/src/server.ts +58 -0
- package/src/snippets/complete_project.js +73 -0
- package/src/snippets/complete_task.js +85 -0
- package/src/snippets/create_folder.js +52 -0
- package/src/snippets/create_project.js +107 -0
- package/src/snippets/create_tag.js +55 -0
- package/src/snippets/create_task.js +159 -0
- package/src/snippets/delete_folder.js +26 -0
- package/src/snippets/delete_project.js +20 -0
- package/src/snippets/delete_tag.js +20 -0
- package/src/snippets/delete_task.js +20 -0
- package/src/snippets/drop_project.js +73 -0
- package/src/snippets/drop_task.js +85 -0
- package/src/snippets/edit_folder.js +46 -0
- package/src/snippets/edit_project.js +106 -0
- package/src/snippets/edit_tag.js +56 -0
- package/src/snippets/edit_task.js +146 -0
- package/src/snippets/get_folder.js +48 -0
- package/src/snippets/get_project.js +77 -0
- package/src/snippets/get_tag.js +51 -0
- package/src/snippets/get_task.js +96 -0
- package/src/snippets/list_folders.js +50 -0
- package/src/snippets/list_projects.js +98 -0
- package/src/snippets/list_tags.js +54 -0
- package/src/snippets/list_tasks.js +127 -0
- package/src/snippets/move_project.js +79 -0
- package/src/snippets/move_task.js +113 -0
- package/src/snippets/resolve_name.js +83 -0
- package/src/tools/completeProject.ts +21 -0
- package/src/tools/completeTask.ts +23 -0
- package/src/tools/createFolder.ts +20 -0
- package/src/tools/createProject.ts +20 -0
- package/src/tools/createTag.ts +20 -0
- package/src/tools/createTask.ts +20 -0
- package/src/tools/deleteFolder.ts +24 -0
- package/src/tools/deleteProject.ts +24 -0
- package/src/tools/deleteTag.ts +24 -0
- package/src/tools/deleteTask.ts +26 -0
- package/src/tools/dropProject.ts +21 -0
- package/src/tools/dropTask.ts +23 -0
- package/src/tools/editFolder.ts +19 -0
- package/src/tools/editProject.ts +20 -0
- package/src/tools/editTag.ts +20 -0
- package/src/tools/editTask.ts +20 -0
- package/src/tools/getFolder.ts +24 -0
- package/src/tools/getProject.ts +24 -0
- package/src/tools/getTag.ts +24 -0
- package/src/tools/getTask.ts +24 -0
- package/src/tools/index.ts +85 -0
- package/src/tools/listFolders.ts +32 -0
- package/src/tools/listProjects.ts +32 -0
- package/src/tools/listTags.ts +32 -0
- package/src/tools/listTasks.ts +56 -0
- package/src/tools/moveProject.ts +20 -0
- package/src/tools/moveTask.ts +20 -0
- package/src/tools/resolveName.ts +37 -0
- package/test/integration/.gitkeep +0 -0
- package/test/integration/completeProject.int.test.ts +25 -0
- package/test/integration/completeTask.int.test.ts +30 -0
- package/test/integration/createFolder.int.test.ts +50 -0
- package/test/integration/createProject.int.test.ts +49 -0
- package/test/integration/createTag.int.test.ts +52 -0
- package/test/integration/createTask.int.test.ts +55 -0
- package/test/integration/deleteFolder.int.test.ts +64 -0
- package/test/integration/deleteProject.int.test.ts +31 -0
- package/test/integration/deleteTag.int.test.ts +61 -0
- package/test/integration/deleteTask.int.test.ts +36 -0
- package/test/integration/dropProject.int.test.ts +24 -0
- package/test/integration/dropTask.int.test.ts +29 -0
- package/test/integration/editFolder.int.test.ts +43 -0
- package/test/integration/editProject.int.test.ts +39 -0
- package/test/integration/editTag.int.test.ts +43 -0
- package/test/integration/editTask.int.test.ts +56 -0
- package/test/integration/fixtures.ts +219 -0
- package/test/integration/getTask.int.test.ts +64 -0
- package/test/integration/listFoldersFiltered.int.test.ts +98 -0
- package/test/integration/listProjects.int.test.ts +73 -0
- package/test/integration/listProjectsFiltered.int.test.ts +96 -0
- package/test/integration/listTagsFiltered.int.test.ts +54 -0
- package/test/integration/listTasksFiltered.int.test.ts +141 -0
- package/test/integration/moveProject.int.test.ts +57 -0
- package/test/integration/moveTask.int.test.ts +61 -0
- package/test/integration/plannedDate.int.test.ts +72 -0
- package/test/integration/preflight.ts +60 -0
- package/test/integration/resolveName.int.test.ts +86 -0
- package/test/integration/taskRecurrence.int.test.ts +106 -0
- package/test/unit/.gitkeep +0 -0
- package/test/unit/bridge.injection.test.ts +66 -0
- package/test/unit/resultProtocol.test.ts +71 -0
- package/test/unit/schemas.createFolder.test.ts +38 -0
- package/test/unit/schemas.createProject.test.ts +115 -0
- package/test/unit/schemas.createTask.test.ts +87 -0
- package/test/unit/schemas.editTag.test.ts +64 -0
- package/test/unit/schemas.folderTagFiltering.test.ts +42 -0
- package/test/unit/schemas.listProjects.test.ts +44 -0
- package/test/unit/schemas.moveOperations.test.ts +60 -0
- package/test/unit/schemas.recurrence.test.ts +120 -0
- package/test/unit/schemas.test.ts +126 -0
- package/test/unit/snippetLoader.test.ts +56 -0
- package/test/unit/tools.deleteTask.test.ts +19 -0
- package/test/unit/tools.listTasks.test.ts +126 -0
- package/tsconfig.json +19 -0
- package/vitest.config.ts +8 -0
- package/vitest.integration.config.ts +18 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# forecast
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
Covers OmniFocus Forecast data, including forecast days, tasks due or deferred on a given day, the configured forecast tag, and forecast badge counts. Individual tools will be defined in the `forecast-and-inspection` change.
|
|
6
|
+
|
|
7
|
+
## Requirements
|
|
8
|
+
|
|
9
|
+
### Requirement: Capability declared
|
|
10
|
+
|
|
11
|
+
The `forecast` capability SHALL cover OmniFocus Forecast data, including forecast days, tasks due or deferred on a given day, the configured forecast tag, and forecast badge counts. Requirements for individual tools SHALL be added by the `forecast-and-inspection` change.
|
|
12
|
+
|
|
13
|
+
#### Scenario: Capability is named and scoped
|
|
14
|
+
- **WHEN** a future change proposes adding forecast tools
|
|
15
|
+
- **THEN** it lands requirements under this capability rather than inventing a new capability name
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# identity-resolution
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
Defines how OmniFocus entities are addressed and resolved — canonical ID addressing, name/path-based lookup with ambiguity reporting, and the folder path format.
|
|
6
|
+
|
|
7
|
+
## Requirements
|
|
8
|
+
|
|
9
|
+
### Requirement: Canonical ID addressing
|
|
10
|
+
|
|
11
|
+
The system SHALL use `id.primaryKey` as the canonical identifier for every addressable OmniFocus entity (Task, Project, Folder, Tag, Perspective). Every read tool SHALL return an `id` field carrying this value. Every write tool (in this and future changes) SHALL accept an `id` parameter as its primary addressing mode.
|
|
12
|
+
|
|
13
|
+
#### Scenario: Read tools return primary key
|
|
14
|
+
- **WHEN** a caller invokes `list_projects`
|
|
15
|
+
- **THEN** every returned element has an `id` field equal to the OmniJS expression `project.id.primaryKey` for that project
|
|
16
|
+
|
|
17
|
+
#### Scenario: IDs are stable within a session
|
|
18
|
+
- **WHEN** a caller reads a project's ID via `list_projects`, then reads the same project again via `get_project` using that ID
|
|
19
|
+
- **THEN** the second read succeeds and returns the same project, without re-resolving by name
|
|
20
|
+
|
|
21
|
+
### Requirement: Name and path resolution with ambiguity reporting
|
|
22
|
+
|
|
23
|
+
The system SHALL provide a `resolve_name` tool that accepts `{type, query, scope?}` and returns a list of candidate entities `[{id, name, path, type, ...}]`. When more than one entity matches the query, the tool SHALL return all matches and SHALL NOT silently pick a winner. The caller is responsible for disambiguating.
|
|
24
|
+
|
|
25
|
+
#### Scenario: Unique match returns single candidate
|
|
26
|
+
- **WHEN** `resolve_name` is called with `{type: "project", query: "Q4 Planning"}` and exactly one project has that name
|
|
27
|
+
- **THEN** the tool returns a list with one element containing the project's id, name, and full folder path
|
|
28
|
+
|
|
29
|
+
#### Scenario: Ambiguous match returns all candidates
|
|
30
|
+
- **WHEN** `resolve_name` is called with `{type: "project", query: "Inbox Cleanup"}` and two projects have that name under different folders
|
|
31
|
+
- **THEN** the tool returns a list with both candidates, each carrying a distinct id and distinct folder path
|
|
32
|
+
|
|
33
|
+
#### Scenario: No match returns empty list
|
|
34
|
+
- **WHEN** `resolve_name` is called with a query that matches no entity of the requested type
|
|
35
|
+
- **THEN** the tool returns an empty list and does not throw an error
|
|
36
|
+
|
|
37
|
+
#### Scenario: Path-qualified query narrows scope
|
|
38
|
+
- **WHEN** `resolve_name` is called with `{type: "folder", query: "Acme", scope: "Work ▸ Clients"}`
|
|
39
|
+
- **THEN** the tool returns only folders named "Acme" that are direct or transitive children of the folder at path "Work ▸ Clients"
|
|
40
|
+
|
|
41
|
+
### Requirement: Folder path addressing
|
|
42
|
+
|
|
43
|
+
The system SHALL represent folder paths as strings using the separator ` ▸ ` (U+25B8 with surrounding spaces) and SHALL support resolving a folder by its full path as an alternative to its ID. When a path is ambiguous (the same path exists under multiple roots, which cannot occur for folders but can for tags), the resolution SHALL report the ambiguity through `resolve_name`.
|
|
44
|
+
|
|
45
|
+
#### Scenario: Folder resolves by full path
|
|
46
|
+
- **WHEN** `resolve_name` is called with `{type: "folder", query: "Work ▸ Clients ▸ Acme"}`
|
|
47
|
+
- **THEN** the tool returns the folder whose full ancestor chain matches that path exactly
|
|
48
|
+
|
|
49
|
+
#### Scenario: Returned paths use the canonical separator
|
|
50
|
+
- **WHEN** any read tool returns a folder path or a project's containing folder path
|
|
51
|
+
- **THEN** the path uses ` ▸ ` as the separator between ancestor names
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
## Requirements
|
|
2
|
+
|
|
3
|
+
### Requirement: Move task to a new container
|
|
4
|
+
|
|
5
|
+
The system SHALL provide a `move_task` tool that accepts `{id: string, projectId?: string, parentTaskId?: string}` and moves the specified task to the given container. Exactly one of `projectId` or `parentTaskId` SHALL be provided; if both or neither are given, the tool SHALL return a validation error. When `projectId` is provided, the task SHALL become a top-level task in that project. When `parentTaskId` is provided, the task SHALL become a direct subtask of that task. The tool SHALL return a structured not-found error if any of the IDs do not correspond to existing objects.
|
|
6
|
+
|
|
7
|
+
#### Scenario: Move task to a project
|
|
8
|
+
- **WHEN** `move_task` is called with `{id: "t1", projectId: "p2"}`
|
|
9
|
+
- **THEN** the task appears as a top-level task in project p2 and is no longer in its previous container
|
|
10
|
+
|
|
11
|
+
#### Scenario: Make task a subtask
|
|
12
|
+
- **WHEN** `move_task` is called with `{id: "t1", parentTaskId: "t2"}`
|
|
13
|
+
- **THEN** the task becomes a direct child of task t2
|
|
14
|
+
|
|
15
|
+
#### Scenario: Both destinations provided is a validation error
|
|
16
|
+
- **WHEN** `move_task` is called with both `projectId` and `parentTaskId`
|
|
17
|
+
- **THEN** the tool returns a validation error before any snippet executes
|
|
18
|
+
|
|
19
|
+
#### Scenario: Non-existent task ID returns not-found error
|
|
20
|
+
- **WHEN** `move_task` is called with an ID that does not correspond to any task
|
|
21
|
+
- **THEN** the tool returns a structured not-found error
|
|
22
|
+
|
|
23
|
+
### Requirement: Move project to a folder
|
|
24
|
+
|
|
25
|
+
The system SHALL provide a `move_project` tool that accepts `{id: string, folderId: string | null}` and moves the specified project to the given folder. When `folderId` is `null`, the project SHALL be moved to the top level (no containing folder). The tool SHALL return a structured not-found error if the project ID or folder ID does not correspond to an existing object.
|
|
26
|
+
|
|
27
|
+
#### Scenario: Move project to a folder
|
|
28
|
+
- **WHEN** `move_project` is called with `{id: "p1", folderId: "f2"}`
|
|
29
|
+
- **THEN** the project is now contained within folder f2
|
|
30
|
+
|
|
31
|
+
#### Scenario: Move project to top level
|
|
32
|
+
- **WHEN** `move_project` is called with `{id: "p1", folderId: null}`
|
|
33
|
+
- **THEN** the project has no parent folder
|
|
34
|
+
|
|
35
|
+
#### Scenario: Non-existent project ID returns not-found error
|
|
36
|
+
- **WHEN** `move_project` is called with a project ID that does not exist
|
|
37
|
+
- **THEN** the tool returns a structured not-found error
|
|
38
|
+
|
|
39
|
+
#### Scenario: Non-existent folder ID returns not-found error
|
|
40
|
+
- **WHEN** `move_project` is called with a folderId that does not correspond to any folder
|
|
41
|
+
- **THEN** the tool returns a structured not-found error
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# perspective-management
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
Covers listing built-in and custom perspectives, reading perspective metadata, and activating a perspective in a document window. Individual tools will be defined in the `perspectives-and-windows` change.
|
|
6
|
+
|
|
7
|
+
## Requirements
|
|
8
|
+
|
|
9
|
+
### Requirement: Capability declared
|
|
10
|
+
|
|
11
|
+
The `perspective-management` capability SHALL cover listing built-in and custom perspectives, reading perspective metadata, and activating a perspective in a document window. Custom perspective *creation and editing* are a non-goal pending verification of OmniJS scriptability of `Perspective.Custom` definitions; if a future investigation proves the API is available, the non-goal may be revisited by a later change. Requirements for individual tools SHALL be added by the `perspectives-and-windows` change.
|
|
12
|
+
|
|
13
|
+
#### Scenario: Capability is named and scoped
|
|
14
|
+
- **WHEN** a future change proposes adding perspective tools
|
|
15
|
+
- **THEN** it lands requirements under this capability rather than inventing a new capability name
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# project-filtering
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
Defines filtering and limiting behavior for the `list_projects` tool, including status, folder, and flagged filters, as well as result limits and enriched project summary fields.
|
|
6
|
+
|
|
7
|
+
## Requirements
|
|
8
|
+
|
|
9
|
+
### Requirement: Filter list_projects results
|
|
10
|
+
|
|
11
|
+
The system SHALL allow `list_projects` to accept an optional `filter` object that restricts which projects are returned. All filter fields are optional and combine as AND conditions. When `filter.status` is omitted, the tool SHALL exclude projects with status `done` or `dropped` by default.
|
|
12
|
+
|
|
13
|
+
Filter fields:
|
|
14
|
+
- `status` (array of ProjectStatus): return only projects whose status is in the array; overrides the default exclusion of done/dropped
|
|
15
|
+
- `folderId` (string): return only projects within the specified folder or any of its descendant folders; throws not-found if the folder ID does not exist
|
|
16
|
+
- `flagged` (boolean): when `true`, return only flagged projects
|
|
17
|
+
|
|
18
|
+
#### Scenario: Default excludes done and dropped projects
|
|
19
|
+
- **WHEN** `list_projects` is called with no filter
|
|
20
|
+
- **THEN** the tool returns only projects with status `active` or `onHold`
|
|
21
|
+
|
|
22
|
+
#### Scenario: Filter by status array
|
|
23
|
+
- **WHEN** `list_projects` is called with `{ filter: { status: ["active"] } }`
|
|
24
|
+
- **THEN** the tool returns only active projects
|
|
25
|
+
|
|
26
|
+
#### Scenario: Explicit status filter retrieves done projects
|
|
27
|
+
- **WHEN** `list_projects` is called with `{ filter: { status: ["done"] } }`
|
|
28
|
+
- **THEN** the tool returns completed projects (the default exclusion does not apply)
|
|
29
|
+
|
|
30
|
+
#### Scenario: Filter by folderId returns projects in subtree
|
|
31
|
+
- **WHEN** `list_projects` is called with `{ filter: { folderId: "abc123" } }`
|
|
32
|
+
- **THEN** the tool returns only projects whose parent folder chain includes the specified folder
|
|
33
|
+
|
|
34
|
+
#### Scenario: Non-existent folderId returns not-found error
|
|
35
|
+
- **WHEN** `list_projects` is called with a `folderId` that does not correspond to any folder
|
|
36
|
+
- **THEN** the tool returns a structured not-found error
|
|
37
|
+
|
|
38
|
+
#### Scenario: Filter by flagged
|
|
39
|
+
- **WHEN** `list_projects` is called with `{ filter: { flagged: true } }`
|
|
40
|
+
- **THEN** the tool returns only flagged projects, excluding done and dropped projects
|
|
41
|
+
|
|
42
|
+
#### Scenario: Combined filters act as AND
|
|
43
|
+
- **WHEN** `list_projects` is called with `{ filter: { status: ["active"], flagged: true } }`
|
|
44
|
+
- **THEN** the tool returns only projects that are both active AND flagged
|
|
45
|
+
|
|
46
|
+
### Requirement: Limit list_projects results
|
|
47
|
+
|
|
48
|
+
The system SHALL allow `list_projects` to accept an optional `limit` integer that caps the number of projects returned. When `limit` is omitted, a default of 100 SHALL apply.
|
|
49
|
+
|
|
50
|
+
#### Scenario: Default limit of 100
|
|
51
|
+
- **WHEN** `list_projects` is called without a `limit` and more than 100 projects match
|
|
52
|
+
- **THEN** the tool returns at most 100 projects
|
|
53
|
+
|
|
54
|
+
#### Scenario: Custom limit
|
|
55
|
+
- **WHEN** `list_projects` is called with `{ limit: 20 }`
|
|
56
|
+
- **THEN** the tool returns at most 20 projects
|
|
57
|
+
|
|
58
|
+
### Requirement: Enriched project summary includes flagged and folderId
|
|
59
|
+
|
|
60
|
+
The `ProjectSummary` returned by `list_projects` SHALL include `flagged` (boolean) and `folderId` (string or null) in addition to existing fields. `folderId` SHALL be the direct parent folder's `id.primaryKey`, or `null` for top-level projects.
|
|
61
|
+
|
|
62
|
+
#### Scenario: Summary includes flagged
|
|
63
|
+
- **WHEN** `list_projects` returns a flagged project
|
|
64
|
+
- **THEN** the project summary includes `flagged: true`
|
|
65
|
+
|
|
66
|
+
#### Scenario: Summary includes folderId for nested project
|
|
67
|
+
- **WHEN** `list_projects` returns a project inside a folder
|
|
68
|
+
- **THEN** the project summary includes `folderId` set to the parent folder's ID
|
|
69
|
+
|
|
70
|
+
#### Scenario: Summary includes folderId null for top-level project
|
|
71
|
+
- **WHEN** `list_projects` returns a top-level project with no containing folder
|
|
72
|
+
- **THEN** the project summary includes `folderId: null`
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# project-management
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
Defines tools for reading and listing OmniFocus projects, including full project listing and detail retrieval by ID.
|
|
6
|
+
|
|
7
|
+
## Requirements
|
|
8
|
+
|
|
9
|
+
### Requirement: List projects
|
|
10
|
+
|
|
11
|
+
The system SHALL provide a `list_projects` tool that returns projects in the OmniFocus database as an array of summaries, each containing `{id, name, folderPath, folderId, status, type, flagged}`. The `status` field SHALL be one of `"active" | "onHold" | "done" | "dropped"`. The `type` field SHALL be one of `"parallel" | "sequential" | "singleActions"`. The `folderPath` field SHALL use the canonical ` ▸ ` separator and SHALL be an empty string for top-level projects. The `folderId` field SHALL be the direct parent folder's ID or `null` for top-level projects. The tool SHALL accept an optional `filter` object (see project-filtering spec) and an optional `limit` integer (default 100). When no `filter.status` is provided, projects with status `done` or `dropped` SHALL be excluded by default.
|
|
12
|
+
|
|
13
|
+
#### Scenario: Active and on-hold projects returned by default
|
|
14
|
+
- **WHEN** `list_projects` is called with no arguments
|
|
15
|
+
- **THEN** the tool returns only projects with status `active` or `onHold`, each with id, name, folderPath, folderId, status, type, and flagged populated
|
|
16
|
+
|
|
17
|
+
#### Scenario: Single Actions list is reported with correct type
|
|
18
|
+
- **WHEN** the database contains a Single Actions list project
|
|
19
|
+
- **THEN** that project appears in the result with `type: "singleActions"`, never as `"parallel"` or `"sequential"`
|
|
20
|
+
|
|
21
|
+
### Requirement: Get project by ID
|
|
22
|
+
|
|
23
|
+
The system SHALL provide a `get_project` tool that accepts `{id: string}` and returns the full detail record of the named project, including `{id, name, note, folderPath, status, type, flagged, deferDate, dueDate, completionDate, reviewInterval, nextReviewDate, lastReviewDate, tagIds}`. If no project exists with that ID, the tool SHALL return a structured not-found error.
|
|
24
|
+
|
|
25
|
+
#### Scenario: Existing project returns full detail
|
|
26
|
+
- **WHEN** `get_project` is called with the ID of an existing project
|
|
27
|
+
- **THEN** the tool returns the project's full detail record
|
|
28
|
+
|
|
29
|
+
#### Scenario: Missing project returns not-found error
|
|
30
|
+
- **WHEN** `get_project` is called with an ID that does not correspond to any project
|
|
31
|
+
- **THEN** the tool returns a structured error with a not-found code
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# Spec: Project Write
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
Provides MCP tools for creating and mutating OmniFocus projects: creating new projects, editing project properties, marking projects complete or dropped, and permanently deleting projects.
|
|
6
|
+
|
|
7
|
+
## Requirements
|
|
8
|
+
|
|
9
|
+
### Requirement: Create project
|
|
10
|
+
|
|
11
|
+
The system SHALL provide a `create_project` tool that creates a new OmniFocus project and returns its full detail record. The tool SHALL accept `{name: string, folderId?: string, note?: string, type?: "parallel" | "sequential" | "singleActions", status?: "active" | "onHold", flagged?: boolean, deferDate?: string, dueDate?: string, reviewInterval?: {steps: number, unit: "days" | "weeks" | "months" | "years"}, tagIds?: string[]}`. If `folderId` is provided the project SHALL be created inside that folder; otherwise it SHALL be created at the top level.
|
|
12
|
+
|
|
13
|
+
#### Scenario: Create top-level project
|
|
14
|
+
- **WHEN** `create_project` is called with `{name: "My Project"}` and no `folderId`
|
|
15
|
+
- **THEN** the tool creates the project at the top level and returns its full detail record including a stable `id`
|
|
16
|
+
|
|
17
|
+
#### Scenario: Create project inside a folder
|
|
18
|
+
- **WHEN** `create_project` is called with `{name: "My Project", folderId: "abc123"}`
|
|
19
|
+
- **THEN** the tool creates the project inside the specified folder and returns its full detail record with `folderPath` reflecting the folder
|
|
20
|
+
|
|
21
|
+
#### Scenario: Create sequential project
|
|
22
|
+
- **WHEN** `create_project` is called with `{name: "My Project", type: "sequential"}`
|
|
23
|
+
- **THEN** the returned project detail includes `type: "sequential"`
|
|
24
|
+
|
|
25
|
+
#### Scenario: Non-existent folder returns not-found error
|
|
26
|
+
- **WHEN** `create_project` is called with a `folderId` that does not correspond to any folder
|
|
27
|
+
- **THEN** the tool returns a structured not-found error
|
|
28
|
+
|
|
29
|
+
### Requirement: Edit project
|
|
30
|
+
|
|
31
|
+
The system SHALL provide an `edit_project` tool that modifies an existing project and returns its updated full detail record. The tool SHALL accept `{id: string}` plus any subset of `{name?: string, note?: string, type?: "parallel" | "sequential" | "singleActions", status?: "active" | "onHold", flagged?: boolean, deferDate?: string | null, dueDate?: string | null, reviewInterval?: {steps: number, unit: "days" | "weeks" | "months" | "years"}, tagIds?: string[]}`. Fields omitted from the call SHALL be left unchanged. When `tagIds` is provided it SHALL replace the project's entire tag set. Passing `null` for a date SHALL clear the field. When `reviewInterval` is provided, only the `steps` value is updated; the `unit` field is accepted by the schema but cannot be changed at runtime due to OmniJS API constraints in the `evaluateJavascript` context.
|
|
32
|
+
|
|
33
|
+
#### Scenario: Put project on hold
|
|
34
|
+
- **WHEN** `edit_project` is called with `{id: "abc123", status: "onHold"}`
|
|
35
|
+
- **THEN** the project's status becomes `"onHold"` and all other fields are unchanged
|
|
36
|
+
|
|
37
|
+
#### Scenario: Set review interval
|
|
38
|
+
- **WHEN** `edit_project` is called with `{id: "abc123", reviewInterval: {steps: 2, unit: "weeks"}}`
|
|
39
|
+
- **THEN** the project's review interval is updated and the returned detail reflects the change
|
|
40
|
+
|
|
41
|
+
#### Scenario: Non-existent project returns not-found error
|
|
42
|
+
- **WHEN** `edit_project` is called with an ID that does not correspond to any project
|
|
43
|
+
- **THEN** the tool returns a structured not-found error
|
|
44
|
+
|
|
45
|
+
### Requirement: Complete project
|
|
46
|
+
|
|
47
|
+
The system SHALL provide a `complete_project` tool that marks an existing project done using OmniJS `markComplete()` and returns the project's updated full detail record.
|
|
48
|
+
|
|
49
|
+
#### Scenario: Complete an existing project
|
|
50
|
+
- **WHEN** `complete_project` is called with the ID of an active project
|
|
51
|
+
- **THEN** the project's status becomes `"done"` and the tool returns the updated detail record
|
|
52
|
+
|
|
53
|
+
#### Scenario: Non-existent project returns not-found error
|
|
54
|
+
- **WHEN** `complete_project` is called with an ID that does not correspond to any project
|
|
55
|
+
- **THEN** the tool returns a structured not-found error
|
|
56
|
+
|
|
57
|
+
### Requirement: Drop project
|
|
58
|
+
|
|
59
|
+
The system SHALL provide a `drop_project` tool that marks an existing project dropped using OmniJS `drop()` and returns the project's updated full detail record.
|
|
60
|
+
|
|
61
|
+
#### Scenario: Drop an existing project
|
|
62
|
+
- **WHEN** `drop_project` is called with the ID of an active project
|
|
63
|
+
- **THEN** the project's status becomes `"dropped"` and the tool returns the updated detail record
|
|
64
|
+
|
|
65
|
+
#### Scenario: Non-existent project returns not-found error
|
|
66
|
+
- **WHEN** `drop_project` is called with an ID that does not correspond to any project
|
|
67
|
+
- **THEN** the tool returns a structured not-found error
|
|
68
|
+
|
|
69
|
+
### Requirement: Delete project
|
|
70
|
+
|
|
71
|
+
The system SHALL provide a `delete_project` tool that permanently deletes a project and all its tasks using OmniJS `deleteObject()`. The tool description SHALL instruct the AI to confirm with the user before invoking this tool, noting that deletion is permanent and removes all tasks within the project.
|
|
72
|
+
|
|
73
|
+
#### Scenario: Delete an existing project
|
|
74
|
+
- **WHEN** `delete_project` is called with the ID of an existing project
|
|
75
|
+
- **THEN** the project and all its tasks are permanently removed from OmniFocus and the tool returns a confirmation envelope
|
|
76
|
+
|
|
77
|
+
#### Scenario: Non-existent project returns not-found error
|
|
78
|
+
- **WHEN** `delete_project` is called with an ID that does not correspond to any project
|
|
79
|
+
- **THEN** the tool returns a structured not-found error
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# recurrence
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
Covers the construction, assignment, reading, and clearing of `Task.RepetitionRule` values on OmniFocus tasks.
|
|
6
|
+
|
|
7
|
+
## Requirements
|
|
8
|
+
|
|
9
|
+
### Requirement: Capability declared
|
|
10
|
+
|
|
11
|
+
The `recurrence` capability covers the construction, assignment, reading, and clearing of `Task.RepetitionRule` values on OmniFocus tasks. The capability exposes a structured schema matching the OmniFocus UI surface: `frequency` (`daily` | `weekly` | `monthly` | `yearly`), `interval` (every N units, ≥1), `daysOfWeek` (weekly only, any subset of the 7 days), and `method` (`fixed` | `dueDate` | `start`). Repetition is set and cleared through `create_task` and `edit_task`; the current rule is returned by `get_task` as structured fields parsed from the underlying RRULE. Raw RRULE strings are not exposed as input — all recurrence is expressed through the structured schema.
|
|
12
|
+
|
|
13
|
+
#### Scenario: Capability is named and scoped
|
|
14
|
+
- **WHEN** a change proposes adding or modifying recurrence-related behavior
|
|
15
|
+
- **THEN** it lands requirements under this capability
|
|
16
|
+
|
|
17
|
+
### Requirement: Set repetition rule on task
|
|
18
|
+
|
|
19
|
+
The system SHALL accept a `repetitionRule` field in `create_task` and `edit_task`. When provided, the snippet SHALL construct an ICS RRULE string from the structured fields and assign it via `new Task.RepetitionRule(rrule, method)`. The `daysOfWeek` field SHALL only be valid when `frequency` is `"weekly"`; providing it for any other frequency SHALL be a validation error. When `edit_task` receives `repetitionRule: null`, the snippet SHALL assign `task.repetitionRule = null` to clear the rule. When `edit_task` omits `repetitionRule` entirely, the existing rule SHALL be left unchanged.
|
|
20
|
+
|
|
21
|
+
#### Scenario: Set daily repetition at creation
|
|
22
|
+
- **WHEN** `create_task` is called with `{ name: "Stand-up", repetitionRule: { frequency: "daily", interval: 1, method: "fixed" } }`
|
|
23
|
+
- **THEN** the created task has a repetition rule of every day (fixed interval) and the returned TaskDetail includes the parsed repetitionRule
|
|
24
|
+
|
|
25
|
+
#### Scenario: Set weekly repetition on specific days
|
|
26
|
+
- **WHEN** `create_task` is called with `{ repetitionRule: { frequency: "weekly", interval: 1, daysOfWeek: ["monday", "wednesday", "friday"], method: "start" } }`
|
|
27
|
+
- **THEN** the task repeats every Mon/Wed/Fri from completion date
|
|
28
|
+
|
|
29
|
+
#### Scenario: Edit task to add repetition
|
|
30
|
+
- **WHEN** `edit_task` is called with `{ id: "t1", repetitionRule: { frequency: "monthly", interval: 1, method: "dueDate" } }`
|
|
31
|
+
- **THEN** the task's repetition rule is set to monthly (due date) and all other fields are unchanged
|
|
32
|
+
|
|
33
|
+
#### Scenario: Clear repetition via null
|
|
34
|
+
- **WHEN** `edit_task` is called with `{ id: "t1", repetitionRule: null }`
|
|
35
|
+
- **THEN** the task's repetition rule is cleared and `get_task` returns `repetitionRule: null` for that task
|
|
36
|
+
|
|
37
|
+
#### Scenario: daysOfWeek on non-weekly frequency is a validation error
|
|
38
|
+
- **WHEN** `create_task` or `edit_task` is called with `{ repetitionRule: { frequency: "daily", daysOfWeek: ["monday"], method: "fixed" } }`
|
|
39
|
+
- **THEN** the tool returns a validation error before any snippet executes
|
|
40
|
+
|
|
41
|
+
### Requirement: Return repetition rule from get_task
|
|
42
|
+
|
|
43
|
+
The system SHALL include a `repetitionRule` field in the `TaskDetail` returned by `get_task`. When a task has no repetition rule, the field SHALL be `null`. When a task has a rule, the field SHALL be a structured object with `frequency`, `interval`, `daysOfWeek` (omitted when not applicable), and `method`, parsed from the underlying RRULE and RepetitionMethod.
|
|
44
|
+
|
|
45
|
+
#### Scenario: get_task returns null when no repetition set
|
|
46
|
+
- **WHEN** `get_task` is called for a task with no repetition rule
|
|
47
|
+
- **THEN** the returned TaskDetail includes `repetitionRule: null`
|
|
48
|
+
|
|
49
|
+
#### Scenario: get_task returns structured repetition fields
|
|
50
|
+
- **WHEN** `get_task` is called for a task with a weekly repetition rule
|
|
51
|
+
- **THEN** the returned TaskDetail includes `repetitionRule` with `frequency: "weekly"`, correct `interval`, `daysOfWeek` array, and `method`
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# settings
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
Covers reading OmniFocus application settings and preferences via the OmniJS API. Individual tools and the enumeration of writable keys will be defined in the `settings` change.
|
|
6
|
+
|
|
7
|
+
## Requirements
|
|
8
|
+
|
|
9
|
+
### Requirement: Capability declared
|
|
10
|
+
|
|
11
|
+
The `settings` capability SHALL cover reading OmniFocus application settings and preferences where the OmniJS API exposes them. Write access SHALL be limited to the subset of settings that OmniJS documents as writable; the specific writable keys SHALL be enumerated when the `settings` change is drafted. Requirements for individual tools SHALL be added by that change.
|
|
12
|
+
|
|
13
|
+
#### Scenario: Capability is named and scoped
|
|
14
|
+
- **WHEN** a future change proposes adding settings tools
|
|
15
|
+
- **THEN** it lands requirements under this capability, with an explicit enumeration of writable keys vs read-only keys
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# tag-management
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
Defines tools for reading and listing OmniFocus tags, including hierarchical tag listing and detail retrieval by ID.
|
|
6
|
+
|
|
7
|
+
## Requirements
|
|
8
|
+
|
|
9
|
+
### Requirement: List tags
|
|
10
|
+
|
|
11
|
+
The system SHALL provide a `list_tags` tool that returns tags in the OmniFocus database as an array of summaries, each containing `{id, name, path, parentId, status}`. Tags form a tree in OmniFocus; the `path` field SHALL use the canonical ` ▸ ` separator and represent the full ancestor chain. The `parentId` field SHALL be `null` for top-level tags. The `status` field SHALL be one of `"active" | "onHold" | "dropped"`. The tool SHALL accept an optional `status` filter string and an optional `limit` integer (default 200). When `status` is omitted, all tags are returned regardless of status. When `limit` is omitted, at most 200 tags are returned.
|
|
12
|
+
|
|
13
|
+
#### Scenario: All tags returned by default
|
|
14
|
+
- **WHEN** `list_tags` is called with no arguments
|
|
15
|
+
- **THEN** the tool returns all tags (active, onHold, and dropped) up to the limit
|
|
16
|
+
|
|
17
|
+
#### Scenario: On-hold tag is reported with correct status
|
|
18
|
+
- **WHEN** the database contains a tag that has been placed on hold
|
|
19
|
+
- **THEN** that tag appears in the result with `status: "onHold"`
|
|
20
|
+
|
|
21
|
+
#### Scenario: Filter by status returns only matching tags
|
|
22
|
+
- **WHEN** `list_tags` is called with `{ filter: { status: "active" } }`
|
|
23
|
+
- **THEN** only active tags are returned
|
|
24
|
+
|
|
25
|
+
#### Scenario: Limit caps the number of returned tags
|
|
26
|
+
- **WHEN** `list_tags` is called with `{ limit: 5 }`
|
|
27
|
+
- **THEN** at most 5 tags are returned
|
|
28
|
+
|
|
29
|
+
### Requirement: Get tag by ID
|
|
30
|
+
|
|
31
|
+
The system SHALL provide a `get_tag` tool that accepts `{id: string}` and returns the full detail record of the named tag, including `{id, name, path, parentId, status, childTagIds}`. If no tag exists with that ID, the tool SHALL return a structured not-found error.
|
|
32
|
+
|
|
33
|
+
#### Scenario: Existing tag returns full detail
|
|
34
|
+
- **WHEN** `get_tag` is called with the ID of an existing tag
|
|
35
|
+
- **THEN** the tool returns the tag's full detail including the IDs of its immediate child tags
|
|
36
|
+
|
|
37
|
+
#### Scenario: Missing tag returns not-found error
|
|
38
|
+
- **WHEN** `get_tag` is called with an ID that does not correspond to any tag
|
|
39
|
+
- **THEN** the tool returns a structured error with a not-found code
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
## ADDED Requirements
|
|
2
|
+
|
|
3
|
+
### Requirement: Create tag
|
|
4
|
+
|
|
5
|
+
The system SHALL provide a `create_tag` tool that creates a new OmniFocus tag and returns its full detail record. The tool SHALL accept `{name: string, parentTagId?: string}`. If `parentTagId` is provided the tag SHALL be created nested under that tag; otherwise it SHALL be created at the top level.
|
|
6
|
+
|
|
7
|
+
#### Scenario: Create top-level tag
|
|
8
|
+
- **WHEN** `create_tag` is called with `{name: "Waiting"}` and no `parentTagId`
|
|
9
|
+
- **THEN** the tool creates the tag at the top level and returns its full detail record including a stable `id`
|
|
10
|
+
|
|
11
|
+
#### Scenario: Create child tag
|
|
12
|
+
- **WHEN** `create_tag` is called with `{name: "Email", parentTagId: "abc123"}`
|
|
13
|
+
- **THEN** the tool creates the tag nested under the specified parent tag and returns its full detail record with `path` and `parentId` set correctly
|
|
14
|
+
|
|
15
|
+
#### Scenario: Non-existent parent tag returns not-found error
|
|
16
|
+
- **WHEN** `create_tag` is called with a `parentTagId` that does not correspond to any tag
|
|
17
|
+
- **THEN** the tool returns a structured not-found error
|
|
18
|
+
|
|
19
|
+
### Requirement: Edit tag
|
|
20
|
+
|
|
21
|
+
The system SHALL provide an `edit_tag` tool that modifies an existing tag and returns its updated full detail record. The tool SHALL accept `{id: string}` plus any subset of `{name?: string, status?: "active" | "onHold" | "dropped"}`. Fields omitted SHALL be left unchanged.
|
|
22
|
+
|
|
23
|
+
#### Scenario: Rename a tag
|
|
24
|
+
- **WHEN** `edit_tag` is called with `{id: "abc123", name: "Delegated"}`
|
|
25
|
+
- **THEN** the tag's name is updated and the tool returns the updated detail record
|
|
26
|
+
|
|
27
|
+
#### Scenario: Put tag on hold
|
|
28
|
+
- **WHEN** `edit_tag` is called with `{id: "abc123", status: "onHold"}`
|
|
29
|
+
- **THEN** the tag's status becomes `"onHold"` and the tool returns the updated detail record
|
|
30
|
+
|
|
31
|
+
#### Scenario: Non-existent tag returns not-found error
|
|
32
|
+
- **WHEN** `edit_tag` is called with an ID that does not correspond to any tag
|
|
33
|
+
- **THEN** the tool returns a structured not-found error
|
|
34
|
+
|
|
35
|
+
### Requirement: Delete tag
|
|
36
|
+
|
|
37
|
+
The system SHALL provide a `delete_tag` tool that permanently deletes a tag and all its child tags using OmniJS `deleteObject()`. Tasks and projects that held the tag have it removed automatically by OmniFocus. The tool description SHALL instruct the AI to confirm with the user before invoking, noting that child tags are also deleted.
|
|
38
|
+
|
|
39
|
+
#### Scenario: Delete a tag
|
|
40
|
+
- **WHEN** `delete_tag` is called with the ID of an existing tag
|
|
41
|
+
- **THEN** the tag is permanently removed from OmniFocus, all tasks that held it have it removed, and the tool returns a confirmation envelope
|
|
42
|
+
|
|
43
|
+
#### Scenario: Delete tag with children removes entire subtree
|
|
44
|
+
- **WHEN** `delete_tag` is called with the ID of a tag that has child tags
|
|
45
|
+
- **THEN** the tag and all its descendant tags are deleted
|
|
46
|
+
|
|
47
|
+
#### Scenario: Non-existent tag returns not-found error
|
|
48
|
+
- **WHEN** `delete_tag` is called with an ID that does not correspond to any tag
|
|
49
|
+
- **THEN** the tool returns a structured not-found error
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
## ADDED Requirements
|
|
2
|
+
|
|
3
|
+
### Requirement: Filter list_tasks results
|
|
4
|
+
|
|
5
|
+
The system SHALL allow `list_tasks` to accept an optional `filter` object that restricts which tasks are returned. All filter fields are optional and combine as AND conditions. When `filter.status` is omitted, the tool SHALL exclude tasks with status `complete` or `dropped` by default.
|
|
6
|
+
|
|
7
|
+
Filter fields:
|
|
8
|
+
- `flagged` (boolean): when `true`, return only flagged tasks
|
|
9
|
+
- `status` (array of TaskStatus): return only tasks whose status is in the array; overrides the default exclusion of complete/dropped
|
|
10
|
+
- `tagId` (string): return only tasks that have this tag assigned
|
|
11
|
+
- `dueBeforeDate` (ISO datetime string): return only tasks where `dueDate` is non-null and on or before this date
|
|
12
|
+
|
|
13
|
+
#### Scenario: Filter by flagged
|
|
14
|
+
- **WHEN** `list_tasks` is called with `{ scope: { all: true }, filter: { flagged: true } }`
|
|
15
|
+
- **THEN** the tool returns only tasks where `flagged` is `true`, excluding complete and dropped tasks
|
|
16
|
+
|
|
17
|
+
#### Scenario: Filter by status array
|
|
18
|
+
- **WHEN** `list_tasks` is called with `{ scope: { all: true }, filter: { status: ["overdue", "dueSoon"] } }`
|
|
19
|
+
- **THEN** the tool returns only tasks with status `overdue` or `dueSoon`
|
|
20
|
+
|
|
21
|
+
#### Scenario: Filter by tagId
|
|
22
|
+
- **WHEN** `list_tasks` is called with `{ scope: { projectId: "abc123" }, filter: { tagId: "tag456" } }`
|
|
23
|
+
- **THEN** the tool returns only tasks in that project that have the specified tag assigned
|
|
24
|
+
|
|
25
|
+
#### Scenario: Filter by dueBeforeDate
|
|
26
|
+
- **WHEN** `list_tasks` is called with `{ scope: { all: true }, filter: { dueBeforeDate: "2026-04-09T23:59:59Z" } }`
|
|
27
|
+
- **THEN** the tool returns only tasks with a non-null dueDate on or before the specified date, excluding complete and dropped tasks
|
|
28
|
+
|
|
29
|
+
#### Scenario: Default excludes complete and dropped
|
|
30
|
+
- **WHEN** `list_tasks` is called with no filter
|
|
31
|
+
- **THEN** the tool returns tasks with any status except `complete` and `dropped`
|
|
32
|
+
|
|
33
|
+
#### Scenario: Explicit status filter overrides default exclusion
|
|
34
|
+
- **WHEN** `list_tasks` is called with `{ filter: { status: ["complete"] } }`
|
|
35
|
+
- **THEN** the tool returns completed tasks (the default exclusion does not apply)
|
|
36
|
+
|
|
37
|
+
#### Scenario: Combined filters act as AND
|
|
38
|
+
- **WHEN** `list_tasks` is called with `{ filter: { flagged: true, tagId: "tag456" } }`
|
|
39
|
+
- **THEN** the tool returns only tasks that are both flagged AND have that tag
|
|
40
|
+
|
|
41
|
+
### Requirement: Limit list_tasks results
|
|
42
|
+
|
|
43
|
+
The system SHALL allow `list_tasks` to accept an optional `limit` integer that caps the number of tasks returned. When `limit` is omitted, a default of 200 SHALL apply.
|
|
44
|
+
|
|
45
|
+
#### Scenario: Default limit of 200
|
|
46
|
+
- **WHEN** `list_tasks` is called without a `limit` and more than 200 tasks match
|
|
47
|
+
- **THEN** the tool returns at most 200 tasks
|
|
48
|
+
|
|
49
|
+
#### Scenario: Custom limit
|
|
50
|
+
- **WHEN** `list_tasks` is called with `{ limit: 50 }`
|
|
51
|
+
- **THEN** the tool returns at most 50 tasks
|
|
52
|
+
|
|
53
|
+
### Requirement: Enriched task summary includes dueDate and tagIds
|
|
54
|
+
|
|
55
|
+
The `TaskSummary` returned by `list_tasks` SHALL include `dueDate` (ISO datetime or null) and `tagIds` (array of tag ID strings) in addition to existing fields.
|
|
56
|
+
|
|
57
|
+
#### Scenario: Summary includes dueDate
|
|
58
|
+
- **WHEN** `list_tasks` returns a task that has a due date set
|
|
59
|
+
- **THEN** the task summary includes `dueDate` as an ISO datetime string
|
|
60
|
+
|
|
61
|
+
#### Scenario: Summary includes tagIds
|
|
62
|
+
- **WHEN** `list_tasks` returns a task that has tags assigned
|
|
63
|
+
- **THEN** the task summary includes `tagIds` as an array of tag ID strings
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# task-management
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
Defines tools for reading and listing OmniFocus tasks, including scoped listing and full detail retrieval by ID.
|
|
6
|
+
|
|
7
|
+
## Requirements
|
|
8
|
+
|
|
9
|
+
### Requirement: List tasks with scope filter
|
|
10
|
+
|
|
11
|
+
The system SHALL provide a `list_tasks` tool that returns tasks within a caller-specified scope. The scope SHALL be one of: `{projectId: string}`, `{folderId: string}` (all tasks in projects under that folder, recursively), `{inbox: true}`, or `{all: true}` (the full flattened task list). The tool SHALL accept an optional `filter` object (see task-filtering spec) and an optional `limit` integer (default 200). The tool SHALL return an array of task summaries, each containing `{id, name, status, flagged, containerId, containerType, dueDate, tagIds}`. When no `filter.status` is provided, tasks with status `complete` or `dropped` SHALL be excluded by default.
|
|
12
|
+
|
|
13
|
+
#### Scenario: List tasks in a specific project
|
|
14
|
+
- **WHEN** `list_tasks` is called with `{projectId: "abc123"}`
|
|
15
|
+
- **THEN** the tool returns actionable tasks (non-complete, non-dropped) directly or transitively contained in that project, each carrying the project id as `containerId` and `"project"` as `containerType`
|
|
16
|
+
|
|
17
|
+
#### Scenario: List inbox tasks
|
|
18
|
+
- **WHEN** `list_tasks` is called with `{inbox: true}`
|
|
19
|
+
- **THEN** the tool returns actionable inbox tasks with `containerType` set to `"inbox"`
|
|
20
|
+
|
|
21
|
+
#### Scenario: Invalid scope is rejected at the TS boundary
|
|
22
|
+
- **WHEN** `list_tasks` is called with `{projectId: "abc", inbox: true}` (mutually exclusive scopes)
|
|
23
|
+
- **THEN** the tool returns a validation error before any snippet executes
|
|
24
|
+
|
|
25
|
+
### Requirement: Get task by ID
|
|
26
|
+
|
|
27
|
+
The system SHALL provide a `get_task` tool that accepts `{id: string}` and returns the full detail record of the named task, including `{id, name, note, status, flagged, deferDate, plannedDate, dueDate, completionDate, estimatedMinutes, containerId, containerType, tagIds, parentTaskId, repetitionRule}`. The `plannedDate` field SHALL be `null` when no planned date is set. The `parentTaskId` field SHALL be `null` for top-level tasks and SHALL contain the parent task's `id.primaryKey` for subtasks. The `repetitionRule` field SHALL be `null` when no repetition rule is set, and SHALL be a structured object with `{frequency, interval, daysOfWeek?, method}` when a rule exists. If no task exists with that ID, the tool SHALL return a structured not-found error.
|
|
28
|
+
|
|
29
|
+
#### Scenario: Existing task returns full detail
|
|
30
|
+
- **WHEN** `get_task` is called with the ID of an existing task
|
|
31
|
+
- **THEN** the tool returns the task's full detail record including all scalar fields, the list of tag IDs assigned to it, `parentTaskId`, `repetitionRule`, and `plannedDate`
|
|
32
|
+
|
|
33
|
+
#### Scenario: Subtask includes parentTaskId
|
|
34
|
+
- **WHEN** `get_task` is called with the ID of a subtask
|
|
35
|
+
- **THEN** the returned record includes `parentTaskId` set to the parent task's stable ID
|
|
36
|
+
|
|
37
|
+
#### Scenario: Task without repetition returns null repetitionRule
|
|
38
|
+
- **WHEN** `get_task` is called for a task with no repetition rule
|
|
39
|
+
- **THEN** the returned TaskDetail includes `repetitionRule: null`
|
|
40
|
+
|
|
41
|
+
#### Scenario: Task with repetition returns structured repetitionRule
|
|
42
|
+
- **WHEN** `get_task` is called for a task that has a repetition rule
|
|
43
|
+
- **THEN** the returned TaskDetail includes `repetitionRule` with `frequency`, `interval`, `method`, and `daysOfWeek` (if applicable)
|
|
44
|
+
|
|
45
|
+
#### Scenario: Task with planned date returns plannedDate
|
|
46
|
+
- **WHEN** `get_task` is called for a task that has a planned date set
|
|
47
|
+
- **THEN** the returned TaskDetail includes `plannedDate` as an ISO datetime string
|
|
48
|
+
|
|
49
|
+
#### Scenario: Missing task returns not-found error
|
|
50
|
+
- **WHEN** `get_task` is called with an ID that does not correspond to any task
|
|
51
|
+
- **THEN** the tool returns a structured error with a not-found code and does not throw an unhandled exception
|