JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 273
  • Score
    100M100P100Q69885F
  • License MIT

CLI that computes per-author GitHub language statistics by sampling commit file changes

Package Exports

  • github-lang-stats

Readme

github-lang-stats

CLI that computes per-author GitHub language statistics by inspecting the files changed in every commit authored by a given user.

Unlike GET /repos/{owner}/{repo}/languages (which returns repo-wide bytes regardless of who wrote them), this tool only counts lines in files you personally changed.

How it works

Phase API Cost
1. Discover repos GraphQL contributionsCollection ~20 calls (one per year)
2. Collect commit SHAs per repo GraphQL history(author: ...) ~1 call per 100 commits
3. Fetch per-commit file details REST GET /repos/:owner/:repo/commits/:sha 1 call per commit

Progress is cached in .github-lang-stats-cache/<user>.json so interrupted runs resume from where they left off.

Metric: lines_changed

For each file in each commit we count additions + deletions. This is a proxy for "language activity" — it's not as precise as bytes stored, but it correctly reflects only work you did.

Installation & usage

npx (no install)

npx github-lang-stats --user=<github-username> --token=<pat> --output=stats.json

Global install

npm i -g github-lang-stats
gls --user=<github-username> --token=<pat>
# or the long form:
github-lang-stats --user=<github-username> --token=<pat>

Programmatic API

npm i github-lang-stats
import { getGithubLangStats } from "github-lang-stats";

const stats = await getGithubLangStats({
  user: "octocat",
  token: process.env.GITHUB_TOKEN,
  fromYear: 2020,              // optional, defaults to 10 years ago
  excludeLanguages: ["JSON"],  // optional
  onProgress: (e) => console.log(e),
});

console.log(stats.totals); // { TypeScript: 412000, … }

Local development

npm install
npm run build
node dist/index.js --user=<github-username> --token=<pat> --output=stats.json

Token scopes required

Scope Why
repo Read access to private repositories
read:user Fetch verified email addresses for commit matching

Options

Usage: gls|github-lang-stats [options]

Options:
  -u, --user <username>      GitHub username (required)
  -t, --token <pat>          GitHub PAT (required)
  -o, --output <path>        Write JSON to file (default: stdout)
  --cache <path>             Override cache file path
  --no-cache                 Disable caching (start fresh each run)
  --concurrency <n>          Concurrent REST requests (default: 5)
  --from-year <year>         Earliest year to include (default: 10 years ago)
  --exclude-langs <langs>    Comma-separated languages to exclude (e.g. HCL,JSON)
  --select-repos             Interactively pick repos to analyse after commit counts are known
  --stats-only               Re-aggregate from cache without fetching new data
  --reset                    Delete cache and start fresh
  -V, --version              Print version
  -h, --help                 Print help

--select-repos interactive picker

When passed, after all commit SHAs have been collected the tool shows a full-screen checkbox list sorted by number of commits (highest first). All repos are pre-selected:

? Choose repos (42 total, sorted by commit count)
❯◉ myorg/monorepo                                       1 248 commits
 ◉ myorg/frontend                                         832 commits
 ◉ octocat/personal-site                                  201 commits
 ...
Key Action
space Toggle selected repo
a Toggle all (select all / deselect all)
i Invert selection
enter Confirm and continue

Output format

{
  "totals": {
    "TypeScript": 412000,
    "JavaScript": 88000,
    "Svelte": 43000
  },
  "byRepo": {
    "myorg/myrepo": {
      "TypeScript": 200000,
      "CSS": 5000
    }
  },
  "meta": {
    "user": "maxfriedmann",
    "generatedAt": "2026-02-20T10:00:00.000Z",
    "totalCommitsProcessed": 12450,
    "totalRepos": 47,
    "unit": "lines_changed"
  }
}

Using the output in the CV widget

The totals field maps directly to the githubLanguageTotals field in the CV widget schema — just copy it in.

Tips

  • First run is slow — at 5k req/hr with 30k commits it can take hours. Let it run overnight; it saves progress every 50 commits.
  • Subsequent runs are fast — only new commits since the last run are fetched.
  • Exclude infrastructure languages with --exclude-langs HCL,Dockerfile if teammates committed those to repos you also touched.
  • Adjust concurrency carefully — GitHub's secondary rate limits may trigger at high concurrency even if your primary limit is not exhausted. 5 is a safe default.