npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@fantastic.dev/repo-gates

v0.4.0

Published

Config-driven repo quality gates for turborepo (and any) monorepos: a quiet check:all orchestrator, CI-parity drift detector, a PR docs-coverage gate, and ratchet guards (file size, debt markers, circular imports, secrets, coverage, bundle size). The engi

Readme

repo-gates

Config-driven quality gates for turborepo (and any) monorepos — one quiet check-all command that runs your whole battery (lint, format, typecheck, tests) alongside ratchet guards for file size, tech-debt markers, circular imports, secret-shaped strings, shadcn UI quality, coverage, and bundle size, plus a CI-parity drift detector and a PR docs-coverage gate. The engine is repo-agnostic; your policy lives in a single repo-gates.config.json.

Quick start

Give your coding agent this prompt:

Read https://github.com/FantasticDevHQ/repo-gates/blob/main/setup.md
and set up @fantastic.dev/repo-gates in this repository.
Enable applicable gates, wire check:all into CI, and verify the setup.

The agent setup guide covers inspection, initialization, policy review, and verification.

Or initialize it yourself from the repository root:

npm install -D @fantastic.dev/repo-gates
npx repo-gates init
npm run check:all

Use your repository's package manager. See setup commands for npm, pnpm, Yarn, and Bun and the initialization reference for details. init enables applicable gates, including all six design-system rules for Tailwind v4 and Shadscan for detected shadcn projects. Review the generated config and any existing-code findings before committing.

What it does

check-all runs an ordered manifest of gates and reports them quietly — one aligned line per gate, a tally, and (on failure) the parsed failure signature instead of a wall of logs:

✓ lint              (2.5s)
✓ format:check      (5.9s)
✓ typecheck         (0.5s)
✓ check:size        (0.3s)
✓ check:debt        (0.3s)
✓ test              (8.1s)
✓ check:coverage    (9.4s)
✓ check:ci-parity   (0.3s)

8/8 gates passed (24.3s)

Scores:
  coverage   lowest packages/api 81.2% lines (12 pkgs ≥ floor)
  file-size  tightest src/app.ts 512/512 (0 to spare)

The gates:

