.claude/skills/ado/SKILL.md
Generic Azure DevOps operations — edit tickets, query work items, add comments, change fields, move sprints, assign users, link items, and more. Triggers on "ado", "edit ticket", "update ticket", "move ticket", "assign ticket", "link ticket", "query ado", "search ado", "add comment to ticket", or ticket number mentions with edit/update intent.
npx skillsauth add Vvanlaar/orch adoInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Perform any Azure DevOps operation: edit fields, add comments, query/search work items, manage links, move sprints, assign users, etc.
Read
~/.claude/skills/_ado-shared.mdfor auth, env vars, fetch/update ticket patterns, and Windows/MSYS2 notes.
The user's input may contain:
#12345, 12345, ADO 12345, ticket 12345set state to resolved, assign to [email protected], change title to "..."add comment "...", comment on ticketfind tickets assigned to me, search for X, list bugs in current sprintlink 12345 to 12346, add parent 12300Extract the intent and parameters before executing.
BBNew\Core unless the user explicitly specifies a different area path.Fetch ticket first (to preserve existing values), then PATCH:
source ~/.env && B64=$(printf ':%s' "$ADO_PAT" | base64 -w0)
# Fetch current values
curl -s -H "Authorization: Basic $B64" \
"https://dev.azure.com/bluebillywig/BBNew/_apis/wit/workitems/<ID>?api-version=7.0"
Common fields and their API paths:
| Field | Path | Example Values |
|-------|------|----------------|
| Title | /fields/System.Title | Any string |
| State | /fields/System.State | New, In Progress, Resolved, Closed |
| Assigned To | /fields/System.AssignedTo | Email address |
| Area Path | /fields/System.AreaPath | Default: BBNew\Core |
| Iteration Path | /fields/System.IterationPath | BBNew\Sprints 2026\Q1\Sprint 8.45 |
| Description | /fields/System.Description | HTML string |
| Tags | /fields/System.Tags | Semicolon-separated: tag1; tag2 |
| Priority | /fields/Microsoft.VSTS.Common.Priority | 1, 2, 3, 4 |
| Severity | /fields/Microsoft.VSTS.Common.Severity | 1 - Critical, 2 - High, 3 - Medium, 4 - Low |
| Test Notes | /fields/BB.Testnotes | HTML string |
| Repository | /fields/Custom.Repository | Semicolon-separated repo names |
| Resolution | /fields/Microsoft.VSTS.Common.Resolution | HTML string (usually PR link) |
| Effort | /fields/Microsoft.VSTS.Scheduling.Effort | Numeric (story points / effort hours) |
| Remaining Work | /fields/Microsoft.VSTS.Scheduling.RemainingWork | Numeric (hours) |
Use write-payload pattern from shared (node heredoc + cygpath + curl PATCH).
For HTML fields (Description, Test Notes): always fetch existing value first and append/merge — never blindly replace.
source ~/.env && B64=$(printf ':%s' "$ADO_PAT" | base64 -w0)
node << 'NS'
const fp = require('path').join(require('os').tmpdir(), 'ado-payload.json');
require('fs').writeFileSync(fp, JSON.stringify({ text: '<p>COMMENT_HTML</p>' }));
NS
WINPATH=$(cygpath -w "$LOCALAPPDATA/Temp/ado-payload.json")
curl -s -H "Authorization: Basic $B64" -X POST \
-H "Content-Type: application/json" \
"https://dev.azure.com/bluebillywig/BBNew/_apis/wit/workitems/<ID>/comments?api-version=7.0-preview.3" \
-d @"$WINPATH"
source ~/.env && B64=$(printf ':%s' "$ADO_PAT" | base64 -w0)
node << 'NS'
const fp = require('path').join(require('os').tmpdir(), 'ado-payload.json');
require('fs').writeFileSync(fp, JSON.stringify({
query: "SELECT [System.Id], [System.Title], [System.State], [System.AssignedTo] FROM workitems WHERE [System.TeamProject] = 'BBNew' AND <CONDITIONS> ORDER BY [System.Id] DESC"
}));
NS
WINPATH=$(cygpath -w "$LOCALAPPDATA/Temp/ado-payload.json")
curl -s -H "Authorization: Basic $B64" -X POST \
-H "Content-Type: application/json" \
"https://dev.azure.com/bluebillywig/BBNew/_apis/wit/wiql?api-version=7.0&\$top=50" \
-d @"$WINPATH"
WIQL returns only IDs. Fetch details in batch:
# Extract IDs and fetch batch (max 200)
curl -s -H "Authorization: Basic $B64" -X POST \
-H "Content-Type: application/json" \
"https://dev.azure.com/bluebillywig/BBNew/_apis/wit/workitemsbatch?api-version=7.0" \
-d @"$WINPATH"
Batch payload: { "ids": [1,2,3], "fields": ["System.Id","System.Title","System.State","System.AssignedTo"] }
Common WIQL conditions:
| Intent | WIQL Condition |
|--------|---------------|
| Assigned to me | [System.AssignedTo] = @Me |
| Assigned to user | [System.AssignedTo] = '[email protected]' |
| Current sprint | [System.IterationPath] = @CurrentIteration |
| State filter | [System.State] = 'In Progress' |
| Bugs only | [System.WorkItemType] = 'Bug' |
| Title contains | [System.Title] CONTAINS 'search term' |
| Tag filter | [System.Tags] CONTAINS 'tag-name' |
| Area path | [System.AreaPath] UNDER 'BBNew\Core' |
| Created recently | [System.CreatedDate] >= @Today - 7 |
Link types:
System.LinkTypes.Hierarchy-Forward — Parent → ChildSystem.LinkTypes.Hierarchy-Reverse — Child → ParentSystem.LinkTypes.Related — Relatednode << 'NS'
const fp = require('path').join(require('os').tmpdir(), 'ado-payload.json');
require('fs').writeFileSync(fp, JSON.stringify([
{
op: 'add',
path: '/relations/-',
value: {
rel: 'System.LinkTypes.Related',
url: 'https://dev.azure.com/bluebillywig/BBNew/_apis/wit/workitems/TARGET_ID'
}
}
]));
NS
WINPATH=$(cygpath -w "$LOCALAPPDATA/Temp/ado-payload.json")
source ~/.env && B64=$(printf ':%s' "$ADO_PAT" | base64 -w0)
curl -s -H "Authorization: Basic $B64" -X PATCH \
-H "Content-Type: application/json-patch+json" \
"https://dev.azure.com/bluebillywig/BBNew/_apis/wit/workitems/<ID>?api-version=7.0" \
-d @"$WINPATH"
To remove a link: fetch ticket with $expand=relations, find the relation index, then:
[{ "op": "remove", "path": "/relations/<INDEX>" }]
Fetch current sprint path first if needed (see shared), then update Iteration Path:
[{ "op": "replace", "path": "/fields/System.IterationPath", "value": "BBNew\\Sprints 2026\\Q1\\Sprint 8.45" }]
source ~/.env && B64=$(printf ':%s' "$ADO_PAT" | base64 -w0)
node << 'NS'
const fp = require('path').join(require('os').tmpdir(), 'ado-payload.json');
require('fs').writeFileSync(fp, JSON.stringify([
{ op: 'add', path: '/fields/System.Title', value: 'TITLE' },
{ op: 'add', path: '/fields/System.AreaPath', value: 'BBNew\\Core' }, // ALWAYS default to BBNew\Core
{ op: 'add', path: '/fields/System.Description', value: '<p>DESCRIPTION</p>' }
]));
NS
WINPATH=$(cygpath -w "$LOCALAPPDATA/Temp/ado-payload.json")
curl -s -H "Authorization: Basic $B64" -X POST \
-H "Content-Type: application/json-patch+json" \
"https://dev.azure.com/bluebillywig/BBNew/_apis/wit/workitems/\$Task?api-version=7.0" \
-d @"$WINPATH"
Replace $Task with: $Bug, $User%20Story, $Feature, $Epic, etc.
For updating multiple tickets, loop through IDs:
for ID in 12345 12346 12347; do
# ... PATCH each
done
After any operation, report concisely:
## ADO #<ID> — <title>
**Action:** <what was done>
- ✅ <field>: <old value> → <new value>
- ✅ Comment added
- ✅ Linked to #<other>
[View ticket](https://dev.azure.com/bluebillywig/BBNew/_workitems/edit/<ID>)
For queries:
## ADO Query Results
| # | Title | State | Assigned To |
|---|-------|-------|-------------|
| 12345 | Fix the thing | In Progress | [email protected] |
| Mistake | Fix |
|---------|-----|
| Replacing HTML fields blindly | Always fetch existing value first, append/merge |
| Using curl -u | Use manual base64 auth header (see shared) |
| Inline -d with HTML | Write to temp file via node heredoc |
| Wrong op on create vs update | add for POST (new), replace for PATCH (existing) |
| Forgetting $expand=all | Add when you need relations/comments |
| WIQL @Me not working | Only works with authenticated user; use explicit email for curl |
| Escaping $ in work item type URL | Use \$Task in bash to prevent variable expansion |
| Setting AreaPath to BBNew | Always use BBNew\Core (not root BBNew) |
tools
Sync GitHub repos from bluebillywig org to an ADO "Repository" picklist field on all work item types
development
Build a new desktop app release (Electron). Triggers on "build desktop", "new release", "desktop release", "build the app"
testing
Use when testing and verifying a bug fix or feature for an ADO ticket. Triggers on /ado-test, "test ticket", or requests to test a ticket number.
development
Use when starting work on an Azure DevOps ticket. Triggers on "start ticket", "work on ticket", ticket number with implementation intent, or requests to plan a feature from a ticket.