Execute the next task from the kanban board. Use when the user wants to make progress on planned work by implementing the next available todo item.
The review workflow requires a column with id review and name Review positioned immediately before the terminal column (conventionally done). Both implement and review must ensure this column exists before moving tasks.
This procedure is idempotent — run it every time; it is a no-op when the column is already in place.
List existing columns:
{"op": "list columns"}
If any column has id: "review", stop — nothing to do.
Otherwise find the terminal column (the one with the highest order — conventionally done). Remember its id as <terminal_id> and its current order as <terminal_order>.
Bump the terminal column out of the way by one position:
{"op": "update column", "id": "<terminal_id>", "order": <terminal_order + 1>}
Insert the review column at the vacated position:
{"op": "add column", "id": "review", "name": "Review", "order": <terminal_order>}
The resulting column order is: ... → doing → review → done (or whatever the terminal column is).
Pick up and execute the next task from the kanban board.
The kanban board is your todo list. Do NOT use any built-in task or todo tools (like TodoWrite or TaskCreate) — always use the kanban tool instead. Every task and work item belongs on the kanban board. This is how work is tracked across both Claude Code and llama-agent sessions, so it must be the single source of truth.
Subtasks are GitHub Flavored Markdown checklists inside the task's description field. There is no separate "add subtask" API — subtasks live in the description as - [ ] / - [x] items. To add subtasks, include them when creating the task or use update task to modify the description.
When the user asks you to track work, create a todo list, or remember tasks — use kanban tasks, not any other mechanism.
kanban with op: "next task" to find the next actionable task. This searches all non-done columns for ready tasks.
op: "next task", filter: "#bug"op: "next task", filter: "@alice"op: "next task", filter: "#bug && @alice"kanban with op: "move task", id: "<task-id>", column: "doing"kanban with op: "get task", id: "<task-id>" to see description and subtaskskanban with op: "update task", id: "<task-id>", and update the description to change - [ ] to - [x] for the completed subtask- [ ] is now - [x]), the task is ready for code review — not directly for done. First ensure the review column exists using the Ensure the Review Column Exists partial above (idempotent — run every time), then move the task there with kanban using op: "move task", id: "<task-id>", column: "review". You MUST do this — never leave a task in "doing" when the work is finished. Do NOT use complete task — that skips the review gate by jumping to the terminal column. After moving to review, stop and tell the user the task is ready for /review.All filtering uses a small expression language with these atoms and operators:
| Syntax | Meaning |
|---|---|
#tag | Match tasks with this tag (includes virtual tags: READY, BLOCKED, BLOCKING) |
$project-slug | Match tasks assigned to this project (by project slug/id) |
@user | Match tasks assigned to this user |
^task-id | Match tasks referencing this task (via depends_on or own id) |
&& / and | Both sides must match |
|| / or | Either side must match |
! / not | Negate the following expression |
() | Grouping |
| Adjacent atoms | Implicit AND: #bug @alice = #bug && @alice |
Use next task with a filter to pick up specific kinds of work one task at a time:
{"op": "next task", "filter": "#bug"}
{"op": "next task", "filter": "@alice"}
{"op": "next task", "filter": "#bug && @alice"}
{"op": "next task", "filter": "$auth-migration"}
{"op": "next task", "filter": "$auth-migration && @alice"}
This is the preferred way to work through tasks — it returns one ready task at a time and excludes done tasks automatically.
Never call list tasks with no parameters — there is no good reason to dump every task. Always use a filter or column, or use next task to get one task at a time:
{"op": "list tasks", "column": "todo"}
{"op": "list tasks", "filter": "#bug"}
{"op": "list tasks", "filter": "#READY"}
{"op": "list tasks", "filter": "#bug && @alice"}
{"op": "list tasks", "filter": "#bug || #feature"}
{"op": "list tasks", "filter": "!#done && #READY"}
{"op": "list tasks", "filter": "$auth-migration"}
{"op": "list tasks", "filter": "$auth-migration && #bug"}
{"op": "list tasks", "filter": "$auth-migration || $frontend"}
Note: list tasks automatically excludes done tasks unless you explicitly request column: "done".
Create tags for the categories that matter to your project:
{"op": "add tag", "id": "bug", "name": "Bug", "color": "ff0000", "description": "Bug fixes"}
{"op": "add tag", "id": "feature", "name": "Feature", "color": "00cc00"}
{"op": "add tag", "id": "chore", "name": "Chore", "color": "888888"}
Each tag needs an id, name, and color (6-char hex, no #). Description is optional.
Tag tasks when you create them or as you learn more about the work:
{"op": "add task", "title": "Fix login crash", "tags": ["bug"]}
{"op": "tag task", "id": "<task-id>", "tag": "feature"}
{"op": "untag task", "id": "<task-id>", "tag": "chore"}
You can list, update, or delete tags as the project evolves:
{"op": "list tags"}
{"op": "update tag", "id": "bug", "name": "Bugfix", "color": "cc0000"}
{"op": "delete tag", "id": "chore"}
Deleting a tag automatically removes it from all tasks.
Projects let you organize related tasks under a shared initiative. Create a project for each plan or workstream.
{"op": "add project", "id": "auth-migration", "name": "Auth Migration"}
{"op": "add project", "id": "frontend", "name": "Frontend", "description": "Frontend redesign", "color": "ff0000", "order": 5}
Required fields: id (slug) and name. Optional fields: description, color (6-char hex, no #), order (position in project list).
Auto-ordering: When order is omitted, projects auto-increment — the first project gets order 0, the next gets 1, and so on. Specify order explicitly to control positioning.
Duplicate detection: Creating a project with an existing id returns an error. Choose unique slugs.
{"op": "get project", "id": "auth-migration"}
Returns {id, name, description, color, order}. Returns a ProjectNotFound error if the ID doesn't exist.
{"op": "update project", "id": "auth-migration", "name": "JWT Auth Migration"}
{"op": "update project", "id": "auth-migration", "description": "New desc", "color": "aabbcc", "order": 42}
All fields except id are optional — only provided fields are changed. Updating with no fields succeeds and returns the current values.
{"op": "list projects"}
Returns {projects: [...], count: N} sorted by order ascending.
{"op": "delete project", "id": "auth-migration"}
Returns {deleted: true, id: "..."} on success. Fails with ProjectHasTasks if any tasks reference the project — reassign or complete those tasks first.
Set the project field when creating or updating a task:
{"op": "add task", "title": "Implement JWT refresh", "project": "auth-migration"}
{"op": "update task", "id": "<task-id>", "project": "frontend"}
Tasks without a project have an empty project field. The task response always includes "project": "<slug>" (or "" if unset).
Once tasks are assigned to projects, use the $project-slug atom in any filter to scope work to a specific project. It composes with other atoms the same way #tag and @user do:
{"op": "next task", "filter": "$auth-migration"}
{"op": "list tasks", "filter": "$auth-migration && #bug"}
{"op": "list tasks", "filter": "$auth-migration || $frontend"}
{"op": "list tasks", "filter": "!$auth-migration"}
The slug after $ is the project id you used in add project. Negation (!$slug) excludes the project, and $a || $b matches tasks in either project.
When starting a plan with multiple related tasks:
project field set to the project ID$project-slug filter atom in list tasks and next task to focus work on a project