45 seconds, no sound. Watch it as a 1080p video.
Every commit, push and pull request across your GitHub organisation, as it happens: who made it (and with which AI agent), what changed, what is stuck, and what shipped. On one calm page that managers and engineers both read.
Big organisations push thousands of commits a day across hundreds of repositories. GitHub shows them one repository at a time, in screens built for the person who wrote the code. Track for Git puts the whole organisation on one timeline, in plain words, and stays fast and current while it does: GitHub's webhooks bring a push within seconds, and polling inside GitHub's rate limits fills in the rest.
It is free and open source (Apache-2.0). You host it yourself on Cloudflare Workers + D1 in a few minutes, or in Node or Docker anywhere else. It reads GitHub with read-only permissions. Your code activity stays in your own account, behind your own sign-in.
- The whole organisation on one timeline. Every repository (or the ones you choose), with lanes by repository, person or team. Each bar is split by who made the commits. Deploys from GitHub Deployments, releases and tags sit on the same line. Click a bar to read its commits.
- Who wrote the code, people and agents. Every commit and pull request says who made it, and with which AI agent: Dana Levi with Claude, Copilot, Ruth Ben with Cursor. Claude, Cursor, Codex, Copilot, Grok, Gemini, Devin, Jules, Aider, Windsurf, Amp and others are recognised out of the box, and you can add your own (below). See what share of the code each one wrote, filter by it, and follow it over time.
- Pull requests a manager can act on. Open, stuck (no activity for 7 days, configurable), merged and closed. Each shows its review state, CI status, age and size. Needs attention lists what is stuck, what fails CI and what waits on requested changes.
- Readable changes. For every commit and pull request: the files, the lines added and removed, and a clean diff with line numbers. Lock files and generated code start folded. Long diffs draw a few hundred lines at a time, so a 10,000-line change never freezes the page.
- Activity heatmaps by repository, person or team, per day in each viewer's own time zone (or the team's, with
TIME_ZONE). Each one can also be read as a table. - What is live now: the commit your newest successful deploy shipped, one click from its diff.
- The Story, for people who do not read diffs: each day in plain sentences. How much changed and by whom, the busiest repositories, the pull requests merged, what was deployed and released.
- Filters that stay in the link. Repository, team, person, agent, branch, path, label and free-text search. Any view can be shared as a URL.
![]() |
![]() |
![]() |
|
Light and dark, keyboard-navigable throughout, screen-reader labelled, built to WCAG 2.1 AA, English UI with every string in one file for translation. Every picture here comes from the built-in demo organisation: the people, the company and the code are made up.
Try it in one minute, with no token:
git clone https://github.com/qderai/track-for-git && cd track-for-git
npm run demo # http://localhost:8787: Acme, a made-up organisation, with commits arriving live(Node 22.13 or later; there is nothing to install for the demo.)
Run it for your organisation: press Deploy to Cloudflare above, or:
npm install
npx wrangler login
npm run deploy # creates the D1 database, applies the migrations, deploys the WorkerThen follow the one-page setup: connect GitHub with a read-only GitHub App (or a fine-grained token), add the webhook, put the dashboard behind Cloudflare Access or "members of this organisation only" GitHub sign-in, and turn the demo off.
Not on Cloudflare? docker compose up, or node server/node.js: the same app on SQLite (built into Node), with
the same settings.
Inside an app you already run? Mount it at a path of that app with track-for-git/embed: the page, its API
and the sync on your own Worker, database and sign-in, with your own deploys on the timeline and a Live now tile.
See Inside an app you already run.
Track for Git recognises an AI agent by the marks agents already leave: its own account (copilot-swe-agent[bot],
devin-ai-integration[bot]), the Co-authored-by: trailer many agents add (Claude Code does by default), a footer such as "Generated with Claude Code", or, for a member's pull request, the branch prefix it uses
(claude/, codex/, cursor/). Names are matched whole, so Claude Monet and Devin Smith stay people. A branch
name is never evidence on a public fork.
Any other agent shows up under its own name when its commits carry one of these trailers:
Assisted-by: Acme Coder
Generated-by: Acme Coder
AI-Agent: Acme Coder
Your company's own agents, with their own accounts or addresses, go in the AGENTS setting:
[{ "id": "forge", "name": "Forge", "logins": ["forge-agent[bot]"], "emails": ["@agents.acme.example"], "names": ["Forge"], "branches": ["forge/"] }]The six most common agents keep fixed colours, so a colour always means the same agent. Others share "Other agents"
in the charts but keep their own names everywhere else. The rules are in src/agents.js: a pull
request adding an agent is very welcome.
51 seconds, the demo organisation.
Qubi is the reviewer built into Track for Git. Give it your own model API key and a GitHub App of yours, switch it on in the dashboard's Qubi tab, and it:
- reviews every pull request and every push to the default branch (after the branch has been quiet for a few minutes, so a burst of pushes is one review), and posts a review with a comment on each line it found a bug on;
- runs bug hunts over a whole repository, or one folder of it, on a schedule (daily or weekly) or from Run now;
- opens a pull request with the fix for a finding it is sure of, when you turn that on (it is off by default). Nothing is merged, and nothing is run: a person reviews the fix.
It stays quiet about style. The prompt (src/qubi/prompt.js) asks for bugs and security holes
only, each with the file and line, a concrete failing input, what goes wrong, the fix and a severity, and tells the
model to read the callers and the validation in front of the code before it reports anything, to drop what it cannot
show a failing case for, and that finding nothing is a good result. Every finding must then quote its line word for
word: Qubi looks the quote up in the file at the reviewed commit and leaves out any finding whose quote is not there
(it also moves a finding to the right line when the model's line number was off). The review says how many it left
out.
Money. Every run has a cap, and so does every 7 days; pull requests from outside the organisation (forks,
strangers) have a smaller allowance of their own, per author and in total. The caps are checked on the server before
any run starts, each call to the model is limited to what the run's cap has left, and the cost is recorded after every
call, so a run that fails still counts. You set the caps in the dashboard, and the ceilings they cannot pass in the
environment (QUBI_MAX_RUN_USD, QUBI_MAX_WEEK_USD). With the defaults (Claude Haiku 5.5 for reviews, $0.25 a run,
$5 a week) a typical pull request review costs about a cent.
What it can touch. The model gets three tools: read a file, list a folder, and search the code, all at the reviewed commit. With a GitHub App, they read through a token minted for that one repository with read permissions only; posting the review, and opening a fix PR, use a second token after the model is done. Text from the pull request (its title, description, diff and code) reaches the model as material to review, never as instructions, and outside authors never get a fix PR. These measures reduce the room for prompt injection; they do not make a model's judgement infallible, so treat Qubi's findings and fixes as a reviewer's suggestions.
Set it up (the details are in docs/setup.md):
npx wrangler d1 migrations apply DB --remote # migration 0002 adds Qubi's tables (npm run deploy does this)
npx wrangler secret put QUBI_API_KEY # your Claude API key
npx wrangler secret put QUBI_GITHUB_APP_ID # a second GitHub App: Contents and Pull requests read & write
npx wrangler secret put QUBI_GITHUB_APP_PRIVATE_KEY
npx wrangler secret put QUBI_ADMINS # who may change Qubi and start paid runs: logins or emailsQubi uses the dashboard's own webhook (pushes and pull requests), so its App needs no webhook of its own. The key is never shown in the page, in the API or in a log; the Qubi tab shows only whether it is set.
Which model. QUBI_PROVIDER picks the API, and the key is QUBI_API_KEY (or the provider's usual variable):
QUBI_PROVIDER |
API | Models and prices |
|---|---|---|
anthropic (default) |
Claude API | Built in: Claude Haiku 5.5, Sonnet 5.5, Opus 5.5, Fable 5.1 |
openai |
OpenAI | You name them in QUBI_MODELS |
xai |
xAI (Grok) | QUBI_MODELS |
gemini |
Google Gemini (its OpenAI-compatible endpoint) | QUBI_MODELS |
mistral, deepseek, groq |
Mistral, DeepSeek, Groq | QUBI_MODELS |
openrouter |
OpenRouter: many providers behind one key | QUBI_MODELS |
openai-compatible |
Anything else with OpenAI's Chat Completions: Ollama, vLLM, LM Studio, a gateway (QUBI_API_URL) |
QUBI_MODELS |
QUBI_MODELS lists each model with its price in US dollars per million tokens, input/output and optionally cached
input, for example (made-up names and prices) QUBI_MODELS="model-large:3/15, model-small:0.3/0.5/0.075". The first is the default for reviews, the second for bug
hunts; a local model is free (llama3:0/0). Qubi does not run without prices, because the caps are only as good as
they are. Copy them from your provider's price page and update them when it changes.
Cursor has no model API of its own to point Qubi at (inside Cursor, "your own key" means Cursor calling your
provider), so it is not a QUBI_PROVIDER. Another API fits in one file (src/qubi/providers/):
a pull request adding one is welcome.
GitHub ──webhook (HMAC-SHA256)──▶ ┐
├──▶ Worker ──▶ D1 (metadata only) ──▶ the page (static, no framework)
GitHub ◀──GraphQL + REST, read-only, every minute──┘ └──▶ diffs on demand, cached, never stored
- Real time: webhooks for pushes, pull requests, reviews, checks, deploys, releases and tags. Every delivery is checked against its signature. Redeliveries are handled once. Repositories you do not watch are ignored.
- Polling as the fallback and the backfill, from a cron trigger once a minute. It lists the organisation's repositories newest-push first (an unchanged list costs nothing thanks to ETags), and touches only the repositories that changed. It asks GraphQL about five at a time in one request: their new commits with line counts, their pull requests with review decision and CI, their deploys, releases and tags. REST then fetches the pushes to other branches. Every answer's rate limit is read, a 10% reserve is kept for people opening diffs, and on a 403/429 it waits for the reset. With hundreds of repositories, a quiet minute usually costs one conditional request, which GitHub does not count against the limit.
- Stored: commit and pull request metadata (who, when, which repository and branch, how many lines, which paths), for 180 days by default. Never stored: diffs and file contents. They are fetched from GitHub when someone opens a commit and kept only in the HTTP cache.
- Fast with thousands of commits a day: every list is paginated with a keyset cursor and virtualised in the page (only the rows on screen exist). Answers are computed once every few seconds for everyone. Charts are aggregated in SQL, in the viewer's time zone, daylight saving included.
- Read-only GitHub permissions. The only POSTs to GitHub are GraphQL queries, the App's token exchange and the sign-in's code exchange. Qubi, the optional reviewer, writes (reviews, comments and, when allowed, fix PRs) through a GitHub App of its own, never through the dashboard's credentials.
- The dashboard refuses to show any data until you choose who may open it (
AUTH). With Cloudflare Access, the Worker also verifies Access's signed token on every API request. With GitHub sign-in, membership is checked with the dashboard's own credentials, and the person's GitHub token is dropped after sign-in. - Secrets are only ever environment secrets. They are never shown, logged or stored in the database.
- A strict Content Security Policy: the page loads nothing from any other origin (no CDN, no fonts, no avatars), and every message, name and line of code is inserted as text.
- Found a problem? See SECURITY.md.
npm test # node:test, against real SQLite with the real migrations
npm run demo # the app on Node with the demo
npm run dev # the app on wrangler dev with a local D1 (DEMO=1 by default)
npm run screenshots # the README's pictures (needs playwright-core and Chrome)
npm run build:embed # after changing public/: src/embed-assets.js, the page for track-for-git/embedsrc/ the Worker: app.js (routes), sync.js (polling), webhook.js, queries.js (the read side), agents.js,
auth.js, demo.js, embed.js (mounting inside another app), github/ (client, App sign-in, GraphQL,
payloads into rows)
public/ the page: no framework, no build; js/, styles.css, locales/en.js (every word)
server/ Node and Docker
migrations/ D1 / SQLite schema
To translate the page, copy public/locales/en.js, translate the values, and add the language in public/js/i18n.js.
Apache-2.0, copyright 2026 Qder AI. See LICENSE and NOTICE. The Qder.ai name and logo are trademarks of Qder AI and are not covered by the license.
Track for Git is not affiliated with or endorsed by the Git project or GitHub. Git is a trademark of the Software Freedom Conservancy; GitHub is a trademark of GitHub, Inc.
Built by Qder.ai. We built it for ourselves first, to watch our own engineers
and agents ship, then opened it up.





