@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,138 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: openspec-sync-specs
|
|
3
|
-
description: Sync delta specs from a change to main specs. Use when the user wants to update main specs with changes from a delta spec, without archiving the change.
|
|
4
|
-
license: MIT
|
|
5
|
-
compatibility: Requires openspec CLI.
|
|
6
|
-
metadata:
|
|
7
|
-
author: openspec
|
|
8
|
-
version: "1.0"
|
|
9
|
-
generatedBy: "1.2.0"
|
|
10
|
-
---
|
|
11
|
-
|
|
12
|
-
Sync delta specs from a change to main specs.
|
|
13
|
-
|
|
14
|
-
This is an **agent-driven** operation - you will read delta specs and directly edit main specs to apply the changes. This allows intelligent merging (e.g., adding a scenario without copying the entire requirement).
|
|
15
|
-
|
|
16
|
-
**Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.
|
|
17
|
-
|
|
18
|
-
**Steps**
|
|
19
|
-
|
|
20
|
-
1. **If no change name provided, prompt for selection**
|
|
21
|
-
|
|
22
|
-
Run `openspec list --json` to get available changes. Use the **AskUserQuestion tool** to let the user select.
|
|
23
|
-
|
|
24
|
-
Show changes that have delta specs (under `specs/` directory).
|
|
25
|
-
|
|
26
|
-
**IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.
|
|
27
|
-
|
|
28
|
-
2. **Find delta specs**
|
|
29
|
-
|
|
30
|
-
Look for delta spec files in `openspec/changes/<name>/specs/*/spec.md`.
|
|
31
|
-
|
|
32
|
-
Each delta spec file contains sections like:
|
|
33
|
-
- `## ADDED Requirements` - New requirements to add
|
|
34
|
-
- `## MODIFIED Requirements` - Changes to existing requirements
|
|
35
|
-
- `## REMOVED Requirements` - Requirements to remove
|
|
36
|
-
- `## RENAMED Requirements` - Requirements to rename (FROM:/TO: format)
|
|
37
|
-
|
|
38
|
-
If no delta specs found, inform user and stop.
|
|
39
|
-
|
|
40
|
-
3. **For each delta spec, apply changes to main specs**
|
|
41
|
-
|
|
42
|
-
For each capability with a delta spec at `openspec/changes/<name>/specs/<capability>/spec.md`:
|
|
43
|
-
|
|
44
|
-
a. **Read the delta spec** to understand the intended changes
|
|
45
|
-
|
|
46
|
-
b. **Read the main spec** at `openspec/specs/<capability>/spec.md` (may not exist yet)
|
|
47
|
-
|
|
48
|
-
c. **Apply changes intelligently**:
|
|
49
|
-
|
|
50
|
-
**ADDED Requirements:**
|
|
51
|
-
- If requirement doesn't exist in main spec → add it
|
|
52
|
-
- If requirement already exists → update it to match (treat as implicit MODIFIED)
|
|
53
|
-
|
|
54
|
-
**MODIFIED Requirements:**
|
|
55
|
-
- Find the requirement in main spec
|
|
56
|
-
- Apply the changes - this can be:
|
|
57
|
-
- Adding new scenarios (don't need to copy existing ones)
|
|
58
|
-
- Modifying existing scenarios
|
|
59
|
-
- Changing the requirement description
|
|
60
|
-
- Preserve scenarios/content not mentioned in the delta
|
|
61
|
-
|
|
62
|
-
**REMOVED Requirements:**
|
|
63
|
-
- Remove the entire requirement block from main spec
|
|
64
|
-
|
|
65
|
-
**RENAMED Requirements:**
|
|
66
|
-
- Find the FROM requirement, rename to TO
|
|
67
|
-
|
|
68
|
-
d. **Create new main spec** if capability doesn't exist yet:
|
|
69
|
-
- Create `openspec/specs/<capability>/spec.md`
|
|
70
|
-
- Add Purpose section (can be brief, mark as TBD)
|
|
71
|
-
- Add Requirements section with the ADDED requirements
|
|
72
|
-
|
|
73
|
-
4. **Show summary**
|
|
74
|
-
|
|
75
|
-
After applying all changes, summarize:
|
|
76
|
-
- Which capabilities were updated
|
|
77
|
-
- What changes were made (requirements added/modified/removed/renamed)
|
|
78
|
-
|
|
79
|
-
**Delta Spec Format Reference**
|
|
80
|
-
|
|
81
|
-
```markdown
|
|
82
|
-
## ADDED Requirements
|
|
83
|
-
|
|
84
|
-
### Requirement: New Feature
|
|
85
|
-
The system SHALL do something new.
|
|
86
|
-
|
|
87
|
-
#### Scenario: Basic case
|
|
88
|
-
- **WHEN** user does X
|
|
89
|
-
- **THEN** system does Y
|
|
90
|
-
|
|
91
|
-
## MODIFIED Requirements
|
|
92
|
-
|
|
93
|
-
### Requirement: Existing Feature
|
|
94
|
-
#### Scenario: New scenario to add
|
|
95
|
-
- **WHEN** user does A
|
|
96
|
-
- **THEN** system does B
|
|
97
|
-
|
|
98
|
-
## REMOVED Requirements
|
|
99
|
-
|
|
100
|
-
### Requirement: Deprecated Feature
|
|
101
|
-
|
|
102
|
-
## RENAMED Requirements
|
|
103
|
-
|
|
104
|
-
- FROM: `### Requirement: Old Name`
|
|
105
|
-
- TO: `### Requirement: New Name`
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
**Key Principle: Intelligent Merging**
|
|
109
|
-
|
|
110
|
-
Unlike programmatic merging, you can apply **partial updates**:
|
|
111
|
-
- To add a scenario, just include that scenario under MODIFIED - don't copy existing scenarios
|
|
112
|
-
- The delta represents *intent*, not a wholesale replacement
|
|
113
|
-
- Use your judgment to merge changes sensibly
|
|
114
|
-
|
|
115
|
-
**Output On Success**
|
|
116
|
-
|
|
117
|
-
```
|
|
118
|
-
## Specs Synced: <change-name>
|
|
119
|
-
|
|
120
|
-
Updated main specs:
|
|
121
|
-
|
|
122
|
-
**<capability-1>**:
|
|
123
|
-
- Added requirement: "New Feature"
|
|
124
|
-
- Modified requirement: "Existing Feature" (added 1 scenario)
|
|
125
|
-
|
|
126
|
-
**<capability-2>**:
|
|
127
|
-
- Created new spec file
|
|
128
|
-
- Added requirement: "Another Feature"
|
|
129
|
-
|
|
130
|
-
Main specs are now updated. The change remains active - archive when implementation is complete.
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
**Guardrails**
|
|
134
|
-
- Read both delta and main specs before making changes
|
|
135
|
-
- Preserve existing content not mentioned in delta
|
|
136
|
-
- If something is unclear, ask for clarification
|
|
137
|
-
- Show what you're changing as you go
|
|
138
|
-
- The operation should be idempotent - running twice should give same result
|
|
@@ -1,168 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: openspec-verify-change
|
|
3
|
-
description: Verify implementation matches change artifacts. Use when the user wants to validate that implementation is complete, correct, and coherent before archiving.
|
|
4
|
-
license: MIT
|
|
5
|
-
compatibility: Requires openspec CLI.
|
|
6
|
-
metadata:
|
|
7
|
-
author: openspec
|
|
8
|
-
version: "1.0"
|
|
9
|
-
generatedBy: "1.2.0"
|
|
10
|
-
---
|
|
11
|
-
|
|
12
|
-
Verify that an implementation matches the change artifacts (specs, tasks, design).
|
|
13
|
-
|
|
14
|
-
**Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.
|
|
15
|
-
|
|
16
|
-
**Steps**
|
|
17
|
-
|
|
18
|
-
1. **If no change name provided, prompt for selection**
|
|
19
|
-
|
|
20
|
-
Run `openspec list --json` to get available changes. Use the **AskUserQuestion tool** to let the user select.
|
|
21
|
-
|
|
22
|
-
Show changes that have implementation tasks (tasks artifact exists).
|
|
23
|
-
Include the schema used for each change if available.
|
|
24
|
-
Mark changes with incomplete tasks as "(In Progress)".
|
|
25
|
-
|
|
26
|
-
**IMPORTANT**: Do NOT guess or auto-select a change. Always let the user choose.
|
|
27
|
-
|
|
28
|
-
2. **Check status to understand the schema**
|
|
29
|
-
```bash
|
|
30
|
-
openspec status --change "<name>" --json
|
|
31
|
-
```
|
|
32
|
-
Parse the JSON to understand:
|
|
33
|
-
- `schemaName`: The workflow being used (e.g., "spec-driven")
|
|
34
|
-
- Which artifacts exist for this change
|
|
35
|
-
|
|
36
|
-
3. **Get the change directory and load artifacts**
|
|
37
|
-
|
|
38
|
-
```bash
|
|
39
|
-
openspec instructions apply --change "<name>" --json
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
This returns the change directory and context files. Read all available artifacts from `contextFiles`.
|
|
43
|
-
|
|
44
|
-
4. **Initialize verification report structure**
|
|
45
|
-
|
|
46
|
-
Create a report structure with three dimensions:
|
|
47
|
-
- **Completeness**: Track tasks and spec coverage
|
|
48
|
-
- **Correctness**: Track requirement implementation and scenario coverage
|
|
49
|
-
- **Coherence**: Track design adherence and pattern consistency
|
|
50
|
-
|
|
51
|
-
Each dimension can have CRITICAL, WARNING, or SUGGESTION issues.
|
|
52
|
-
|
|
53
|
-
5. **Verify Completeness**
|
|
54
|
-
|
|
55
|
-
**Task Completion**:
|
|
56
|
-
- If tasks.md exists in contextFiles, read it
|
|
57
|
-
- Parse checkboxes: `- [ ]` (incomplete) vs `- [x]` (complete)
|
|
58
|
-
- Count complete vs total tasks
|
|
59
|
-
- If incomplete tasks exist:
|
|
60
|
-
- Add CRITICAL issue for each incomplete task
|
|
61
|
-
- Recommendation: "Complete task: <description>" or "Mark as done if already implemented"
|
|
62
|
-
|
|
63
|
-
**Spec Coverage**:
|
|
64
|
-
- If delta specs exist in `openspec/changes/<name>/specs/`:
|
|
65
|
-
- Extract all requirements (marked with "### Requirement:")
|
|
66
|
-
- For each requirement:
|
|
67
|
-
- Search codebase for keywords related to the requirement
|
|
68
|
-
- Assess if implementation likely exists
|
|
69
|
-
- If requirements appear unimplemented:
|
|
70
|
-
- Add CRITICAL issue: "Requirement not found: <requirement name>"
|
|
71
|
-
- Recommendation: "Implement requirement X: <description>"
|
|
72
|
-
|
|
73
|
-
6. **Verify Correctness**
|
|
74
|
-
|
|
75
|
-
**Requirement Implementation Mapping**:
|
|
76
|
-
- For each requirement from delta specs:
|
|
77
|
-
- Search codebase for implementation evidence
|
|
78
|
-
- If found, note file paths and line ranges
|
|
79
|
-
- Assess if implementation matches requirement intent
|
|
80
|
-
- If divergence detected:
|
|
81
|
-
- Add WARNING: "Implementation may diverge from spec: <details>"
|
|
82
|
-
- Recommendation: "Review <file>:<lines> against requirement X"
|
|
83
|
-
|
|
84
|
-
**Scenario Coverage**:
|
|
85
|
-
- For each scenario in delta specs (marked with "#### Scenario:"):
|
|
86
|
-
- Check if conditions are handled in code
|
|
87
|
-
- Check if tests exist covering the scenario
|
|
88
|
-
- If scenario appears uncovered:
|
|
89
|
-
- Add WARNING: "Scenario not covered: <scenario name>"
|
|
90
|
-
- Recommendation: "Add test or implementation for scenario: <description>"
|
|
91
|
-
|
|
92
|
-
7. **Verify Coherence**
|
|
93
|
-
|
|
94
|
-
**Design Adherence**:
|
|
95
|
-
- If design.md exists in contextFiles:
|
|
96
|
-
- Extract key decisions (look for sections like "Decision:", "Approach:", "Architecture:")
|
|
97
|
-
- Verify implementation follows those decisions
|
|
98
|
-
- If contradiction detected:
|
|
99
|
-
- Add WARNING: "Design decision not followed: <decision>"
|
|
100
|
-
- Recommendation: "Update implementation or revise design.md to match reality"
|
|
101
|
-
- If no design.md: Skip design adherence check, note "No design.md to verify against"
|
|
102
|
-
|
|
103
|
-
**Code Pattern Consistency**:
|
|
104
|
-
- Review new code for consistency with project patterns
|
|
105
|
-
- Check file naming, directory structure, coding style
|
|
106
|
-
- If significant deviations found:
|
|
107
|
-
- Add SUGGESTION: "Code pattern deviation: <details>"
|
|
108
|
-
- Recommendation: "Consider following project pattern: <example>"
|
|
109
|
-
|
|
110
|
-
8. **Generate Verification Report**
|
|
111
|
-
|
|
112
|
-
**Summary Scorecard**:
|
|
113
|
-
```
|
|
114
|
-
## Verification Report: <change-name>
|
|
115
|
-
|
|
116
|
-
### Summary
|
|
117
|
-
| Dimension | Status |
|
|
118
|
-
|--------------|------------------|
|
|
119
|
-
| Completeness | X/Y tasks, N reqs|
|
|
120
|
-
| Correctness | M/N reqs covered |
|
|
121
|
-
| Coherence | Followed/Issues |
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
**Issues by Priority**:
|
|
125
|
-
|
|
126
|
-
1. **CRITICAL** (Must fix before archive):
|
|
127
|
-
- Incomplete tasks
|
|
128
|
-
- Missing requirement implementations
|
|
129
|
-
- Each with specific, actionable recommendation
|
|
130
|
-
|
|
131
|
-
2. **WARNING** (Should fix):
|
|
132
|
-
- Spec/design divergences
|
|
133
|
-
- Missing scenario coverage
|
|
134
|
-
- Each with specific recommendation
|
|
135
|
-
|
|
136
|
-
3. **SUGGESTION** (Nice to fix):
|
|
137
|
-
- Pattern inconsistencies
|
|
138
|
-
- Minor improvements
|
|
139
|
-
- Each with specific recommendation
|
|
140
|
-
|
|
141
|
-
**Final Assessment**:
|
|
142
|
-
- If CRITICAL issues: "X critical issue(s) found. Fix before archiving."
|
|
143
|
-
- If only warnings: "No critical issues. Y warning(s) to consider. Ready for archive (with noted improvements)."
|
|
144
|
-
- If all clear: "All checks passed. Ready for archive."
|
|
145
|
-
|
|
146
|
-
**Verification Heuristics**
|
|
147
|
-
|
|
148
|
-
- **Completeness**: Focus on objective checklist items (checkboxes, requirements list)
|
|
149
|
-
- **Correctness**: Use keyword search, file path analysis, reasonable inference - don't require perfect certainty
|
|
150
|
-
- **Coherence**: Look for glaring inconsistencies, don't nitpick style
|
|
151
|
-
- **False Positives**: When uncertain, prefer SUGGESTION over WARNING, WARNING over CRITICAL
|
|
152
|
-
- **Actionability**: Every issue must have a specific recommendation with file/line references where applicable
|
|
153
|
-
|
|
154
|
-
**Graceful Degradation**
|
|
155
|
-
|
|
156
|
-
- If only tasks.md exists: verify task completion only, skip spec/design checks
|
|
157
|
-
- If tasks + specs exist: verify completeness and correctness, skip design
|
|
158
|
-
- If full artifacts: verify all three dimensions
|
|
159
|
-
- Always note which checks were skipped and why
|
|
160
|
-
|
|
161
|
-
**Output Format**
|
|
162
|
-
|
|
163
|
-
Use clear markdown with:
|
|
164
|
-
- Table for summary scorecard
|
|
165
|
-
- Grouped lists for issues (CRITICAL/WARNING/SUGGESTION)
|
|
166
|
-
- Code references in format: `file.ts:123`
|
|
167
|
-
- Specific, actionable recommendations
|
|
168
|
-
- No vague suggestions like "consider reviewing"
|
package/CONTRIBUTING.md
DELETED
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
# Contributing
|
|
2
|
-
|
|
3
|
-
## Snippet authoring rules
|
|
4
|
-
|
|
5
|
-
Every OmniFocus operation in this server is implemented as a standalone `.js` file under `src/snippets/`. These rules are invariants — violating them causes the same class of bugs the server was built to eliminate.
|
|
6
|
-
|
|
7
|
-
### Rule 1: One `__ARGS__` placeholder per snippet, no other interpolation
|
|
8
|
-
|
|
9
|
-
Every snippet file must contain exactly one occurrence of the token `__ARGS__`. The runtime injects arguments by doing:
|
|
10
|
-
|
|
11
|
-
```ts
|
|
12
|
-
template.replace("__ARGS__", JSON.stringify(args))
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
This is the **only** way user-supplied data enters a snippet. No string concatenation, no template literals constructing JS source, no manual escaping. If you find yourself writing `"'"+someValue+"'"` or `` `const name = "${value}"` `` inside a snippet template, stop — that is the apostrophe/injection bug pattern this architecture was built to eliminate.
|
|
16
|
-
|
|
17
|
-
Because `JSON.stringify` produces output that is a syntactic subset of JavaScript, the injected args literal is safe for all inputs including apostrophes, double quotes, backslashes, newlines, emoji, and arbitrary unicode.
|
|
18
|
-
|
|
19
|
-
### Rule 2: Snippets must work standalone
|
|
20
|
-
|
|
21
|
-
Every snippet must be paste-able into the OmniFocus Automation Console for manual testing. Replace `__ARGS__` with a hand-written object literal:
|
|
22
|
-
|
|
23
|
-
```js
|
|
24
|
-
// Paste this into OmniFocus Automation Console:
|
|
25
|
-
const args = { id: "jMBMptE7rJ1" };
|
|
26
|
-
// ... rest of the snippet
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
If your snippet cannot be tested this way, it's not a self-contained snippet — refactor until it is. This is what makes the snippets independently verifiable and debuggable.
|
|
30
|
-
|
|
31
|
-
### Rule 3: Snippets return a JSON string in the result envelope format
|
|
32
|
-
|
|
33
|
-
Every snippet's last expression must be a JSON string matching:
|
|
34
|
-
|
|
35
|
-
```js
|
|
36
|
-
JSON.stringify({ ok: true, data: <your result> })
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
On error, throw (don't return an error envelope) — the JXA shim catches the exception and constructs the error envelope automatically:
|
|
40
|
-
|
|
41
|
-
```js
|
|
42
|
-
// Good
|
|
43
|
-
throw new Error("Project not found: " + args.id);
|
|
44
|
-
|
|
45
|
-
// Don't do this — the shim handles it
|
|
46
|
-
// return JSON.stringify({ ok: false, error: { ... } })
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
The one exception is inline error returns used to avoid the overhead of an exception for expected cases (like "not found" in a list operation). If you do this, use the same envelope shape:
|
|
50
|
-
|
|
51
|
-
```js
|
|
52
|
-
return JSON.stringify({ ok: false, error: { name: "NotFoundError", message: "..." } });
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
### Rule 4: No JXA scripting-dictionary domain methods
|
|
56
|
-
|
|
57
|
-
The JXA shim uses `Application('OmniFocus').evaluateJavascript()` and nothing else. Inside snippets (which run in OmniJS), you have the full Omni Automation API: `flattenedProjects`, `flattenedTasks`, `flattenedFolders`, `flattenedTags`, `new Project(...)`, `new Folder(...)`, `moveTasks(...)`, etc.
|
|
58
|
-
|
|
59
|
-
Do **not** use the JXA scripting dictionary for domain operations. Concretely, in the `src/runtime/jxaShim.ts` JXA wrapper, the only call to the OmniFocus application object is `evaluateJavascript`. In snippets themselves, there is no JXA application object — you're inside OmniJS already.
|
|
60
|
-
|
|
61
|
-
This rule is why the server can do things the prior AppleScript-based server could not: folder creation, Single Actions lists, task moves, stable IDs, and recurrence rules all live in the OmniJS API, not the scripting dictionary.
|
|
62
|
-
|
|
63
|
-
## Adding a new tool
|
|
64
|
-
|
|
65
|
-
1. Write the OmniJS snippet as `src/snippets/<tool_name>.js`. Verify it works standalone in the Automation Console.
|
|
66
|
-
2. Add the zod input schema and handler to `src/tools/<ToolName>.ts`.
|
|
67
|
-
3. Export from `src/tools/index.ts` and add to `allTools`.
|
|
68
|
-
4. Add the corresponding spec requirement and scenario to the relevant capability spec file under `openspec/changes/<change-name>/specs/<capability>/spec.md`.
|
|
69
|
-
5. Write a unit test for schema validation and a unit test for injection safety if the tool has new parameter types.
|
|
70
|
-
6. Write an integration test that exercises the tool against a real OmniFocus instance, scoped to a `withTestFolder` fixture.
|
|
71
|
-
|
|
72
|
-
## Running tests
|
|
73
|
-
|
|
74
|
-
```bash
|
|
75
|
-
# Unit tests (no OmniFocus required)
|
|
76
|
-
npm test
|
|
77
|
-
|
|
78
|
-
# Integration tests (requires OmniFocus running on macOS)
|
|
79
|
-
npm run test:integration
|
|
80
|
-
|
|
81
|
-
# Clean up stale test fixtures from interrupted runs
|
|
82
|
-
npm run test:cleanup-fixtures
|
|
83
|
-
```
|
|
@@ -1,162 +0,0 @@
|
|
|
1
|
-
## Context
|
|
2
|
-
|
|
3
|
-
OmniFocus exposes two distinct automation surfaces on macOS:
|
|
4
|
-
|
|
5
|
-
1. **The scripting dictionary**, reachable from AppleScript and JXA (`osascript -l JavaScript`). This is the legacy surface the prior MCP server targeted by generating AppleScript source strings. It lacks folder creation, Single Actions list type, task moves, stable IDs, and recurrence-rule construction. It also requires string-concatenation of arguments, which is the root cause of the apostrophe and name-ambiguity bug classes.
|
|
6
|
-
|
|
7
|
-
2. **OmniJS**, the modern Omni Automation JavaScript runtime that lives *inside* the OmniFocus process. This is the API documented at omni-automation.com. It exposes `Task`, `Project`, `Folder`, `Tag`, `Task.RepetitionRule`, `Perspective`, `id.primaryKey`, `moveTasks`, `new Folder`, and everything else the prior server couldn't reach. It is reachable from outside OmniFocus *only* via the `evaluateJavascript` method on the scripting-dictionary application object.
|
|
8
|
-
|
|
9
|
-
The two are not interchangeable. The scripting dictionary is a capability-limited facade over the same underlying database; OmniJS is the authoritative modern API. Every "cannot do X" limitation in the prior server is a limitation of the scripting dictionary, not of JXA or macOS automation generally.
|
|
10
|
-
|
|
11
|
-
The project starts from a clean slate: no legacy code, no prior specs, no backwards-compatibility constraints. The only inputs are the Omni Automation API surface, the MCP protocol, and the enumerated failure modes of the prior server.
|
|
12
|
-
|
|
13
|
-
## Goals / Non-Goals
|
|
14
|
-
|
|
15
|
-
**Goals:**
|
|
16
|
-
|
|
17
|
-
- Expose the full Omni Automation API surface through an MCP server, eventually reaching 1:1 coverage. This change establishes the foundation; subsequent changes fill in capabilities.
|
|
18
|
-
- Eliminate by construction the bug classes of the prior server: apostrophe-breaking, name-ambiguity routing, parse-error-on-success, type-coercion failures in batch parameters, missing capability coverage.
|
|
19
|
-
- Make every domain operation authored as a static, standalone `.js` snippet that can be pasted into OmniFocus's Automation Console for manual verification.
|
|
20
|
-
- Provide stable, id-based addressing for every OmniFocus entity, with name/path resolution as a separate, explicit, ambiguity-reporting step.
|
|
21
|
-
- Establish an integration-test harness that exercises a real OmniFocus instance without polluting the user's data or leaking fixtures across sync.
|
|
22
|
-
- Name every target capability up front so future changes have a landing place and the scope of "full API coverage" is visible from the first commit.
|
|
23
|
-
|
|
24
|
-
**Non-Goals:**
|
|
25
|
-
|
|
26
|
-
- Cross-platform support. Omni Automation is macOS-only; the server is macOS-only.
|
|
27
|
-
- Compatibility with the prior AppleScript-based MCP server. Tool names, parameter shapes, and result envelopes are all new.
|
|
28
|
-
- Interactive UI automation: `Form`, `Alert`, `FilePicker`, `FileSaver`. These APIs require a human to click in the OmniFocus app while the script blocks; they cannot cross an MCP boundary without changing semantics.
|
|
29
|
-
- Device-local side effects unrelated to OmniFocus data: `Speech`, `Device`, clipboard mutation.
|
|
30
|
-
- Window mutation (resize, close, focus). Window *state* is exposed read-only; mutation is low-value and fragile.
|
|
31
|
-
- User-visible notifications posted from scripts. Out by default; reconsiderable if a concrete use case emerges.
|
|
32
|
-
- Custom perspective *creation and editing*. Listing and activation are in scope; programmatic definition of perspective rules is likely not scriptable even in OmniFocus Pro and is marked non-goal pending verification when the perspective-management change is drafted.
|
|
33
|
-
- CI coverage for integration tests. CI runs unit tests only; integration tests are local-developer-only by the nature of requiring a running OmniFocus instance.
|
|
34
|
-
- Transactional semantics across batch operations. OmniFocus does not expose transactions; batch tools return per-item result arrays with partial-success semantics.
|
|
35
|
-
|
|
36
|
-
## Decisions
|
|
37
|
-
|
|
38
|
-
### Decision 1: JXA is a thin shim; all domain logic runs inside OmniJS via `evaluateJavascript`.
|
|
39
|
-
|
|
40
|
-
**Choice:** Every tool invocation spawns `osascript -l JavaScript` running a minimal JXA wrapper whose only job is to call `Application('OmniFocus').evaluateJavascript(snippet)`, catch exceptions, and print one line of JSON to stdout. All domain logic — creating folders, setting project types, constructing repetition rules, reading IDs, walking hierarchies — lives in OmniJS snippets and runs inside the OmniFocus process.
|
|
41
|
-
|
|
42
|
-
**Rationale:** The capability gap between the scripting dictionary and OmniJS is the *entire* reason the prior server failed to cover critical features. Targeting OmniJS directly is the only path to feature parity with the Omni Automation API. JXA's only value here is as the carrier that reaches `evaluateJavascript`; nothing else in the JXA scripting-dictionary surface is used.
|
|
43
|
-
|
|
44
|
-
**Alternatives considered:**
|
|
45
|
-
|
|
46
|
-
- *Pure AppleScript, better escaping.* Rejected: the capability gap is structural; no amount of escaping fixes the missing folder, type, move, ID, and recurrence capabilities.
|
|
47
|
-
- *`omnifocus://` URL automation.* Rejected: URL schemes support a narrow slice of operations (add to inbox, open document, complete task) and provide no return channel for IDs or query results.
|
|
48
|
-
- *OmniFocus plug-in bundles installed into the user's library.* Rejected: requires out-of-band user setup, complicates distribution, and still requires an IPC channel back to the MCP server.
|
|
49
|
-
- *Shortcuts.app automation.* Rejected: similar return-channel and capability limitations, and adds a dependency on Shortcuts being configured.
|
|
50
|
-
|
|
51
|
-
**Bugs this kills:** "cannot create folders," "cannot create Single Actions list," "cannot move tasks between projects," "no project IDs returned," "cannot set recurrence," "folder name ambiguity" (because paths can be resolved via `flattenedFolders` inside OmniJS rather than scripting-dictionary `whose` clauses).
|
|
52
|
-
|
|
53
|
-
### Decision 2: Arguments are embedded as JSON literals; no string interpolation anywhere.
|
|
54
|
-
|
|
55
|
-
**Choice:** Snippet files are static `.js` files containing exactly one placeholder token: `__ARGS__`. At runtime, the TypeScript side constructs the script to execute via `template.replace("__ARGS__", JSON.stringify(args))`. The snippet reads `const args = __ARGS__;` as its first line. No other interpolation of user-supplied data into script source is permitted anywhere in the codebase.
|
|
56
|
-
|
|
57
|
-
**Rationale:** JSON is a syntactic subset of JavaScript. `JSON.stringify` of any JavaScript value produces a string that is both valid JSON and a valid JavaScript expression literal. Embedding it directly into source is injection-safe for arbitrary strings, including apostrophes, quotes, backslashes, newlines, unicode, and emoji — precisely the inputs that broke the prior server. It also has the useful property that the generated script is a standalone, paste-ready snippet: a developer can hand-edit `__ARGS__` in the template and paste it into OmniFocus's Automation Console to reproduce any operation.
|
|
58
|
-
|
|
59
|
-
**Alternatives considered:**
|
|
60
|
-
|
|
61
|
-
- *Passing arguments via environment variables.* Rejected: `evaluateJavascript` runs inside OmniFocus's process, which does not inherit the `osascript` environment. The snippet could not read them.
|
|
62
|
-
- *Writing arguments to a temp file and having the snippet read it.* Rejected: OmniJS has no general filesystem read primitive; `FileWrapper` is attachment-scoped. Adds a cleanup burden and a failure mode.
|
|
63
|
-
- *String-escape templating (Handlebars, Mustache, manual backslash escaping).* Rejected: every escape scheme has edge cases. The JSON-literal approach has none because JSON is closed under JavaScript expression syntax.
|
|
64
|
-
|
|
65
|
-
**Bugs this kills:** "apostrophes in task/project names break AppleScript string generation," plus the entire class of unicode and escape-sequence bugs that haven't been hit yet but would eventually surface under string concatenation.
|
|
66
|
-
|
|
67
|
-
### Decision 3: Results flow as exactly one line of JSON on stdout.
|
|
68
|
-
|
|
69
|
-
**Choice:** Every snippet's last expression is `JSON.stringify({ok: true, data: <result>})` or throws. The JXA shim wraps the `evaluateJavascript` call in try/catch. On success, it prints the returned string verbatim followed by a single newline. On failure, it prints `JSON.stringify({ok: false, error: {name, message, stack}})` followed by a single newline. The TypeScript runtime reads stdout, takes the first JSON-parseable line, and asserts `ok`.
|
|
70
|
-
|
|
71
|
-
**Rationale:** A strict protocol eliminates ambiguity about whether an operation succeeded. The prior server's "parse error on success" bug was a direct consequence of treating incidental AppleScript output as part of the result payload. With a one-line JSON contract, success and failure are trivially distinguishable and the protocol is easy to test in isolation.
|
|
72
|
-
|
|
73
|
-
**Alternatives considered:**
|
|
74
|
-
|
|
75
|
-
- *Multi-line JSON with framing.* Rejected: unnecessary complexity for the single-operation-per-invocation model.
|
|
76
|
-
- *Exit code as the success signal.* Rejected: `osascript` exit codes are unreliable across JXA error modes, and an exit code doesn't carry error details.
|
|
77
|
-
- *Writing results to a temp file.* Rejected: adds a cleanup burden and a filesystem round-trip with no benefit.
|
|
78
|
-
|
|
79
|
-
**Bugs this kills:** "`remove_item` returns parse error on success even when operation succeeded."
|
|
80
|
-
|
|
81
|
-
### Decision 4: `id.primaryKey` is the canonical address for every entity; name resolution is a separate, explicit, ambiguity-reporting step.
|
|
82
|
-
|
|
83
|
-
**Choice:** Every read tool returns `{id, ...}` where `id` is `obj.id.primaryKey`. Every write tool (in this and future changes) accepts `id` as the primary addressing parameter. Name and path resolution is a dedicated `resolve_name` tool that takes `{type, query, scope?}` and returns a list of candidate `{id, name, path, ...}` entries. When more than one candidate matches, the tool returns all of them and the caller must decide; it never silently picks.
|
|
84
|
-
|
|
85
|
-
**Rationale:** The prior server's duplicate-name and folder-ambiguity bugs are consequences of name-based addressing with silent disambiguation. IDs are stable across sessions within OmniFocus. Making ID the canonical address and forcing callers (including LLMs) to resolve ambiguity explicitly is the only way to produce deterministic behavior in a database where names are user-chosen and frequently collide.
|
|
86
|
-
|
|
87
|
-
**Alternatives considered:**
|
|
88
|
-
|
|
89
|
-
- *Name-based addressing with a "pick the first match" default.* Rejected: this is exactly what the prior server did, and it routed to the wrong target.
|
|
90
|
-
- *Path-only addressing.* Rejected: paths are fragile to user renames and still ambiguous when the same path exists under different roots (e.g., the same tag name under different parents).
|
|
91
|
-
- *Auto-disambiguation via fuzzy scoring.* Rejected: silent selection is precisely the failure mode being fixed.
|
|
92
|
-
|
|
93
|
-
**Open question:** Whether `id.primaryKey` values survive OmniFocus sync across devices and across database rebuilds. This is believed to be true but will be verified during the execution-runtime implementation and documented in the runtime README. If IDs turn out not to survive sync, the contract still holds within a single session; callers re-resolving after a sync event is an acceptable fallback.
|
|
94
|
-
|
|
95
|
-
**Bugs this kills:** "duplicate project names cause wrong-target routing," "folder name ambiguity — same folder name under different parents routes incorrectly," "no project IDs returned — projects only addressable by name."
|
|
96
|
-
|
|
97
|
-
### Decision 5: Tool parameters are validated with zod at the TS boundary; enums are strict; no type coercion.
|
|
98
|
-
|
|
99
|
-
**Choice:** Every MCP tool handler defines its input schema as a zod object. Handlers receive already-parsed, already-validated arguments. Enums like project type are `z.enum(["parallel", "sequential", "singleActions"])`. Batch arrays are `z.array(z.object({...}))` — never strings that look like JSON. Invalid inputs are rejected at the TS boundary with structured error messages before any snippet runs.
|
|
100
|
-
|
|
101
|
-
**Rationale:** The prior server had `sequential` parameters that arrived as strings and were treated as booleans with broken coercion, and `batch_remove_items` parameters that arrived as serialized JSON strings instead of arrays. These are the bug pattern you get when tool schemas are loose and the handler does ad-hoc type massaging. zod at the boundary makes the schema the contract.
|
|
102
|
-
|
|
103
|
-
**Alternatives considered:**
|
|
104
|
-
|
|
105
|
-
- *Ad-hoc `typeof` checks in handlers.* Rejected: this is what produced the original bugs.
|
|
106
|
-
- *JSON Schema validation via ajv.* Considered and deferred: zod is TypeScript-native, produces better type inference, and integrates naturally with the MCP SDK's expected schema surface. ajv is fine but adds a second schema language.
|
|
107
|
-
|
|
108
|
-
**Bugs this kills:** "`sequential` parameter must be a true boolean, not string," "`batch_remove_items` items parameter must be a JSON array, not serialized string."
|
|
109
|
-
|
|
110
|
-
### Decision 6: Integration tests scope all fixtures under a disposable folder and refuse to run under sync by default.
|
|
111
|
-
|
|
112
|
-
**Choice:** Every integration test run generates a UUID and creates a top-level folder named `__MCP_TEST_<uuid>__` in the user's OmniFocus database. All fixture projects, folders, tasks, and tags are created inside it. Teardown deletes the folder and everything under it. A preflight check inspects the database's sync configuration and refuses to run if sync is enabled, unless `MCP_TEST_ALLOW_SYNC=1` is set in the environment. Integration tests run serially (OmniFocus is a singleton). CI runs unit tests only.
|
|
113
|
-
|
|
114
|
-
**Rationale:** Integration tests against a real OmniFocus instance are the only way to validate the OmniJS API contract — mocks would drift. But real-database testing has a failure mode where fixtures leak into the user's actual workflow or, worse, sync to other devices. Fixture-folder scoping contains the blast radius; the sync preflight prevents cross-device leakage by default with an explicit opt-out for users who understand the tradeoff.
|
|
115
|
-
|
|
116
|
-
**Alternatives considered:**
|
|
117
|
-
|
|
118
|
-
- *Mocking the OmniJS API.* Rejected: this is the path to building something that passes tests and fails in production.
|
|
119
|
-
- *Dedicated test OmniFocus document.* Rejected: OmniFocus does not support multiple documents in a way that's ergonomic for automated switching.
|
|
120
|
-
- *Snapshot-and-restore the database.* Rejected: there is no supported API for this; copying the sqlite file behind OmniFocus's back is unsafe.
|
|
121
|
-
|
|
122
|
-
## Risks / Trade-offs
|
|
123
|
-
|
|
124
|
-
- **Risk:** `id.primaryKey` may not survive OmniFocus sync or database rebuilds. → **Mitigation:** Verify during runtime implementation and document findings. If IDs don't survive sync, document the session-scope guarantee and update `resolve_name` guidance accordingly. This is an open question, not a blocker.
|
|
125
|
-
|
|
126
|
-
- **Risk:** `evaluateJavascript` may have undocumented size limits on the script argument. → **Mitigation:** Keep snippets small and focused (one operation per file). For batch operations (future change), the snippet iterates over an args array; it does not inline per-item code.
|
|
127
|
-
|
|
128
|
-
- **Risk:** OmniJS error messages inside `evaluateJavascript` may be lossy when surfaced through JXA. → **Mitigation:** The snippet catches its own errors and constructs the error envelope itself before returning, so error details never cross the OmniJS→JXA boundary as a raw exception.
|
|
129
|
-
|
|
130
|
-
- **Risk:** Integration tests running against a real OmniFocus instance can still pollute the user's data if teardown fails mid-run. → **Mitigation:** Fixture folders are prefixed with `__MCP_TEST_` and include a UUID; a `clean-test-fixtures` dev script scans for and removes any stragglers. README documents the manual cleanup command.
|
|
131
|
-
|
|
132
|
-
- **Risk:** OmniFocus sync conflicts from concurrent modifications during a test run. → **Mitigation:** Serial test execution plus the sync preflight. Under the opt-in sync-allowed mode, users accept the risk explicitly.
|
|
133
|
-
|
|
134
|
-
- **Trade-off:** The MCP server is macOS-only. This is an inherent property of Omni Automation and not negotiable without abandoning the capability goals.
|
|
135
|
-
|
|
136
|
-
- **Trade-off:** Every tool invocation spawns an `osascript` process, which has startup cost. For single operations this is fine; for high-frequency batch operations it would be noticeable. Batch tools (future change) will therefore execute as a single snippet that iterates internally, not as a loop of tool calls from the caller side.
|
|
137
|
-
|
|
138
|
-
- **Trade-off:** Snippet files live outside the TypeScript compilation unit. Refactoring a shared OmniJS helper means editing `.js` files that tsc doesn't type-check. Accepted because testability-by-paste is more valuable than type-checking across the boundary; the JS side is small and exercised by integration tests.
|
|
139
|
-
|
|
140
|
-
## Bug-to-decision traceability
|
|
141
|
-
|
|
142
|
-
Every documented failure mode of the prior server is mapped to the decision that eliminates it, so the rationale survives archival:
|
|
143
|
-
|
|
144
|
-
| Prior-server bug | Killed by |
|
|
145
|
-
|---|---|
|
|
146
|
-
| Cannot create folders | Decision 1 (OmniJS exposes `new Folder`) |
|
|
147
|
-
| Cannot create Single Actions list | Decision 1 (OmniJS exposes `containsSingletonActions`) |
|
|
148
|
-
| Cannot move tasks between projects | Decision 1 (OmniJS exposes `moveTasks`) |
|
|
149
|
-
| No project IDs returned | Decisions 1 + 4 (`id.primaryKey` exists in OmniJS and is the canonical address) |
|
|
150
|
-
| Cannot set recurrence/repeat | Decision 1 (OmniJS exposes `Task.RepetitionRule`) |
|
|
151
|
-
| Apostrophes break script generation | Decision 2 (JSON-literal argument embedding) |
|
|
152
|
-
| Duplicate project names cause wrong-target routing | Decision 4 (ID addressing + ambiguity-reporting resolution) |
|
|
153
|
-
| Folder name ambiguity under different parents | Decision 4 (ID addressing + path-based resolution with ambiguity reporting) |
|
|
154
|
-
| `sequential` parameter must be a true boolean, not string | Decision 5 (zod enum validation at TS boundary) |
|
|
155
|
-
| `batch_remove_items` items parameter must be a JSON array | Decision 5 (zod array validation at TS boundary) |
|
|
156
|
-
| `remove_item` returns parse error on success | Decision 3 (strict one-line JSON result protocol) |
|
|
157
|
-
|
|
158
|
-
## Open Questions
|
|
159
|
-
|
|
160
|
-
- Do `id.primaryKey` values survive OmniFocus sync across devices and database rebuilds? To be verified during runtime implementation.
|
|
161
|
-
- Does `evaluateJavascript` have a practical size limit on the script argument? To be measured during runtime implementation and documented as an upper bound for future batch snippet design.
|
|
162
|
-
- Does OmniJS expose a reliable way to detect whether sync is enabled without mutating state? Needed for the integration-test preflight check. If not, the preflight falls back to reading the user's OmniFocus preferences via JXA before entering the OmniJS bridge.
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
## Why
|
|
2
|
-
|
|
3
|
-
OmniFocus needs an MCP server that exposes the full Omni Automation JavaScript API to LLM callers. A prior AppleScript-string-generating server exists but has structural limitations: it cannot create folders, cannot create Single Actions lists, cannot move tasks between projects, cannot set recurrence rules, returns no stable IDs, breaks on apostrophes in names, silently routes to the wrong target on duplicate names, and mis-parses successful results as errors. These are not bugs to patch — they are consequences of generating AppleScript source as concatenated strings against a scripting dictionary that lacks the needed capabilities.
|
|
4
|
-
|
|
5
|
-
This change starts from a clean slate with an architecture that kills those bug classes by construction: all operations execute inside OmniFocus's OmniJS runtime (where the modern API lives) via a JXA bridge, arguments are embedded as JSON literals (never concatenated), identity is always `id.primaryKey`, and results flow as a strict one-line JSON protocol. This first change establishes the runtime foundation and names every capability that will eventually exist, so subsequent changes can fill in the API surface incrementally without re-litigating the architecture.
|
|
6
|
-
|
|
7
|
-
## What Changes
|
|
8
|
-
|
|
9
|
-
- Establish a TypeScript MCP server scaffold (`@modelcontextprotocol/sdk`, zod for schemas, vitest for tests) with `src/runtime/`, `src/snippets/`, `src/tools/`, `src/schemas/`, `src/server.ts`, `test/unit/`, `test/integration/`, `package.json`, `tsconfig.json`.
|
|
10
|
-
- Implement the execution runtime: a JXA shim that calls `Application('OmniFocus').evaluateJavascript(snippet)`, wraps results in a strict `{ok, data} | {ok: false, error}` envelope, and prints exactly one line of JSON to stdout.
|
|
11
|
-
- Implement the snippet-authoring convention: static `.js` files with a single `__ARGS__` placeholder, injected by `template.replace("__ARGS__", JSON.stringify(args))`. No other interpolation is permitted anywhere in the codebase.
|
|
12
|
-
- Implement the identity model: every read returns `id.primaryKey`; every write will accept `id` as the canonical address. Name/path resolution is a separate `resolve_name` tool that returns candidate lists on ambiguity and never silently picks a winner.
|
|
13
|
-
- Deliver the minimum-viable read surface needed to prove the bridge end-to-end: `list_projects`, `get_project`, `list_folders`, `get_folder`, `list_tasks`, `get_task`, `list_tags`, `get_tag`, `resolve_name`. No mutating tools in this change.
|
|
14
|
-
- Establish the integration-test harness against a real OmniFocus instance, with all fixtures scoped under a disposable `__MCP_TEST_<uuid>__` top-level folder and a preflight that refuses to run when OmniFocus sync is enabled (unless `MCP_TEST_ALLOW_SYNC=1`).
|
|
15
|
-
- Name every target capability as a stub spec so future changes have a landing place: `recurrence`, `perspective-management`, `window-state`, `forecast`, `database-inspection`, `batch-operations`, `attachments`, `url-automation`, `settings`. Stubs declare scope and non-goals without specifying requirements that later changes will own.
|
|
16
|
-
- Document explicit non-goals: interactive UI APIs (`Form`, `Alert`, `FilePicker`, `FileSaver`), `Speech`, `Device`, clipboard mutation, window mutation, notifications, and custom perspective creation/editing. These are recorded as non-goals in design.md so they don't look like oversights.
|
|
17
|
-
|
|
18
|
-
## Capabilities
|
|
19
|
-
|
|
20
|
-
### New Capabilities
|
|
21
|
-
|
|
22
|
-
- `execution-runtime`: The JXA→OmniJS bridge, snippet loader, argument-injection rule, one-line JSON result protocol, and error model. Fully specified in this change.
|
|
23
|
-
- `identity-resolution`: `id.primaryKey` addressing, name/path resolution with ambiguity reporting, and the `resolve_name` tool contract. Fully specified in this change.
|
|
24
|
-
- `task-management`: Task read operations (`list_tasks`, `get_task`) specified in this change; full CRUD, move, completion, and hierarchy operations land in the `core-crud` change.
|
|
25
|
-
- `project-management`: Project read operations (`list_projects`, `get_project`) specified in this change; CRUD, type (parallel/sequential/singleActions), status, and review metadata land in `core-crud`.
|
|
26
|
-
- `folder-management`: Folder read operations (`list_folders`, `get_folder`) specified in this change; CRUD, nesting, rename, and move land in `core-crud`.
|
|
27
|
-
- `tag-management`: Tag read operations (`list_tags`, `get_tag`) specified in this change; CRUD, nesting, assignment, and status land in `core-crud`.
|
|
28
|
-
- `recurrence`: Stub naming the capability for Task.RepetitionRule construction, RepetitionMethod, structured schema plus raw RRULE escape hatch, and clearing recurrence. Requirements land in the `recurrence` change.
|
|
29
|
-
- `perspective-management`: Stub naming the capability for listing built-in and custom perspectives, activating a perspective in a window, and reading perspective details. Custom perspective creation/editing is a non-goal pending API verification.
|
|
30
|
-
- `window-state`: Stub naming the capability for read-only window state (current window, active perspective, sidebar selection, content selection). Window mutation is a non-goal.
|
|
31
|
-
- `forecast`: Stub naming the capability for forecast days, tasks due/deferred on a day, and forecast metadata.
|
|
32
|
-
- `database-inspection`: Stub naming the capability for scoped, paged, filtered database traversal including `dump_database`, inbox contents, and database metadata.
|
|
33
|
-
- `batch-operations`: Stub naming the capability for batch variants of mutating tools with per-item result arrays and partial-success semantics.
|
|
34
|
-
- `attachments`: Stub naming the capability for `FileWrapper`-based attachment listing and round-trip on task notes.
|
|
35
|
-
- `url-automation`: Stub naming the capability for `omnifocus://` URL construction and parsing.
|
|
36
|
-
- `settings`: Stub naming the capability for reading app settings and preferences where exposed.
|
|
37
|
-
|
|
38
|
-
### Modified Capabilities
|
|
39
|
-
|
|
40
|
-
None. This is a clean-slate project with no prior specs.
|
|
41
|
-
|
|
42
|
-
## Impact
|
|
43
|
-
|
|
44
|
-
- **New repository structure** with TypeScript toolchain, MCP SDK, zod, vitest.
|
|
45
|
-
- **Runtime dependency on macOS** (`osascript`) and a running OmniFocus instance. The server is macOS-only by nature of the Omni Automation platform.
|
|
46
|
-
- **Integration tests mutate a real OmniFocus database.** Mitigated by fixture-folder scoping and a sync-enabled preflight check. Documented loudly in the README.
|
|
47
|
-
- **No CI coverage for integration tests.** CI runs unit tests only (snippet injection, zod schema validation, result-protocol parsing). Integration is local-developer-only.
|
|
48
|
-
- **Replaces a prior AppleScript-based MCP server.** Callers migrating from the old server will encounter breaking changes in tool names, parameter types, and result shapes. This is accepted because the prior server's contract was structurally broken; compatibility is not a goal.
|
|
49
|
-
- **No external network dependencies.** The server runs entirely locally against the user's OmniFocus database.
|
package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/specs/attachments/spec.md
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
## ADDED Requirements
|
|
2
|
-
|
|
3
|
-
### Requirement: Capability declared
|
|
4
|
-
|
|
5
|
-
The `attachments` capability SHALL cover listing, reading, adding, and removing file attachments on task notes via the OmniJS `FileWrapper` API. The on-wire representation of attachment contents across the MCP boundary (inline base64, filesystem path reference, or a hybrid with a size threshold) SHALL be decided when the `attachments` change is drafted. Requirements for individual tools SHALL be added by that change.
|
|
6
|
-
|
|
7
|
-
#### Scenario: Capability is named and scoped
|
|
8
|
-
- **WHEN** a future change proposes adding attachment tools
|
|
9
|
-
- **THEN** it lands requirements under this capability and makes the on-wire encoding decision explicit
|
package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/specs/batch-operations/spec.md
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
## ADDED Requirements
|
|
2
|
-
|
|
3
|
-
### Requirement: Capability declared
|
|
4
|
-
|
|
5
|
-
The `batch-operations` capability SHALL cover batch variants of every mutating tool (create, update, delete, move, complete, assign). Batch tools SHALL accept strictly typed arrays validated at the TypeScript boundary (never serialized JSON strings) and SHALL return a per-item result array with partial-success semantics: each element is an `{ok, data?, error?}` envelope carrying that item's outcome independent of other items. Batch tools SHALL execute as a single OmniJS snippet that iterates internally, not as a loop of per-item bridge invocations. Requirements for individual batch tools SHALL be added by the `batch-operations` change.
|
|
6
|
-
|
|
7
|
-
#### Scenario: Capability is named and semantics are fixed
|
|
8
|
-
- **WHEN** a future change proposes adding batch tools
|
|
9
|
-
- **THEN** it lands requirements under this capability with typed-array validation and partial-success result semantics as invariants
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
## ADDED Requirements
|
|
2
|
-
|
|
3
|
-
### Requirement: Capability declared
|
|
4
|
-
|
|
5
|
-
The `database-inspection` capability SHALL cover scoped, paged, and filtered traversal of the full OmniFocus database, including a `dump_database` tool that accepts `{scope, id?, include, format}` parameters, inbox inspection, and database metadata (name, sync status, object counts). The default behavior of `dump_database` SHALL exclude completed tasks, dropped entities, and task notes to keep payloads manageable; those are opt-in via the `include` parameter. Requirements for individual tools SHALL be added by the `forecast-and-inspection` change.
|
|
6
|
-
|
|
7
|
-
#### Scenario: Capability is named and scoped
|
|
8
|
-
- **WHEN** a future change proposes adding database-inspection tools
|
|
9
|
-
- **THEN** it lands requirements under this capability rather than inventing a new capability name
|