@meirblachman/pr-review-needed
v0.1.39
Published
Generates a markdown summary of Azure DevOps PRs needing review
Downloads
1,671
Readme
PR Review Needed
A TypeScript CLI tool that queries Azure DevOps for open pull requests and generates a markdown summary of PRs needing reviewer feedback — inspired by dotnet/aspire#13834.
Now with GitHub support! Monitor both Azure DevOps and GitHub repositories from a single configuration.
How It Works
- Authenticates to Azure DevOps using
AzureCliCredential(no PAT required); for GitHub, uses theghCLI token for private repos (no auth needed for public repos) - Fetches all active, non-draft PRs (excluding those tagged
NO-MERGE) - Analyzes comment threads, reviewer votes, and push activity to determine which PRs are waiting on reviewers
- Fetches pipeline/build status for each PR from the Azure DevOps Build API or GitHub Check Runs
- Generates a markdown file, HTML report, JSON report, or terminal dashboard with PRs sorted by wait time
A PR is considered "needing review" when:
- It has no approving vote (vote ≥ 5)
- The last meaningful activity is from the PR author (the ball is in reviewers' court)
- Bot/service-account activity is ignored
Prerequisites
- Node.js 24+
- Azure CLI — logged in via
az login(for Azure DevOps repos) - GitHub CLI (
gh) — logged in viagh auth login(for private GitHub repos only; public repos need no auth) - Access to the target Azure DevOps organization and/or GitHub repositories
- (Optional) Microsoft Graph access — for resolving team members from org hierarchy
Installation
npm install -g @meirblachman/pr-review-neededThen run the CLI:
pr-review-needed setup
pr-review-needed runFrom Source
git clone https://github.com/meir017/ado-pr-review-needed.git
cd ado-pr-review-needed
npm install
npm run build && npm run bundleUsage
Setup
Generate a template configuration file in the current directory:
pr-review-needed setupThis creates a pr-review-config.json with placeholder values. Edit it to add your Azure DevOps and/or GitHub repository URLs and team members.
Run
# Generate pr-review-summary.md (default output)
pr-review-needed run
# Custom output path
pr-review-needed run --output docs/review-status.md
# Use a custom config file
pr-review-needed run --config path/to/my-config.json
# Interactive terminal dashboard
pr-review-needed run --format terminal
# Generate JSON report
pr-review-needed run --format json --output pr-review-summary.json
# Generate self-contained HTML report
pr-review-needed run --format html --output pr-review-summary.html
# Enable verbose debug logging
pr-review-needed run --verboseCLI Flags
| Flag | Description |
|------|-------------|
| --output <path> | Output file path (default: pr-review-summary.md) |
| --config <path> | Path to a custom config file (default: pr-review-config.json in project root) |
| --format <type> | Output format: markdown, json, html, terminal (default: markdown) |
| --verbose | Enable debug logging |
| --webhook-url <url> | Send JSON report to a webhook URL |
| --notify | Send notifications (default: true if webhooks configured) |
| --no-notify | Disable notifications |
| --nudge | Send nudge comments on stale PRs (default: true if configured) |
| --no-nudge | Disable auto-nudge comments |
| --dry-run | Log actions without making changes |
Configuration
The tool reads repository targets from pr-review-config.json. You can specify one or more Azure DevOps and/or GitHub repository URLs:
{
"repositories": [
{ "url": "https://dev.azure.com/{org}/{project}/_git/{repo}" },
{ "url": "https://dev.azure.com/{org}/{project}/_git/{another-repo}", "skipRestartMerge": true },
{ "url": "https://github.com/{owner}/{repo}" },
{ "url": "https://github.com/{owner}/{another-repo}", "skipRestartMerge": true }
],
"orgManager": "[email protected]",
"teamMembers": ["[email protected]", "[email protected]"]
}All supported ADO URL formats work:
https://dev.azure.com/{org}/{project}/_git/{repo}https://{org}.visualstudio.com/{project}/_git/{repo}[email protected]:v3/{org}/{project}/{repo}
GitHub URL format:
https://github.com/{owner}/{repo}
When multiple repositories are configured, the markdown output groups PRs by repository.
Config Fields
| Field | Description |
|-------------|-------------|
| repositories | Array of repository objects (see below) |
| orgManager | (Optional) Manager UPN — recursively fetches the full org tree via MS Graph |
| manager | (Optional) Manager UPN — fetches only direct reports via MS Graph |
| teamMembers | (Optional) Explicit list of team member emails to scope PR results |
| ignoreManagers | (Optional) When true, hides PRs authored by managers (anyone with direct reports in the org tree) |
| botUsers | (Optional) Array of user emails, unique names, or display names to treat as deterministic bots. Their activity is ignored and their PRs get the APPROVE action. |
| aiBotUsers | (Optional) Array of user emails, unique names, or display names to treat as AI bots (e.g. GitHub Copilot, Claude, Codex). Their activity is filtered like bots, but their PRs still require human review (action stays REVIEW). Built-in AI bot patterns are detected automatically. |
| quantifier | (Optional) PR size quantifier config — see PR Quantifier below |
| restartMergeAfterDays | (Optional) Trigger "restart merge" on PRs older than this many days. Default: 30. Set to -1 to disable. |
| staleness | (Optional) Staleness alert configuration — see Staleness Alerts below |
| notifications | (Optional) Teams notification configuration — see Teams Notifications below |
Repository Object Fields
Each entry in the repositories array is an object with the following fields:
| Field | Description |
|-------|-------------|
| url | (Required) Full ADO or GitHub repository URL |
| skipRestartMerge | (Optional) When true, skip restart-merge for this repository. Default: false. |
Example Output
Markdown
## PRs Needing Review
_Last updated: 2025-02-09T10:00:00.000Z_
| PR | Author | Size | Pipelines | Waiting for feedback |
|---|---|---|---|---|
| [#1234 - Fix config parsing](https://dev.azure.com/...) ❌ | Alice | 🔴 XL | 🔴 2/3 failed | 🔴 5 days ago |
| [#1250 - Add new template](https://dev.azure.com/...) | Bob | 🟡 M | 🟢 2/2 passed | 🟡 2 days ago |
| [#1260 - Update docs](https://dev.azure.com/...) | Carol | 🟢 S | 🟡 1/1 running | 🟢 3 hours ago |
_Total: 3 PRs needing review._Legend
| Icon | Meaning | |------|---------| | 🟢 | Waiting ≤ 1 day / Size XS or S / All pipelines passed | | 🟡 | Waiting 2–3 days / Size M / Pipelines running | | 🔴 | Waiting > 3 days / Size L or XL / Pipeline failures | | ❌ | Has merge conflicts |
Output Formats
| Format | Command | Description |
|--------|---------|-------------|
| Markdown | --format markdown (default) | PR tables with emoji badges |
| JSON | --format json | Machine-readable report with all data |
| HTML | --format html | Self-contained HTML dashboard with sorting, filtering, search, and CSV export |
| Dashboard | --dashboard | Interactive terminal view with ANSI colors and clickable links |
PR Quantifier
Inspired by microsoft/PullRequestQuantifier, the tool can classify each PR by change size (XS, S, M, L, XL) based on total lines added + deleted. This helps encourage smaller, more reviewable PRs.
Enabling the Quantifier
The quantifier is enabled by default. To disable it, add to your pr-review-config.json:
{
"quantifier": {
"enabled": false
}
}To customize thresholds or file exclusions:
{
"repositories": ["..."],
"quantifier": {
"enabled": true,
"excludedPatterns": ["package-lock.json", "*.generated.cs", "*.Designer.cs"],
"thresholds": [
{ "label": "XS", "maxChanges": 10 },
{ "label": "S", "maxChanges": 40 },
{ "label": "M", "maxChanges": 100 },
{ "label": "L", "maxChanges": 400 },
{ "label": "XL", "maxChanges": 1000 }
]
}
}Quantifier Config Fields
| Field | Description |
|-------|-------------|
| enabled | (Optional) Set to false to disable. Defaults to true. |
| excludedPatterns | (Optional) Glob patterns for files to exclude from the change count (e.g., lockfiles, auto-generated code). |
| thresholds | (Optional) Custom size thresholds. Each entry has a label and maxChanges. Defaults to the PullRequestQuantifier standard (XS≤10, S≤40, M≤100, L≤400, XL≤1000). |
How It Works
- Fetches PR iteration changes from Azure DevOps to get the list of changed files
- Filters out files matching
excludedPatterns - Uses the ADO file diffs API to count lines added and deleted
- Sums additions + deletions and maps to a size label using the configured thresholds
- Displays the size label as a column in the markdown table and terminal dashboard
Staleness Alerts
PRs are automatically tagged with staleness badges based on how long they have been waiting. This helps surface ancient PRs that need attention.
Default Thresholds
| Badge | Min Days | |-------|----------| | ⚠️ Aging | 7 days | | 🔴 Stale | 14 days | | 💀 Abandoned | 30 days |
Staleness badges appear as a column in the markdown tables and inline in the terminal dashboard.
Customizing Thresholds
{
"staleness": {
"enabled": true,
"thresholds": [
{ "label": "⏰ Overdue", "minDays": 3 },
{ "label": "🔥 Critical", "minDays": 14 }
]
}
}Set "enabled": false to disable staleness badges entirely. Defaults are applied when the staleness section is omitted.
Pipeline Status
Each PR's CI/CD pipeline status is automatically fetched from the Azure DevOps Build API (or GitHub Check Runs for GitHub repos) and displayed across all output formats. For ADO repos, the tool queries builds on the refs/pull/{id}/merge branch and de-duplicates to show only the latest run per pipeline definition. For GitHub repos, check runs on the PR's head commit are used.
Display
| Badge | Meaning | |-------|---------| | 🟢 3/3 passed | All pipelines succeeded | | 🔴 2/3 failed | One or more pipelines failed | | 🟡 1/2 running | Pipelines still in progress | | ⚪ 2 pipeline(s) | Other/unknown status |
The Pipelines column only appears when at least one PR has pipeline data. No additional configuration is required — pipeline status is fetched automatically whenever build data is available for a PR.
GitHub Support
In addition to Azure DevOps, the tool fully supports GitHub repositories. Add any https://github.com/{owner}/{repo} URL to your config to start monitoring GitHub PRs.
Authentication
- Public repos: No authentication needed — the tool uses unauthenticated GitHub API requests.
- Private repos: Run
gh auth loginonce; the tool automatically retrieves your token viagh auth token.
Feature Mapping
GitHub concepts are mapped to the same unified model used for Azure DevOps:
| Feature | GitHub Equivalent |
|---------|-------------------|
| Reviewer votes | GitHub review states: APPROVED → approve, CHANGES_REQUESTED → reject, COMMENTED → waiting, PENDING → no vote |
| Pipeline status | GitHub Check Runs on the PR's head commit |
| DORA metrics | GitHub Actions workflow runs + merged PR history via search API |
| Restart merge | Update branch — merges the base branch into the PR head branch |
| Auto-nudge | Posts a GitHub issue comment on stale PRs |
| PR size (quantifier) | Computed from PR files' additions + deletions |
| File labels | Same glob pattern matching as ADO repos |
Review Metrics
The tool computes review cycle time metrics from existing PR thread and push data and adds a 📈 Review Metrics section to the output:
- Per-PR: time to first review, number of review rounds, age
- Aggregate: median PR age, average time to first review, average review rounds, count of PRs with no review activity
- Per-Author: open PR count, average age, average rounds, fastest review time
The dashboard shows a compact aggregate summary.
Reviewer Workload
A 👥 Reviewer Workload section shows per-reviewer statistics to help identify bottlenecks:
| Column | Description | |--------|-------------| | Assigned | Total PRs where the reviewer is assigned | | Pending | PRs in "needing review" where the reviewer hasn't approved | | Completed | PRs where the reviewer voted to approve | | Avg Response | Average time from PR creation to the reviewer's first comment | | Load | 🟢 Light / 🟡 Medium / 🔴 Heavy indicator |
Default load thresholds: 🟢 ≤10 pending & ≤2d response, 🟡 ≤20 pending & ≤4d response, 🔴 above. The dashboard shows the top 5 bottleneck reviewers.
Teams Notifications
Send PR review summaries to a Microsoft Teams channel via incoming webhook. Notifications are sent as Adaptive Cards with collapsible sections.
Configuration
{
"notifications": {
"teams": {
"webhookUrl": "https://outlook.office.com/webhook/...",
"filters": {
"sections": ["needingReview", "waitingOnAuthor"]
}
}
}
}Config Fields
| Field | Description |
|-------|-------------|
| webhookUrl | (Required) Teams incoming webhook URL |
| filters.sections | (Optional) Array of sections to include: "approved", "needingReview", "waitingOnAuthor". Defaults to all. |
| filters.minStalenessLevel | (Optional) Only include PRs at or above this staleness level |
Notifications are sent automatically after generating the report. Use --no-notify to suppress, or --notify to explicitly enable.
Running Tests
npm test
# With coverage
npx vitest run --coverage