scitex_todo — Python API
scitex-todo: a canonical YAML task store with pluggable adapters.
The task store (YAML, top-level tasks: list) is the single source of
truth. Adapters render or import it; the mermaid adapter (YAML -> dependency
PNG) ships today. See the project roadmap for org and Web-UI adapters.
Quick Start
>>> import scitex_todo as todo
>>> tasks = todo.load_tasks("tasks.yaml")
>>> src = todo.build_mermaid(tasks)
>>> todo.render(src, "tasks.png")
'mmdc'
- exception scitex_todo.TaskNotFoundError[source]
Bases:
KeyErrorRaised when an update/complete target id is not in the store.
- exception scitex_todo.TaskValidationError[source]
Bases:
ValueErrorRaised when a task store fails structural validation.
- scitex_todo.add_task(store=None, *, id, title, status='pending', scope=None, assignee=None, priority=None, parent=None, note=None, depends_on=None, blocks=None, repo=None)[source]
Append a new task to
storeand persist viasave_tasks().Returns the inserted task mapping (a fresh dict, not the underlying YAML node) for convenient round-trip use by callers — the CLI prints it, the MCP tools serialize it as the JSON result.
- Raises:
TaskValidationError – On duplicate id or any other structural fault — save_tasks re-runs the full validation gate before touching disk.
- Return type:
- scitex_todo.complete_task(store=None, task_id=None, *, by=None)[source]
Mark
task_idasdoneand stamp_log_meta.completed_{at,by}.Idempotent per
GITIGNORED/QUESTIONS.md#3: re-completing adonetask is a no-op (timestamps stay frozen from the first completion). Passby=to override the$SCITEX_TODO_AGENT→$USER→"unknown"precedence chain.Returns the (post-mutation) task mapping.
- Raises:
TaskNotFoundError – If no task matches
task_id.- Return type:
- scitex_todo.list_tasks(store=None, *, scope=None, assignee=None, status=None)[source]
Snapshot the store, then filter by scope / assignee / status.
Filter semantics: -
scope=None(default): use$SCITEX_TODO_SCOPEif set, elseno filter.
scope=""opts out of the env default explicitly.assignee/status:None= no filter; any string = exact match. (Generic Req 8 — no fuzzy / glob; callers compose.)
The returned list contains fresh dicts, safe to mutate without affecting the on-disk store (no save here).
- scitex_todo.resolve_store(store=None)[source]
Return the resolved task store path and the precedence chain.
Mirrors the data the scitex-todo resolve-store CLI verb and the resolve_store MCP tool emit. Keeping a Python API by the same name as the MCP tool satisfies audit §6 (Convention A: tool_name == api_name).
Output shape:
{ "resolved": "/abs/path/to/tasks.yaml", "explicit": <the `store` arg you passed, or None>, "env_tasks": <value of $SCITEX_TODO_TASKS, or None>, "user_store": "/abs/path/to/~/.scitex/todo/tasks.yaml", "bundled_example": "/abs/path/to/bundled/example.yaml", "pkg_short": "scitex_todo", "exists": bool, }- Return type:
- scitex_todo.summarize_tasks(store=None, *, scope=None, assignee=None)[source]
Return numeric progress counts grouped by status, scope, assignee.
Output shape (always present keys):
{ "store": "/abs/path/to/tasks.yaml", "total": int, "by_status": {<status>: int, ...}, # one key per VALID_STATUSES "by_scope": {<scope|"">: int, ...}, "by_assignee": {<assignee|"">: int, ...}, }
Tasks with no scope / assignee bucket under the empty string
"". Theby_statusmap is densified to allVALID_STATUSESso consumers (web UI, progress widgets) don’t have to special-case zero-count keys.- Return type:
- scitex_todo.update_task(store=None, task_id=None, **fields)[source]
Update fields of the task with id
task_id; return the merged dict.Any keyword argument becomes a field on the task. Passing
Nonefor a field DELETES it (matches the operator’s mental model: “clear the scope” = update_task(…, scope=None)). To leave a field untouched, just omit it.- Raises:
TaskNotFoundError – If no task matches
task_id.TaskValidationError – If the resulting mutation is structurally invalid.
- Return type: