Cadence
There are thousands of todo CLIs. Cadence is the one built for an AI agent to run as a first-class user, not a bolted-on chat wrapper on top of a human tool — and it turns git, which you already have installed, into the undo/history/audit/sync layer instead of building a bespoke (and weaker) version of all four.
What that buys you, concretely:
- Hand your agent a messy brain-dump and get tracked subtasks back.
cadence decompose 12 --into "book flights" "book hotel" "pack"(or thedecompose_taskMCP tool) turns one vague task into real, independently completable children — no more one giant to-do that never gets checked off. - Ask "why did this get bumped to top priority" and get a real answer.
Every
reprioritise,schedule, and edit is a git commit under the hood, so the answer isgit log/git blameon your own task store, not a guess or a feature nobody built. - Undo a bad agent action instantly, with the same guarantee git gives
your code.
cadence undoreverts the last change — yours or your agent's — because it is a git revert, not a bespoke undo stack that only covers some operations. - Keep two devices' task lists in sync without running a server.
cadence syncpulls and pushes against a git remote you already control (a repo, a USB stick, anything git can reach) — no account, no hosted backend, no uptime to worry about.
The plan is to publish Cadence as a public, installable, open-source project by 6 November 2026. That date is a delivery commitment, not an estimate.
Status
Published on PyPI as cadence-todo
— install with pip install cadence-todo. It builds, runs, and is tested
from a fresh clone, and CI is green on a clean GitHub-hosted runner (see the
finish-line checklist below for what's still outstanding). See
docs/bakeoff.md for the five candidate concepts we
researched, the evidence behind each, and which one we chose and why, and
docs/human-surface.md for the CLI's binding
design spec.
Install
pip install cadence-todo
Every line below is a real CLI command, run yourself, no agent required —
this is the whole bet: every change to your list, yours or an agent's,
comes with a legible reason and a clean undo, because it's backed by the
same git you already trust for code, not a bespoke history feature Cadence
invented and might get wrong. Real output, cadence-todo 0.2.9,
NO_COLOR=1:
$ cadence add "Plan Mara's 30th birthday party"
Added #1: Plan Mara's 30th birthday party
$ cadence decompose 1 --into "Book a venue" "Order a cake" "Send invites"
Decomposed #1 into 3 subtasks: #2, #3, #4
$ cadence reprioritise 2 high --reason "venue books up fastest"
Reprioritised #2 (none → high): Book a venue
$ cadence why 2
#2 Book a venue — history (newest first):
- high just now Reprioritised (none → high)
"venue books up fastest" — you, via CLI
- none just now Created as subtask of #1 (Plan Mara's 30th birthday
party)
No reason was recorded for this change. Reasons are optional —
pass --reason "..." (CLI) or a `reason` argument (MCP tool call)
to leave one next time.
$ cadence undo
Undid: Reprioritised #2 (none → high) undone: Book a venue
That why line is the payoff: a straight answer to "why did this change,"
without you ever opening git, a hidden folder, or asking your agent to
explain itself. cadence list and cadence done <id> work exactly the
way you'd expect from any todo CLI — full command reference below.
By default tasks live in ~/.cadence/cadence.db (a local SQLite file) with
a git-backed history alongside it. Set CADENCE_DB_PATH to point at a
scratch file instead (used by the test suite and useful for an agent that
wants an isolated store).
Start the MCP server (agent surface) over stdio, exposing tools such as
add_task, list_tasks, complete_task, schedule_task,
decompose_task, reprioritise_task, undo, and sync_tasks with
structured JSON returns:
cadence mcp
Remote access (Claude web, Claude mobile, another machine)
cadence mcp (above) talks stdio, so only a local process on the same
machine — e.g. Claude Code / VSCode — can reach it. If you also want Claude
web or Claude on your phone to reach the same task store, run the HTTP
transport instead:
cadence mcp --http
This is still local-first: it starts a server on your own machine
(default 127.0.0.1:8765), backed by the exact same store as cadence mcp and the cadence CLI — there is no hosted backend, no account, no
third party in the middle. It prints the port it's listening on and
requires a bearer token on every request, generated once on first use and
stored at ~/.config/cadence/mcp_http_token (owner-read/write only, same
directory as the rest of Cadence's config). Get that token with:
cadence mcp --show-token
To use it from a remote client (Claude web/mobile, or an agent on another
machine): expose the port to that client somehow — an SSH tunnel,
Tailscale, or a TLS-terminating reverse proxy are
all reasonable choices; Cadence does not add TLS itself — then configure
the client with the resulting URL's /mcp path and an Authorization: Bearer <token> header carrying the token from cadence mcp --show-token.
A request with a missing or wrong token gets a clean 401 with the same
{"ok": false, "error", "message", "hint"} shape every other Cadence
error uses, not a stack trace.
You can also pass --host, --port, or an explicit --token (or set
CADENCE_MCP_TOKEN) to override the generated one, e.g. for a fixed token
across restarts in a script.
Building from source
git clone https://github.com/dominicplouffe/Cadence.git
cd Cadence
pip install -e .
pip install pytest
pytest -q
Get Started
Three ways to point an agent at Cadence, depending on where that agent runs.
1. Claude Code / VSCode, running on the same machine (stdio)
This is the simplest path: the client starts cadence mcp itself as a
subprocess, talks to it over stdio, and no network or token is involved.
pip install cadence-todo
Add it as a project-scoped MCP server, either with the CLI:
claude mcp add --transport stdio cadence -- cadence mcp
or by hand in .mcp.json at your project root:
{
"mcpServers": {
"cadence": {
"type": "stdio",
"command": "cadence",
"args": ["mcp"]
}
}
}
Restart Claude Code (or reload the window) and it will list add_task,
list_tasks, complete_task, schedule_task, decompose_task,
reprioritise_task, why_task, undo, sync_tasks, export_tasks, and
register_project as available tools.
2. Claude web or Claude on your phone (HTTP transport, over a tunnel)
Claude web and mobile can't spawn a local process on your machine, so they need to reach Cadence over HTTPS instead. Run the HTTP transport locally:
cadence mcp --http
This starts a server on 127.0.0.1:8765 — still on your own machine, still
backed by the same store as the CLI and the stdio server, no hosted
backend. It requires a bearer token on every request (cadence mcp --show-token to print it). By itself this is only reachable from
localhost; you need a tunnel to get a real HTTPS URL a remote client can
use. Do not run this with --host 0.0.0.0 — that exposes the port to
your whole network with only the bearer token standing between it and
anyone on that network; a tunnel keeps the port itself bound to
127.0.0.1 and only forwards over an authenticated HTTPS connection.
Cloudflare Quick Tunnel (no account needed, URL changes each run):
cloudflared tunnel --url http://127.0.0.1:8765
prints an HTTPS URL like https://some-random-words.trycloudflare.com.
Configure the remote client with <that URL>/mcp and an Authorization: Bearer <token> header carrying the token from cadence mcp --show-token.
Tailscale Funnel is a reasonable alternative if you already run
Tailscale on both machines and want a stable URL instead of a new one per
run: tailscale funnel 8765 after cadence mcp --http is running.
Real transcript, cadence-todo 0.2.15, fresh install from PyPI, a live
Cloudflare Quick Tunnel (not simulated):
$ pip install cadence-todo==0.2.15
$ cadence add "Verify README Get Started (0.2.15)"
Added #1: Verify README Get Started (0.2.15)
$ cadence mcp --http --port 8767
[cadence mcp --http] listening on http://127.0.0.1:8767/mcp -- bearer token required on every request
$ cloudflared tunnel --url http://127.0.0.1:8767
...
Your quick Tunnel has been created!
https://fastest-david-remedies-absent.trycloudflare.com
No token, over the tunnel — still a clean 401:
$ curl -sS -i https://fastest-david-remedies-absent.trycloudflare.com/mcp \
-X POST -H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{...}}'
HTTP/2 401
{"ok":false,"error":"unauthorized","message":"Missing or wrong bearer token.","hint":"Send 'Authorization: Bearer <token>' matching the token this server was started with (see `cadence mcp --http --show-token`)."}
Correct token, over the tunnel — a real initialize response:
$ curl -sS -i https://fastest-david-remedies-absent.trycloudflare.com/mcp \
-X POST -H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "Authorization: Bearer 29bd96fe9fd267b0f7cb33a188d947ce14267654be081e0d2ae44153dd660f9c" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"verify-client","version":"1.0"}}}'
HTTP/2 200
mcp-session-id: d77b4b16fe114b1eb85633a11f106834
content-type: text/event-stream
event: message
data: {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":"2025-06-18",
"capabilities":{...},
"serverInfo":{"name":"cadence","version":"1.29.1"},
"instructions":"Cadence is a local-first todo store. ..."}}
A follow-up tool call over that same tunnel session lands in the real,
local store — confirmed with a plain cadence list afterward, no
difference between a task created locally and one created through the
tunnel:
$ curl ... -d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"add_task","arguments":{"title":"Confirmed via tunnel (README verify)"}}}'
HTTP/2 200
data: {"jsonrpc":"2.0","id":2,"result":{"content":[{"type":"text","text":"{\n \"ok\": true,\n \"task\": {\n \"id\": 2,\n \"title\": \"Confirmed via tunnel (README verify)\", ...}}"}],"isError":false}}
$ cadence list
[ ] 1 Verify README Get Started (0.2.15)
[ ] 2 Confirmed via tunnel (README verify)
3. More than one project
If you keep separate Cadence stores per project (separate CADENCE_DB_PATH
values, e.g. one per git repo), register each one so the cross-project
commands can find it:
$ cd ~/proj1 && cadence register
Registered /home/you/proj1/cadence.db (as 'proj1').
$ cd ~/proj2 && cadence add "File Q3 taxes"
Added #1: File Q3 taxes
$ cadence schedule 1 2026-01-01
Scheduled #1 for 2026-01-01: File Q3 taxes
$ cadence register
Registered /home/you/proj2/cadence.db (as 'proj2').
$ cadence overdue --all-projects
[!] proj2 #1 File Q3 taxes | overdue 242d
1 overdue across 2 registered projects. Run 'cadence register' in a project directory to add another.
cadence sync --all-projects works the same way — one call syncs every
registered project's store against whatever remote it already has
configured, instead of running cadence sync separately in each project
directory. The MCP tool equivalents are register_project,
overdue_tasks(all_projects=true), and
sync_tasks(all_projects=true).
What "agentic-first" means here
- Primary interface is agent-legible by design. An agent that has never read the docs should be able to work out what each tool does, what it returns, and what it did wrong, from the interface itself.
- A human surface is not optional. A capability that exists only for agents is half-built, and so is one that exists only for humans.
- Local-first bias. We prefer something a person installs with a single command and owns the data of, over a hosted multi-tenant web app.
The finish line
Three things must be true and independently checkable by someone outside this company, on or before the committed date:
- Published on a public package registry, installable with one command.
- CI on a clean GitHub-hosted runner goes green on the full suite, including an end-to-end test that installs the published artifact and drives it.
- A committed transcript exists of an agent with no access to this repository completing a fixed ten-step script using only the published package.
Contributing
Not yet open for external contributions — the project is pre-publication. Once published, this section will describe how to build from a fresh clone, run the test suite, and submit changes.
License
MIT — see LICENSE.
Metadata
Release files for cadence-todo 0.2.32
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| cadence_todo-0.2.32.tar.gz | 112.4 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| cadence_todo-0.2.32-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 185.9 kB
Release files / cadence_todo-0.2.32.tar.gz
| Download URL | cadence_todo-0.2.32.tar.gz |
|---|---|
| Size | 112.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
01e4e3585888b9599e8d443218f261c1200117b516013db36c887d2a6145e22a
|
|
BLAKE2b-256 checksum How to use checksums |
ceb5630565dd42c97ca2732b41839b4aaafe1d9d1a5f35ee4c9d83ec61824c49
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Release files / cadence_todo-0.2.32-py3-none-any.whl
| Download URL | cadence_todo-0.2.32-py3-none-any.whl |
|---|---|
| Size | 73.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
a259da1860ad4ba2d6f15f1220d2f620189f55aa1aeeffbf00a4f9f8621c1302
|
|
BLAKE2b-256 checksum How to use checksums |
016a596e938386d11d1ae6b61ac7990e33d710bf19dd6d0c726ebbc22545e1c9
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Security for cadence-todo 0.2.32
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.