This Skill approaches PRs from thereviewer'sperspective: find the PRs to look at, understand them, and respond to them. Author-side operations such as creating PRs, publishing drafts, and setting auto-complete use repos-pull-request-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 one.
-
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" -
If the user wants "all projects", repeat rule 1 for each project in the list; if there are more than 20 projects, first tell the user the count and confirm once.
-
Do not continue until the user provides or selects a project.
Repository is optional: when listing PRs, omitting it means the entire project; if the user specifies a repository, use _apis/git/repositories/{repository}/pullrequests。
Connection and Authentication
Always use curl to call the Azure DevOps REST API directly; each 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. - Host: except for
dev.azure.com, the host for the Identities API that queries group members isvssps.dev.azure.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 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: Code (Read), Work Items (Read), Identity (Read); commenting, replying, resolving discussions, and voting additionally require Code (Read & write).
- Encoding: URL-encode project and repository names in paths and file paths in query strings (write spaces as
%20); in query strings write$as%24(for example%24top) to avoid shell expansion. - Errors: HTTP 401, 403, 203, or an HTML login page in the response means the token is invalid, expired, or has insufficient permissions; report it and stop. HTTP 404 means the organization, project, repository, or PR ID is wrong.
- 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".
Current User
Rules 1 and 4 require the current user's identity ID (this endpoint does not carry api-version):
curl -sS -u ":$AZURE_DEVOPS_PAT" "https://dev.azure.com/$AZURE_DEVOPS_ORG/_apis/connectionData"
Use authenticatedUser.id;authenticatedUser.providerDisplayName is the display name.
Rules
1. List PRs awaiting my review or created by me
-
PRs awaiting my review:
curl -sS -u ":$AZURE_DEVOPS_PAT" \ "https://dev.azure.com/$AZURE_DEVOPS_ORG/{project}/_apis/git/pullrequests?searchCriteria.reviewerId={myId}&searchCriteria.status=active&%24top=100&api-version=7.1"reviewerIdonly findsPRs directlyassigned to me. To cover PRs assigned to my team or group:-
Without
reviewerIdlistsearchCriteria.status=activePRs, and pick out reviewers inreviewers[]whoseisContaineristrue. -
Get my direct group memberships (
memberOfis a list of group descriptors):curl -sS -u ":$AZURE_DEVOPS_PAT" \ "https://vssps.dev.azure.com/$AZURE_DEVOPS_ORG/_apis/identities?identityIds={myId}&queryMembership=Direct&api-version=7.1" -
Use
identityIds={groupId1},{groupId2}to look up thedescriptorof these group reviewers in one go; any that appear in mymemberOfcount as "awaiting my review via {group name}".
-
-
PRs I created: replace
reviewerIdwithsearchCriteria.creatorId={myId}。 -
Other filters:
searchCriteria.targetRefName=refs/heads/main、searchCriteria.status=completed|abandoned|all。 -
For paging use
%24topand%24skip; when the number of returned items equals%24top, fetch the next page. -
The list API's
descriptionis truncated to 400 characters; when you need the full description, use rule 2 to get a single PR. -
When there are no PRs, explicitly state that no PRs match the criteria.
Example: "List the PRs awaiting my review in the Contoso project", "Which PRs have I opened in Contoso that haven't been merged yet?"
2. Review a single PR
-
Get the PR (no need to know the repository):
curl -sS -u ":$AZURE_DEVOPS_PAT" \ "https://dev.azure.com/$AZURE_DEVOPS_ORG/{project}/_apis/git/pullrequests/{pullRequestId}?api-version=7.1"Note down
repository.id、repository.project.id(project GUID),repository.webUrl、isDraft、mergeStatus、reviewers[].vote、autoCompleteSetBy。 -
Get iterations, use the last one's
id、sourceRefCommit.commitIdandcommonRefCommit.commitId(merge base):curl -sS -u ":$AZURE_DEVOPS_PAT" \ "https://dev.azure.com/$AZURE_DEVOPS_ORG/{project}/_apis/git/repositories/{repositoryId}/pullRequests/{pullRequestId}/iterations?api-version=7.1" -
Get the changed files of the entire PR relative to the merge base (
%24compareTo=0):curl -sS -u ":$AZURE_DEVOPS_PAT" \ "https://dev.azure.com/$AZURE_DEVOPS_ORG/{project}/_apis/git/repositories/{repositoryId}/pullRequests/{pullRequestId}/iterations/{iterationId}/changes?%24compareTo=0&%24top=2000&api-version=7.1"changeEntries[]hasitem.path、changeType(add, edit, delete, rename) andchangeTrackingId(used for inline comments). If the response hasnextSkip, use%24skip={nextSkip}to fetch the next page. -
Generate the diff: the server does not provide a diff, so fetch the file contents of both versions and compare them locally. When there are more than 20 files, list them first and ask the user to choose or pick the key files based on risk.
curl -sS -u ":$AZURE_DEVOPS_PAT" -o base.txt \ "https://dev.azure.com/$AZURE_DEVOPS_ORG/{project}/_apis/git/repositories/{repositoryId}/items?path={filePath}&versionDescriptor.version={commonRefCommit}&versionDescriptor.versionType=commit&api-version=7.1" curl -sS -u ":$AZURE_DEVOPS_PAT" -o source.txt \ "https://dev.azure.com/$AZURE_DEVOPS_ORG/{project}/_apis/git/repositories/{repositoryId}/items?path={filePath}&versionDescriptor.version={sourceRefCommit}&versionDescriptor.versionType=commit&api-version=7.1" git diff --no-index --unified=3 base.txt source.txtWithout
%24format, plain text content is returned. Added files return 404 on the base (TF401174), deleted files return 404 on the source: treat that side as an empty file. For binary files, just note "binary file changed". Put temporary files in the temp directory and delete them after use. -
Get the threads, keeping only threads whose
comments[0].commentTypeis notsystemand whoseisDeletedis nottrue:curl -sS -u ":$AZURE_DEVOPS_PAT" \ "https://dev.azure.com/$AZURE_DEVOPS_ORG/{project}/_apis/git/repositories/{repositoryId}/pullRequests/{pullRequestId}/threads?api-version=7.1"statusisactiveorpendingmeans unresolved;threadContext.filePathandthreadContext.rightFileStart.lineare the positions of inline comments. -
Get the linked work items (returns
value[].id), then usePOST _apis/wit/workitemsbatchto getSystem.Title、System.State、System.WorkItemType:curl -sS -u ":$AZURE_DEVOPS_PAT" \ "https://dev.azure.com/$AZURE_DEVOPS_ORG/{project}/_apis/git/repositories/{repositoryId}/pullRequests/{pullRequestId}/workitems?api-version=7.1" -
Get the branch policy evaluation results.
artifactIdmust use theproject GUID(therepository.project.idfrom step 1), not the project name:curl -sS -u ":$AZURE_DEVOPS_PAT" \ "https://dev.azure.com/$AZURE_DEVOPS_ORG/{project}/_apis/policy/evaluations?artifactId=vstfs:///CodeReview/CodeReviewId/{projectGuid}/{pullRequestId}&api-version=7.1-preview.1"This resource is only available in the preview version.
configuration.isBlockingThose withtrueand whosestatusis notapprovedornotApplicableare the reasons the PR is blocked;configuration.type.displayNameis the policy name.
Example: "Review PR 1234 in the Contoso project for me", "Why can't PR 1234 be merged yet?"
3. Look up a PR from a commit
curl -sS -u ":$AZURE_DEVOPS_PAT" -X POST -H "Content-Type: application/json" \
"https://dev.azure.com/$AZURE_DEVOPS_ORG/{project}/_apis/git/repositories/{repository}/pullrequestquery?api-version=7.1" \
-d '{"queries":[{"type":"commit","items":["{commitSha}"]}]}'
This is a read-only query (returns HTTP 201 on success).type: commit Find the PRs that contain the commit;type: lastMergeCommit find the PRs merged with that commit.results[0] is the lookup table for { "<sha>": [PR, ...] } . The commit SHA must be the full 40 characters.
Example: "Which PR brought in commit 1a2b3c4?"
4. Comments, replies, resolving discussions, voting (writes)
Before each write action, list the content about to be sent (PR, file and line numbers, full comment text, vote value or status) for the user, and execute only after one confirmation. Present review comments you generate to the user first, and let the user decide whether to post them.
-
Add a general comment:
curl -sS -u ":$AZURE_DEVOPS_PAT" -X POST -H "Content-Type: application/json" \ "https://dev.azure.com/$AZURE_DEVOPS_ORG/{project}/_apis/git/repositories/{repositoryId}/pullRequests/{pullRequestId}/threads?api-version=7.1" \ -d '{"comments":[{"parentCommentId":0,"content":"建議補上單元測試。","commentType":1}],"status":1}' -
Add an inline comment: add location information to the above body.
changeTrackingIdcomes from rule 2, step 3,secondComparingIterationis theid:"threadContext": {"filePath": "/src/app.cs", "rightFileStart": {"line": 42, "offset": 1}, "rightFileEnd": {"line": 42, "offset": 1}}, "pullRequestThreadContext": {"changeTrackingId": 3, "iterationContext": {"firstComparingIteration": 1, "secondComparingIteration": 2}} -
Reply to a thread:
curl -sS -u ":$AZURE_DEVOPS_PAT" -X POST -H "Content-Type: application/json" \ "https://dev.azure.com/$AZURE_DEVOPS_ORG/{project}/_apis/git/repositories/{repositoryId}/pullRequests/{pullRequestId}/threads/{threadId}/comments?api-version=7.1" \ -d '{"parentCommentId":1,"content":"已修正,謝謝。","commentType":1}' -
Change thread status(for example, mark as resolved):
curl -sS -u ":$AZURE_DEVOPS_PAT" -X PATCH -H "Content-Type: application/json" \ "https://dev.azure.com/$AZURE_DEVOPS_ORG/{project}/_apis/git/repositories/{repositoryId}/pullRequests/{pullRequestId}/threads/{threadId}?api-version=7.1" \ -d '{"status":2}'Status values:
1active、2fixed (resolved),3wontFix、4closed、5byDesign、6pending。 -
Vote(
{myId}comes from "current user"; if I am not yet a reviewer, I will be added automatically):curl -sS -u ":$AZURE_DEVOPS_PAT" -X PUT -H "Content-Type: application/json" \ "https://dev.azure.com/$AZURE_DEVOPS_ORG/{project}/_apis/git/repositories/{repositoryId}/pullRequests/{pullRequestId}/reviewers/{myId}?api-version=7.1" \ -d '{"vote":10}'Vote values:
10approved,5approved with suggestions,0no vote (withdrawn),-5waiting for author,-10rejected.
Present the result
Votes are presented with emoji: ✅ approved (10), ☑️ approved with suggestions (5), ⏳ no vote (0), ⏸️ waiting for author (-5), ❌ rejected (-10). The PR link format is {repository.webUrl}/pullrequest/{pullRequestId}; branch names have refs/heads/。
PR listPresent as a table: PR ID (clickable link), title (append 📝 for draft), repository, author, target branch, my vote, creation date. For PRs "through groups waiting for my review", note the group name after the title.
Single PR reviewPresent in order:
- Summary: title, author, source → target branch, status (including draft,
mergeStatuswhether there are conflicts), whether auto-complete is set. - Blocking reasons: list all blocking policies that have not passed; when all pass, clearly state that it can be merged.
- Reviewers: each reviewer and vote, whether they are a required reviewer (
isRequired)。 - Changed files: table listing paths and change types, and the total number of files.
- Change highlights: summarize important changes file by file according to the diff, possible risks (error handling, security, compatibility, missing tests) and concrete suggestions; indicate that these are AI-generated review comments.
- Unresolved threads: author, file and line numbers, content summary; if there are none, state so clearly.
- Linked work items: ID (link
https://dev.azure.com/{organization}/{project}/_workitems/edit/{ID}), type, title, status; if there are none, remind that the PR should link to a work item.