# TaskRest REST API adapter bridging JSON params to TaskManager — includes rapid capture, daily plan, snooze, and recurring task completion. --- ## Table of Contents 1. [Overview](#overview) 2. [Features](#features) 3. [Usage](#usage) 4. [API Reference](#api-reference) 5. [MCP Tools](#mcp-tools) 6. [HTTP Endpoints](#http-endpoints) 7. [Related Documentation](#related-documentation) --- ## Overview `TaskRest` is a pure functional module (no GenServer, no state) that serves as the data transformation layer between external interfaces (MCP tools, REST API, web forms) and `TaskManager`. It accepts string-keyed params from JSON, converts them to atom-keyed maps, and returns results ready for `Jason.encode!/1`. Beyond basic CRUD, `TaskRest` provides higher-level operations: natural language capture, daily planning/briefing, task snoozing, and recurring task completion with auto-spawning. ## Features | Feature | Function | Description | |---------|----------|-------------| | CRUD | `list`, `get`, `create`, `update`, `delete` | Standard task operations | | Complete | `complete` | Mark task done | | Capture | `capture/3` | Natural language → task creation | | Daily Plan | `daily_plan/2` | Morning briefing with focus task | | Snooze | `snooze/4` | Defer task with NL time expression | | Recurring Complete | `complete_recurring/4` | Complete + auto-spawn next instance | | Dashboard | `dashboard/2` | Stats summary with task lists | | Query | `query/4` | Execute any named TaskWeb query | ## Usage ### Rapid Capture ```elixir {:ok, task} = TaskRest.capture("buy groceries tomorrow #home !!", "Asia/Ho_Chi_Minh") # => {:ok, %{"title" => "buy groceries", "priority" => 5, "tags" => ["#home"], ...}} ``` ### Morning Briefing ```elixir plan = TaskRest.daily_plan(:tasks_db, "Asia/Ho_Chi_Minh") # => %{ # focus: %{"title" => "deploy v2.0", "priority" => 10, ...}, # overdue: [...], # due_today: [...], # recurring_today: [...], # becoming_active: [...], # upcoming: [...], # stats: %{open: 8, overdue: 2, completed_yesterday: 3, completed_this_week: 12} # } ``` ### Snooze a Task ```elixir {:ok, task} = TaskRest.snooze(123, "next monday", "Asia/Ho_Chi_Minh") # => {:ok, %{"after_at" => 1771812000, ...}} {:ok, task} = TaskRest.snooze(123, "in 2 hours") # => {:ok, %{"after_at" => , ...}} ``` ### Complete Recurring Task ```elixir {:ok, result} = TaskRest.complete_recurring(456, "Asia/Ho_Chi_Minh", :tasks_db, "felt great") # => {:ok, %{completed: %{...}, next: %{"title" => "meditate", "recur" => "daily", ...}}} ``` ## API Reference ### `list/2` List tasks with optional filters. **Parameters:** - `params` (map, optional) — `"status"` and/or `"tag"` filters - `db` (atom, optional) — database name, defaults to `:tasks_db` **Returns:** list of task maps. ```elixir TaskRest.list() # all open tasks TaskRest.list(%{"status" => "blocked"}) # blocked tasks TaskRest.list(%{"tag" => "#work"}) # tasks tagged #work TaskRest.list(%{"tag" => "#home", "status" => "todo"}) # combined filter ``` ### `get/2` Get a single task by ID, including its subtasks. **Parameters:** - `id` (integer) — task ID - `db` (atom, optional) — defaults to `:tasks_db` **Returns:** `{:ok, task_map_with_subtasks}` or `{:error, reason}` ```elixir {:ok, task} = TaskRest.get(123) # task["subtasks"] => [%{...}, ...] ``` ### `create/2` Create a task from JSON string-keyed params. **Parameters:** - `params` (map) — string-keyed task fields - `db` (atom, optional) — defaults to `:tasks_db` **Returns:** `{:ok, task_map}` or `{:error, reason}` ```elixir {:ok, task} = TaskRest.create(%{ "title" => "Review PR", "priority" => "8", "tags" => "#work", "due_at" => 1771725600 }) ``` ### `update/3` Update a task from JSON string-keyed params. **Parameters:** - `id` (integer) — task ID - `params` (map) — fields to update - `db` (atom, optional) — defaults to `:tasks_db` **Returns:** `{:ok, updated_task_map}` or `{:error, reason}` ```elixir {:ok, task} = TaskRest.update(123, %{"priority" => "10", "status" => "in_progress"}) ``` ### `complete/2` Mark a task as completed. **Parameters:** - `id` (integer) — task ID - `db` (atom, optional) — defaults to `:tasks_db` **Returns:** `{:ok, task_map}` or `{:error, reason}` ```elixir {:ok, task} = TaskRest.complete(123) ``` ### `delete/2` Delete a task by ID. **Parameters:** - `id` (integer) — task ID - `db` (atom, optional) — defaults to `:tasks_db` **Returns:** `{:ok, %{deleted: count}}` or `{:error, reason}` ```elixir {:ok, %{deleted: 1}} = TaskRest.delete(123) ``` ### `capture/3` Rapid task capture from natural language text. Parses with `TaskCapture`, then creates the task. **Parameters:** - `text` (string) — natural language task description - `tz` (string, optional) — IANA timezone, defaults to `"Etc/UTC"` - `db` (atom, optional) — defaults to `:tasks_db` **Returns:** `{:ok, task_map}` or `{:error, reason}` ```elixir {:ok, task} = TaskRest.capture("deploy v2.0 feb 28 #deploy p10", "Asia/Ho_Chi_Minh") ``` ### `daily_plan/2` Assemble a morning briefing / daily plan. **Parameters:** - `db` (atom, optional) — defaults to `:tasks_db` - `tz` (string, optional) — IANA timezone, defaults to `"Etc/UTC"` **Returns:** map with the following sections: | Key | Type | Description | |-----|------|-------------| | `overdue` | list | Tasks past their due date | | `due_today` | list | Tasks due today | | `recurring_today` | list | Recurring tasks due today | | `becoming_active` | list | Deferred tasks whose `after_at` arrives today | | `focus` | map or nil | Single highest-priority actionable task | | `upcoming` | list | Tasks due in the next 3 days | | `stats` | map | `completed_yesterday`, `completed_this_week`, `overdue`, `open` | ```elixir plan = TaskRest.daily_plan(:tasks_db, "Asia/Ho_Chi_Minh") plan.focus["title"] # => "deploy v2.0" plan.stats.open # => 8 ``` ### `snooze/4` Snooze a task by setting its `after_at` to a future time, parsed from natural language. **Parameters:** - `id` (integer) — task ID - `until` (string) — natural language time expression (e.g. `"tomorrow"`, `"in 2 hours"`, `"next monday"`) - `tz` (string, optional) — IANA timezone, defaults to `"Etc/UTC"` - `db` (atom, optional) — defaults to `:tasks_db` **Returns:** `{:ok, task_map}` or `{:error, reason}` ```elixir {:ok, task} = TaskRest.snooze(123, "next monday", "Asia/Ho_Chi_Minh") task["after_at"] # => 1771812000 ``` Uses `TaskCapture.parse_time/2` for NL time parsing. ### `complete_recurring/4` Complete a task and spawn the next recurring instance if applicable. **Parameters:** - `id` (integer) — task ID - `tz` (string, optional) — IANA timezone, defaults to `"Etc/UTC"` - `db` (atom, optional) — defaults to `:tasks_db` - `note` (string or nil, optional) — note to add before completing **Returns:** `{:ok, %{completed: task, next: task | nil}}` or `{:error, reason}` ```elixir {:ok, result} = TaskRest.complete_recurring(456, "Asia/Ho_Chi_Minh", :tasks_db, "done for today") result.completed["status"] # => "completed" result.next["recur"] # => "daily" result.next["due_at"] # => ``` ### `dashboard/2` Returns a dashboard summary with stats and task lists. **Parameters:** - `db` (atom, optional) — defaults to `:tasks_db` - `tz` (string, optional) — IANA timezone, defaults to `"Etc/UTC"` **Returns:** map with `stats`, `due_today`, `overdue`, `blocked`, `completed_today`. ```elixir dash = TaskRest.dashboard() dash.stats.open # => 8 dash.stats.overdue # => 2 ``` ### `query/4` Execute a named query via `TaskWeb.execute_query/4`. **Parameters:** - `name` (string) — query name (e.g. `"due_today"`, `"overdue"`, `"open_tagged"`) - `params` (map, optional) — query-specific params (e.g. `%{"tag" => "#work"}`) - `db` (atom, optional) — defaults to `:tasks_db` - `tz` (string, optional) — IANA timezone, defaults to `"Etc/UTC"` **Returns:** query result (list, map, or scalar). ```elixir tasks = TaskRest.query("due_today") tasks = TaskRest.query("open_tagged", %{"tag" => "#work"}) ``` ### `query_index/0` Returns the full query index with categories and parameter info. **Returns:** map with `categories` and `param_queries`. ```elixir index = TaskRest.query_index() index.categories # => %{"Status" => ["open_tasks", ...], ...} ``` ## MCP Tools Five MCP tools map directly to `TaskRest` functions: | MCP Tool | Permission | Function | Description | |----------|-----------|----------|-------------| | `task_capture` | admin | `capture/3` | NL text → task | | `task_plan` | view | `daily_plan/2` | Morning briefing | | `task_next` | view | `TaskManager.next_actionable/2` | Top priority task | | `task_snooze` | admin | `snooze/4` | Defer with NL time | | `task_done` | admin | `complete_recurring/4` | Complete + spawn next | ## HTTP Endpoints | Method | Path | Handler | |--------|------|---------| | `POST` | `/api/tasks/capture` | `TaskRest.capture/3` | | `GET` | `/api/tasks/plan` | `TaskRest.daily_plan/2` (JSON) | | `GET` | `/tasks/plan` | `TaskWeb.render_plan/2` (HTML) | | `POST` | `/api/tasks/:id/snooze` | `TaskRest.snooze/4` | ## Related Documentation - [TaskCapture](task_capture.md) — natural language parser - [TaskRecurrence](task_recurrence.md) — recurrence engine - [TaskManager](task_manager.md) — database layer, CRUD, queries --- *Source: `lib/task_rest.ex` — Last updated: 2026-02-21*