ado Azure DevOps CLI

The Azure DevOps CLI
shaped like gh, built for agents

ado is a single self-contained binary for macOS, Linux and Windows that manages Azure DevOps — projects, repositories, work items, pipelines, pull requests and 19 more service areas — with the command vocabulary, JSON surface and exit codes that gh taught agents to expect.

npm recommended
npm install -g @gilbertwong1996/ado --foreground-scripts

Works on macOS, Linux, and Windows. Other install methods →

CI status Requires Rust 1.98 or newer Licensed under Apache-2.0
Version
0.7.0
Commands
150 across 24 service areas
Binary
~5 MiB, no runtime dependency
Startup
about 6 ms (measured)
Platforms
macOS arm64/x86_64, Linux x86_64/aarch64, Windows x86_64
License
Apache-2.0

What it does

One binary covers the Azure DevOps surface a team touches daily, and the command names are the ones gh uses wherever the concept maps.

24 service areas, 150 commands

Projects, repositories, branches, branch policies, work items and their comments and attachments, pull requests, review comments and reviewers, pipelines, builds, artifacts, variable groups, secure files, releases, packages, wikis, teams, users, areas, iterations, agent pools and queues, service connections, extensions, banners, test results and coverage, search and configuration.

gh's shape, name for name

Singular nouns, gh's verbs, one level deep: ado pr view, ado pr edit, ado work-item list, ado pr-comment add, ado pr merge. The flags transfer too — -R names the repository, -w opens the page, -q and -t format the output, -L caps a list. An agent that knows gh already knows the grammar.

3 auth methods

Personal Access Token, browser OAuth with PKCE, and device code. MSA personal accounts (*.visualstudio.com) are supported, and ado auth token hands the stored credential to git or curl. No az CLI dependency.

Cross-platform

Five native archives — macOS arm64 and x86_64, Linux x86_64 and aarch64 (musl), Windows x86_64 — built on native runners with cargo-dist.

CI/CD friendly

ado ci watch PROJECT BUILD_ID streams build logs in real time and exits 2 when the build is cancelled. Per-run variables, variable groups, PR automation, package publishing.

An escape hatch for the rest

ado api /_apis/anything -X post -f key=value sends one authenticated request to any endpoint, prints the server's body as it arrived, and exits with the server's status.

Why "AI-native"?

Most CLIs are designed for humans and retrofitted for agents with a --json flag that dumps everything. ado is designed the other way around: the machine surface is the primary one, and the table is the convenience.

How ado's human output and its agent-facing output differ
Feature Human-friendly AI-agent-friendly
Output Bordered table on a terminal; tab-separated rows, no header, when piped Stable JSON envelope on every command
Field selection — --json=number,title prints only those fields — gh's vocabulary where a catalog maps it, the payload's own keys on every other command
Post-processing Pipe to your own tools -q/--jq EXPR and -t/--template TPL, compiled in-process
Discovery ado --help at every level ado schema --json — the whole tree, 47 groups and 150 leaves, in one round trip
Documentation Embedded help pages ado skills list / ado skills read NAME --json
Errors One-line [Code] message on stderr {ok: false, error: {code, status, message, details}}
Exit codes 0 or 1 0 success, 1 failure, 2 cancelled watch, 4 no credential
Auth Browser OAuth (interactive) PAT, device code, browser — plus ado auth token to hand the credential on

Field-selected JSON in one command

$ ado pr list Alpha Core --json=number,title
[
  { "number": 137, "title": "Add payment retries" },
  { "number": 138, "title": "Retire the legacy ledger" }
]

$ ado pr list Alpha Core --json -q '.[] | select(.pullRequestId > 137) | .title'
Retire the legacy ledger

Errors that agents can match on

$ ado project view nonexistent --json
{
  "ok": false,
  "error": {
    "code": "not_found",
    "message": "Resource not found. Check the project/repo/build ID and your permissions.",
    "status": 404,
    "details": { "body": "..." }
  }
}

Every error carries a stable code from a set of ten: auth_required, not_logged_in, not_found, forbidden, conflict, validation_error, api_error, network_error, cancelled, unknown. status and details appear only when the error has them. An agent matches on error.code and the exit status, never on English text.

ado vs the official az devops

Both talk to the same REST API. They are built for different things: the Azure CLI is Microsoft's supported, complete client for every Azure service; ado is a small, fast, agent-shaped client for Azure DevOps alone. Neither wins outright.

