Skip to content

Repository files navigation

azwi

azwi fetches Azure DevOps work items and turns them into clean context for coding agents. It returns descriptions, acceptance criteria, comments, attachments, and linked pull requests as deterministic JSON or readable Markdown.

The CLI is designed for both people and agents. Successful output stays on stdout. Logs, progress, and maintenance notices go to stderr.

Prerequisite

azwi is designed to be used with uv. Install uv before continuing. The documented workflows and managed agent skill use uvx to run the tool without requiring a global installation.

Quick start with an agent

  1. Install uv using the link above.
  2. For automatic authentication, install Azure CLI if it is not already available. A manually supplied PAT also works without Azure CLI.
  3. Run setup using the PowerShell or Bash commands below and choose an authentication method. Replace the example URL with a work item you can access.
  4. Use $azure-workitem in your agent.

PowerShell:

uvx azwi setup "https://dev.azure.com/my-org/Payments/_workitems/edit/2195"

Bash or zsh:

uvx azwi setup "https://dev.azure.com/my-org/Payments/_workitems/edit/2195"

Setup extracts your organization, verifies the supplied work item through a normal fetch, saves the configuration, and installs the managed agent skill. When no authentication is already configured, setup offers these methods:

Method Behavior
managed-pat Default menu choice. Azure CLI signs you in so azwi can create and renew an organization-specific PAT with Work Items: Read and Code: Read. Ordinary fetches use the saved PAT without starting Azure CLI.
entra Azure CLI supplies a temporary Microsoft Entra token. azwi keeps it in memory for the command and does not save a token. It uses your account's Azure DevOps permissions.
manual-pat Paste a PAT at a masked terminal prompt. You manage its expiration and replacement. Azure CLI is not required.
environment Use AZWI_PAT from the execution environment. azwi prints instructions but does not set or persist shell variables.

PATs are saved in ~/.azwi/credentials.toml, separately from non-secret settings in ~/.azwi/config.toml. A non-empty AZWI_PAT overrides every saved method and is never copied to disk automatically. Unset it before configuring a different method.

Bare uvx azwi setup asks for a work item URL or organization name. You can also use uvx azwi setup --org my-org. Azure CLI methods verify organization access even without a work item URL. A URL additionally verifies comments and linked PR metadata through the normal fetch. No project default is required.

Then use $azure-workitem in Codex, Claude Code, or another agent harness that supports skills:

Use $azure-workitem to inspect 2195. Summarize the requested change, acceptance criteria, and any relevant pull request discussion.

The skill accepts numeric IDs and supported Azure DevOps Cloud URLs. It requests PR comments and downloads only when needed. Start a new agent session if the installed skill is not yet available.

Microsoft sign-in and automatic renewal

Select a method explicitly to skip the method menu:

uvx azwi setup "<work-item-url>" --auth managed-pat
uvx azwi setup "<work-item-url>" --auth entra

azwi first tries your existing Azure CLI session. If Microsoft requires sign-in, setup offers normal sign-in, device-code sign-in, another authentication method, or cancellation. After you choose sign-in, Azure CLI owns the account window, browser, or device-code instructions. Keep the original terminal open. When Azure CLI finishes, azwi automatically checks the account, verifies access, and completes setup. You do not copy a token or rerun the original command. Sign-in also updates your shared Azure CLI session.

The handoff runs az login --tenant TENANT_ID --scope 499b84ac-1321-427f-aa17-267ca6975798/.default --allow-no-subscriptions --output none. The subscription picker is disabled only for that child process. Azure CLI uses its normal platform sign-in experience. Device-code sign-in adds --use-device-code. Both child output streams go to azwi's stderr, keeping stdout available for the final JSON report. Ctrl+C cancels the handoff. azwi also stops it after five minutes. On Windows, child process containment also stops Azure CLI descendants if the terminal or launcher terminates azwi abruptly. In that case the launcher may determine the exit code.

azwi discovers the organization's tenant when possible. Use --tenant TENANT_ID if discovery is unavailable or the account has access to several tenants. Use --login-method device-code to prefer device-code sign-in when login is needed. Setup saves the verified tenant and Azure DevOps identity per organization. A later account mismatch fails without changing the managed PAT.

