Azure DevOps forge viewer
mnml-forge-azdevops is a terminal Azure DevOps viewer. Runs standalone in any terminal. It’s the Azure DevOps side of the forge integration class — sibling to mnml-forge-bitbucket, mnml-forge-github, and mnml-forge-gitlab. See Building integrations for the model.
┌─ azure devops ───────────────────────────────────────────────────┐│ ▸1.Mine (4) 2.Reviewing (6) 3.api PRs (12) 4.Builds (50) │└──────────────────────────────────────────────────────────────────┘┌─ Mine ───────────────────────────────────────────────────────────┐│ ID │ STATUS │ REPO │ SRC → DEST │ TITLE ││ #421 │ active │ api │ feat/x → main │ Add /v2 … ││ #418 │ active │ web │ chore/deps → main │ Bump axios … ││ … │└──────────────────────────────────────────────────────────────────┘ 1-9 tab · ↑↓/jk move · Enter/o open · r refresh · q quitInstall
Section titled “Install”cargo install --git https://github.com/chris-mclennan/mnml-forge-azdevops mnml-forge-azdevopsmnml-forge-azdevops --installHomebrew tap + binary releases will follow once the binary stabilises.
-
Create an Azure DevOps PAT at
https://dev.azure.com/<org>/_usersSettings/tokens.Minimum scopes: Code (Read) for PR tabs and Build (Read) for Build tabs. Add User Profile (Read) if you want
mode = "mine"/mode = "reviewing"tabs — those resolve the current user’s GUID via/_apis/connectionDataat startup. -
Save the PAT to
~/.config/mnml-forge-azdevops/tokenwithchmod 600:Terminal window mkdir -p ~/.config/mnml-forge-azdevopspbpaste > ~/.config/mnml-forge-azdevops/token # or paste it in $EDITORchmod 600 ~/.config/mnml-forge-azdevops/token -
Run once to scaffold the config template:
Terminal window mnml-forge-azdevopsWrites
~/.config/mnml-forge-azdevops.tomland exits with instructions. Editorg,project, and the[[tabs]]list. -
Re-run — the TUI launches with your configured tabs.
-
Verify the resolved config + auth state without launching the TUI:
Terminal window mnml-forge-azdevops --checkHits
/_apis/connectionDatato confirm the PAT works.
Auth shape
Section titled “Auth shape”Azure DevOps PATs go on the wire as HTTP Basic with an empty username and the PAT as the password — that’s the platform’s documented convention, not a quirk of this viewer. The client base64-encodes :<token> and sends Authorization: Basic <encoded> on every request. There is no Bearer-token variant.
Config
Section titled “Config”org = "your-org" # required: <org> in dev.azure.com/<org>/project = "your-project" # optional default; tabs can override
refresh_interval_secs = 60org is required at the top level. project is an optional default — every tab must end up with one, either inherited from the top or set inline via project = "..." on the row. A tab can also override the org per-row with org = "..." if you operate across multiple organizations under a single PAT.
Tab kinds
Section titled “Tab kinds”Each [[tabs]] entry is one tab. The kind field (defaults to pull_requests) decides what the tab shows:
kind | What it shows | Required fields |
|---|---|---|
pull_requests (default) | PR list, with state filter + optional mine/reviewing modes | one of repo / mode |
builds | Recent builds for the project, newest-first | none (project-scoped); optional repo / branch / definition |
PR-specific fields (state, mode) are ignored on builds tabs; branch / definition are ignored on pull_requests tabs.
[[tabs]]name = "Reviewing"mode = "reviewing" # kind defaults to pull_requests
[[tabs]]name = "api PRs"repo = "api"state = "active"
[[tabs]]name = "Builds"kind = "builds"Pull-request tab shapes
Section titled “Pull-request tab shapes”Three shapes for kind = "pull_requests":
Per-repo
Section titled “Per-repo”[[tabs]]name = "api PRs"repo = "api"state = "active" # active / completed / abandoned / allrepo is the <repo> segment in dev.azure.com/<org>/<project>/_git/<repo>. Uses the default org + project from the top of the config unless overridden inline.
mode = "mine" — PRs you created
Section titled “mode = "mine" — PRs you created”[[tabs]]name = "Mine"mode = "mine"Project-spanning — hits GET /{org}/{project}/_apis/git/pullrequests?searchCriteria.creatorId=<your-guid>. Resolves the current user’s GUID once at startup via /_apis/connectionData (needs User Profile (Read)).
mode = "reviewing" — PRs you’re a reviewer on
Section titled “mode = "reviewing" — PRs you’re a reviewer on”[[tabs]]name = "Reviewing"mode = "reviewing"Same as mine but with searchCriteria.reviewerId instead of creatorId.
The state field (active / completed / abandoned / all) applies to all three shapes. all drops the searchCriteria.status query param.
Builds tabs
Section titled “Builds tabs”kind = "builds" is project-scoped by default; every narrower is optional. The STATUS column collapses Azure DevOps’s two-stage state into one chip via Build::status_chip() — queued while not-started, running while in-progress, then the result (succeeded / failed / cancelled / partiallySucceeded / …) once the build completes.
# All builds in the project.[[tabs]]name = "Builds"kind = "builds"
# Narrowed to one repo + one branch.[[tabs]]name = "api main"kind = "builds"repo = "api"branch = "main"
# Narrowed to one pipeline definition (by numeric ID).[[tabs]]name = "release pipeline"kind = "builds"definition = 42The branch filter accepts both short names (main) and full refs (refs/heads/main) — the client normalizes short names to refs/heads/<name> before querying. definition is the pipeline-definition ID (visible in the URL on dev.azure.com), not the name.
Default scaffold
Section titled “Default scaffold”The first-run template ships with 4 tabs plus a commented-out repo+branch builds example:
| Tab | Kind | Notes |
|---|---|---|
| Mine | pull_requests | mode = "mine" |
| Reviewing | pull_requests | mode = "reviewing" |
| your-repo PRs | pull_requests | Per-repo, state active |
| Builds | builds | Project-scoped, all repos / branches |
Edit or replace freely; you’re not locked in.
| Chord | Action |
|---|---|
1-9 | Switch to that tab |
Tab / BackTab | Cycle tabs forward / back |
↑ / k, ↓ / j | Move selection |
PgUp / PgDn | Jump 10 rows |
g / G | Top / bottom |
Enter / o | Open focused PR or build in your browser |
y | Yank focused row’s URL to the OS clipboard (pbcopy / xclip / wl-copy / clip.exe) |
r | Refresh active tab |
q / Esc / Ctrl+C | Quit |
Auto-refresh runs every refresh_interval_secs seconds (default 60, set to 0 to disable).
Headless JSON modes
Section titled “Headless JSON modes”Two flags expose the sibling to mnml’s cross-host pr.picker:
# Stdout: { "host": "azdevops", "prs": [SiblingPr, ...] }mnml-forge-azdevops --list-prs --json
# Stdout: { "url": "..." | null }mnml-forge-azdevops --find-pipeline-for-pr \ --owner <org>/<project> --repo <repo> --branch <branch> --jsonSiblingPr fields: id, url, owner, repo, title, author, source_branch, dest_branch, state, updated_at, remote_url_https, remote_url_ssh. Same shape across all four mnml-forge-* siblings
— see the cross-host PR workflow page
for how mnml fans out across hosts.
Per-host caveats here:
ownerfor Azure DevOps is<org>/<project>(not just org), since AZ scopes repos under nested project paths.--find-pipeline-for-prfinds the most-recent build for the given branch (AZ uses “build” instead of “pipeline” in its API surface).
Two run modes
Section titled “Two run modes”Standalone
Section titled “Standalone”Just run mnml-forge-azdevops in any terminal. The TUI takes over until you q.
Hosted as a mnml Pty pane
Section titled “Hosted as a mnml Pty pane”:term mnml-forge-azdevopsmnml spawns it in a Pty pane — splittable, focusable, key-routed like any other pane.
Wire it into mnml’s left rail
Section titled “Wire it into mnml’s left rail”mnml-forge-azdevops ships as a default chip in mnml’s rail under INTEGRATIONS — no config needed if you’ve kept the built-in defaults. To customise the icon, drop this into your ~/.config/mnml/config.toml:
[[ui.integration_icon]]id = "azdevops"glyph = "\U0000EBE8" # nf-cod-azure (TOML 8-digit form)fallback = "A"command = ":term mnml-forge-azdevops"color = "blue"tooltip = "Open Azure DevOps PRs + builds"Setting [[ui.integration_icon]] replaces the built-in defaults, so copy the defaults from src/config.rs into your config first if you want to extend rather than replace. See the launcher-icon strips for the field reference and the \UXXXXXXXX Nerd-Font escape convention.
Status
Section titled “Status”v0.1 (this release) — Pull Requests + Builds tabs, project-spanning mine / reviewing PR modes, optional repo / branch / definition narrowers on Build tabs, no detail panel yet (Enter opens the PR / build page in the browser). A right-half PR detail panel with comments / diff is queued for v0.2 alongside the comments / iterations endpoints.
Source
Section titled “Source”The viewer lives in its own sibling repo: github.com/chris-mclennan/mnml-forge-azdevops. MIT-licensed and built around the same ratatui substrate mnml uses, so most of its UI patterns will look familiar. See Building integrations for the anatomy of an integration, or Community integrations for the directory of siblings.