This Skill takes theauthor'sperspective when handling PRs: creating a PR from the current local branch, and modifying an existing PR. It does not provide manual completion or merging of PRs; to merge, set auto-complete, or ask the user to do it in the web UI. Reviews, comments, replies, and votes use repos-pull-request-review。
Repository Determination
In the git working directory, extract the organization, project, and repository from the remote URL, and get the current branch:
git remote get-url origin
git rev-parse --abbrev-ref HEAD
| Remote URL Format | Description |
|---|---|
https://{org}@dev.azure.com/{org}/{project}/_git/{repository} |
HTTPS URL copied from the web UI |
https://dev.azure.com/{org}/{project}/_git/{repository} |
HTTPS URL without a username |
https://{org}.visualstudio.com/[DefaultCollection/]{project}/_git/{repository} |
Legacy domain;DefaultCollection/ optional |
git@ssh.dev.azure.com:v3/{org}/{project}/{repository} |
SSH URL |
-
Names in the URL may be URL-encoded (e.g.,
My%20App): use them as-is when putting them into the API path, and decode them first when displaying to the user. -
When the organization in the remote URL and
AZURE_DEVOPS_ORGdiffer, ask the user once which one to use. -
rev-parseReturnsHEADindicates detached HEAD: ask the user to first switch to the branch for which the PR is to be created, and stop. -
When the current directory is not a git repository, the remote is not Azure DevOps, or the user wants to handle a different repository, ask the user to provide the project and repository names; if still not provided, list the projects and repositories for the user to choose:
curl -sS -u ":$AZURE_DEVOPS_PAT" \ "https://dev.azure.com/$AZURE_DEVOPS_ORG/_apis/projects?%24top=500&api-version=7.1" curl -sS -u ":$AZURE_DEVOPS_PAT" \ "https://dev.azure.com/$AZURE_DEVOPS_ORG/{project}/_apis/git/repositories?api-version=7.1"Display
value[].name;isDisabledastruerepositories marked as disabled. Rule 1 must be executed in that repository's local working directory.
Connection and Authentication
Always use curl to call the Azure DevOps REST API directly, with each command carrying its own authentication parameters.
- Organization: read the environment variable
AZURE_DEVOPS_ORG(organization name, e.g.,contoso). If 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. - Host: apart from
dev.azure.com, the host of the Identities API that looks up reviewer identity by email or name isvssps.dev.azure.com。 - Token: send the PAT with Basic authentication:
-u ":$AZURE_DEVOPS_PAT"; when onlyAZURE_DEVOPS_EXT_PATis set (the variable used by the Azure CLI azure-devops extension), use it 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. Tokens are only read 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: required for reading: Code (Read), Identity (Read) (to look up reviewers), Work Items (Read) (to confirm work items); required for writing: Code (Read & write) (to create and modify PRs).
- Encoding: project and repository names in the path, and branch names and reviewer names in the query string must be URL-encoded (spaces written as
%20, and characters such as Chinese and#must also be encoded); in the query string,$is written as%24(e.g.%24top), to avoid shell expansion. - Error: HTTP 401, 403, 203, or a response that is an HTML login page means the token is invalid, expired, or lacks permission; report it and stop. HTTP 404 means the organization, project, repository, or PR ID is wrong. When creating a PR, HTTP 409 (
TF401179) means there is already an in-progress PR for the same source and target branches; switch to rule 2 to modify it. - Windows PowerShell: Call
curl.exe(not thecurlalias), write environment variables 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
Checking the PR author and setting auto-complete 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. Create a PR from the current branch
-
Confirm the source branch has been pushed:
git status --porcelain git ls-remote --heads origin {branch}- If there are uncommitted changes, remind the user: these changes will not be included in the PR.
ls-remoteno output means the remote does not have this branch: ask the user whether to rungit push -u origin {branch}, and only push after they agree.- The remote already has the branch, but after
git fetch origin {branch},git rev-list --count origin/{branch}..HEADis greater than 0 (there are unpushed commits), likewise ask first, thengit push。
-
Determine the target branch: if the user specified one, use it; otherwise use the repository's
defaultBranch(e.g.refs/heads/main). Also note downidandwebUrl:curl -sS -u ":$AZURE_DEVOPS_PAT" \ "https://dev.azure.com/$AZURE_DEVOPS_ORG/{project}/_apis/git/repositories/{repository}?api-version=7.1"If the source and target branches are the same, stop.
-
Check whether a PR already exists:
curl -sS -u ":$AZURE_DEVOPS_PAT" \ "https://dev.azure.com/$AZURE_DEVOPS_ORG/{project}/_apis/git/repositories/{repository}/pullrequests?searchCriteria.sourceRefName=refs/heads/{branch}&searchCriteria.status=active&api-version=7.1"The same source branch may have multiple in-progress PRs (with different target branches). If there is a PR whose
targetRefNameequals the target branch, show its ID and link, ask whether to modify it instead (rule 2), and do not create a duplicate. -
Draft the title and description:
git fetch origin {targetBranch} git log --oneline --no-merges origin/{targetBranch}..HEAD- If there are no commits, stop: this branch has no changes to merge.
- Only one commit: use its first line as the title, and the rest of its body as the description (
git log -1 --format=%b)。 - Multiple commits: the title summarizes the overall purpose, and the description lists the key changes in bullet points.
- A title or description provided by the user takes priority. The description is limited to 4000 characters; if it exceeds that, condense it.
-
Apply the PR template: the template is only auto-filled when creating a PR on the web; when creating via the API, you must read and fill it in yourself. Templates are always taken fromthe default branch, and file names and folders are case-insensitive:
git fetch origin {defaultBranch} git ls-tree -r --name-only origin/{defaultBranch} \ | grep -i -E '^(\.azuredevops/|\.vsts/|docs/)?pull_request_template(\.md|\.txt|/)' git show "origin/{defaultBranch}:{templatePath}"Search in order in
.azuredevops/、.vsts/、docs/and the repository root, and use the first matching template:- Branch-specific templates:
pull_request_template/branches/under it, the.mdor.txtnamed after the target branch. You can match only the first level (release.mdapplies toreleaseand allrelease/*), or use nested folders to match multiple levels (release/october.mdapplies torelease/octoberandrelease/october/*)。 - Default templates:
pull_request_template.mdorpull_request_template.txt。
pull_request_template/(excludingbranches/); the other files in it areoptional templates, append only when the user requests it. When applying the template, keep its heading and checklist, and fill the change summary from step 4 into the corresponding section. - Branch-specific templates:
-
Link work items: Find work item IDs from the branch name and commit messages, for example
AB#101、#101、feature/101-login. List the candidate IDs for the user to confirm, and link only the confirmed items. You can first look up titles and states to help confirm (IDs that do not exist or that you lack permission for arevalueinnull):curl -sS -u ":$AZURE_DEVOPS_PAT" \ "https://dev.azure.com/$AZURE_DEVOPS_ORG/_apis/wit/workitems?ids=101,102&fields=System.Title,System.State,System.WorkItemType&errorPolicy=omit&api-version=7.1" -
Resolve reviewers: When the user specifies reviewers by email or name, look up the identity IDs:
curl -sS -u ":$AZURE_DEVOPS_PAT" \ "https://vssps.dev.azure.com/$AZURE_DEVOPS_ORG/_apis/identities?searchFilter=General&filterValue={emailOrName}&api-version=7.1"Use
value[].id;providerDisplayNameis the display name,properties.Mail.$valueis the email. Teams or groups are written as[{project}]\{team}(for example[Contoso]\Contoso Team, sent after URL encoding). When multiple matches are found, ask the user to choose; when none are found, report it and do not guess. Set required reviewers toisRequired: true。 -
Confirm and create: List the content about to be sent for the user, and execute only after one confirmation: repository, source → target branch, title, full description, whether it is a draft, reviewers (required/optional), work items, and tags.
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}/pullrequests?api-version=7.1" \ --data-binary @- <<'EOF' { "sourceRefName": "refs/heads/feature/101-login", "targetRefName": "refs/heads/main", "title": "新增登入頁面", "description": "## 變更內容\n- 新增登入表單\n- 補上單元測試", "isDraft": false, "reviewers": [{"id": "{reviewerId}", "isRequired": true}], "workItemRefs": [{"id": "101"}], "labels": [{"name": "frontend"}] } EOFOn success it returns HTTP 201; note the
pullRequestId. Omit the field when there are no reviewers, work items, or tags. When the description contains newlines and quotes, first usejq -n --arg title "..." --arg description "..."to generate JSON, to avoid escaping errors. -
Auto-complete cannot be set at creation time. When the user wants auto-complete, after creation proceed to "Set auto-complete" in rule 2.
Example: "Help me open a PR from the current branch to main", "Open a draft PR, the reviewer is someone@contoso.com」
2. Modify an existing PR
First get the PR (no need to know the repository), and note the repository.id、status、isDraft、createdBy.id、autoCompleteSetBy、completionOptions、targetRefName and reviewers[]:
curl -sS -u ":$AZURE_DEVOPS_PAT" \
"https://dev.azure.com/$AZURE_DEVOPS_ORG/{project}/_apis/git/pullrequests/{pullRequestId}?api-version=7.1"
createdBy.id is not the current user, first remind the user that this is not their PR.status is completed PRs cannot be modified.
Before each modification, list the PR (ID and title), the fields to change, the current values, and the new values for the user, and execute only after one confirmation. Except for adding reviewers, use the same PATCH endpoint and send only the fields to change:
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}?api-version=7.1" \
-d '{"isDraft":false}'
| Action | Body |
|---|---|
| Publish draft | {"isDraft":false} |
| Convert back to draft | {"isDraft":true} |
| Modify title or description | {"title":"...","description":"..."} |
| Change target branch | {"targetRefName":"refs/heads/release"} |
| Cancel auto-complete | {"autoCompleteSetBy":{"id":"00000000-0000-0000-0000-000000000000"}} |
| Abandon | {"status":"abandoned"} |
| Reactivate an abandoned PR | {"status":"active"} |
- The updatable fields listed in the Learn documentation are
status、title、description(limit 4000 characters),completionOptions、mergeOptions、autoCompleteSetBy.idandtargetRefName(the PR retargeting feature must be enabled); other fields may be ignored or return an error.isDraftis not in the list, but the Azure DevOps CLI'saz repos pr update --draftuses this PATCH to sendisDraft. After sending, check the response'sisDraft; when there is no change, report it and ask the user to operate in the web UI. - To cancel auto-complete, use an empty GUID, the same as
az repos pr update --auto-complete false; after sending, get the PR again and confirm thatautoCompleteSetByno longer exists. - Do not provide
"status":"completed"(manual completion), and do not providebypassPolicy(bypass policy).
Set auto-complete: Automatically complete the PR after all required (blocking) policies pass. A draft PR will not complete, so publish it first.
-
Query the merge strategies allowed for the target branch.
type.idisfa4e907d-c16b-4a4c-9dfa-4916e5d171ab(Require a merge strategy) andisEnabledistrue, onlysettingsinallowNoFastForward、allowSquash、allowRebase、allowRebaseMergeistrueis available; without this policy, all are available:curl -sS -u ":$AZURE_DEVOPS_PAT" \ "https://dev.azure.com/$AZURE_DEVOPS_ORG/{project}/_apis/git/policy/configurations?repositoryId={repositoryId}&refName=refs/heads/main&api-version=7.1" -
Confirm the merge strategy, whether to delete the source branch, and whether to transition the work item status with the user, then submit (
{myId}taken from "current user"):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}?api-version=7.1" \ -d '{"autoCompleteSetBy":{"id":"{myId}"},"completionOptions":{"mergeStrategy":"squash","deleteSourceBranch":true,"transitionWorkItems":true}}'mergeStrategy:noFastForward(create a merge commit),squash(squash into a single commit),rebase(fast-forward after rebase),rebaseMerge(create a merge commit after rebase). To customize the merge message, addmergeCommitMessage. On success, the response'sautoCompleteSetBy.idequals{myId}。
Add reviewer: look up the identity ID according to rule 1 step 7; do not submit again for anyone already in reviewers[] , to avoid overwriting existing votes. One call per reviewer:
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/{reviewerId}?api-version=7.1" \
-d '{"vote":0,"isRequired":false}'
For a required reviewer, set isRequired to true. To remove a reviewer, ask the user to do it on the web page.
Examples: "Publish PR 101", "Add someone@contoso.com to PR 101 as a required reviewer", "Set PR 101 to squash auto-complete and delete the branch", "Cancel PR 101's auto-complete", "Abandon PR 101"
Present the result
The PR link format is {repository.webUrl}/pullrequest/{pullRequestId}; remove refs/heads/from branch names; display dates as YYYY/MM/DD。
- Confirmation before creation: list each field to be submitted, display descriptions in full (do not give only a summary), and indicate the template source file and the detected work item.
- After creation: PR ID (clickable link), title (add 📝 for draft), source → target branch, reviewers (mark required ones as "Required"), linked work items (
https://dev.azure.com/{organization}/{project}/_workitems/edit/{ID})。 - When a PR already exists: list the existing PR's ID, title, target branch, and link, and ask whether to modify it instead.
- After modification: the PR link, and each field's "before → after"; for auto-complete, list the merge strategy, whether to delete the source branch, and work item transitions.
- On failure: display the HTTP status code and the
messagein the response; do not automatically retry writes.