@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,76 @@
|
|
|
1
|
+
import { spawn } from "child_process";
|
|
2
|
+
import { loadSnippet } from "./snippetLoader.js";
|
|
3
|
+
import { buildJxaScript } from "./jxaShim.js";
|
|
4
|
+
import { parseResultLine, ExecutionError } from "./resultProtocol.js";
|
|
5
|
+
const DEFAULT_TIMEOUT_MS = 30_000;
|
|
6
|
+
/**
|
|
7
|
+
* Execute an OmniJS snippet inside OmniFocus via the JXA bridge.
|
|
8
|
+
*
|
|
9
|
+
* @param name - Snippet filename (without .js extension) under src/snippets/
|
|
10
|
+
* @param args - Arguments to inject. Embedded as a JSON literal; safe for
|
|
11
|
+
* all unicode, apostrophes, quotes, etc. (Design Decision 2).
|
|
12
|
+
* @param opts - Optional timeout override (default 30s)
|
|
13
|
+
* @returns The `data` field from the success envelope
|
|
14
|
+
* @throws ExecutionError if the snippet throws inside OmniJS
|
|
15
|
+
* @throws Error on timeout, process errors, or protocol violations
|
|
16
|
+
*/
|
|
17
|
+
export async function runSnippet(name, args, opts = {}) {
|
|
18
|
+
const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
19
|
+
// Load and inject args — the only interpolation allowed (Decision 2)
|
|
20
|
+
const template = loadSnippet(name);
|
|
21
|
+
const snippet = template.replace("__ARGS__", JSON.stringify(args));
|
|
22
|
+
// Wrap snippet in JXA shim
|
|
23
|
+
const jxaScript = buildJxaScript(snippet);
|
|
24
|
+
return new Promise((resolve, reject) => {
|
|
25
|
+
const MAX_OUTPUT_BYTES = 10 * 1024 * 1024; // 10 MB
|
|
26
|
+
const ac = new AbortController();
|
|
27
|
+
const timer = setTimeout(() => {
|
|
28
|
+
ac.abort();
|
|
29
|
+
child.kill("SIGKILL");
|
|
30
|
+
reject(new Error(`Snippet "${name}" timed out after ${timeoutMs}ms`));
|
|
31
|
+
}, timeoutMs);
|
|
32
|
+
const child = spawn("osascript", ["-l", "JavaScript"], {
|
|
33
|
+
signal: ac.signal,
|
|
34
|
+
});
|
|
35
|
+
let stdout = "";
|
|
36
|
+
let stderr = "";
|
|
37
|
+
child.stdout.on("data", (chunk) => {
|
|
38
|
+
stdout += chunk.toString();
|
|
39
|
+
if (stdout.length > MAX_OUTPUT_BYTES) {
|
|
40
|
+
child.kill("SIGKILL");
|
|
41
|
+
clearTimeout(timer);
|
|
42
|
+
reject(new Error(`Snippet "${name}" exceeded maximum output size`));
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
child.stderr.on("data", (chunk) => {
|
|
46
|
+
stderr += chunk.toString();
|
|
47
|
+
});
|
|
48
|
+
child.on("error", (err) => {
|
|
49
|
+
clearTimeout(timer);
|
|
50
|
+
if (err.code === "ABORT_ERR")
|
|
51
|
+
return; // handled by timer
|
|
52
|
+
reject(err);
|
|
53
|
+
});
|
|
54
|
+
child.on("close", (code) => {
|
|
55
|
+
clearTimeout(timer);
|
|
56
|
+
try {
|
|
57
|
+
const envelope = parseResultLine(stdout);
|
|
58
|
+
if (envelope.ok) {
|
|
59
|
+
resolve(envelope.data);
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
reject(new ExecutionError(envelope.error));
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
catch (parseErr) {
|
|
66
|
+
const hint = stdout.slice(0, 200) || stderr.slice(0, 200);
|
|
67
|
+
reject(new Error(`Failed to parse bridge output for snippet "${name}" (exit ${code})` +
|
|
68
|
+
(hint ? `: ${hint}` : "")));
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
// Write JXA script to stdin
|
|
72
|
+
child.stdin.write(jxaScript);
|
|
73
|
+
child.stdin.end();
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=bridge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bridge.js","sourceRoot":"","sources":["../../src/runtime/bridge.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { runSnippet, type RunOptions } from "./bridge.js";
|
|
2
|
+
export { loadSnippet, clearSnippetCache } from "./snippetLoader.js";
|
|
3
|
+
export { parseResultLine, ExecutionError, type ResultEnvelope } from "./resultProtocol.js";
|
|
4
|
+
export { buildJxaScript } from "./jxaShim.js";
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/runtime/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AAC1D,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACpE,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,KAAK,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC3F,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/runtime/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAmB,MAAM,aAAa,CAAC;AAC1D,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACpE,OAAO,EAAE,eAAe,EAAE,cAAc,EAAuB,MAAM,qBAAqB,CAAC;AAC3F,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JXA wrapper template. The `__SNIPPET__` placeholder is replaced with the
|
|
3
|
+
* OmniJS snippet to execute. The resulting script is passed to
|
|
4
|
+
* `osascript -l JavaScript` via stdin.
|
|
5
|
+
*
|
|
6
|
+
* This shim:
|
|
7
|
+
* - Calls Application('OmniFocus').evaluateJavascript() with the snippet
|
|
8
|
+
* - Wraps in try/catch
|
|
9
|
+
* - Prints exactly one line of JSON: {ok, data} | {ok: false, error: {...}}
|
|
10
|
+
*
|
|
11
|
+
* The JXA scripting dictionary is used ONLY for evaluateJavascript.
|
|
12
|
+
* No other scripting-dictionary domain methods are invoked here.
|
|
13
|
+
*/
|
|
14
|
+
export declare const JXA_SHIM_TEMPLATE = "\n(function() {\n var snippet = __SNIPPET__;\n var app = Application('OmniFocus');\n app.includeStandardAdditions = true;\n try {\n var result = app.evaluateJavascript(snippet);\n // result is already a JSON string produced by the snippet\n $.NSFileHandle.fileHandleWithStandardOutput.writeData(\n $.NSString.alloc.initWithString(result + '\\n')\n .dataUsingEncoding($.NSUTF8StringEncoding)\n );\n } catch(e) {\n var errName = e.name || 'Error';\n var errMsg = e.message || String(e);\n // OmniJS wraps thrown errors; the original name is embedded in the message\n // as \"SomeName: original message\". Recover it when the name is generic.\n var nameMatch = errMsg.match(/^([A-Z]\\w*Error): ([\\s\\S]*)$/);\n if (nameMatch) { errName = nameMatch[1]; errMsg = nameMatch[2]; }\n var err = JSON.stringify({\n ok: false,\n error: {\n name: errName,\n message: errMsg\n }\n });\n $.NSFileHandle.fileHandleWithStandardOutput.writeData(\n $.NSString.alloc.initWithString(err + '\\n')\n .dataUsingEncoding($.NSUTF8StringEncoding)\n );\n }\n})();\n";
|
|
15
|
+
/**
|
|
16
|
+
* Build the complete JXA script to pass to osascript.
|
|
17
|
+
* The snippet is embedded as a JS string literal via JSON.stringify,
|
|
18
|
+
* so it is safe to contain any characters including quotes, backslashes, etc.
|
|
19
|
+
*/
|
|
20
|
+
export declare function buildJxaScript(snippet: string): string;
|
|
21
|
+
//# sourceMappingURL=jxaShim.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jxaShim.d.ts","sourceRoot":"","sources":["../../src/runtime/jxaShim.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,iBAAiB,ynCAgC7B,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEtD"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JXA wrapper template. The `__SNIPPET__` placeholder is replaced with the
|
|
3
|
+
* OmniJS snippet to execute. The resulting script is passed to
|
|
4
|
+
* `osascript -l JavaScript` via stdin.
|
|
5
|
+
*
|
|
6
|
+
* This shim:
|
|
7
|
+
* - Calls Application('OmniFocus').evaluateJavascript() with the snippet
|
|
8
|
+
* - Wraps in try/catch
|
|
9
|
+
* - Prints exactly one line of JSON: {ok, data} | {ok: false, error: {...}}
|
|
10
|
+
*
|
|
11
|
+
* The JXA scripting dictionary is used ONLY for evaluateJavascript.
|
|
12
|
+
* No other scripting-dictionary domain methods are invoked here.
|
|
13
|
+
*/
|
|
14
|
+
export const JXA_SHIM_TEMPLATE = `
|
|
15
|
+
(function() {
|
|
16
|
+
var snippet = __SNIPPET__;
|
|
17
|
+
var app = Application('OmniFocus');
|
|
18
|
+
app.includeStandardAdditions = true;
|
|
19
|
+
try {
|
|
20
|
+
var result = app.evaluateJavascript(snippet);
|
|
21
|
+
// result is already a JSON string produced by the snippet
|
|
22
|
+
$.NSFileHandle.fileHandleWithStandardOutput.writeData(
|
|
23
|
+
$.NSString.alloc.initWithString(result + '\\n')
|
|
24
|
+
.dataUsingEncoding($.NSUTF8StringEncoding)
|
|
25
|
+
);
|
|
26
|
+
} catch(e) {
|
|
27
|
+
var errName = e.name || 'Error';
|
|
28
|
+
var errMsg = e.message || String(e);
|
|
29
|
+
// OmniJS wraps thrown errors; the original name is embedded in the message
|
|
30
|
+
// as "SomeName: original message". Recover it when the name is generic.
|
|
31
|
+
var nameMatch = errMsg.match(/^([A-Z]\\w*Error): ([\\s\\S]*)$/);
|
|
32
|
+
if (nameMatch) { errName = nameMatch[1]; errMsg = nameMatch[2]; }
|
|
33
|
+
var err = JSON.stringify({
|
|
34
|
+
ok: false,
|
|
35
|
+
error: {
|
|
36
|
+
name: errName,
|
|
37
|
+
message: errMsg
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
$.NSFileHandle.fileHandleWithStandardOutput.writeData(
|
|
41
|
+
$.NSString.alloc.initWithString(err + '\\n')
|
|
42
|
+
.dataUsingEncoding($.NSUTF8StringEncoding)
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
})();
|
|
46
|
+
`;
|
|
47
|
+
/**
|
|
48
|
+
* Build the complete JXA script to pass to osascript.
|
|
49
|
+
* The snippet is embedded as a JS string literal via JSON.stringify,
|
|
50
|
+
* so it is safe to contain any characters including quotes, backslashes, etc.
|
|
51
|
+
*/
|
|
52
|
+
export function buildJxaScript(snippet) {
|
|
53
|
+
return JXA_SHIM_TEMPLATE.replace("__SNIPPET__", JSON.stringify(snippet));
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=jxaShim.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jxaShim.js","sourceRoot":"","sources":["../../src/runtime/jxaShim.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgChC,CAAC;AAEF;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,OAAe;IAC5C,OAAO,iBAAiB,CAAC,OAAO,CAAC,aAAa,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC;AAC3E,CAAC"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
declare const ErrorDetail: z.ZodObject<{
|
|
3
|
+
name: z.ZodString;
|
|
4
|
+
message: z.ZodString;
|
|
5
|
+
stack: z.ZodOptional<z.ZodString>;
|
|
6
|
+
}, "strip", z.ZodTypeAny, {
|
|
7
|
+
name: string;
|
|
8
|
+
message: string;
|
|
9
|
+
stack?: string | undefined;
|
|
10
|
+
}, {
|
|
11
|
+
name: string;
|
|
12
|
+
message: string;
|
|
13
|
+
stack?: string | undefined;
|
|
14
|
+
}>;
|
|
15
|
+
declare const Envelope: z.ZodDiscriminatedUnion<"ok", [z.ZodObject<{
|
|
16
|
+
ok: z.ZodLiteral<true>;
|
|
17
|
+
data: z.ZodUnknown;
|
|
18
|
+
}, "strip", z.ZodTypeAny, {
|
|
19
|
+
ok: true;
|
|
20
|
+
data?: unknown;
|
|
21
|
+
}, {
|
|
22
|
+
ok: true;
|
|
23
|
+
data?: unknown;
|
|
24
|
+
}>, z.ZodObject<{
|
|
25
|
+
ok: z.ZodLiteral<false>;
|
|
26
|
+
error: z.ZodObject<{
|
|
27
|
+
name: z.ZodString;
|
|
28
|
+
message: z.ZodString;
|
|
29
|
+
stack: z.ZodOptional<z.ZodString>;
|
|
30
|
+
}, "strip", z.ZodTypeAny, {
|
|
31
|
+
name: string;
|
|
32
|
+
message: string;
|
|
33
|
+
stack?: string | undefined;
|
|
34
|
+
}, {
|
|
35
|
+
name: string;
|
|
36
|
+
message: string;
|
|
37
|
+
stack?: string | undefined;
|
|
38
|
+
}>;
|
|
39
|
+
}, "strip", z.ZodTypeAny, {
|
|
40
|
+
ok: false;
|
|
41
|
+
error: {
|
|
42
|
+
name: string;
|
|
43
|
+
message: string;
|
|
44
|
+
stack?: string | undefined;
|
|
45
|
+
};
|
|
46
|
+
}, {
|
|
47
|
+
ok: false;
|
|
48
|
+
error: {
|
|
49
|
+
name: string;
|
|
50
|
+
message: string;
|
|
51
|
+
stack?: string | undefined;
|
|
52
|
+
};
|
|
53
|
+
}>]>;
|
|
54
|
+
export type ResultEnvelope = z.infer<typeof Envelope>;
|
|
55
|
+
export declare class ExecutionError extends Error {
|
|
56
|
+
readonly errorName: string;
|
|
57
|
+
readonly remoteStack?: string;
|
|
58
|
+
constructor(detail: z.infer<typeof ErrorDetail>);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Scans stdout for the first JSON-parseable line and validates it as a result
|
|
62
|
+
* envelope. Ignores any preceding non-JSON chatter (e.g. osascript warnings).
|
|
63
|
+
*/
|
|
64
|
+
export declare function parseResultLine(stdout: string): ResultEnvelope;
|
|
65
|
+
export {};
|
|
66
|
+
//# sourceMappingURL=resultProtocol.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resultProtocol.d.ts","sourceRoot":"","sources":["../../src/runtime/resultProtocol.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,QAAA,MAAM,WAAW;;;;;;;;;;;;EAIf,CAAC;AAYH,QAAA,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAA+D,CAAC;AAE9E,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,QAAQ,CAAC,CAAC;AAEtD,qBAAa,cAAe,SAAQ,KAAK;IACvC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;gBAElB,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC;CAMhD;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,cAAc,CAuB9D"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
const ErrorDetail = z.object({
|
|
3
|
+
name: z.string(),
|
|
4
|
+
message: z.string(),
|
|
5
|
+
stack: z.string().optional(),
|
|
6
|
+
});
|
|
7
|
+
const SuccessEnvelope = z.object({
|
|
8
|
+
ok: z.literal(true),
|
|
9
|
+
data: z.unknown(),
|
|
10
|
+
});
|
|
11
|
+
const ErrorEnvelope = z.object({
|
|
12
|
+
ok: z.literal(false),
|
|
13
|
+
error: ErrorDetail,
|
|
14
|
+
});
|
|
15
|
+
const Envelope = z.discriminatedUnion("ok", [SuccessEnvelope, ErrorEnvelope]);
|
|
16
|
+
export class ExecutionError extends Error {
|
|
17
|
+
errorName;
|
|
18
|
+
remoteStack;
|
|
19
|
+
constructor(detail) {
|
|
20
|
+
super(detail.message);
|
|
21
|
+
this.name = "ExecutionError";
|
|
22
|
+
this.errorName = detail.name;
|
|
23
|
+
this.remoteStack = detail.stack;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Scans stdout for the first JSON-parseable line and validates it as a result
|
|
28
|
+
* envelope. Ignores any preceding non-JSON chatter (e.g. osascript warnings).
|
|
29
|
+
*/
|
|
30
|
+
export function parseResultLine(stdout) {
|
|
31
|
+
const lines = stdout.split("\n");
|
|
32
|
+
for (const line of lines) {
|
|
33
|
+
const trimmed = line.trim();
|
|
34
|
+
if (!trimmed)
|
|
35
|
+
continue;
|
|
36
|
+
let parsed;
|
|
37
|
+
try {
|
|
38
|
+
parsed = JSON.parse(trimmed);
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
continue; // not JSON, skip
|
|
42
|
+
}
|
|
43
|
+
const result = Envelope.safeParse(parsed);
|
|
44
|
+
if (result.success) {
|
|
45
|
+
return result.data;
|
|
46
|
+
}
|
|
47
|
+
// Parsed as JSON but not a valid envelope — this is a protocol error
|
|
48
|
+
throw new Error(`Bridge returned JSON that is not a valid result envelope: ${trimmed}`);
|
|
49
|
+
}
|
|
50
|
+
throw new Error(`No valid JSON result envelope found in osascript output:\n${stdout}`);
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=resultProtocol.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resultProtocol.js","sourceRoot":"","sources":["../../src/runtime/resultProtocol.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,WAAW,GAAG,CAAC,CAAC,MAAM,CAAC;IAC3B,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;IAChB,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE;IACnB,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CAC7B,CAAC,CAAC;AAEH,MAAM,eAAe,GAAG,CAAC,CAAC,MAAM,CAAC;IAC/B,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC;IACnB,IAAI,EAAE,CAAC,CAAC,OAAO,EAAE;CAClB,CAAC,CAAC;AAEH,MAAM,aAAa,GAAG,CAAC,CAAC,MAAM,CAAC;IAC7B,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC;IACpB,KAAK,EAAE,WAAW;CACnB,CAAC,CAAC;AAEH,MAAM,QAAQ,GAAG,CAAC,CAAC,kBAAkB,CAAC,IAAI,EAAE,CAAC,eAAe,EAAE,aAAa,CAAC,CAAC,CAAC;AAI9E,MAAM,OAAO,cAAe,SAAQ,KAAK;IAC9B,SAAS,CAAS;IAClB,WAAW,CAAU;IAE9B,YAAY,MAAmC;QAC7C,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACtB,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAC;QAC7B,IAAI,CAAC,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC;QAC7B,IAAI,CAAC,WAAW,GAAG,MAAM,CAAC,KAAK,CAAC;IAClC,CAAC;CACF;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,MAAc;IAC5C,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACjC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QAC5B,IAAI,CAAC,OAAO;YAAE,SAAS;QACvB,IAAI,MAAe,CAAC;QACpB,IAAI,CAAC;YACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAC/B,CAAC;QAAC,MAAM,CAAC;YACP,SAAS,CAAC,iBAAiB;QAC7B,CAAC;QACD,MAAM,MAAM,GAAG,QAAQ,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QAC1C,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;YACnB,OAAO,MAAM,CAAC,IAAI,CAAC;QACrB,CAAC;QACD,qEAAqE;QACrE,MAAM,IAAI,KAAK,CACb,6DAA6D,OAAO,EAAE,CACvE,CAAC;IACJ,CAAC;IACD,MAAM,IAAI,KAAK,CACb,6DAA6D,MAAM,EAAE,CACtE,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"snippetLoader.d.ts","sourceRoot":"","sources":["../../src/runtime/snippetLoader.ts"],"names":[],"mappings":"AA0CA,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CA+BhD;AAED,8CAA8C;AAC9C,wBAAgB,iBAAiB,IAAI,IAAI,CAExC"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { readFileSync } from "fs";
|
|
2
|
+
import { join, dirname } from "path";
|
|
3
|
+
import { fileURLToPath } from "url";
|
|
4
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
5
|
+
const SNIPPETS_DIR = join(__dirname, "..", "snippets");
|
|
6
|
+
// Allowlist of valid snippet names. Adding a new snippet requires an explicit
|
|
7
|
+
// entry here, which prevents path-traversal attacks if a snippet name ever
|
|
8
|
+
// reaches this loader from a dynamic source.
|
|
9
|
+
const ALLOWED_SNIPPETS = new Set([
|
|
10
|
+
"get_folder",
|
|
11
|
+
"get_project",
|
|
12
|
+
"get_tag",
|
|
13
|
+
"get_task",
|
|
14
|
+
"list_folders",
|
|
15
|
+
"list_projects",
|
|
16
|
+
"list_tags",
|
|
17
|
+
"list_tasks",
|
|
18
|
+
"resolve_name",
|
|
19
|
+
"create_task",
|
|
20
|
+
"edit_task",
|
|
21
|
+
"complete_task",
|
|
22
|
+
"drop_task",
|
|
23
|
+
"delete_task",
|
|
24
|
+
"create_project",
|
|
25
|
+
"edit_project",
|
|
26
|
+
"complete_project",
|
|
27
|
+
"drop_project",
|
|
28
|
+
"delete_project",
|
|
29
|
+
"create_folder",
|
|
30
|
+
"edit_folder",
|
|
31
|
+
"delete_folder",
|
|
32
|
+
"create_tag",
|
|
33
|
+
"edit_tag",
|
|
34
|
+
"delete_tag",
|
|
35
|
+
"move_task",
|
|
36
|
+
"move_project",
|
|
37
|
+
]);
|
|
38
|
+
const cache = new Map();
|
|
39
|
+
export function loadSnippet(name) {
|
|
40
|
+
if (!ALLOWED_SNIPPETS.has(name)) {
|
|
41
|
+
throw new Error(`Unknown snippet: "${name}"`);
|
|
42
|
+
}
|
|
43
|
+
if (cache.has(name)) {
|
|
44
|
+
return cache.get(name);
|
|
45
|
+
}
|
|
46
|
+
const filePath = join(SNIPPETS_DIR, `${name}.js`);
|
|
47
|
+
let content;
|
|
48
|
+
try {
|
|
49
|
+
content = readFileSync(filePath, "utf-8");
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
throw new Error(`Snippet "${name}" could not be loaded`);
|
|
53
|
+
}
|
|
54
|
+
const matches = content.split("__ARGS__").length - 1;
|
|
55
|
+
if (matches === 0) {
|
|
56
|
+
throw new Error(`Snippet "${name}" contains no __ARGS__ placeholder. Every snippet must have exactly one.`);
|
|
57
|
+
}
|
|
58
|
+
if (matches > 1) {
|
|
59
|
+
throw new Error(`Snippet "${name}" contains ${matches} __ARGS__ placeholders. Exactly one is required.`);
|
|
60
|
+
}
|
|
61
|
+
cache.set(name, content);
|
|
62
|
+
return content;
|
|
63
|
+
}
|
|
64
|
+
/** For testing: clear the in-memory cache. */
|
|
65
|
+
export function clearSnippetCache() {
|
|
66
|
+
cache.clear();
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=snippetLoader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"snippetLoader.js","sourceRoot":"","sources":["../../src/runtime/snippetLoader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,IAAI,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AACrC,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AAEpC,MAAM,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAC1D,MAAM,YAAY,GAAG,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC;AAEvD,8EAA8E;AAC9E,2EAA2E;AAC3E,6CAA6C;AAC7C,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAC;IAC/B,YAAY;IACZ,aAAa;IACb,SAAS;IACT,UAAU;IACV,cAAc;IACd,eAAe;IACf,WAAW;IACX,YAAY;IACZ,cAAc;IACd,aAAa;IACb,WAAW;IACX,eAAe;IACf,WAAW;IACX,aAAa;IACb,gBAAgB;IAChB,cAAc;IACd,kBAAkB;IAClB,cAAc;IACd,gBAAgB;IAChB,eAAe;IACf,aAAa;IACb,eAAe;IACf,YAAY;IACZ,UAAU;IACV,YAAY;IACZ,WAAW;IACX,cAAc;CACf,CAAC,CAAC;AAEH,MAAM,KAAK,GAAG,IAAI,GAAG,EAAkB,CAAC;AAExC,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;QAChC,MAAM,IAAI,KAAK,CAAC,qBAAqB,IAAI,GAAG,CAAC,CAAC;IAChD,CAAC;IAED,IAAI,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;QACpB,OAAO,KAAK,CAAC,GAAG,CAAC,IAAI,CAAE,CAAC;IAC1B,CAAC;IAED,MAAM,QAAQ,GAAG,IAAI,CAAC,YAAY,EAAE,GAAG,IAAI,KAAK,CAAC,CAAC;IAClD,IAAI,OAAe,CAAC;IACpB,IAAI,CAAC;QACH,OAAO,GAAG,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAC5C,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,YAAY,IAAI,uBAAuB,CAAC,CAAC;IAC3D,CAAC;IAED,MAAM,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;IACrD,IAAI,OAAO,KAAK,CAAC,EAAE,CAAC;QAClB,MAAM,IAAI,KAAK,CACb,YAAY,IAAI,0EAA0E,CAC3F,CAAC;IACJ,CAAC;IACD,IAAI,OAAO,GAAG,CAAC,EAAE,CAAC;QAChB,MAAM,IAAI,KAAK,CACb,YAAY,IAAI,cAAc,OAAO,kDAAkD,CACxF,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACzB,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,8CAA8C;AAC9C,MAAM,UAAU,iBAAiB;IAC/B,KAAK,CAAC,KAAK,EAAE,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export declare const IdSchema: z.ZodString;
|
|
3
|
+
export declare const EntityType: z.ZodEnum<["task", "project", "folder", "tag", "perspective"]>;
|
|
4
|
+
export declare const ProjectType: z.ZodEnum<["parallel", "sequential", "singleActions"]>;
|
|
5
|
+
export declare const ProjectStatus: z.ZodEnum<["active", "onHold", "done", "dropped"]>;
|
|
6
|
+
export declare const TaskStatus: z.ZodEnum<["available", "incomplete", "completedByChildren", "complete", "dropped", "dueSoon", "overdue", "flagged", "blocked", "next"]>;
|
|
7
|
+
export declare const TagStatus: z.ZodEnum<["active", "onHold", "dropped"]>;
|
|
8
|
+
export declare const FolderStatus: z.ZodEnum<["active", "dropped"]>;
|
|
9
|
+
//# sourceMappingURL=enums.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"enums.d.ts","sourceRoot":"","sources":["../../src/schemas/enums.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,eAAO,MAAM,QAAQ,aAAqD,CAAC;AAE3E,eAAO,MAAM,UAAU,gEAMrB,CAAC;AAEH,eAAO,MAAM,WAAW,wDAAsD,CAAC;AAE/E,eAAO,MAAM,aAAa,oDAAkD,CAAC;AAE7E,eAAO,MAAM,UAAU,0IAWrB,CAAC;AAEH,eAAO,MAAM,SAAS,4CAA0C,CAAC;AAEjE,eAAO,MAAM,YAAY,kCAAgC,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export const IdSchema = z.string().min(1, "ID must be a non-empty string");
|
|
3
|
+
export const EntityType = z.enum([
|
|
4
|
+
"task",
|
|
5
|
+
"project",
|
|
6
|
+
"folder",
|
|
7
|
+
"tag",
|
|
8
|
+
"perspective",
|
|
9
|
+
]);
|
|
10
|
+
export const ProjectType = z.enum(["parallel", "sequential", "singleActions"]);
|
|
11
|
+
export const ProjectStatus = z.enum(["active", "onHold", "done", "dropped"]);
|
|
12
|
+
export const TaskStatus = z.enum([
|
|
13
|
+
"available",
|
|
14
|
+
"incomplete",
|
|
15
|
+
"completedByChildren",
|
|
16
|
+
"complete",
|
|
17
|
+
"dropped",
|
|
18
|
+
"dueSoon",
|
|
19
|
+
"overdue",
|
|
20
|
+
"flagged",
|
|
21
|
+
"blocked",
|
|
22
|
+
"next",
|
|
23
|
+
]);
|
|
24
|
+
export const TagStatus = z.enum(["active", "onHold", "dropped"]);
|
|
25
|
+
export const FolderStatus = z.enum(["active", "dropped"]);
|
|
26
|
+
//# sourceMappingURL=enums.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"enums.js","sourceRoot":"","sources":["../../src/schemas/enums.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,+BAA+B,CAAC,CAAC;AAE3E,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,CAAC,IAAI,CAAC;IAC/B,MAAM;IACN,SAAS;IACT,QAAQ;IACR,KAAK;IACL,aAAa;CACd,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,UAAU,EAAE,YAAY,EAAE,eAAe,CAAC,CAAC,CAAC;AAE/E,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC;AAE7E,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,CAAC,IAAI,CAAC;IAC/B,WAAW;IACX,YAAY;IACZ,qBAAqB;IACrB,UAAU;IACV,SAAS;IACT,SAAS;IACT,SAAS;IACT,SAAS;IACT,SAAS;IACT,MAAM;CACP,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC,CAAC;AAEjE,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { IdSchema, EntityType, ProjectType, ProjectStatus, TaskStatus, TagStatus, FolderStatus, } from "./enums.js";
|
|
2
|
+
export { RepetitionRuleInput, RepetitionRuleDetail, TaskSummary, ListTasksFilter, TaskDetail, CreateTaskInput, EditTaskInput, MoveTaskInput, ReviewIntervalInput, CreateProjectInput, EditProjectInput, MoveProjectInput, ListProjectsFilter, ProjectSummary, ProjectDetail, ListFoldersFilter, CreateFolderInput, EditFolderInput, FolderSummary, FolderDetail, ListTagsFilter, CreateTagInput, EditTagInput, TagSummary, TagDetail, ResolveCandidate, } from "./shapes.js";
|
|
3
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/schemas/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,QAAQ,EACR,UAAU,EACV,WAAW,EACX,aAAa,EACb,UAAU,EACV,SAAS,EACT,YAAY,GACb,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,mBAAmB,EACnB,oBAAoB,EACpB,WAAW,EACX,eAAe,EACf,UAAU,EACV,eAAe,EACf,aAAa,EACb,aAAa,EACb,mBAAmB,EACnB,kBAAkB,EAClB,gBAAgB,EAChB,gBAAgB,EAChB,kBAAkB,EAClB,cAAc,EACd,aAAa,EACb,iBAAiB,EACjB,iBAAiB,EACjB,eAAe,EACf,aAAa,EACb,YAAY,EACZ,cAAc,EACd,cAAc,EACd,YAAY,EACZ,UAAU,EACV,SAAS,EACT,gBAAgB,GACjB,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { IdSchema, EntityType, ProjectType, ProjectStatus, TaskStatus, TagStatus, FolderStatus, } from "./enums.js";
|
|
2
|
+
export { RepetitionRuleInput, RepetitionRuleDetail, TaskSummary, ListTasksFilter, TaskDetail, CreateTaskInput, EditTaskInput, MoveTaskInput, ReviewIntervalInput, CreateProjectInput, EditProjectInput, MoveProjectInput, ListProjectsFilter, ProjectSummary, ProjectDetail, ListFoldersFilter, CreateFolderInput, EditFolderInput, FolderSummary, FolderDetail, ListTagsFilter, CreateTagInput, EditTagInput, TagSummary, TagDetail, ResolveCandidate, } from "./shapes.js";
|
|
3
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/schemas/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,QAAQ,EACR,UAAU,EACV,WAAW,EACX,aAAa,EACb,UAAU,EACV,SAAS,EACT,YAAY,GACb,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,mBAAmB,EACnB,oBAAoB,EACpB,WAAW,EACX,eAAe,EACf,UAAU,EACV,eAAe,EACf,aAAa,EACb,aAAa,EACb,mBAAmB,EACnB,kBAAkB,EAClB,gBAAgB,EAChB,gBAAgB,EAChB,kBAAkB,EAClB,cAAc,EACd,aAAa,EACb,iBAAiB,EACjB,iBAAiB,EACjB,eAAe,EACf,aAAa,EACb,YAAY,EACZ,cAAc,EACd,cAAc,EACd,YAAY,EACZ,UAAU,EACV,SAAS,EACT,gBAAgB,GACjB,MAAM,aAAa,CAAC"}
|