ado and the official Azure DevOps CLI compared
Dimension ado az devops (official)
Install One ~5 MiB binary via npm or a release archive; no runtime Python plus the Azure CLI package; installed by a system package manager, installer or pip
Startup About 6 ms Python interpreter start-up, then the CLI's own imports — noticeably longer for a one-off command
Command shape ado <noun> <verb>, one level, gh's vocabulary az devops <area> <verb> and siblings such as az boards, az pipelines, az repos
Machine output Envelope on every command, field selection, --jq, --template, TSV when piped --output json|table|tsv; no field selection and no in-process jq
Discovery for agents ado schema --json dumps the command tree; embedded skills install into five agent directories az find and --help; no machine-readable command tree of this kind
Errors Stable error.code plus exit 4 for "no credential" Human-readable messages; scripts parse text
Coverage 150 commands over 24 Azure DevOps areas, plus ado api for anything unmodelled Every Azure DevOps API, and every other Azure service besides
Sign-in PAT, browser OAuth, device code PAT, browser, service principal and managed identity
Support One maintainer, Apache-2.0, community issues Microsoft-supported, with a support plan and an extension ecosystem

Choose ado when…

  • The caller is a script or an LLM agent: field-selected JSON, in-process jq, TSV and exit 4 are there without extra glue.
  • You want a single binary on a CI runner — no Python, no az install step, about 6 ms to first output.
  • The caller already knows gh: the verbs and nouns transfer.
  • You need one endpoint the CLI does not model: ado api prints the server's answer verbatim.

Choose az devops when…

  • You need service-principal or managed-identity sign-in. ado does not offer it.
  • You need the long tail of Azure DevOps APIs, or any other Azure service in the same script.
  • You want a vendor-supported tool with a support contract.
  • Your scripts already call az and work; there is no reason to port them.

Running both is normal: ado never reads or writes az's credentials, so the two coexist on one machine without interfering.

For LLM agents

The binary carries three skills — ado-cli, ado-auth and ado-ci — that teach an agent how to use it. Install them once and the agent loads them at startup, with no shell-out to discover the surface.

$ ado skills install

  Installing 3 skills to 4 target(s):
    - claude: ~/.claude/skills
    - codex:  ~/.codex/skills
    - cursor: ~/.cursor/skills
    - pi:     ~/.pi/agent/skills

  Note: copilot installs per-repo (to <repo>/.github/ado-cli/).

  Installed: 12
  Skipped:   0
  Errors:    0

$ ls ~/.pi/agent/skills
ado-cli/  ado-auth/  ado-ci/

What the skills teach

ado-cli

The command reference for all 24 service areas, plus the output contract: field selection, --jq, Go templates, TSV, exit codes. The big one — load it when the agent first meets ado.

ado-auth

Every sign-in path — PAT, browser OAuth, device code — the resolution order, and what to do with exit 4. No az dependency.

ado-ci

CI/CD patterns: trigger pipelines, manage variable groups, automate PRs, publish packages, and branch on the exit codes a pipeline cares about.

Or — discover dynamically

An agent that installs nothing can still read the whole surface in one call:

$ ado schema --json | jq '.schema.subcommands | length'
47

$ ado schema "pr list" --json | jq -c '.schema.jsonFields[:5]'
["number","id","title","state","author"]

Other install methods

Prefer a single binary over npm? Download the one that matches your platform from the latest release, or build from source.

📥 Download binary

Grab the archive for your platform and put the binary on your $PATH.

curl -L -o ado.tar.gz https://github.com/gilbertwong96/ado_cli/releases/latest/download/ado-aarch64-apple-darwin.tar.gz
tar -xzf ado.tar.gz ado-aarch64-apple-darwin/ado
chmod +x ado-aarch64-apple-darwin/ado && sudo mv ado-aarch64-apple-darwin/ado /usr/local/bin/

Replace ado-aarch64-apple-darwin.tar.gz with the archive for your platform (see the release page).

🔨 Build from source

Requires Rust 1.98+. Use this if you want to hack on ado itself.

git clone https://github.com/gilbertwong96/ado_cli && cd ado_cli
cargo build --release --locked
cp target/release/ado /usr/local/bin/

Quick start

# 1. Authenticate (pick one)
ado auth login                                          # browser OAuth, org auto-detected
ado auth login --method pat --org myorg --pat TOKEN     # Personal Access Token
ado auth login --method device                          # no browser needed

# 2. Set the default organization once
ado config set default_org myorg

# 3. List projects
ado project list

# 4. Machine-readable output for a script or an agent
ado project list --json=name,state
ado pr list myorg myrepo -q '.[].title'

# 5. Create a work item
ado work-item create myproject --type Bug --title "Fix login page"

# 6. Name the repository once, gh-style
ado pr list -R myproject/myrepo

# 7. Search work items, code or wiki pages
ado search code "retry backoff" --project myproject

# 8. Open the page a command is about, instead of printing it
ado pr view -R myproject/myrepo 137 --web

# 9. Watch a pipeline run in real time
ado ci watch myproject 12345

# 10. Anything the CLI does not model
ado api "/myproject/_apis/git/repositories?api-version=7.1" -i

# 11. Install skills to your LLM agent
ado skills install --target pi