A robust, two-way synchronizer between Todoist and a local Markdown file, using SilverBullet.md markdown flavor.
This tool allows AI agents and humans to manage Todoist tasks by directly reading and editing a single Markdown file (TASKS.md). It uses the Todoist v1 Sync API for efficient updates and Markdown AST parsing for lossless file modification. It uses hidden .todoist-sync-state.json to store sync state and local cache, which is required to ensure safe two-way sync.
It is based on escudero89/todoist-sync, but heavily rewritten to use TypeScript, two-way sync from single file, and to use SilverBullet markdown flavor.
- Two-Way Sync: Changes made in
TASKS.md(new tasks, checked/unchecked, content updates, deletions) are pushed to Todoist. - Conflict Resolution: If a task is modified both locally and remotely, the local version is "forked" (ID removed and marked as conflict), and the remote version is applied.
- Atomic Writes: Uses temporary files and moves to ensure your state and tasks files are never corrupted.
- Watch Mode: Can run as a daemon, watching the file for changes and polling Todoist for remote updates.
- Install dependencies:
npm install
- Configure environment:
cp .env.example .env # Edit .env and add your TODOIST_API_TOKEN
--dir <path>: Specify the directory whereTASKS.mdand state are stored. Defaults to the current directory.
Runs a single reconciliation "tick" and exits.
npm run sync -- --once --dir ./my-tasksWatches TASKS.md for local changes (with debounce) and polls Todoist every minute.
npm run dev
# or
npm run sync -- --watchTasks are stored in TASKS.md as a simple Markdown list:
* [ ] Buy milk [id: "123456"]
* [x] Finished task [id: "789012"] [completed: "2026-04-18"]
* [ ] New local task- New Tasks: Simply add a new list item. The synchronizer will assign it a real Todoist ID on the next tick.
- Completing Tasks: Change
[ ]to[x]. - Deleting Tasks: Remove the line from the file.
- Updating Content: Change the text after the checkbox.
- Attributes: Use
[key: value]and#tagsat the end of the line.[id: "..."]: The unique Todoist task ID.[priority: p1]: Critical (Red)[priority: p2]: High (Orange)[priority: p3]: Medium (Blue)[priority: p4]: Low (White). Default, hidden on save.[due: "string"]: Due date (e.g.,tomorrow,every Friday).[completed: "yyyy-mm-dd"]: Date when task was completed.#tag: Maps to Todoist labels.
- Hierarchy: Use indented sub-lists to represent sub-tasks.
- Projects: Use level 1 headings (
# Project Name) to group tasks into projects. tasks before any heading go to Inbox.
| File | Purpose |
|---|---|
src/index.ts |
Main execution entry point |
src/ |
Holds the split modular codebase (sync, api, markdown, etc.) |
.env |
TODOIST_API_TOKEN |
TASKS.md |
The live task list. Edit this file! |
.todoist-sync-state.json |
Internal sync state (sync token + local cache) |
Agents can interact with Todoist by:
- Reading
TASKS.mdto see current tasks. - Modifying
TASKS.mddirectly. - The agent should ensure
npm run devis running or triggernpm run sync -- --onceafter making changes if they want immediate synchronization.