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

repofmt

v0.3.0

Published

A CLI tool that checks consistency across repositories

Readme

CI Coverage Status

Node.js TypeScript GitHub Actions Jest

repofmt

This tool checks consistency across repositories.

Usage

GitHub Action

See repofmt-action for more details on how to use the GitHub action.

CLI

npx repofmt run [options]

Options

| Option | Description | Required | Default | |--------|-------------|----------|---------| | --config <path> | Path to the config file. | No | repofmt.config.ts | | --token <token> | GitHub token (can also use GITHUB_TOKEN env var). | No | |

Configuration

Inputs

github-token

github-token is the GitHub token to use for API access. It is optional and must be a personal access token with the repo scope. If not provided, the action will use the default GitHub token.

repofmt.config.ts

The action can be configured by creating a repofmt.config.ts file at the root of your repository. repofmt.config.js, repofmt.config.mjs, and repofmt.config.cjs are also supported.

export default {
  filters: {
    archived: false,
    organizations: ['my-org', 'other-org'],
    visibility: 'public',
    include: ['^my-org-', '^other-org-'],
    exclude: ['^deprecated-'],
  },
  rules: [
    {
      name: 'file-exists',
      level: 'error',
      options: {
        caseSensitive: true,
        path: 'file.md',
      },
    },
  ],
};

Filters

The filters option allows you to control which repositories are checked by the action.

{
  filters: {
    archived: false,
    organizations: ['my-org', 'other-org'],
    visibility: 'public',
    include: ['^my-org-', '^other-org-'],
    exclude: ['^deprecated-'],
  }
}

| Option | Description | Required | Default | |------------------|-----------------------------------------------------------------|----------|--------------| | archived | Filter repositories by archived status: true (only archived), false (only not archived), or undefined (all). | No | undefined | | organizations | Array of organization names to include. A repository must belong to one of the specified organizations to be included. | No | | | visibility | Filter repositories by visibility: 'public', 'private', or 'all'. | No | 'all' | | include | Array of regex patterns to match repository names (matches both name and full_name). A repository is included if it matches any pattern in the array. | No | | | exclude | Array of regex patterns to exclude repository names (matches both name and full_name). A repository is excluded if it matches any pattern in the array. | No | |

  • A repository must pass all specified filters to be included.

Rules

Each rule has the following structure:

{
  name: 'rule-name',
  level: 'error' | 'warning',
  options: { /* rule-specific options */ },
  exceptions?: string[]
}

Common Rule Fields

| Field | Description | Required | Default | |-------------|-----------------------------------------------------------------|----------|--------------| | name | The name of the rule to apply. | Yes | | | level | The alert level: 'error' or 'warning'. | Yes | | | options | Rule-specific configuration options. | Yes | | | exceptions| Array of regex patterns to exclude repositories from this rule. A repository is excluded if its name or full_name matches any pattern in the array. | No | |

Exceptions: The exceptions field allows you to bypass a specific rule for certain repositories. This is useful when you want a rule to apply to most repositories but need to exclude specific ones. Exceptions are checked per-rule, so you can have different exceptions for different rules.

Example: Skip a rule for legacy repositories:

{
  name: 'file-exists',
  level: 'error',
  options: {
    path: 'README.md',
  },
  exceptions: ['^legacy-', '^deprecated-'],
}

file-contains

The file-contains rule checks if a file contains a specific string.

{
  "name": "file-contains",
  "level": "error",
  "options": {
    "path": "README.md",
    "contains": "MIT License",
    "caseSensitive": false
  }
}

| Option | Description | Required | Default | |-----------------|-----------------------------------------------------------------|----------|--------------| | path | The path to the file(s) to check, or an array of paths/glob patterns. | Yes | | | contains | The string that the file(s) must contain. | Yes | | | caseSensitive | Whether the string matching should be case-sensitive. | No | false |

The path option supports:

  • Single file paths (e.g., README.md, .github/workflows/ci.yml)
  • Glob patterns (e.g., **/*.md, src/**/*.ts)
  • Arrays of paths/patterns (e.g., ['README.md', 'LICENSE'], ['**/*.md', '**/*.txt'])

When using glob patterns or arrays, all matching files must contain the specified string.

Example: Check if a README contains a license notice:

{
  "name": "file-contains",
  "level": "error",
  "options": {
    "path": "README.md",
    "contains": "MIT License"
  }
}

Example: Check if all markdown files contain a license notice:

{
  "name": "file-contains",
  "level": "error",
  "options": {
    "path": "**/*.md",
    "contains": "MIT License"
  }
}

Example: Check multiple specific files:

{
  "name": "file-contains",
  "level": "error",
  "options": {
    "path": ["README.md", "LICENSE"],
    "contains": "MIT"
  }
}

