106 lines
3.4 KiB
Python
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()
|