Projects 2026 live
todo-harvest
Pulls tasks from Vikunja, Plane, Jira, Microsoft To Do and Notion into one local file with one schema, and pushes changes back to Vikunja and Plane.
Python

My tasks live in five places. Work is in Jira, the team board is in Plane, the family list is in Microsoft To Do, and notes with checkboxes are in Notion. The self-hosted Vikunja was supposed to replace them all, and it has not.
todo-harvest pulls all five into one file with one schema and resolves conflicts by timestamp. It then pushes the result back to the two services that accept writes, Vikunja and Plane. Every task gets the same fields: id, title, status, priority, dates, tags, category and url. The service's own payload is kept under raw.
There is no daemon and no polling. pull, push and sync run only when you call them and do what their names say. inspect and export make no network calls at all.
A SQLite mapping gives each task a stable local id the first time it is pulled, and remembers which id the task has on each service. That is how a task created by a push is recognised on the next pull. For instances with their own vocabulary there are status_map, priority_map and, for Notion, field_map.
Local state is the source of truth. A pull never overwrites a newer local edit. A push never creates a task on a service where it has no mapping, unless you set a default project. It is written in Python, and the tokens go in a single config.yaml that is git-ignored.