Find reviewers who know the code—and explain why they fit.
A deterministic TypeScript library, CLI, and GitHub Action for GitHub and GitLab, including custom API hosts. Ranks repository members using commit history, CODEOWNERS, recency, access, and current review load. No AI service, telemetry, or runtime dependencies.
REVIEWER SCORE REASON
alice 0.6008 ranked
Every selection includes its five signal scores, contributed files, owned files, history sources, and selection reason. Suggestions are read-only. Assignment is a separate, explicit operation.
API documentation · Guides · CLI · GitHub Action · Enterprise · Releases
Requires Node.js 22.14 or newer.
pnpm add reviewer-suggestion
# or: npm install reviewer-suggestion
The package provides ESM, CommonJS, TypeScript declarations, and the reviewer-suggestion executable.
import {
createGitHubProvider,
suggestReviewers,
assignReviewers,
} from 'reviewer-suggestion';
const provider = createGitHubProvider({ token: process.env.GITHUB_TOKEN! });
const request = { repository: 'your-org/your-repo', number: 42 };
const result = await suggestReviewers(provider, request, {
limit: 2,
exclude: ['automation-bot'],
});
for (const reviewer of result.selected) {
console.log(reviewer.username, reviewer.score, reviewer.breakdown);
}
// Explicit write: omit this call when you only want recommendations.
const assignment = await assignReviewers(provider, request, result.selected);
if (assignment.failed.length) console.error(assignment.failed);
For GitLab, use the MR IID (the number shown in its URL), not its global database ID:
import { createGitLabProvider, suggestReviewers } from 'reviewer-suggestion';
const provider = createGitLabProvider({
token: process.env.GITLAB_TOKEN!,
// Omit for GitLab.com. Enterprise GitHub uses createGitHubProvider instead.
apiUrl: 'https://gitlab.example.com/api/v4',
});
const result = await suggestReviewers(
provider,
{ repository: 'group/project', number: 17 },
{ aliases: { 'developer@example.com': 'alice' } },
);
console.log(result.selected);
Providers accept an optional Consola-compatible logger and a custom fetch; the library is silent by default, and custom ReviewerProvider implementations can wrap existing Octokit or Gitbeaker clients. See the recipes guide.
rankReviewers(snapshot, options) runs the same engine on previously collected or synthetic data — no network, no account, no token — and now pins the clock so a ranking reproduces exactly:
import { rankReviewers, type ReviewerSnapshot } from 'reviewer-suggestion';
declare const snapshot: ReviewerSnapshot; // collected earlier, or synthetic
console.log(rankReviewers(snapshot, { now: '2026-01-01T00:00:00Z' }).selected);
The compiled example builds a complete synthetic snapshot you can run immediately.
Set GITHUB_TOKEN or GITLAB_TOKEN through your shell or CI secret store.
pnpm exec reviewer-suggestion suggest \
--provider github --repository your-org/your-repo --number 42
pnpm exec reviewer-suggestion suggest \
--provider gitlab --repository group/project --number 17 --json
pnpm exec reviewer-suggestion assign \
--provider github --repository your-org/your-repo --number 42 --dry-run
# Remove --dry-run to request reviews explicitly.
pnpm exec reviewer-suggestion --help
| Flag | Behavior |
|---|---|
--provider |
github or gitlab |
--repository |
GitHub owner/repo; GitLab project ID or namespace/path |
--number |
PR number or MR IID |
--config |
Path to a JSON ranking configuration |
--limit, --min-score |
Override ranking limits |
--exclude |
Comma-separated usernames |
--fallback |
Enable fallback when no ranked candidate passes |
--api-url |
Full custom HTTPS API root |
--json |
Structured result on stdout; operational errors on stderr |
--dry-run |
Run assignment command without making writes |
Explicit flags override JSON configuration, which overrides defaults. Credentials are accepted through environment variables, not arguments or configuration. Exit codes: 0 success (including no suggestions), 1 operational/assignment failure, 2 invalid arguments or configuration.
name: Suggest reviewers
on:
pull_request_target:
types: [opened, reopened, ready_for_review]
permissions:
contents: read
pull-requests: read
jobs:
suggest:
if: github.event.pull_request.draft == false
runs-on: ubuntu-latest
steps:
- uses: moh3n9595/reviewer-suggestion@v1
id: reviewers
with:
token: ${{ secrets.REVIEWER_TOKEN }}
assign: 'false'
Use a release commit SHA when you need an immutable Action reference. The Action reads APIs and base-revision configuration; it does not require a checkout. Keep pull_request_target jobs free of steps that check out or execute pull-request code.
To assign, set assign: 'true' and use a token with review-request write permission. Organization/team access usually calls for a GitHub App installation token — see authentication and permissions for the per-operation permission matrix and an App-token workflow.
| Input | Default |
|---|---|
provider |
github |
token |
${{ github.token }} |
repository, number |
Inferred from GitHub pull request events |
api-url |
Provider cloud API |
config-path |
No configuration file; when supplied, read at base revision |
limit |
Configuration value, otherwise 2 |
assign |
'false' |
Outputs: reviewers (JSON username array), result (full suggestion JSON), and assignment (assigned/skipped/failed identities). The Action also writes a Markdown job summary. Supply provider: gitlab, explicit repository and number, and a GitLab token to target GitLab from GitHub Actions.
score = expertise × 0.45 + ownership × 0.25 + recency × 0.15
+ access × 0.10 + availability × 0.05
Each signal is bounded to [0, 1] and backed by evidence: per-file commit history (renames and parent directories included), CODEOWNERS at the base revision, repository access level, and current open review load. Ties break on normalized username and ID — never randomness or API arrival order — and a captured clock makes every run reproducible. A normal selection requires actual contribution or ownership evidence; fallback is explicit and off by default.
Configuration is plain JSON with strict validation:
{
"limit": 2,
"minScore": 0.05,
"exclude": ["automation-bot"],
"aliases": { "developer@example.com": "alice" }
}
The ranking model guide documents every signal, option, default, fallback tier, and failure semantic.
Built to pass a security review, not just a demo:
npm audit signatures.apiUrl, private CAs via NODE_EXTRA_CA_CERTS, proxy guidance, and air-gapped installation paths.The enterprise guide states each claim in citable form, with the enforcement behind it.
Essential failures throw ReviewerError with a stable code; optional signal failures return warnings and partial: true instead of guessing. No suggestion is a valid outcome, not a network error. Assignment revalidates identities and eligibility, preserves existing reviewers, and reconciles ambiguous writes by re-reading the request rather than blindly retrying. The troubleshooting guide documents every code and its fix.
npm audit signatures.Releases follow Conventional Commits: fix publishes a patch, feat a minor, and breaking changes a major. Each release updates the committed changelog, publishes matching npm and GitHub versions, attaches the runnable Action bundle and an SPDX SBOM, and advances the compatible major Action tag only after synchronization checks pass. v1.2.3 is an immutable release; v1 intentionally moves to the newest compatible v1 Action. See release setup.
nvm install
nvm use
corepack enable
pnpm install
pnpm run verify
pnpm run build:action
Husky runs staged ESLint/Prettier checks and commitlint. CI repeats the checks independently. See CONTRIBUTING.md, security reporting, and the code of conduct.
MIT © Mohsen Madani.