Package Exports
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (team-roulette) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
team-roulette
Pick a reviewer from CODEOWNERS — locally, from a public owner/repo, or in the browser.
Web app: luandev.github.io/team-roulette/play · Source: github.com/luandev/team-roulette
Requires Node.js 18+ for the CLI. Zero runtime dependencies.
CLI
npm install -g team-roulette
team-roulette owner/repo # pick one owner
team-roulette owner/repo path/to/file # match a path
team-roulette -m owner/repo # pick with animation
team-roulette --list owner/repo # shuffle all owners
team-roulette -j owner/repo # JSON outputWeb
- Open the web app — enter
owner/repo, a raw URL, or upload CODEOWNERS - Path defaults to
*; choose pick, list, or sort - Run — full-screen result (memory in localStorage)
Public repos load over HTTPS from GitHub. No account, no backend.
The landing page replays CLI demos (asciinema .cast files) in the hero — click examples or use-case cards to switch recordings.
Install
# From npm (when published)
npm install -g team-roulette
# From source
git clone https://github.com/luandev/team-roulette
cd team-roulette && npm install && npm run build
npm linkWeb (local)
npm run dev:web # http://localhost:5173/team-roulette/play
npm run build:casts # regenerate web/public/casts/*.cast from CLI renderers
npm run build:web # build:casts + web/dist/Deploy: push to main — .github/workflows/deploy-pages.yml publishes web/dist to GitHub Pages.
Use cases
| Scenario | Command |
|---|---|
| PR reviewer — who reviews this path? | team-roulette owner/repo path/to/file.ts |
| Standup order — shuffle speakers | team-roulette --list owner/repo |
| On-call / incident — pick one platform owner | team-roulette kubernetes/kubernetes pkg/ |
| Driver vs navigator — one of several owners | team-roulette -m vercel/next.js |
| Ownership audit — who matches most rules? | team-roulette --sort frequency elastic/kibana |
| History fairness — who was picked least? | team-roulette --sort history owner/repo |
| CI / automation | team-roulette -j owner/repo |
| Workshop icebreaker | team-roulette -m apple/swift |
| Private / local file | Upload CODEOWNERS in the web app or run CLI in repo root |
GitHub repo shorthand
Pass owner/repo as the first argument. CODEOWNERS is fetched over pure HTTPS from raw.githubusercontent.com (no token needed for public repos). Branches tried: main, then master.
team-roulette microsoft/vscode
team-roulette grafana/grafana
team-roulette microsoft/vscode src/vs/workbench/| Behavior | Default |
|---|---|
| Match path | * (whole repo) when only owner/repo is given |
| Output | Selected: @handle or Selected: @org/team [team] (single pick, no animation) |
No global * rule |
Falls back to all owners in the file |
Add -m to enable pick animations (-m alone uses slot-machine; -m glitch for a specific style). If the token after -m looks like owner/repo, it is treated as the repo, not an animation name. Add --list to shuffle and display everyone.
Repos without GitHub CODEOWNERS (e.g. kubernetes/kubernetes) are loaded from root OWNERS + OWNERS_ALIASES (Kubernetes/Prow format).
Owner types
CODEOWNERS and Kubernetes OWNERS entries are classified automatically:
| Kind | Example | Label |
|---|---|---|
| User | @alice |
(none) |
| Team | @grafana/grafana-backend-services-squad |
[team] |
team@example.com |
[email] |
Picking a team means “this GitHub team owns the review” — not a random team member. Use --users-only to exclude teams from the pool, or --teams-only to pick only teams.
team-roulette grafana/grafana # may pick @grafana/… [team]
team-roulette --users-only grafana/grafana # only @username handles
team-roulette kubernetes/kubernetes # uses root OWNERS (not CODEOWNERS)The web app has an Owner pool filter (All / Users only / Teams only) with the same behavior.
Single selection
Picks one owner using fairness memory (unless --no-memory).
# Local CODEOWNERS (walks up for .github/CODEOWNERS)
team-roulette src/auth/login.ts
team-roulette .
# Remote shorthand
team-roulette microsoft/vscode src/vs/
# Animations
team-roulette -m slot-machine src/auth/
team-roulette -m glitch microsoft/vscode
team-roulette -m eliminator grafana/grafana
# Pure random (ignores history)
team-roulette --no-memory src/auth/
# Owner filters
team-roulette --users-only grafana/grafana
team-roulette --teams-only grafana/grafana
# JSON
team-roulette -j src/auth/login.ts
# { "selected": "@alice", "selectedKind": "user", "candidateKinds": {...}, ... }Pick animation modes (-m):
| Mode | Description |
|---|---|
slot-machine |
Three-row reel with deceleration (default when -m used) |
glitch |
Characters lock in left-to-right |
eliminator |
Names struck through until one remains |
List mode (--list / -l)
Shuffles all matching owners into a random order and reveals them with animation (no fairness pick — everyone is shown).
team-roulette --list microsoft/vscode
team-roulette -l src/auth/
team-roulette --list microsoft/vscode src/vs/workbench/
# List animations (default: cascade if -m omitted)
team-roulette --list -m slot-machine elastic/kibana
team-roulette --list -m glitch microsoft/vscode
team-roulette --list -m eliminator src/auth/
team-roulette --list -m cascade microsoft/vscode
# JSON (shuffled order in "selected" array)
team-roulette -j --list microsoft/vscode
# { "selected": ["@bob", "@alice", ...], "list": true, ... }List animation modes (--list -m):
| Mode | Description |
|---|---|
cascade |
Rows reveal top-to-bottom (default for --list) |
slot-machine |
Rows swap rapidly then lock in |
glitch |
Each row decrypts in sequence |
eliminator |
Live reorder with swap highlights |
Analytical sort (--sort)
No animation, no random pick — prints an ordered table for inspection.
team-roulette --sort alpha src/auth/
team-roulette --sort frequency microsoft/vscode
team-roulette --sort history src/auth/| Type | Order |
|---|---|
alpha |
Alphabetical |
frequency |
Most CODEOWNERS rule matches first |
history |
Least often picked first (memory log) |
Explicit sources (-s / --source)
# GitHub API (supports private repos with GITHUB_TOKEN)
team-roulette --source github:microsoft/vscode src/vs/
# GitLab API
team-roulette --source gitlab:gitlab-org/gitlab src/foo.rb
# Direct HTTPS URL to a CODEOWNERS file
team-roulette --source https://raw.githubusercontent.com/org/repo/main/.github/CODEOWNERS
team-roulette --list --source https://raw.githubusercontent.com/org/repo/main/.github/CODEOWNERSJSON output (-j / --json)
Always plain JSON, no ANSI. Field selected is used for all modes (agnostic naming):
| Mode | selected |
list |
|---|---|---|
| Single pick | "@handle" |
false |
--list |
["@a", "@b", ...] |
true |
Example:
{
"selected": "@joaomoreno",
"candidates": ["@alice", "@bob"],
"list": false,
"repoKey": "github:microsoft/vscode",
"path": "*",
"mode": "slot-machine",
"memory": true,
"source": "microsoft/vscode"
}Fairness memory
Single-select mode stores history at:
~/.config/team-roulette/history.json
(or $XDG_CONFIG_HOME/team-roulette/history.json)
- Excludes the last picked owner when multiple candidates exist
- Prefers owners with the lowest pick count in history
- Random tie-break
--no-memory disables this. --list does not write to history.
Environment variables
| Variable | Purpose |
|---|---|
GITHUB_TOKEN |
Auth for --source github: (API path) |
GITLAB_TOKEN |
Auth for GitLab API |
NO_COLOR |
Disable ANSI colors |
XDG_CONFIG_HOME |
Override config directory |
All CLI flags
-l, --list Shuffle & display all owners (animated)
-s, --source <target> github:owner/repo | gitlab:group/project | https://...
-m, --mode <template> Animation mode (pick or list)
--no-memory Random single pick (no history)
--sort <alpha|frequency|history> Ordered table output
-j, --json JSON output
-h, --help
-v, --versionAdding animations
Single pick — implement AnimationTemplate in src/animations/ and register in src/animations/registry.ts.
List — implement ListAnimationTemplate in src/animations/list/ and register in src/animations/list-registry.ts.
Development
npm install
npm test
npm run build
node dist/bin/team-roulette.js --list microsoft/vscode
node dist/bin/team-roulette.js -j --list -m glitch microsoft/vscodeCommits and releases
This project uses Conventional Commits and semantic-release for automated semver and GitHub releases.
feat: add new feature → minor bump
fix: patch bug → patch bump
feat!: breaking change → major bumpExamples: feat(web): add file picker, fix(cli): correct path matching
npm publish (CI)
Pushes to main run .github/workflows/release.yml, which publishes team-roulette to npm when there are releasable commits.
GitHub secret: NPM_TOKEN — create at npmjs.com → Access Tokens:
- Granular token: Packages → Read and Write for
team-roulette(or all packages on first publish) - Classic token: type Automation (not Publish — that is for local login)
Paste the token into Repository → Settings → Secrets → Actions → NPM_TOKEN. Re-run the failed Release workflow or push a fix: / feat: commit after updating the secret.
npm install -g team-roulette
team-roulette owner/repoLicense
MIT