Lightweight GitHub Copilot usage dashboard for teams. It reads raw metrics exposed via new Github REST API endpoints for Copilot usage metrics, aggregates them per user, and serves a browser-based leaderboard-style dashboard with filters and trend hints.
Detailed documentation on every metrics
The dashboard tracks activity across two Copilot interfaces:
- IDE plugin — chat asks, code completions, agent/coding-agent runs; favorite IDE with version, language, model
- CLI — GitHub Copilot CLI prompt counts, active days via CLI, and last known CLI version (shown in user detail popup)
⚠️ Disclaimer: this is a fully vibe-coded project that did not go through comprehensive code review or testing. Results may be inaccurate, and bugs are possible.
- Github Copilot built-in dashboards are still in Public Preview, they are yet rudimentary and require special access rights difficult to obtain in large enterprises.
- Existing external dashboards (by Github, by Microsoft) were not promptly updated for new REST API compartibility and likely stop working on April 2, 2026 when Github sunsets its legacy Github Metrics API.
Dashbord aims at AI/Agile Coaches, Teams leaders, Engineering managers, Project- and Delivery managers, Procurement associates and helps themto quickly answer questions like:
- Who is actively using Copilot and who is not?
- Which models/IDEs/languages are most used?
- How usage differs across teams and time?
This repository is provided with mocked data stored under mock\*.jsonfor demonstration and development.
- Runtime: Node.js (CommonJS)
- Backend: built-in
http,fs,httpsmodules (no framework) - Frontend: vanilla HTML/CSS/JavaScript
- Data source: GitHub REST API
- Storage: local JSON/NDJSON files (
data/*.json)
Project was intentionally built simple and file-based, so you can run it locally without infrastructure or implement your own data persistancy layer.
server.js— starts the web server and serves API + static UIupdate-data.js— fetches Copilot metrics, stores raw snapshots underdata/raw/*.json, and reconciles them intodata/data.jsoningest-data.js— imports user-provided NDJSON files fromdata/raw/inbox/intodata/data.jsonwithout calling the GitHub APIdebug.js— downloads hisotrical data todata/debug/*.jsonand compares it with localdata/data.jsondata/config.json— stores enterprises and organizations with their slugs, token variable names, and last-sync statedata/users.json— UserId mapping to Display name, Team, Role, Revoked status (all optional)data/teams.json— team ID mapping to display title, business unit, and manager namedata/data.json— all your data used to build a dashboarddata/raw/inbox/— drop your own NDJSON files here for ingestdata/raw/processed/— files are moved here automatically after successful ingestpublic/— dashboard UI assetsdocs/– documentation and screnshots
- Node.js 18+
npm installCopy .env.example to .env:
cp .env.example .envBy default, .env.example contains USE_MOCK_DATA=true, so the app loads config/data/users from mock/*.json for local demo/development.
npm run devOpen http://localhost:3000 - you should see mocked data loaded.
- You created one or more GitHub personal access tokens (classic) with the appropriate scopes:
- Enterprise-level metrics:
read:enterpriseormanage_billing:copilot - Organization-level metrics:
read:orgormanage_billing:copilot
- Enterprise-level metrics:
- Copilot usage metrics policy must be enabled.
-
Add your token variable(s) to
.env(use.env.exampleas a template). Each token variable name is referenced fromconfig.jsonvia theenv_tokenfield, for example:GITHUB_ENTERPRISE_TOKEN=ghp_... GITHUB_ORG_TOKEN=ghp_... -
Set
USE_MOCK_DATA=falsein.envbefore productive use, so data is loaded from/datainstead of/mock. -
Copy
mock/config.jsontodata/config.json -
Edit
data/config.json. Each enterprise and organization entry that should be synced via the API must have:slug— the GitHub enterprise or organization slug used in API URLslabel— optional display name used in UI/logs; when omitted,slugis used as fallbackenv_token— name of the.envvariable holding the access token for that scopelast_report_day— set to""on first run; updated automatically after each syncmissing_data_days— leave as[]; managed automatically
Entries without an
env_tokenfield are skipped during API sync (useful for display-only entries).Enterprise and organization metrics are fetched independently — enterprise-level data is pulled first, then each org under it.
-
(optionally) Edit
data/users.jsonto map GH usernames/accounts to display names, teams, roles, revoked status, and optional emails.Current schema is an array:
[ { "accounts": ["github-login", "secondary-login"], "name": "Display Name", "team": "Team Name", "role": "Senior Developer", "revoked": false, "emails": ["user@company.com"] } ]Notes:
accountsshould contain all known logins for the same person (first entry is canonical in UI grouping).emailsis optional; values are displayed and can be used for account cleanup workflows.- Account logins should be lowercase for reliable matching.
-
(optionally) Edit
data/teams.jsonto provide human-readable titles, business units, and manager names for each team referenced inusers.json.Current schema is an array:
[ { "id": "my_team_id", "title": "My Team", "unit": "Business Unit", "manager": "Jane Smith" } ]Notes:
idmust match theteamfield value used inusers.json.titleis shown in the team filter dropdown, table rows, and user detail popup.unitis used to group teams in the dropdown (optgroups) and displayed alongside the title.manageris stored but not currently rendered in the UI (reserved for future use).- Teams missing from
teams.jsonfall back to displaying their raw ID.
npm run updateThis runs update-data.js, which for each configured enterprise and organization (those with an env_token):
- fetches the latest 28-day report from the enterprise endpoint, then from each org endpoint,
- backfills any calendar gaps via per-day API calls,
- saves raw NDJSON files to
data/raw/, - reconciles every downloaded record with
data/data.jsonbyuser_id:day: new records are added, identical records are left unchanged, and real telemetry changes replace the prior NDJSON line, - ignores the rolling
report_start_dayandreport_end_dayexport metadata when comparing records; metadata-only changes neither replace a stored line nor produce a log entry, - logs each replacement and its changed telemetry fields, so late corrections are visible in the update output,
- updates
last_report_dayandmissing_data_daysindata/config.jsonfor each scope.
flowchart LR
A[GitHub Copilot Metrics API] --> B[update-data.js\nnpm run update]
B --> C[data/raw/*.json\nraw daily snapshots]
B --> D[data/data.json\nreconciled NDJSON history]
B --> E[data/config.json\nlast_report_day advanced]
J[User-provided NDJSON\nvia email / SFTP / etc.] --> K[data/raw/inbox/]
K --> L[ingest-data.js\nnpm run ingest]
L --> D
L --> M[data/raw/processed/\narchived after ingest]
F[data/users.json\nuser/team mapping] --> G[server.js]
N[data/teams.json\nteam titles & units] --> G
D --> G[server.js]
E --> G
H[public/* UI] --> G
G --> I[Dashboard in browser]
This integration is not real-time. GitHub Copilot metrics are published as daily NDJSON-style reports, and new files typically appear in the API earliest on the next business day. In practice, reporting delays of 24 hours or more are normal. IDE telemetry is asynchronous and usually settles within three complete UTC days, so each update reconciles the entire latest 28-day report instead of treating previously imported days as final. This lets later exports replace preliminary zero or incomplete metrics with their corrected values.
npm startOpens http://localhost:3000 - you should see real data loaded.
In many enterprise environments developers do not have direct access to the GitHub API — metrics files are instead delivered by a central team via email, SFTP, shared drive, or a similar intermediary channel. The npm run ingest command covers this use-case.
Files must be in the same NDJSON format produced by the GitHub Copilot Metrics API (one JSON object per line, each with at least user_id and day fields).
Create the inbox folder before first use (the script also creates it automatically on first run):
mkdir -p data/raw/inbox
mkdir -p data/raw/processed- Place one or more NDJSON files into
data/raw/inbox/. - Run the ingest command:
npm run ingestThe script will:
- Parse every
*.jsonfile found indata/raw/inbox/ - Skip any record whose
user_id:daykey already exists indata.json(no duplicates) - Append genuinely new records to
data/data.json - Move each processed file to
data/raw/processed/so it is not ingested again
- Start (or restart) the dashboard:
npm startOr as a single command sequence:
npm run ingest && npm startYou can drop multiple files at once — all are processed in a single run. If a filename collision occurs in processed/, a counter suffix is added automatically.
npm start— run the dashboard server (node server.js)npm run dev— same as start (no watcher currently)npm run update— fetch and reconcile Copilot metrics from the GitHub API; logs any revisions to previously stored user/day recordsnpm run ingest— import user-provided NDJSON files fromdata/raw/inbox/intodata/data.json
debug.js is an all-in-one debug and analysis utility. Run it without arguments to see all available commands.
User-level verification — re-downloads raw data from the API and compares it against data.json:
node debug.js YYYY-MM-DD— re-download one day and comparenode debug.js latest— re-download last 28 days and compare
Org-level metrics — fetches and analyses org-wide aggregated data (active-user counts, breakdowns by IDE/feature/model):
node debug.js org fetch— download latest 28-day org metricsnode debug.js org discover— compare key vocabulary (IDEs, models, features) vsdata.jsonnode debug.js org compare YYYY-MM-DD— check day totals discrepancy: user aggregate vs org
All downloads are saved to data/debug/.
- No database (file-based storage only)
- No auth/access control (deploy locally or in secure environment)
- Update process is not automatic:
- You must run
npm run updatemanually, or - schedule it externally (cron, CI job, task scheduler)
- You must run
- Limited validation and error handling
- No tests
- Metrics interpretation is generic and may not match your KPIs
Contributions are welcomed to address the issues and bring more features.
Licensed under the MIT License.
