CLI tool
pimutils/todoman avatar
pimutils/todoman

todoman: a command line todo manager that is just iCalendar files

✅ Simple, standards-based, command line todo (aka: task) manager.

602 stars102 forksPythonISC

At a glance

What is it?
todoman is an ISC licensed Python command line todo manager that stores every task as an iCalendar file, which is what lets the same list sync over CalDAV and stay readable by other tools. The trade is that it is Unix only, and Windows support is explicitly declined.
Who is it for?
todoman suits a terminal user who wants a task list that is not trapped in a vendor's database and can be synced with a CalDAV server through vdirsyncer, and who is comfortable with iCalendar semantics. It does not suit a Windows user, since the project says it does not support Windows and that very basic testing indicates it does not work, and it does not suit someone who wants a shared team tracker with assignments and comments.
Can I use it commercially?
Yes. ISC 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 151 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

The storage format is the design

todoman is described as a simple, standards-based command line todo, also called a task, manager. The sentence that matters is the second one: todos are stored into icalendar files, which means they can be synced via CalDAV using, for example, vdirsyncer.

That single decision explains almost everything else about the project. A task list stored in icalendar files is not a private database format. It is a set of files, one per todo, in a directory, which is the layout that the vdir specification describes and the layout that CalDAV servers speak. A user who already runs vdirsyncer against a CardDAV or CalDAV account can point todoman at the same directory and have the same tasks in the terminal.

The alternative design, a SQLite file or a JSON blob owned by the application, would be simpler to implement and would break every other tool's ability to read the list. The cost of the standards-based route is that the data model is constrained by what iCalendar can express, and that the directory has to be laid out in a way the sync tooling understands.

For a reader, this is also the fastest way to judge whether the project fits. If the todo list is going to live in one place forever, a simpler tool with a nicer interface is the better answer. If the list has to travel between machines, to a phone, or through a server you do not own, being a directory of ics files is the feature.

One file per todo, read from a configured directory

The feature list says todos are read from individual ics files from the configured directory, and that this matches the vdir specification. That is a plain statement of the data layout, and it has consequences worth spelling out.

Each task is a separate file, so a todo can be created, edited, listed and removed as a file operation, and a sync tool can diff the directory and upload what changed rather than re-uploading a whole collection. It also means the list is greppable and diffable, and that a task can be moved between lists by moving a file.

The set of supported fields is stated as the most common TODO features for now: summary, description, location, due date and priority. The phrase for now is doing some work, because it tells you the field coverage tracks what the underlying iCalendar standard and the common clients support, and it is not a promise that more will never be added.

What todoman does with fields it does not understand is the part that makes the format choice safe in practice. The documentation states that unsupported fields may not be shown but are never deleted or altered. So a task carrying a field from another tool, or a property a sync client adds, survives a round trip through todoman untouched. For a tool that writes to files other software also writes to, a guarantee of non-destructive behaviour is more valuable than another field in the interface.

A curses interface built on click and urwid

The interface is a terminal interface, and the packaging metadata says so in the classifiers: `Environment :: Console` and `Environment :: Console :: Curses`. The heavy lifting for that is `urwid`, the urwid dependency in the project, with `click` and `click-log` handling the command line and the logging.

Two other dependencies explain behaviour a user will notice. `humanize` is there to render times and dates in a form a person wants to read, which is what a due date column needs. `parsedatetime` and `python-dateutil` handle the parsing side, which matters because a todo can be written with a due date in whatever form the user types.

`pyxdg` is the one that looks incidental and is not. It is the library for the XDG base directories specification, so the configuration and data locations follow the platform convention on Linux rather than being invented by the application. The repository carries a `config.py.sample` file, which is the sample configuration, and the choice of `pyxdg` is the reason a config file can be found in a predictable place on a Unix-like system.

The command itself is short. The packaging metadata declares a single console script:

toml
[project.scripts]
todo = "todoman.cli:cli"

So the binary is `todo`, and the `repl` optional dependency pulls in `click-repl` for an interactive shell, which is the mode a user is most likely to end up in for a task list.

Unix only, and the project says so plainly

The README is unusually direct about platform support. Todoman should run fine on any Unix-like operating system, and it has been tested on GNU/Linux, BSD and macOS. Windows is not supported, and very basic testing seems to indicate it does not work.

That is a decision, not an oversight, and the reason is visible in the dependency list. `urwid` and the curses interface expect a terminal with the capabilities that Unix-like systems provide, and `pyxdg` places configuration in the platform's XDG directories, which are a Unix convention. A port to Windows would mean reworking how the interface draws and where the configuration lives, and the project has decided that is not worth doing.

