How counting works.
Public repos are counted from GitHub every night. Everything else is counted on your own computer by a small tool that reads your git history and sends only numbers. This page is the whole manual.
Setup
Needs Node.js 18+ and git. Sign in on the site first, then in a terminal:
Want the numbers first? npx @yaroslavhaidash/gitstats-cli@latest stats runs before link, with no account: it prints the same table and sends nothing.
- A browser tab opens asking you to confirm the link for this computer. It opens in whichever browser your OS calls default, so check the account named on that page before you confirm. If it is not you, hit not you? sign out first. The terminal then prints the account it paired as on its own line. To settle it up front: npx @yaroslavhaidash/gitstats-cli@latest link --user <your-login> refuses the pairing if the browser confirms as anyone else.
- It scans your home folder for git repos and counts your commits for the last year. The first sync after linking also counts each repo's full history, once.
- It prints every repo it found with totals and asks Upload? [Y/n]. Nothing is sent before you answer.
- It installs a background sync (macOS launchd, Windows Task Scheduler, Linux systemd user timer) that re-runs every day, also after the machine was off.
Repos outside your home folder: npx @yaroslavhaidash/gitstats-cli@latest link --root /path --root /other. Run it on every computer you commit from.
Commands
Two ways to run them. Always works: npx @yaroslavhaidash/gitstats-cli@latest <command>. Shorter: add ~/.gitstats/bin to your PATH once and use gitstats <command>.
# zsh / bash, once:
echo 'export PATH="$HOME/.gitstats/bin:$PATH"' >> ~/.zshrc && source ~/.zshrcstats- Count this machine and print the table, without pairing with anything. No account, no upload, no config file, no network call at all. It reads git and exits. Run it first if you want to see the numbers before you decide. Options: --root <dir> and --email <addr> (both repeatable), --fetch (refresh each repo from its origin first).
link- Pair this computer, scan for repos, show what it found, upload after you confirm, install the background sync. Re-running replaces the previous link. Options: --root <dir> (repeatable, default: your home folder), --yes (skip the confirmation).
sync- Recount the last year and upload now. Safe to run any time; every run replaces the previous numbers, so amends and rebases correct themselves.
status- Show server, account, machine, scanned folders, emails, and when the last sync ran.
pause- Stop the background sync. Nothing is deleted; `sync` still works by hand.
resume- Start the background sync again.
roots add <dir>- Also scan this folder (for repos outside your home directory). Syncs right away.
add <path>- Track one specific repo wherever it is. Syncs right away.
emails add <email>- Count commits made with another email (a work address, an old one). Syncs right away.
names on- Also send repo names, so your own page shows them instead of private-xxxx. Whether others see names is a separate setting on your profile.
names off- Stop sending names. Already-stored names stay until the next sync replaces them.
claude on- Count your Claude Code usage from its own logs on this computer again after `claude off`. On by default on every linked computer, unless you turned it off in settings. It sends only numbers per UTC day per model: input, output, cache-read and cache-write tokens, replies, sessions, effort levels, tool calls, subagent share, replies per 6-hour block and how many messages you typed, never your messages, Claude's answers, paths or project names. The first sync counts every log Claude Code still keeps. Options: --yes (skip the confirmation).
claude off- Stop sending Claude Code usage. What was already sent stays until you delete it.
claude- Print the last 14 days of Claude Code usage from this computer's logs (replies, sessions, messages, tool calls, tokens, output, then effort, 6-hour blocks and subagent share), counted the way a sync counts them. Sends nothing.
update- Install the latest published version now. Every sync also checks once a day on its own; config and schedule are untouched.
unlink- Revoke this computer on the server, remove the background sync and the local config. Your uploaded numbers stay on the board until you delete your account.
What leaves your computer
- a keyed hash of the remote URL
- HMAC-SHA256 with a secret unique to you. Same repo on two of your machines = one entry. The server cannot turn it back into a URL; it can only confirm a guess.
- a language guess
- From the extensions of files you changed, lockfiles ignored.
- weekly numbers
- For each Sunday-to-Saturday week: commits, lines added, lines deleted. Your commits only, on the default branch, merge commits excluded.
- commits and lines per day
- For each date: how many commits you made and how many lines you added and deleted, so private repos show on your calendar and in the daily charts.
- pending work
- The same numbers again for commits that only exist on a branch the default branch has not taken in yet. Counted from your pushed branches only (a remote branch whose last commit is under 30 days old), so stale local branches and squash-merged worktrees do not pile up. Shown separately under the numbers on your page, to you and to anyone allowed to see that repo's numbers, and never counted on any board.
- repo name
- Only if you ran `names on`. Off by default.
Never sent:
- file contents
- diffs
- file or folder names
- branch names
- commit messages
- other people's commits
- anything about repos you did not commit to
No GitHub token is created and nothing gets access to your GitHub account. The tool runs as you, on your machine, like `git log` does. The source is public at github.com/yaroslavhaidash/gitstats-cli (one file, under a thousand lines, MIT). Read it before you run it, or watch its traffic with any proxy.
Claude Code usage (claudestats)
On with every linked computer, off in settings. The same CLI also counts your Claude Code usage from the logs Claude Code keeps on your computer, and claudestats puts it on a board next to your crew's.
- What is claudestats?
- The other side of this site, behind the logo menu: the same crews and boards, ranked by Claude Code usage instead of commits. It counts Claude Code in the terminal and in the desktop app's Code tab, both of which write the same logs. claude.ai chat is not in those logs and is not counted.
- What does TOKENS count?
- Every token Claude Code sent to the model or got back: input, cache write, cache read and output, as its own logs record them. Cache reads are most of it, usually over 95%: on every turn Claude Code sends the conversation so far again, mostly from the cache. So TOKENS measures how much you used it, turns times context, and matches what other usage tools print.
- What does OUTPUT count?
- Only what the model generated, thinking included. It does not grow with the size of the context, so it is the closer measure of how much Claude wrote for you. Pick it on the tabs and the board orders by it.
- What does MESSAGES count?
- The prompts you typed into Claude Code, one per message you sent: not tool results, not slash command output, not the summary Claude Code writes when it compacts a conversation, not prompts a subagent got. A message counts under the model of the reply that answered it. Days a CLI before 0.7.0 sent have no message counts and show as not counted, never as zero; 0.7.0 sends every log Claude Code still keeps once, so most days fill in.
- What is effort?
- The reasoning effort Claude Code ran each reply at (low, medium, high, xhigh, max), from its own logs. Replies without one are shown as not set.
- What are subagents and 6-hour blocks?
- Subagent share is the part of your tokens and replies that ran in subagents (Task and agents) rather than in your conversation. The 6-hour profile counts replies in 00 to 06, 06 to 12, 12 to 18 and 18 to 24 of your computer's own clock: four blocks a day, never a time.
- What is a session?
- One Claude Code conversation, counted on the UTC day of its first reply. A resumed or forked conversation that gets a new session id counts again on its first day. Subagents are part of their parent's session.
- What is sent, and what never is?
- Per UTC day and per model: the four token counts, replies and sessions, replies and output per effort level, tool calls, subagent tokens and replies, replies in each 6-hour block of your local day, messages typed, the highest Claude Code version seen, and a keyed hash that tells your computers apart. Never your prompts, Claude's replies, code, tool names, file or project names, session or message ids, timestamps, or anything finer than those four blocks of a day.
- How do I turn it off?
- It is on for every linked computer. Turn it off for all of them in settings (each computer stops after its next sync), or on one computer with `claude off`; `claude on` turns that one back on. Settings also deletes everything already sent; turn counting off first, or the next sync sends the last week back. `claude` prints what would be counted and sends nothing.
- How far back does it count?
- Claude Code deletes old conversation logs after a while (its cleanupPeriodDays setting). The first sync counts every log still on the computer, and a day once counted stays here after Claude Code removes its log, but days older than your logs cannot be counted.
- Two computers?
- Both count; each one only ever replaces its own numbers. The one exception: if you sync your ~/.claude folder between computers (iCloud, Dropbox, dotfiles), run `claude off` on all but one of them, or the same days count twice.
Use gitstats from Claude Code, Codex, Cursor
gitstats is a remote MCP server at https://gitstats.org/api/mcp, so an assistant can answer "how was my week", "which repo took most of my time this month" or "am I ahead of my crew". It is read-only and sees exactly what you see on the site: your own numbers, the boards of your crews, and another member's summary only where their page is open to you. Repos counted without their name stay private-<hash>.
claude.ai (web, desktop, mobile)
Add to claude.ai as a custom connector: Customize → Connectors → Add custom connector, name it gitstats, URL https://gitstats.org/api/mcp, leave the OAuth client settings as they come, Add, then Connect. You sign in with GitHub on a gitstats screen that asks whether to connect Claude; no token to copy. On Team and Enterprise an owner adds it under Organization settings → Connectors first. Disconnect it any time in settings.
For the others, create a token under settings → AI assistants (it is shown once), then:
Claude Code
claude mcp add --transport http gitstats https://gitstats.org/api/mcp --header "Authorization: Bearer <token>"
Codex
export GITSTATS_MCP_TOKEN=<token> codex mcp add gitstats --url https://gitstats.org/api/mcp --bearer-token-env-var GITSTATS_MCP_TOKEN
Codex reads the token from that variable on every start, so export it in your shell profile.
Cursor
In ~/.cursor/mcp.json (or .cursor/mcp.json in one project), with GITSTATS_MCP_TOKEN set in your environment:
{
"mcpServers": {
"gitstats": {
"url": "https://gitstats.org/api/mcp",
"headers": { "Authorization": "Bearer ${env:GITSTATS_MCP_TOKEN}" }
}
}
}Tools: my_summary, my_daily (days a linked computer counted, kept apart from weekly figures laid over their days), my_repos, my_crews, crew_board, member_summary. Each takes a window (week, month, year, all, or from/to dates; my_daily does not take all) and lines or commits. 60 calls a minute per token. Revoke the token or disconnect the app in settings and the next call fails. Any client that speaks MCP OAuth can also connect without a token: point it at the URL and it will send you to the same sign-in.
Questions
- Can I see the numbers without signing up?
- Yes. `npx @yaroslavhaidash/gitstats-cli@latest stats` scans this computer, prints the per-repo table and the year's totals, and exits. It pairs with nothing, uploads nothing, writes no config and makes no network call. It only reads your git history. If you like what you see, run `link`.
- Is my work repo safe?
- Yes. Only weekly counts of your own commits leave the machine, under a keyed hash instead of the repo name. No content, names, paths, or branch names. Nothing touches your employer's GitHub org.
- Which commits count?
- Commits on the default branch (origin/main or whatever origin/HEAD points to) authored by one of your emails, last 365 days, merge commits excluded. Squash-merged PRs count once. Work sitting on an unmerged branch is counted separately as “pending” by a linked computer: it shows as a faint line under the numbers on your page and as hatched caps on your lines-per-week chart, visible to whoever may see those repos’ numbers, which is your crewmates when you share private repos with them, and it never counts toward a board ranking. Only branches you have pushed count, and only while their last commit is under 30 days old; branches that live on your laptop alone are ignored. On the board, WEEK means this calendar week (Monday 00:00 UTC up to today, not the last seven days), MONTH means the 1st of this month up to today, YEAR is the last 365 days, and ALL is everything since 2000.
- What does ALL count?
- Public repos: GitHub's full history, written once per member by the nightly job, oldest year last. Private repos: what a linked computer counted, which is every repo still cloned there, under the emails you configured, since the first sync after linking counts each repo's full history once. GitHub's side has limits: a repo with more than 10,000 commits reports 0 lines (its commits still count), only a repo's top 100 contributors are listed, only 100 repos a year are found per member, commits count only under an email linked to your account and only on the default branch, and work in forks rarely counts. Where GitHub only knows a week, the week is laid over its days and drawn hatched. The tiles show no percentages on ALL, since everyone grows by a fraction of a percent a week.
- Some of my commits are missing.
- Usually the email. Run `status` to see which emails are matched, then `emails add you@work.com`. Or the repo is outside your home folder: `roots add /path`.
- Why are the line counts huge?
- Lockfiles, generated code and vendored files count as lines, same as on GitHub. The language guess ignores lockfiles, the line totals do not.
- I have several worktrees or clones of one repo.
- Counted once. The primary clone wins; they all read the same origin/HEAD.
- Two laptops?
- Run `link` on both. The same repo from both machines is one entry, whichever synced last wins, and they agree because both read origin/HEAD.
- How do I stop it?
- `pause` keeps the link but stops the background runs. `unlink` revokes the computer, removes the schedule and the local config. You can also hit REVOKE on your settings page; the machine's next sync then fails harmlessly.
- How does it update?
- On its own, daily. Every sync checks the npm registry at most once a day and, when there is a newer version, installs it and finishes the sync under it. Your config and the schedule are untouched. If the check fails you keep the version you have and the reason lands in ~/.gitstats/sync.log. `gitstats update` forces it now, `gitstats sync --no-update` skips the check.
- Who sees what?
- Your settings page has a two-column matrix: crewmates on one side, everyone else signed in on the other. Per column you pick whether they can open your page, whether private repos count in the numbers they see, and whether repo names are shown, public-only, or hidden. New accounts start open on the numbers and closed on the names: your page is open to everyone signed in, private repos count in both columns, crewmates see public repo names and everyone else sees none. On your own page each repo also has a [hide name] switch that beats the matrix, for the one client repo you would rather not name.
- Can I get my data out, or delete it?
- Both, on your settings page under Data. EXPORT downloads every row we hold about you as JSON: profile, crews, linked computers, weekly and daily numbers. DELETE ACCOUNT asks you to type your login, then removes all of it from the site and signs you out; a crew you started passes to whoever joined first, and an empty one is deleted. One copy is kept for 30 days, readable only by the site owner, so a delete pressed by mistake can be undone; after that the nightly job drops it. Run `unlink` on each computer to clear the local config as well.
- Where does it keep things?
- ~/.gitstats/config.json (your link token, mode 600), ~/.gitstats/cli (the installed copy), ~/.gitstats/sync.log (background runs), plus one launchd plist / scheduled task / systemd timer, named after the config directory (com.gitstats.sync.<hash> on macOS) so a second install under another HOME cannot replace or remove this machine's job.