<!-- Generated by scripts/generate-badge-docs.mjs from src/badges.js — edit the catalog, not this file. -->

# Badge setup

Every badge in the template, what it actually measures, and what you have to do
outside this repo before it shows anything real.

The CLI leaves out any badge whose inputs it does not have, and prints which ones
it left out and why. Re-run it with the flag from the table below to add one:

```bash
bunx template-git-repo --badges-only --discord-id 1110227955554209923 --discord-invite https://discord.gg/xxxx
```

The block lives between `<!-- template-git-repo:badges:start -->` and
`<!-- template-git-repo:badges:end -->` in your README. Re-running replaces what is
between the markers and nothing else, so it is safe to run again after you publish a
package or finish setting up Codecov.

## The four rows

The block renders one row per group, in this order. The split is by the question a
reader is asking, not by where the badge is hosted — stars sit with the download
counts because both answer "is anyone using this", and the sandbox buttons sit at
the bottom so they do not compete with the link to the live app.

| Row | Carries |
| --- | --- |
| Row 1 — what this is and where to try it | `doi`, `deepwiki`, `website`, `docs`, `api`, `youtube`, `uptime`, `deploy-cloudflare` |
| Row 2 — is it used, is it working, is it shipped | `stars`, `npm-downloads`, `npm-version`, `npm-total-downloads`, `npm-types`, `install-size`, `codecov`, `codecov-flag`, `workflow`, `test-report` |
| Row 3 — is it alive, who is behind it | `contributors`, `forks`, `issues`, `pull-requests`, `prs-merged`, `discussions`, `commit-activity`, `last-commit`, `discord` |
| Row 4 — run it yourself, and what it is built with | `stackblitz`, `codespaces`, `prs-welcome`, `license`, `stack` |

A row whose badges are all missing or excluded is dropped rather than rendered
empty, so a repo with nothing published still gets a tidy three-row block.

## Index

