CLI tool
todotxt/todo.txt-cli avatar
todotxt/todo.txt-cli

todo.txt-cli: the whole task manager is one shell script and one config file

☑️ A simple and extensible shell script for managing your todo.txt file.

6,200 stars732 forksShellGPL-3.0

At a glance

What is it?
todo.txt-cli keeps your task list in a plain text file you already know how to open, and manages it with a single shell script. The interesting parts are the install paths that put the config in different places and the flags that sit before the action.
Who is it for?
todo.txt-cli suits people who already keep tasks in a text file and want a command line over it, not a replacement for a richer task app. Before committing, check three things: which config location your install method created, whether your shell reads the bundled completion file, and what the last version bump actually changed.
Can I use it commercially?
Yes, with conditions. GPL-3.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
Is it still maintained?
Yes. The repository last received commits 8 days ago.
What is it written in?
Mainly Shell, according to GitHub's language statistics.

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

From daily snapshots

Star history

  • todotxt/todo.txt-cli6,200 ★ · +24
Star history
Repository2026-09-232026-11-14
todotxt/todo.txt-cli6.2k6.2k

24 Stars in the last 52 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

Options come before the action, and the config file is a -d flag

The usage line is short enough to memorise:

shell
todo.sh [-+@aAcDfhMnNpPtTvVx] [-d todo_config] action [task_number] [task_description]

Options sit in front of the action, the action takes no flag of its own, and the task number is positional rather than named. The one option you will actually reach for is -d, and it takes a file path rather than a directory. Leave it off and the script looks in the locations listed by todo.sh help; a default make install writes the template to /usr/local/etc/todo/config. That is the file your own edits would share with every user on the machine.

The front page does not enumerate the actions. It sends you to USAGE.md in the repository root for the full list, so the set of verbs is not something you can learn without leaving the README. The todo.txt filename itself is not a command line option either: where the file lives comes from configuration, not from an argument, which is why relocating it is a config edit rather than a flag.

make install writes into /usr/local unless you name three directories

The Makefile is short and every path in it is overridable. The default prefix is /usr/local, the install command is /usr/bin/install, and the shell is /bin/bash. Three variables decide where things land. INSTALL_DIR sets where todo.sh goes, defaulting to /usr/local/bin. CONFIG_DIR sets the directory that will hold the todo/config configuration template, defaulting to /usr/local/etc. BASH_COMPLETION sets where the completion script goes, defaulting to /usr/local/share/bash-completion/completions.

Each one is guarded by an ifdef, so the fallback only applies when you say nothing. The README offers an example that overrides all three at once, and labels it as legacy locations that are not recommended:

shell
make install CONFIG_DIR=/etc INSTALL_DIR=/usr/bin BASH_COMPLETION=/etc/bash_completion.d

Two details in that block catch people out. DEST_CONFIG resolves to sysconfdir/todo/config, so the config target is a file named config inside a todo directory, not a config file directly in the directory you passed. And DEST_COMPLETION installs the completion script under the name todo.sh, which is how a shell finds it as a completion for the todo.sh command.

Homebrew puts the config in your home directory, make leaves it system wide

The macOS path is two commands rather than a build:

shell
brew install todo-txt

cp -n $(brew --prefix)/opt/todo-txt/todo.cfg ~/.todo.cfg

The second command is the interesting one. Homebrew leaves the template inside its own prefix, and the copy is what puts it where the script will look. The -n flag on cp is there so an existing file is never overwritten, which means a second run of that command does nothing and does not tell you it did nothing.

The consequence is that the two supported platforms hand you different setups by default. A Homebrew user ends up with a per-user config at ~/.todo.cfg. A default source install on Linux ends up with a shared config at /usr/local/etc/todo/config. Nothing reconciles the two, and nothing tells you which one the script picked if you have both on the same machine. If you have run the Homebrew copy before, the -n flag will preserve whatever was already there, which is the intended behaviour and also the reason a stale config survives an upgrade unnoticed.

TODO_DIR is the setting most users change, and the list of config paths is not published

No configuration is required to run the script, which is true and slightly misleading, because the config file is also where the list of every available option lives. The shipped template carries a commented-out list of all options, and most people end up editing it for one of three reasons: moving the todo.txt directory into a subdirectory of the home directory, moving it onto a cloud drive through the TODO_DIR variable, or changing colors and the highlighting applied to projects, contexts and dates.

The advice given is to copy that template into one of the locations listed by `todo.sh help` on -d CONFIG_FILE, and to do it even when the file is already installed in the global configuration location. The catch is that the README does not print those locations. You have to run the help command and read them to find out where the script actually looks, and the global path at /etc/todo/config is only one of the candidates.

