@scardis/omnifocus-mcp 0.1.0 → 0.1.2
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/dist/schemas/shapes.d.ts +3 -33
- package/dist/schemas/shapes.d.ts.map +1 -1
- package/dist/schemas/shapes.js +3 -5
- package/dist/schemas/shapes.js.map +1 -1
- package/dist/server.js +3 -5
- package/dist/server.js.map +1 -1
- package/dist/tools/createTask.d.ts +1 -11
- package/dist/tools/createTask.d.ts.map +1 -1
- package/dist/tools/editTask.d.ts +1 -11
- package/dist/tools/editTask.d.ts.map +1 -1
- package/dist/tools/index.d.ts +2 -22
- package/dist/tools/index.d.ts.map +1 -1
- package/package.json +7 -1
- package/src/snippets/edit_task.js +4 -6
- package/.claude/commands/opsx/apply.md +0 -152
- package/.claude/commands/opsx/archive.md +0 -157
- package/.claude/commands/opsx/bulk-archive.md +0 -242
- package/.claude/commands/opsx/continue.md +0 -114
- package/.claude/commands/opsx/explore.md +0 -173
- package/.claude/commands/opsx/ff.md +0 -97
- package/.claude/commands/opsx/new.md +0 -69
- package/.claude/commands/opsx/onboard.md +0 -550
- package/.claude/commands/opsx/propose.md +0 -106
- package/.claude/commands/opsx/sync.md +0 -134
- package/.claude/commands/opsx/verify.md +0 -164
- package/.claude/skills/openspec-apply-change/SKILL.md +0 -156
- package/.claude/skills/openspec-archive-change/SKILL.md +0 -114
- package/.claude/skills/openspec-bulk-archive-change/SKILL.md +0 -246
- package/.claude/skills/openspec-continue-change/SKILL.md +0 -118
- package/.claude/skills/openspec-explore/SKILL.md +0 -288
- package/.claude/skills/openspec-ff-change/SKILL.md +0 -101
- package/.claude/skills/openspec-new-change/SKILL.md +0 -74
- package/.claude/skills/openspec-onboard/SKILL.md +0 -554
- package/.claude/skills/openspec-propose/SKILL.md +0 -110
- package/.claude/skills/openspec-sync-specs/SKILL.md +0 -138
- package/.claude/skills/openspec-verify-change/SKILL.md +0 -168
- package/CONTRIBUTING.md +0 -83
- package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/.openspec.yaml +0 -2
- package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/design.md +0 -162
- package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/proposal.md +0 -49
- package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/specs/attachments/spec.md +0 -9
- package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/specs/batch-operations/spec.md +0 -9
- package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/specs/database-inspection/spec.md +0 -9
- package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/specs/execution-runtime/spec.md +0 -69
- package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/specs/folder-management/spec.md +0 -25
- package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/specs/forecast/spec.md +0 -9
- package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/specs/identity-resolution/spec.md +0 -45
- package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/specs/perspective-management/spec.md +0 -9
- package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/specs/project-management/spec.md +0 -25
- package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/specs/recurrence/spec.md +0 -9
- package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/specs/settings/spec.md +0 -9
- package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/specs/tag-management/spec.md +0 -25
- package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/specs/task-management/spec.md +0 -29
- package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/specs/url-automation/spec.md +0 -9
- package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/specs/window-state/spec.md +0 -9
- package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/tasks.md +0 -84
- package/openspec/changes/archive/2026-04-09-folder-crud/.openspec.yaml +0 -2
- package/openspec/changes/archive/2026-04-09-folder-crud/design.md +0 -58
- package/openspec/changes/archive/2026-04-09-folder-crud/proposal.md +0 -28
- package/openspec/changes/archive/2026-04-09-folder-crud/specs/folder-write/spec.md +0 -45
- package/openspec/changes/archive/2026-04-09-folder-crud/tasks.md +0 -41
- package/openspec/changes/archive/2026-04-09-folder-tag-list-filtering/.openspec.yaml +0 -2
- package/openspec/changes/archive/2026-04-09-folder-tag-list-filtering/design.md +0 -38
- package/openspec/changes/archive/2026-04-09-folder-tag-list-filtering/proposal.md +0 -30
- package/openspec/changes/archive/2026-04-09-folder-tag-list-filtering/specs/folder-management/spec.md +0 -21
- package/openspec/changes/archive/2026-04-09-folder-tag-list-filtering/specs/tag-management/spec.md +0 -21
- package/openspec/changes/archive/2026-04-09-folder-tag-list-filtering/tasks.md +0 -35
- package/openspec/changes/archive/2026-04-09-move-operations/.openspec.yaml +0 -2
- package/openspec/changes/archive/2026-04-09-move-operations/design.md +0 -43
- package/openspec/changes/archive/2026-04-09-move-operations/proposal.md +0 -25
- package/openspec/changes/archive/2026-04-09-move-operations/specs/move-operations/spec.md +0 -41
- package/openspec/changes/archive/2026-04-09-move-operations/tasks.md +0 -40
- package/openspec/changes/archive/2026-04-09-project-crud/.openspec.yaml +0 -2
- package/openspec/changes/archive/2026-04-09-project-crud/design.md +0 -60
- package/openspec/changes/archive/2026-04-09-project-crud/proposal.md +0 -29
- package/openspec/changes/archive/2026-04-09-project-crud/specs/project-write/spec.md +0 -74
- package/openspec/changes/archive/2026-04-09-project-crud/tasks.md +0 -48
- package/openspec/changes/archive/2026-04-09-project-filtering/.openspec.yaml +0 -2
- package/openspec/changes/archive/2026-04-09-project-filtering/design.md +0 -52
- package/openspec/changes/archive/2026-04-09-project-filtering/proposal.md +0 -26
- package/openspec/changes/archive/2026-04-09-project-filtering/specs/project-filtering/spec.md +0 -66
- package/openspec/changes/archive/2026-04-09-project-filtering/specs/project-management/spec.md +0 -13
- package/openspec/changes/archive/2026-04-09-project-filtering/tasks.md +0 -41
- package/openspec/changes/archive/2026-04-09-tag-crud/.openspec.yaml +0 -2
- package/openspec/changes/archive/2026-04-09-tag-crud/design.md +0 -45
- package/openspec/changes/archive/2026-04-09-tag-crud/proposal.md +0 -28
- package/openspec/changes/archive/2026-04-09-tag-crud/specs/tag-write/spec.md +0 -49
- package/openspec/changes/archive/2026-04-09-tag-crud/tasks.md +0 -41
- package/openspec/changes/archive/2026-04-09-task-crud/.openspec.yaml +0 -2
- package/openspec/changes/archive/2026-04-09-task-crud/design.md +0 -62
- package/openspec/changes/archive/2026-04-09-task-crud/proposal.md +0 -29
- package/openspec/changes/archive/2026-04-09-task-crud/specs/task-management/spec.md +0 -17
- package/openspec/changes/archive/2026-04-09-task-crud/specs/task-write/spec.md +0 -89
- package/openspec/changes/archive/2026-04-09-task-crud/tasks.md +0 -55
- package/openspec/changes/archive/2026-04-09-task-filtering/.openspec.yaml +0 -2
- package/openspec/changes/archive/2026-04-09-task-filtering/design.md +0 -61
- package/openspec/changes/archive/2026-04-09-task-filtering/proposal.md +0 -26
- package/openspec/changes/archive/2026-04-09-task-filtering/specs/task-filtering/spec.md +0 -63
- package/openspec/changes/archive/2026-04-09-task-filtering/specs/task-management/spec.md +0 -17
- package/openspec/changes/archive/2026-04-09-task-filtering/tasks.md +0 -42
- package/openspec/changes/archive/2026-04-10-planned-date/.openspec.yaml +0 -2
- package/openspec/changes/archive/2026-04-10-planned-date/design.md +0 -27
- package/openspec/changes/archive/2026-04-10-planned-date/proposal.md +0 -29
- package/openspec/changes/archive/2026-04-10-planned-date/specs/task-management/spec.md +0 -29
- package/openspec/changes/archive/2026-04-10-planned-date/specs/task-write/spec.md +0 -69
- package/openspec/changes/archive/2026-04-10-planned-date/tasks.md +0 -26
- package/openspec/changes/archive/2026-04-10-task-recurrence/.openspec.yaml +0 -2
- package/openspec/changes/archive/2026-04-10-task-recurrence/design.md +0 -81
- package/openspec/changes/archive/2026-04-10-task-recurrence/proposal.md +0 -28
- package/openspec/changes/archive/2026-04-10-task-recurrence/specs/recurrence/spec.md +0 -47
- package/openspec/changes/archive/2026-04-10-task-recurrence/specs/task-management/spec.md +0 -25
- package/openspec/changes/archive/2026-04-10-task-recurrence/specs/task-write/spec.md +0 -61
- package/openspec/changes/archive/2026-04-10-task-recurrence/tasks.md +0 -39
- package/openspec/config.yaml +0 -20
- package/openspec/specs/attachments/spec.md +0 -15
- package/openspec/specs/batch-operations/spec.md +0 -15
- package/openspec/specs/database-inspection/spec.md +0 -15
- package/openspec/specs/execution-runtime/spec.md +0 -75
- package/openspec/specs/folder-management/spec.md +0 -39
- package/openspec/specs/folder-write/spec.md +0 -45
- package/openspec/specs/forecast/spec.md +0 -15
- package/openspec/specs/identity-resolution/spec.md +0 -51
- package/openspec/specs/move-operations/spec.md +0 -41
- package/openspec/specs/perspective-management/spec.md +0 -15
- package/openspec/specs/project-filtering/spec.md +0 -72
- package/openspec/specs/project-management/spec.md +0 -31
- package/openspec/specs/project-write/spec.md +0 -79
- package/openspec/specs/recurrence/spec.md +0 -51
- package/openspec/specs/settings/spec.md +0 -15
- package/openspec/specs/tag-management/spec.md +0 -39
- package/openspec/specs/tag-write/spec.md +0 -49
- package/openspec/specs/task-filtering/spec.md +0 -63
- package/openspec/specs/task-management/spec.md +0 -51
- package/openspec/specs/task-write/spec.md +0 -115
- package/openspec/specs/url-automation/spec.md +0 -15
- package/openspec/specs/window-state/spec.md +0 -15
- package/scripts/cleanup-fixtures.ts +0 -89
- package/server.json +0 -21
- package/src/runtime/bridge.ts +0 -97
- package/src/runtime/index.ts +0 -4
- package/src/runtime/jxaShim.ts +0 -55
- package/src/runtime/resultProtocol.ts +0 -62
- package/src/runtime/snippetLoader.ts +0 -79
- package/src/schemas/enums.ts +0 -32
- package/src/schemas/index.ts +0 -38
- package/src/schemas/shapes.ts +0 -267
- package/src/server.ts +0 -58
- package/src/tools/completeProject.ts +0 -21
- package/src/tools/completeTask.ts +0 -23
- package/src/tools/createFolder.ts +0 -20
- package/src/tools/createProject.ts +0 -20
- package/src/tools/createTag.ts +0 -20
- package/src/tools/createTask.ts +0 -20
- package/src/tools/deleteFolder.ts +0 -24
- package/src/tools/deleteProject.ts +0 -24
- package/src/tools/deleteTag.ts +0 -24
- package/src/tools/deleteTask.ts +0 -26
- package/src/tools/dropProject.ts +0 -21
- package/src/tools/dropTask.ts +0 -23
- package/src/tools/editFolder.ts +0 -19
- package/src/tools/editProject.ts +0 -20
- package/src/tools/editTag.ts +0 -20
- package/src/tools/editTask.ts +0 -20
- package/src/tools/getFolder.ts +0 -24
- package/src/tools/getProject.ts +0 -24
- package/src/tools/getTag.ts +0 -24
- package/src/tools/getTask.ts +0 -24
- package/src/tools/index.ts +0 -85
- package/src/tools/listFolders.ts +0 -32
- package/src/tools/listProjects.ts +0 -32
- package/src/tools/listTags.ts +0 -32
- package/src/tools/listTasks.ts +0 -56
- package/src/tools/moveProject.ts +0 -20
- package/src/tools/moveTask.ts +0 -20
- package/src/tools/resolveName.ts +0 -37
- package/test/integration/.gitkeep +0 -0
- package/test/integration/completeProject.int.test.ts +0 -25
- package/test/integration/completeTask.int.test.ts +0 -30
- package/test/integration/createFolder.int.test.ts +0 -50
- package/test/integration/createProject.int.test.ts +0 -49
- package/test/integration/createTag.int.test.ts +0 -52
- package/test/integration/createTask.int.test.ts +0 -55
- package/test/integration/deleteFolder.int.test.ts +0 -64
- package/test/integration/deleteProject.int.test.ts +0 -31
- package/test/integration/deleteTag.int.test.ts +0 -61
- package/test/integration/deleteTask.int.test.ts +0 -36
- package/test/integration/dropProject.int.test.ts +0 -24
- package/test/integration/dropTask.int.test.ts +0 -29
- package/test/integration/editFolder.int.test.ts +0 -43
- package/test/integration/editProject.int.test.ts +0 -39
- package/test/integration/editTag.int.test.ts +0 -43
- package/test/integration/editTask.int.test.ts +0 -56
- package/test/integration/fixtures.ts +0 -219
- package/test/integration/getTask.int.test.ts +0 -64
- package/test/integration/listFoldersFiltered.int.test.ts +0 -98
- package/test/integration/listProjects.int.test.ts +0 -73
- package/test/integration/listProjectsFiltered.int.test.ts +0 -96
- package/test/integration/listTagsFiltered.int.test.ts +0 -54
- package/test/integration/listTasksFiltered.int.test.ts +0 -141
- package/test/integration/moveProject.int.test.ts +0 -57
- package/test/integration/moveTask.int.test.ts +0 -61
- package/test/integration/plannedDate.int.test.ts +0 -72
- package/test/integration/preflight.ts +0 -60
- package/test/integration/resolveName.int.test.ts +0 -86
- package/test/integration/taskRecurrence.int.test.ts +0 -106
- package/test/unit/.gitkeep +0 -0
- package/test/unit/bridge.injection.test.ts +0 -66
- package/test/unit/resultProtocol.test.ts +0 -71
- package/test/unit/schemas.createFolder.test.ts +0 -38
- package/test/unit/schemas.createProject.test.ts +0 -115
- package/test/unit/schemas.createTask.test.ts +0 -87
- package/test/unit/schemas.editTag.test.ts +0 -64
- package/test/unit/schemas.folderTagFiltering.test.ts +0 -42
- package/test/unit/schemas.listProjects.test.ts +0 -44
- package/test/unit/schemas.moveOperations.test.ts +0 -60
- package/test/unit/schemas.recurrence.test.ts +0 -120
- package/test/unit/schemas.test.ts +0 -126
- package/test/unit/snippetLoader.test.ts +0 -56
- package/test/unit/tools.deleteTask.test.ts +0 -19
- package/test/unit/tools.listTasks.test.ts +0 -126
- package/tsconfig.json +0 -19
- package/vitest.config.ts +0 -8
- package/vitest.integration.config.ts +0 -18
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
## Why
|
|
2
|
-
|
|
3
|
-
`list_tasks` currently returns all tasks in scope with no filtering — an LLM asking "what's overdue?" on a large database receives thousands of tasks and must reason over the entire set. Filtering in the snippet before the JXA bridge means the LLM only sees the relevant tasks, making planning queries fast and practical.
|
|
4
|
-
|
|
5
|
-
## What Changes
|
|
6
|
-
|
|
7
|
-
- `list_tasks` accepts an optional `filter` object: `flagged`, `status` (array), `tagId`, `dueBeforeDate`
|
|
8
|
-
- `list_tasks` accepts an optional `limit` (default 200)
|
|
9
|
-
- **BREAKING**: Default behavior changes — when no `status` filter is given, complete and dropped tasks are excluded. Pass `status: ["complete"]` explicitly to retrieve completed tasks.
|
|
10
|
-
- `TaskSummary` is enriched with `dueDate` and `tagIds` fields so the LLM can reason about results without fetching full task detail for each match
|
|
11
|
-
|
|
12
|
-
## Capabilities
|
|
13
|
-
|
|
14
|
-
### New Capabilities
|
|
15
|
-
- `task-filtering`: Filter parameters and enriched summary for `list_tasks`
|
|
16
|
-
|
|
17
|
-
### Modified Capabilities
|
|
18
|
-
- `task-management`: `list_tasks` tool signature changes (new filter/limit params, enriched return shape, changed default behavior)
|
|
19
|
-
|
|
20
|
-
## Impact
|
|
21
|
-
|
|
22
|
-
- `src/snippets/list_tasks.js` — rewritten to apply filters and return enriched fields
|
|
23
|
-
- `src/schemas/shapes.ts` — `TaskSummary` gains `dueDate` and `tagIds`; new `ListTasksFilter` schema
|
|
24
|
-
- `src/tools/listTasks.ts` — input schema updated with filter and limit
|
|
25
|
-
- `test/unit/tools.listTasks.test.ts` — existing unit tests may need updating for new schema shape
|
|
26
|
-
- `test/integration/listTasks.int.test.ts` — new integration tests for filter combinations
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
## ADDED Requirements
|
|
2
|
-
|
|
3
|
-
### Requirement: Filter list_tasks results
|
|
4
|
-
|
|
5
|
-
The system SHALL allow `list_tasks` to accept an optional `filter` object that restricts which tasks are returned. All filter fields are optional and combine as AND conditions. When `filter.status` is omitted, the tool SHALL exclude tasks with status `complete` or `dropped` by default.
|
|
6
|
-
|
|
7
|
-
Filter fields:
|
|
8
|
-
- `flagged` (boolean): when `true`, return only flagged tasks
|
|
9
|
-
- `status` (array of TaskStatus): return only tasks whose status is in the array; overrides the default exclusion of complete/dropped
|
|
10
|
-
- `tagId` (string): return only tasks that have this tag assigned
|
|
11
|
-
- `dueBeforeDate` (ISO datetime string): return only tasks where `dueDate` is non-null and on or before this date
|
|
12
|
-
|
|
13
|
-
#### Scenario: Filter by flagged
|
|
14
|
-
- **WHEN** `list_tasks` is called with `{ scope: { all: true }, filter: { flagged: true } }`
|
|
15
|
-
- **THEN** the tool returns only tasks where `flagged` is `true`, excluding complete and dropped tasks
|
|
16
|
-
|
|
17
|
-
#### Scenario: Filter by status array
|
|
18
|
-
- **WHEN** `list_tasks` is called with `{ scope: { all: true }, filter: { status: ["overdue", "dueSoon"] } }`
|
|
19
|
-
- **THEN** the tool returns only tasks with status `overdue` or `dueSoon`
|
|
20
|
-
|
|
21
|
-
#### Scenario: Filter by tagId
|
|
22
|
-
- **WHEN** `list_tasks` is called with `{ scope: { projectId: "abc123" }, filter: { tagId: "tag456" } }`
|
|
23
|
-
- **THEN** the tool returns only tasks in that project that have the specified tag assigned
|
|
24
|
-
|
|
25
|
-
#### Scenario: Filter by dueBeforeDate
|
|
26
|
-
- **WHEN** `list_tasks` is called with `{ scope: { all: true }, filter: { dueBeforeDate: "2026-04-09T23:59:59Z" } }`
|
|
27
|
-
- **THEN** the tool returns only tasks with a non-null dueDate on or before the specified date, excluding complete and dropped tasks
|
|
28
|
-
|
|
29
|
-
#### Scenario: Default excludes complete and dropped
|
|
30
|
-
- **WHEN** `list_tasks` is called with no filter
|
|
31
|
-
- **THEN** the tool returns tasks with any status except `complete` and `dropped`
|
|
32
|
-
|
|
33
|
-
#### Scenario: Explicit status filter overrides default exclusion
|
|
34
|
-
- **WHEN** `list_tasks` is called with `{ filter: { status: ["complete"] } }`
|
|
35
|
-
- **THEN** the tool returns completed tasks (the default exclusion does not apply)
|
|
36
|
-
|
|
37
|
-
#### Scenario: Combined filters act as AND
|
|
38
|
-
- **WHEN** `list_tasks` is called with `{ filter: { flagged: true, tagId: "tag456" } }`
|
|
39
|
-
- **THEN** the tool returns only tasks that are both flagged AND have that tag
|
|
40
|
-
|
|
41
|
-
### Requirement: Limit list_tasks results
|
|
42
|
-
|
|
43
|
-
The system SHALL allow `list_tasks` to accept an optional `limit` integer that caps the number of tasks returned. When `limit` is omitted, a default of 200 SHALL apply.
|
|
44
|
-
|
|
45
|
-
#### Scenario: Default limit of 200
|
|
46
|
-
- **WHEN** `list_tasks` is called without a `limit` and more than 200 tasks match
|
|
47
|
-
- **THEN** the tool returns at most 200 tasks
|
|
48
|
-
|
|
49
|
-
#### Scenario: Custom limit
|
|
50
|
-
- **WHEN** `list_tasks` is called with `{ limit: 50 }`
|
|
51
|
-
- **THEN** the tool returns at most 50 tasks
|
|
52
|
-
|
|
53
|
-
### Requirement: Enriched task summary includes dueDate and tagIds
|
|
54
|
-
|
|
55
|
-
The `TaskSummary` returned by `list_tasks` SHALL include `dueDate` (ISO datetime or null) and `tagIds` (array of tag ID strings) in addition to existing fields.
|
|
56
|
-
|
|
57
|
-
#### Scenario: Summary includes dueDate
|
|
58
|
-
- **WHEN** `list_tasks` returns a task that has a due date set
|
|
59
|
-
- **THEN** the task summary includes `dueDate` as an ISO datetime string
|
|
60
|
-
|
|
61
|
-
#### Scenario: Summary includes tagIds
|
|
62
|
-
- **WHEN** `list_tasks` returns a task that has tags assigned
|
|
63
|
-
- **THEN** the task summary includes `tagIds` as an array of tag ID strings
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
## MODIFIED Requirements
|
|
2
|
-
|
|
3
|
-
### Requirement: List tasks with scope filter
|
|
4
|
-
|
|
5
|
-
The system SHALL provide a `list_tasks` tool that returns tasks within a caller-specified scope. The scope SHALL be one of: `{projectId: string}`, `{folderId: string}` (all tasks in projects under that folder, recursively), `{inbox: true}`, or `{all: true}` (the full flattened task list). The tool SHALL accept an optional `filter` object (see task-filtering spec) and an optional `limit` integer (default 200). The tool SHALL return an array of task summaries, each containing `{id, name, status, flagged, containerId, containerType, dueDate, tagIds}`. When no `filter.status` is provided, tasks with status `complete` or `dropped` SHALL be excluded by default.
|
|
6
|
-
|
|
7
|
-
#### Scenario: List tasks in a specific project
|
|
8
|
-
- **WHEN** `list_tasks` is called with `{projectId: "abc123"}`
|
|
9
|
-
- **THEN** the tool returns actionable tasks (non-complete, non-dropped) directly or transitively contained in that project, each carrying the project id as `containerId` and `"project"` as `containerType`
|
|
10
|
-
|
|
11
|
-
#### Scenario: List inbox tasks
|
|
12
|
-
- **WHEN** `list_tasks` is called with `{inbox: true}`
|
|
13
|
-
- **THEN** the tool returns actionable inbox tasks with `containerType` set to `"inbox"`
|
|
14
|
-
|
|
15
|
-
#### Scenario: Invalid scope is rejected at the TS boundary
|
|
16
|
-
- **WHEN** `list_tasks` is called with `{projectId: "abc", inbox: true}` (mutually exclusive scopes)
|
|
17
|
-
- **THEN** the tool returns a validation error before any snippet executes
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
## 1. Schema changes (src/schemas/)
|
|
2
|
-
|
|
3
|
-
- [x] 1.1 Add `dueDate: z.string().datetime().nullable()` and `tagIds: z.array(IdSchema)` to `TaskSummary` in `shapes.ts`
|
|
4
|
-
- [x] 1.2 Define `ListTasksFilter` zod schema: `{ flagged: z.literal(true).optional(), status: z.array(TaskStatus).optional(), tagId: IdSchema.optional(), dueBeforeDate: z.string().datetime().optional() }`
|
|
5
|
-
- [x] 1.3 Export `ListTasksFilter` from `src/schemas/index.ts`
|
|
6
|
-
|
|
7
|
-
## 2. Snippet rewrite (src/snippets/list_tasks.js)
|
|
8
|
-
|
|
9
|
-
- [x] 2.1 Add `dueDate` and `tagIds` to the `mapTask` helper
|
|
10
|
-
- [x] 2.2 Implement default status exclusion: when `args.filter?.status` is absent, exclude `Task.Status.Completed` and `Task.Status.Dropped`
|
|
11
|
-
- [x] 2.3 Implement `flagged` filter: when `args.filter?.flagged` is `true`, exclude non-flagged tasks
|
|
12
|
-
- [x] 2.4 Implement `status` array filter: when `args.filter?.status` is provided, keep only tasks whose status maps to one of the given strings
|
|
13
|
-
- [x] 2.5 Implement `tagId` filter: when `args.filter?.tagId` is provided, keep only tasks that have a tag whose `id.primaryKey` matches
|
|
14
|
-
- [x] 2.6 Implement `dueBeforeDate` filter: when provided, keep only tasks where `task.dueDate` is non-null and `<= new Date(args.filter.dueBeforeDate)`
|
|
15
|
-
- [x] 2.7 Apply `args.limit` (default 200) as a slice after all filters
|
|
16
|
-
|
|
17
|
-
## 3. Tool handler update (src/tools/listTasks.ts)
|
|
18
|
-
|
|
19
|
-
- [x] 3.1 Add `filter: ListTasksFilter.optional()` and `limit: z.number().int().positive().optional()` to `listTasksSchema`
|
|
20
|
-
- [x] 3.2 Pass `filter` and `limit` through to `runSnippet`
|
|
21
|
-
- [x] 3.3 Update tool description to document the filter params and the new default-exclusion behavior
|
|
22
|
-
|
|
23
|
-
## 4. Unit tests (test/unit/)
|
|
24
|
-
|
|
25
|
-
- [x] 4.1 Update `tools.listTasks.test.ts`: add `dueDate` and `tagIds` to any TaskSummary fixtures that are now required fields
|
|
26
|
-
- [x] 4.2 Add schema tests for `ListTasksFilter`: valid filter passes; invalid status enum rejected; dueBeforeDate must be ISO datetime
|
|
27
|
-
|
|
28
|
-
## 5. Integration tests (test/integration/)
|
|
29
|
-
|
|
30
|
-
- [x] 5.1 `listTasksFiltered.int.test.ts`: create tasks with known properties in a fixture folder; verify `flagged: true` filter returns only flagged tasks
|
|
31
|
-
- [x] 5.2 Verify `status` filter: create a completed task; confirm it is absent by default and present when `status: ["complete"]` is passed
|
|
32
|
-
- [x] 5.3 Verify `tagId` filter: assign a tag to one task; confirm only that task is returned
|
|
33
|
-
- [x] 5.4 Verify `dueBeforeDate` filter: create a task with a due date in the past; confirm it is returned; create one due far in the future; confirm it is not
|
|
34
|
-
- [x] 5.5 Verify `limit`: create more tasks than the limit; confirm result is capped
|
|
35
|
-
- [x] 5.6 Verify enriched summary: returned tasks include non-null `dueDate` and `tagIds` when applicable
|
|
36
|
-
|
|
37
|
-
## 6. Verification
|
|
38
|
-
|
|
39
|
-
- [x] 6.1 `npm run typecheck` clean
|
|
40
|
-
- [x] 6.2 `npm test` (unit suite) clean
|
|
41
|
-
- [x] 6.3 Manually run integration suite; verify fixture cleanup
|
|
42
|
-
- [ ] 6.4 Connect to Claude Desktop; exercise filter queries against a real database
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
## Context
|
|
2
|
-
|
|
3
|
-
OmniFocus 4 distinguishes three date types on tasks: `deferDate` (hides until), `plannedDate` (Forecast target), and `dueDate` (hard deadline). The MCP server already handles defer and due with the pattern `isoOrNull(task.deferDate)` for reading and `task.deferDate = args.deferDate ? new Date(args.deferDate) : null` for writing. `plannedDate` follows the identical pattern — confirmed writable via OmniJS.
|
|
4
|
-
|
|
5
|
-
## Goals / Non-Goals
|
|
6
|
-
|
|
7
|
-
**Goals:**
|
|
8
|
-
- Add `plannedDate` to `create_task`, `edit_task`, `get_task`, and all snippets returning `TaskDetail`
|
|
9
|
-
- Follow the exact same pattern as `deferDate` and `dueDate`
|
|
10
|
-
|
|
11
|
-
**Non-Goals:**
|
|
12
|
-
- `effectiveDeferDate` / `effectiveDueDate` (computed fields inherited from projects — read-only, not actionable)
|
|
13
|
-
- Planned date on projects (projects don't have `plannedDate` in OmniJS)
|
|
14
|
-
|
|
15
|
-
## Decisions
|
|
16
|
-
|
|
17
|
-
### Decision 1: Same pattern as existing dates
|
|
18
|
-
|
|
19
|
-
`plannedDate` is added as `z.string().datetime().optional()` in `CreateTaskInput`, `z.string().datetime().nullable().optional()` in `EditTaskInput`, and `z.string().datetime().nullable()` in `TaskDetail`. No new abstractions needed.
|
|
20
|
-
|
|
21
|
-
### Decision 2: All TaskDetail-returning snippets updated
|
|
22
|
-
|
|
23
|
-
Five snippets return `TaskDetail`: `create_task`, `edit_task`, `get_task`, `complete_task`, `drop_task`. All five get `plannedDate: isoOrNull(task.plannedDate)` added to their return object.
|
|
24
|
-
|
|
25
|
-
## Risks / Trade-offs
|
|
26
|
-
|
|
27
|
-
- **Minimal risk** — purely additive, follows established patterns exactly. No behavioral change for existing callers.
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
## Why
|
|
2
|
-
|
|
3
|
-
OmniFocus 4 has three distinct date concepts — defer, planned, and due — but the MCP server only exposes `deferDate` and `dueDate`. The missing `plannedDate` is the soft "when I intend to work on this" date that shows up in Forecast view without hiding the task or triggering overdue status. This is the right date type for many recurring tasks (e.g., cleaning, reviews) where you want Forecast visibility without hard-deadline pressure.
|
|
4
|
-
|
|
5
|
-
The OmniJS API already exposes `task.plannedDate` as a fully readable, writable, and clearable property — no API barrier.
|
|
6
|
-
|
|
7
|
-
## What Changes
|
|
8
|
-
|
|
9
|
-
- `create_task`: add optional `plannedDate` field
|
|
10
|
-
- `edit_task`: add optional `plannedDate` field (nullable to clear)
|
|
11
|
-
- `get_task` / `TaskDetail`: return `plannedDate` in the detail record
|
|
12
|
-
- All snippets returning `TaskDetail` (`complete_task`, `drop_task`) include `plannedDate`
|
|
13
|
-
|
|
14
|
-
## Capabilities
|
|
15
|
-
|
|
16
|
-
### New Capabilities
|
|
17
|
-
|
|
18
|
-
(none)
|
|
19
|
-
|
|
20
|
-
### Modified Capabilities
|
|
21
|
-
|
|
22
|
-
- `task-write`: `create_task` and `edit_task` gain a `plannedDate` parameter
|
|
23
|
-
- `task-management`: `get_task` returns `plannedDate` in `TaskDetail`
|
|
24
|
-
|
|
25
|
-
## Impact
|
|
26
|
-
|
|
27
|
-
- `src/schemas/shapes.ts`: `CreateTaskInput`, `EditTaskInput`, `TaskDetail` extended
|
|
28
|
-
- `src/snippets/create_task.js`, `edit_task.js`, `get_task.js`, `complete_task.js`, `drop_task.js`: add `plannedDate` field
|
|
29
|
-
- No breaking changes — all new fields are optional; existing callers unaffected
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
## MODIFIED Requirements
|
|
2
|
-
|
|
3
|
-
### Requirement: Get task by ID
|
|
4
|
-
|
|
5
|
-
The system SHALL provide a `get_task` tool that accepts `{id: string}` and returns the full detail record of the named task, including `{id, name, note, status, flagged, deferDate, plannedDate, dueDate, completionDate, estimatedMinutes, containerId, containerType, tagIds, parentTaskId, repetitionRule}`. The `parentTaskId` field SHALL be `null` for top-level tasks and SHALL contain the parent task's `id.primaryKey` for subtasks. The `repetitionRule` field SHALL be `null` when no repetition rule is set, and SHALL be a structured object with `{frequency, interval, daysOfWeek?, method}` when a rule exists. The `plannedDate` field SHALL be `null` when no planned date is set. If no task exists with that ID, the tool SHALL return a structured not-found error.
|
|
6
|
-
|
|
7
|
-
#### Scenario: Existing task returns full detail
|
|
8
|
-
- **WHEN** `get_task` is called with the ID of an existing task
|
|
9
|
-
- **THEN** the tool returns the task's full detail record including all scalar fields, the list of tag IDs assigned to it, `parentTaskId`, `repetitionRule`, and `plannedDate`
|
|
10
|
-
|
|
11
|
-
#### Scenario: Subtask includes parentTaskId
|
|
12
|
-
- **WHEN** `get_task` is called with the ID of a subtask
|
|
13
|
-
- **THEN** the returned record includes `parentTaskId` set to the parent task's stable ID
|
|
14
|
-
|
|
15
|
-
#### Scenario: Task without repetition returns null repetitionRule
|
|
16
|
-
- **WHEN** `get_task` is called for a task with no repetition rule
|
|
17
|
-
- **THEN** the returned TaskDetail includes `repetitionRule: null`
|
|
18
|
-
|
|
19
|
-
#### Scenario: Task with repetition returns structured repetitionRule
|
|
20
|
-
- **WHEN** `get_task` is called for a task that has a repetition rule
|
|
21
|
-
- **THEN** the returned TaskDetail includes `repetitionRule` with `frequency`, `interval`, `method`, and `daysOfWeek` (if applicable)
|
|
22
|
-
|
|
23
|
-
#### Scenario: Task with planned date returns plannedDate
|
|
24
|
-
- **WHEN** `get_task` is called for a task that has a planned date set
|
|
25
|
-
- **THEN** the returned TaskDetail includes `plannedDate` as an ISO datetime string
|
|
26
|
-
|
|
27
|
-
#### Scenario: Missing task returns not-found error
|
|
28
|
-
- **WHEN** `get_task` is called with an ID that does not correspond to any task
|
|
29
|
-
- **THEN** the tool returns a structured error with a not-found code and does not throw an unhandled exception
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
## MODIFIED Requirements
|
|
2
|
-
|
|
3
|
-
### Requirement: Create task
|
|
4
|
-
|
|
5
|
-
The system SHALL provide a `create_task` tool that creates a new OmniFocus task and returns its full detail record. The tool SHALL accept `{name: string, note?: string, flagged?: boolean, deferDate?: string, plannedDate?: string, dueDate?: string, estimatedMinutes?: number, projectId?: string, parentTaskId?: string, tagIds?: string[], repetitionRule?: RepetitionRuleInput}`. Placement SHALL be determined as follows: if both `projectId` and `parentTaskId` are provided the tool SHALL return an error; if only `projectId` is provided the task is placed at the project root; if only `parentTaskId` is provided the task is created as a subtask inheriting its parent's project; if neither is provided the task is placed in the inbox. When `repetitionRule` is provided, the task SHALL have the specified recurrence set at creation.
|
|
6
|
-
|
|
7
|
-
#### Scenario: Create inbox task
|
|
8
|
-
- **WHEN** `create_task` is called with `{name: "Buy milk"}` and no `projectId` or `parentTaskId`
|
|
9
|
-
- **THEN** the tool creates the task in the OmniFocus inbox and returns its full detail record including a stable `id`
|
|
10
|
-
|
|
11
|
-
#### Scenario: Create task in a project
|
|
12
|
-
- **WHEN** `create_task` is called with `{name: "Write tests", projectId: "abc123"}`
|
|
13
|
-
- **THEN** the tool creates the task at the root of the specified project and returns its full detail record
|
|
14
|
-
|
|
15
|
-
#### Scenario: Create subtask
|
|
16
|
-
- **WHEN** `create_task` is called with `{name: "Review PR", parentTaskId: "xyz789"}`
|
|
17
|
-
- **THEN** the tool creates the task as a child of the specified parent task and returns its full detail record
|
|
18
|
-
|
|
19
|
-
#### Scenario: Ambiguous placement is rejected
|
|
20
|
-
- **WHEN** `create_task` is called with both `projectId` and `parentTaskId`
|
|
21
|
-
- **THEN** the tool returns a validation error before any snippet executes
|
|
22
|
-
|
|
23
|
-
#### Scenario: Non-existent project returns not-found error
|
|
24
|
-
- **WHEN** `create_task` is called with a `projectId` that does not correspond to any project
|
|
25
|
-
- **THEN** the tool returns a structured not-found error
|
|
26
|
-
|
|
27
|
-
#### Scenario: Create task with repetition rule
|
|
28
|
-
- **WHEN** `create_task` is called with `{ name: "Weekly review", repetitionRule: { frequency: "weekly", interval: 1, method: "start" } }`
|
|
29
|
-
- **THEN** the task is created with the specified recurrence and the returned TaskDetail includes the parsed repetitionRule
|
|
30
|
-
|
|
31
|
-
#### Scenario: Create task with planned date
|
|
32
|
-
- **WHEN** `create_task` is called with `{ name: "Clean kitchen", plannedDate: "2026-04-15T09:00:00Z" }`
|
|
33
|
-
- **THEN** the task is created with the specified planned date and the returned TaskDetail includes `plannedDate`
|
|
34
|
-
|
|
35
|
-
### Requirement: Edit task
|
|
36
|
-
|
|
37
|
-
The system SHALL provide an `edit_task` tool that modifies an existing task and returns its updated full detail record. The tool SHALL accept `{id: string}` plus any subset of `{name?: string, note?: string, flagged?: boolean, deferDate?: string | null, plannedDate?: string | null, dueDate?: string | null, estimatedMinutes?: number | null, tagIds?: string[], repetitionRule?: RepetitionRuleInput | null}`. Fields omitted from the call SHALL be left unchanged. When `tagIds` is provided it SHALL replace the task's entire tag set; when omitted, tags SHALL be unchanged. Passing `null` for a date or `estimatedMinutes` SHALL clear the field. Passing `repetitionRule: null` SHALL clear the task's recurrence; passing a `RepetitionRuleInput` object SHALL set or replace the recurrence; omitting `repetitionRule` SHALL leave the existing recurrence unchanged.
|
|
38
|
-
|
|
39
|
-
#### Scenario: Edit a single field
|
|
40
|
-
- **WHEN** `edit_task` is called with `{id: "abc123", flagged: true}`
|
|
41
|
-
- **THEN** only the `flagged` field is changed; all other fields retain their previous values
|
|
42
|
-
|
|
43
|
-
#### Scenario: Replace tag set
|
|
44
|
-
- **WHEN** `edit_task` is called with `{id: "abc123", tagIds: ["t1", "t2"]}`
|
|
45
|
-
- **THEN** the task's tags are set to exactly `["t1", "t2"]`, replacing any previously assigned tags
|
|
46
|
-
|
|
47
|
-
#### Scenario: Clear a date field
|
|
48
|
-
- **WHEN** `edit_task` is called with `{id: "abc123", dueDate: null}`
|
|
49
|
-
- **THEN** the task's due date is cleared
|
|
50
|
-
|
|
51
|
-
#### Scenario: Clear planned date
|
|
52
|
-
- **WHEN** `edit_task` is called with `{id: "abc123", plannedDate: null}`
|
|
53
|
-
- **THEN** the task's planned date is cleared
|
|
54
|
-
|
|
55
|
-
#### Scenario: Non-existent task returns not-found error
|
|
56
|
-
- **WHEN** `edit_task` is called with an ID that does not correspond to any task
|
|
57
|
-
- **THEN** the tool returns a structured not-found error
|
|
58
|
-
|
|
59
|
-
#### Scenario: Non-existent tag ID returns not-found error
|
|
60
|
-
- **WHEN** `edit_task` is called with a `tagIds` array containing an ID that does not correspond to any tag
|
|
61
|
-
- **THEN** the tool returns a structured not-found error and the task is not modified
|
|
62
|
-
|
|
63
|
-
#### Scenario: Set repetition via edit
|
|
64
|
-
- **WHEN** `edit_task` is called with `{ id: "t1", repetitionRule: { frequency: "monthly", interval: 1, method: "dueDate" } }`
|
|
65
|
-
- **THEN** the task's recurrence is set and all other fields are unchanged
|
|
66
|
-
|
|
67
|
-
#### Scenario: Clear repetition via edit
|
|
68
|
-
- **WHEN** `edit_task` is called with `{ id: "t1", repetitionRule: null }`
|
|
69
|
-
- **THEN** the task's recurrence is cleared and all other fields are unchanged
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
## 1. Schema changes (src/schemas/)
|
|
2
|
-
|
|
3
|
-
- [x] 1.1 Add `plannedDate: z.string().datetime().optional()` to `CreateTaskInput` in `shapes.ts`
|
|
4
|
-
- [x] 1.2 Add `plannedDate: z.string().datetime().nullable().optional()` to `EditTaskInput` in `shapes.ts`
|
|
5
|
-
- [x] 1.3 Add `plannedDate: z.string().datetime().nullable()` to `TaskDetail` in `shapes.ts`
|
|
6
|
-
|
|
7
|
-
## 2. Snippet updates (src/snippets/)
|
|
8
|
-
|
|
9
|
-
- [x] 2.1 Update `create_task.js`: set `task.plannedDate` when provided; include `plannedDate: isoOrNull(task.plannedDate)` in returned TaskDetail
|
|
10
|
-
- [x] 2.2 Update `edit_task.js`: set/clear `task.plannedDate` using same pattern as deferDate/dueDate; include in returned TaskDetail
|
|
11
|
-
- [x] 2.3 Update `get_task.js`: include `plannedDate: isoOrNull(task.plannedDate)` in returned detail
|
|
12
|
-
- [x] 2.4 Update `complete_task.js`: include `plannedDate: isoOrNull(task.plannedDate)` in returned TaskDetail
|
|
13
|
-
- [x] 2.5 Update `drop_task.js`: include `plannedDate: isoOrNull(task.plannedDate)` in returned TaskDetail
|
|
14
|
-
|
|
15
|
-
## 3. Integration tests (test/integration/)
|
|
16
|
-
|
|
17
|
-
- [x] 3.1 `plannedDate.int.test.ts`: create task with plannedDate; verify get_task returns it
|
|
18
|
-
- [x] 3.2 `plannedDate.int.test.ts`: edit task to set plannedDate; verify get_task reflects it
|
|
19
|
-
- [x] 3.3 `plannedDate.int.test.ts`: edit task to clear plannedDate (null); verify get_task returns null
|
|
20
|
-
- [x] 3.4 `plannedDate.int.test.ts`: create task without plannedDate; verify get_task returns null (backward compat)
|
|
21
|
-
|
|
22
|
-
## 4. Verification
|
|
23
|
-
|
|
24
|
-
- [x] 4.1 `npm run typecheck` clean
|
|
25
|
-
- [x] 4.2 `npm test` (unit suite) clean
|
|
26
|
-
- [x] 4.3 Manually run integration suite
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
## Context
|
|
2
|
-
|
|
3
|
-
OmniFocus tasks carry a `Task.RepetitionRule` which combines an ICS RRULE string with a `RepetitionMethod` enum (`Fixed`, `DueDate`, `Start`). The OmniJS API is:
|
|
4
|
-
- `task.repetitionRule = new Task.RepetitionRule(rruleString, method)` — set
|
|
5
|
-
- `task.repetitionRule = null` — clear
|
|
6
|
-
- `task.repetitionRule.ruleString` — read the RRULE back
|
|
7
|
-
- `task.repetitionRule.method` — read the method back
|
|
8
|
-
|
|
9
|
-
The OmniFocus UI exposes: frequency (daily/weekly/monthly/yearly), interval (every N units), days-of-week (weekly only), and repeat method (fixed interval / due date / completion date). This change targets exactly that surface.
|
|
10
|
-
|
|
11
|
-
## Goals / Non-Goals
|
|
12
|
-
|
|
13
|
-
**Goals:**
|
|
14
|
-
- `create_task`: accept optional `repetitionRule` to set recurrence at creation
|
|
15
|
-
- `edit_task`: accept optional `repetitionRule` (nullable to clear) on existing tasks
|
|
16
|
-
- `get_task` / `TaskDetail`: return current repetition as structured fields (or null)
|
|
17
|
-
- Validate that `daysOfWeek` is only meaningful on `frequency: "weekly"`
|
|
18
|
-
|
|
19
|
-
**Non-Goals:**
|
|
20
|
-
- RRULE patterns not reachable from the OmniFocus UI (e.g., "every 2nd Tuesday of the month", end-by-count, end-by-date)
|
|
21
|
-
- Project repetition (projects don't have `repetitionRule` in OmniJS)
|
|
22
|
-
- Raw RRULE escape hatch — scope is UI surface only
|
|
23
|
-
|
|
24
|
-
## Decisions
|
|
25
|
-
|
|
26
|
-
### Decision 1: Structured schema, no raw rrule
|
|
27
|
-
|
|
28
|
-
Input schema uses structured fields (`frequency`, `interval`, `daysOfWeek`, `method`). The snippet constructs the RRULE internally via `new Task.RepetitionRule(rruleString, methodEnum)`. This keeps the API LLM-friendly and prevents invalid RRULE strings from being passed in.
|
|
29
|
-
|
|
30
|
-
Considered: raw `{ rrule, method }` input. Rejected because LLMs may produce invalid RRULE syntax, and the UI surface is small enough to model fully without an escape hatch.
|
|
31
|
-
|
|
32
|
-
**OmniJS API notes (discovered empirically):**
|
|
33
|
-
- Constructor: `new Task.RepetitionRule(rrule, method)` — NOT `.make()`
|
|
34
|
-
- `Task.RepetitionMethod.Fixed` — fixed interval
|
|
35
|
-
- `Task.RepetitionMethod.DueDate` — repeat from due date
|
|
36
|
-
- `Task.RepetitionMethod.DeferUntilDate` — repeat from completion date (maps to our `"start"` method)
|
|
37
|
-
- Enum values have no `.name` property; use `String(enumValue)` which returns e.g. `"[object Task.RepetitionMethod: DeferUntilDate]"`
|
|
38
|
-
|
|
39
|
-
### Decision 2: RepetitionRuleInput is its own schema, embedded in CreateTaskInput and EditTaskInput
|
|
40
|
-
|
|
41
|
-
```
|
|
42
|
-
RepetitionRuleInput = z.object({
|
|
43
|
-
frequency: z.enum(["daily", "weekly", "monthly", "yearly"]),
|
|
44
|
-
interval: z.number().int().positive().default(1),
|
|
45
|
-
daysOfWeek: z.array(z.enum(["sunday","monday","tuesday","wednesday","thursday","friday","saturday"])).optional(),
|
|
46
|
-
method: z.enum(["fixed", "dueDate", "start"]),
|
|
47
|
-
})
|
|
48
|
-
.refine(d => !d.daysOfWeek || d.frequency === "weekly", {
|
|
49
|
-
message: "daysOfWeek is only valid when frequency is 'weekly'"
|
|
50
|
-
})
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
In `EditTaskInput`: `repetitionRule: RepetitionRuleInput.nullable().optional()` — omit to leave unchanged, `null` to clear, object to set.
|
|
54
|
-
|
|
55
|
-
### Decision 3: Read side returns structured fields parsed from RRULE
|
|
56
|
-
|
|
57
|
-
`TaskDetail.repetitionRule` is `RepetitionRuleDetail | null`:
|
|
58
|
-
```
|
|
59
|
-
RepetitionRuleDetail = z.object({
|
|
60
|
-
frequency: z.enum(["daily","weekly","monthly","yearly"]),
|
|
61
|
-
interval: z.number(),
|
|
62
|
-
daysOfWeek: z.array(...).optional(),
|
|
63
|
-
method: z.enum(["fixed","dueDate","start"]),
|
|
64
|
-
})
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
The snippet parses `task.repetitionRule.ruleString` using simple regex/split to extract FREQ, INTERVAL, and BYDAY. If parsing fails (unsupported RRULE set outside the UI), return `{ rrule: rawString, method }` as a fallback with a `_raw: true` flag — this way the LLM sees something rather than crashing.
|
|
68
|
-
|
|
69
|
-
### Decision 4: RRULE construction in snippet, not TypeScript
|
|
70
|
-
|
|
71
|
-
Keeps the logic next to the OmniJS API call. TypeScript layer only validates the structured input and passes it through.
|
|
72
|
-
|
|
73
|
-
### Decision 5: daysOfWeek maps to BYDAY abbreviations
|
|
74
|
-
|
|
75
|
-
Day mapping: sunday→SU, monday→MO, tuesday→TU, wednesday→WE, thursday→TH, friday→FR, saturday→SA. When `daysOfWeek` is absent for a weekly rule, no `BYDAY` component is added (repeats on the same weekday as the task's due/defer date).
|
|
76
|
-
|
|
77
|
-
## Risks / Trade-offs
|
|
78
|
-
|
|
79
|
-
- **Tasks without a due or defer date + Fixed method**: OmniFocus behavior is undefined (no anchor date). Document in tool description; not validated by the schema.
|
|
80
|
-
- **RRULE parsing on read**: Simple regex parsing covers the UI surface; complex patterns set via OmniJS directly will hit the `_raw` fallback.
|
|
81
|
-
- **interval default**: Schema defaults `interval` to 1 but it must be passed explicitly in the RRULE string (`INTERVAL=1`) — OmniFocus requires it.
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
## Why
|
|
2
|
-
|
|
3
|
-
OmniFocus tasks support repeat intervals, but the MCP server has no way to set, edit, or read repetition rules — forcing users to manually configure recurrence in OmniFocus after every task creation. This gap means AI-driven task creation is incomplete for any workflow that involves repeating tasks.
|
|
4
|
-
|
|
5
|
-
## What Changes
|
|
6
|
-
|
|
7
|
-
- `create_task`: add optional `repetitionRule` field to set recurrence at creation time
|
|
8
|
-
- `edit_task`: add optional `repetitionRule` field (nullable to clear recurrence) on existing tasks
|
|
9
|
-
- `get_task` / `TaskDetail`: return current `repetitionRule` as structured fields (or null if none)
|
|
10
|
-
- New `RepetitionRuleInput` and `RepetitionRuleDetail` schemas covering the OmniFocus UI surface: frequency, interval, optional days-of-week (weekly only), and repeat method
|
|
11
|
-
|
|
12
|
-
## Capabilities
|
|
13
|
-
|
|
14
|
-
### New Capabilities
|
|
15
|
-
|
|
16
|
-
- `recurrence`: Requirements for reading and writing OmniFocus task repetition rules (frequency, interval, daysOfWeek, method). Note: the `recurrence` spec already exists as a placeholder — this change fills it in.
|
|
17
|
-
|
|
18
|
-
### Modified Capabilities
|
|
19
|
-
|
|
20
|
-
- `task-write`: `create_task` and `edit_task` gain a `repetitionRule` parameter
|
|
21
|
-
- `task-management`: `get_task` returns `repetitionRule` in `TaskDetail`
|
|
22
|
-
|
|
23
|
-
## Impact
|
|
24
|
-
|
|
25
|
-
- `src/schemas/shapes.ts`: new `RepetitionRuleInput` and `RepetitionRuleDetail` types; `CreateTaskInput`, `EditTaskInput`, `TaskDetail` extended
|
|
26
|
-
- `src/schemas/index.ts`: new exports
|
|
27
|
-
- `src/snippets/create_task.js`, `edit_task.js`, `get_task.js`: snippet updates
|
|
28
|
-
- No breaking changes — all new fields are optional; existing callers unaffected
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
## MODIFIED Requirements
|
|
2
|
-
|
|
3
|
-
### Requirement: Capability declared
|
|
4
|
-
|
|
5
|
-
The `recurrence` capability covers the construction, assignment, reading, and clearing of `Task.RepetitionRule` values on OmniFocus tasks. The capability exposes a structured schema matching the OmniFocus UI surface: `frequency` (`daily` | `weekly` | `monthly` | `yearly`), `interval` (every N units, ≥1), `daysOfWeek` (weekly only, any subset of the 7 days), and `method` (`fixed` | `dueDate` | `start`). Repetition is set and cleared through `create_task` and `edit_task`; the current rule is returned by `get_task` as structured fields parsed from the underlying RRULE. Raw RRULE strings are not exposed as input — all recurrence is expressed through the structured schema.
|
|
6
|
-
|
|
7
|
-
#### Scenario: Capability is named and scoped
|
|
8
|
-
- **WHEN** a change proposes adding or modifying recurrence-related behavior
|
|
9
|
-
- **THEN** it lands requirements under this capability
|
|
10
|
-
|
|
11
|
-
## ADDED Requirements
|
|
12
|
-
|
|
13
|
-
### Requirement: Set repetition rule on task
|
|
14
|
-
|
|
15
|
-
The system SHALL accept a `repetitionRule` field in `create_task` and `edit_task`. When provided, the snippet SHALL construct an ICS RRULE string from the structured fields and assign it via `Task.RepetitionRule.make(rrule, method)`. The `daysOfWeek` field SHALL only be valid when `frequency` is `"weekly"`; providing it for any other frequency SHALL be a validation error. When `edit_task` receives `repetitionRule: null`, the snippet SHALL assign `task.repetitionRule = null` to clear the rule. When `edit_task` omits `repetitionRule` entirely, the existing rule SHALL be left unchanged.
|
|
16
|
-
|
|
17
|
-
#### Scenario: Set daily repetition at creation
|
|
18
|
-
- **WHEN** `create_task` is called with `{ name: "Stand-up", repetitionRule: { frequency: "daily", interval: 1, method: "fixed" } }`
|
|
19
|
-
- **THEN** the created task has a repetition rule of every day (fixed interval) and the returned TaskDetail includes the parsed repetitionRule
|
|
20
|
-
|
|
21
|
-
#### Scenario: Set weekly repetition on specific days
|
|
22
|
-
- **WHEN** `create_task` is called with `{ repetitionRule: { frequency: "weekly", interval: 1, daysOfWeek: ["monday", "wednesday", "friday"], method: "start" } }`
|
|
23
|
-
- **THEN** the task repeats every Mon/Wed/Fri from completion date
|
|
24
|
-
|
|
25
|
-
#### Scenario: Edit task to add repetition
|
|
26
|
-
- **WHEN** `edit_task` is called with `{ id: "t1", repetitionRule: { frequency: "monthly", interval: 1, method: "dueDate" } }`
|
|
27
|
-
- **THEN** the task's repetition rule is set to monthly (due date) and all other fields are unchanged
|
|
28
|
-
|
|
29
|
-
#### Scenario: Clear repetition via null
|
|
30
|
-
- **WHEN** `edit_task` is called with `{ id: "t1", repetitionRule: null }`
|
|
31
|
-
- **THEN** the task's repetition rule is cleared and `get_task` returns `repetitionRule: null` for that task
|
|
32
|
-
|
|
33
|
-
#### Scenario: daysOfWeek on non-weekly frequency is a validation error
|
|
34
|
-
- **WHEN** `create_task` or `edit_task` is called with `{ repetitionRule: { frequency: "daily", daysOfWeek: ["monday"], method: "fixed" } }`
|
|
35
|
-
- **THEN** the tool returns a validation error before any snippet executes
|
|
36
|
-
|
|
37
|
-
### Requirement: Return repetition rule from get_task
|
|
38
|
-
|
|
39
|
-
The system SHALL include a `repetitionRule` field in the `TaskDetail` returned by `get_task`. When a task has no repetition rule, the field SHALL be `null`. When a task has a rule, the field SHALL be a structured object with `frequency`, `interval`, `daysOfWeek` (omitted when not applicable), and `method`, parsed from the underlying RRULE and RepetitionMethod.
|
|
40
|
-
|
|
41
|
-
#### Scenario: get_task returns null when no repetition set
|
|
42
|
-
- **WHEN** `get_task` is called for a task with no repetition rule
|
|
43
|
-
- **THEN** the returned TaskDetail includes `repetitionRule: null`
|
|
44
|
-
|
|
45
|
-
#### Scenario: get_task returns structured repetition fields
|
|
46
|
-
- **WHEN** `get_task` is called for a task with a weekly repetition rule
|
|
47
|
-
- **THEN** the returned TaskDetail includes `repetitionRule` with `frequency: "weekly"`, correct `interval`, `daysOfWeek` array, and `method`
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
## MODIFIED Requirements
|
|
2
|
-
|
|
3
|
-
### Requirement: Get task by ID
|
|
4
|
-
|
|
5
|
-
The system SHALL provide a `get_task` tool that accepts `{id: string}` and returns the full detail record of the named task, including `{id, name, note, status, flagged, deferDate, dueDate, completionDate, estimatedMinutes, containerId, containerType, tagIds, parentTaskId, repetitionRule}`. The `parentTaskId` field SHALL be `null` for top-level tasks and SHALL contain the parent task's `id.primaryKey` for subtasks. The `repetitionRule` field SHALL be `null` when no repetition rule is set, and SHALL be a structured object with `{frequency, interval, daysOfWeek?, method}` when a rule exists. If no task exists with that ID, the tool SHALL return a structured not-found error.
|
|
6
|
-
|
|
7
|
-
#### Scenario: Existing task returns full detail
|
|
8
|
-
- **WHEN** `get_task` is called with the ID of an existing task
|
|
9
|
-
- **THEN** the tool returns the task's full detail record including all scalar fields, the list of tag IDs assigned to it, `parentTaskId`, and `repetitionRule`
|
|
10
|
-
|
|
11
|
-
#### Scenario: Subtask includes parentTaskId
|
|
12
|
-
- **WHEN** `get_task` is called with the ID of a subtask
|
|
13
|
-
- **THEN** the returned record includes `parentTaskId` set to the parent task's stable ID
|
|
14
|
-
|
|
15
|
-
#### Scenario: Task without repetition returns null repetitionRule
|
|
16
|
-
- **WHEN** `get_task` is called for a task with no repetition rule
|
|
17
|
-
- **THEN** the returned TaskDetail includes `repetitionRule: null`
|
|
18
|
-
|
|
19
|
-
#### Scenario: Task with repetition returns structured repetitionRule
|
|
20
|
-
- **WHEN** `get_task` is called for a task that has a repetition rule
|
|
21
|
-
- **THEN** the returned TaskDetail includes `repetitionRule` with `frequency`, `interval`, `method`, and `daysOfWeek` (if applicable)
|
|
22
|
-
|
|
23
|
-
#### Scenario: Missing task returns not-found error
|
|
24
|
-
- **WHEN** `get_task` is called with an ID that does not correspond to any task
|
|
25
|
-
- **THEN** the tool returns a structured error with a not-found code and does not throw an unhandled exception
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
## MODIFIED Requirements
|
|
2
|
-
|
|
3
|
-
### Requirement: Create task
|
|
4
|
-
|
|
5
|
-
The system SHALL provide a `create_task` tool that creates a new OmniFocus task and returns its full detail record. The tool SHALL accept `{name: string, note?: string, flagged?: boolean, deferDate?: string, dueDate?: string, estimatedMinutes?: number, projectId?: string, parentTaskId?: string, tagIds?: string[], repetitionRule?: RepetitionRuleInput}`. Placement SHALL be determined as follows: if both `projectId` and `parentTaskId` are provided the tool SHALL return an error; if only `projectId` is provided the task is placed at the project root; if only `parentTaskId` is provided the task is created as a subtask inheriting its parent's project; if neither is provided the task is placed in the inbox. When `repetitionRule` is provided, the task SHALL have the specified recurrence set at creation.
|
|
6
|
-
|
|
7
|
-
#### Scenario: Create inbox task
|
|
8
|
-
- **WHEN** `create_task` is called with `{name: "Buy milk"}` and no `projectId` or `parentTaskId`
|
|
9
|
-
- **THEN** the tool creates the task in the OmniFocus inbox and returns its full detail record including a stable `id`
|
|
10
|
-
|
|
11
|
-
#### Scenario: Create task in a project
|
|
12
|
-
- **WHEN** `create_task` is called with `{name: "Write tests", projectId: "abc123"}`
|
|
13
|
-
- **THEN** the tool creates the task at the root of the specified project and returns its full detail record
|
|
14
|
-
|
|
15
|
-
#### Scenario: Create subtask
|
|
16
|
-
- **WHEN** `create_task` is called with `{name: "Review PR", parentTaskId: "xyz789"}`
|
|
17
|
-
- **THEN** the tool creates the task as a child of the specified parent task and returns its full detail record
|
|
18
|
-
|
|
19
|
-
#### Scenario: Ambiguous placement is rejected
|
|
20
|
-
- **WHEN** `create_task` is called with both `projectId` and `parentTaskId`
|
|
21
|
-
- **THEN** the tool returns a validation error before any snippet executes
|
|
22
|
-
|
|
23
|
-
#### Scenario: Non-existent project returns not-found error
|
|
24
|
-
- **WHEN** `create_task` is called with a `projectId` that does not correspond to any project
|
|
25
|
-
- **THEN** the tool returns a structured not-found error
|
|
26
|
-
|
|
27
|
-
#### Scenario: Create task with repetition rule
|
|
28
|
-
- **WHEN** `create_task` is called with `{ name: "Weekly review", repetitionRule: { frequency: "weekly", interval: 1, method: "start" } }`
|
|
29
|
-
- **THEN** the task is created with the specified recurrence and the returned TaskDetail includes the parsed repetitionRule
|
|
30
|
-
|
|
31
|
-
### Requirement: Edit task
|
|
32
|
-
|
|
33
|
-
The system SHALL provide an `edit_task` tool that modifies an existing task and returns its updated full detail record. The tool SHALL accept `{id: string}` plus any subset of `{name?: string, note?: string, flagged?: boolean, deferDate?: string | null, dueDate?: string | null, estimatedMinutes?: number | null, tagIds?: string[], repetitionRule?: RepetitionRuleInput | null}`. Fields omitted from the call SHALL be left unchanged. When `tagIds` is provided it SHALL replace the task's entire tag set; when omitted, tags SHALL be unchanged. Passing `null` for a date or `estimatedMinutes` SHALL clear the field. Passing `repetitionRule: null` SHALL clear the task's recurrence; passing a `RepetitionRuleInput` object SHALL set or replace the recurrence; omitting `repetitionRule` SHALL leave the existing recurrence unchanged.
|
|
34
|
-
|
|
35
|
-
#### Scenario: Edit a single field
|
|
36
|
-
- **WHEN** `edit_task` is called with `{id: "abc123", flagged: true}`
|
|
37
|
-
- **THEN** only the `flagged` field is changed; all other fields retain their previous values
|
|
38
|
-
|
|
39
|
-
#### Scenario: Replace tag set
|
|
40
|
-
- **WHEN** `edit_task` is called with `{id: "abc123", tagIds: ["t1", "t2"]}`
|
|
41
|
-
- **THEN** the task's tags are set to exactly `["t1", "t2"]`, replacing any previously assigned tags
|
|
42
|
-
|
|
43
|
-
#### Scenario: Clear a date field
|
|
44
|
-
- **WHEN** `edit_task` is called with `{id: "abc123", dueDate: null}`
|
|
45
|
-
- **THEN** the task's due date is cleared
|
|
46
|
-
|
|
47
|
-
#### Scenario: Non-existent task returns not-found error
|
|
48
|
-
- **WHEN** `edit_task` is called with an ID that does not correspond to any task
|
|
49
|
-
- **THEN** the tool returns a structured not-found error
|
|
50
|
-
|
|
51
|
-
#### Scenario: Non-existent tag ID returns not-found error
|
|
52
|
-
- **WHEN** `edit_task` is called with a `tagIds` array containing an ID that does not correspond to any tag
|
|
53
|
-
- **THEN** the tool returns a structured not-found error and the task is not modified
|
|
54
|
-
|
|
55
|
-
#### Scenario: Set repetition via edit
|
|
56
|
-
- **WHEN** `edit_task` is called with `{ id: "t1", repetitionRule: { frequency: "monthly", interval: 1, method: "dueDate" } }`
|
|
57
|
-
- **THEN** the task's recurrence is set and all other fields are unchanged
|
|
58
|
-
|
|
59
|
-
#### Scenario: Clear repetition via edit
|
|
60
|
-
- **WHEN** `edit_task` is called with `{ id: "t1", repetitionRule: null }`
|
|
61
|
-
- **THEN** the task's recurrence is cleared and all other fields are unchanged
|