Skip to content

skhell/adoboards

Repository files navigation

NPM CLI for Azure DevOps Boards with AI support

adoboards

If you feel like Homer Simpson backing into bushes right before sprint review, you are in the right place.

We've all been there. You're very busy into actual work and then someone reminds you that your user stories need descriptions, acceptance criteria, t-shirt sizes, and business value fields filled in before tomorrow's ceremony. The first time I heard "ceremony" I genuinely thought it was some kind of cult initiation.

Don't get me wrong Scrum and the job of Scam Masters are genuinely good when used properly. But let's be honest: most of us have seen it applied in ways that create more overhead than value, especially for architecture and infrastructure work where the real complexity lives in the system design, not in a Jira ticket template.

After nearly 20 years in the industry, I noticed something: the time spent writing and grooming work items is often disproportionate to the value they deliver. So inspired by git cli commands I built a tool to fix that.

adoboards is a CLI that pulls your Epics, Features, and User Stories from Azure DevOps Boards, organizes them locally as markdown files, and optionally lets AI handle the tedious writing for you.

You can quickly:

  1. Work from CLI with git-like commands (clone, push, pull, status, diff, add)
  2. Create work items from templates manually - no AI required, no corporate restrictions
  3. Optionally generate Epics, Features, and User Stories with Claude, ChatGPT, Gemini, or Azure OpenAI - API keys safely stored in KeePass (recommended)
  4. Let AI optimize existing items so you stop guessing "what do I pick for t-shirt size or business value?"
  5. Plan entire sprints with AI-powered capacity distribution
  6. Generate sprint reports from local state in seconds - ready to paste into emails or meetings

The CLI is fully functional without AI. Clone, edit, push, pull, status - all work with just your ADO PAT. AI features (gen, optimize, plan) are there when you want them, but never mandatory. If your org restricts API access, you lose nothing - the core workflow stands on its own.

Less time writing about work, more time doing the work.

Table of content

Install guide

Requirements

  • Node.js 18+ (ESM modules)
  • KeePassXC (recommended) with keepassxc-cli configured on your PATH for secrets management
  • An Azure DevOps organization with Boards enabled
  • Optional: an AI provider API key (Anthropic, OpenAI, Google Gemini, or Azure OpenAI for business/corporate compliant environments) for content generation features

Install from npm

npm install -g adoboards
adoboards --version

That's it. One command, globally available.

Install from source (for development)

git clone https://github.com/skhell/adoboards.git
cd adoboards
npm install
npm install -g .
adoboards --version

Setup wizard

Run the interactive config wizard it walks you through everything:

adoboards config

