Open-source project
bergercookie/syncall avatar
bergercookie/syncall

syncall keeps a task list and a calendar in step through paired executables

Bi-directional synchronization between services such as Taskwarrior, Google Calendar, Notion, Asana, and more

621 stars62 forksPythonMIT

At a glance

What is it?
syncall is a Python framework for bi-directional synchronization between task and note services, where each pairing ships as its own command such as tw-gcal-sync. Its strength is the extension model, and its weak point is a release that stopped at version 1.8.0 in 2024.
Who is it for?
syncall fits people who already keep tasks in Taskwarrior and want them mirrored into Google Calendar, Google Tasks, Notion, Google Keep, Asana or a CalDAV server, and it fits contributors willing to implement a synchronization side to extend it. It does not fit teams that need a hosted service with a user interface, and it does not fit anyone who needs a current release line, since the only tagged release is v1.8.0 from 2024-01-29 while the last push was on 2026-07-21.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 95 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Each pairing of services gets its own command

syncall is described as a one-stop tool for bi-directionally synchronizing data across services, aimed at note taking and task management data but not limited to it. The design decision that defines the project is that every synchronization ships as its own executable rather than one configurable command with plugins. The supported pairings and their executables are listed in a table: tw-gtasks-sync for Taskwarrior with Google Tasks, tw-gcal-sync for Taskwarrior with Google Calendar, tw-notion-sync for Taskwarrior with Notion checkboxes, tw-gkeep-sync for Taskwarrior with Google Keep checkboxes, tw-asana-sync for Taskwarrior with Asana tasks, tw-caldav-sync for Taskwarrior with a generic CalDAV server, and fs-gkeep-sync for local files with Google Keep notes. The same list is repeated in the project scripts, where each executable maps to an entry point inside the syncall package. Six of the seven pair Taskwarrior with something else, so the tool assumes Taskwarrior is the hub, not an optional participant.

Bi-directional means deletions propagate both ways

The project defines what it means by bi-directional in concrete terms. On an addition, a modification or a deletion of an item on one side, a corresponding change happens on the other side, so the two sides eventually agree. The word eventually matters, because the model is a repeated reconciliation rather than a change feed, and that has consequences. A delete on one side will be mirrored, so an accidental deletion propagates unless something stops it. Conflict resolution is handled by named strategies, and the documented set is MostRecentRS, LeastRecentRS, AlwaysFirstRS and AlwaysSecondRS, with the note that an individual executable may specify otherwise. This is timestamp based resolution with two fixed policies and two time based ones. The project states that all synchronizations support conflict resolution in the sense of successfully dealing with edits made on both sides, which is a weaker claim than guaranteeing a correct merge. For services whose side has no reliable timestamp, the fixed strategies are the fallback and they are a policy choice, not a resolution.

Each run ends with a per-service summary of changes

Runs are not silent. The project shows the expected end-of-run output, listing created, updated and deleted counts separately for each side of the synchronization.

code
Google Keep
-----------
        * Items created: 3
        * Items updated: 2
        * Items deleted: 1

Taskwarrior
-----------
        * Items created: 1
        * Items updated: 0
        * Items deleted: 0

The shape of that report is worth noting because it is the primary feedback channel. There is no daemon and no continuous mode described, so syncall looks like a command you schedule, with cron or a systemd timer on Linux, the machine being Unix oriented according to the project classifiers. That makes the summary the only way to notice that a pair stopped working. If a token expires or a service changes its response format, an item count that stays flat is the signal. The project does not document a dry run flag or a verbose mode in what is shown here, so treating the summary as something to check is not optional. Notice also that the counts are per service rather than per direction, which means a two-sided change shows up once in each block and you cannot tell from the summary which side originated it.

Installation is a pip extra per service integration

You have to name at least one extra, using the bracket syntax of pip, and the base package alone gives you nothing useful. For Google services and Notion the documented command is:

sh
pip3 install syncall[notion,google]

The available extras are visible in the project metadata: google pulls in google-api-python-client and google-auth-oauthlib, gkeep pulls in gkeepapi, notion pulls in notion-client, asana pulls in asana, caldav pulls in caldav and icalendar, tw pulls in taskw-ng and xdg, and fs pulls in xattr. Installing from PyPI with several extras at once follows the same bracket syntax, and installing from a git checkout is documented too, including the devel branch as bleeding edge.

sh
git clone https://github.com/bergercookie/syncall
cd syncall
git checkout devel

The extras list accepted by that install is gkeep, fs, google, tw, caldav and asana.

One detail to check before you install. The installation notes state a requirement of Python version >= 3.8, and Taskwarrior version >= 2.6 for the integrations that need it, while the project metadata declares requires-python as >=3.11,<4 and lists classifiers up to Python 3.15. The metadata is the authoritative constraint for a modern install, so a Python 3.8 environment will fail at install time even though the notes suggest it would work.

Extending the tool means writing a synchronization side

The stated main goal of syncall is to be extendable, and the mechanism is concrete. Adding a service, adding a side, or pairing two services that have not been paired before means implementing the corresponding synchronization sides and conversion methods, with the process written up in docs/implement-a-new-synchronization.md and a contributing guide. The name ClickUp appears in the project documentation as the example of a service to add and as the example of a new pairing to introduce. The conversion method is the interesting part, because a pairing between two services needs a translation from one item model to the other, and that translation is what you write when you add a side. The shared library item-synchronizer is one of the runtime dependencies, and the presence of a separate package for the item layer suggests the reconciliation engine is not written from scratch for each service. Contributors should note the development tooling in the repository: a justfile whose recipes wrap devbox, a devbox.json for a reproducible environment, pre-commit configuration, and coverage reporting through coveralls.

Local development runs through devbox and just

The repository has a working setup for contributors rather than only for end users, and the pieces are visible in the top level files. The justfile imports a base from bootstrap/just/justfile-base, exposes a default recipe that lists the available recipes, and a shell recipe that opens a shell with the devbox environment sourced. Setup is split into setup-deps and setup-pre-commit, where dependencies are installed in editable mode with every extra and the hooks are registered with pre-commit install. Checks run through pre-commit run --all-files, which is aliased as check, and the test suite runs through pytest.

sh
    uv pip install -e ".[all]"
    pre-commit install
    pre-commit run --all-files

The environment is pinned with devbox.lock, poetry.lock and uv.lock all present in the tree, which is three lockfiles for one project and suggests some churn in the packaging tooling over time. There is also a completions directory and a recipe that regenerates shell completions for bash, zsh and fish across all sync executables. The fs-gkeep-sync pair, which synchronises local files with Google Keep, depends on xattr, which is a filesystem extended attribute library, hinting that local file metadata is used to track state for that pair.

The release trail is thin, the commit trail is not

This is where a prospective adopter needs to slow down. The only tagged release listed for the project is v1.8.0, dated 2024-01-29, titled with a note about using taskw-ng to interact with Taskwarrior and making Taskwarrior filtering more powerful. The repository itself is not archived, and the last push was on 2026-07-21, so work has continued on the master branch well after that release. In other words the code is being changed and the published artefact is not moving with it. That gap has a concrete consequence: the only published release listed is 1.8.0, while the master branch carries a different dependency set, since the metadata now requires Python 3.11 or newer and lists taskw-ng in the tw extra, which is exactly the change the 1.8.0 release note describes. The project documentation also points at the devel branch for bleeding edge. The licence is MIT, which is permissive and imposes no obligations on downstream users beyond keeping the notice, so the licence is not a barrier here. The release cadence is.

Editorial conclusion

syncall fits people who already keep tasks in Taskwarrior and want them mirrored into Google Calendar, Google Tasks, Notion, Google Keep, Asana or a CalDAV server, and it fits contributors willing to implement a synchronization side to extend it. It does not fit teams that need a hosted service with a user interface, and it does not fit anyone who needs a current release line, since the only tagged release is v1.8.0 from 2024-01-29 while the last push was on 2026-07-21. Before adopting it, install with pip3 install syncall[notion,google], read the per-service README listed for your pairing, and check the required Python version yourself because the installation notes and pyproject.toml disagree.

Frequently asked questions

How do I install syncall?

Use pip with at least one extra for the service you need, for example pip3 install syncall[notion,google], or install from a git checkout with pip3 install --user --upgrade .[gkeep,fs,google,tw,caldav,asana] after checking out the devel branch.

Which service pairs does synall support?

Taskwarrior with Google Tasks, Google Calendar, Notion checkboxes, Google Keep checkboxes, Asana tasks and a generic CalDAV server, plus local files with Google Keep notes, each with its own executable such as tw-gcal-sync or fs-gkeep-sync.

How does syncall resolve conflicts?

The documented conflict resolution strategies are MostRecentRS, LeastRecentRS, AlwaysFirstRS and AlwaysSecondRS, though an individual executable can specify otherwise.

What Python version does syncall need?

The installation notes say Python version >= 3.8, while the project metadata declares requires-python as >=3.11,<4. The metadata value is the one that governs a current install.

How do I add support for a new service to syncall?

You implement the corresponding synchronization sides and conversion methods, following docs/implement-a-new-synchronization.md and the contributing guide. The project documentation uses ClickUp as the example of a service to add.

Official sources

  1. bergercookie/syncall on GitHub
  2. Issues
  3. License: MIT
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/bergercookie-syncall.svg)](https://hysenlabs.com/projects/bergercookie-syncall)

Was this useful?