file-exists

The file-exists rule checks if a file exists in the repository.

{
  "name": "file-exists",
  "level": "error",
  "options": {
    "caseSensitive": true,
    "path": "file.md"
  }
}

| Option | Description | Required | Default | |-----------------|-----------------------------------------------------------------|----------|--------------| | caseSensitive | Whether to check if the file exists in a case-sensitive manner. | No | false | | path | The path to the file, or an array of alternative paths. | Yes | | | type | The type of entry to look for: file, directory, or any. | No | file |

The path option supports nested paths (e.g., .github/workflows/ci.yml).

You can also provide an array of alternative paths. The rule passes if at least one of the entries exists:

{
  "name": "file-exists",
  "level": "error",
  "options": {
    "path": ["config.yml", "config.yaml"]
  }
}

You can use the type option to check for directories instead of files:

{
  "name": "file-exists",
  "level": "error",
  "options": {
    "path": "src",
    "type": "directory"
  }
}

file-forbidden

The file-forbidden rule checks that a file does NOT exist in the repository. This is useful for enforcing that certain files (like .DS_Store, .env, etc.) are not committed.

{
  "name": "file-forbidden",
  "level": "error",
  "options": {
    "caseSensitive": true,
    "path": ".DS_Store"
  }
}

| Option | Description | Required | Default | |-----------------|-----------------------------------------------------------------|----------|--------------| | caseSensitive | Whether to check if the file exists in a case-sensitive manner. | No | false | | path | The path to the file(s) that should not exist. | Yes | | | type | The type of entry to look for: file, directory, or any. | No | file |

The path option supports nested paths (e.g., src/config/secrets.json).

You can provide an array of paths to forbid multiple files:

{
  "name": "file-forbidden",
  "level": "error",
  "options": {
    "path": [".env", ".env.local", ".env.production"]
  }
}

You can use the type option to forbid directories instead of files:

{
  "name": "file-forbidden",
  "level": "error",
  "options": {
    "path": "node_modules",
    "type": "directory"
  }
}

file-not-contains

The file-not-contains rule checks that a file does NOT contain a specific string. This is useful for enforcing that certain content (like secrets, deprecated code, etc.) is not present in files. If you use this rule to look for secrets, do NOT enter a specific secret (as you don't want your committed config to contain a secret), but instead use common secret patterns, such as the "sk_" prefix for example..

{
  "name": "file-not-contains",
  "level": "error",
  "options": {
    "path": "README.md",
    "contains": "SECRET",
    "caseSensitive": false
  }
}

| Option | Description | Required | Default | |-----------------|-----------------------------------------------------------------|----------|--------------| | path | The path to the file(s) to check, or an array of paths/glob patterns. | Yes | | | contains | The string that the file(s) must NOT contain. | Yes | | | caseSensitive | Whether the string matching should be case-sensitive. | No | false |

The path option supports:

  • Single file paths (e.g., README.md, .github/workflows/ci.yml)
  • Glob patterns (e.g., **/*.md, src/**/*.ts)
  • Arrays of paths/patterns (e.g., ['README.md', 'LICENSE'], ['**/*.md', '**/*.txt'])

When using glob patterns or arrays, all matching files must not contain the specified string.

Example: Check that a README does not contain a secret:

{
  "name": "file-not-contains",
  "level": "error",
  "options": {
    "path": "README.md",
    "contains": "SECRET_KEY"
  }
}

Example: Check that all markdown files do not contain deprecated content:

{
  "name": "file-not-contains",
  "level": "error",
  "options": {
    "path": "**/*.md",
    "contains": "DEPRECATED"
  }
}

Example: Check multiple specific files:

{
  "name": "file-not-contains",
  "level": "error",
  "options": {
    "path": [".env", ".env.local"],
    "contains": "password"
  }
}

github-actions/timeout-minutes

The github-actions/timeout-minutes rule checks if the GitHub Actions timeout is set.

{
  "name": "github-actions/timeout-minutes",
  "level": "error",
  "options": {
    "maximum": 30
  }
}

| Option | Description | Required | Default | |------------------|-----------------------------------------------------------------|----------|--------------| | maximum | Maximum allowed timeout value in minutes. If provided, the rule will check that all timeout values are lower than this maximum. | No | |

json-has-keys

The json-has-keys rule checks if all specified keys are defined in JSON files. Keys can be nested using dot notation (e.g., parent.child.grandchild).

{
  "name": "json-has-keys",
  "level": "error",
  "options": {
    "path": "package.json",
    "keys": ["name", "version", "scripts.build"]
  }
}

| Option | Description | Required | Default | |-----------------|-----------------------------------------------------------------|----------|--------------| | path | The path to the JSON file(s) to check, or an array of paths/glob patterns. | Yes | | | keys | Array of keys to check. Use dot notation for nested keys (e.g., "parent.child"). | Yes | |

The path option supports:

  • Single file paths (e.g., package.json, tsconfig.json)
  • Glob patterns (e.g., **/*.json, config/*.json)
  • Arrays of paths/patterns (e.g., ['package.json', 'package-lock.json'])

When using glob patterns or arrays, all matching files must contain all specified keys.

Example: Check that package.json has required fields:

{
  "name": "json-has-keys",
  "level": "error",
  "options": {
    "path": "package.json",
    "keys": ["name", "version", "description"]
  }
}

Example: Check nested keys:

{
  "name": "json-has-keys",
  "level": "error",
  "options": {
    "path": "tsconfig.json",
    "keys": ["compilerOptions.target", "compilerOptions.module"]
  }
}

Example: Check multiple JSON files:

{
  "name": "json-has-keys",
  "level": "error",
  "options": {
    "path": ["package.json", "package-lock.json"],
    "keys": ["name", "version"]
  }
}

Example: Check all JSON files in a directory:

{
  "name": "json-has-keys",
  "level": "error",
  "options": {
    "path": "config/**/*.json",
    "keys": ["name"]
  }
}