| Badge | Needs | Flag |
| --- | --- | --- |
| [Zenodo DOI](#doi) `doi` | doi | `--doi <10.5281/zenodo.NNNNNNN>` |
| [Ask DeepWiki](#deepwiki) `deepwiki` | — | on by default |
| [Live app](#website) `website` | websiteUrl | `--website <url>` |
| [Documentation](#docs) `docs` | docsUrl | `--docs <url>` |
| [API reference](#api) `api` | apiUrl | `--api <url>` |
| [YouTube demo](#youtube) `youtube` | youtubeUrl | `--youtube <url>` |
| [Uptime](#uptime) `uptime` | uptimeUrl | `--uptime <url>` |
| [Deploy to Cloudflare Workers](#deploy-cloudflare) `deploy-cloudflare` | cloudflareDeploy | `--cloudflare-deploy` |
| [GitHub stars](#stars) `stars` | — | on by default |
| [npm monthly downloads](#npm-downloads) `npm-downloads` | npmPackage | `--npm-package <name>` |
| [npm version](#npm-version) `npm-version` | npmPackage | `--npm-package <name>` |
| [npm total downloads](#npm-total-downloads) `npm-total-downloads` | npmPackage | `--npm-package <name>` |
| [TypeScript types](#npm-types) `npm-types` | npmPackage | `--npm-package <name>` |
| [Install size](#install-size) `install-size` | npmPackage | `--npm-package <name>` |
| [Code coverage](#codecov) `codecov` | — | on by default |
| [Coverage for one package](#codecov-flag) `codecov-flag` | codecovFlag | `--codecov-flag <name>` |
| [CI status](#workflow) `workflow` | workflowFile | `--workflow <file>` |
| [Hosted test report](#test-report) `test-report` | testReportUrl | `--test-report <url>` |
| [Contributors](#contributors) `contributors` | — | on by default |
| [GitHub forks](#forks) `forks` | — | on by default |
| [Open issues](#issues) `issues` | — | on by default |
| [Open pull requests](#pull-requests) `pull-requests` | — | on by default |
| [Merged pull requests](#prs-merged) `prs-merged` | — | on by default |
| [GitHub Discussions](#discussions) `discussions` | — | on by default |
| [Commit activity](#commit-activity) `commit-activity` | — | on by default |
| [Last commit](#last-commit) `last-commit` | — | on by default |
| [Discord](#discord) `discord` | discordId, discordInvite | `--discord-id <id>` `--discord-invite <url>` |
| [Open in StackBlitz](#stackblitz) `stackblitz` | stackblitzUrl | `--stackblitz <url>` |
| [Open in GitHub Codespaces](#codespaces) `codespaces` | — | on by default |
| [PRs welcome](#prs-welcome) `prs-welcome` | — | on by default |
| [License](#license) `license` | — | on by default |
| [Tech stack chips](#stack) `stack` | stack | `--stack <A,B,C>` |

## Row 1 — what this is and where to try it

### doi

**Zenodo DOI**

Sign in to zenodo.org with GitHub, flip this repository on under Account → GitHub, then publish a GitHub Release. Zenodo archives the release and mints a DOI. Use the *concept* DOI (the one that always resolves to the newest version), not the per-release DOI.

Enable with: `--doi <10.5281/zenodo.NNNNNNN>`

### deepwiki

**Ask DeepWiki**

Nothing to configure for a public repo — visit deepwiki.com/<owner>/<repo> once to trigger the first index. Private repos need the DeepWiki GitHub App installed.

Included by default — no configuration needed.

### website

**Live app**

The deployed thing, not the repo. It is first in the row on purpose: a visitor who can click through to a running app decides in seconds whether to read the rest. Static badge — nothing checks that the URL is up, which is what the uptime badge is for.

Enable with: `--website <url>`

### docs

**Documentation**

Point this at wherever your docs are actually hosted. This is a static shields.io badge — it says "Docs" whether or not the link works, so it is on you to keep the URL alive.

Enable with: `--docs <url>`

### api

**API reference**

Point this at your OpenAPI / Swagger page. Static badge, same caveat as Docs.

Enable with: `--api <url>`

### youtube

**YouTube demo**

A demo video does more for a README than three paragraphs. Any YouTube URL works.

Enable with: `--youtube <url>`

### uptime

**Uptime**

Create a monitor at uptimerobot.com, then a public status page, and link the status page here. This is a static badge — it says "brightgreen" even while you are down. For a live one use the UptimeRobot shields endpoint with a read-only API key.

Enable with: `--uptime <url>`

### deploy-cloudflare

**Deploy to Cloudflare Workers**

The repo must contain a wrangler.toml (or wrangler.jsonc) at the path the button clones. Cloudflare forks the repo into the visitor's account and runs the build, so anything the build needs must come from `[vars]` or be prompted for — a build that requires a secret fails for every visitor.

Enable with: `--cloudflare-deploy`

## Row 2 — is it used, is it working, is it shipped

### stars

**GitHub stars**

Nothing to configure. Public repos only — shields.io cannot read a private repo.

Included by default — no configuration needed.

### npm-downloads

**npm monthly downloads**

Requires at least one published version. In a monorepo pick the package you want to advertise — the badge counts one package, not the workspace.

Enable with: `--npm-package <name>`

### npm-version

**npm version**

Shows the `latest` dist-tag. Publishing under a different tag will not move it.

Enable with: `--npm-package <name>`

### npm-total-downloads

**npm total downloads**

All-time downloads for one package. Worth pairing with the monthly count rather than showing alone: a large total and a flat month say different things, and only the monthly number tells you the project is still being installed.

Enable with: `--npm-package <name>`

### npm-types

**TypeScript types**

Reads the published tarball: it says "TypeScript" when the manifest has a `types`/`typings` field pointing at a real `.d.ts`, and stays grey when the declarations were left out of `files`. Publish once and check it — this is the badge that catches a build that shipped JS without its types.

Enable with: `--npm-package <name>`

### install-size

**Install size**

Nothing to configure, but read what it measures: packagephobia reports what `npm install` writes to disk, dependencies included — not the bundled browser size. A package whose one dependency is an AWS SDK client looks enormous here and tree-shakes to very little in a bundler.

Enable with: `--npm-package <name>`

### codecov

**Code coverage**

Add the repo at codecov.io, copy its upload token into a CODECOV_TOKEN repository secret, and make sure a workflow uploads `coverage/lcov.info` (tests.yml does). Until the first successful upload the badge reads "unknown", which looks identical to a broken badge — check the Codecov dashboard, not the badge, when debugging.

Included by default — no configuration needed.

### codecov-flag

**Coverage for one package**

The repo-wide number is the wrong one to put on a package README in a monorepo — it mixes in every other package. This one narrows to a single Codecov flag, so it needs that flag to exist: an entry under `flag_management.individual_flags` in codecov.yml, and an upload that passes `flags: <name>` (tests.yml does, from its matrix). The flag name has to match both exactly, or the badge reads "unknown" while the repo-wide badge looks fine.

Enable with: `--codecov-flag <name>`

### workflow

**CI status**

The filename must match the workflow file exactly, and `?branch=` must name your default branch — without it the badge shows the most recent run on *any* branch, so a failing feature branch reads as a broken main.

Enable with: `--workflow <file>`

### test-report

**Hosted test report**

Backed by `.github/workflows/deploy-test-reports.yml`, which publishes the Vitest HTML reporter output to Cloudflare Workers on every push to the default branch. Needs CLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_ID secrets.

Enable with: `--test-report <url>`

## Row 3 — is it alive, who is behind it

### contributors

**Contributors**

Nothing to configure, but it counts commit authors GitHub could match to an account — commits made with an unregistered email are attributed to nobody and do not appear here. Use `contributors-anon` instead if your history has many of those.

Included by default — no configuration needed.

### forks

**GitHub forks**

Nothing to configure. Counts direct forks only, not forks of forks, so it undercounts a repo that has been forked along a chain. Public repos only.

Included by default — no configuration needed.

### issues

**Open issues**

Nothing to configure beyond having Issues enabled (Settings → Features). Read it as a work-in-flight number, not a defect count: shields.io asks GitHub Search, which counts pull requests as issues unless the badge path excludes them — this one uses the `/issues/` path, which already does.

Included by default — no configuration needed.

### pull-requests

**Open pull requests**

Nothing to configure. Counts open PRs including drafts. Pair it with the merged count below — an open count alone reads the same whether the queue moves in a day or has been stuck for a year.

Included by default — no configuration needed.

### prs-merged

**Merged pull requests**

Nothing to configure. shields.io has no "merged" endpoint — `issues-pr-closed` counts every PR that is no longer open, so PRs closed without merging are in this number too. On a repo that closes a lot of stale PRs, label it "PRs closed" instead of overstating what landed.

Included by default — no configuration needed.

### discussions

**GitHub Discussions**

Discussions has to be turned on (Settings → Features → Discussions) or the badge reads "repo not found" — which looks identical to a private repo. Turn it on before adding the badge, not after.

Included by default — no configuration needed.

### commit-activity

**Commit activity**

Nothing to configure. Commits per month — a quiet month reads as an abandoned project, which is worth knowing before you add it.

Included by default — no configuration needed.

### last-commit

**Last commit**

Nothing to configure. Same caveat as commit activity.

Included by default — no configuration needed.

### discord

**Discord**

Two different values: the numeric server id drives the online-member count (Server Settings → Widget → Enable Server Widget, then copy the Server ID), and the invite link is where the badge points. Without the widget enabled the badge reads "invite" instead of a count.

Enable with: `--discord-id <id>` `--discord-invite <url>`

## Row 4 — run it yourself, and what it is built with

### stackblitz

**Open in StackBlitz**

Nothing to configure — StackBlitz imports a public repo straight from a URL: `https://stackblitz.com/github/<owner>/<repo>/tree/<branch>/<path>`. Point it at a directory that boots on its own (a package with its own package.json, or an `examples/` folder), because StackBlitz installs from the manifest at that path and a bare monorepo root will not run. Node APIs need the WebContainer runtime, which means the directory must be ESM and its deps must be installable from npm.

Enable with: `--stackblitz <url>`

### codespaces

**Open in GitHub Codespaces**

Nothing to configure — the link boots the repo in a default container. Add a `.devcontainer/devcontainer.json` if the default image is missing something your install needs, because the visitor sees the failure, not you. Codespaces bills the visitor's own free hours, so the button costs you nothing.

Included by default — no configuration needed.

### prs-welcome

**PRs welcome**

Static. Worth backing with a CONTRIBUTING.md so the badge is not the only thing that says it.

Included by default — no configuration needed.

### license

**License**

Reads the license GitHub detected, which comes from a recognized LICENSE file at the repo root. A custom or modified license shows as "unknown" no matter what the file says.

Included by default — no configuration needed.

### stack

**Tech stack chips**

Plain static shields with a simple-icons logo — `?logo=<slug>` accepts any slug from simpleicons.org. Purely decorative: they say what the project is built with at a glance. Pass a comma-separated list (e.g. `--stack Claude,Cloudflare,Next.js`).

Enable with: `--stack <A,B,C>`

## Removing one

```bash
bunx template-git-repo --badges-only --exclude commit-activity,last-commit
```

Or edit the block between the markers directly — the CLI only rewrites it when you
run it again.

## Why some badges stay grey

| Badge reads | Almost always means |
| --- | --- |
| Codecov: `unknown` | No upload has landed yet. Check the Codecov dashboard, not the badge — a missing `CODECOV_TOKEN` and a repo that was never added look identical from here. |
| CI: `no status` | The workflow filename in the badge URL does not match a file in `.github/workflows/`, or it has never run on the branch in `?branch=`. |
| npm: `invalid` | The package name is not on the registry, or it is scoped and the scope is missing from the URL. |
| Discord: `invite` instead of a count | The server widget is off. Server Settings → Widget → Enable Server Widget. |
| Stars/last-commit: `repo not found` | shields.io cannot read private repos. |
