This Skill handleswritingwork items: creating, breaking down, updating, and commenting. It does not provide deletion; when a work item needs to be deleted, ask the user to do it themselves in the web interface. Never add bypassRules=true: process rules (required fields, allowed states) must still take effect as usual.
Project Selection
-
If the user has provided a project name (for example "Contoso project"), use it directly and do not call the project list API.
-
If not provided, ask the user to provide it once.
-
If it is still not provided, list the projects for the user to choose from:
curl -sS -u ":$AZURE_DEVOPS_PAT" \ "https://dev.azure.com/$AZURE_DEVOPS_ORG/_apis/projects?%24top=500&api-version=7.1" -
Do not continue until the user provides or selects a project.
When updating, breaking down, or commenting, the project is based on the work item's System.TeamProject , and there is no need to ask separately.
Connection and Authentication
Always use curl to call the Azure DevOps REST API directly, and every command carries its own authentication parameters.
- Organization: read the environment variable
AZURE_DEVOPS_ORG(organization name, for examplecontoso). If it is not set, ask the user once; if what the user gives is a URL, extract the organization name fromhttps://dev.azure.com/{org}orhttps://{org}.visualstudio.com. - Token: send the PAT with Basic authentication:
-u ":$AZURE_DEVOPS_PAT"; if onlyAZURE_DEVOPS_EXT_PAT(the variable used by the Azure CLI azure-devops extension) is set, use it instead. When switching to a Microsoft Entra ID token, replace it with-H "Authorization: Bearer $AZURE_DEVOPS_TOKEN". If neither is set, ask the user to setAZURE_DEVOPS_PATin their own terminal and retry, then stop. Read the token only from environment variables: do not ask the user to paste the token into the conversation, and do not print it or write it to a file. - PAT Permission Scopes: reading (work items, types, states, process configuration) requires Work Items (Read) and Project and Team (Read); creating, updating, and commenting additionally require Work Items (Read & write).
- Encoding: project, team, and work item type names in paths must be URL-encoded (write spaces as
%20); in query strings, write$as%24(for example%24expand) to avoid expansion by the shell. - Errors: HTTP 401, 403, 203, or a response that is an HTML login page means the token is invalid, expired, or lacks sufficient permissions; report it and stop. HTTP 404 means the organization, project, work item type, or ID is incorrect. HTTP 400 with
typeKeyasRuleValidationExceptionmeans the field value does not conform to the process rules; show the originalmessageto the user and stop; do not change the value yourself and retry. - Windows PowerShell: call
curl.exe(rather than thecurlalias), and write the environment variable as$env:AZURE_DEVOPS_PAT; PowerShell does not support<<'EOF', so first write the JSON body to a temporary file, then send it with--data-binary "@body.json".
Write Process (Follow for Every Create and Update)
- First use GET to obtain the latest data (work item, type fields, and states); do not guess field names or state names from memory.
- Assemble the JSON Patch body, add to the URL
validateOnly=truedry run. A successful dry run returns HTTP 200, and the response containsfields、_links、url, butnoidandrev, and it will not create or modify anything; unspecified fields are filled with default values (for example,System.Stateshows the initial state for that type). - List for the user what is about to be sent: target (project and type, or work item ID and title), each field's current value → new value, impact (for example, "will create 4 child Tasks"), and attach the
System.State、System.AreaPath、System.IterationPathfrom the dry-run response. Execute only after one confirmation. - Remove
validateOnly=trueand submit for real, then report the result.
Always use --data-binary @- <<'EOF' heredoc for the JSON body; shell variables are not expanded inside the heredoc, so values such as organization names must be written in directly. Backslashes in paths must be written in JSON as \\(Contoso\\Sprint 1)。
Fields, types, and states
| Field | Reference name | Description |
|---|---|---|
| Title | System.Title |
Required |
| Description | System.Description |
HTML, for example <p>重設密碼流程</p> |
| Assigned to | System.AssignedTo |
Email or unique name;"" means unassigned |
| Area | System.AreaPath |
Contoso\Web |
| Iteration | System.IterationPath |
Contoso\Sprint 1(excluding the Iteration level) |
| Tags | System.Tags |
Separated by ; :api; backend. Writing replaces the entire field; to keep existing tags, read them out first and then merge |
| Priority | Microsoft.VSTS.Common.Priority |
1–4, 1 is highest |
| Acceptance criteria | Microsoft.VSTS.Common.AcceptanceCriteria |
HTML; types such as Task do not have this field |
-
Estimate and work fieldsVaries by process; obtain from process settings, do not guess:
curl -sS -u ":$AZURE_DEVOPS_PAT" \ "https://dev.azure.com/$AZURE_DEVOPS_ORG/{project}/_apis/work/processconfiguration?api-version=7.1"typeFields.Effort.referenceNameis the requirement-level estimate field (Scrum isMicrosoft.VSTS.Scheduling.Effort, Agile isMicrosoft.VSTS.Scheduling.StoryPoints, CMMI isMicrosoft.VSTS.Scheduling.Size, custom processes depend on the actual response);typeFields.RemainingWork.referenceNameis the Task's remaining work;typeFields.Activity.referenceNameis the Task's activity category.requirementBacklog.workItemTypes[].nameis the requirement-level type,taskBacklog.workItemTypes[].nameis the Task-level type. -
Type list(
value[].name;isDisabledis thetruethat cannot be created):curl -sS -u ":$AZURE_DEVOPS_PAT" \ "https://dev.azure.com/$AZURE_DEVOPS_ORG/{project}/_apis/wit/workitemtypes?api-version=7.1" -
Type fields(confirm the fields exist;
alwaysRequiredistrueand must be provided when creating that type):curl -sS -u ":$AZURE_DEVOPS_PAT" \ "https://dev.azure.com/$AZURE_DEVOPS_ORG/{project}/_apis/wit/workitemtypes/{type}/fields?api-version=7.1" -
Type states:
curl -sS -u ":$AZURE_DEVOPS_PAT" \ "https://dev.azure.com/$AZURE_DEVOPS_ORG/{project}/_apis/wit/workitemtypes/{type}/states?api-version=7.1"Each entry has
nameandcategory(Proposed、InProgress、Resolved、Completed、Removed). When the user says "change to In Progress" or "change to Done", usecategoryto find the corresponding state name; if the samecategoryhas multiple states (common in custom processes), ask the user to choose. If the target state is not in the list, do not submit.
Rules
1. Create a work item
-
Determine the project and type (type names follow the type list). If the user does not specify Area and Iteration, the API will place it at the project root node, and it may not appear on any team's backlog. To place it in a team:
- Use the team's default area for Area:
GET https://dev.azure.com/$AZURE_DEVOPS_ORG/{project}/{team}/_apis/work/teamsettings/teamfieldvalues?api-version=7.1ofdefaultValue。 - For Iteration, use
.../{team}/_apis/work/teamsettings/iterations?api-version=7.1from the team's Iteration list (path); for the current Sprint, add&%24timeframe=current。
- Use the team's default area for Area:
-
Dry run (the type is written in the path as
%24plus the URL-encoded name, for example%24User%20Story、%24Product%20Backlog%20Item、%24Bug):curl -sS -u ":$AZURE_DEVOPS_PAT" -X POST -H "Content-Type: application/json-patch+json" \ "https://dev.azure.com/$AZURE_DEVOPS_ORG/{project}/_apis/wit/workitems/%24User%20Story?validateOnly=true&api-version=7.1" \ --data-binary @- <<'EOF' [ {"op": "add", "path": "/fields/System.Title", "value": "使用者可以重設密碼"}, {"op": "add", "path": "/fields/System.AreaPath", "value": "Contoso\\Web"}, {"op": "add", "path": "/fields/System.IterationPath", "value": "Contoso\\Sprint 1"}, {"op": "add", "path": "/fields/Microsoft.VSTS.Common.AcceptanceCriteria", "value": "<ul><li>收到重設信後 30 分鐘內有效</li></ul>"}, {"op": "add", "path": "/relations/-", "value": {"rel": "System.LinkTypes.Hierarchy-Reverse", "url": "https://dev.azure.com/contoso/_apis/wit/workItems/101"}} ] EOFThe last line sets the new item as a child of work item 101 (
Hierarchy-Reversepoints to the parent item); omit it if there is no parent item. -
After confirming per the "write flow", remove
validateOnly=trueand formally submit. The response'sidis the new work item ID.
Example: "Create a User Story in the Contoso project: users can reset their password, under Feature 101"
2. Break the work item down into child Tasks
-
Get the parent item (including relations):
curl -sS -u ":$AZURE_DEVOPS_PAT" \ "https://dev.azure.com/$AZURE_DEVOPS_ORG/_apis/wit/workitems/{id}?%24expand=relations&api-version=7.1"Note down the
System.TeamProject、System.WorkItemType、System.Title、System.AreaPath、System.IterationPath。relationsinrelwhereSystem.LinkTypes.Hierarchy-Forwardis an existing child item (the ID is the last segment ofurl); usePOST _apis/wit/workitemsbatchto get the title, to avoid creating duplicate Tasks. -
Get the Task-level type name and the reference name of the
RemainingWork、Activityfield from the process configuration. -
Plan the Task list based on the parent item's description and acceptance criteria (title, optionally remaining work, activity type, assignee). Each Task inherits the parent item's
System.AreaPathandSystem.IterationPath, and adds aHierarchy-Reverserelation pointing to the parent item:curl -sS -u ":$AZURE_DEVOPS_PAT" -X POST -H "Content-Type: application/json-patch+json" \ "https://dev.azure.com/$AZURE_DEVOPS_ORG/{project}/_apis/wit/workitems/%24Task?validateOnly=true&api-version=7.1" \ --data-binary @- <<'EOF' [ {"op": "add", "path": "/fields/System.Title", "value": "實作重設密碼 API"}, {"op": "add", "path": "/fields/System.AreaPath", "value": "Contoso\\Web"}, {"op": "add", "path": "/fields/System.IterationPath", "value": "Contoso\\Sprint 1"}, {"op": "add", "path": "/fields/Microsoft.VSTS.Scheduling.RemainingWork", "value": 6}, {"op": "add", "path": "/relations/-", "value": {"rel": "System.LinkTypes.Hierarchy-Reverse", "url": "https://dev.azure.com/contoso/_apis/wit/workItems/101"}} ] EOF -
Dry run each Task, present the whole list (title, work hours, assignment, inherited Area and Iteration) to the user as table rows, and after a single confirmation create them one by one for real. On a mid-way failure, stop and report the Tasks that were created and those that were not.
Example: "Break PBI 101 down into three Tasks: frontend, backend, testing"
3. Update fields, status, Sprint, or assignee
-
Get the latest version, note down
revand the current values of the fields to modify:curl -sS -u ":$AZURE_DEVOPS_PAT" \ "https://dev.azure.com/$AZURE_DEVOPS_ORG/_apis/wit/workitems/{id}?api-version=7.1" -
When changing status, first validate the target status against the status list of the item's
System.WorkItemType. -
Dry run. The first operation is always
test/rev(value is therevfrom step 1) for optimistic concurrency control:curl -sS -u ":$AZURE_DEVOPS_PAT" -X PATCH -H "Content-Type: application/json-patch+json" \ "https://dev.azure.com/$AZURE_DEVOPS_ORG/{project}/_apis/wit/workitems/{id}?validateOnly=true&api-version=7.1" \ --data-binary @- <<'EOF' [ {"op": "test", "path": "/rev", "value": 7}, {"op": "add", "path": "/fields/System.State", "value": "Committed"}, {"op": "add", "path": "/fields/System.AssignedTo", "value": "alex@contoso.com"}, {"op": "add", "path": "/fields/System.IterationPath", "value": "Contoso\\Sprint 2"} ] EOF -
Per the "write flow", list current value → new value, and after confirmation remove
validateOnly=trueand formally submit. -
If the response is HTTP 412, or the error message indicates that the
testoperation failed, it means someone else changed this item during that period: re-run step 1, list their changes together with your changes for the user, and confirm again with the newrev; do not overwrite directly.
- Change parent item: use
?%24expand=relationsto get the item, find the index N whererelisSystem.LinkTypes.Hierarchy-Reverse, in the same body first{"op": "remove", "path": "/relations/N"}, thenaddthe new parent item relation (an item can have only one parent item).
Example: "Change 101 to In Progress and assign it to Alex", "Move 101 to the next Sprint"
4. Add a comment
Comments have no dry-run mechanism: first list the full comment text and the target work item (ID and title) to the user, and only submit after a single confirmation.
curl -sS -u ":$AZURE_DEVOPS_PAT" -X POST -H "Content-Type: application/json" \
"https://dev.azure.com/$AZURE_DEVOPS_ORG/{project}/_apis/wit/workItems/{id}/comments?format=markdown&api-version=7.1-preview.4" \
-d '{"text":"API 設計已完成,請協助 **review**。"}'
This resource has only a preview version and must include 7.1-preview.4。format to determine the format of text :markdown or html; the documentation lists it as a required parameter, so always specify it explicitly. When the comment contains single quotes or multiple lines, use a heredoc instead. The response's id is the comment ID.
Example: "Comment on 101 saying the API design is complete"
Presenting results
- Dry-run result: Indicate "This is a dry run; nothing has been written yet," and present in a table "Field | Current value | Value after submission"; when creating, write (new item)for "Current value". When breaking down, instead list the Task list: title, remaining work, assignment, area, Iteration.
- Write completed: Each item displays ID (link
https://dev.azure.com/{organization}/{project}/_workitems/edit/{ID}), type, title, status, and the newrev; when breaking down, list all newly created child Tasks in a table, with links to the parent items. - On failure, list the HTTP status code and
message, explaining which items were written and which were not.