This Skill only reads: it executes the conditions provided by the user or a saved query, then presents the results using the query's own fields. For fixed-format "My work" reports, use boards-my-workinstead; to modify the work items found, use boards-work-item-write。
Project selection
-
If the user provides 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 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.
Teamis optional: only include the team in the URL when the query uses @CurrentIteration, and the user is not referring to the project's default team ({project}/{team}/_apis/...). If the user does not mention a team, omit it; in that case @CurrentIteration resolves against the project's default team.
Connection and authentication
Always use curl to call the Azure DevOps REST API directly, with authentication parameters included in each command.
- Organization: read the environment variable
AZURE_DEVOPS_ORG(organization name, for examplecontoso). If it is not set, ask the user once; if the user provides 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_PATis set (the variable used by the Azure CLI azure-devops extension), use that instead. When using a Microsoft Entra ID token instead, switch to-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 tokens only from environment variables: do not ask the user to paste tokens into the conversation, and do not print or write them to files. - PAT permission scopes:Work Items (Read)、Project and Team (Read)。
- Encoding: URL-encode the project, team name, and query path in the path (including folder and query names containing Chinese), as well as keywords for query searches (write spaces as
%20); in the query string, write$as%24(for example%24top) to avoid shell expansion. - Errors: HTTP 401, 403, 203, or an HTML login page response means the token is invalid, expired, or lacks permissions; report it and stop. HTTP 404 means the organization, project, team, or query path is incorrect. The
messageof an HTTP 400 will point out the problem with the WIQL (for example, theTF51005field does not exist, or theVS403721team has no current Iteration scheduled); show the original text to the user and help fix it. - Windows PowerShell: call
curl.exe(not 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".
WIQL quick reference
| Requirement | Syntax |
|---|---|
| Current user | [System.AssignedTo] = @Me |
| Project in URL | [System.TeamProject] = @project |
| Relative dates | [System.ChangedDate] >= @Today - 7; also @StartOfWeek、@StartOfMonth |
| Team's current, previous sprint | [System.IterationPath] = @CurrentIteration、@CurrentIteration - 1(the team is determined by the URL) |
| Team's area | [System.AreaPath] IN (@TeamAreas('[Contoso]\\Contoso Team')) |
| Below hierarchy | [System.AreaPath] UNDER 'Contoso\\Web' |
| Requirement level type | [System.WorkItemType] IN GROUP 'Microsoft.RequirementCategory' |
| Text | [System.Title] CONTAINS WORDS 'login'; tag [System.Tags] CONTAINS 'api' |
- Link queryUse
FROM WorkItemLinks, conditions are divided into[Source].、[Target].and[System.Links.LinkType], and finally add the mode:MODE (Recursive): tree (queryTypeistree), can only be combined withSystem.LinkTypes.Hierarchy-Forward。MODE (MustContain): direct link (queryTypeisoneHop), only keeps sources that have targets matching the conditions.
- Strings in WIQL use single quotes, backslashes in JSON are written as
\\; always use heredoc for the body. - Query results are capped at 20,000 items; exceeding it will besilently truncated, with no error returned; WIQL itself is capped at 32,000 characters. When the count is exactly at the limit, remind the user that the results may be incomplete and suggest adding conditions.
Rules
1. Run an ad-hoc WIQL
-
Write the user's requirement as WIQL,
SELECTlist the fields to display, and addORDER BY. Show the WIQL to the user before executing. -
Execute (when there is a team, change the URL to
{project}/{team}/_apis/wit/wiql):curl -sS -u ":$AZURE_DEVOPS_PAT" -X POST -H "Content-Type: application/json" \ "https://dev.azure.com/$AZURE_DEVOPS_ORG/{project}/_apis/wit/wiql?%24top=200&api-version=7.1" \ --data-binary @- <<'EOF' {"query": "SELECT [System.Id], [System.Title], [System.State], [System.AssignedTo] FROM WorkItems WHERE [System.TeamProject] = @project AND [System.WorkItemType] = 'Bug' AND [System.ChangedDate] >= @Today - 7 ORDER BY [System.ChangedDate] DESC"} EOF%24top=200first fetch the first 200 items; remove it when the user wants all of them (still subject to the 20,000 item limit). For link queries,%24topcounts the rows ofworkItemRelations, which cuts off the tree; do not add it unless the result is very large. -
Fill in the data and present it according to "Getting field values" below.
Example: "Query Bugs changed in the last 7 days in the Contoso project", "List the tree of all child items under Feature 101"
Link query example (each Feature and all its child items at every level):
{"query": "SELECT [System.Id], [System.Title], [System.State] FROM WorkItemLinks WHERE ([Source].[System.TeamProject] = @project AND [Source].[System.WorkItemType] = 'Feature') AND ([System.Links.LinkType] = 'System.LinkTypes.Hierarchy-Forward') AND ([Target].[System.TeamProject] = @project) MODE (Recursive)"}
2. Find and run a saved query
-
When the user gives a full path (for example "Shared Queries/Sprint/Active Bugs"), use it directly; each segment of the path must be URL-encoded:
curl -sS -u ":$AZURE_DEVOPS_PAT" \ "https://dev.azure.com/$AZURE_DEVOPS_ORG/{project}/_apis/wit/queries/Shared%20Queries/{folder}/{name}?%24expand=wiql&api-version=7.1"The response has
id、queryType(flat、tree、oneHop) andwiql. The root folders areShared Queries(shared queries) andMy Queries(my queries, only the token owner's own are visible). -
When only a name or keyword is given, search by name:
curl -sS -u ":$AZURE_DEVOPS_PAT" \ "https://dev.azure.com/$AZURE_DEVOPS_ORG/{project}/_apis/wit/queries?%24filter={keyword}&%24expand=minimal&%24top=50&api-version=7.1"When multiple are found, list
pathfor the user to choose. -
To browse the list, get the query tree (
%24depthmax 2;hasChildrenistruebut has nochildrenfolder, then expand with that folder path plus?%24depth=2):curl -sS -u ":$AZURE_DEVOPS_PAT" \ "https://dev.azure.com/$AZURE_DEVOPS_ORG/{project}/_apis/wit/queries?%24depth=2&%24expand=minimal&api-version=7.1"isFolderistrueare folders, the rest are queries. -
Run a query by ID (when the query uses
@CurrentIterationand the user specifies a team, add to the URL{team}):curl -sS -u ":$AZURE_DEVOPS_PAT" \ "https://dev.azure.com/$AZURE_DEVOPS_ORG/{project}/_apis/wit/wiql/{queryId}?api-version=7.1"You can also add
&%24top=200. The response format is the same as rule 1. -
Fill in the data and present it according to "Getting field values" below; in the title, note the query's
path。
Example: "Run the shared query 'Active Bugs'", "List which shared queries the Contoso project has"
Getting field values
WIQL only returns IDs; field values must be fetched separately:
-
Collect IDs:
queryResultTypeisworkItemtakeworkItems[].id; isworkItemLinkwhen takingworkItemRelations[]ofsource.idandtarget.id(remove duplicates). If there are no IDs, state that there are no matching work items, then stop. -
The field uses the response
columns[].referenceName(that is, the queriedSELECTfield), with at most 200 IDs each time; if more, split into batches:curl -sS -u ":$AZURE_DEVOPS_PAT" -X POST -H "Content-Type: application/json" \ "https://dev.azure.com/$AZURE_DEVOPS_ORG/{project}/_apis/wit/workitemsbatch?api-version=7.1" \ -d '{"ids":[101,102],"fields":["System.Id","System.Title","System.State","System.AssignedTo"],"errorPolicy":"Omit"}'Fields with empty values do not appear in
fields, and are displayed as blank. Identity fields (for example,System.AssignedTo) are objects; displaydisplayName。 -
in the order returned by WIQL (that is the result of
ORDER BY); batch responses are sorted by ID and do not preserve the requested order.
Present the results
- First display the executed WIQL or query path, and the count (when there is
%24topor the 20,000 item limit is reached, note that there may be more). - General query: present as a table, with fields and order according to
columns[](usecolumns[].namefor headers). The ID is a clickable link[{ID}](https://dev.azure.com/{organization}/{project}/_workitems/edit/{ID})({project}must be URL-encoded); dates are displayed asYYYY/MM/DD, times are converted to the user's local time zone, and UTC is indicated when it cannot be determined. - Tree query:
sourceisnull; rows that are root nodes, and the subsequent rows whosesource.idpoints to them are child items; output in the order ofworkItemRelations[], indicating the level before the title column with indentation or└─. - Direct link query: one row per source, with matching targets listed indented below, noting the link type (
rel)。