yaml-has-keys

The yaml-has-keys rule checks if all specified keys are defined in YAML files. Keys can be nested using dot notation (e.g., parent.child.grandchild).

{
  "name": "yaml-has-keys",
  "level": "error",
  "options": {
    "path": "config.yml",
    "keys": ["name", "version", "scripts.build"]
  }
}

| Option | Description | Required | Default | |-----------------|-----------------------------------------------------------------|----------|--------------| | path | The path to the YAML file(s) to check, or an array of paths/glob patterns. | Yes | | | keys | Array of keys to check. Use dot notation for nested keys (e.g., "parent.child"). | Yes | |

The path option supports:

  • Single file paths (e.g., config.yml, .github/workflows/ci.yml)
  • Glob patterns (e.g., **/*.yml, **/*.yaml, config/*.yaml)
  • Arrays of paths/patterns (e.g., ['config.yml', 'app.yaml'])

When using glob patterns or arrays, all matching files must contain all specified keys.

Example: Check that a config file has required fields:

{
  "name": "yaml-has-keys",
  "level": "error",
  "options": {
    "path": "config.yml",
    "keys": ["name", "version", "description"]
  }
}

Example: Check nested keys:

{
  "name": "yaml-has-keys",
  "level": "error",
  "options": {
    "path": ".github/workflows/ci.yml",
    "keys": ["on.push.branches", "jobs.build.steps"]
  }
}

Example: Check multiple YAML files:

{
  "name": "yaml-has-keys",
  "level": "error",
  "options": {
    "path": ["config.yml", "app.yaml"],
    "keys": ["name", "version"]
  }
}

Example: Check all YAML files in a directory:

{
  "name": "yaml-has-keys",
  "level": "error",
  "options": {
    "path": "config/**/*.{yml,yaml}",
    "keys": ["name"]
  }
}

license/exists

The license/exists rule checks if a LICENSE file exists in the repository.

{
  "name": "license/exists",
  "level": "error",
  "options": {
    "caseSensitive": true,
    "path": "LICENSE.md"
  }
}

| Option | Description | Required | Default | |-----------------|-----------------------------------------------------------------|----------|--------------| | caseSensitive | Whether to check if the file exists in a case-sensitive manner. | No | false | | path | The path to the file, or an array of alternative paths. | No | LICENSE.md |

You can provide an array of alternative paths:

{
  "name": "license/exists",
  "level": "error",
  "options": {
    "path": ["LICENSE", "LICENSE.md", "LICENSE.txt"]
  }
}

python/pyproject-dependencies-alphabetical-order

The python/pyproject-dependencies-alphabetical-order rule checks if dependencies in pyproject.toml are in alphabetical order.

{
  "name": "python/pyproject-dependencies-alphabetical-order",
  "level": "error",
  "options": {
    "path": "pyproject.toml",
    "sections": ["project.dependencies", "project.optional-dependencies", "tool.poetry.dependencies"]
  }
}

| Option | Description | Required | Default | |-----------------|-----------------------------------------------------------------|----------|--------------| | path | The path to the pyproject.toml file. | No | pyproject.toml | | sections | Array of dependency sections to check. Supported sections: project.dependencies, project.optional-dependencies, tool.poetry.dependencies. | No | ["project.dependencies", "project.optional-dependencies", "tool.poetry.dependencies"] |

The rule checks that dependencies are sorted alphabetically by package name (ignoring version specifiers and extras). For project.optional-dependencies, it checks each optional dependency group separately.

