- Python 100%
| src/todoquery | ||
| tests | ||
| .gitignore | ||
| .python-version | ||
| AGENTS.md | ||
| CONTEXT.md | ||
| pyproject.toml | ||
| README.md | ||
| sample.txt | ||
| TDD_LOG.md | ||
| uv.lock | ||
todoquery
A small zero-runtime-dependency Python 3.11+ library and CLI for parsing todo.txt-like files into editable JSON, then filtering and sorting the parsed items.
Repository navigation
For future agents or maintainers entering this repo cold:
AGENTS.mdcontains operating instructions, commands, contracts, and change guidelines.CONTEXT.mdcontains static project context, architecture, parser/query behavior, and known limitations.TDD_LOG.mdrecords the original red/green implementation trail.
Install in another uv project
From another project, install this local package:
cd /path/to/your/other/project
uv add /home/raspi/todoquery
For editable/development mode, so changes in /home/raspi/todoquery are reflected immediately:
cd /path/to/your/other/project
uv add --editable /home/raspi/todoquery
Then use it from Python:
from todoquery import TodoQuery
json_text = (
TodoQuery.from_file("todos.txt")
.filter(priority="A")
.sort("due_date")
.to_json(indent=2)
)
print(json_text)
The CLI is also available in the other project after install:
uv run todoquery todos.txt --filter priority=A --sort due_date
Run locally with uv
uv run todoquery path/to/todos.txt
uv run todoquery path/to/todos.txt --filter priority=A --sort due_date
uv run todoquery path/to/todos.txt --filter completed=true --sort completion_date
uv run todoquery path/to/todos.txt --output todos.json
For descending sort fields that begin with -, pass the value with equals syntax so argparse does not treat it as an option:
uv run todoquery path/to/todos.txt --sort=-priority
Python API
from todoquery import TodoQuery, parse_todo_line
todo = parse_todo_line("(A) 2026-06-13 Call Bob @phone +Personal due:2026-06-14 tag:errand")
print(todo.to_dict())
query = TodoQuery.from_text(open("todos.txt").read())
print(query.filter(priority="A").sort("due_date").to_json())
Each todo dictionary has this shape:
{
"raw": "(A) 2026-06-13 Call Bob @phone +Personal due:2026-06-14 tag:errand",
"text": "Call Bob",
"priority": "A",
"created_date": "2026-06-13",
"contexts": ["phone"],
"projects": ["Personal"],
"due_date": "2026-06-14",
"tags": {"tag": "errand"},
"completed": false,
"completion_date": null
}
Completed todo.txt tasks are supported. Lines beginning with x COMPLETION_DATE are marked completed, and an optional following creation date plus original priority tag are extracted:
x 2026-06-13 2026-06-12 pri:A Return library books +Errands @town due:2026-06-13
The example above parses with completed=true, completion_date="2026-06-13", created_date="2026-06-12", and priority="A". The completed-task pri:A token is treated as the original todo.txt priority; on incomplete tasks, pri:A remains a generic tag (tags["pri"] = "A").
Filters
TodoQuery.filter(**criteria) supports:
- Exact fields:
priority="A",due_date="2026-06-14",completed=True - Contains:
contexts__contains="phone",projects__contains="Personal",tags__contains="tag",text__contains="Bob" - Exact tag value:
tags__tag="errand" - ISO string comparisons:
due_date__lt,due_date__lte,due_date__gt,due_date__gte, and the same forcreated_dateandcompletion_date. Missing values do not match comparisons.
Sorting
TodoQuery.sort(*fields) accepts field names, with -field for descending. Multi-field sorts are stable. Missing values sort last in both ascending and descending order.
completion_date can be sorted like any other field, for example .sort("completion_date") or CLI --sort completion_date.