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 #
--jsonon any command prints the API's JSON (the same objects as the API reference) instead of text — pipe it tojq.- Colours only when the output is a terminal;
NO_COLOR=1or--no-colorturns them off,FORCE_COLOR=1on. todomode addandtodomode importsend an idempotency key and retry once, with the same key, if the connection drops or the server stumbles — so a flaky network never adds a task twice.- Exit codes:
| Code | Meaning |
|---|---|
| 0 | Success. |
| 1 | It did not work: not found, ambiguous, invalid input, a notes conflict. |
| 2 | Usage error (bad arguments, a token that is not token-shaped, plain http, a server that answers with a redirect). |
| 3 | Not logged in, or the token was rejected (revoked, expired). |
| 4 | Not allowed: a read-only token, or a project shared with you read-only. |
| 5 | Your trial or subscription has ended (reading still works). |
| 6 | Rate limited — 120 requests a minute per token; wait and retry. |
| 7 | TodoMode 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 #
| Variable | Meaning |
|---|---|
TODOMODE_TOKEN | The token, instead of todomode login. |
TODOMODE_URL | Another TodoMode server (default https://app.todomode.com); todomode login --url saves one. Plain http:// is refused except to localhost. |
XDG_CONFIG_HOME, XDG_CACHE_HOME | Where the config (todomode/config.json) and the last listing (todomode/last.json) live; default ~/.config, ~/.cache. |
VISUAL, EDITOR | The editor for todomode note. |
NO_COLOR, FORCE_COLOR | Colours off / on. |
Troubleshooting #
CERTIFICATE_VERIFY_FAILEDon macOS with Python from python.org: run Install Certificates.command in that Python's folder under Applications.- "Your API token was not accepted": it was revoked or expired — make a new one and
todomode loginagain. - "answered with a redirect":
TODOMODE_URL(orlogin --url) is not the TodoMode server itself — say, the marketing site, orhttp://where the server wantshttps://. Set it to the address the redirect names. The CLI does not follow redirects, so your token never goes there. python3: command not found: install Python 3 (xcode-select --installon macOS, your package manager on Linux), or run it aspython3 ~/.local/bin/todomodefrom wherever Python is.