Example: Check if dependencies are in alphabetical order:

{
  "name": "python/pyproject-dependencies-alphabetical-order",
  "level": "error",
  "options": {}
}

Example: Check only Poetry dependencies:

{
  "name": "python/pyproject-dependencies-alphabetical-order",
  "level": "error",
  "options": {
    "sections": ["tool.poetry.dependencies"]
  }
}

python/requirements-txt-dependencies-alphabetical-order

The python/requirements-txt-dependencies-alphabetical-order rule checks if dependencies in requirements.txt are in alphabetical order.

{
  "name": "python/requirements-txt-dependencies-alphabetical-order",
  "level": "error",
  "options": {
    "path": "requirements.txt"
  }
}

| Option | Description | Required | Default | |-----------------|-----------------------------------------------------------------|----------|--------------| | path | The path to the requirements.txt file. | No | requirements.txt |

The rule checks that dependencies are sorted alphabetically by package name (ignoring version specifiers, extras, and comments). It handles:

  • Standard packages: package, package==1.0.0, package>=1.0.0, package[extra]>=1.0.0
  • Editable installs: -e ., -e ./local-package
  • Git URLs: git+https://github.com/user/repo.git, git+https://github.com/user/repo.git@branch#egg=package-name
  • File paths: ./local-package, /path/to/package
  • Comments and empty lines are ignored

Example: Check if dependencies are in alphabetical order:

{
  "name": "python/requirements-txt-dependencies-alphabetical-order",
  "level": "error",
  "options": {}
}

Example: Check a custom requirements file:

{
  "name": "python/requirements-txt-dependencies-alphabetical-order",
  "level": "error",
  "options": {
    "path": "requirements-dev.txt"
  }
}

readme/exists

The readme/exists rule checks if a README file exists in the repository.

{
  "name": "readme/exists",
  "level": "error",
  "options": {
    "caseSensitive": true,
    "path": "README.md"
  }
}

| Option | Description | Required | Default | |-----------------|-----------------------------------------------------------------|----------|-------------| | caseSensitive | Whether to check if the file exists in a case-sensitive manner. | No | false | | path | The path to the file, or an array of alternative paths. | No | README.md |

You can provide an array of alternative paths:

{
  "name": "readme/exists",
  "level": "error",
  "options": {
    "path": ["README.md", "README.rst", "README.txt"]
  }
}

readme/has-badges

The readme/has-badges rule checks if a README file contains markdown badges (e.g., [![...](...)](...)).

{
  "name": "readme/has-badges",
  "level": "error",
  "options": {
    "path": "README.md",
    "minCount": 3,
    "patterns": ["CI", "Coverage"]
  }
}

| Option | Description | Required | Default | |-----------|--------------------------------------------------------------------------------------------------|----------|-------------| | path | The path to the README file. | No | README.md | | minCount | The minimum number of badges required. If not specified, at least one badge is required. | No | | | patterns | Array of regex patterns to match against badge alt text, image URLs, or link URLs. A badge must be found for all patterns. | No | |

Examples:

Check that README has at least one badge:

{
  "name": "readme/has-badges",
  "level": "error"
}

Check that README has at least 3 badges:

{
  "name": "readme/has-badges",
  "level": "error",
  "options": {
    "minCount": 3
  }
}

Check that README has specific badges (CI and Coverage):

{
  "name": "readme/has-badges",
  "level": "error",
  "options": {
    "patterns": ["CI", "Coverage"]
  }
}

Check that README has at least 2 badges and includes a CI badge:

{
  "name": "readme/has-badges",
  "level": "error",
  "options": {
    "minCount": 2,
    "patterns": ["CI"]
  }
}

Patterns are matched case-insensitively against badge alt text, image URLs, and link URLs. For example, the pattern "CI" will match badges with alt text like "CI", "ci", or URLs containing "CI" or "ci".

readme/has-section

The readme/has-section rule checks if a README file contains a specific markdown section header (e.g., ## Usage, ## Installation).

{
  "name": "readme/has-section",
  "level": "error",
  "options": {
    "section": "Usage",
    "path": "README.md",
    "caseSensitive": false
  }
}

| Option | Description | Required | Default | |-----------------|------------------------------------------------------------------------------|----------|-------------| | section | The name of the section to check for (without the ## prefix). | Yes | | | path | The path to the README file. | No | README.md | | caseSensitive | Whether to check the section header in a case-sensitive manner. | No | false |

Example:

Check that README has a "Usage" section:

{
  "name": "readme/has-section",
  "level": "error",
  "options": {
    "section": "Usage",
    "caseSensitive": true
  }
}

Note: This rule uses substring matching, so a section named "Usage Examples" will match a search for "Usage". For exact matching, specify the full section name.