Managed PATs request a 30-day lifetime and renew when used within seven days of expiration. Repeating setup reuses a healthy managed PAT. You can configure the lifetime and renewal window:

uvx azwi setup --org my-org --auth managed-pat --pat-lifetime-days 30 --renew-before-days 7

Organization policy may require a shorter lifetime or prohibit PAT creation. Active PAT renewal preserves the secret. Failed early renewal uses the still-valid PAT, reports the repair command, and waits an hour before trying again. An expired managed PAT is replaced and verified before being saved. azwi attempts to revoke the old token afterward. These operations happen when the tool runs, with no background service. Run setup in your terminal when sign-in is required. Manually supplied and environment PATs are never renewed automatically.

Entra mode invokes Azure CLI for each command, then shares that token across the command's API calls. Azure CLI can reuse its own cache without interactive sign-in. azwi does not maintain an additional token cache. In a local investigation, cached acquisition added about two seconds per invocation. This varies by machine. Managed PAT fetches avoid that cost outside the renewal window.

An interactive first fetch with missing configuration offers setup and resumes the fetch on success. For agents and automation, always use --non-interactive with fetches and fields. It prevents prompts and sign-in windows even when a terminal is attached. The managed skill includes this flag. Missing or unusable authentication returns an error with setup guidance.

Existing users and changing authentication methods

Existing saved PATs and AZWI_PAT continue to work after upgrading. Existing manual PATs stay manually managed. Upgrading does not replace them or enable automatic renewal. Setup reuses the configured method unless you explicitly select another with --auth.

To switch to managed PAT authentication, unset AZWI_PAT in the terminal if it is set, then run:

uvx azwi setup "https://dev.azure.com/my-org/Payments/_workitems/edit/2195" --auth managed-pat

Use --auth entra instead for temporary Azure CLI tokens. Use --auth manual-pat to return to a manually managed PAT, or --auth environment to require AZWI_PAT for that organization. These choices are saved per organization. They do not change other organizations' authentication methods. Supplying a work item URL verifies the selected method before completing setup.

After upgrading, start a new agent session to pick up the updated skill's non-interactive commands. Installed releases update pristine older managed skills automatically. Local source builds and custom skill locations require an explicit skill install. See Manage the agent skill.

Credentials and environment overrides

Saved credentials use one PAT per organization:

[orgs."my-org"]
pat = "<your-pat>"

Setup creates this file for you. It stores the PAT as plain text with normal inherited filesystem permissions. It does not apply special restrictions or change ACLs. Keep it out of repositories and shared config exports. config show, setup reports, and diagnostics never display saved PATs.

Managed entries also contain the authorization ID, expiry, tenant, identity, and optional renewal retry time. azwi uses atomic replacement and a credential-file lock when updating them. Other organization entries are preserved. The selected method and non-secret settings live under [orgs."my-org".auth] in config.toml.

To create a manual PAT, open Azure DevOps User settings > Personal access tokens and select Work Items: Read and Code: Read. See Microsoft's PAT instructions. Choose an expiration that fits your organization's policy. A longer lifetime reduces renewal interruptions. Use a shorter lifetime when the information or environment calls for it. Enter it with uvx azwi setup "<work-item-url>" --auth manual-pat.

A non-empty AZWI_PAT overrides the saved token. This also works if the credentials file is unavailable or malformed. If file storage is unavailable, set the variable in the environment where azwi runs:

PowerShell:

$env:AZWI_PAT = "<your-pat>"
uvx azwi setup "https://dev.azure.com/my-org/Payments/_workitems/edit/2195"

Bash or zsh:

export AZWI_PAT="<your-pat>"
uvx azwi setup "https://dev.azure.com/my-org/Payments/_workitems/edit/2195"

These assignments affect the current shell and its child processes. An already-running agent application does not receive the change. Configure the environment where the agent actually executes uvx, including remote or sandboxed environments. Setup cannot change its parent shell's environment. Do not paste the PAT into an agent conversation.

