
Push To Ado
- 1 installs
- 1 repo stars
- Updated July 5, 2026
- craigspaterson/agent-skills
Push changes and updates to Azure DevOps from agent workflows.
About
Agent skill for pushing code and work items to Azure DevOps. Automates CI/CD pipeline triggers and project tracking updates.
- Azure DevOps work item creation and updates
- Pipeline automation from agents
Push To Ado by the numbers
- 1 all-time installs (skills.sh)
- Ranked #2,476 of 3,282 Productivity & Planning skills by installs in the Skillselion catalog
- Data as of Jul 8, 2026 (Skillselion catalog sync)
npx skills add https://github.com/craigspaterson/agent-skills --skill push-to-adoAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 1 |
|---|---|
| repo stars | ★ 1 |
| Last updated | July 5, 2026 |
| Repository | craigspaterson/agent-skills ↗ |
What it does
Push changes and updates to Azure DevOps from agent workflows.
Files
Push to ADO
Three scripts are available. Choose based on what the user provides:
| Scenario | Script |
|---|---|
Single story drafted by /user-story or /technical-story | upload.ps1 |
| Full backlog with Epics → Features → Stories | upload-hierarchy.ps1 |
| Bulk update existing work items with improved content | update-work-items.ps1 |
---
Single story — upload.ps1
Extracts structured fields from the story already in context and calls upload.ps1 to create the ADO work item. Do not re-derive the API logic — all implementation lives in the script.
Step 1 — Extract fields from the story in context
Parse the story markdown (output of /technical-story or /user-story) and collect:
| Field | Source in story markdown |
|---|---|
-Title | **Title:** line |
-StoryPoints | numeric value from **Story Points:** line |
-Priority | value from **Priority:** line (Critical / High / Medium / Low) |
-ParentId | parent feature ID — ask user if not already in context |
-Description | composed markdown block: story statement + Out of Scope + Dependencies + Notes sections |
-AcceptanceCriteria | all AC blocks only (AC1, AC2, … formatted as markdown) |
-Tags | optional; semicolon-separated tags (e.g. "Technical") — omit if none |
Work item type is always User Story — hardcoded in the script.
Step 2 — Call the script
pwsh ~/.claude/skills/push-to-ado/scripts/upload.ps1 `
-Title "..." `
-Description "..." `
-AcceptanceCriteria "..." `
-StoryPoints 3 `
-Priority "High" `
-ParentId 2881288Omit -ParentId if no parent was provided.
Pass -Org and -Project only if the user explicitly provides them; otherwise the script reads from $env:ADO_ORG / $env:ADO_PROJECT.
Step 3 — Report result
After the script completes, show the user the returned work item URL. If the script errors, surface the error message and do not retry silently.
---
Full hierarchy — upload-hierarchy.ps1
Use when the user provides a structured backlog with multiple Epics, Features, and Stories to create in one pass.
The script defines a New-WorkItem helper function that accepts any ADO work item type (Epic, Feature, User Story) and handles auth, parent linking, and field mapping. Technical stories are created as User Story type with the tag Technical.
How to use it
1. Open scripts/upload-hierarchy.ps1 and replace the placeholder comment with New-WorkItem calls for the user's backlog, following the call pattern documented below. 2. Run it:
ADO_ORG=myorg ADO_PROJECT=myproject pwsh ~/.claude/skills/push-to-ado/scripts/upload-hierarchy.ps1New-WorkItem parameters
| Parameter | Type | Required | Notes |
|---|---|---|---|
-Type | string | yes | Epic, Feature, or User Story |
-Title | string | yes | |
-Description | string | yes | Rendered as Markdown in ADO |
-AcceptanceCriteria | string | no | Rendered as Markdown; omit for Epics/Features |
-StoryPoints | int | no | Omit for Epics/Features |
-Priority | string | no | Critical, High, Medium (default), Low |
-ParentId | int | no | ADO work item ID of the parent |
-Tags | string | no | Semicolon-separated; use "Technical" to tag technical stories |
Parent linking pattern
Capture the returned ID from each Epic/Feature call and pass it as -ParentId to its children:
$epicId = New-WorkItem -Type "Epic" -Title "My Epic" ...
$featureId = New-WorkItem -Type "Feature" -Title "My Feature" -ParentId $epicId ...
New-WorkItem -Type "User Story" -Title "My Story" -ParentId $featureId ...---
Bulk update existing items — update-work-items.ps1
Use when stories already exist in ADO (created via upload-hierarchy.ps1 or manually) and need their Title, Description, Acceptance Criteria, Story Points, or Priority replaced with improved content — for example, after re-drafting stories through /user-story or /technical-story.
How to use it
1. Open scripts/update-work-items.ps1 and replace the placeholder comment with one Update-WorkItem call per item, providing the known ADO work item ID for each. 2. Run it:
ADO_ORG=myorg ADO_PROJECT=myproject pwsh ~/.claude/skills/push-to-ado/scripts/update-work-items.ps1Update-WorkItem parameters
| Parameter | Type | Required | Notes |
|---|---|---|---|
-Id | int | yes | Existing ADO work item ID (e.g. 42) |
-Title | string | yes | Replaces the current title |
-Description | string | yes | Replaces System.Description; rendered as Markdown |
-AcceptanceCriteria | string | no | Replaces AC field; rendered as Markdown |
-StoryPoints | int | yes | Replaces current story points |
-Priority | string | yes | Critical, High, Medium, or Low |
-Tags | string | no | Semicolon-separated tags; replaces existing tags when provided |
Typical workflow
1. Draft improved stories using /user-story or /technical-story 2. Note the ADO ID of each item to update 3. Populate update-work-items.ps1 with one Update-WorkItem call per item 4. Run the script — all fields are patched in a single PATCH request per item
push-to-ado
Uploads and updates work items in Azure DevOps. Supports three modes:
- Single story — push one drafted User Story or Technical Story via
upload.ps1 - Full hierarchy — create a complete Epic → Feature → Story backlog in one pass via
upload-hierarchy.ps1 - Bulk update — patch Title, Description, AC, Story Points, Priority, and Tags on existing items via
update-work-items.ps1
Prerequisites
- PowerShell 7+ (
pwsh) - Azure CLI (
az) - Logged in to Azure:
az login - ADO access to the target organisation and project
Setup
Set environment variables to avoid being prompted on every run:
# ~/.claude/settings.json env block, or your shell profile
export ADO_ORG=myorg
export ADO_PROJECT=myprojectOr pass them directly as script parameters (-Org / -Project) for upload.ps1.
Install
npx skills add craigspaterson/agent-skills/skills/push-to-adoUsage — single story
After drafting a story with /user-story or /technical-story, say:
push to ADOThe agent extracts the title, description, acceptance criteria, story points, and priority from the story in context, then calls scripts/upload.ps1 to create the work item. It will ask for a parent feature ID if one wasn't provided.
Usage — full hierarchy
When you have a structured backlog with Epics, Features, and Stories, say:
push this backlog to ADOThe agent writes a tailored call to scripts/upload-hierarchy.ps1, which creates all items in the correct parent–child order. Technical stories are created as User Story type with the tag Technical.
Usage — bulk update
When you have existing ADO items with content that needs replacing (e.g. after re-drafting through /user-story or /technical-story), say:
update those ADO itemsThe agent populates scripts/update-work-items.ps1 with one Update-WorkItem call per item using the known ADO IDs, then runs it to PATCH all items in a single pass.
Output
On success the agent reports each item created or updated:
[Epic] My Epic
-> AB#101 https://dev.azure.com/myorg/myproject/_workitems/edit/101
[Feature] My Feature
-> AB#102 https://dev.azure.com/myorg/myproject/_workitems/edit/102
AB#103 My Story
-> Updated#!/usr/bin/env pwsh
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'
$Org = $env:ADO_ORG
$Project = $env:ADO_PROJECT
if (-not $Org) { Write-Error "ADO_ORG not set"; exit 1 }
if (-not $Project) { Write-Error "ADO_PROJECT not set"; exit 1 }
$baseUrl = "https://dev.azure.com/$Org/$([Uri]::EscapeDataString($Project))"
$priorityMap = @{ Critical = 1; High = 2; Medium = 3; Low = 4 }
Write-Host "Authenticating with Azure..."
$token = az account get-access-token --resource 499b84ac-1321-427f-aa17-267ca6975798 --query accessToken -o tsv
if (-not $token) { Write-Error "az login required"; exit 1 }
$headers = @{ Authorization = "Bearer $token" }
function Update-WorkItem {
param(
[int]$Id,
[string]$Title,
[string]$Description,
[string]$AcceptanceCriteria = "",
[int]$StoryPoints,
[string]$Priority,
[string]$Tags = ""
)
$ops = [System.Collections.Generic.List[hashtable]]::new()
$ops.Add(@{ op = "add"; path = "/fields/System.Title"; value = $Title })
$ops.Add(@{ op = "add"; path = "/fields/System.Description"; value = $Description })
$ops.Add(@{ op = "add"; path = "/multilineFieldsFormat/System.Description"; value = "Markdown" })
if ($AcceptanceCriteria) {
$ops.Add(@{ op = "add"; path = "/fields/Microsoft.VSTS.Common.AcceptanceCriteria"; value = $AcceptanceCriteria })
$ops.Add(@{ op = "add"; path = "/multilineFieldsFormat/Microsoft.VSTS.Common.AcceptanceCriteria"; value = "Markdown" })
}
$ops.Add(@{ op = "add"; path = "/fields/Microsoft.VSTS.Scheduling.StoryPoints"; value = $StoryPoints })
$ops.Add(@{ op = "add"; path = "/fields/Microsoft.VSTS.Common.Priority"; value = $priorityMap[$Priority] })
if ($Tags) {
$ops.Add(@{ op = "add"; path = "/fields/System.Tags"; value = $Tags })
}
$url = "$baseUrl/_apis/wit/workItems/$Id`?api-version=7.1"
$body = $ops | ConvertTo-Json -Depth 6
Write-Host " AB#$Id $Title"
Invoke-RestMethod -Method Patch -Uri $url -Headers $headers -Body $body -ContentType "application/json-patch+json" | Out-Null
Write-Host " -> Updated"
}
# Update-WorkItem calls go here — generated by the push-to-ado skill.
#!/usr/bin/env pwsh
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'
$Org = $env:ADO_ORG
$Project = $env:ADO_PROJECT
if (-not $Org) { Write-Error "ADO_ORG not set"; exit 1 }
if (-not $Project) { Write-Error "ADO_PROJECT not set"; exit 1 }
$baseUrl = "https://dev.azure.com/$Org/$([Uri]::EscapeDataString($Project))"
$priorityMap = @{ Critical = 1; High = 2; Medium = 3; Low = 4 }
Write-Host "Authenticating with Azure..."
$token = az account get-access-token --resource 499b84ac-1321-427f-aa17-267ca6975798 --query accessToken -o tsv
if (-not $token) { Write-Error "az login required"; exit 1 }
$headers = @{ Authorization = "Bearer $token" }
function New-WorkItem {
param(
[string]$Type,
[string]$Title,
[string]$Description,
[string]$AcceptanceCriteria = "",
[int]$StoryPoints = 0,
[string]$Priority = "Medium",
[int]$ParentId = 0,
[string]$Tags = ""
)
$ops = [System.Collections.Generic.List[hashtable]]::new()
$ops.Add(@{ op = "add"; path = "/fields/System.Title"; value = $Title })
$ops.Add(@{ op = "add"; path = "/fields/System.Description"; value = $Description })
$ops.Add(@{ op = "add"; path = "/multilineFieldsFormat/System.Description"; value = "Markdown" })
$ops.Add(@{ op = "add"; path = "/fields/Microsoft.VSTS.Common.Priority"; value = $priorityMap[$Priority] })
if ($AcceptanceCriteria) {
$ops.Add(@{ op = "add"; path = "/fields/Microsoft.VSTS.Common.AcceptanceCriteria"; value = $AcceptanceCriteria })
$ops.Add(@{ op = "add"; path = "/multilineFieldsFormat/Microsoft.VSTS.Common.AcceptanceCriteria"; value = "Markdown" })
}
if ($StoryPoints -gt 0) {
$ops.Add(@{ op = "add"; path = "/fields/Microsoft.VSTS.Scheduling.StoryPoints"; value = $StoryPoints })
}
if ($Tags) {
$ops.Add(@{ op = "add"; path = "/fields/System.Tags"; value = $Tags })
}
if ($ParentId -gt 0) {
$ops.Add(@{
op = "add"
path = "/relations/-"
value = @{
rel = "System.LinkTypes.Hierarchy-Reverse"
url = "$baseUrl/_apis/wit/workItems/$ParentId"
attributes = @{ comment = "" }
}
})
}
$encodedType = [Uri]::EscapeDataString($Type)
$url = "$baseUrl/_apis/wit/workItems/`$$encodedType`?api-version=7.1"
$body = $ops | ConvertTo-Json -Depth 6
Write-Host " [$Type] $Title"
$r = Invoke-RestMethod -Method Patch -Uri $url -Headers $headers -Body $body -ContentType "application/json-patch+json"
$id = $r.id
Write-Host " -> AB#$id $baseUrl/_workitems/edit/$id"
return $id
}
# Work item definitions go here — generated by the push-to-ado skill.
#!/usr/bin/env pwsh
<#
.SYNOPSIS
Creates an Azure DevOps User Story work item from a drafted story.
.PARAMETER Title
Work item title.
.PARAMETER Description
Markdown content for System.Description (story statement, out of scope, dependencies, notes).
.PARAMETER AcceptanceCriteria
Markdown content for Microsoft.VSTS.Common.AcceptanceCriteria (AC blocks only).
.PARAMETER StoryPoints
Fibonacci story point value (1, 2, 3, 5, 8, 13).
.PARAMETER Priority
Priority label: Critical, High, Medium, or Low.
.PARAMETER ParentId
Optional. ADO work item ID of the parent feature to link as parent.
.PARAMETER Tags
Optional. Semicolon-separated list of tags to apply (e.g. "Technical; Frontend").
.PARAMETER Org
Optional. ADO organisation name. Falls back to $env:ADO_ORG.
.PARAMETER Project
Optional. ADO project name. Falls back to $env:ADO_PROJECT.
#>
param(
[Parameter(Mandatory)][string]$Title,
[Parameter(Mandatory)][string]$Description,
[Parameter(Mandatory)][string]$AcceptanceCriteria,
[Parameter(Mandatory)][int]$StoryPoints,
[Parameter(Mandatory)][string]$Priority,
[int]$ParentId,
[string]$Tags,
[string]$Org,
[string]$Project
)
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'
# --- Config resolution ---
if (-not $Org) { $Org = $env:ADO_ORG }
if (-not $Project) { $Project = $env:ADO_PROJECT }
if (-not $Org) {
$Org = Read-Host "ADO organisation name (e.g. 'myorg' for https://dev.azure.com/myorg)"
Write-Host "Tip: set `$env:ADO_ORG in ~/.claude/settings.json to avoid this prompt."
}
if (-not $Project) {
$Project = Read-Host "ADO project name"
Write-Host "Tip: set `$env:ADO_PROJECT in ~/.claude/settings.json to avoid this prompt."
}
$baseUrl = "https://dev.azure.com/$Org/$([Uri]::EscapeDataString($Project))"
# --- Authentication ---
Write-Host "Authenticating with Azure..."
$token = az account get-access-token --resource 499b84ac-1321-427f-aa17-267ca6975798 --query accessToken -o tsv
if (-not $token) {
Write-Error "Failed to get ADO access token. Ensure you are logged in with 'az login'."
exit 1
}
$headers = @{ Authorization = "Bearer $token" }
# --- Priority mapping ---
$priorityMap = @{ Critical = 1; High = 2; Medium = 3; Low = 4 }
$priorityValue = $priorityMap[$Priority]
if (-not $priorityValue) {
Write-Warning "Unknown priority '$Priority', defaulting to Medium (3)."
$priorityValue = 3
}
# --- Build JSON Patch body ---
$patchOps = [System.Collections.Generic.List[hashtable]]::new()
$patchOps.Add(@{ op = "add"; path = "/fields/System.Title"; value = $Title })
$patchOps.Add(@{ op = "add"; path = "/fields/System.Description"; value = $Description })
$patchOps.Add(@{ op = "add"; path = "/multilineFieldsFormat/System.Description"; value = "Markdown" })
$patchOps.Add(@{ op = "add"; path = "/fields/Microsoft.VSTS.Common.AcceptanceCriteria"; value = $AcceptanceCriteria })
$patchOps.Add(@{ op = "add"; path = "/multilineFieldsFormat/Microsoft.VSTS.Common.AcceptanceCriteria"; value = "Markdown" })
$patchOps.Add(@{ op = "add"; path = "/fields/Microsoft.VSTS.Scheduling.StoryPoints"; value = $StoryPoints })
$patchOps.Add(@{ op = "add"; path = "/fields/Microsoft.VSTS.Common.Priority"; value = $priorityValue })
if ($Tags) {
$patchOps.Add(@{ op = "add"; path = "/fields/System.Tags"; value = $Tags })
}
if ($ParentId) {
$parentUrl = "https://dev.azure.com/$Org/$([Uri]::EscapeDataString($Project))/_apis/wit/workItems/$ParentId"
$patchOps.Add(@{
op = "add"
path = "/relations/-"
value = @{
rel = "System.LinkTypes.Hierarchy-Reverse"
url = $parentUrl
attributes = @{ comment = "" }
}
})
}
$body = $patchOps | ConvertTo-Json -Depth 5
# --- Create work item ---
$workItemType = "User Story"
$encodedType = [Uri]::EscapeDataString($workItemType)
$createUrl = "$baseUrl/_apis/wit/workItems/`$$encodedType`?api-version=7.1"
Write-Host "Creating '$workItemType' in ADO..."
$response = Invoke-RestMethod `
-Method Patch `
-Uri $createUrl `
-Headers $headers `
-Body $body `
-ContentType "application/json-patch+json"
$itemId = $response.id
$itemUrl = "https://dev.azure.com/$Org/$([Uri]::EscapeDataString($Project))/_workitems/edit/$itemId"
Write-Host ""
Write-Host "Work item created: AB#$itemId"
Write-Host $itemUrl