Use when creating or modifying Azure DevOps PR reviews via Azure CLI, adding comments to threads, voting on PRs, or getting errors about non-existent commands - explains CLI vs REST API split and provides correct az devops invoke patterns
When you need to comment on Azure DevOps PRs or get errors about non-existent commands like `az repos pr comment`, this skill guides you through the CLI/REST API split. It explains that commenting requires `az devops invoke` with proper JSON for file positioning, while voting uses simple `az repos pr set-vote` commands.
/plugin marketplace add mthalman/superpowers/plugin install superpowers@cc-pluginsThis skill inherits all available tools. When active, it can use any tool Claude has access to.
Create and modify Azure DevOps pull request reviews using Azure CLI and REST API.
The key insight: Azure CLI has NO direct commands for PR comments/threads. You must use REST API via az devops invoke.
⚠️ CRITICAL: There is NO az repos pr comment, NO az repos pr thread, NO az repos pr review create. If you find yourself typing these, STOP - they don't exist. Use az devops invoke instead.
Azure CLI commands (direct support):
az repos pr set-vote - Vote on PR (approve, reject, etc.)az repos pr reviewer - Manage reviewers (add, list, remove)az repos pr create/list/show/update - Basic PR managementREST API only (via az devops invoke):
Critical: Commands like az repos pr comment or az repos pr thread DO NOT EXIST. Don't invent them.
Always choose the simplest tool:
az repos pr set-voteaz devops invokeaz repos pr reviewer| Task | Tool | Command Pattern |
|---|---|---|
| Vote on PR | CLI | az repos pr set-vote --id {pr} --vote {approve|approve-with-suggestions|reject|reset|wait-for-author} |
| Add/remove reviewers | CLI | az repos pr reviewer add/remove --id {pr} --reviewers user@email |
| Create comment thread | REST API | az devops invoke --area git --resource threads --route-parameters project={project} repositoryId={repo} pullRequestId={pr} --in-file thread.json --http-method POST |
| Add to existing thread | REST API | az devops invoke --area git --resource comments --route-parameters project={project} repositoryId={repo} pullRequestId={pr} threadId={threadId} --in-file comment.json --http-method POST |
| List threads | REST API | az devops invoke --area git --resource threads --route-parameters project={project} repositoryId={repo} pullRequestId={pr} --http-method GET |
Voting (CLI):
az repos pr set-voteCommenting (REST API):
az devops invokeYou can do both independently. Common pattern: Create comment threads, then vote.
For file-specific comments, thread context specifies location:
{
"comments": [{"parentCommentId": 0, "content": "Your comment", "commentType": 1}],
"status": 1,
"threadContext": {
"filePath": "/src/file.ts",
"rightFileStart": {"line": 10, "offset": 1},
"rightFileEnd": {"line": 10, "offset": 1}
}
}
Key points:
threadContext entirely for general PR comments1 = active (default for new threads)2 = fixed3 = wontFix4 = closed5 = byDesign6 = pending1 = text (use this for code review comments)0 = unknown2 = codeChange3 = system# Option 1: Approve without comments
az repos pr set-vote \
--id 42 \
--vote approve \
--org https://dev.azure.com/myorg
# Option 2: Approve with suggestions
az repos pr set-vote \
--id 42 \
--vote approve-with-suggestions \
--org https://dev.azure.com/myorg
# Option 3: Request changes
az repos pr set-vote \
--id 42 \
--vote reject \
--org https://dev.azure.com/myorg
Vote options:
approve - LGTM, no concernsapprove-with-suggestions - Approve but have minor suggestionsreject - Request changes before mergingwait-for-author - Waiting for author responsereset - Clear your vote# 1. Create thread.json with your comment
cat > thread.json << 'EOF'
{
"comments": [
{
"parentCommentId": 0,
"content": "Consider extracting this to a constant",
"commentType": 1
}
],
"status": 1,
"threadContext": {
"filePath": "/src/config.ts",
"rightFileStart": {"line": 10, "offset": 1},
"rightFileEnd": {"line": 10, "offset": 1}
}
}
EOF
# 2. Create thread via REST API
az devops invoke \
--area git \
--resource threads \
--route-parameters \
project="MyProject" \
repositoryId="abc-123-def" \
pullRequestId=42 \
--in-file thread.json \
--http-method POST \
--org https://dev.azure.com/myorg \
--api-version 7.1
cat > thread.json << 'EOF'
{
"comments": [
{
"parentCommentId": 0,
"content": "Add error handling for this block",
"commentType": 1
}
],
"status": 1,
"threadContext": {
"filePath": "/src/api/handler.ts",
"rightFileStart": {"line": 25, "offset": 1},
"rightFileEnd": {"line": 27, "offset": 1}
}
}
EOF
az devops invoke \
--area git \
--resource threads \
--route-parameters \
project="MyProject" \
repositoryId="abc-123-def" \
pullRequestId=42 \
--in-file thread.json \
--http-method POST \
--org https://dev.azure.com/myorg \
--api-version 7.1
# For comments not tied to specific code lines
cat > thread.json << 'EOF'
{
"comments": [
{
"parentCommentId": 0,
"content": "Overall this looks good! Just a few minor suggestions.",
"commentType": 1
}
],
"status": 1
}
EOF
az devops invoke \
--area git \
--resource threads \
--route-parameters \
project="MyProject" \
repositoryId="abc-123-def" \
pullRequestId=42 \
--in-file thread.json \
--http-method POST \
--org https://dev.azure.com/myorg \
--api-version 7.1
# 1. Create comment.json
cat > comment.json << 'EOF'
{
"content": "Also consider adding logging here",
"parentCommentId": 0,
"commentType": 1
}
EOF
# 2. Add to thread via REST API
az devops invoke \
--area git \
--resource comments \
--route-parameters \
project="MyProject" \
repositoryId="abc-123-def" \
pullRequestId=42 \
threadId=15 \
--in-file comment.json \
--http-method POST \
--org https://dev.azure.com/myorg \
--api-version 7.1
Note: threadId is returned when you create a thread. Save it if you need to add follow-up comments.
# Get all threads (returns JSON array)
az devops invoke \
--area git \
--resource threads \
--route-parameters \
project="MyProject" \
repositoryId="abc-123-def" \
pullRequestId=42 \
--http-method GET \
--org https://dev.azure.com/myorg \
--api-version 7.1 \
--query "value[].{id:id, status:status, filePath:threadContext.filePath, line:threadContext.rightFileStart.line}"
# 1. Create first comment thread
cat > thread1.json << 'EOF'
{
"comments": [{"parentCommentId": 0, "content": "First comment", "commentType": 1}],
"status": 1,
"threadContext": {
"filePath": "/src/file1.ts",
"rightFileStart": {"line": 10, "offset": 1},
"rightFileEnd": {"line": 10, "offset": 1}
}
}
EOF
az devops invoke --area git --resource threads \
--route-parameters project="MyProject" repositoryId="abc-123" pullRequestId=42 \
--in-file thread1.json --http-method POST \
--org https://dev.azure.com/myorg --api-version 7.1
# 2. Create second comment thread
cat > thread2.json << 'EOF'
{
"comments": [{"parentCommentId": 0, "content": "Second comment", "commentType": 1}],
"status": 1,
"threadContext": {
"filePath": "/src/file2.ts",
"rightFileStart": {"line": 25, "offset": 1},
"rightFileEnd": {"line": 27, "offset": 1}
}
}
EOF
az devops invoke --area git --resource threads \
--route-parameters project="MyProject" repositoryId="abc-123" pullRequestId=42 \
--in-file thread2.json --http-method POST \
--org https://dev.azure.com/myorg --api-version 7.1
# 3. Vote to approve with suggestions
az repos pr set-vote --id 42 --vote approve-with-suggestions \
--org https://dev.azure.com/myorg
# Use leftFileStart/leftFileEnd for code that was removed
cat > thread.json << 'EOF'
{
"comments": [
{
"parentCommentId": 0,
"content": "Why was this removed?",
"commentType": 1
}
],
"status": 1,
"threadContext": {
"filePath": "/src/removed.ts",
"leftFileStart": {"line": 15, "offset": 1},
"leftFileEnd": {"line": 15, "offset": 1}
}
}
EOF
az devops invoke --area git --resource threads \
--route-parameters project="MyProject" repositoryId="abc-123" pullRequestId=42 \
--in-file thread.json --http-method POST \
--org https://dev.azure.com/myorg --api-version 7.1
| Error | Cause | Fix |
|---|---|---|
az repos pr comment: command not found | Invented non-existent command | Use REST API via az devops invoke |
az repos pr thread: command not found | Invented non-existent command | Use REST API via az devops invoke |
--thread-position: unrecognized argument | Tried to use non-existent parameter | Use threadContext JSON with rightFileStart/rightFileEnd |
Line must be positive | Used 0-indexed lines | Lines start at 1, not 0 |
Thread not found | Wrong threadId | List threads with GET request to verify ID |
Invalid thread context | Malformed JSON structure | Check threadContext has correct nested structure |
# List repositories to find ID
az repos list \
--project MyProject \
--org https://dev.azure.com/myorg \
--query "[].{name:name, id:id}"
# List projects
az devops project list \
--org https://dev.azure.com/myorg \
--query "value[].{name:name, id:id}"
# Show PR info including head commit
az repos pr show \
--id 42 \
--org https://dev.azure.com/myorg \
--query "{title:title, status:status, sourceRef:sourceRefName, targetRef:targetRefName}"
# WRONG - these commands don't exist
az repos pr comment create --line 10 --file src/file.ts
az repos pr thread add --thread-id 15
az repos pr reviewer create --thread-position 10
Why wrong: Azure CLI has no direct comment/thread commands. These will fail.
DO instead: Use az devops invoke with REST API endpoints.
{
"threadContext": {
"rightFileStart": {"line": 0, "offset": 0}
}
}
Why wrong: Lines and offsets are 1-indexed in Azure DevOps.
DO instead:
{
"threadContext": {
"rightFileStart": {"line": 1, "offset": 1}
}
}
{
"threadContext": {
"filePath": "/src/new-feature.ts",
"leftFileStart": {"line": 10, "offset": 1}
}
}
Why wrong: New code uses rightFileStart, not leftFileStart.
DO instead:
rightFileStart/rightFileEnd for new/added codeleftFileStart/leftFileEnd for old/deleted code# WRONG - no combined command exists
az repos pr review create --vote approve --comment "LGTM"
Why wrong: Voting and commenting are separate operations.
DO instead:
# Create threads first
az devops invoke --area git --resource threads ...
# Then vote
az repos pr set-vote --id 42 --vote approve
Azure CLI uses your authenticated Azure DevOps organization. Ensure you're logged in:
# Login to Azure DevOps
az login
# Set default organization (optional)
az devops configure --defaults organization=https://dev.azure.com/myorg
# Verify authentication
az devops user show
Simple vote/approval?
├─ YES → az repos pr set-vote --vote {approve|reject|...}
└─ NO → Continue
Need to add/remove reviewers?
├─ YES → az repos pr reviewer add/remove
└─ NO → Continue
Need comments on specific code?
├─ YES → az devops invoke with REST API (threads endpoint)
└─ NO → az repos pr update for description changes
| Scenario | CLI | REST API | Recommendation |
|---|---|---|---|
| Vote on PR | ✅ az repos pr set-vote | ✅ Works | CLI simpler |
| Manage reviewers | ✅ az repos pr reviewer | ✅ Works | CLI simpler |
| Comment on code | ❌ No command | ✅ Required | REST API only |
| General PR comment | ❌ No command | ✅ Required | REST API only |
| Add to existing thread | ❌ No command | ✅ Required | REST API only |
| Update PR title/description | ✅ az repos pr update | ✅ Works | CLI simpler |
| Parameter | Description | Example |
|---|---|---|
--area | API area | git |
--resource | API resource | threads, comments |
--route-parameters | URL route parameters | project="MyProject" repositoryId="abc-123" pullRequestId=42 |
--in-file | JSON input file | thread.json |
--http-method | HTTP verb | POST, GET, PATCH |
--org | Organization URL | https://dev.azure.com/myorg |
--api-version | API version | 7.1 (use latest) |
--query | JMESPath query for output | value[].id |
# Get thread IDs and file paths
--query "value[].{id:id, file:threadContext.filePath}"
# Get active threads only
--query "value[?status==\`1\`].{id:id, file:threadContext.filePath}"
# Get thread comments
--query "value[].{id:id, comments:comments[].content}"
Applies Anthropic's official brand colors and typography to any sort of artifact that may benefit from having Anthropic's look-and-feel. Use it when brand colors or style guidelines, visual formatting, or company design standards apply.
Creating algorithmic art using p5.js with seeded randomness and interactive parameter exploration. Use this when users request creating art using code, generative art, algorithmic art, flow fields, or particle systems. Create original algorithmic art rather than copying existing artists' work to avoid copyright violations.
Create beautiful visual art in .png and .pdf documents using design philosophy. You should use this skill when the user asks to create a poster, piece of art, design, or other static piece. Create original visual designs, never copying existing artists' work to avoid copyright violations.