On Windows, you can optionally persist the current value for your own account without administrator rights:

[Environment]::SetEnvironmentVariable("AZWI_PAT", $env:AZWI_PAT, "User")

Future applications must inherit the updated environment. On Linux and macOS, persistent shell variables usually belong in the appropriate user shell startup file. Bash login shells and interactive shells read different files. Shell configuration does not automatically configure every desktop application. Persisting an environment variable this way stores the PAT as an ordinary setting. Environment variables are not encrypted secret storage.

Check or repair setup

uvx azwi config check
uvx azwi config check "https://dev.azure.com/my-org/Payments/_workitems/edit/2195"

config check reports the effective organization and its source, authentication method, credential source, known expiry, credentials file path, skill status, and next steps. Without a URL it only checks local readiness and never starts Azure CLI. A locally configured Entra profile does not prove that its session still works. With a URL it performs a default fetch and may silently acquire an Entra token. Checks never launch sign-in, renew or create PATs, or modify azwi files. Azure CLI may update its own cache during token acquisition. Run checks through the agent when diagnosing differences between terminal and agent access. A verified work item without returned linked PRs does not establish Code access.

For managed PAT or Entra sign-in repair, rerun uvx azwi setup "<work-item-url>" in your terminal. If a manually supplied PAT expires or is rejected, add --replace-pat. Setup verifies a replacement before saving it. If you use AZWI_PAT, update its value in the execution environment instead. Unset it before using --replace-pat. Make sure the token applies to the selected organization and has both required scopes. Rejected credentials never cause a silent switch to another authentication method.

Setup and checks return JSON by default. Add --format plain for a compact text report. Setup installs the skill by default and respects existing managed-skill protections. Use --skills-dir DIR with either command for a custom skill root. setup --non-interactive can use a saved PAT, AZWI_PAT, or a usable Azure CLI session for an explicitly selected or already configured Azure CLI method. It never prompts or launches sign-in. Without a configured method or PAT, it saves the organization and installs the skill, then returns exit code 4 with ready: false and repair instructions. A failure to save an entered PAT also returns incomplete readiness and environment-variable instructions. Sandboxed and remote agents need access to the credentials or Azure CLI session in their own execution environment.

What it returns

The default JSON output is stable and designed for agent parsing. Markdown output is designed for reading or adding directly to a prompt.

A Markdown result looks like this:

# 2195 Login bug

# Metadata

- Type: Bug
- State: Active
- Assigned To: Alice
- Changed Date: 2026-03-10T10:00:00Z

# Description:

Main **issue**

## Repro Steps

1. Open app
2. Click sign in

# Acceptance Criteria:

Should be fixed

JSON always contains top-level work_item metadata and a sections object. Text fields contain both rendered Markdown and the Azure DevOps field reference name. Raw HTML is not included.

Format Best for
json Agent tools, scripts, and automation
markdown Reading, saved context, and direct prompt input

Use the CLI directly

Fetch a work item without installing the package globally:

uvx azwi 2195 --org my-org

Save the organization as a default so later calls need only the work item ID:

uvx azwi config set-defaults --org my-org
uvx azwi 2195

Request Markdown instead of the default JSON:

uvx azwi 2195 --format markdown

Write the result to a file:

uvx azwi 2195 --format markdown --output work-item-2195.md

Existing output files are preserved unless --force is supplied.

To install the command as a persistent tool:

uv tool install azwi
azwi 2195

The examples below continue to use uvx azwi so they work without a global installation.

How fetching works

The fetch model has four core rules:

  1. A work item ID is looked up within an Azure DevOps organization.
  2. The organization comes from --org, user config, or AZWI_ORG.
  3. The fetched work item's System.TeamProject field determines the project used for field mappings and follow-up requests.
  4. Requested sections are returned in a fixed order and failures stop the command instead of producing partial output.

The main interface is:

azwi <work_item_id> [options]

There is no fetch subcommand and no --project option for direct work item lookup. Project selection remains available for project-scoped commands such as fields.

Choose the context you need

Without --section, azwi returns all standard sections. Repeat --section to request a smaller result.

