agent-todo-tracker/agent_todos/cli.py

106 lines
3.4 KiB
Python

import json
from pathlib import Path
import click
from .models import Priority, Status, Todo
from .store import Store
@click.group()
def main() -> None:
pass
@main.command()
@click.option("--path", default=".agent_todos.db", help="SQLite DB path.")
def init(path: str) -> None:
Store(db_path=path)
click.echo(f"Initialized TODO DB at {path}")
@main.command()
@click.argument("title")
@click.option("-r", "--repo", default="", help="Repo slug, e.g. org/repo")
@click.option("-p", "--priority", default="medium", type=click.Choice(["high", "medium", "low"]))
@click.option("-a", "--assignee", default="")
@click.option("-t", "--tags", multiple=True)
@click.option("--path", default=".agent_todos.db")
def create(title: str, repo: str, priority: str, assignee: str, tags: tuple[str, ...], path: str) -> None:
todo = Store(db_path=path).create(
_make_todo(title=title, repo=repo, priority=priority, assignee=assignee, tags=list(tags))
)
@main.command(name="list")
@click.option("--repo")
@click.option("--status", type=click.Choice(["open", "in_progress", "blocked", "resolved"]))
@click.option("--json", "as_json", is_flag=True)
@click.option("--path", default=".agent_todos.db")
def list_todos(repo, status, as_json, path): # type: ignore[no-untyped-def]
s = Status(status) if status else None
todos = Store(db_path=path).list(status=s, repo=repo)
if as_json:
click.echo(json.dumps([_serialize(t) for t in todos], indent=2))
else:
for t in todos:
click.echo(f"#{t.id} [{t.status.value}] {t.title} ({t.repo or 'no-repo'}) — p:{t.priority.value}")
@main.command()
@click.argument("todo_id", type=int)
@click.option("-s", "--status", type=click.Choice(["open", "in_progress", "blocked", "resolved"]))
@click.option("-t", "--title")
@click.option("-p", "--priority", type=click.Choice(["high", "medium", "low"]))
@click.option("-a", "--assignee")
@click.option("--path", default=".agent_todos.db")
def update(todo_id: int, status, title, priority, assignee, path: str) -> None:
fields: dict = {}
if status:
fields["status"] = Status(status)
if title:
fields["title"] = title
if priority:
fields["priority"] = Priority(priority)
if assignee is not None:
fields["assignee"] = assignee
todo = Store(db_path=path).update(todo_id, **fields)
if todo:
click.echo(f"Updated #{todo.id}: {todo.title} -> {todo.status.value}")
else:
raise click.ClickException("TODO not found")
@main.command()
@click.argument("todo_id", type=int)
@click.option("--path", default=".agent_todos.db")
def close(todo_id: int, path: str) -> None:
todo = Store(db_path=path).update(todo_id, status=Status.resolved)
if todo:
click.echo(f"Resolved #{todo.id}: {todo.title}")
else:
raise click.ClickException("TODO not found")
def _make_todo(*, title: str, repo: str, priority: str, assignee: str, tags: list[str]) -> Todo:
return Todo(id=None, title=title, repo=repo, priority=Priority(priority), assignee=assignee, tags=tags)
def _serialize(todo: Todo) -> dict:
return {
"id": todo.id,
"title": todo.title,
"repo": todo.repo,
"status": todo.status.value,
"priority": todo.priority.value,
"assignee": todo.assignee,
"tags": todo.tags,
"created_at": todo.created_at,
"updated_at": todo.updated_at,
"resolved_at": todo.resolved_at,
}
if __name__ == "__main__":
main()