The consequence for anyone evaluating it is simple and worth stating before anything else: a Windows user is out of luck, and a user who needs a single tool across macOS, Linux and Windows will have to keep two systems or pick something else. That is a real cost for a lot of people, and no amount of iCalendar standardisation removes it.

The alternative on the Unix side is straightforward. There are other command line task managers, and there are web and desktop ones, and the distinguishing feature of todoman is not the interface but the interoperability. A task list that lives as ics files can be opened by a calendar application on the same phone, and can be carried between a work machine and a home machine by the same sync tool the user already runs.

Packaging, tests and the release pipeline

The packaging metadata describes a project with a considered dependency policy. Python 3.10 or newer is required, and the classifiers list CPython 3.10, 3.11 and 3.12 along with PyPy. The licence is ISC, and the licence text is shipped in the package through the `license-files` entry rather than only living in the repository.

The runtime dependencies are pinned with ranges, and one of them is pinned with an exclusion:

toml
dependencies = [
    "click>=7.1,<9.0,!=8.3.0",
    "click-log>=0.2.1",
    "humanize",
    "icalendar>=4.0.3",
    "parsedatetime",
    "python-dateutil",
    "pyxdg",
    "urwid",
]

The `!=8.3.0` exclusion on click is the kind of detail that tells you the dependencies are watched rather than assumed.

The optional dependency groups are conventional and reveal the workflow. The `lint` group carries `ruff` and `mypy` with type stubs for `pytz` and `python-dateutil`, the `test` group carries `pytest`, `pytest-cov`, `hypothesis` and `freezegun`, the `docs` group carries `sphinx-click` and `sphinx_rtd_theme`, and the `repl` group carries `click-repl`. The presence of `freezegun` and `hypothesis` tells you the test suite freezes time and generates cases, which matters for a program whose output depends on the current date.

The lint configuration in the same file is strict about annotations, bugbear footguns, comprehensions, import order, logging calls, naming and pytest style. The repository layout backs this up with a `tox.ini`, a `.pre-commit-config.yaml`, a `codecov.yml`, a `tests/` directory, a `docs/` directory and a `contrib/` directory, and the CI badge in the README points at a sourcehut builds pipeline rather than GitHub Actions. There is also a `publish-release.yaml` workflow, and the project version is dynamic rather than written into the metadata.

On upkeep, the repository is not archived and the last push was on 2026-05-25, with no GitHub releases published, and the changelog lives in `CHANGELOG.rst` in the repository.

Editorial conclusion

todoman suits a terminal user who wants a task list that is not trapped in a vendor's database and can be synced with a CalDAV server through vdirsyncer, and who is comfortable with iCalendar semantics. It does not suit a Windows user, since the project says it does not support Windows and that very basic testing indicates it does not work, and it does not suit someone who wants a shared team tracker with assignments and comments. Before adopting, read the manual at todoman.readthedocs.io for the install path, since the project description does not carry one, and check the sample configuration in config.py.sample, since the whole storage model depends on where the ics directory is pointed.

Frequently asked questions

Where does todoman store my todo items?

Each todo is stored as an individual icalendar file in the configured directory, which matches the vdir specification. Because the data is plain files in a standard layout, the list can be synced through CalDAV with tools such as vdirsyncer.

Does todoman work on Windows?

No. The project states that it should run fine on any Unix-like operating system, that it has been tested on GNU/Linux, BSD and macOS, and that Windows is not supported with very basic testing indicating it does not work.

What happens to task fields that todoman does not support?

They may not be shown, but the documentation states they are never deleted or altered. That guarantee is what makes the tool safe to point at a directory that other calendar and sync software also writes to.

Which todo fields does todoman support?

The most common ones for now: summary, description, location, due date and priority. The values are parsed with parsedatetime and python-dateutil and rendered with humanize, so dates can be written in a readable form and displayed in a readable form.

How do I install todoman and where is its configuration?

The project description points to the manual at todoman.readthedocs.io for detailed usage and configuration documentation rather than giving a command in the README itself. The sample configuration is shipped in the repository as config.py.sample, and the pyxdg dependency means configuration follows the XDG base directory convention on Unix-like systems.

Official sources

  1. Issues
  2. License: ISC
  3. pimutils/todoman on GitHub
  4. Project website
  5. README
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/pimutils-todoman.svg)](https://hysenlabs.com/projects/pimutils-todoman)

Was this useful?