Goal Command
Fetch all default context uvx azwi 2195
Fetch acceptance criteria only uvx azwi 2195 --section acceptance
Fetch metadata and comments uvx azwi 2195 --section metadata --section comments
Increase the comment limit uvx azwi 2195 --section comments --comment-limit 20
Include all linked PR states uvx azwi 2195 --section prs --pr-status all
Add a custom field once uvx azwi 2195 --extra-field Custom.DevNotes
Save prompt-ready Markdown uvx azwi 2195 --format markdown --output work-item-2195.md

Available sections:

Section Content
metadata Type, state, assignee, and changed date
description Description plus bug repro steps and system information
acceptance Acceptance criteria
comments Work item discussion, newest first
attachments Attachment names, URLs, comments, sizes, and local paths when downloaded
prs Linked pull requests and optional review discussion

Section output order is fixed by the tool, not by the order of --section arguments.

The comment limit defaults to 10 and accepts values from 1 through 50. Linked pull request metadata defaults to active PRs. Requested section keys remain present in JSON even when their content is empty.

Configure defaults and fields

azwi stores non-secret defaults and field mappings in ~/.azwi/config.toml. Use the CLI to manage the common settings:

uvx azwi config show
uvx azwi config set-defaults --org my-org --project Payments
uvx azwi config set-field --global --acceptance Microsoft.VSTS.Common.AcceptanceCriteria
uvx azwi config set-field --project Payments --description Custom.DevDescription
uvx azwi config add-extra-field --project Payments Custom.ReleaseNotes

config show displays the effective resolved configuration. Config updates create the file when needed and never write AZWI_PAT into it. config check does not create or update azwi configuration, credentials, or skill files.

Settings are resolved in this order:

  1. Explicit CLI flags
  2. Matching project-specific config
  3. Matching organization-specific config
  4. Top-level config defaults
  5. Environment variables
  6. Built-in defaults

The common single-organization configuration looks like this:

[defaults]
org = "my-org"
project = "Payments"

[defaults.fields]
description = "System.Description"
acceptance = "Microsoft.VSTS.Common.AcceptanceCriteria"
repro_steps = "Microsoft.VSTS.TCM.ReproSteps"
system_info = "Microsoft.VSTS.TCM.SystemInfo"

[projects."Payments".fields]
extra_fields = ["Custom.DevNotes"]

Organization-specific profiles are also supported:

[orgs."other-org".defaults]
project = "ProjectX"

[orgs."other-org".defaults.fields]
acceptance = "Custom.Acceptance"

[orgs."other-org".projects."ProjectY".fields]
extra_fields = ["Custom.ReleaseNotes"]

Discover and override fields

List the available field reference names for a work item type:

uvx azwi fields --type Bug --project Payments
uvx azwi fields --type "User Story" --project Payments

Override logical fields for one invocation:

uvx azwi 2195 --field-description Custom.DevDescription
uvx azwi 2195 --field-acceptance Custom.Acceptance
uvx azwi 2195 --field-repro-steps Custom.ReproSteps
uvx azwi 2195 --field-system-info Custom.SystemInfo

Use repeatable --extra-field REFNAME options to add fields without replacing the standard sections. Markdown labels extra fields by reference name. JSON returns them in the extra_fields object.

Download attachments and images

The attachments section lists attachment metadata without downloading files. Downloads are always explicit:

uvx azwi 2195 --download-attachments work-item-2195-files

--download-attachments DIR automatically includes the attachments section. Without selectors, it downloads every work item attachment.

Use repeatable exact-match selectors to list or download specific attachments:

uvx azwi 2195 --section attachments --attachment-name notes.txt
uvx azwi 2195 --download-attachments files --attachment-url https://dev.azure.com/...

The attachment output contains exact name and url values for follow-up calls. If any selector does not match, the command fails instead of silently returning a partial result.

Relative download directories resolve from the current working directory. With --output, rendered attachment paths are relative to the output file location. Without --output, they are relative to the current working directory when possible.

Use --download-images DIR with --output to download remote images found in rendered Markdown and rewrite their links to local relative paths:

uvx azwi 2195 --format markdown --output work-item-2195.md --download-images work-item-2195-images

