The todomode command line

todomode puts TodoMode in your terminal: add a task without leaving the shell, list a project as an outline, tick things off by number, edit a note in your own editor, pipe a markdown list in. Everything has --json for scripts. It is one file with no dependencies beyond Python 3.8 or newer, which macOS (with the Command Line Tools) and almost every Linux already have.

Install #

mkdir -p ~/.local/bin
curl -fsSL https://app.todomode.com/cli/todomode -o ~/.local/bin/todomode && chmod +x ~/.local/bin/todomode
todomode --version

Make sure ~/.local/bin is on your PATH (or put the file anywhere that is). Current version: 1.0.1. To check the download, compare its SHA-256 with ours:

cd ~/.local/bin && curl -fsSL https://app.todomode.com/cli/todomode.sha256 | sha256sum -c -
# macOS: the same with  shasum -a 256 -c -  instead of  sha256sum -c -

SHA-256 of version 1.0.1: e4445679320201ad660726b7b586b5493c6f95311c85c5cdfe9355fc43e2cff6

To update, run the same curl line again.

Log in #

Make a token in TodoMode — Settings → API → Create token (read and write, unless you only want to look) — then:

todomode login            # paste the token when asked; it is not echoed
todomode whoami           # check

The token is saved in ~/.config/todomode/config.json, readable only by you. Instead of logging in you can set TODOMODE_TOKEN in the environment (handy in CI). todomode logout forgets it — the token itself keeps working until you revoke it in Settings → API.

Everyday use #

todomode add "Fix login due:fri at:9am #work !high"       # to your Inbox
todomode add "Book flights due:next week" -p Travel       # to a project (a unique prefix is enough)
todomode add "Write tests" --under 3                      # as a subtask of #3 in the last listing

todomode ls                    # every project, open tasks, as outlines
todomode ls Work               # one project
todomode ls Work --all         # with completed tasks;  --done for only those
todomode ls --due overdue      # across projects; also --tag work, --priority high
todomode today                 # overdue and due today
todomode agenda                # ... through the end of the week (--within nextweek|all)
todomode search "#work due:thisweek -review"

todomode done 3                # complete #3 from the last listing
todomode done "water plants"   # or by words that match exactly one open task
todomode undone 3
todomode show 3                # everything about a task, with its note
todomode note 3                # edit the note in $EDITOR
todomode note 3 -a "Called them, waiting to hear back"
todomode mv 3 Someday          # move it (and its subtasks) to another project
todomode rm 3                  # to the trash; prints the command that undoes it
todomode open 3                # open it in the app (prints the link too)
todomode projects

New tasks understand the app's shorthand: due:tomorrow 9am, at:14:30, #tag, !high, @weekly, @ every 2 weeks, r:-1h (a reminder an hour before). A token the server cannot read (due:someday) is an error, and nothing is added.

Numbers, ids and words #

Every listing (ls, today, agenda, search) numbers its rows, and the next command can use those numbers — todomode done 2 5. The numbers stay valid until the next listing, and they always point at the task you saw, even if the list has changed since. You can also give a task's id (task_…, shown by --json) or a few words: if they match exactly one task, that is the one; if they match several, you get a numbered list to choose from.

Notes in your editor #

todomode note 3 opens the note in $VISUAL or $EDITOR (vi if neither is set), like git commit. Save and quit to store it; quit without changes and nothing happens. If someone changed the same note while you were editing — in the app, on your phone — your save is refused rather than overwriting theirs, and your text is kept in ~/.cache/todomode/ so you can merge it.

Import a list #

todomode import plan.md -p Work
printf -- "- Groceries\n  - milk\n  - bread\n- Call the bank !high\n" | todomode import -

Markdown lists (-, *, 1., [ ], [x]), indented text, OPML or a Todoist CSV. Nesting becomes subtasks; #tags, !priority and @daily work in lines (due: does not — add dated tasks with todomode add).

Scripting #

CodeMeaning
0Success.
1It did not work: not found, ambiguous, invalid input, a notes conflict.
2Usage error (bad arguments, a token that is not token-shaped, plain http, a server that answers with a redirect).
3Not logged in, or the token was rejected (revoked, expired).
4Not allowed: a read-only token, or a project shared with you read-only.
5Your trial or subscription has ended (reading still works).
6Rate limited — 120 requests a minute per token; wait and retry.
7TodoMode could not be reached, or had a problem.
# Everything due today as plain titles
todomode today --json | jq -r '.sections[].tasks[].text'

# Add a task from a git hook, quietly
todomode add -q "Review $(git rev-parse --short HEAD) #code" >/dev/null

Use it as a local MCP server #

todomode mcp speaks the Model Context Protocol on stdin/stdout and relays to TodoMode's hosted MCP server with your saved token — for AI tools that can only start local servers. Most can use the hosted server directly; see Connect AI assistants.

todomode mcp --print-config                          # a snippet for claude_desktop_config.json
claude mcp add todomode -- todomode mcp              # Claude Code, if you prefer it local

Settings #

VariableMeaning
TODOMODE_TOKENThe token, instead of todomode login.
TODOMODE_URLAnother TodoMode server (default https://app.todomode.com); todomode login --url saves one. Plain http:// is refused except to localhost.
XDG_CONFIG_HOME, XDG_CACHE_HOMEWhere the config (todomode/config.json) and the last listing (todomode/last.json) live; default ~/.config, ~/.cache.
VISUAL, EDITORThe editor for todomode note.
NO_COLOR, FORCE_COLORColours off / on.

Troubleshooting #