It will ask for:

  1. Your Azure DevOps org URL and project name
  2. A default area path (e.g. YourTeam/DCArchitecture)
  3. Your email address - used to auto-assign work items during gen and kept in sync automatically on every pull
  4. Default project path - where gen saves items when no --project flag is given
  5. Secrets backend KeePass (recommended), OS keychain, or local env vars
  6. Path to your .kdbx file (if using KeePass)
  7. Your preferred AI provider (optional - skip if you don't have API access)
  8. AI persona describe your role/title and what your team does - will be used by AI prompt to write in your domain language
  9. Team capacity team size, velocity per person, sprint length
  10. Reports directory where sprint reports are saved (default: ~/Downloads/adoreports/)

Or skip the wizard and set the secrets backend directly:

adoboards config --secrets keepass
adoboards config --secrets env

Secrets

Let's talk about the elephant in the room: where do your API keys live?

Most CLI tools want you to dump secrets in a .env file or some plain-text config. That's fine until your laptop gets stolen, your disk gets cloned, or your manager's "IT audit" script scans your home directory. Fun times.

adoboards supports three backends pick your poison:

Backend How it works Best for
KeePass (default) Reads secrets from your .kdbx file via keepassxc-cli Corporate laptops where KeePass is already org-approved
Keytar Uses macOS Keychain / Windows Credential Manager Personal machines without KeePass
Env vars Reads ADOBOARDS_ADO_PAT, ADOBOARDS_ANTHROPIC_KEY, etc. CI/CD pipelines, headless Linux boxes

Why KeePass is the default

  • It's probably already installed and approved on your corporate machine no security team arguments
  • AES-256 encryption with a master password only you know even root can't read it
  • Your keys are not in memory when adoboards isn't running (unlike env vars sitting in your shell profile)
  • Cross-platform: works on macOS, Windows, Linux
  • You were going to store these keys somewhere anyway might as well be the vault IT already trusts

Getting keepassxc-cli on your PATH

KeePassXC desktop app includes keepassxc-cli, but it's not always on your PATH by default. Here's how to fix that:

macOS (Homebrew or standalone .dmg):

# If installed via Homebrew cask (most common)
brew install --cask keepassxc

# The CLI is buried inside the .app bundle. Symlink it:
sudo ln -s /Applications/KeePassXC.app/Contents/MacOS/keepassxc-cli /usr/local/bin/keepassxc-cli

# Verify
keepassxc-cli --version

Note

If you installed via .dmg and KeePassXC is in /Applications, the symlink command above works the same.

Windows:

# If installed via default installer, the CLI is at:
# C:\Program Files\KeePassXC\keepassxc-cli.exe

# Add to PATH permanently (run in Admin PowerShell):
[Environment]::SetEnvironmentVariable("Path", $env:Path + ";C:\Program Files\KeePassXC", "User")

# Or with winget:
winget install KeePassXCTeam.KeePassXC
# Then add to PATH as above

# Verify (restart terminal first)
keepassxc-cli --version

Linux:

# Debian/Ubuntu
sudo apt install keepassxc

# Fedora
sudo dnf install keepassxc

# Arch
sudo pacman -S keepassxc

# CLI is included and already on PATH after install
keepassxc-cli --version

After this, adoboards config should show: keepassxc-cli detected on PATH.

How it actually works under the hood

adoboards never sees your master password. It calls keepassxc-cli as a subprocess:

keepassxc-cli show -a Password ~/secrets.kdbx "adoboards/ado-pat"
# -> KeePassXC prompts for master password (in its own process)
# -> returns the secret on stdout
# -> adoboards grabs it, uses it, lets it go out of scope
# -> secret never stored in any object, file, or log

If KeePassXC desktop is already open and unlocked, the secret service socket kicks in no prompt needed. So after the first unlock of your day, it's seamless.

KeePass entry setup

adoboards does not create KeePass entries for you and that's on purpose. Creating entries would mean your secrets pass through our code during setup, which violates the whole point. You set them up once, by hand, and adoboards only ever reads them.

  1. Open your .kdbx file in KeePassXC
  2. Right-click the root group -> New Group -> name it adoboards
  3. Inside that group, create entries (right-click -> New Entry):
Entry title (Title field) Password field contains
ado-pat Your Azure DevOps Personal Access Token
anthropic-key Anthropic Claude API key
openai-key OpenAI ChatGPT API key
gemini-key Google Gemini API key
azure-openai-key Azure OpenAI Service API key

You only need ado-pat to use the CLI. AI provider keys are optional - add one only if you want gen, optimize, or plan features.

How to get your Azure DevOps PAT (even with corporate SSO)

If your org uses single sign-on (SAML, Entra ID, ADFS, etc.) you can still create a PAT SSO handles the browser login, and the PAT is generated after you're authenticated.

  1. Sign in to Azure DevOps in your browser (SSO handles auth as usual)
  2. Click your profile icon (top right) -> Personal access tokens
    • Or go directly to: https://dev.azure.com/YOUR_ORG/_usersSettings/tokens
  3. Click New Token
    • Name: adoboards
    • Organization: select your org (or "All accessible organizations" if allowed)
    • Expiration: set the maximum your org allows (often 1 year)
    • Scopes: select Custom defined, then enable:
      • Work Items -> Read & Write
      • That's it adoboards doesn't need anything else
  4. Click Create and copy the token immediately Azure shows it only once
  5. In KeePassXC: open your adoboards/ado-pat entry -> paste the token into the Password field -> Save

Note

If "New Token" is greyed out or you get a permissions error: your org admin may have restricted PAT creation. Check with your IT/DevOps team some orgs require you to request PAT permissions through an internal portal or open ticket first to your service desk.

How to get your AI provider API key

Provider Where to get it
Anthropic (Claude) console.anthropic.com/settings/keys -> Create Key
OpenAI (ChatGPT) platform.openai.com/api-keys -> Create new secret key
Google (Gemini) aistudio.google.com/apikey -> Create API key
Azure OpenAI Azure Portal -> your OpenAI resource -> Keys and Endpoint

Copy the key -> paste it as the Password of the matching KeePass entry (e.g. adoboards/anthropic-key).

Azure OpenAI Service (corporate compliance)

If your organization provides Azure OpenAI Service, you can use it instead of a personal API key. This keeps everything on your company's Azure subscription - fully compliant, no personal spend.

  1. Run adoboards config and select azure-openai as AI provider
  2. Enter your Azure OpenAI endpoint (e.g. https://your-resource.openai.azure.com)
  3. Enter your deployment name (the model you deployed, e.g. gpt-4o)
  4. Get your API key from Azure Portal -> your OpenAI resource -> Keys and Endpoint -> copy Key 1
  5. Store the key:
    • KeePass: create entry adoboards/azure-openai-key with the key as Password
    • Env vars: export ADOBOARDS_AZURE_OPENAI_KEY="your-key"

Then use it:

adoboards gen "Migrate DNS to Azure" --provider azure-openai

Or set it as default in adoboards config and skip --provider entirely.

Switching backends

# During setup wizard
adoboards config

# Or directly
adoboards config --secrets keepass
adoboards config --secrets keytar
adoboards config --secrets env

Env vars backend (CI/headless only not recommended for daily use)

Note

Not recommended for your personal machine. Env vars sit in plain text in your shell profile, .env file, or process environment. Anyone with read access to your home directory can see them. A leaked .env in a git push and your keys are on GitHub forever.

Use env vars only for CI/CD pipelines, Docker containers, or headless Linux servers where you can't run KeePassXC.

If you still want to use it:

# Option A: export directly in your shell
export ADOBOARDS_ADO_PAT="your-pat-here"
export ADOBOARDS_ANTHROPIC_KEY="sk-ant-..."    # if using Claude
export ADOBOARDS_OPENAI_KEY="sk-..."           # if using ChatGPT
export ADOBOARDS_GEMINI_KEY="AI..."            # if using Gemini
export ADOBOARDS_AZURE_OPENAI_KEY="..."        # if using Azure OpenAI in your business/enterprise environment

# Option B: use the .env template
cp .env.example .env
# Edit .env with your actual values
adoboards config --secrets env

The .env.example file has all the variable names with comments explaining where to get each key.


Commands

Clone your boards locally

Just like git one URL, project included:

# Clone current year's active items (default - skips Closed/Removed and old history)
adoboards clone https://dev.azure.com/acmecorp/YourProject

# Clone EVERYTHING - all years, all states including Closed/Removed
adoboards clone https://dev.azure.com/acmecorp/YourProject --all

# Clone only YOUR items - the stuff assigned to you
adoboards clone https://dev.azure.com/acmecorp/YourProject --assignee @me

# Clone only your team's area
adoboards clone https://dev.azure.com/acmecorp/YourProject --area "YourProject\YourTeam\DCArchitecture"

# Combine filters - your area, your items, this year
adoboards clone https://dev.azure.com/acmecorp/YourProject --area "YourProject\Network Team" --assignee @me

# Clone items from specific colleagues
adoboards clone https://dev.azure.com/acmecorp/YourProject --assignee "[email protected],[email protected]"

# Clone items changed since a specific date
adoboards clone https://dev.azure.com/acmecorp/YourProject --since 2025-06-01

# Works with old-style URLs too
adoboards clone https://acmecorp.visualstudio.com/YourProject --area "YourProject\YourTeam"

Clone filters

Flag Default What it does
--area <path> (none - entire project) Only items under this area path and all sub-areas beneath it
--assignee <users> (none - all users) @me for your items, or comma-separated emails for specific people
--since <date> Jan 1 of current year Only items changed since this date
--iteration <path> (from config) Only create iteration folders under this root path (e.g. Project\TeamA)
--all (off) Disable all filters - clone everything including Closed/Removed items and all history

By default, clone skips Closed and Removed items and only pulls items changed since January 1st of the current year. This keeps your local tree focused on what matters now - not the 8000 stories from 2019 nobody will ever read again.

If your ADO project has hundreds of iterations from multiple teams, use --iteration or set iterationFilter in adoboards config to only sync your team's iterations:

# Only iterations under your team's path
adoboards clone https://dev.azure.com/acmecorp/Project --iteration "Project\TeamA"

# Or set it permanently in config (applies to all future clones)
adoboards config
# -> Iteration root path: Project\TeamA

Area matching is case-insensitive "YourProject\network team" works the same as "YourProject\Network Team". Just wrap the value in quotes so the shell doesn't split on spaces.

The --area flag uses ADO's UNDER operator it grabs the area you specify plus every sub-area beneath it, no matter how deep. So if your area has 5 sub-sections with their own children, they all come down.

Locally, the full area hierarchy is preserved as folders:

YourProject/
|--- areas/
    |--- YourTeam/
        |--- DCArchitecture/
            |--- SubA/            <- all sub-areas become subfolders
            │   |--- backlog/
            |--- SubB/
            │   |--- backlog/
            |--- backlog/             <- items directly in DCArchitecture
            |--- iterations/
                |--- 2026-Q2/
                    |--- Sprint-4/

Create new work items (no AI needed)

Create items in your backlog first, then move them into sprints when they're ready - just like you would in ADO:

# Create items directly in the backlog folder
adoboards new story --title "Deploy DNS resolver east-us" --dir areas/TeamName/backlog
adoboards new feature --title "Automate firewall rules" --parent 42 --dir areas/TeamName/backlog
adoboards new epic --title "Zero trust network segmentation" --dir areas/TeamName/backlog
adoboards new bug --title "DNS timeout under load" --dir areas/TeamName/backlog
adoboards new task --title "Update runbook for failover" --dir areas/TeamName/backlog

Each command creates a blank template with id: pending. Open it in your editor, fill in the details, then push:

adoboards add areas/TeamName/backlog/STORY-pending-deploy-dns-resolver-east-us.md
adoboards push

Frontmatter field reference

Every work item is a markdown file with a YAML frontmatter block. Here's what each field maps to in ADO:

Field Types ADO field Notes
id all System.Id Set to pending for new items; replaced with real ID after push
type all System.WorkItemType Epic, Feature, User Story, Bug, Task
title all System.Title
state all System.State New, Active, Resolved, Closed
area all System.AreaPath Full ADO area path
iteration all System.IterationPath Full ADO iteration path
parent all System.Parent Numeric ID, or FEAT/FEAT-1/EPIC placeholders when generating
assignee all System.AssignedTo Email address; auto-filled from your config
tags all System.Tags List of strings; stored as tag1; tag2 in ADO
priority all Microsoft.VSTS.Common.Priority 1 (high) – 4 (very low)
storyPoints Story, Bug, Task Microsoft.VSTS.Scheduling.StoryPoints Numeric; paired with tshirt
tshirt Story, Bug, Task (derived) XS=1, S=3, M=5, L=8, XL=13
effort Epic, Feature Microsoft.VSTS.Scheduling.Effort Engineering days estimate
businessValue Epic, Feature Microsoft.VSTS.Common.BusinessValue 1–10
timeCriticality Epic Microsoft.VSTS.Common.TimeCriticality 1–10
complexity Epic Microsoft.VSTS.Common.Complexity 1–10
risk Epic, Feature, Story Microsoft.VSTS.Common.Risk "1 - High", "2 - Medium", "3 - Low"
startDate Epic, Feature, Story Microsoft.VSTS.Scheduling.StartDate YYYY-MM-DD
targetDate Epic, Feature Microsoft.VSTS.Scheduling.TargetDate YYYY-MM-DD
finishDate Story, Bug, Task Microsoft.VSTS.Scheduling.FinishDate YYYY-MM-DD

Moving items into sprints

When a story is ready for a sprint, move the file from backlog/ to the sprint folder and update the iteration field in the frontmatter. You can do this from VSCode (drag and drop) or from the terminal:

mv areas/TeamName/backlog/STORY-pending-deploy-dns.md areas/TeamName/iterations/2026-Q2/Sprint-4/

Then edit the frontmatter to match:

iteration: "YourProject\2026-Q2\Sprint-4"

Push the change and ADO picks up the new sprint assignment.

Other options

# Set area and iteration at creation time (skip backlog, go straight to sprint)
adoboards new story --title "Urgent fix" --iteration "2026-Q2/Sprint-4" --dir areas/TeamName/iterations/2026-Q2/Sprint-4

# Create in current directory (if you're already in the right folder)
adoboards new story --title "Quick item"

Area is auto-populated from your clone config if --area is not provided.

Check what changed

adoboards status          # What's modified, new, moved, or deleted locally
adoboards diff            # Field-level diff of all modified files vs remote
adoboards diff story.md   # Diff a single file

status tracks files by their frontmatter id, not just file path. If you move a file to a different folder, it shows as moved (not deleted). If you also edited it, it shows as moved + modified.

diff compares your local frontmatter and body sections against the last known remote state (stored in refs.json). Shows red for remote values and green for local changes - per field, not per line.

Stage and push changes to ADO

adoboards add .           # Stage everything
adoboards add story.md    # Stage a specific file
adoboards push            # Push staged changes to Azure DevOps

Note

If your path contains spaces, wrap it in quotes

adoboards add "areas/project/subproject name with space/backlog/STORY-pending-my-story.md"

adoboards status always shows paths with spaces pre-quoted so you can copy-paste them directly.

New items (id: pending) get created in ADO and their local file/folder is automatically renamed from STORY-pending-slug.md -> STORY-XXXXXXX-slug.md after push. Existing items get patched with only the changed fields.

Unstage

Changed your mind? Remove files from the staging area without touching your local edits:

adoboards unstage .           # Clear everything staged
adoboards unstage story.md    # Unstage a specific file

Pull remote changes

adoboards pull            # Sync remote changes to local files

Pull works like git pull - it restores the correct folder structure from ADO. If you moved files around locally, pull moves them back to where they belong. If you edited a file AND moved it, your edits are preserved but the file goes back to the correct path. If both you and someone on ADO changed the same item, you get a .remote.md conflict file to resolve manually.

Pull also silently refreshes your email address from ADO on every run - useful if your company changes your email or you switch accounts. No manual adoboards config needed.

Pull will refuse to run if you have staged files that haven't been pushed yet - just like git pull warns about uncommitted changes. Push first, then pull.

Folder guardrails

By default, add and push enforce that files live under the correct folder structure (areas/<team>/backlog/ or areas/<team>/iterations/<sprint>/). This prevents accidents like renaming iterations to iteratoins which would break syncing.

If you need to create custom folders (e.g. for a new area path not yet in ADO), set allowFolderEdits: true in .adoboards/config.json. Structural folders (areas, backlog, iterations) are always protected regardless of this setting.

Generate sprint reports (offline)

adoboards report                    # Overview of all sprints with progress bars
adoboards report --sprint 4         # Detailed report for a specific sprint
adoboards report --sprint Sprint-4  # Or by name

Without --sprint, you get a global overview - all sprints listed with progress bars, points, and item counts. With --sprint, you get the detailed breakdown: items grouped by state, assignees, and attention flags for missing story points or assignees.

No API calls - works entirely from your local files.

Reports are saved as markdown files to your configured reports directory (default: ./reports/). Set it during adoboards config or just let it default. File names include the date: 2026-03-08-sprint-4.md. Ready to paste into email, Teams, or attach to a ceremony invite.


Burnout-Free, LLM-Assisted content

This is where the magic happens. Stop staring at empty fields with just a title and let AI fill them in.

AI adapts to your role and domain. During adoboards config, set your role (e.g. "Senior Solution Architect") and team context (e.g. "Infrastructure and cloud platform, Azure/AWS"). The AI uses this to write in your language - not generic consumer speak.

Generate work items from an idea

# Full hierarchy: Epic > Features > Stories from one sentence
adoboards gen "Migrate on-prem DNS to Azure Private DNS"

# Just an epic
adoboards gen "Zero-trust network segmentation" --type epic

# A feature under an existing epic
adoboards gen "Automate firewall rule deployment" --type feature --parent 42

# Stories for a feature
adoboards gen "DNS failover testing" --type story --parent 67

The idea can also be a file path - useful when you have a design doc or notes file:

adoboards gen ~/Documents/ideas/migration-proposal.md
adoboards gen ./design-doc.md --type feature --parent 42

By default, files are saved to your defaultProjectPath from config. Override per command:

# Save to a different project folder
adoboards gen "Build monitoring dashboard" --project ~/Documents/adoboards/OtherProject

# Override assignee (default: your email from config)
adoboards gen "Automate failover" --assignee "[email protected]"

Pick your AI provider per command if you want:

adoboards gen "..." --provider openai
adoboards gen "..." --provider gemini
adoboards gen "..." --provider azure-openai

Generated files land in areas/<team>/backlog/ with id: pending. The assignee field is pre-filled with your email from config. Review, then stage and push:

adoboards status # See what was generated
adoboards add . # Stage all new items
adoboards push  # Create in ADO (Epic first, then Features, then Stories)

Push respects hierarchy order automatically - Epics are created before Features, Features before Stories, and parent: FEAT/FEAT-1/FEAT-2 placeholders are resolved to the real IDs that come back from ADO.

Optimize existing work items

Already have stories but they're vague, missing acceptance criteria, or have nonsensical t-shirt sizes?

adoboards optimize 1234              # Optimize a single story, bug, or task by ID
adoboards optimize 1234              # Optimize a feature + all connected stories (prompts Y/N)
adoboards optimize 1234              # Optimize an epic + all features + stories (prompts Y/N)
adoboards optimize areas/YourTeam/   # Optimize everything under an area path
adoboards optimize story.md          # Optimize a single file

Target by work item ID and the command automatically resolves the hierarchy:

  • Story / Bug / Task - optimizes that item directly, no prompt
  • Feature - optimizes the feature and all connected stories; asks [Y/n] before touching child items
  • Epic - optimizes the epic, all child features, and all their stories; asks [Y/n] before proceeding

Changes are written to files immediately. Acceptance criteria are formatted as - [ ] checkboxes (compatible with GitHub and Azure DevOps markdown).

AI rewrites descriptions, acceptance criteria, business value justification, and t-shirt rationale but never touches your metadata (IDs, state, assignments, iterations).

AI-powered sprint planning

adoboards plan --quarter Q2
adoboards plan --quarter Q2 --apply   # Apply the plan to files immediately

Collects unassigned stories, reads your team capacity config, and uses AI to distribute work across sprints respecting dependencies and capacity limits. Shows a capacity bar per sprint so you can see the load at a glance.


Feedback

If it saves you from one more sprint planning nightmare, it was worth building. Star the project and if you want invite me for a coffe.

About

NPM CLI for Azure DevOps Boards with AI support

Topics

Resources

License

Security policy

Stars

Watchers

Forks

Sponsor this project

  •  

Contributors