So the two things to verify on a fresh install are which file the script picked up and whether your todo.txt is where TODO_DIR says it is. Both are checkable from the command line before you trust an empty list.

GEN-VERSION-FILE builds the version at install time instead of shipping it

There is no VERSION-FILE in the repository. Instead the Makefile has a target that runs the GEN-VERSION-FILE script at the top of the tree, and then includes the result with -include VERSION-FILE so that make picks up a VERSION variable it did not have before. That variable is used by both todo.sh and the Makefile itself, so the script and its build agree on the version number without anyone editing a file by hand.

The distribution layout follows from it. DISTNAME is built as todo.txt_cli-$(VERSION), SRC_FILES names the two files copied unmodified, which are todo.cfg and todo_completion, and DISTFILES collects those plus todo.sh into the versioned directory before it is created. Nothing in that chain is unusual; the point is that a build without the generator script has no version, which is worth knowing if you are vendoring the script into a larger project.

The Makefile also generates its own help text. The default target is help, and the target list is scraped out of inline double-hash comments with sed and piped through column. Running plain make prints that list rather than building anything, so a build on a machine without sed or column behaves differently from one that has them.

One script, one template, and a repository with no plugin directory

The whole project is small enough to read in a sitting. At the top level there is todo.sh, todo.cfg, the todo_completion file, the Makefile, the GEN-VERSION-FILE generator, a tests directory, USAGE.md, CHANGELOG.md, SECURITY.md, LICENSE, and a .github directory holding the contributing guide and the code of conduct. The license is GNU General Public License v3.0 and the project site is at todotxt.org.

Calling it extensible is fair in one direction and misleading in another. Extension happens through the config file, which controls where the data lives and how it is coloured and highlighted, rather than through code you drop into a directory. There is no plugins folder or library folder at the top level of this tree.

Two limits follow. Anything beyond config, such as generating new reports or feeding a task list into another tool, means writing your own wrapper around the script or driving todo.txt directly. And the completion file is a bash completion: a shell that is not bash gets no tab completion, since the Makefile installs it into a bash-completion directory and nothing installs an alternative.

Three releases in six years, with commits far more recent than any tag

The repository is not archived and the default branch is master, with a push on 11 October 2026. The tags tell a different story about shipping: v2.14.0 on 1 September 2026, then v2.13.0 on 25 December 2024, then v2.12.0 on 12 August 2020. Work on the branch and the release cadence are two different clocks, and for a script that people install by copying rather than by upgrading a service, the changelog matters more than the tag dates.

Installation is documented for three platforms and no others: macOS through Homebrew, Linux by running make, make install and make test from a checkout, and Arch Linux through the AUR package at aur.archlinux.org. There is no documented Windows path, and none of the three routes gets you a binary. Questions go to GitHub Discussions, bugs to GitHub issues after a positive word in chat, and the project also points at a Gitter room, a subreddit and a Twitter account.

What you give up by choosing this script is the part to weigh. There is no sync, no mobile client, no due-date reminders and no shared task database in this repository. It edits one file on one machine, which is exactly why the format stays readable to everything else that can open a text editor.

Editorial conclusion

todo.txt-cli suits people who already keep tasks in a text file and want a command line over it, not a replacement for a richer task app. Before committing, check three things: which config location your install method created, whether your shell reads the bundled completion file, and what the last version bump actually changed. It runs on macOS, Linux and Arch Linux; the project documents no Windows path, so a Windows host needs the text file plus something else to drive it.

Frequently asked questions

How do I install todo.txt-cli on macOS?

Run brew install todo-txt, then copy the config template out of the Homebrew prefix with cp -n $(brew --prefix)/opt/todo-txt/todo.cfg ~/.todo.cfg. The -n flag stops the copy from overwriting a config file you already have.

Where does todo.txt-cli look for its configuration?

A default source install writes the template to /usr/local/etc/todo/config, while the Homebrew path puts a copy at ~/.todo.cfg. The -d option takes a file path, and todo.sh help lists the locations the script accepts.

Does todo.txt-cli have an official Windows installer?

No Windows install is described. The documented routes are Homebrew on macOS, make install from a checkout on Linux, and the Arch User Repository package.

How do I add a task with a project and a context using todo.txt-cli?

The action comes first and the task text goes in quotes: todo.sh add "THING I NEED TO DO +project @context". The plus sign and the at sign are part of the text you type, and the config template controls how they get highlighted.

Official sources

  1. License: GPL-3.0
  2. Project website
  3. README
  4. Releases
  5. todotxt/todo.txt-cli on GitHub
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/todotxt-todo-txt-cli.svg)](https://hysenlabs.com/projects/todotxt-todo-txt-cli)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/todotxt-todo-txt-cli.svg)](https://hysenlabs.com/projects/todotxt-todo-txt-cli)

Was this useful?