Your project's task list inside the pi coding agent: browse it, edit it, and hand a task to the agent. It works over the tracker the project already uses.
Try it in your browser: real pi with the extension, on a scripted model, so no key and nothing leaves the page.
- Installation:
pi install npm:@soleone/pi-tasks - Toggle the Tasks UI with
ctrl + shift + roralt + x, or use/tasks.
- Navigate with
w/s(up / down arrows also work) ato go back (Escand left arrow also work)spaceto change status0to4to change prioritytto change task typeffor keyword search (title, description)
gto toggle between flat and grouped hierarchy modeseto expand or collapse the selected parent in grouped modexto toggle between active and closed tasksnto create a child under the selected taskdto open task detailsEnterto work off a taskTabto insert task details in prompt and close Tasks UIcto create a root taskDeleteto close the selected task
Grouped mode shows root tasks first and reveals indented children on expansion; a search there keeps the path to each match. x switches to closed tasks, most recently closed first (TODO.md keeps file order), and back. Blocked rows list their open blockers, and the preview shows whether each blocker is open, closed or missing.
Tabto switch focus between inputsEnterto savepto choose or clear the parent task when hierarchy is supportedbto toggle blocker tasks when blocked-by dependencies are supported
Relationship pickers exclude the current task. Parent cycles, self-links, and dependency cycles are rejected before persistence; unsupported backend relationships are hidden and rejected by the adapter rather than silently discarded.
By default, project-specific matches take precedence over generic defaults. A matching Tasks category, .tq project, .beads project, or existing TODO.md/todo.md is a project match, so an existing TODO file selects todo-md instead of the installed sq default. If no project-specific match applies, installed sq is the default, followed by Tasks as a category-scoped fallback when its workspace and tasks-cli are available. If none are available, todo-md remains the final fallback. When multiple project matches apply, registry order breaks the tie.
For most setups, sq is recommended as the default backend. It is lightweight, works well in brand new directories, and can create its local data on demand. Install it from the sq installation guide.
- Tasks - Reads and writes a Tasks workspace through
tasks-cli, scoped to the@categorythat matches the project name. Detection runs first, so a matching category claims the project. - sq - Uses the
sqcli to manage tasks in a.siftdirectory via aissues.jsonlfile. No initialization necessary. tq- Uses thetqcli to manage tasks in a.tq/tasks.jsonlfile. Automatically preferred when a.tqdirectory is detected.- beads - Uses the
bdcli to manage tasks into a.beadsdirectory containing multiple files. todo-md- Creates or reads aTODO.mdfile with different sections to emulate priority.
The tasks backend talks to the same command path as the Tasks desktop app, so the list, the app, and any other agent stay in sync without a shared file format to negotiate. Automatic detection needs two things:
- The Tasks workspace exists (
com.soleone.tasks/tasks.dbin the platform data directory, or whereverTASKS_DATABASE_PATHpoints). - The project name matches a
@categorythat at least one non-canceled task uses.
So a task titled Fix the bar @pi-tasks belongs to this repository, and only those tasks are listed. For Git projects, pi-tasks uses the main repository directory, so linked worktrees share the same category; other projects use the current directory. The project name is lowercased and anything outside letters, numbers, hyphens and underscores becomes a hyphen, so My.App reads as @my-app. If no category matches, Tasks stays out of the way during normal detection. If no other project backend applies, it can serve as the fallback using the derived project category, so unrelated workspace tasks are not shown.
Because Tasks derives @category from the title, the adapter keeps the scope token there: created tasks get it appended, renamed tasks keep it, and a title that uses a different category is rejected instead of quietly moving the task out of the project.
The adapter runs tasks-cli --json once per operation and never touches SQLite itself. Task ids are UUIDs, so the list shows an eight-character prefix that the adapter resolves back to the full id.
The CLI is expected on PATH as tasks-cli (never the tasks app binary, which opens a window). Otherwise PI_TASKS_TASKS_COMMAND points at it.
| Situation | Setup |
|---|---|
Installed from .deb / .rpm |
none, /usr/bin/tasks-cli is already on PATH |
| Development build | symlink it under the packaged name (below) or set PI_TASKS_TASKS_COMMAND |
| AppImage | extract it, then set PI_TASKS_TASKS_COMMAND to squashfs-root/usr/bin/tasks-cli |
| CLI from source | pnpm build:backend, then PI_TASKS_TASKS_COMMAND=<checkout>/dist/backend/tasks-cli.js, which runs on the same Node as pi |
A development build names the sidecar after its target triple, so adding src-tauri/binaries to PATH does not put tasks-cli on PATH. Give it the packaged name instead:
host_tuple="$(rustc -vV | sed -n 's/^host: //p')"
ln -s "$SRC/products/tasks/src-tauri/binaries/tasks-cli-$host_tuple" ~/.local/bin/tasks-cliTasks has no task types or due dates, so the type stays task; unsupported task types and due dates are rejected rather than silently discarded. Status and priority map directly:
| pi-tasks | Tasks |
|---|---|
open |
open |
inProgress |
in_progress (task.start) |
deferred |
paused (task.pause) |
closed |
done (task.complete); canceled tasks also read as closed |
p0 - p4 |
priority 0 - 4 |
Versioned mutations carry the task's current expectedVersion, so a concurrent edit from the app surfaces as a stale-version error rather than a silent overwrite. Canceled dependencies stay in place as history; only the blockers the list shows are added or removed.
Parent/child hierarchy and blocked-by dependencies are independent: a child is not automatically blocked by its parent. Any task may be a parent, although the epic type is useful for groups.
| Backend | Hierarchy | Blocked by |
|---|---|---|
tasks |
Native parentId (task.move) |
Native task.dependency.add / task.dependency.remove |
sq / tq |
metadata.pi_tasks.parentRef |
Native blocked_by |
todo-md |
Nested checklist indentation | Not supported |
beads |
Not supported | Read-only when native blocker records are present |
Pi-tasks metadata is always namespaced under pi_tasks; sq/tq hierarchy therefore uses:
{"pi_tasks":{"taskType":"epic","parentRef":"parent-id"}}Nested TODO.md tasks use two spaces per level. Non-checklist nested bullets remain task descriptions:
- [ ] **Parent**
- [ ] **Child**
- Child descriptionFor local UI testing, scripts/seed-hierarchy-demo.sh creates an idempotent sq demo epic with children, a grandchild, resolved and unresolved blockers, and a two-blocker task.
PI_TASKS_TODO_PATH- override the TODO file pathPI_TASKS_BACKEND- to explicitly choose a backend implementation. Currently supported values:taskssqtqbeadstodo-md
Tasks backend, all optional:
PI_TASKS_TASKS_CATEGORY- use this category instead of the one derived from the project name. Setting it activates the backend even before a task uses the category.PI_TASKS_TASKS_COMMAND- thetasks-clicommand or path, for a development build, an extracted AppImage sidecar, ortasks-cli.js. Replaces thePATHlookup; a bare name still resolves throughPATH.PI_TASKS_TASKS_DB- path totasks.db. Defaults toTASKS_DATABASE_PATH, then the platform data directory.PI_TASKS_TASKS_SYNC_ROOT- canonical JSON root used for category detection. Defaults toTASKS_SYNC_ROOT, thensyncbeside the database.PI_TASKS_TASKS_DATA_DIR- directory holdingtasks.db, for a Tasks profile that does not live in the default data directory.
