"""Task tools: list, log work.""" from __future__ import annotations from fabledassistant.services.notes import get_note_by_title, list_notes from fabledassistant.services.tools._helpers import ( parse_due_date, resolve_project, ) from fabledassistant.services.tools._registry import tool @tool( name="list_tasks", description="List tasks with optional filters. For overdue tasks, set due_before to today's date.", parameters={ "q": {"type": "string", "description": "Optional keyword filter — searches title and body"}, "status": {"type": "array", "items": {"type": "string", "enum": ["todo", "in_progress", "done", "cancelled"]}, "description": "Filter by one or more statuses. Omit for all."}, "priority": {"type": "string", "enum": ["none", "low", "medium", "high"], "description": "Filter by priority level"}, "due_before": {"type": "string", "description": "Return tasks due before this date (YYYY-MM-DD). Use today's date to find overdue tasks."}, "due_after": {"type": "string", "description": "Return tasks due on or after this date (YYYY-MM-DD)"}, "project": {"type": "string", "description": "Filter tasks by project name"}, "milestone": {"type": "string", "description": "Filter tasks by milestone name within a project"}, "limit": {"type": "integer", "description": "Maximum number of tasks to return (default 10)"}, }, read_only=True, briefing=True, ) async def list_tasks_tool(*, user_id, arguments, **_ctx): project_id = None milestone_id = None project_name = arguments.get("project") milestone_name = arguments.get("milestone") if project_name: proj = await resolve_project(user_id, project_name) if proj: project_id = proj.id if milestone_name: from fabledassistant.services.milestones import get_milestone_by_title ms = await get_milestone_by_title(user_id, proj.id, milestone_name) if ms: milestone_id = ms.id elif milestone_name: from fabledassistant.services.milestones import find_milestone_by_title ms = await find_milestone_by_title(user_id, milestone_name) if ms: milestone_id = ms.id notes, total = await list_notes( user_id=user_id, q=arguments.get("q") or None, is_task=True, status=arguments.get("status"), priority=arguments.get("priority"), due_before=parse_due_date(arguments.get("due_before")), due_after=parse_due_date(arguments.get("due_after")), project_id=project_id, milestone_id=milestone_id, exclude_paused_projects=project_id is None, limit=int(arguments.get("limit", 10)), sort="due_date", order="asc", ) results = [] for n in notes: results.append({ "id": n.id, "title": n.title, "status": n.status, "priority": n.priority, "due_date": str(n.due_date) if n.due_date else None, "project_id": n.project_id, "preview": (n.body[:120] + "...") if n.body and len(n.body) > 120 else (n.body or ""), }) return { "success": True, "type": "tasks", "data": {"total": total, "count": len(results), "results": results}, } @tool( name="log_work", description="Add a work log entry to a task to record progress, work done, or time spent. Use this when the user says they worked on, completed, or spent time on a task.", parameters={ "task": {"type": "string", "description": "Title or keyword identifying the task (required)"}, "content": {"type": "string", "description": "Description of the work done (required)"}, "duration_minutes": {"type": "integer", "description": "Optional time spent in minutes"}, }, required=["task", "content"], ) async def log_work_tool(*, user_id, arguments, **_ctx): from fabledassistant.services.task_logs import create_log as _create_log task_query = arguments.get("task", "") content = arguments.get("content", "").strip() if not task_query: return {"success": False, "error": "task is required"} if not content: return {"success": False, "error": "content is required"} duration_minutes = arguments.get("duration_minutes") if duration_minutes is not None: try: duration_minutes = int(duration_minutes) except (TypeError, ValueError): duration_minutes = None note = await get_note_by_title(user_id, task_query) if note is None or note.status is None: notes, _ = await list_notes(user_id=user_id, q=task_query, is_task=True, limit=5) note = notes[0] if notes else None if note is None: return {"success": False, "error": f"No task found matching '{task_query}'."} try: log = await _create_log(user_id, note.id, content, duration_minutes) except ValueError as e: return {"success": False, "error": str(e)} return {"success": True, "log": log.to_dict(), "task": note.title}