
Azure Devops
- 146 installs
- 70 repo stars
- Updated July 26, 2026
- rysweet/amplihack
Connect repos, pipelines, boards, and releases to Azure DevOps—trigger builds, query work items, manage PR policies, and automate delivery from Claude Code sessions.
About
The azure-devops skill integrates Claude Code with Microsoft Azure DevOps for pipelines, repositories, boards, and release automation. It enables teams building SaaS or API products to orchestrate CI/CD, track work items, and manage pull-request workflows without leaving the coding agent session.
- Automates pipeline and repo operations from the agent
- Aligns work items with code changes
- Supports PR and policy workflows in Azure Repos
- Bridges local edits with cloud build agents
- Reduces manual portal context switching
Azure Devops by the numbers
- 146 all-time installs (skills.sh)
- +2 installs in the week ending Jul 26, 2026 (Skillselion tracking)
- Ranked #459 of 1,438 DevOps & CI/CD skills by installs in the Skillselion catalog
- Data as of Aug 2, 2026 (Skillselion catalog sync)
npx skills add https://github.com/rysweet/amplihack --skill azure-devopsAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 146 |
|---|---|
| repo stars | ★ 70 |
| Last updated | July 26, 2026 |
| Repository | rysweet/amplihack ↗ |
What it does
Connect repos, pipelines, boards, and releases to Azure DevOps—trigger builds, query work items, manage PR policies, and automate delivery from Claude Code sessions.
Files
Azure DevOps Skill
Complete Azure DevOps integration covering boards, repositories, pipelines, and artifacts.
Auto-activates when: User mentions Azure DevOps, ADO, work items, boards, repos, pipelines, artifacts, or Azure DevOps URLs.
Purpose
This skill provides comprehensive guidance for Azure DevOps automation through purpose-built Python CLI tools that handle:
Work Items (Boards)
- Work item creation with HTML-formatted descriptions
- Work item updates (state, assignments, fields)
- Work item deletion with confirmation
- Parent-child relationship linking
- WIQL query execution
- Work item type and field discovery
Repositories
- Repository listing with details
- Pull request creation with reviewers and work items
- Branch validation
- Clone URL access
Pipelines
- Pipeline listing and execution
- Build monitoring and logs
- Deployment management
Artifacts
- Package feed management
- Package publishing and downloading
- Version management
Quick Start
1. Authentication First
ALWAYS start by checking authentication:
python .claude/scenarios/az-devops-tools/auth_check.py --auto-fixThis verifies Azure CLI is installed, you're logged in, org/project are configured, and you have access.
See: [@authentication.md]
2. Common Operations
Create Work Item
python .claude/scenarios/az-devops-tools/create_work_item.py \
--type "User Story" \
--title "Implement feature" \
--description @story.mdQuery Work Items
python .claude/scenarios/az-devops-tools/list_work_items.py --query mineCreate Pull Request
python .claude/scenarios/az-devops-tools/create_pr.py \
--source feature/branch \
--target main \
--title "Add feature"Progressive Loading References
For detailed guidance on specific operations, see:
- [@authentication.md] - Authentication methods (PAT, OAuth, environment variables)
- [@work-items.md] - Work item CRUD operations, field updates, state transitions
- [@queries.md] - WIQL query patterns, filtering, sorting
- [@html-formatting.md] - HTML formatting in work item descriptions/comments
- [@repos.md] - Repository operations, pull request workflows
- [@pipelines.md] - Pipeline triggers, build monitoring, deployment
- [@artifacts.md] - Package management, artifact publishing
- [@HOW_TO_CREATE_YOUR_OWN.md] - Template for creating similar integration tools
Available Tools
| Tool | Purpose | When to Use |
|---|---|---|
auth_check.py | Verify authentication | Before any operations |
create_work_item.py | Create work items | Add User Stories, Tasks, Bugs, etc. |
update_work_item.py | Update work items | Change state, assignee, fields |
delete_work_item.py | Delete work items | Remove work items (with confirmation) |
get_work_item.py | Get work item details | View complete work item info |
list_work_items.py | Query work items | Find, filter, and list work items |
link_parent.py | Link parent-child | Create Epic → Feature → Story hierarchies |
query_wiql.py | Execute WIQL queries | Complex filtering with WIQL |
format_html.py | Convert to HTML | Format rich descriptions |
list_types.py | Discover types/fields | Explore available options |
list_repos.py | List repositories | View all repositories in project |
create_pr.py | Create pull request | Submit code for review |
Common Patterns
Pattern 1: Create Work Item with Parent
# Create parent work item
python .claude/scenarios/az-devops-tools/create_work_item.py \
--type "Epic" \
--title "Q1 Planning Initiative" \
--description @epic_desc.md
# Output: Created work item #12345
# Create child and link to parent
python .claude/scenarios/az-devops-tools/create_work_item.py \
--type "Feature" \
--title "Authentication System" \
--description @feature_desc.md \
--parent-id 12345
# Output: Created work item #12346 and linked to parent #12345Pattern 2: Query and Update Work Items
# Find your active work items
python .claude/scenarios/az-devops-tools/list_work_items.py \
--query mine \
--format ids-only
# Update work item state
python .claude/scenarios/az-devops-tools/update_work_item.py \
--id 12345 \
--state "Active" \
--comment "Starting work on this"Pattern 3: Feature Branch to Pull Request
# List repositories
python .claude/scenarios/az-devops-tools/list_repos.py
# Create pull request
python .claude/scenarios/az-devops-tools/create_pr.py \
--source feature/auth \
--target main \
--title "Add authentication" \
--description @pr_desc.md \
--reviewers "user1@domain.com,user2@domain.com" \
--work-items "12345,12346"Pattern 4: Discover Available Types
# List all work item types in your project
python .claude/scenarios/az-devops-tools/list_types.py
# Show fields for specific type
python .claude/scenarios/az-devops-tools/list_types.py \
--type "User Story" \
--fieldsCritical Learnings
HTML Formatting Required
Azure DevOps work item descriptions use HTML, not Markdown or plain text.
The tools handle this automatically:
create_work_item.pyconverts markdown to HTML by default- Use
--no-htmlto disable conversion - Or use
format_html.pydirectly for custom formatting
See: [@html-formatting.md]
Two-Step Parent Linking
You cannot specify a parent during work item creation via CLI (Azure limitation).
The tools provide two approaches:
Option A: Use --parent-id flag (recommended):
python .claude/scenarios/az-devops-tools/create_work_item.py \
--type "Task" \
--title "My Task" \
--parent-id 12345Option B: Link separately:
# Step 1: Create
TASK_ID=$(python .claude/scenarios/az-devops-tools/create_work_item.py \
--type "Task" \
--title "My Task" \
--json | jq -r '.id')
# Step 2: Link
python .claude/scenarios/az-devops-tools/link_parent.py \
--child $TASK_ID \
--parent 12345Area Path and Work Item Types
- Area path format:
ProjectName\TeamName\SubArea - Work item types vary by project (standard + custom types)
- Use
list_types.pyto discover what's available in your project
Error Recovery
| Error | Tool to Use | Example |
|---|---|---|
| Authentication failed | auth_check.py --auto-fix | Auto-login and configure |
| Invalid work item type | list_types.py | See available types |
| Field validation error | list_types.py --type "Type" --fields | See valid fields |
| Parent link failed | Check IDs exist, verify hierarchy rules | Epic → Feature → Story → Task |
| Branch does not exist | Verify with git branch -a | Push branch first |
Tool Implementation
All tools are in ~/.amplihack/.claude/scenarios/az-devops-tools/:
- Standalone Python programs (can run independently)
- Importable modules (can use in other scripts)
- Comprehensive error handling
- Tests in
tests/directory
See: Tool README
Philosophy
These tools follow amplihack principles:
- Ruthless Simplicity: Each tool does one thing well
- Zero-BS: Every function works, no stubs or TODOs
- Reusable: Importable and composable
- Fail-Fast: Clear errors with actionable guidance
- Self-Contained: Standard library + azure CLI wrapper only
Quick Reference
# Setup (first time)
python .claude/scenarios/az-devops-tools/auth_check.py --auto-fix
# Create work item
python .claude/scenarios/az-devops-tools/create_work_item.py \
--type "User Story" \
--title "Title" \
--description @desc.md
# Update work item
python .claude/scenarios/az-devops-tools/update_work_item.py \
--id 12345 \
--state "Active"
# Query work items
python .claude/scenarios/az-devops-tools/list_work_items.py --query mine
# Create pull request
python .claude/scenarios/az-devops-tools/create_pr.py \
--source feature/branch \
--target main \
--title "Add feature"
# Discover types
python .claude/scenarios/az-devops-tools/list_types.pyArtifacts and Package Management Guide
Guide for working with Azure Artifacts - package feeds for NuGet, npm, Python, Maven, and Universal packages.
Overview
Azure Artifacts provides package management integrated with Azure DevOps:
- Feeds - Package repositories
- Packages - NuGet, npm, Python, Maven, Universal
- Upstream sources - Proxy public registries
- Retention policies - Automatic cleanup
Common Commands
List Feeds
az artifacts feed list --output tableCreate Feed
az artifacts feed create --name my-feed --description "Team packages"Show Feed Details
az artifacts feed show --feed my-feedList Packages in Feed
az artifacts package list --feed my-feed --output tableDownload Package
az artifacts universal download \
--feed my-feed \
--name my-package \
--version 1.0.0 \
--path ./downloadPackage Types
NuGet (.NET)
Publish NuGet Package
# Add feed as source
nuget sources add \
-Name AzureDevOps \
-Source https://pkgs.dev.azure.com/ORG/_packaging/FEED/nuget/v3/index.json
# Push package
nuget push MyPackage.1.0.0.nupkg \
-Source AzureDevOps \
-ApiKey azConsume NuGet Package
Add to project file:
<PackageReference Include="MyPackage" Version="1.0.0" />npm (JavaScript)
Publish npm Package
# Set registry
npm config set registry https://pkgs.dev.azure.com/ORG/_packaging/FEED/npm/registry/
# Authenticate
npm login --registry=https://pkgs.dev.azure.com/ORG/_packaging/FEED/npm/registry/
# Publish
npm publishConsume npm Package
Add to .npmrc:
registry=https://pkgs.dev.azure.com/ORG/_packaging/FEED/npm/registry/
always-auth=truePython (PyPI)
Publish Python Package
# Install twine
pip install twine
# Upload to feed
twine upload --repository-url https://pkgs.dev.azure.com/ORG/_packaging/FEED/pypi/upload dist/*Consume Python Package
Configure pip:
pip install --index-url https://pkgs.dev.azure.com/ORG/_packaging/FEED/pypi/simple/ my-packageUniversal Packages
Publish Universal Package
az artifacts universal publish \
--feed my-feed \
--name my-package \
--version 1.0.0 \
--description "My package" \
--path ./package-contentsDownload Universal Package
az artifacts universal download \
--feed my-feed \
--name my-package \
--version 1.0.0 \
--path ./downloadFeed Permissions
Common permission levels:
- Reader - Download packages
- Contributor - Download and publish packages
- Owner - Full control including feed settings
Grant Feed Permissions
az artifacts feed permission add \
--feed my-feed \
--user user@domain.com \
--role contributorUpstream Sources
Configure Upstream
Upstream sources proxy public registries:
az artifacts feed upstream add \
--feed my-feed \
--name nuget-org \
--protocol nuget \
--upstream-source-type publicBenefits:
- Cached packages for faster downloads
- Protection against upstream deletions
- Single source for all dependencies
Retention Policies
Configure Retention
az artifacts feed retention set \
--feed my-feed \
--count-limit 100 \
--days-to-keep-recently-downloaded 30Keeps:
- Last 100 versions
- Packages downloaded in last 30 days
Common Workflows
Publish from Pipeline
In azure-pipelines.yml:
- task: UniversalPackages@0
displayName: "Publish package"
inputs:
command: publish
publishDirectory: "$(Build.ArtifactStagingDirectory)"
feedsToUsePublish: "internal"
vstsFeedPublish: "my-feed"
vstsFeedPackagePublish: "my-package"
versionOption: "patch"Consume in Pipeline
- task: UniversalPackages@0
displayName: "Download package"
inputs:
command: download
downloadDirectory: "$(Build.SourcesDirectory)"
feedsToUse: "internal"
vstsFeed: "my-feed"
vstsFeedPackage: "my-package"
vstsPackageVersion: "1.0.0"Version Promotion
Promote package to release view:
az artifacts package promote \
--feed my-feed \
--package my-package \
--version 1.0.0 \
--view ReleaseViews
Feeds can have views for different quality levels:
- Local - All versions
- Prerelease - Preview versions
- Release - Production-ready versions
List Views
az artifacts feed view list --feed my-feedCreate View
az artifacts feed view create \
--feed my-feed \
--name Staging \
--description "Staging packages"Best Practices
1. Use upstream sources - Cache public packages 2. Set retention policies - Manage storage costs 3. Use views - Separate prerelease from production 4. Version semantically - Follow SemVer (major.minor.patch) 5. Automate publishing - Use pipelines
Tips
1. Authentication - Use PAT tokens with Packaging (Read/Write) scope 2. Multiple feeds - Separate by team or project 3. Feed URLs - Different per package type (npm, NuGet, PyPI) 4. Permissions - Start restrictive, grant as needed 5. Monitoring - Review package usage and storage
Troubleshooting
"Feed not found"
List available feeds:
az artifacts feed list"Authentication failed"
Generate PAT token with Packaging scope:
1. Personal Access Tokens in Azure DevOps 2. Create token with Packaging (Read/Write) 3. Use as password in package manager
"Version already exists"
Cannot overwrite published versions. Increment version number.
See Also
Authentication Setup
Complete guide to setting up authentication for Azure DevOps CLI tools.
Prerequisites
- Azure CLI installed
- Azure account with DevOps access
- Network access to dev.azure.com
Step 1: Install Azure CLI
macOS
brew install azure-cliWindows
winget install Microsoft.AzureCLILinux (Ubuntu/Debian)
curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bashVerify installation:
az --versionStep 2: Install DevOps Extension
az extension add --name azure-devopsVerify extension:
az extension list --output table | grep azure-devopsStep 3: Login to Azure
az loginThis opens your browser for interactive login. After successful login, you'll see your subscriptions listed.
Alternative: Service Principal Login
For automation/CI:
az login --service-principal \
--username APP_ID \
--password PASSWORD \
--tenant TENANT_IDStep 4: Configure Defaults
Set default organization and project:
az devops configure --defaults \
organization=https://dev.azure.com/YOUR_ORG \
project=YOUR_PROJECTView current configuration:
az devops configure --listStep 5: Verify Access
Use the auth_check tool:
python .claude/scenarios/az-devops-tools/auth_check.pyExpected output:
✓ Azure CLI installed
✓ Logged in
✓ DevOps extension installed
✓ Organization configured
✓ Project configured
✓ Organization accessible
✓ Project accessibleAuto-Fix Common Issues
python .claude/scenarios/az-devops-tools/auth_check.py --auto-fixThis attempts to:
- Install DevOps extension if missing
- Guide you through missing configuration
Configuration Priority
Tools load configuration in this order (highest to lowest):
1. Command-line arguments (--org, --project) 2. Environment variables 3. az devops configure defaults 4. Config file (if specified)
Environment Variables
export AZURE_DEVOPS_ORG_URL="https://dev.azure.com/YOUR_ORG"
export AZURE_DEVOPS_PROJECT="YOUR_PROJECT"Add to your ~/.bashrc or ~/.zshrc for persistence.
Troubleshooting
"az: command not found"
Azure CLI not installed or not in PATH.
- Reinstall Azure CLI
- Check PATH:
echo $PATH - Restart shell
"ERROR: az devops: 'devops' is not in the 'az' command group"
DevOps extension not installed.
az extension add --name azure-devops"Please run 'az login' to setup account"
Not logged in to Azure.
az login"TF401019: The Git repository with name or identifier does not exist"
Wrong organization or project.
- Verify org URL format:
https://dev.azure.com/ORG_NAME - Check project name (case-sensitive)
- Verify access permissions
"Authentication failed"
Token expired or insufficient permissions.
# Re-login
az logout
az login
# Verify permissions in Azure DevOps web portalSecurity Best Practices
- Use service principals for automation
- Rotate credentials regularly
- Don't commit credentials to git
- Use Azure Key Vault for production
- Enable MFA on your Azure account
See Also
How to Create Your Own Azure DevOps Tool
This guide shows you how to create a new tool in the az-devops-tools suite.
Template Structure
Every tool follows this pattern:
#!/usr/bin/env python3
"""Brief description of what the tool does.
Philosophy:
- Single responsibility
- Standard library preferred
- Clear error messages
- Reusable functions
Public API:
main_function: Primary tool functionality
helper_function: Utility function
"""
import argparse
import sys
from typing import Optional
from .common import (
AzCliWrapper,
ExitCode,
handle_error,
load_config,
)
def main_function(arg1: str, arg2: Optional[str] = None) -> bool:
"""Do the main work of the tool.
Args:
arg1: Required argument
arg2: Optional argument
Returns:
True if successful, False otherwise
Raises:
ValueError: If arguments are invalid
"""
# Implementation here
pass
def main() -> None:
"""CLI entry point."""
parser = argparse.ArgumentParser(
description="Tool description",
formatter_class=argparse.RawDescriptionHelpFormatter,
)
# Required arguments
parser.add_argument(
"--required",
required=True,
help="Required argument",
)
# Optional arguments
parser.add_argument(
"--optional",
help="Optional argument",
)
# Common arguments
parser.add_argument("--org", help="Azure DevOps organization URL")
parser.add_argument("--project", help="Project name")
parser.add_argument("--config", help="Config file path")
args = parser.parse_args()
# Load configuration
config = load_config(args.config)
org = args.org or config.get("org")
project = args.project or config.get("project")
# Validate required config
if not org or not project:
handle_error(
"Organization and project are required",
ExitCode.CONFIG_ERROR,
"Set via --org/--project, environment variables, or az devops configure",
)
# Execute main function
try:
success = main_function(
arg1=args.required,
arg2=args.optional,
)
sys.exit(ExitCode.SUCCESS if success else ExitCode.COMMAND_ERROR)
except ValueError as e:
handle_error(str(e), ExitCode.VALIDATION_ERROR)
except Exception as e:
handle_error(f"Unexpected error: {e}", ExitCode.COMMAND_ERROR)
if __name__ == "__main__":
main()
__all__ = ["main_function", "main"]Key Components
1. Module Docstring
"""Brief description of what the tool does.
Philosophy:
- Single responsibility
- Standard library preferred
- Clear error messages
- Reusable functions
Public API:
main_function: Primary tool functionality
helper_function: Utility function
"""2. Use Common Utilities
Import from common.py:
from .common import (
AzCliWrapper, # For az CLI commands
ExitCode, # Standard exit codes
handle_error, # Error handling
load_config, # Configuration loading
validate_work_item_id, # Validation helpers
format_table, # Output formatting
)3. Argument Parsing
parser = argparse.ArgumentParser(
description="Tool description",
formatter_class=argparse.RawDescriptionHelpFormatter,
)
# Always include common arguments
parser.add_argument("--org", help="Azure DevOps organization URL")
parser.add_argument("--project", help="Project name")
parser.add_argument("--config", help="Config file path")4. Configuration Loading
config = load_config(args.config)
org = args.org or config.get("org")
project = args.project or config.get("project")
# Validate required config
if not org or not project:
handle_error(
"Organization and project are required",
ExitCode.CONFIG_ERROR,
"Set via --org/--project, environment variables, or az devops configure",
)5. Error Handling
try:
# Do work
result = wrapper.devops_command(["work-item", "show", "--id", work_item_id])
if not result.success:
handle_error(
f"Failed to show work item {work_item_id}",
ExitCode.COMMAND_ERROR,
result.stderr,
)
except ValueError as e:
handle_error(str(e), ExitCode.VALIDATION_ERROR)
except Exception as e:
handle_error(f"Unexpected error: {e}", ExitCode.COMMAND_ERROR)6. Dual Usage (CLI + Import)
def main_function(arg1: str) -> bool:
"""Reusable function that does the work."""
# Implementation
pass
def main() -> None:
"""CLI wrapper around main_function."""
parser = argparse.ArgumentParser(...)
args = parser.parse_args()
success = main_function(args.arg1)
sys.exit(ExitCode.SUCCESS if success else ExitCode.COMMAND_ERROR)
if __name__ == "__main__":
main()Testing
Create tests in tests/test_your_tool.py:
"""Tests for your_tool module."""
import pytest
from unittest.mock import Mock, patch
from ..your_tool import main_function
class TestMainFunction:
"""Test main_function behavior."""
def test_success_case(self):
"""Test successful execution."""
result = main_function("valid_input")
assert result is True
def test_validation_error(self):
"""Test validation error handling."""
with pytest.raises(ValueError, match="Invalid input"):
main_function("")
@patch(".claude.scenarios.az_devops_tools.your_tool.AzCliWrapper")
def test_cli_integration(self, mock_wrapper):
"""Test CLI command execution."""
mock_result = Mock(success=True, stdout="output")
mock_wrapper.return_value.devops_command.return_value = mock_result
result = main_function("input")
assert result is TrueDesign Principles
Single Responsibility
Each tool does ONE thing well:
auth_checkonly checks authenticationformat_htmlonly formats HTMLcreate_work_itemonly creates work items
Composability
Tools can be combined:
from .format_html import markdown_to_html
from .create_work_item import create_work_item
# Format description
html_description = markdown_to_html(markdown_text)
# Create work item with formatted description
create_work_item(
title="My Story",
description=html_description,
work_item_type="User Story",
)Clear Errors
Always provide actionable error messages:
# BAD
print("Error: Invalid input")
# GOOD
handle_error(
"Work item ID must be a positive integer",
ExitCode.VALIDATION_ERROR,
f"Got: '{work_item_id}'. Example: 1234",
)No Swallowed Exceptions
# BAD
try:
do_something()
except:
pass # Silent failure
# GOOD
try:
do_something()
except SpecificError as e:
handle_error(f"Failed to do something: {e}", ExitCode.COMMAND_ERROR)Checklist
Before submitting a new tool:
- [ ] Module docstring with philosophy and public API
- [ ] Uses
common.pyutilities - [ ] Argument parser with common arguments (--org, --project, --config)
- [ ] Configuration loading with fallbacks
- [ ] Proper error handling with actionable messages
- [ ] Standard exit codes
- [ ] Both CLI and importable usage
- [ ] Tests with >80% coverage
- [ ] Added to
__init__.pyexports - [ ] Documented in main README.md
- [ ] Examples in tool docstring
Example: Creating a "list-projects" Tool
#!/usr/bin/env python3
"""List all projects in an Azure DevOps organization.
Philosophy:
- Single responsibility: list projects only
- Standard library for formatting
- Clear error messages
- Reusable list_projects function
Public API:
list_projects: Get list of projects
"""
import argparse
import json
import sys
from typing import List, Dict
from .common import (
AzCliWrapper,
ExitCode,
handle_error,
load_config,
format_table,
)
def list_projects(org: str, format: str = "table") -> List[Dict[str, str]]:
"""List all projects in organization.
Args:
org: Organization URL
format: Output format (table, json, csv)
Returns:
List of project dictionaries with name, id, description
"""
wrapper = AzCliWrapper(org=org)
result = wrapper.devops_command(["project", "list"])
if not result.success:
handle_error(
"Failed to list projects",
ExitCode.COMMAND_ERROR,
result.stderr,
)
projects = result.json_output.get("value", [])
return [
{
"name": p["name"],
"id": p["id"],
"description": p.get("description", ""),
}
for p in projects
]
def main() -> None:
"""CLI entry point."""
parser = argparse.ArgumentParser(
description="List Azure DevOps projects",
formatter_class=argparse.RawDescriptionHelpFormatter,
)
parser.add_argument(
"--format",
choices=["table", "json", "csv"],
default="table",
help="Output format",
)
parser.add_argument("--org", required=True, help="Organization URL")
parser.add_argument("--config", help="Config file path")
args = parser.parse_args()
config = load_config(args.config)
org = args.org or config.get("org")
if not org:
handle_error(
"Organization is required",
ExitCode.CONFIG_ERROR,
"Set via --org, environment variable, or az devops configure",
)
try:
projects = list_projects(org, args.format)
if args.format == "json":
print(json.dumps(projects, indent=2))
elif args.format == "csv":
print("name,id,description")
for p in projects:
print(f"{p['name']},{p['id']},{p['description']}")
else: # table
rows = [[p["name"], p["id"], p["description"]] for p in projects]
print(format_table(["Name", "ID", "Description"], rows))
sys.exit(ExitCode.SUCCESS)
except Exception as e:
handle_error(f"Failed to list projects: {e}", ExitCode.COMMAND_ERROR)
if __name__ == "__main__":
main()
__all__ = ["list_projects", "main"]This template creates a fully functional tool that:
- Lists projects in an organization
- Supports multiple output formats
- Has proper error handling
- Can be used as CLI or imported
- Follows all design principles
HTML Formatting Guide
Azure DevOps work items use HTML for descriptions and comments. The format_html tool converts markdown to proper HTML.
Why HTML?
Azure DevOps displays work item descriptions as HTML. Plain text looks unprofessional and lacks formatting.
Auto-Formatting
The create_work_item tool automatically converts markdown to HTML:
python .claude/scenarios/az-devops-tools/create_work_item.py \
--type "User Story" \
--title "My Story" \
--description "# Title
This is **bold** and this is *italic*.
- List item 1
- List item 2"Disable with --no-format:
python .claude/scenarios/az-devops-tools/create_work_item.py \
--type Task \
--title "My Task" \
--description "<p>Already HTML</p>" \
--no-formatStandalone Formatter
Convert markdown files to HTML:
# From file
python .claude/scenarios/az-devops-tools/format_html.py story.md
# From stdin
echo "# Title" | python .claude/scenarios/az-devops-tools/format_html.py
# Save to file
python .claude/scenarios/az-devops-tools/format_html.py story.md -o output.htmlSupported Markdown
Headings
Markdown: # H1, ## H2, ### H3 HTML: <h1>H1</h1>, <h2>H2</h2>, <h3>H3</h3>
Bold and Italic
Markdown: **bold**, *italic*, ***bold italic*** HTML: <strong>bold</strong>, <em>italic</em>, <strong><em>bold italic</em></strong>
Lists
Markdown:
- Item 1
- Item 2
1. First
2. SecondHTML:
<ul>
<li>Item 1</li>
<li>Item 2</li>
</ul>
<ol>
<li>First</li>
<li>Second</li>
</ol>Code
Markdown: ` inline code **HTML:** <code>inline code</code>`
Markdown:
````markdown
def hello():
print("Hello!")````
HTML:
<pre><code class="language-python">def hello():
print("Hello!")
</code></pre>Links
Markdown: [Link text](https://example.com) HTML: <a href="https://example.com">Link text</a>
Programmatic Usage
from .claude.scenarios.az_devops_tools.format_html import markdown_to_html
markdown = """
# User Story
As a user, I want to **log in** so I can access my account.
## Acceptance Criteria
- User can enter credentials
- System validates login
- Error shown on failure
"""
html = markdown_to_html(markdown)
print(html)Best Practices
1. Use markdown - Easier to write and maintain 2. Preview in Azure DevOps - Check formatting after creation 3. Keep it simple - Avoid complex HTML 4. Use code blocks - For code snippets and logs 5. Structure with headings - Makes descriptions scannable
Limitations
The formatter supports common markdown only:
- No tables
- No images
- No nested lists
- No HTML entities
For advanced formatting, use raw HTML with --no-format.
See Also
- [@work-items.md] - Work item operations
- Markdown Guide
Pipeline Operations Guide
Guide for working with Azure DevOps pipelines and builds using CLI tools.
Overview
Azure DevOps Pipelines provide CI/CD automation. Common operations include:
- Listing pipelines
- Queuing builds
- Checking build status
- Viewing logs
- Managing deployments
Common Commands
List Pipelines
az pipelines list --output tableShow Pipeline Details
az pipelines show --id PIPELINE_IDQueue a Build
az pipelines run --id PIPELINE_IDQueue Build with Branch
az pipelines run --id PIPELINE_ID --branch feature/branch-nameList Recent Builds
az pipelines build list --output tableGet Build Status
az pipelines build show --id BUILD_IDDownload Build Logs
az pipelines build logs download --id BUILD_ID --output-dir ./logsPipeline Triggers
Pipelines can be triggered by:
Push Triggers
Automatically run on push to specific branches:
trigger:
branches:
include:
- main
- feature/*Pull Request Triggers
Run validation builds for PRs:
pr:
branches:
include:
- mainScheduled Triggers
Run on a schedule:
schedules:
- cron: "0 0 * * *"
displayName: Daily midnight build
branches:
include:
- mainManual Triggers
Queue builds manually via CLI or web portal.
Build Variables
Predefined Variables
Common system variables:
$(Build.SourceBranch)- Source branch$(Build.BuildNumber)- Build number$(Build.SourceVersion)- Commit SHA$(Build.Repository.Name)- Repository name
Custom Variables
Set in pipeline YAML:
variables:
buildConfiguration: "Release"
vmImage: "ubuntu-latest"Override at queue time:
az pipelines run --id PIPELINE_ID --variables buildConfiguration=DebugBuild Artifacts
Publish Artifacts
In pipeline:
- task: PublishBuildArtifacts@1
inputs:
pathToPublish: "$(Build.ArtifactStagingDirectory)"
artifactName: "drop"Download Artifacts
az pipelines build artifacts download --id BUILD_ID --output-dir ./artifactsMonitoring Builds
Check Build Status
# Get latest build for pipeline
az pipelines build list --pipeline-id PIPELINE_ID --top 1View Build Timeline
az pipelines build show --id BUILD_ID --openThis opens the build in your browser.
Stream Build Logs
# Not directly supported - use polling:
while true; do
az pipelines build show --id BUILD_ID --query status
sleep 10
doneDeployment Management
List Releases
az pipelines release list --output tableCreate Release
az pipelines release create --definition-id RELEASE_DEF_IDApprove Deployment
az pipelines release approval approve --id APPROVAL_IDCommon Workflows
Trigger Build on PR Creation
When you create a PR, pipeline validation builds run automatically if configured.
# Create PR (automatically triggers build)
python .claude/scenarios/az-devops-tools/create_pr.py \
--source feature/branch \
--target main \
--title "My feature"
# Check PR build status
az pipelines build list --branch refs/pull/PR_NUMBER/mergeManual Build with Custom Parameters
az pipelines run \
--id PIPELINE_ID \
--branch feature/branch-name \
--variables buildConfiguration=Debug testEnabled=trueCheck If Build Passed
BUILD_STATUS=$(az pipelines build show --id BUILD_ID --query status -o tsv)
if [ "$BUILD_STATUS" = "completed" ]; then
RESULT=$(az pipelines build show --id BUILD_ID --query result -o tsv)
if [ "$RESULT" = "succeeded" ]; then
echo "Build passed"
else
echo "Build failed"
fi
fiPipeline YAML Best Practices
1. Use templates - Reuse common steps 2. Parameterize - Use variables for flexibility 3. Cache dependencies - Speed up builds 4. Run tests - Validate changes 5. Publish artifacts - Make outputs available
Tips
1. Enable PR builds - Catch issues before merge 2. Set up notifications - Get alerts on build failures 3. Use build badges - Show build status in README 4. Review logs - Understand failures quickly 5. Clean up old builds - Manage storage usage
Troubleshooting
"Pipeline not found"
List available pipelines:
az pipelines list"Build failed"
Download and review logs:
az pipelines build logs download --id BUILD_ID --output-dir ./logs
cat logs/*.log"Permission denied"
Verify you have Build Administrator or Contributor permissions.
See Also
WIQL Query Guide
Work Item Query Language (WIQL) guide for querying Azure DevOps work items.
Predefined Queries
Common queries are built-in to list_work_items.py:
mine
Your assigned work items:
python .claude/scenarios/az-devops-tools/list_work_items.py --query mineunassigned
Open work items with no assignee:
python .claude/scenarios/az-devops-tools/list_work_items.py --query unassignedrecent
Recently changed work items:
python .claude/scenarios/az-devops-tools/list_work_items.py --query recentactive
Active work items:
python .claude/scenarios/az-devops-tools/list_work_items.py --query activeteam
Team's open work items:
python .claude/scenarios/az-devops-tools/list_work_items.py --query teamCustom WIQL Queries
Basic Syntax
SELECT [Field1], [Field2], [Field3]
FROM workitems
WHERE [Condition]
ORDER BY [Field] ASC/DESCExample: Active Tasks
python .claude/scenarios/az-devops-tools/list_work_items.py \
--wiql "SELECT [System.Id], [System.Title] FROM workitems WHERE [System.WorkItemType] = 'Task' AND [System.State] = 'Active'"Example: High Priority Bugs
python .claude/scenarios/az-devops-tools/list_work_items.py \
--wiql "SELECT [System.Id], [System.Title], [Microsoft.VSTS.Common.Priority] FROM workitems WHERE [System.WorkItemType] = 'Bug' AND [Microsoft.VSTS.Common.Priority] = 1"Example: Recently Created
python .claude/scenarios/az-devops-tools/list_work_items.py \
--wiql "SELECT [System.Id], [System.Title] FROM workitems WHERE [System.CreatedDate] >= @Today - 7 ORDER BY [System.CreatedDate] DESC"Output Formats
Table (Default)
python .claude/scenarios/az-devops-tools/list_work_items.py --query mineJSON
python .claude/scenarios/az-devops-tools/list_work_items.py --query mine --format jsonCSV
python .claude/scenarios/az-devops-tools/list_work_items.py --query mine --format csv > items.csvIDs Only
python .claude/scenarios/az-devops-tools/list_work_items.py --query mine --format ids-onlyLimit Results
python .claude/scenarios/az-devops-tools/list_work_items.py --query recent --limit 10Common Fields
System Fields
[System.Id]- Work item ID[System.Title]- Title[System.WorkItemType]- Type (Bug, Task, Story, etc.)[System.State]- State (New, Active, Closed, etc.)[System.AssignedTo]- Assigned user[System.CreatedDate]- Creation date[System.ChangedDate]- Last modified date[System.AreaPath]- Area path[System.IterationPath]- Sprint/iteration[System.Tags]- Tags
Microsoft VSTS Fields
[Microsoft.VSTS.Common.Priority]- Priority (1-4)[Microsoft.VSTS.Common.Severity]- Severity (1-4)[Microsoft.VSTS.Common.StackRank]- Backlog rank
Operators
Comparison
=- Equals<>- Not equals<- Less than>- Greater than<=- Less than or equal>=- Greater than or equal
Logical
AND- Both conditions trueOR- Either condition trueNOT- Negate condition
String
CONTAINS- Field contains valueLIKE- Pattern matching
Special
IN- Value in listUNDER- Under area/iteration path@Me- Current user@Today- Current date@Project- Current project
Example Queries
Find Bugs Assigned to Me
python .claude/scenarios/az-devops-tools/list_work_items.py \
--wiql "SELECT [System.Id], [System.Title] FROM workitems WHERE [System.WorkItemType] = 'Bug' AND [System.AssignedTo] = @Me"Find Stories in Sprint
python .claude/scenarios/az-devops-tools/list_work_items.py \
--wiql "SELECT [System.Id], [System.Title] FROM workitems WHERE [System.WorkItemType] = 'User Story' AND [System.IterationPath] = 'MyProject\\Sprint 1'"Find Items with Tag
python .claude/scenarios/az-devops-tools/list_work_items.py \
--wiql "SELECT [System.Id], [System.Title] FROM workitems WHERE [System.Tags] CONTAINS 'security'"Find Items in Area
python .claude/scenarios/az-devops-tools/list_work_items.py \
--wiql "SELECT [System.Id], [System.Title] FROM workitems WHERE [System.AreaPath] UNDER 'MyProject\\Platform'"Tips
1. Test queries in Azure DevOps web UI first 2. Use --limit for large result sets 3. Use @Me, @Today, @Project for dynamic queries 4. Escape single quotes in strings with two single quotes 5. Field names are case-sensitive
See Also
- WIQL Syntax Reference
- [@work-items.md] - Work item operations
Repository Operations Guide
Guide for working with Azure DevOps repositories using CLI tools.
Listing Repositories
Basic List
python .claude/scenarios/az-devops-tools/list_repos.pyWith Details
python .claude/scenarios/az-devops-tools/list_repos.py --include-detailsThis shows:
- Repository size
- Default branch
- Web URLs
- Clone URLs (HTTP/SSH)
JSON Output
python .claude/scenarios/az-devops-tools/list_repos.py --format jsonCreating Pull Requests
Basic PR
python .claude/scenarios/az-devops-tools/create_pr.py \
--source feature/auth \
--target main \
--title "Add authentication"PR with Description from File
python .claude/scenarios/az-devops-tools/create_pr.py \
--source feature/auth \
--target main \
--title "Add authentication" \
--description @pr_description.mdPR with Reviewers and Work Items
python .claude/scenarios/az-devops-tools/create_pr.py \
--source feature/bug-fix \
--target main \
--title "Fix critical bug" \
--reviewers "user1@domain.com,user2@domain.com" \
--work-items "12345,12346"Draft PR
python .claude/scenarios/az-devops-tools/create_pr.py \
--source feature/wip \
--target main \
--title "WIP: New feature" \
--draftPR with Auto-Complete
python .claude/scenarios/az-devops-tools/create_pr.py \
--source feature/done \
--target main \
--title "Complete feature" \
--auto-complete \
--delete-source-branchCommon Workflows
Feature Branch to Main
# Create feature branch (outside tool)
git checkout -b feature/new-feature main
# ... make changes, commit ...
# Push branch
git push -u origin feature/new-feature
# Create PR
python .claude/scenarios/az-devops-tools/create_pr.py \
--source feature/new-feature \
--target main \
--title "Add new feature" \
--description @feature_desc.md \
--reviewers "team@domain.com"Bug Fix with Work Item Link
# Create bug fix branch
git checkout -b bugfix/issue-123 main
# ... fix bug, commit ...
# Push and create PR
git push -u origin bugfix/issue-123
python .claude/scenarios/az-devops-tools/create_pr.py \
--source bugfix/issue-123 \
--target main \
--title "Fix: Issue #123 login button" \
--work-items "123"Clone URLs
Repositories have two clone URL formats:
HTTPS (Recommended)
https://dev.azure.com/ORG/PROJECT/_git/REPOBest for most scenarios. Uses Azure DevOps credentials or PAT tokens.
SSH
git@ssh.dev.azure.com:v3/ORG/PROJECT/REPORequires SSH key setup. Better for automation.
Branch Management
Branch Naming Conventions
Common patterns:
feature/feature-name- New featuresbugfix/issue-number- Bug fixeshotfix/critical-issue- Production hotfixesrelease/version- Release branches
Protected Branches
Main/master branches typically have policies:
- Require pull request reviews
- Require build validation
- Require work item linking
Use create_pr.py to work with protected branches.
Repository Permissions
Common permission levels:
- Reader - Clone, pull, view code
- Contributor - Clone, pull, push, create branches
- Project Administrator - All permissions
Tips and Best Practices
1. Use feature branches - Never commit directly to main 2. Link work items - Connect PRs to work items for traceability 3. Write good PR titles - Clear, concise description of changes 4. Add reviewers early - Get feedback during development 5. Use draft PRs - For work-in-progress that needs visibility
Troubleshooting
"Branch does not exist"
Verify branch exists:
git branch -a | grep branch-namePush if local only:
git push -u origin branch-name"Pull request already exists"
Check existing PRs for these branches:
az repos pr list --source-branch feature/branch-name"Permission denied"
Verify you have Contributor access to the repository.
See Also
- Azure DevOps Repos
- Pull Request Overview
- [@work-items.md] - Link work items to PRs
Work Item Management Guide
Complete guide to creating and managing work items with Azure DevOps CLI tools.
Work Item Types
Standard types in most projects:
- Epic - Large body of work (months)
- Feature - Shippable functionality (weeks)
- User Story - User-facing feature (days)
- Task - Technical work (hours/days)
- Bug - Defect or issue
Check available types in your project:
python .claude/scenarios/az-devops-tools/list_types.pyCreating Work Items
Basic Creation
python .claude/scenarios/az-devops-tools/create_work_item.py \
--type "User Story" \
--title "Implement user login"With Description (Markdown Auto-Converted)
python .claude/scenarios/az-devops-tools/create_work_item.py \
--type "User Story" \
--title "User authentication" \
--description "# Story
As a user, I want to log in with my credentials.
## Acceptance Criteria
- User can enter email/password
- System validates credentials
- Invalid login shows error message"With All Options
python .claude/scenarios/az-devops-tools/create_work_item.py \
--type Bug \
--title "Login button not responding" \
--description "Button click does nothing" \
--assigned-to user@example.com \
--area "MyProject\\Frontend" \
--iteration "MyProject\\Sprint 1" \
--tags "ui,critical,login" \
--fields "Microsoft.VSTS.Common.Priority=1" \
--fields "Microsoft.VSTS.Common.Severity=1-Critical"With Parent Link
# Creates Task and links to Story #1234
python .claude/scenarios/az-devops-tools/create_work_item.py \
--type Task \
--title "Write unit tests" \
--parent 1234Linking Work Items
Create parent-child relationships:
# Link Task #5678 to Story #1234
python .claude/scenarios/az-devops-tools/link_parent.py \
--child 5678 \
--parent 1234Valid relationships:
- Task → User Story, Bug, Feature, Epic
- Bug → Feature, Epic
- User Story → Feature, Epic
- Feature → Epic
Updating Work Items
Update state, assignments, or other fields:
# Update state
python .claude/scenarios/az-devops-tools/update_work_item.py --id 12345 --state "Active"
# Reassign work item
python .claude/scenarios/az-devops-tools/update_work_item.py --id 12345 --assign-to "user@domain.com"
# Update multiple fields with comment
python .claude/scenarios/az-devops-tools/update_work_item.py --id 12345 --state "Resolved" --comment "Fixed issue"Querying Work Items
List and filter work items:
# List my active work items
python .claude/scenarios/az-devops-tools/list_work_items.py --state Active --assigned-to @me
# List all bugs
python .claude/scenarios/az-devops-tools/list_work_items.py --type Bug
# Use predefined query
python .claude/scenarios/az-devops-tools/list_work_items.py --query mineCommon Workflows
Create Epic with Features
# Create Epic
epic_output=$(python .claude/scenarios/az-devops-tools/create_work_item.py \
--type Epic \
--title "Authentication System")
epic_id=$(echo "$epic_output" | grep "ID:" | awk '{print $2}')
# Create Features under Epic
for feature in "OAuth Integration" "Session Management" "RBAC"; do
python .claude/scenarios/az-devops-tools/create_work_item.py \
--type Feature \
--title "$feature" \
--parent "$epic_id"
doneCreate Story with Tasks
# Create Story
story_output=$(python .claude/scenarios/az-devops-tools/create_work_item.py \
--type "User Story" \
--title "Implement login UI")
story_id=$(echo "$story_output" | grep "ID:" | awk '{print $2}')
# Create Tasks
for task in "Design mockup" "Implement form" "Add validation" "Write tests"; do
python .claude/scenarios/az-devops-tools/create_work_item.py \
--type Task \
--title "$task" \
--parent "$story_id"
doneField Reference
Common System Fields
System.Title- Work item title (required)System.Description- HTML descriptionSystem.State- Current state (New, Active, Closed, etc.)System.AssignedTo- Assigned user (email or display name)System.AreaPath- Area path (format: Project\\Team\\Area)System.IterationPath- Sprint/iterationSystem.Tags- Comma-separated tags
Microsoft VSTS Fields
Microsoft.VSTS.Common.Priority- 1 (highest) to 4 (lowest)Microsoft.VSTS.Common.Severity- 1-Critical, 2-High, 3-Medium, 4-LowMicrosoft.VSTS.Common.StackRank- Backlog ordering
Discover All Fields
# Show fields for specific type
python .claude/scenarios/az-devops-tools/list_types.py --type "User Story"
# Show all fields including system
python .claude/scenarios/az-devops-tools/list_types.py --type Bug --all-fieldsTips and Best Practices
1. Use markdown descriptions - Auto-converted to HTML for better formatting 2. Set area path - Helps with team organization and queries 3. Link work items early - Easier to track relationships 4. Use tags - Improves searchability 5. Validate types first - Use list_types before creating
Troubleshooting
"Invalid work item type"
Check available types:
python .claude/scenarios/az-devops-tools/list_types.py"Work item type with spaces"
Use quotes:
--type "User Story" # Correct
--type User Story # Wrong"Parent link failed"
Verify both IDs exist and relationship is valid.
See Also
- [@queries.md] - WIQL query patterns
- [@html-formatting.md] - Rich text formatting
- MS Learn: Work Items