A minimalist, high-density task manager designed for engineers.
OpenTodo operates as a stateful terminal engine and a fully featured Python SDK. It supports both local SQLite sandboxes and cloud-native PostgreSQL databases. With native YAML ingestion, you can manage your tasks exactly how you manage your infrastructure: as code.
Install OpenTodo globally via pip:
pip install opentodoThe otodo CLI is designed for speed and stateful navigation.
You must initialize your database routing before your first command.
Local SQLite:
otodo initCloud PostgreSQL:
otodo init --url "postgresql://user:password@localhost:5433/postgres"Create a list, add tasks, and view your dashboard.
otodo create "v1-release"
otodo add "Draft architecture docs" -p 10
otodo add "Configure CI/CD" -p 5Type otodo to view your high-density table of pending tasks. The engine automatically sorts by priority.
otodootodo add <title> [-p priority]- Add a task.otodo done <id>- Mark a task complete.otodo update <id> [-t title] [-p priority]- Update a task.otodo rm <id>- Delete a task.otodo lists- View all workspaces.otodo use <list_name>- Switch active workspace.
(Tip: Use otodo --help to see all available flags and options).
OpenTodo's core engine can be imported directly into your own Python applications, web servers (like FastAPI), or automation scripts.
Import the client and connect it to your preferred database.
from opentodo.core import OpenTodo
# For local SQLite:
client = OpenTodo(output_dir="/path/to/data")
# For cloud PostgreSQL:
# client = OpenTodo(db_string="postgresql://user:pass@localhost:5433/db")# Create a new list
client.lists.create("backend-sprint")
# Fetch all lists
all_lists = client.lists.get_all()
# Rename a list
client.lists.rename("backend-sprint", "v1-backend")# Add a task to a specific list
client.todos.add(list_name="v1-backend", title="Write SDK tests", priority=8)
# Fetch pending tasks (automatically sorted by priority)
tasks = client.todos.get_by_list("v1-backend", status="pending")
# Complete a task using its true Database ID
task_id = tasks[0]["id"]
client.todos.complete(task_id)The SDK raises domain-specific exceptions for safe error handling.
from opentodo.exceptions import ListNotFoundError, TodoNotFoundError
try:
client.todos.complete(999)
except TodoNotFoundError as e:
print(f"Failed: {e}")Store your standard operating procedures, sprint plans, or onboarding tasks in a YAML file and apply them idempotently via the CLI or the SDK.
Example sprint.yaml:
version: "1.0"
lists:
v1-release:
- title: "Write API documentation"
priority: 8
- title: "Configure Dockerfile"
priority: 5Apply via CLI:
otodo apply sprint.yamlApply via SDK:
results = client.manifest.apply("sprint.yaml")
print(results)Note: The apply command uses an upsert strategy. It will never overwrite existing completed tasks or duplicate existing pending tasks.
By default, the CLI marks tasks with priority >= 8 as HIGH and >= 4 as MED. You can change these visual thresholds instantly:
# Set High to >= 50, and Medium to >= 20
otodo scale 50 20