Research notes, 2026-10-15. Purpose: decide what a from-scratch plain-text checklist tool for this machine should be, by first knowing what already exists. Companion document: checklist-impl-plan.html.
The house tracks work in a hand-maintained markdown file (rebuild-v2-checklist.md).
We want a CLI to replace it with the property of many lists, one file each,
chosen by passing a file path as the first argument: ~/todo.checklist for the
system-wide list, ~/projects/rose-web/bugs.checklist for a project's bugs, and so on.
Required operations: add, list (all or incomplete), mark done, delete.
This document surveys what exists, extracts the design lessons that matter, and records why we build our own small one instead of adopting a tool.
Command-line task tools cluster by storage model. The storage model decides almost everything else — portability, crash behavior, how a second surface (GUI/web) can attach, and what "simple" even means.
| Family | Storage | Examples | Character |
|---|---|---|---|
| Format spec + thin clients | One plain-text file per user, format is the product | todo.txt (spec), todos (ruby), todo.sh (steveyegge), gtd.sh | The format is canonical; clients are interchangeable; the community is large and the spec is stable. |
| Database-backed CLIs | Local database (SQLite / per-task TOML files) | Taskwarrior (task), todoman | Rich features (dependencies, recurrence, tags) at the cost of a private store and an rc-file ritual; the visible file is not the source of truth. |
| File-per-list plain text | One human-readable file, path chosen per invocation or per repo | tick (markdown .tick/tasks/), Reinschrift (one markdown file), markdown-do, md2do, cli-kneeboard |
The file is the source of truth; every surface reads/writes the same file; git-versionable by default. |
| Project schedulers | XML/INI project files | TaskJuggler | Dates, dependencies, critical path, Gantt output. A different problem (planning, not tracking). |
The todo.txt spec's first rule is the single most repeated invariant in this whole field:
> A single line in your todo.txt text file represents a single task.
Fields are positional and minimal: optional priority (A), optional
created/completed dates, then free text with +project / @context tags
in it. The philosophy stated in the spec: human-readable without any tool, and sensible to
plain-text-editor operations (that's why completed tasks lead with a lowercase x —
it sorts to the bottom alphabetically and looks like a filled checkbox).
The spec deliberately caps the field at key:value extensions and says nothing is
required. Clients like todos (ruby) and steveyegge's todo.sh add
subcommands (add, completed, ls, archive).
Weakness for our use: the canonical model is one file per user, fixed location
(~/todo.txt). "Many lists, path as argument" is not in the spec; clients
get it by abusing the --file flag or symlinks.
Sources: too-much-todotxt/spec · todotxt.org · LWN "Managing tasks with todo.txt and Taskwarrior"
task)The heavyweight. Feature-complete (priority, tags, dependencies, recurrence, annotations,
reporting, Lua plugins), fast, beloved. Its storage is a private rc-configured store
(~/.task, historically JSON-lines, now versioned), not a file you choose.
Three facts matter for us: (1) one database per install — no natural many-lists-by-path story;
(2) the store format evolves across versions, so files are not portable plain text;
(3) it has a 25+ year feature-attraction problem — it is the tool that becomes your life,
with reports and plugins and rc rituals. ToolGPA's comparison grades it a 2.8 vs todo.txt's 2.6
out of 4.0, with taskwarrior winning features and todo.txt winning ease of use. That gap is the
whole field in one table.
Sources: taskwarrior.org (man task.1) · toolgpa.com/compare/taskwarrior-vs-todo-txt
Taskwarrior-compatible CLI, storage is one TOML file per task in a directory.
Closer to us (file-based, git-friendly, path-addressable data dirs) but the one-file-per-task
layout scatters state — you can't cat a list — and TOML round-tripping through a
third-party dependency for a five-verb tool is complexity we don't need.
Python, markdown storage in .tick/tasks/ inside a project. Notably the only
serious tool in the field with stable task IDs — a task carries an id so
references survive reordering and deletion. Also tracks agents/claims/done-claims, which is
agent-harness territory, not personal tracking.
Closest in spirit: one ordinary markdown file, with a stable ^ID token,
+project/@context/due:/rec: tokens, and
four surfaces (GNOME app, top-bar extension, CLI with JSON output, self-hosted web app) all
reading and writing the same file. It proves the core proposition of our file-based
design: a single plain-text file can be the shared system of truth for every surface, including
machines (reinschrift-cli … --json). Weakness for us: it's a Linux/GNOME app,
and its token grammar is todo.txt's full dialect — more surface area than we need on macOS.
Source: reinschrift.dumke.me
A small family of tools that treat TODO.md files as - [ ] checkbox lists:
add/toggle/locate, sections, aliases. Useful to confirm two things: (1) the
- [ ] / - [x] checkbox is the natural checkbox for humans — every editor and
every markdown renderer understands it with zero training; (2) the common failure mode of this
family is no stable ID. Toggle-by-text is brittle once two lines share wording;
locate-by-text breaks on edits. That's a gap our design fills with tick's stable-id idea.
Sources: i-am-fran/markdown-do · md2do.com · chimbosonic/cli-kneeboard
grep-able, and trivially parseable. Non-negotiable in our design..checklist file per list, no second store.key:value
extension clause is a footgun we do not inherit.| Option | Verdict |
|---|---|
| Taskwarrior | Overkill in two directions: one private DB (no multi-list-by-path), and a 25-year feature gravity. Wrong shape. |
| todos / todo.sh | Single canonical list (~/todo.txt); ruby dependency; no stable ids. Would require constant --file gymnastics. |
| todoman | Closest file-based fit, but one-TOML-per-task scatters state and ships a dependency for five verbs. Python deps on a 9B-LLM house that also runs ComfyUI are a RAM problem. |
| Reinschrift | Wrong platform focus (GNOME) and a token grammar we don't need. Its architecture validates our design; its code doesn't run here. |
| markdown-do / md2do / kneeboard | Do the 80% (checkbox lists in files) but are missing the one thing that makes lists safe: stable ids. |
| Build a small one | The gap is precisely the combination our need defines: file-per-list and stable ids and fixed grammar. No tool has all three without dragging along the todo.txt dialect. A ~150-line stdlib-only Python tool has the whole feature set we asked for. The owner's standard applies: fewer features = fewer bugs; the build is cheap, the alternatives each cost a dependency or a private store. |
Superseded by owner ruling (2026-10-15). After reviewing this
survey, the owner switched storage from one-file-per-list to one list per user
(the todo.txt family): a single ~/todo.checklist, with zero-to-many tags per item
validated against the house vocabulary file that mem already uses for topics
(now ~/tags.txt, owned by its own tags tool after the decoupling;
mem reads it too) — one shared tag world, one place to extend it (tags new NAME).
No lists scattered across projects; no "should this list be in
Git?" ambiguity. The survey below stands as written; the v1 conclusion's file-per-list
stategy is retired, its build the small tool verdict survives. The plan document
(→ ~/checklist-impl-plan.html, regenerated same day) reflects v2.
checklist — one stdlib-only
executable, file-per-list, five fixed fields, stable monotonic id, atomic writes,
0/1/2 exit-code contract. Spec, reference implementation, tests, and installation plan are
in checklist-impl-plan.html.