Skip to main content

work-items

Work item operations in Plane — create, read, update, delete, search, filter with PQL, assign, label, link, comment, relate, and log time on work items (issues/tasks/tickets). Use when the user wants to create a task, update an issue, add a comment, attach a label, link a PR, set a blocker, log work time, or inspect a specific item. Covers work item types, custom properties, and multi-item batch edits.

Source facts

Repository
Agents-Store/cursor-plugins
Last source activity
November 5, 2026 at 13:40
Detected SKILL.md language
English
Stars
0
Forks
0

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

File Explorer
3 files

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
work-items
description
Work item operations in Plane — create, read, update, delete, search, filter with PQL, assign, label, link, comment, relate, and log time on work items (issues/tasks/tickets). Use when the user wants to create a task, update an issue, add a comment, attach a label, link a PR, set a blocker, log work time, or inspect a specific item. Covers work item types, custom properties, and multi-item batch edits.
# Work Items This skill covers the full life of a work item in Plane: creation, inspection, updates, relations, comments, links, labels, work logs, and custom properties. ## Tool Name Resolution Plane MCP exposes one tool per resource and the operation goes into the `action` parameter: `workitem(action=create, ...)`, `workitem_relation(action=create, ...)`. This skill writes calls in that form. Resolve the real tool names (`mcp__<server>__<resource>`) for your current Plane connection through the `connector-bootstrap` skill - never assume a server prefix. Every tool's own description lists its actions with the required and optional parameters; read it before the first call on a resource. ## Field Names The `workitem` tool has **fixed** parameter names and validates every call against the declared set: an unknown or misspelled parameter is an error, not a silent drop. | Concept | Parameter | |---------|-----------| | Work item id | `workitem_id` | | Human identifier | `workitem_identifier` (`PROJ-42`, for `retrieve_by_identifier`) | | State UUID | `state` | | Label UUIDs | `labels` | | Assignees | `assignees` | | Points | `point` or `estimate_point` | | Parent | `parent` | | Work item type | `type_id` | | Priority | `priority` (`urgent`, `high`, `medium`, `low`, `none`) | | Description | `description_html` (or `description_stripped` for plain text; `description_html` wins if both are given) | To clear a field on update pass `assignees=[]` or `labels=[]`, or `start_date=null` / `target_date=null`; a field you leave out is not changed. If a call is rejected, re-read the tool description rather than guessing another spelling. ## Available Calls | Call | Purpose | |------|---------| | `workitem(action=list)` | List items in a project, or the whole workspace without `project_id`; `pql` filter, `order_by`, `cursor`, `per_page`, `expand`, `fields` | | `workitem(action=list_archived)` | Archived items of a project | | `workitem(action=search)` | Text search (`query`), workspace-wide | | `workitem(action=count)` | Count items, optionally grouped (`group_by`, `sub_group_by`) | | `workitem(action=create)` | Create a new work item | | `workitem(action=retrieve)` | Get an item by UUID | | `workitem(action=retrieve_by_identifier)` | Get an item by human identifier (`workitem_identifier="PROJ-42"`) | | `workitem(action=update)` | Update any field (state, points, priority, assignees, description) | | `workitem(action=manage_assignee)` / `workitem(action=manage_label)` | Add or remove assignees or labels without replacing the list | | `workitem(action=archive)` | Archive (`archive=false` restores); only completed or cancelled items | | `workitem(action=delete)` | Delete an item permanently | | `workitem_comment(action=list\|retrieve\|create\|update\|delete)` | Comments | | `workitem_link(action=list\|retrieve\|create\|update\|delete)` | External links (PRs, docs, designs) | | `workitem_relation(action=list\|create\|delete)` | Blocked-by / blocks / start-finish dependencies, plus custom relation definitions | | `workitem_activity(action=list\|retrieve)` | Audit trail | | `work_log(action=list\|create\|update\|delete)` | Time tracking | | `workitem_type(action=list\|resolve\|create\|...)` | Custom types (bug, story, task, spike, epic) | | `workitem_property(action=list\|create\|set_value\|...)` | Custom fields and their values | | `workitem_attachment(action=list\|upload_from_url\|download_url\|read\|delete)` | Files on a work item | | `label(action=list\|create)` | Labels | | `state(action=list)` | Resolve state UUIDs for filtering and updating | ## Creating a Work Item ``` 1. connector-bootstrap → resolve tool names and instance 2. project(action=list) → pick project_id 3. state(action=list, project_id=<id>) → map target state to UUID 4. label(action=list, project_id=<id>) → map labels to UUIDs (optional) 5. member(action=list_project, project_id=<id>) → resolve assignee UUIDs 6. workitem(action=create, project_id=<id>, name="...", description_html="...", // with acceptance criteria priority="high", // urgent | high | medium | low | none point=3, // Fibonacci 1..8 (see agile-fundamentals) state=<state_uuid>, assignees=[<user_uuid>], labels=[<label_uuid>], type_id=<type_uuid>) // optional custom type, from workitem_type(action=resolve) ``` ### Description Template ```html <h3>Context</h3> <p>Why this matters, links to related work.</p> <h3>Acceptance Criteria</h3> <ul> <li>Given … when … then …</li> <li>Given … when … then …</li> </ul> <h3>Out of Scope</h3> <ul> <li>Explicit exclusions</li> </ul> <h3>Notes</h3> <p>Implementation hints, open questions.</p> ``` ## Updating a Work Item Always resolve IDs first. Typical updates: ``` workitem(action=update, project_id=<id>, workitem_id=<id>, state=<state_uuid>) # move state workitem(action=update, project_id=<id>, workitem_id=<id>, point=5) # estimate workitem(action=update, project_id=<id>, workitem_id=<id>, priority="high") # re-prioritize workitem(action=update, project_id=<id>, workitem_id=<id>, assignees=[<user_uuid>]) # replace assignees workitem(action=update, project_id=<id>, workitem_id=<id>, labels=[<label_uuid>]) # replace labels ``` `update` replaces the whole `assignees` or `labels` list. To add or drop one entry without reading the current list first, use the merge actions (removals apply first, each takes one id or several): ``` workitem(action=manage_assignee, project_id=<id>, workitem_id=<id>, add_user_id=<uuid>) workitem(action=manage_label, project_id=<id>, workitem_id=<id>, remove_label_id=<uuid>) ``` Batch updates: loop over items; do not call in parallel if the tool is not idempotent by default — check the tool description. ## Relations (Blockers, Duplicates, Relates-to) ``` workitem_relation(action=create, project_id=<id>, workitem_id=<id>, workitem_ids=["<other_uuid>"], // related work item UUIDs (array) relation_type="blocking" | "blocked_by" | "start_before" | "start_after" | "finish_before" | "finish_after" ) ``` The related items go in `workitem_ids` (array of UUID strings), and the relation is one of six built-in dependency types. Other relationships — duplicate, relates-to and any workspace-defined custom relation — are *definitions*: call `workitem_relation(action=list_definitions)`, match the user's wording to an entry, and pass `relation_definition_id` plus `relation_definition_label` (the matched outward or inward label, which sets the direction) instead of `relation_type`. To remove a relation use `workitem_relation(action=delete, project_id, workitem_id, related_workitem_id, is_dependency)`; `is_dependency` must match the kind that was created (default false). Before adding an item to a sprint, always check that it has no unresolved `blocked_by` relations — see `agile-fundamentals` Definition of Ready. PQL can also answer it in one query: `blocks("PROJ-12")` lists the items that block PROJ-12 (direction per the `get_pql_reference` wording — confirm on your instance; `workitem_relation(action=list)` is the authoritative answer). ## Comments and Links - **Comments** use `comment_html` (not `description_html`): `workitem_comment(action=create, project_id, workitem_id, comment_html="<p>…</p>")`. `access` is `INTERNAL` or `EXTERNAL`. To mention someone, write `@[<user uuid>]` inline (`comment_html="<p>@[<user uuid>] can you review?</p>"`) and the chip and the notification are generated - a bare `@name` is ordinary text and notifies nobody; the id must belong to a member of the work item's project (`member(action=list_project)` resolves a name to one). - **Links** are external URLs (`http://` or `https://`). `workitem_link(action=create)` and `update` accept both **`url` and `title`**; the title is the text Plane shows in place of the URL (without one, the URL itself is shown). ``` workitem_link(action=create, project_id=<id>, workitem_id=<id>, url="https://github.com/org/repo/pull/420", title="PR #420: fix login redirect" // optional ) ``` ## Attachments ``` workitem_attachment(action=upload_from_url, project_id=<id>, workitem_id=<id>, url="https://example.com/mockup.png", name="Mockup v3") workitem_attachment(action=list, project_id=<id>, workitem_id=<id>) → attachment ids workitem_attachment(action=read, project_id=<id>, workitem_id=<id>, attachment_id=<id>) → images and text inline workitem_attachment(action=download_url, project_id=<id>, workitem_id=<id>, attachment_id=<id>) → a link for anything else ``` `upload_from_url` makes the server fetch the file, so the URL must be reachable without authentication and must not resolve to a private address; there is no way to upload a local file through the tool. `workitem_attachment(action=delete)` is permanent and asks for confirmation. ## Work Logs (Time Tracking) ``` work_log(action=create, project_id=<id>, workitem_id=<id>, description="Debug + fix", duration=150 // integer MINUTES (2h 30m = 150) ) ``` **`duration` is an integer number of minutes**, not an ISO 8601 string like `"PT2H30M"` and not seconds. Convert user input accordingly: "2h 30m" → 150; "PT2H30M" → parse, then convert to minutes. The project must have time tracking enabled, otherwise the call fails. Check with `project(action=retrieve, project_id)` (`is_time_tracking_enabled`) and, with the user's consent, enable it with `project(action=update, project_id, is_time_tracking_enabled=true)`. `work_log(action=list)` needs a `workitem_id`; for project totals use `project(action=worklog_summary, project_id)`. Use to compare estimated story points vs actual time (see `velocity-metrics`). ## Searching and Filtering ### Human identifier lookup ``` workitem(action=retrieve_by_identifier, workitem_identifier="PROJ-42") ``` Pass the full `PROJECT-N` identifier as one string; the server splits it. A malformed identifier is rejected with a message naming the expected form. ### Full-text search ``` workitem(action=search, query="login bug") ``` `search` is **workspace-scoped**: it has no `project_id`, and the result contains matching work items across the whole workspace — filter by project client-side if needed. The parameter is `query`. ### Filtered list with PQL Filtering is done by the server with the `pql` parameter (Plane Query Language) of `workitem(action=list)`, `list_archived` and `count`. Call `get_pql_reference` before composing anything beyond these examples. At most 5 conditions per query; UUID fields (`assignee`, `state`, `label`, `cycle`, `module`, `type`, `milestone`, `project`, `createdBy`) need UUIDs, resolve names first. ``` workitem(action=list, project_id=<id>, pql='assignee = currentUser() AND stateGroup = "started"') workitem(action=list, project_id=<id>, pql='priority IN ("urgent","high") AND stateGroup IN openStates()') workitem(action=list, project_id=<id>, pql='isOverdue()') workitem(action=list, project_id=<id>, pql='title ~ "webhook"') workitem(action=list, project_id=<id>, pql='cycle IN activeCycle() AND stateGroup IN closedStates()') workitem(action=list, pql='id = "PROJ-42"') # no project_id = whole workspace workitem(action=count, project_id=<id>, pql='stateGroup = "started"', group_by=assignees__id) ``` `state__group` is a `group_by` key for `count`, not a PQL field: filter with `stateGroup`. `count` with a `project_id` adds the condition `project = "<id>"` to your `pql`, so that one counts against the 5-condition limit. Pagination: results come back as an envelope with `next_cursor`; pass it as `cursor` for the next page (`per_page` sets the page size). Use `order_by`, `expand` and the sparse fieldset `fields` (for example `fields="id,name,point,estimate_point,state"`; use `project`, not `project_id`) to shrink the payload. ## Custom Types and Properties If the project uses custom work item types (Bug, Story, Spike, Task, Epic): ```
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub