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