Read and write Azure Boards work items with first-class Markdown support
Project description
abwi
A CLI for reading and writing Azure Boards work items with first-class
Markdown support — what az boards makes hard, abwi makes the default.
Why
- Azure Boards supports native Markdown in large text fields, but
az devopscannot write it: setting a field's format requires an extramultilineFieldsFormatentry in the JSON Patch document, which the Azure CLI never sends.abwiadds it automatically, so Description, Repro Steps, Acceptance Criteria, and friends land as real Markdown — not HTML-escaped text. - Every multiline field is addressed generically with
-f <refname>=<value>, with curl-style input:@filereads a file,@-reads stdin, and a leading\@escapes a literal@. No per-field flags to memorize.
[!WARNING] Switching a field to Markdown is irreversible per work item — once a field on a given work item stores Markdown, Azure DevOps does not allow converting it back to HTML. This is an Azure DevOps limitation, not an
abwione. If your organization or process is not ready for Markdown, use the--format htmlfallback.
Install
via npm
npm install -g @abwi/cli
abwi --help
via PyPI
pip install abwi
abwi --help
via GitHub Releases
Download the binary for your platform from Releases.
via Go
go install github.com/lohn/abwi/cmd/abwi@latest
Authentication
Entra ID (default). Sign in once with the Azure CLI; abwi picks up the
Azure CLI credential automatically:
az login
PAT (explicit opt-in, not recommended). Set auth = "pat" in the
global config file (or pass --auth pat) and export the token as
ABWI_PAT (falling back to AZURE_DEVOPS_EXT_PAT, the variable the Azure
CLI uses):
export ABWI_PAT=... # or AZURE_DEVOPS_EXT_PAT
abwi --auth pat show 123
PAT values are read only from environment variables, never from config files, so a token can't end up committed by accident.
The auth key is honored only in the global config file — a checked-out
repository must not be able to switch your authentication mode, so auth in
a repo-local .abwi.toml is ignored with a warning.
Configuration
abwi merges configuration from two TOML files:
- Local:
.abwi.toml, found by walking up from the current directory (put it at your repository root) - Global: the per-user config directory —
~/.config/abwi/config.tomlon Linux (Go'sos.UserConfigDir, so~/Library/Application Support/abwi/config.tomlon macOS and%AppData%\abwi\config.tomlon Windows)
Precedence, highest first: flags > environment (ABWI_ORG,
ABWI_PROJECT) > local file > global file.
# .abwi.toml — committed at the repository root
org = "https://dev.azure.com/myorg"
project = "MyProject"
default-type = "Product Backlog Item"
# Shorthand names for -f, expanded to full field reference names
[aliases]
ac = "Microsoft.VSTS.Common.AcceptanceCriteria"
repro = "Microsoft.VSTS.TCM.ReproSteps"
Available keys:
| Key | Description | Default |
|---|---|---|
org |
Organization URL (https://dev.azure.com/<org>) |
— |
project |
Project name | — |
format |
Large text format: markdown / html |
markdown |
auth |
Authentication: entra / pat — global config only |
entra |
default-type |
Work item type used when create --type is omitted |
— |
[aliases] |
Table of shorthand names for -f, expanded to full reference names |
— |
Run abwi config to see the resolved values and where each one came from —
see Usage below.
Usage
Create a work item (--type falls back to default-type from the config;
-d/-f values support @file and @-):
abwi create -T Bug -t "Crash when saving a draft" \
-d @description.md \
-f Microsoft.VSTS.TCM.ReproSteps=@repro.md
# The same, using the [aliases] shorthand from the config
abwi create -T Bug -t "Crash when saving a draft" -d @description.md -f repro=@repro.md
Update fields of an existing work item (Markdown from stdin via @-; use
\@ when a value must start with a literal @):
generate-criteria | abwi update 123 -s Active -f ac=@- \
-f System.Description='\@mentions start with an escaped at-sign'
Show a work item (--json for the raw response):
abwi show 123
List work items — yours by default, most recently changed first; filter with flags or take over with a full WIQL query:
abwi list -T Bug -s Active --limit 20
abwi list --all # everyone's, not just yours
abwi list --assignee "someone@example.com" # someone else's
abwi list --wiql @query.wiql # full control
Comments (posted as Markdown by default):
abwi comment add 123 "Reproduced on \`main\`; see #456."
abwi comment add 123 @- # comment body from stdin
abwi comment list 123
Link and unlink (--type: parent, child, related — the default — or a
full System.LinkTypes.* reference name):
abwi link 123 456 --type parent # make #456 the parent of #123
abwi unlink 123 456 # --type disambiguates multiple links
Show the resolved configuration, each value annotated with its origin
(flag, env, local, global, or default):
$ abwi config
# global: /home/you/.config/abwi/config.toml
# local: /home/you/src/myrepo/.abwi.toml
org = "https://dev.azure.com/myorg" # local
project = "MyProject" # env
format = "markdown" # default
auth = "entra" # default
default-type = "Product Backlog Item" # local
[aliases]
ac = "Microsoft.VSTS.Common.AcceptanceCriteria"
repro = "Microsoft.VSTS.TCM.ReproSteps"
abwi config <key> prints a single value; --json prints the whole thing as
JSON.
The --format html fallback
If your organization does not support Markdown work items yet (or you want to
avoid the irreversible switch), pass --format html or set
format = "html" in the config. You still write Markdown — abwi converts
it to HTML via goldmark before sending,
and the fields stay in HTML format:
abwi create -T Bug -t "Crash when saving a draft" --format html -d @description.md
License
MIT © lohn
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distributions
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file abwi-0.1.0-py3-none-win_arm64.whl.
File metadata
- Download URL: abwi-0.1.0-py3-none-win_arm64.whl
- Upload date:
- Size: 3.8 MB
- Tags: Python 3, Windows ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
85a47c6026f988c5fb80a9833e0061e034b6931ac233a84874178d9a47c49b99
|
|
| MD5 |
31584436fad6398ab2e23fe8638e9eb2
|
|
| BLAKE2b-256 |
84f33f596b5ff5dac4382444e1acecec99d2e5af0892f8eba428f3ec00bcc6a7
|
Provenance
The following attestation bundles were made for abwi-0.1.0-py3-none-win_arm64.whl:
Publisher:
release.yaml on lohn/abwi
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
abwi-0.1.0-py3-none-win_arm64.whl -
Subject digest:
85a47c6026f988c5fb80a9833e0061e034b6931ac233a84874178d9a47c49b99 - Sigstore transparency entry: 2152421427
- Sigstore integration time:
-
Permalink:
lohn/abwi@9cc83368a9bc14935112d57c614554b3a69d6e2f -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/lohn
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yaml@9cc83368a9bc14935112d57c614554b3a69d6e2f -
Trigger Event:
push
-
Statement type:
File details
Details for the file abwi-0.1.0-py3-none-win_amd64.whl.
File metadata
- Download URL: abwi-0.1.0-py3-none-win_amd64.whl
- Upload date:
- Size: 4.3 MB
- Tags: Python 3, Windows x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b3ad6a1f8e5ba2bcb77b9b2d80176387105c47a1cc502cb19f1319c5a1ab0006
|
|
| MD5 |
1fdc574c1f80096e7f0307013c6eb251
|
|
| BLAKE2b-256 |
e0a0cc8a4bbe2d3c35ccef623844485c32a46875f2b982a6f01800caab681a84
|
Provenance
The following attestation bundles were made for abwi-0.1.0-py3-none-win_amd64.whl:
Publisher:
release.yaml on lohn/abwi
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
abwi-0.1.0-py3-none-win_amd64.whl -
Subject digest:
b3ad6a1f8e5ba2bcb77b9b2d80176387105c47a1cc502cb19f1319c5a1ab0006 - Sigstore transparency entry: 2152421250
- Sigstore integration time:
-
Permalink:
lohn/abwi@9cc83368a9bc14935112d57c614554b3a69d6e2f -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/lohn
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yaml@9cc83368a9bc14935112d57c614554b3a69d6e2f -
Trigger Event:
push
-
Statement type:
File details
Details for the file abwi-0.1.0-py3-none-manylinux_2_17_x86_64.whl.
File metadata
- Download URL: abwi-0.1.0-py3-none-manylinux_2_17_x86_64.whl
- Upload date:
- Size: 4.2 MB
- Tags: Python 3, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
96e2ae9fc995503a8e355881ac970fae855c2c49c69ad90057d55c42cda27a40
|
|
| MD5 |
91bae87dee86a99ea1e893b971205e56
|
|
| BLAKE2b-256 |
37fc17c0e0a4b1f1a0681fae4c4c6ec1f112c2442255f6b23d047e2ce0513e5d
|
Provenance
The following attestation bundles were made for abwi-0.1.0-py3-none-manylinux_2_17_x86_64.whl:
Publisher:
release.yaml on lohn/abwi
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
abwi-0.1.0-py3-none-manylinux_2_17_x86_64.whl -
Subject digest:
96e2ae9fc995503a8e355881ac970fae855c2c49c69ad90057d55c42cda27a40 - Sigstore transparency entry: 2152421309
- Sigstore integration time:
-
Permalink:
lohn/abwi@9cc83368a9bc14935112d57c614554b3a69d6e2f -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/lohn
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yaml@9cc83368a9bc14935112d57c614554b3a69d6e2f -
Trigger Event:
push
-
Statement type:
File details
Details for the file abwi-0.1.0-py3-none-manylinux_2_17_aarch64.whl.
File metadata
- Download URL: abwi-0.1.0-py3-none-manylinux_2_17_aarch64.whl
- Upload date:
- Size: 3.8 MB
- Tags: Python 3, manylinux: glibc 2.17+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8420905beec401e1f53becc54902280ef5925a3028cfd43360883bb2f64ef516
|
|
| MD5 |
b3db63114022e827e251d16b4c80efdd
|
|
| BLAKE2b-256 |
67aa8ee819ee5591e0175c534f7ba6d1e88aad0486f169e4c5e087432cdb6f7d
|
Provenance
The following attestation bundles were made for abwi-0.1.0-py3-none-manylinux_2_17_aarch64.whl:
Publisher:
release.yaml on lohn/abwi
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
abwi-0.1.0-py3-none-manylinux_2_17_aarch64.whl -
Subject digest:
8420905beec401e1f53becc54902280ef5925a3028cfd43360883bb2f64ef516 - Sigstore transparency entry: 2152421492
- Sigstore integration time:
-
Permalink:
lohn/abwi@9cc83368a9bc14935112d57c614554b3a69d6e2f -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/lohn
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yaml@9cc83368a9bc14935112d57c614554b3a69d6e2f -
Trigger Event:
push
-
Statement type:
File details
Details for the file abwi-0.1.0-py3-none-macosx_11_0_arm64.whl.
File metadata
- Download URL: abwi-0.1.0-py3-none-macosx_11_0_arm64.whl
- Upload date:
- Size: 3.9 MB
- Tags: Python 3, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e61e6d17d65ba75bb2f57070540dfe8970c9a92c9b7db4b922d12992daacf9c3
|
|
| MD5 |
5cf338b69a3fb77d2917ad887fb4777a
|
|
| BLAKE2b-256 |
53df5a57364cc2ade18a0f28d568e54010871a227e883011023111263fecf99a
|
Provenance
The following attestation bundles were made for abwi-0.1.0-py3-none-macosx_11_0_arm64.whl:
Publisher:
release.yaml on lohn/abwi
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
abwi-0.1.0-py3-none-macosx_11_0_arm64.whl -
Subject digest:
e61e6d17d65ba75bb2f57070540dfe8970c9a92c9b7db4b922d12992daacf9c3 - Sigstore transparency entry: 2152421223
- Sigstore integration time:
-
Permalink:
lohn/abwi@9cc83368a9bc14935112d57c614554b3a69d6e2f -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/lohn
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yaml@9cc83368a9bc14935112d57c614554b3a69d6e2f -
Trigger Event:
push
-
Statement type:
File details
Details for the file abwi-0.1.0-py3-none-macosx_10_9_x86_64.whl.
File metadata
- Download URL: abwi-0.1.0-py3-none-macosx_10_9_x86_64.whl
- Upload date:
- Size: 4.2 MB
- Tags: Python 3, macOS 10.9+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
06d7639f42fef89a9bae63006fcdb9c1153cc610df90203e09426653843ddd40
|
|
| MD5 |
64db3e3404bcb8acac5699809550ffdb
|
|
| BLAKE2b-256 |
d6aa23408faa3b829e801ac3232253c8fbfdd2945314a2f04cf1663c9a82e388
|
Provenance
The following attestation bundles were made for abwi-0.1.0-py3-none-macosx_10_9_x86_64.whl:
Publisher:
release.yaml on lohn/abwi
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
abwi-0.1.0-py3-none-macosx_10_9_x86_64.whl -
Subject digest:
06d7639f42fef89a9bae63006fcdb9c1153cc610df90203e09426653843ddd40 - Sigstore transparency entry: 2152421362
- Sigstore integration time:
-
Permalink:
lohn/abwi@9cc83368a9bc14935112d57c614554b3a69d6e2f -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/lohn
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yaml@9cc83368a9bc14935112d57c614554b3a69d6e2f -
Trigger Event:
push
-
Statement type: