diff --git a/.claude/scripts/sync.sh b/.claude/scripts/sync.sh index f7e48ad..f8cb213 100755 --- a/.claude/scripts/sync.sh +++ b/.claude/scripts/sync.sh @@ -482,9 +482,11 @@ for group_name in sorted(groups.keys(), key=lambda x: (x == "Personal", x)): if t.get("assigned_to_machine") and t["assigned_to_machine"].upper() != "$TODO_MACHINE".upper(): assigned += f"/{t['assigned_to_machine']}" auto = " [auto]" if t.get("auto_created") else "" - print(f" [ ] {t['text']}{auto}{assigned} (id:{t['id'][:8]})") + due = f" due:{t['due_at'][:16]}" if t.get("due_at") else "" + print(f" [ ] {t['text']}{auto}{due}{assigned} (id:{t['id'][:8]})") for st in subtask_map.get(t["id"], []): - print(f" [ ] {st['text']} (id:{st['id'][:8]})") + st_due = f" due:{st['due_at'][:16]}" if st.get("due_at") else "" + print(f" [ ] {st['text']}{st_due} (id:{st['id'][:8]})") PYEOF echo "" else diff --git a/api/models/coord_todo.py b/api/models/coord_todo.py index a5b0aec..08b4c9a 100644 --- a/api/models/coord_todo.py +++ b/api/models/coord_todo.py @@ -60,6 +60,11 @@ class CoordTodo(Base, UUIDMixin, TimestampMixin): doc="Status: pending / done / cancelled" ) + due_at: Mapped[Optional[datetime]] = mapped_column( + DateTime, + doc="When the item is due" + ) + completed_at: Mapped[Optional[datetime]] = mapped_column( DateTime, doc="When the item was marked done" diff --git a/api/schemas/coord_todo.py b/api/schemas/coord_todo.py index e5220c6..5422507 100644 --- a/api/schemas/coord_todo.py +++ b/api/schemas/coord_todo.py @@ -19,6 +19,7 @@ class CoordTodoCreate(BaseModel): assigned_to_machine: Optional[str] = Field(None, description="Target hostname", max_length=100) auto_created: bool = Field(False, description="True when Claude auto-generated this item") source_context: Optional[str] = Field(None, description="Why Claude auto-created this item") + due_at: Optional[datetime] = Field(None, description="When the item is due") created_by_user: str = Field(..., description="User creating the item", max_length=50) created_by_machine: str = Field(..., description="Hostname where the item is created", max_length=100) @@ -31,6 +32,7 @@ class CoordTodoUpdate(BaseModel): assigned_to_user: Optional[str] = Field(None, max_length=50) assigned_to_machine: Optional[str] = Field(None, max_length=100) project_key: Optional[str] = Field(None, max_length=100) + due_at: Optional[datetime] = Field(None, description="When the item is due") completed_by: Optional[str] = Field(None, description="Session or user completing the item", max_length=100) @@ -45,6 +47,7 @@ class CoordTodoResponse(BaseModel): assigned_to_machine: Optional[str] auto_created: bool source_context: Optional[str] + due_at: Optional[datetime] status: str completed_at: Optional[datetime] completed_by: Optional[str] diff --git a/migrations/versions/20260526_150000_coord_todos_due_at.py b/migrations/versions/20260526_150000_coord_todos_due_at.py new file mode 100644 index 0000000..f0742ec --- /dev/null +++ b/migrations/versions/20260526_150000_coord_todos_due_at.py @@ -0,0 +1,24 @@ +"""coord_todos add due_at column + +Revision ID: 20260526_150000 +Revises: 20260526_120000 +Create Date: 2026-05-26 15:00:00 +""" + +from typing import Sequence, Union + +import sqlalchemy as sa +from alembic import op + +revision: str = "20260526_150000" +down_revision: Union[str, None] = "20260526_120000" +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + op.add_column("coord_todos", sa.Column("due_at", sa.DateTime(), nullable=True)) + + +def downgrade() -> None: + op.drop_column("coord_todos", "due_at")