@torsday/omnifocus-mcp 1.2.0 → 1.2.1

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.
Files changed (3) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/dist/index.js +2992 -1175
  3. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -5,6 +5,28 @@ All notable changes to `@torsday/omnifocus-mcp` will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). See [ADR-0011](./docs/adr/0011-versioning-and-stability.md) for the explicit definition of breaking vs additive changes in this project.
6
6
 
7
7
 
8
+ ## [1.2.1](https://github.com/torsday/omnifocus-mcp/compare/v1.2.0...v1.2.1) (2026-04-30)
9
+
10
+ **Summary** — A focused reliability patch for OmniFocus 4.x compatibility and cross-transport ID interoperability. Seven bug fixes address real failure modes surfaced since v1.2.0: JXA scripts now correctly handle OF 4.x's quirky `class()` exceptions on tag parents and containing projects; `byId` misses are mapped to typed `NotFound` errors instead of leaking the raw `-1728` osascript code; and four write-path operations (`task_create`, `task_duplicate`, `task_reorder`, `project_move`) are routed through OmniJS to guarantee ID interoperability across all transport paths per ADR-0019. The `parentId` subtask filter also works correctly again after a `tasks()` vs `flattenedTasks()` regression. No breaking changes; all v1.2.0 call shapes are unchanged.
11
+
12
+ ### Fixed
13
+
14
+ - **JXA tag parent `class()` guard — OF 4.x exception safety** — `tag_list` and `tag_get` now guard the `parent.class()` call and per-element `tag.id()` calls against the `Can't convert types` exception that OmniFocus 4.x throws on certain specifier types. Previously these would surface as opaque JXA errors; they now degrade gracefully and return the tag without parent info rather than crashing the response. ([bcaefb9](https://github.com/torsday/omnifocus-mcp/commit/bcaefb9de9bdef4ce92876bf580eb2a48927c45f))
15
+
16
+ - **`byId` miss mapped to `NotFound` at the JXA boundary (closes [#674](https://github.com/torsday/omnifocus-mcp/issues/674))** — JXA's `flattenedTasks.byId()` returns a specifier with error code `-1728` ("Can't get object") when the ID doesn't exist, rather than `null`. This raw code was leaking through to callers. It is now intercepted at the transport boundary and converted to the typed `NotFoundError` the rest of the stack expects. ([ec53b88](https://github.com/torsday/omnifocus-mcp/commit/ec53b88e9733a3eaa24568da896d8a3da5ff377c))
17
+
18
+ - **`containingProject().class()` exception preserves `projectId`** — In OF 4.x, calling `.class()` on a real project specifier throws rather than returning a class name. A prior guard was catching the exception but resetting `projectId` to `null` as a side effect. Fixed: the exception path now correctly leaves `projectId` set to the project's ID. ([b40a4a0](https://github.com/torsday/omnifocus-mcp/commit/b40a4a0d77c0165c4d2ae6305ecfddf1efb49e18))
19
+
20
+ - **`parentId` subtask filter returns direct children only (closes [#695](https://github.com/torsday/omnifocus-mcp/issues/695))** — `task_list` with a `parentId` filter was calling `flattenedTasks()` on the parent, which recursively includes all descendants. Corrected to `tasks()` so only direct children are returned, matching the documented behavior. ([2796b30](https://github.com/torsday/omnifocus-mcp/commit/2796b30b9cf82090c67a4cbb0631f9e19712aaaf))
21
+
22
+ - **`task_create` routed through OmniJS for ID interoperability (closes [#680](https://github.com/torsday/omnifocus-mcp/issues/680))** — `task_create` previously used JXA, which assigns a different ID namespace than OmniJS. The returned task ID could not be reliably round-tripped through OmniJS write operations. Routed through OmniJS per ADR-0019 to guarantee ID interoperability across all transport paths. ([0c9959a](https://github.com/torsday/omnifocus-mcp/commit/0c9959abae2bb0e4afb0bdeb76bd60310ee141de))
23
+
24
+ - **`task_duplicate` routed through OmniJS for ID interoperability (closes [#692](https://github.com/torsday/omnifocus-mcp/issues/692))** — Same cross-transport ID issue as `task_create`. `task_duplicate` now runs via OmniJS and returns an ID that is valid for all subsequent operations regardless of transport. ([972ee8c](https://github.com/torsday/omnifocus-mcp/commit/972ee8c79154f523554e7e642a0262df1502dacb))
25
+
26
+ - **`project_move` and `project_create` routed through OmniJS + JXA folder readback fixed (closes [#681](https://github.com/torsday/omnifocus-mcp/issues/681))** — Both operations now run through OmniJS, fixing cross-transport ID interoperability failures. A secondary bug — JXA folder status/move setters silently no-oping — is also fixed. ([a1bf707](https://github.com/torsday/omnifocus-mcp/commit/a1bf70729738f3b1ec3534f0e82fdea7f72f5818), [509a6bd](https://github.com/torsday/omnifocus-mcp/commit/509a6bd4d4bdca71ae3d9b889f42ec44db55d195))
27
+
28
+ - **`task_reorder` validates parent before mutating (closes [#676](https://github.com/torsday/omnifocus-mcp/issues/676))** — `task_reorder` was applying the reorder even when the supplied `taskIds` belonged to different parent containers, producing silent data corruption. It now validates that all task IDs share the declared parent before any mutation. ([dc28308](https://github.com/torsday/omnifocus-mcp/commit/dc283086946d9418f7ea194136d5e91cb0e148a2))
29
+
8
30
  ## [1.2.0](https://github.com/torsday/omnifocus-mcp/compare/v1.1.0...v1.2.0) (2026-04-29)
9
31
 
10
32
  **Summary** — The headline additions are **outbound webhooks** (a full HTTPS + HMAC delivery subsystem that fires when OmniFocus state changes), **macOS Calendar integration** via a Swift EventKit bridge (with new `omnifocus://calendar` and `omnifocus://agenda` resources that merge calendar events with the OF Forecast view), **decision-journal** support (record user judgment on tasks/projects so agent-driven scans stop re-litigating the same anomaly), **natural-language perspective authoring** (a new MCP prompt + `perspective_create`/`update`/`delete`/`evaluate_dry_run` tools), **`task_defer_smart`** (intent-bearing defer-date grammar so agents stop landing tasks on weekends or 11 pm), and **mutation testing** wired in as a release-time hard gate (Stryker, calibrated baseline, fails publish on regression). Several existing surfaces were tightened — `task_extract_from_image` moved its post-parse validation rules into the Zod schema for cleaner error envelopes, several batch tools gained `.describe()` coverage on inner fields, and a handful of read-side responses now pair human-readable names with opaque IDs for the same reason v1.1.0 introduced the convention. Two new ADRs lock the architectures (ADR-0016 webhook delivery, ADR-0018 calendar bridge). No breaking changes; all v1.0.x / v1.1.x call shapes are unchanged.
@@ -186,6 +208,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
186
208
 
187
209
  ## [Unreleased]
188
210
 
211
+ ### Fixed
212
+
213
+ - **`moveProject` routes through OmniJS + JXA folder-readback survives OF 4.x quirk (closes #681)** — fourth fix in the [ADR-0019](./docs/adr/0019-cross-transport-id-interoperability.md) series. The first slice of #681 routed `createProject` through OmniJS but left `moveProject` on JXA, where `target.move({ to: folder.projects.end })` fails with "Attempted to move data objects to a nil container" on OmniJS-created project specifiers. New `src/scripts/omnijs/project_move.js` uses `moveSections([proj], destination)` and resolves the destination via `flattenedFolders.filter(...)` (or `library` for the root). Routing flips `moveProject: "jxa"` → `"omnijs"`. Separately, the JXA folder-readback path on every project script (`project_get.js`, `project_get_many.js`, `project_list.js`, `project_create.js`, `project_update.js`) carried the same broken `f.class() !== "document"` guard that #673 already fixed for tasks: `f.class()` throws "Can't convert types" on a real Folder specifier in OmniFocus 4.x JXA, so the readback was silently returning `folderId: null` for every project in a folder. Replaced with the nested-try-catch pattern from #673 — treat the throw as "real folder", treat a successful return of `"document"` as the only skip path. The moveProject integration test now passes; project reads through JXA correctly surface `folderId` again.
214
+ - **`duplicateTask` routes through OmniJS for cross-transport ID interoperability (closes #692)** — third sibling fix in the [ADR-0019](./docs/adr/0019-cross-transport-id-interoperability.md) series after [#680](https://github.com/torsday/omnifocus-mcp/issues/680) (createTask) and [#681](https://github.com/torsday/omnifocus-mcp/issues/681) (createProject). JXA's `task.duplicate()` and `container.make({...})` produce transient specifier IDs that downstream OmniJS reads can't resolve. OmniJS's `duplicateTasks([source], position)` and `new Task(name, position)` produce clones whose `id.primaryKey` is interoperable with both transports. New `src/scripts/omnijs/task_duplicate.js` mirrors the JXA props-copy surface (name, note, flagged, defer/due dates, estimatedMinutes, sequential, tags) and resets completion state on the clone (matching the JXA contract). Recursive clones use `duplicateTasks` and walk the resulting subtree to clear inherited `completed` flags; non-recursive clones build a single fresh task via `new Task(...)` — naturally produces an uncompleted childless result. Routing flips `duplicateTask: "jxa"` → `duplicateTask: "omnijs"`. Three of four duplicateTask integration tests now pass (was 1 of 4). The recursive case partially passes — `descendantCount` correct, but its downstream `listTasks({ parentId })` assertion still trips on a separate pre-existing JXA filter bug where parentId returns grandchildren too. Will file a follow-up for that.
215
+ - **`createTask` routes through OmniJS for cross-transport ID interoperability (closes #680)** — sibling fix to [#681](https://github.com/torsday/omnifocus-mcp/issues/681). Per [ADR-0019](./docs/adr/0019-cross-transport-id-interoperability.md), JXA's `Task(props) + push()` returned a transient specifier ID that didn't match OmniFocus's persistent `id.primaryKey`, breaking subsequent OmniJS-routed downstream operations (`moveTask`, `reorderTask`, `duplicateTask`) which use the persistent key. New `src/scripts/omnijs/task_create.js` mirrors the JXA props-set surface (parent-task / project / inbox positions, note, flagged, defer/due dates, estimatedMinutes, tagIds, sequential, completedByChildren) and produces a task whose ID round-trips correctly across both transports. Routing-table flip: `createTask: "jxa"` → `createTask: "omnijs"`. Five of the seven named integration tests in #680 now pass: `createTask with projectId places the task in that project`, `moveTask into a project updates projectId`, and four `reorderTask` variants. The three `duplicateTask` failures and the `reorderTask validation when reference has different parent` failure trace to separate root causes (filed as follow-ups). Caller wrappers, OmniJsTransport contract, router exclusivity allowlist, and the routing-domain unit tests all updated to reflect the move; concurrent-test JXA-write fixtures now demonstrate via `updateTask` (still JXA-routed) since `createTask` is no longer the canonical example.
216
+
189
217
  ---
190
218
 
191
219
  ## [1.0.0] — 2026-04-25