| Command | What it does | | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | check-all | Runs the whole manifest quietly: aligned ✓/✗ gate (N.Ns), a tally, and parsed failure signatures (never the full log). On success prints a compact Scores: block. --bail stops at the first failure; CHECK_ALL_VERBOSE=1 streams everything. | | check-ci-parity | Fails when a pnpm run <gate> in .github/workflows/ci*.yml isn't reachable from check-all — kills CI/local drift. | | check-size | Per-file line ceiling; large files are grandfathered and may only shrink. --init seeds baselines. | | check-debt | TODO/FIXME/HACK/XXX must carry a tracker ref (ABC-123 / #123 / URL) or be allowlisted. --init seeds the allowlist. | | check-circular | Flags new circular-import groups (relative imports within scanRoots, resolved into a graph, reduced to strongly-connected components). Existing cycles are grandfathered. --init seeds the allowlist. | | check-secrets | Static scan of every git-tracked file for credential-shaped strings (AWS/GitHub/Slack/Stripe/npm/Google keys, PEM headers, userinfo-in-URL). Findings are reported as a redacted fingerprint — never the matched text. Not a substitute for a dedicated secret scanner (gitleaks/trufflehog): no entropy analysis, no git-history scan. --init seeds the allowlist — review every entry, it silences whatever it captures. | | check-agents | Fails if a pnpm run <x> or a backticked path in AGENTS.md no longer resolves. | | check:design-system | Optional consumer script using @shadcn/lint to enforce Tailwind v4 design-system rules through ESLint or Oxlint. Defining the script includes it in check-all. | | check:shadscan | Optional consumer script for React repositories using shadcn/ui. Runs a pinned Shadscan audit and fails below the repository's ratcheted score floor. Defining the script automatically adds it to check-all; non-shadcn repositories omit it. | | check-docs-coverage | PR gate: a changed "surface" (config-defined glob) must come with a docs change, or a docs: n/a - <reason> opt-out in the PR body. Reads the changed-file list from the GitHub API (GITHUB_REPOSITORY/PR_NUMBER/GITHUB_TOKEN/PR_BODY); a no-op outside a PR context (safe to include in check:all). See CI for wiring it as its own pull_request-triggered job. | | check-coverage | Holds each package to its own floor (no repo-wide aggregate — a high package can't mask a low one); unlisted packages must meet a default. Floors ratchet up. --init seeds; --skip-run reuses existing summaries. | | check-bundle-size | Builds each configured target (turbo, cached), then ratchets raw+gzip totals AND the largest single chunk per bucket. --init re-baselines. | | report-test-timing / report-quality-metrics | Non-gating dashboards → $GITHUB_STEP_SUMMARY. |

Why

  • One quiet command. check-all is the single entry point — aligned pass/fail, a tally, and parsed failure signatures instead of a wall of logs. --bail stops early; CHECK_ALL_VERBOSE=1 streams everything.
  • Ratchets, not fixed limits. File size, tech debt, circular imports, secret-shaped strings, shadcn UI quality, coverage, and bundle size only move in the right direction. Seed each baseline from your current tree, so day one is green — no big cleanup up front.
  • Per-package coverage floors. Each package is held to its own floor, so a well-covered package can't mask a thin one.
  • Docs don't drift behind the product. check-docs-coverage blocks a PR that changes a user-facing surface without touching docs — unless the author opts out on the record.
  • CI ↔ local parity. check-ci-parity fails if your CI workflow drifts from the check-all manifest, so "green locally" means "green in CI."
  • Config-driven & reusable. The engine ships no repo-specific assumptions; drop it into any repo and describe policy in one JSON file.

How to use it

Install

pnpm add -D @fantastic.dev/repo-gates
# or: npm i -D @fantastic.dev/repo-gates  /  yarn add -D @fantastic.dev/repo-gates

Ships compiled JS + types — no build step or Node type-stripping required in your repo (Node ≥ 18).

Quickstart

pnpm exec repo-gates init
pnpm run check:all

init configures the repository, rather than only printing instructions. Run it at the root containing package.json. It detects npm, pnpm, Yarn, or Bun from packageManager or lockfiles, adds missing scripts, writes the gate manifest, and installs applicable UI tools with that package manager. Installation of repo-gates itself has no postinstall hook that modifies your repository; initialization is an explicit command.

| Gate | What init does | | --- | --- | | File size, debt markers, circular imports | Adds scripts and seeds missing baselines from the current source. Existing baselines are never reseeded. | | Secrets | Adds check:secrets, without automatically allowlisting findings. It scans Git-tracked files. | | CI parity | Adds check:ci-parity. Run check:all from your CI workflow to enforce the generated manifest. | | Design system | Detects Tailwind v4, installs pinned @shadcn/lint and a compatible linter/parser when absent, and configures all six rules as errors. | | Shadscan | Detects components.json in root/workspace packages, installs @shadscan/[email protected], and checks each detected project with an initial floor of 80. | | Agent docs | Enables check:agents when AGENTS.md files or configured targets exist. | | Coverage | Enables check:coverage when test:coverage exists. Configure that script to emit json-summary reports, then run repo-gates check-coverage --init to measure the initial floors. | | Docs coverage, bundle size | Enables their scripts when docsCoverage.surfaces or bundleSize.targets are configured. See the respective policy sections below. | | Existing lint, format, typecheck, test, dependency and duplication checks | Includes existing scripts whose names match the default manifest. It does not invent framework-specific commands. Missing core scripts are listed at completion. |

A fresh config scans the repository root, excludes dependency/build directories, and includes JS and TS source extensions. Workspace discovery reads package.json workspaces or pnpm-workspace.yaml, supporting literal paths, *, **, and ! exclusions. It detects installed dependency versions or ordinary declared semver ranges, including pnpm catalogs. Existing lint configs remain untouched; the design-system pass gets a separate generated config. ESLint is preferred when declared at the root; an existing root Oxlint setup is used otherwise. If neither is present, initialization adds ESLint.

Re-running init preserves existing scripts, lint configs, explicit policy values, and baselines, while adding missing applicable gates. Existing custom manifests keep deliberate omissions of scripts that were already present; newly added gate scripts are inserted into the manifest. Review the generated files and commit them with your lockfile. An install failure exits nonzero and leaves the generated files available for repair; run your package manager's install command or rerun init to retry.

repo-gates init --skip-install         # write dependencies/configs; install later
repo-gates init --no-design-system     # skip automatic Tailwind rule setup
repo-gates init --no-shadscan           # skip automatic Shadscan setup
repo-gates init --shadscan-floor 70     # choose an initial integer floor, 0–100

Opt-out flags skip setup; they do not remove existing scripts or dependencies. To disable an existing gate, remove its entry from repo-gates.config.json. The initial Shadscan floor is a policy choice, not a measured baseline. It may fail on the first run. Raise it as findings are fixed, and never lower an established floor to pass a regression. UI tooling needs Node 20.19+ for the ESLint setup; Oxlint requires Node 20.19.x or 22.12+.

Wire it up

{
  "scripts": {
    "check:all": "repo-gates check-all",
    "check:size": "repo-gates check-size",
    "check:debt": "repo-gates check-debt",
    "check:shadscan": "pnpm dlx @shadscan/[email protected] ./apps/web --json --fail-under 40 --no-roast --no-interactive",
    "check:coverage": "repo-gates check-coverage",
    "check:ci-parity": "repo-gates check-ci-parity"
  }
}

Add the Shadscan ratchet for shadcn repositories

init adds this gate automatically for detected shadcn/ui projects. For manual setup, only define check:shadscan when the repository uses shadcn/ui. Run Shadscan once against the React application package, choose a conservative floor below or equal to the assessed score, and commit that floor as the starting ratchet:

pnpm dlx @shadscan/[email protected] ./apps/web --json --fail-under 40 --no-roast --no-interactive

Keep the CLI version exact so the same source is evaluated by the same ruleset locally and in CI. The default gate manifest treats check:shadscan as conditional: defining the package script includes it in check-all (and any pre-commit hook that runs check-all); omitting it leaves non-shadcn repositories unaffected. Raise --fail-under as findings are remediated, and never lower it to make a regression pass. See the Shadscan pre-commit documentation for hook-manager-specific wiring.

Add design-system checks with @shadcn/lint

repo-gates init creates this setup automatically for detected Tailwind v4 repositories. For manual setup, define check:design-system to run a separate design-system gate immediately after lint. The default manifest skips it when the script is absent. This is a pass/fail check, with no score or ratchet. Shadcn/ui is optional; custom Tailwind design systems are supported too.

Install the plugin in the consumer package that owns the lint configuration. It requires Node 20.19+ and ESLint 9.30+, or Oxlint 1.80+. The optional gate does not change repo-gates' own Node 18 requirement.

pnpm add -D -E @shadcn/[email protected]
# If ESLint and a TSX parser are not already installed:
pnpm add -D -E eslint@^9.30.0 @typescript-eslint/parser@^8.40.0

Our default setup enables all six rules as errors. For a separate ESLint pass, create eslint.design-system.config.mjs:

import { plugin as shadcn } from "@shadcn/lint";
import tsParser from "@typescript-eslint/parser";
import { designSystemRules } from "@fantastic.dev/repo-gates/design-system";

export default [
  { ignores: ["**/node_modules/**", "**/dist/**", "**/.next/**", "**/coverage/**"] },
  {
    files: ["**/*.{js,jsx,ts,tsx}"],
    languageOptions: {
      parser: tsParser,
      parserOptions: { ecmaFeatures: { jsx: true } },
    },
    plugins: { shadcn },
    rules: {
      ...designSystemRules,
      // Add consumer overrides here, after the preset.
    },
  },
  {
    // Adjust to the directories where your design-system components are defined.
    files: ["src/components/ui/**", "components/ui/**", "packages/ui/src/components/**"],
    rules: { "shadcn/no-restyle": "off" },
  },
];

Add the consumer script, adjusting the source path to your application:

{
  "scripts": {
    "check:design-system": "eslint --config eslint.design-system.config.mjs src --max-warnings 0"
  }
}

Run pnpm run check:design-system, then pnpm exec repo-gates check-all. If your repository overrides the gates array, add { "name": "check:design-system", "conditional": true } to that array; arrays replace the defaults in full.

Update the rules

The shipped @fantastic.dev/repo-gates/design-system preset is the source of ESLint defaults. Edit your consumer's eslint.design-system.config.mjs and add rule overrides after ...designSystemRules. Generated Oxlint setups use .oxlintrc.design-system.json; edit the rules in its first overrides entry, which contains a copy of the same defaults scoped to the detected UI packages. The defaults are:

| Rule | What it checks | | --- | --- | | shadcn/no-restyle | Styling overrides on design-system components; layout classes are allowed. | | shadcn/no-raw-colors | Colors that bypass the design-system theme. | | shadcn/no-arbitrary-values | Arbitrary Tailwind values such as p-[13px]. | | shadcn/no-inline-styles | Inline styles and <style> elements. | | shadcn/no-unknown-classes | Classes that Tailwind cannot generate. | | shadcn/require-static-classes | Component class values the linter cannot statically read. |

All six use error. The existing component-directory override turns off only no-restyle, so components can define their own styles. The other five remain active there too. Upstream recommends also exempting component definitions from no-arbitrary-values and require-static-classes when needed; our default keeps those checks enabled until you explicitly add an exception.

To disable a rule, set its value to "off". To change its options, use an array such as ["error", { allow: ["layout", "spacing"] }] for no-restyle. For a scoped exception, append a config entry after the defaults, for example:

{
  files: ["src/components/ui/**"],
  rules: {
    "shadcn/no-arbitrary-values": "off",
    "shadcn/require-static-classes": "off",
  },
}

Adjust the glob to your component directory. A later matching ESLint entry replaces the earlier setting for that rule. "warn" is also supported, but our script uses --max-warnings 0, so warnings still fail the gate. Remove that flag or raise its limit if warnings should be non-blocking.

Component and theme discovery uses components.json; custom imports and shared UI packages may need settings.shadcn configuration. See the shadcn/lint repository, rule options, and settings. After editing the configuration, run pnpm run check:design-system. Existing consumers can run repo-gates init to add missing gate setup. Existing design-system scripts and configurations are preserved; adopt the preset explicitly if your config still lists rules individually. Upgrading repo-gates can update the imported ESLint preset; Oxlint's copied rules remain unchanged until you edit them. New upstream rules are not enabled merely by upgrading @shadcn/lint. Review rule changes when upgrading repo-gates.

You can instead register the plugin in your existing ESLint or Oxlint config and run it through the existing lint gate. In that case, omit the separate script to avoid checking the same rules twice. Oxlint uses "jsPlugins": ["@shadcn/lint"]; its JS plugin API is currently alpha. See the upstream setup instructions. Keep versions pinned and commit the consumer lockfile for consistent CI results.

Partial coverage runs

If CI runs tests for only a subset of packages, generate their coverage summaries first, then validate them with the installed CLI:

pnpm exec repo-gates check-coverage --skip-run --partial

Use your repository's package manager and affected-test command. Clear previous coverage reports before generating the current run's summaries; repo-gates reads every matching report and does not determine which packages are affected or whether reports are fresh.

Measured packages must meet their existing floors, and new packages must meet the default floor. A budgeted package without a summary keeps its floor only if its package.json exists. Missing manifests still fail, even if an ignored node_modules directory remains. The output lists unrun packages. A run with no matching summaries still fails; skip the coverage job upstream when no packages need testing.

--partial does not verify that every intended package produced coverage. Keep periodic full runs to detect missing reports. Without --partial, missing summaries remain errors. --init --partial is rejected before tests run or baselines change; initialize coverage floors from a full run.

How it finds your repo

Every command resolves the repo root from process.cwd() and loads repo-gates.config.json from there (falling back to the built-in DEFAULT_CONFIG). The config is a partial overlay on the defaults — set only what differs.

Configuration

repo-gates.config.json is a partial overlay on the built-in defaults — set only what differs from your repo. JSON is parsed strictly (no // comments); use a "$comment" key for inline notes, as below.

Example

A realistic config for a pnpm + turbo monorepo (an Electron app, a web app, shared packages) — the annotated version below is jsonc for readability; a copy-pasteable, strictly-valid repo-gates.config.json (comments as "$comment" keys instead of //) lives at examples/repo-gates.config.json:

{
  "$comment": "Anything omitted falls back to DEFAULT_CONFIG.",
  "runner": "pnpm run",

  // Each package/app's vitest coverage-summary.json — check-coverage holds each to
  // its own floor (gates/coverage-budgets.json), seeded by `check-coverage --init`.
  "coverage": {
    "summaryGlobs": [
      "apps/*/coverage/coverage-summary.json",
      "packages/*/coverage/coverage-summary.json"
    ]
  },

  // Build + measure bundle budgets. Each target is built via
  // `turbo run build --filter <filter>`, then dist is measured by bucket.
  "bundleSize": {
    "targets": [
      { "name": "web", "filter": "@acme/web", "distDir": "apps/web/dist",
        "buckets": { "js": [".js"], "css": [".css"] } }
    ]
  },

  // check-agents: keep AGENTS.md's `pnpm run <script>` + backticked paths resolving.
  "agents": { "targets": ["AGENTS.md"] },

  // report-test-timing reads these junit files (non-gating dashboard).
  "report": { "junitGlobs": ["apps/*/test-results/junit.xml", "packages/*/test-results/junit.xml"] },

  // Architectural import rules → ESLint (see "Import boundaries" below).
  "boundaries": [
    {
      // ONE cross-app rule for every app (message written once). Safe as long as
      // apps don't import their own package by name.
      "name": "no-cross-app",
      "files": ["apps/**"],
      "patterns": [
        { "forbid": ["@acme/web", "@acme/web/**", "@acme/desktop", "@acme/desktop/**"],
          "message": "Apps must not import each other — share via packages/*." }
      ]
    },
    {
      // Nested scope: inherits no-cross-app's patterns via `extends` (no copy-paste),
      // and adds its own.
      "name": "desktop-renderer",
      "files": ["apps/desktop/src/renderer/**"],
      "ignores": ["**/*.test.ts", "**/*.test.tsx"],
      "extends": ["no-cross-app"],
      "patterns": [
        { "forbid": ["node:*", "better-sqlite3", "**/main/**"],
          "allowTypeImports": true,
          "message": "Renderer is a browser context — reach main via IPC, not a value import (import type is fine)." }
      ]
    }
  ]
}

Reference

| Key | Default | Purpose | | --- | --- | --- | | runner | "pnpm run" | How a gate script is invoked ("pnpm run", "bun run", "npm run"). | | gates | 18-gate manifest | Ordered { name, conditional }[]. A non-conditional gate in the selected manifest is required: it runs even when its script is missing, so the run fails loudly. Conditional gates run only if their script exists. Overriding this array replaces the default policy, so it is valid to omit test when check:coverage runs the complete suite with coverage (avoids executing the suite twice). | | scanRoots | ["apps","packages","scripts"] | Roots the file-size + debt + circular-import walkers scan. | | excludeDirSegments | node_modules, dist, out, .turbo, coverage, … | Directory names pruned from scans. | | excludePathPrefixes | [] | Repo-relative path prefixes excluded from scans. | | sourceExtensions | [".ts",".tsx"] | Extensions the size/debt/circular-import guards treat as source. | | fileSize.threshold | 600 | Default per-file line ceiling (larger files are grandfathered in the budgets file). | | fileSize.budgetsPath | gates/file-size-budgets.json | Grandfathered per-file budgets (check-size --init seeds). | | fileSize.extensions | sourceExtensions | Extensions the size guard counts, when they differ from sourceExtensions (e.g. add ".css": a stylesheet's length is worth capping, but the debt and circular-import guards cannot read it). | | debt.markerTokens | ["TODO","FIXME","HACK","XXX"] | Tokens that must carry a tracker reference. | | debt.trackerPatterns | ABC-123, #123, URL | Regex sources for a valid tracker reference. | | debt.allowlistPath | gates/debt-marker-allowlist.json | Untracked-marker allowlist (check-debt --init seeds). | | circular.allowlistPath | gates/circular-imports-allowlist.json | Grandfathered circular-import groups (check-circular --init seeds). | | secrets.patterns | AWS/GitHub/Slack/Stripe/npm/Google key shapes, PEM headers, URL creds | Regex sources tested against every git-tracked line. | | secrets.binaryExtensions | images, fonts, archives, media | Extensions skipped as non-text. | | secrets.allowlistPath | gates/secrets-allowlist.json | Grandfathered findings (check-secrets --init seeds — review before trusting). | | docsCoverage.surfaces | [] (no-op) | [{ label, glob, on: "added"\|"changed" }] — user-facing surfaces that require docs when changed. | | docsCoverage.docsGlobs | [] | Globs a PR must touch for a triggered surface to count as documented. | | docsCoverage.exclude | [] | Globs removed from both surface and docs matching (tests, fixtures). | | coverage.summaryGlobs | apps/*, packages/* | Globs matching each package's coverage-summary.json. | | coverage.budgetsPath | gates/coverage-budgets.json | Per-package floors (check-coverage --init seeds; floors ratchet up). | | bundleSize.targets | [] (no-op) | [{ name, filter, distDir, buckets }] — built via turbo, then raw+gzip+largest-chunk ratcheted. | | bundleSize.budgetsPath | gates/bundle-size-budgets.json | Bundle baselines. | | agents.targets | [] | Agent docs (e.g. ["AGENTS.md"]) whose pnpm run <x> + backticked paths must resolve. | | report.junitGlobs | [] | junit files for the report-test-timing dashboard. | | report.topN | 20 | Slowest-tests cutoff in that dashboard. | | ciParity.{configPath,rootGate,entryGates,workflowPrefix} | gates/ci-parity-config.json, check:all, ["check:all","verify"], ci | Inputs to the CI-parity reachability graph. | | boundaries | [] | Import-boundary rules → ESLint (below). |

The full RepoGatesConfig type is exported from the package for editor autocompletion.

Import boundaries (ESLint)

Architectural import rules are data in repo-gates.config.json under boundaries; the transform @fantastic.dev/repo-gates/eslint-boundaries turns them into @typescript-eslint/no-restricted-imports flat configs you spread into your eslint.config.mjs:

import { boundariesToEslintConfigs } from "@fantastic.dev/repo-gates/eslint-boundaries";
import repoGates from "./repo-gates.config.json" with { type: "json" };

export default [
  // …your other flat configs…
  ...boundariesToEslintConfigs(repoGates.boundaries ?? []),
];

Each boundary is one file scope carrying pattern groups; per-group allowTypeImports lets a browser context still import type a Node-only module. ignores exempts files (commonly tests, which run in Node).

Because ESLint flat config is last-wins per rule, a file matched by several boundaries only keeps the last one's patterns — so a nested scope (renderer/** ⊂ apps/**) must carry the broader patterns too. Instead of copy-pasting them, use extends: { "name": "desktop-renderer", "extends": ["no-cross-app"], … } merges the named boundaries' patterns in ahead of its own (resolved transitively, cycles rejected). Author each rule — and its message — once, at its natural scope.

Scores protocol: any gate contributes a headline to check-all's success Scores: block by printing SCORE: <label> — <value> on success; check-all collects and aligns them.

CI

Run the battery as one job step (Node ≥ 18, deps installed):

- run: pnpm exec repo-gates check-all

Keep the CI workflow and the check-all manifest in lock-step with repo-gates check-ci-parity.

Full copy-paste-able GitHub Actions workflows, from a single check-all step up to a per-gate turborepo battery with remote caching, live in examples/github-actions/:

  • minimal.yml — one check-all step.
  • single-package.yml — gates broken into individual steps for a single-package (or lightly-workspaced) repo.
  • monorepo-turborepo.yml — the full battery (deps/dups/size/debt/circular/secrets/agents/bundle-size/coverage ratchets + Turbo remote cache) for a pnpm + turbo monorepo.
  • docs-coverage.yml — check-docs-coverage wired as its own pull_request-triggered job, passing GITHUB_TOKEN/PR_NUMBER/PR_BODY from the event and checking out the PR's base commit (tamper-resistant — a PR can't narrow its own docs-coverage policy to dodge the gate). Separate from the other examples because it's a PR-diff gate, not part of the local check:all battery — see the config's docsCoverage docs above.

Programmatic use

import { loadContext, runCheckAll } from "@fantastic.dev/repo-gates";

process.exitCode = runCheckAll(loadContext(), { verbose: false });

Development

pnpm install
pnpm test          # vitest
pnpm run typecheck # tsc --noEmit
pnpm run build     # tsup → dist/ (esm + d.ts)

Releases

Release Please maintains a release PR after changes merge into main. It updates package.json, CHANGELOG.md, and .release-please-manifest.json. Review and merge that PR to create the version tag and GitHub release and publish to npm with trusted publishing. No direct push to main or npm token is needed.

Use Conventional Commit titles for squash-merged PRs:

| Title prefix | Version change | | --- | --- | | fix:, docs:, perf: | Patch | | chore:, ci:, build:, refactor:, test:, revert: | Patch | | feat: | Minor | | A ! suffix, such as feat!:, or a BREAKING CHANGE: footer | Major, including before 1.0 |

Edit release-please-config.json to change release policy. The manifest records the last released version; let the release PR update it. Do not manually bump versions for normal changes. Non-conventional titles may be omitted from releases.

The workflow explicitly dispatches CI for bot-created release PRs because GitHub's built-in token does not trigger their normal PR workflows. Publishing runs in the same release.yml workflow after release creation, rather than waiting for a bot-created tag to trigger another run. Release PRs are not automatically merged; merging one is the release decision.

If publishing fails after the GitHub release is created, use Re-run failed jobs on that Release run to retain its release outputs. Check npm first if the publish result is uncertain; published versions cannot be overwritten. Manual v* tags remain supported and must match package.json. Keep the workflow named release.yml, since npm's trusted-publisher configuration uses that filename.

License

MIT © Kelly Kampen