Ado
A small Claude Code marketplace: ado, swift-concurrency, modern-swift, go-dev, worktrees.
npx -y skills add pszypowicz/claude-skills --skill adoAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Azure DevOps operations - PRs, pipelines, policies, builds, variable groups, environments, feeds, branches, work items, comments. This skill should be used when the user asks to "create a PR", "list pipelines", "run a pipeline", "check build status", "debug failed pipeline", "create a policy", "add reviewers", "create a work item", "update a task", "list environments", "approve a pipeline", "manage variable groups", "delete a branch", "comment on a PR", "review PR comments", "comment on work items", "resolve PR thread", "code suggestion", or mentions Azure DevOps, ADO, or az devops CLI. Uses az CLI with PAT auth, falling back to az login tokens.
SKILL.md
33.7 KB, ~9.4k tokens by cl100k_base, as published. Nobody here has run it
Azure DevOps Operations
Use az CLI commands for most operations and bash scripts for complex multi-step orchestration.
Authentication
Credentials come from environment variables exported in the terminal before the session starts; they are inherited read-only. This skill only reads them - it never exports or mutates session credentials. Two shapes are supported:
- Single org (default): the plain triple
ADO_ORG,ADO_PROJECT,AZURE_DEVOPS_EXT_PAT. Commands use these directly. - Named profiles: one namespaced triple per alias
<X>-<X>_ADO_ORG,<X>_ADO_PROJECT,<X>_ADO_PAT(e.g.WORK_ADO_ORG,PERSONAL_ADO_ORG). Any number can be loaded at once; the set of*_ADO_ORGvars is the profile registry.
When no PAT is exported in either shape, an az login session can stand in for it - see "az CLI token fallback" below.
Check what is available:
echo "ORG=${ADO_ORG:-MISSING} PROJECT=${ADO_PROJECT:-MISSING} PAT=${AZURE_DEVOPS_EXT_PAT:+set} TOKEN=${ADO_TOKEN:+set}"
env | grep -o '^[A-Za-z0-9_]*_ADO_ORG' | sort # named profiles, if any
az account show --query user.name -o tsv 2>/dev/null || echo "az: not logged in"
Selecting a profile
With the plain triple (single org), run commands as-is - $ADO_ORG / $ADO_PROJECT / $AZURE_DEVOPS_EXT_PAT are already set.
With named profiles, source the helper and bind one. It resolves an alias in this order: an explicit name, then $ADO_PROFILE, then the working directory (a clone under .../dev.azure.com/<org>/...), then the sole profile; if it cannot decide it lists the profiles and fails - ask the user which to use.
source "${CLAUDE_SKILL_DIR}/scripts/lib/ado-profile.sh"
eval "$(ado_env auto)" # bind the whole block to the auto-detected profile...
eval "$(ado_env WORK)" # ...or to a named one; then use $ADO_ORG etc. as normal
ado_with WORK 'az repos pr list --org "$ADO_ORG" -p "$ADO_PROJECT" --detect false' # ...or run one command
ado_profile # print which alias auto-detect would choose (empty => plain triple)
ado_env / ado_with bind the generic triple only transiently (the current block, or that single command); nothing persists past it, and the wrapper scripts under scripts/ resolve the same way. Pass an ado_with command as a single-quoted string so $ADO_ORG expands after binding.
Working across profiles (source -> destination)
Every loaded profile is available at once, so a migration-style task reads from one and writes to another - address each side explicitly (one block cannot bind two orgs):
source "${CLAUDE_SKILL_DIR}/scripts/lib/ado-profile.sh"
ado_with SRC 'az boards work-item show --id <ID> --org "$ADO_ORG" --detect false -o json' # read from SRC
ado_with DST 'az boards work-item create --title "..." --org "$ADO_ORG" -p "$ADO_PROJECT" --detect false' # write to DST
For raw REST, interpolate the namespaced vars directly: "${SRC_ADO_ORG}/..." with -u ":$SRC_ADO_PAT" for the source, "${DST_ADO_ORG}/..." with -u ":$DST_ADO_PAT" for the destination.
az CLI token fallback (no PAT exported)
Once org and project are known, auth resolves in this order (mirrors scripts/lib/ado-client.sh, which the bundled scripts use automatically):
AZURE_DEVOPS_EXT_PAT- PAT. TheazCLI picks it up automatically; curl uses Basic auth.ADO_TOKEN- pre-minted Entra access token; curl uses Bearer auth.- az CLI login - no credential env vars needed, requires a prior
az login. Theaz devops/az repos/az pipelines/az boardscommands authenticate with the signed-in account on their own; onlyADO_ORGandADO_PROJECTare still required. For curl, mint a token for the Azure DevOps resource (499b84ac-1321-427f-aa17-267ca6975798- fixed public GUID, same for every tenant).
az-minted tokens expire after about an hour. On an unexpected 401 (or an HTML sign-in page in a response body) mid-session, re-mint the token and rebuild AUTH before suspecting a permissions problem. The Bearer token also works against the vssps.dev.azure.com and feeds.dev.azure.com hosts.
If ADO_ORG or ADO_PROJECT is missing, derive them from the repo remote when possible (git remote get-url origin; HTTPS remotes look like https://dev.azure.com/<org>/<project>/_git/<repo>, SSH like [email protected]:v3/<org>/<project>/<repo>) and set them for the commands you run.
REST auth header
Set AUTH once from whichever source is available - the curl snippets in this skill pass -H "$AUTH" and work with either form:
if [[ -n "${AZURE_DEVOPS_EXT_PAT:-}" ]]; then
AUTH="Authorization: Basic $(printf ':%s' "$AZURE_DEVOPS_EXT_PAT" | base64 | tr -d '\n')"
else
AUTH="Authorization: Bearer ${ADO_TOKEN:-$(az account get-access-token --resource 499b84ac-1321-427f-aa17-267ca6975798 --query accessToken -o tsv)}"
fi
Stop and instruct the user only when no auth source works (no PAT in either shape, no ADO_TOKEN, and az account show fails):
Either run
az loginin your terminal, or set the following (outside Claude Code) and start a new session:export ADO_ORG=https://dev.azure.com/<org> export ADO_PROJECT=<project> export AZURE_DEVOPS_EXT_PAT=<pat>
Prefer the az CLI; fall back to curl -H "$AUTH" for REST endpoints that az doesn't cover (az rest is ARM-only and does not work against ADO APIs).
All az commands use --org "$ADO_ORG" -p "$ADO_PROJECT" --detect false.
Exception: These subcommands are org-scoped - they take --org but not --project:
az boards work-item show,update,relation addaz repos pr show,update,set-voteaz repos pr work-item addaz repos pr reviewer addaz repos pr policy listaz repos pr policy queue
Only az boards work-item create and az boards query accept --project.
PR Operations
# List PRs
az repos pr list -r <repo> --status active --top N --org "$ADO_ORG" -p "$ADO_PROJECT" --detect false
# Show PR details (org-scoped - no --project)
az repos pr show --id <ID> --org "$ADO_ORG" --detect false
# Show PR policy evaluations
az repos pr policy list --id <ID> --org "$ADO_ORG" --detect false
# Create PR
az repos pr create -r <repo> -s <branch> --title "text" \
--auto-complete true --squash true --delete-source-branch true \
--required-reviewers [email protected] [email protected] \
--org "$ADO_ORG" -p "$ADO_PROJECT" --detect false
# Update PR
az repos pr update --id <ID> --title "new" --description "new" \
--auto-complete true --squash true \
--org "$ADO_ORG" --detect false
# Complete/merge PR
az repos pr update --id <ID> --status completed --squash true \
--delete-source-branch true --bypass-policy true \
--org "$ADO_ORG" --detect false
# Abandon PR
az repos pr update --id <ID> --status abandoned --org "$ADO_ORG" --detect false
# Vote on PR
az repos pr set-vote --id <ID> --vote approve --org "$ADO_ORG" --detect false
# Add reviewers (optional or required)
# NOTE: az repos pr reviewer add fails under PAT auth with "A valid reviewer must
# be supplied." regardless of input format (email, entitlement GUID, identity GUID).
# Use REST PUT with an identity GUID resolved from vssps - see "Reviewer identity
# resolution" below.
az repos pr reviewer add --id <ID> --reviewers <GUID1> <GUID2> --required true \
--org "$ADO_ORG" --detect false
# Link work item to PR
az repos pr work-item add --id <PR_ID> --work-items <WI_ID> \
--org "$ADO_ORG" --detect false
# Requeue failed BVP
az repos pr policy list --id <ID> -o json --org "$ADO_ORG" --detect false # find failed evaluation IDs
az repos pr policy queue --id <ID> -e <eval-id> --org "$ADO_ORG" --detect false
Reviewer identity resolution
Adding PR reviewers under PAT auth requires the vssps identity id, not the entitlement id returned by az devops user list. They are different GUIDs for the same user, and only the vssps one works. az repos pr reviewer add fails for both, so use REST:
# AUTH as set in the Authentication section (Basic PAT or az Bearer token)
VSSPS="${ADO_ORG/dev.azure.com/vssps.dev.azure.com}"
REPO_ID=$(az repos show -r <repo> --org "$ADO_ORG" -p "$ADO_PROJECT" --detect false --query id -o tsv)
# 1. Resolve identity id from email
REVIEWER_ID=$(curl -sS -H "$AUTH" \
"${VSSPS}/_apis/identities?searchFilter=General&filterValue=<email>&api-version=7.1" \
| python3 -c "import sys,json; print(json.load(sys.stdin)['value'][0]['id'])")
# 2. Add as required reviewer
curl -sS -X PUT -H "$AUTH" -H "Content-Type: application/json" \
"${ADO_ORG}/_apis/git/repositories/${REPO_ID}/pullRequests/<PR_ID>/reviewers/${REVIEWER_ID}?api-version=7.1" \
-d "{\"vote\":0,\"isRequired\":true,\"id\":\"${REVIEWER_ID}\"}"
If the PUT response body comes back empty (no displayName, no error), the id was wrong - re-resolve via vssps instead of retrying with the same value. az devops user list ids silently fail this way.
PR Comments & Threads
All PR comment operations require curl with $AUTH (az rest does not work with ADO APIs).
# AUTH as set in the Authentication section (Basic PAT or az Bearer token)
THREADS="${ADO_ORG}/${ADO_PROJECT}/_apis/git/repositories/<repo>/pullRequests/<prId>/threads"
# List active threads (script - handles filtering + formatting)
${CLAUDE_SKILL_DIR}/scripts/ado-pr-threads.sh --pr-id <ID> --repo <repo> --status active
# List threads on a specific file
${CLAUDE_SKILL_DIR}/scripts/ado-pr-threads.sh --pr-id <ID> --repo <repo> --file "/src/main.tf"
# Create general comment
curl -s -H "$AUTH" -H "Content-Type: application/json" -X POST \
"${THREADS}?api-version=7.1" \
-d '{"comments":[{"content":"LGTM, one question about error handling.","commentType":1}],"status":"active"}'
# Create file-level comment
curl -s -H "$AUTH" -H "Content-Type: application/json" -X POST \
"${THREADS}?api-version=7.1" \
-d '{"comments":[{"content":"Should use a data source here.","commentType":1}],"status":"active","threadContext":{"filePath":"/src/main.tf","rightFileStart":{"line":42,"offset":1},"rightFileEnd":{"line":42,"offset":1}}}'
# Code suggestion (uses ```suggestion block - ADO renders as applicable diff)
curl -s -H "$AUTH" -H "Content-Type: application/json" -X POST \
"${THREADS}?api-version=7.1" \
-d '{"comments":[{"content":"Consider:\n\n```suggestion\ndata \"azurerm_resource_group\" \"example\" {\n name = var.rg_name\n}\n```","commentType":1}],"status":"active","threadContext":{"filePath":"/src/main.tf","rightFileStart":{"line":42,"offset":1},"rightFileEnd":{"line":44,"offset":1}}}'
# Reply to thread
curl -s -H "$AUTH" -H "Content-Type: application/json" -X POST \
"${THREADS}/<threadId>/comments?api-version=7.1" \
-d '{"parentCommentId":1,"content":"Fixed in latest push.","commentType":1}'
# Update a comment
curl -s -H "$AUTH" -H "Content-Type: application/json" -X PATCH \
"${THREADS}/<threadId>/comments/<commentId>?api-version=7.1" \
-d '{"content":"Updated response."}'
# Delete a comment (soft-delete, returns HTTP 200)
curl -s -H "$AUTH" -H "Content-Type: application/json" -X DELETE \
"${THREADS}/<threadId>/comments/<commentId>?api-version=7.1"
# Resolve thread
curl -s -H "$AUTH" -H "Content-Type: application/json" -X PATCH \
"${THREADS}/<threadId>?api-version=7.1" \
-d '{"status":"fixed"}'
# Status values: active, fixed, wontFix, closed, byDesign, pending
# Reactivate thread
curl -s -H "$AUTH" -H "Content-Type: application/json" -X PATCH \
"${THREADS}/<threadId>?api-version=7.1" \
-d '{"status":"active"}'
Pipeline Operations
az pipelines list --name <filter> --org "$ADO_ORG" -p "$ADO_PROJECT" --detect false
az pipelines runs list --pipeline-ids <id> --top N --org "$ADO_ORG" -p "$ADO_PROJECT" --detect false
az pipelines run --id <id> --branch <branch> --parameters key=value --org "$ADO_ORG" -p "$ADO_PROJECT" --detect false
az pipelines build cancel --build-id <id> --org "$ADO_ORG" -p "$ADO_PROJECT" --detect false
az pipelines create --name <name> --repository <repo> --yml-path <path> --org "$ADO_ORG" -p "$ADO_PROJECT" --detect false
az pipelines update --id <id> --name <name> --org "$ADO_ORG" -p "$ADO_PROJECT" --detect false
az pipelines delete --id <id> --org "$ADO_ORG" -p "$ADO_PROJECT" --detect false
Run specific stages only (skip others)
az pipelines run does not expose stagesToSkip. Use the /pipelines/{id}/runs REST endpoint directly. Stages listed in stagesToSkip end skipped, and downstream stages that depend on them run as if the upstream succeeded - so this is the correct way to rerun e.g. only a cleanup/destroy stage after a cancelled apply, without editing the pipeline YAML or adding approval gates.
# Identifier is the YAML `- stage: <id>`, not the displayName. Grab it from a
# prior run's timeline: `records[].type == 'Stage'` -> `.identifier`.
curl -sS -H "$AUTH" -X POST \
"$ADO_ORG/$ADO_PROJECT/_apis/pipelines/<pipelineId>/runs?api-version=7.1-preview.1" \
-H "Content-Type: application/json" \
-d '{
"resources": { "repositories": { "self": { "refName": "refs/heads/<branch>" } } },
"stagesToSkip": ["RepoValidate", "TerraformValidate", "TerraformPlan", "TerraformApply"]
}'
Common use case: previous BVP run was cancelled mid-apply, leaving an orphan resource group in state. Skip every upstream stage and let only TerraformDestroy run - it reads the existing state backend and tears down what the cancelled apply created.
Contrast with cancellation cascade (which is not overridden by stagesToSkip on a running build): when a running build is cancelled, the upstream ends Canceled and downstream defaults to Skipped via the implicit succeeded() condition. No rerun button appears on the skipped stage. The fix there is a fresh queue with stagesToSkip as above, not re-running within the cancelled build.
Note: the older /build/builds?api-version=7.1 endpoint silently ignores stagesToSkip. Use /pipelines/{id}/runs?api-version=7.1-preview.1.
Policy Operations
# Step 1: Get repository ID
REPO_ID=$(az repos show -r <repo> --org "$ADO_ORG" -p "$ADO_PROJECT" --detect false --query id -o tsv)
# Step 2: List policies filtered by repository
az repos policy list --repository-id "$REPO_ID" --branch <branch> --org "$ADO_ORG" -p "$ADO_PROJECT" --detect false
az repos policy update --id <id> --blocking true --enabled true --org "$ADO_ORG" -p "$ADO_PROJECT" --detect false
az repos policy delete --id <id> --org "$ADO_ORG" -p "$ADO_PROJECT" --detect false
Create policy (complex - use script for identity resolution):
${CLAUDE_SKILL_DIR}/scripts/ado-create-policy.sh --repo <name> --type build --pipeline-id <id> [--branch main] [--display-name TEXT] [--blocking]
${CLAUDE_SKILL_DIR}/scripts/ado-create-policy.sh --repo <name> --type approver [--min-approvers N] [--creator-vote] [--reset-on-push] [--branch main] [--blocking]
${CLAUDE_SKILL_DIR}/scripts/ado-create-policy.sh --repo <name> --type required-reviewer --reviewer <email|name|team|GUID> [--reviewer ...] [--filename-patterns "/*.tf,/pipelines/*"] [--branch main] [--blocking]
Variable Groups
az pipelines variable-group list --group-name <filter> --org "$ADO_ORG" -p "$ADO_PROJECT" --detect false
az pipelines variable-group show --id <id> --org "$ADO_ORG" -p "$ADO_PROJECT" --detect false
az pipelines variable-group variable create --group-id <id> --name KEY --value VALUE --org "$ADO_ORG" -p "$ADO_PROJECT" --detect false
az pipelines variable-group variable update --group-id <id> --name KEY --new-value VALUE --org "$ADO_ORG" -p "$ADO_PROJECT" --detect false
az pipelines variable-group variable delete --group-id <id> --name KEY --org "$ADO_ORG" -p "$ADO_PROJECT" --detect false
Work Items
Custom Fields
Project-specific custom fields and work item types should be defined in your CLAUDE.md. See examples/project-claude-ado.md for a template.
Read Operations
# Show work item (all fields + relations)
az boards work-item show --id <ID> --expand all --org "$ADO_ORG" --detect false -o json
# Show specific fields only (NOTE: --expand and -f are mutually exclusive)
az boards work-item show --id <ID> -f "System.Title,System.State,System.AssignedTo,<custom-field-1>,<custom-field-2>" --org "$ADO_ORG" --detect false -o json
# Query work items (WIQL) - this command accepts --project
az boards query --wiql "SELECT [System.Id], [System.Title], [System.State] FROM workitems WHERE [System.AreaPath] UNDER '<AreaPath>' AND [System.State] = 'Active' AND [System.WorkItemType] = '<TaskType>'" --org "$ADO_ORG" -p "$ADO_PROJECT" --detect false
# List child work items of a parent
az boards query --wiql "SELECT [System.Id], [System.Title], [System.State], [System.AssignedTo] FROM workitems WHERE [System.Parent] = <PARENT_ID>" --org "$ADO_ORG" -p "$ADO_PROJECT" --detect false
# Show work item with relations (to find parent/child links)
az boards work-item show --id <ID> --expand relations --org "$ADO_ORG" --detect false -o json
# Parse parent: jq '.relations[] | select(.attributes.name == "Parent") | .url' - extract ID from URL tail
Create & Update
# Create task under a project
az boards work-item create --type "<TaskType>" --title "Implement feature X" \
--description "What needs to be done and why" \
--assigned-to "[email protected]" \
--area "<AreaPath>" \
--iteration "<IterationPath>" \
-f "<custom-field-1>[email protected]" "<custom-field-2>[email protected]" \
--org "$ADO_ORG" -p "$ADO_PROJECT" --detect false
# Update work item (state, assignment, custom fields)
az boards work-item update --id <ID> --state Active --assigned-to "[email protected]" \
--org "$ADO_ORG" --detect false
# Set custom fields on existing work item
az boards work-item update --id <ID> \
-f "<custom-field-1>[email protected]" "<custom-field-2>[email protected]" \
--org "$ADO_ORG" --detect false
# Quick append discussion comment (az CLI shorthand - no comment ID returned, no @mention support)
az boards work-item update --id <ID> --discussion "Started work on this" \
--org "$ADO_ORG" --detect false
Comments
Full CRUD on work item comments requires REST API (curl with $AUTH). The --discussion flag above is append-only and does not support @mentions.
@Mentions in comments: Use the REST API with HTML mention format. The --discussion flag renders mentions as plain text.
# AUTH as set in the Authentication section (Basic PAT or az Bearer token)
WI_COMMENTS="${ADO_ORG}/${ADO_PROJECT}/_apis/wit/workItems/<id>/comments"
# Post comment with @mention (use identity GUID and email)
curl -s -H "$AUTH" -H "Content-Type: application/json" -X POST \
"${WI_COMMENTS}?api-version=7.1-preview.4" \
-d '{"text":"<a href=\"mailto:[email protected]\" data-vss-mention=\"version:2.0,<IDENTITY_GUID>\">@Display Name</a> please review."}'
# AUTH as set in the Authentication section (Basic PAT or az Bearer token)
WI_COMMENTS="${ADO_ORG}/${ADO_PROJECT}/_apis/wit/workItems/<id>/comments"
# List comments (most recent first)
curl -s -H "$AUTH" -H "Content-Type: application/json" \
"${WI_COMMENTS}?\$top=10&order=desc&api-version=7.1-preview.4"
# Add comment (returns comment with id for later update/delete)
curl -s -H "$AUTH" -H "Content-Type: application/json" -X POST \
"${WI_COMMENTS}?api-version=7.0-preview.3" \
-d '{"text":"Started investigation. Root cause is missing RBAC assignment."}'
# Update comment
curl -s -H "$AUTH" -H "Content-Type: application/json" -X PATCH \
"${WI_COMMENTS}/<commentId>?api-version=7.1-preview.4" \
-d '{"text":"Updated: fix is in PR #142."}'
# Delete comment (soft-delete, returns HTTP 204)
curl -s -H "$AUTH" -H "Content-Type: application/json" -X DELETE \
"${WI_COMMENTS}/<commentId>?api-version=7.1-preview.4"
Relations
# Add parent relation (make task child of user story)
az boards work-item relation add --id <CHILD_ID> --relation-type parent --target-id <PARENT_ID> \
--org "$ADO_ORG" --detect false
# Add child relation
az boards work-item relation add --id <PARENT_ID> --relation-type child --target-id <CHILD_ID> \
--org "$ADO_ORG" --detect false
# Link work item to PR (from PR side - preferred)
az repos pr work-item add --id <PR_ID> --work-items <WI_ID> \
--org "$ADO_ORG" --detect false
# List relation types available
az boards work-item relation list-type --org "$ADO_ORG" --detect false
Teams
# List teams in project
az devops team list --org "$ADO_ORG" -p "$ADO_PROJECT" --detect false
# List team members
az devops team list-member --team "TeamName" --org "$ADO_ORG" -p "$ADO_PROJECT" --detect false
Scripts
# PR threads (list, filter by status/file)
${CLAUDE_SKILL_DIR}/scripts/ado-pr-threads.sh --pr-id <id> --repo <repo> --status active
${CLAUDE_SKILL_DIR}/scripts/ado-pr-threads.sh --pr-id <id> --repo <repo> --file "/src/main.tf" --json
# Run details with stage/job tree
${CLAUDE_SKILL_DIR}/scripts/ado-get-run.sh --run-id <id>
# Task logs
${CLAUDE_SKILL_DIR}/scripts/ado-get-logs.sh --run-id <id> --failed-only
${CLAUDE_SKILL_DIR}/scripts/ado-get-logs.sh --run-id <id> --task "Plan" --tail 100
Branches, Tags & Feeds
az repos ref delete --name heads/<branch> -r <repo> --org "$ADO_ORG" -p "$ADO_PROJECT" --detect false
az artifacts feed list --org "$ADO_ORG" --detect false
az artifacts feed create --name <name> --org "$ADO_ORG" --detect false
az artifacts feed delete --feed <name> --org "$ADO_ORG" --detect false
Tags
Prefer annotated tags (git tag -a): ADO's Tags view shows the tagger, date, and message only for annotated tag objects - a lightweight tag renders with an empty creator and no message. Fetch the remote first so the tag lands on the current commit and existing tags are visible.
git fetch origin --tags
git tag -a v1.2.3 <commit> -m "<repo> v1.2.3: what this release ships"
git push origin v1.2.3
Environments, Checks & Approvals (REST API)
These operations have no az CLI support. Use curl with the REST API. See the "Manage Environment Checks & Approvals" workflow recipe below, and reference.md for advanced patterns (branch control checks, deployment records).
Workflow Recipes
PR Lifecycle
# 1. Create PR with auto-complete
az repos pr create -r my-repo -s feature/foo --title "Add feature" \
--auto-complete true --squash true --delete-source-branch true \
--org "$ADO_ORG" -p "$ADO_PROJECT" --detect false
# 2. Check status and policy evaluations
az repos pr show --id 42 --org "$ADO_ORG" -p "$ADO_PROJECT" --detect false
az repos pr policy list --id 42 --org "$ADO_ORG" --detect false
# 3. Requeue failed BVP if needed
az repos pr policy list --id 42 -o json --org "$ADO_ORG" --detect false # find eval ID
az repos pr policy queue --id 42 -e <eval-id> --org "$ADO_ORG" --detect false
# 4. Force-complete if stuck
az repos pr update --id 42 --status completed --squash true \
--delete-source-branch true --bypass-policy true \
--org "$ADO_ORG" --detect false
# 5. Abandon if no longer needed
az repos pr update --id 42 --status abandoned --org "$ADO_ORG" --detect false
# 6. Delete leftover branch
az repos ref delete --name heads/feature/foo -r my-repo --org "$ADO_ORG" -p "$ADO_PROJECT" --detect false
Debug Failed Pipeline
# 1. Find pipeline and recent runs
az pipelines list --name "bvp" --org "$ADO_ORG" -p "$ADO_PROJECT" --detect false
az pipelines runs list --pipeline-ids 8 --top 5 --org "$ADO_ORG" -p "$ADO_PROJECT" --detect false
# 2. Inspect run (stage/job tree)
${CLAUDE_SKILL_DIR}/scripts/ado-get-run.sh --run-id 12345
# 3. Get failed task logs
${CLAUDE_SKILL_DIR}/scripts/ado-get-logs.sh --run-id 12345 --failed-only
# 4. Drill into specific task
${CLAUDE_SKILL_DIR}/scripts/ado-get-logs.sh --run-id 12345 --task "Terraform Plan" --tail 100
Manage Policies
# List policies on a repo (requires repo GUID)
REPO_ID=$(az repos show -r my-repo --org "$ADO_ORG" -p "$ADO_PROJECT" --detect false --query id -o tsv)
az repos policy list --repository-id "$REPO_ID" --org "$ADO_ORG" -p "$ADO_PROJECT" --detect false
# Add BVP
${CLAUDE_SKILL_DIR}/scripts/ado-create-policy.sh --repo my-repo --type build --pipeline-id 7 --display-name "BVP" --blocking
# Disable temporarily
az repos policy update --id 5 --enabled false --org "$ADO_ORG" -p "$ADO_PROJECT" --detect false
# Re-enable
az repos policy update --id 5 --enabled true --org "$ADO_ORG" -p "$ADO_PROJECT" --detect false
# Delete
az repos policy delete --id 5 --org "$ADO_ORG" -p "$ADO_PROJECT" --detect false
Manage Variable Groups
az pipelines variable-group list --org "$ADO_ORG" -p "$ADO_PROJECT" --detect false
az pipelines variable-group show --id 3 --org "$ADO_ORG" -p "$ADO_PROJECT" --detect false
az pipelines variable-group variable create --group-id 3 --name TF_STATE_RG --value rg-terraform --org "$ADO_ORG" -p "$ADO_PROJECT" --detect false
az pipelines variable-group variable update --group-id 3 --name TF_STATE_RG --new-value rg-terraform-v2 --org "$ADO_ORG" -p "$ADO_PROJECT" --detect false
az pipelines variable-group variable delete --group-id 3 --name OLD_VAR --org "$ADO_ORG" -p "$ADO_PROJECT" --detect false
Manage Environment Checks & Approvals
# List environments
# AUTH as set in the Authentication section (Basic PAT or az Bearer token)
BASE="${ADO_ORG}/${ADO_PROJECT}/_apis"
curl -s -H "$AUTH" "$BASE/distributedtask/environments?api-version=7.1"
# List checks on environment
curl -s -H "$AUTH" "$BASE/pipelines/checks/configurations?resourceType=environment&resourceId=<ENV_ID>&\$expand=settings&api-version=7.1-preview.1"
# Approve a pipeline run
curl -s -X PATCH -H "$AUTH" -H "Content-Type: application/json" \
-d '[{"approvalId":"<ID>","status":"approved","comment":"Verified plan"}]' \
"$BASE/pipelines/approvals?api-version=7.1-preview.1"
PR Review Workflow
# 1. Show PR details + list active threads
az repos pr show --id <ID> --org "$ADO_ORG" -p "$ADO_PROJECT" --detect false
${CLAUDE_SKILL_DIR}/scripts/ado-pr-threads.sh --pr-id <ID> --repo <repo> --status active
# 2. Read a specific thread in full (--json + python3 for formatting)
${CLAUDE_SKILL_DIR}/scripts/ado-pr-threads.sh --pr-id <ID> --repo <repo> --json \
| python3 -c "import sys,json; threads=json.load(sys.stdin); t=[x for x in threads if x['id']==<threadId>]; print(json.dumps(t[0],indent=2)) if t else print('Not found')"
# 3. Reply to reviewer feedback
# AUTH as set in the Authentication section (Basic PAT or az Bearer token)
THREADS="${ADO_ORG}/${ADO_PROJECT}/_apis/git/repositories/<repo>/pullRequests/<ID>/threads"
curl -s -H "$AUTH" -H "Content-Type: application/json" -X POST \
"${THREADS}/<threadId>/comments?api-version=7.1" \
-d '{"parentCommentId":1,"content":"Fixed in latest push.","commentType":1}'
# 4. Provide code suggestion
curl -s -H "$AUTH" -H "Content-Type: application/json" -X POST \
"${THREADS}?api-version=7.1" \
-d '{"comments":[{"content":"Consider:\n\n```suggestion\nnew_code_here\n```","commentType":1}],"status":"active","threadContext":{"filePath":"/path","rightFileStart":{"line":10,"offset":1},"rightFileEnd":{"line":12,"offset":1}}}'
# 5. Resolve addressed threads
curl -s -H "$AUTH" -H "Content-Type: application/json" -X PATCH \
"${THREADS}/<threadId>?api-version=7.1" \
-d '{"status":"fixed"}'
# 6. Ask for clarification (create new general thread)
curl -s -H "$AUTH" -H "Content-Type: application/json" -X POST \
"${THREADS}?api-version=7.1" \
-d '{"comments":[{"content":"Question: what is the expected behavior when X?","commentType":1}],"status":"active"}'
Work Item → Development → PR Workflow
# 1a. Get ticket context (all fields + relations for parent/child)
az boards work-item show --id <WI_ID> --expand all --org "$ADO_ORG" --detect false -o json
# 1b. Load work item comments for context
# AUTH as set in the Authentication section (Basic PAT or az Bearer token)
curl -s -H "$AUTH" -H "Content-Type: application/json" \
"${ADO_ORG}/${ADO_PROJECT}/_apis/wit/workItems/<WI_ID>/comments?\$top=10&order=desc&api-version=7.1-preview.4"
# 2. Get parent story for context (parse parent URL from relations, extract ID)
az boards work-item show --id <PARENT_ID> --expand all --org "$ADO_ORG" --detect false -o json
# Note parent's: custom fields, area-path, iteration-path
# 3a. Pick up existing unassigned task
az boards work-item update --id <WI_ID> --state Active --assigned-to "[email protected]" \
--org "$ADO_ORG" --detect false
# Set custom fields if missing
az boards work-item update --id <WI_ID> \
-f "<custom-field-1>[email protected]" "<custom-field-2>[email protected]" \
--org "$ADO_ORG" --detect false
# 3b. Or create new task from parent story (inherit custom fields, area, iteration)
az boards work-item create --type "<TaskType>" --title "Implement X" \
--description "Description of the task" \
--area "<AreaPath>" \
--iteration "<IterationPath>" \
-f "<custom-field-1>[email protected]" "<custom-field-2>[email protected]" \
--assigned-to "[email protected]" \
--org "$ADO_ORG" -p "$ADO_PROJECT" --detect false
# Link to parent
az boards work-item relation add --id <NEW_TASK_ID> --relation-type parent --target-id <PARENT_ID> \
--org "$ADO_ORG" --detect false
# 4. Do development work...
# 5. Create PR
az repos pr create -r my-repo -s feature/branch --title "Implement X" \
--auto-complete true --squash true --delete-source-branch true \
--org "$ADO_ORG" -p "$ADO_PROJECT" --detect false
# 6. Link work item to PR
az repos pr work-item add --id <PR_ID> --work-items <WI_ID> \
--org "$ADO_ORG" --detect false
# 7. Add reviewers to PR (use GUIDs from identity fields, not emails - PAT can't resolve emails)
az repos pr reviewer add --id <PR_ID> --reviewers <REVIEWER1_GUID> <REVIEWER2_GUID> --required true \
--org "$ADO_ORG" --detect false
Find Team Members for Review
# 1. List teams
az devops team list --org "$ADO_ORG" -p "$ADO_PROJECT" --detect false -o table
# 2. Get members of a team
az devops team list-member --team "<TeamName>" --org "$ADO_ORG" -p "$ADO_PROJECT" --detect false -o table
# 3. Add as PR reviewer (use member GUID from list-member output - email lookup fails with PAT auth)
az repos pr reviewer add --id <PR_ID> --reviewers <MEMBER_GUID> \
--org "$ADO_ORG" --detect false
Troubleshooting
--expand and -f are mutually exclusive
az boards work-item show does not allow --expand and -f (fields) together. Use one or the other.
Creating and activating a task requires two calls
Setting --state Active and --assigned-to in the same create call is not supported. Create the task first (it starts as New), then update it in a second call:
# 1. Create
az boards work-item create --type "<TaskType>" --title "..." --description "..." \
--area "<AreaPath>" --iteration "<IterationPath>" \
--org "$ADO_ORG" -p "$ADO_PROJECT" --detect false -o json
# 2. Activate and assign
az boards work-item update --id <NEW_ID> --state Active --assigned-to "[email protected]" \
--org "$ADO_ORG" --detect false
Identity Discovery
At the start of each session that involves assigning work items or adding PR reviewers, resolve the current user's identity:
az ad signed-in-user show --query '{displayName: displayName, mail: mail, id: id}' -o json
Use the returned email for --assigned-to and the GUID for reviewer operations. Do not persist this to memory - the user may switch accounts between sessions.
Instructions
Check CLAUDE.md for project-specific ADO configuration (custom work item types, area paths, iteration paths, custom fields, team conventions).
When the user asks for an ADO operation:
- Resolve auth per the Authentication section (PAT,
ADO_TOKEN, or az login token); instruct the user only if no source works - Use
azCLI with-o jsonwhen parsing programmatically,-o tablefor display - For complex queries, chain commands (e.g., list pipelines -> find ID -> run pipeline)
- Use scripts from
${CLAUDE_SKILL_DIR}/scripts/for: create-policy, get-run, get-logs, pr-threads - For environments/checks/approvals and comments, use REST curl patterns
- For work items, remember:
az boards work-item show/update/relation addare org-scoped (no--project), onlycreateandquerytake--project - When creating tasks from stories, inherit custom fields, area-path, and iteration-path from the parent
- When linking work items to PRs, prefer
az repos pr work-item add(from PR side) - If an
azcommand fails unexpectedly, fall back to raw curl - When reading a work item or PR for context, also load comments/threads for full picture
- For PR review, start by listing active threads to see what needs attention
- Code suggestions use
```suggestionblocks - ADO renders them as applicable diffs
Additional Resources
For detailed reference material and real output examples, consult:
reference.md- Full az CLI flag tables, JMESPath query patterns, REST API details for environments/checks/approvals, work item comments, PR comment threads, work item field reference, identity resolution cascade, policy type GUIDsexamples.md- Real output samples for all major commands (PR list/show/policy, pipeline list/runs, run tree, work item JSON, query results, task logs, PR threads, work item comments, code suggestions)examples/project-claude-ado.md- Template for project-specific ADO configuration (custom fields, work item types, area/iteration paths) to add to your CLAUDE.md
What ships with it: 9 files
57.5 KB alongside SKILL.md, 6 of them executable
examples/
- project-claude-ado.md1.0 KB
scripts/
- ado-create-policy.shruns11.6 KB
- ado-get-logs.shruns3.3 KB
- ado-get-run.shruns3.6 KB
- ado-pr-threads.shruns3.0 KB
- lib/ado-client.shruns5.1 KB
- lib/ado-profile.shruns4.9 KB
- examples.md8.5 KB
- reference.md16.5 KB
Gives 0 of the 12 instructions most pr commit review skills give in ~9.4k tokens
Counted across 888 of the 1,342 authors here whose files we hold, read 2026-08-07
- Use conventional commits formatin 127 of 888, across 115 files
- Keep subject line under 72 charactersin 62 of 888, across 48 files
- Delete branches after mergein 51 of 888, across 38 files
- Use imperative mood in subject linein 51 of 888, across 42 files
- Use imperative mood in commit messagesin 44 of 888
- Verify directory is ignored before creating worktreein 43 of 888, across 12 files
- Generate a conventional commit messagein 43 of 888
- Add unignored worktree directories to gitignorein 42 of 888, across 10 files
- Make atomic commitsin 39 of 888, across 27 files
- Run tests before committingin 36 of 888, across 25 files
- Verify clean test baselinein 35 of 888, across 9 files
- Split unrelated changes into separate commitsin 35 of 888, across 30 files
Said here and by no other author read
- Use az CLI for most operations
- Use bash scripts for complex orchestration
- Read auth credentials from environment variables
- Never export or mutate session credentials
- Use --detect false on all az commands
- Omit --project on org-scoped commands
Grouped from the skills themselves: near-identical wordings counted once, and counted by distinct author, so one author publishing three of these counts once. Length counted with cl100k_base; the agent that loads this file may tokenize it differently.