Relative image directories also resolve from the current working directory. Image downloading without --output is a usage error.

Include pull request discussion

The prs section lists linked pull requests. PR thread comments are high-volume context and remain opt-in:

uvx azwi 2195 --include-pr-comments

This option automatically includes the prs section. Active threads are included by default. Include active and resolved threads with:

uvx azwi 2195 --include-pr-comments --pr-comment-status all

Azure DevOps system comments are excluded unless --include-pr-system-comments is supplied.

Manage the agent skill

The standard skill location is ~/.agents/skills/azure-workitem/SKILL.md.

uvx azwi skill install
uvx azwi skill status
uvx azwi skill status --format plain
uvx azwi skill remove

Skill commands return JSON by default. All three commands accept --skills-dir DIR for a custom skills root. The older install-skill, skill-status, and remove-skill command aliases remain available.

Normal invocations of an installed release automatically update an older managed skill when its installed content is unchanged. Synchronization is local. It does not query PyPI, refresh uv's cache, or update the CLI. Missing skills, unmanaged skills, modified managed skills, equal versions, and newer versions are left alone.

Inspect version, integrity, and update eligibility before replacing modified managed content:

uvx azwi skill status
uvx azwi skill install --force

Install-time --force can replace altered content only when the skill is managed by azwi. It never overwrites an unmanaged skill or downgrades a newer version.

Automatic synchronization checks only the standard location. Local checkouts, direct source installs, editable builds, and custom locations require explicit skill commands. Updates affect future agent sessions and may not change instructions already loaded by a running agent.

skill remove removes the managed SKILL.md and its directory when empty. It preserves unrelated files. Removing unmanaged content requires --force. Installation and removal refuse linked paths and unexpected file types.

Reference

Useful discovery and metadata commands:

uvx azwi --help
uvx azwi 2195 --help
uvx azwi fields --help
uvx azwi config --help
uvx azwi setup --help
uvx azwi config check --help
uvx azwi skill --help
uvx azwi --about
uvx azwi version

Environment variables:

Variable Purpose
AZWI_PAT Overrides every saved authentication method. Otherwise use the selected organization's configured method, with manual PAT lookup as the legacy default
AZWI_ORG Default organization for fetch and fields
AZWI_PROJECT Default project for project-scoped commands such as fields

Exit codes:

Code Meaning
0 Success
2 Usage or input error
3 Configuration error
4 Authentication error
5 Work item or resource not found
6 Azure DevOps API error
7 Throttling retries exhausted

These codes describe exits controlled by azwi. Cancelling at the Microsoft sign-in menu returns 4. Ctrl+C can cause a terminal or launcher to terminate the command and report a different nonzero status, such as -1 in PowerShell on Windows. Treat any nonzero status as an unsuccessful command. Cancellation does not produce a success report, and Azure CLI login processes are cleaned up when azwi exits.

uvx azwi --about prints the command name and version, a short summary, the project URL, and the MIT license. The project source is available on GitHub.

Development and release

The repository supports both execution paths required by the project:

  • uv run ./azwi.py ... through PEP 723 metadata in the root wrapper
  • uvx azwi ... through the package defined in pyproject.toml

Run the CLI from a checkout:

uv run ./azwi.py --help
uv run ./azwi.py 2195 --org my-org

Run the tests and build the distribution:

uv run python -m unittest discover -s tests -v
uv build --no-sources
uv run python tests/wheel_smoke.py dist/azwi-1.4.0-py3-none-any.whl

The wheel smoke check uses temporary environments and a temporary home directory. It validates wheel packaging, index-style metadata, local and editable installs, uvx, and the PEP 723 wrapper. It requires access to build and runtime dependencies.

To release a version:

  1. Update the version and changelog, then commit and tag the release, such as v1.4.0.
  2. Push the commit and tag. The tag triggers GitHub Actions to build and publish to PyPI using Trusted Publishing.
  3. Verify the workflow succeeds and create the GitHub release for that tag.

License

MIT

About

Azure DevOps work item fetcher for agentic coding tools

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Contributors

Languages