🪝 Effortless Code Quality: Ultimate Pre-Commit Hooks Guide for 2025
Table of Contents
- Background
- What are Git hooks?
- Why use pre-commit?
- Common Use Cases
- Installation
- Hooks
- 01 🔒 Security
- 02 🔍 Code Qualityy
- 🐍 Python
- 🟨 JavaScript & Web Tools
- ✅ Data & Config Validation
- 📝 Markdown
- 🐚 Shell
- 🐮 Makefile
- 📊 SQL Code
- 📓 Notebooks
- 🖼️ Image Optimization
- ✨ Additional File Types
- 03 📁 Filesystem
- 04 🌳 Git Quality
- 🪵 Repo Constraints
- 🗒️ Commit Message Standards
- 05 🧪 Fast Tests (Local)
- Conclusion
- Other hooks to consider
- Individual Hooks
- Hook lists
- What's Missing?
- Ensemble Hooks
Background
I’ve been tinkering with app development since middle school, but it wasn’t until the latter half of my undergrad that I got my first taste of professional software engineering which was a whirlwind of new concepts — CI/CD, unit testing, packaging, dependency management, linting, formatting, schemas, databases, remote machines, cloud infrastructure, Git branches beyond main
, etc.
This flood of information, combined with my growing obsession with systems design (and mounting frustration with outdated tools and sloppy code), sparked a deep dive into modern development practices. I channeled this energy into creating GOTem (Gatlen’s Opinionated Template), a project template that weaves together what I consider the best practices and most robust tooling available. It’s a bit overkill, but building GOTem served two purposes: helping me learn these tools through hands-on experience, and creating a foundation for future work that embodies the practices I was discovering. It’s a relaxing side project alongside my normal responsibilities that I hope will bring a unique perspective to the AI Safety research space — at least until these skills are automated away. (✖﹏✖)
During this journey, I fell in love with pre-commit hooks — simplicity, power, and a wonderful “set-it-and-forget-it” quality. Unlike heavier solutions like GitHub Actions, pre-commit hooks integrate seamlessly into daily development while remaining lightweight. They’re particularly valuable for research teams who need quality assurance but don’t want to wrestle with complex CI/CD setups — you can make them as strict or lenient as your workflow demands.
What follows is my carefully curated collection of pre-commit hooks, primarily (but not exclusively) focused on Python development. While my original documentation includes detailed explanations of alternatives and other information, Medium’s format limitations mean I’ve had to summarize this content. For the complete rationale behind these choices, check out GOTem’s Pre-Commit documentation.
This page explains what pre-commit hooks are, why they are used, and the specific selection I have decided to make for GOTem to keep your projects pristine with every commit. The source config file can be found here.
What are Git hooks?
Git hooks are scripts that run automatically at some stage in the git-lifecycle. Most commonly, pre-commit hooks are used, running before a commit goes through. They act as a first line of defense for code quality by:
- Catching formatting issues
- Finding potential security risks
- Validating configurations
- Running quick tests
- Enforcing team standards
Why use pre-commit?
Pre-commit is a framework that makes these Git hooks:
- Easy to share — Hooks are defined in a single YAML file
- Language-agnostic — Works with Python, JavaScript, and more
- Fast — Only runs on staged files and are much quicker than CI/CD
- Forgettable — Team members don’t need to memorize QA tools; hooks run automatically
- Extendable — Large ecosystem of ready-to-use hooks
Pre-commit helps maintain code quality without slowing down development. While CI/CD pipelines might take minutes to run, pre-commit hooks provide instant feedback right when you commit. Despite the name, Pre-commit can install hooks at any stage (ex: Use a pre-push hook as a slightly more time-intensive pre-commit and push multiple commits at once.)
Common Use Cases
Pre-commit can be as strict as you want depending on your project’s quality-time tradeoff. Here are cases where commit-level checks make more sense than pull-request level:
- Linting/formatting code and data files
- Re-building code or documentation
- Making database migrations
- Preventing secrets or large files from being committed
- Requiring commit messages to follow a standard (Like Commitizen)
- Running fast tests
Note: Husky is a NodeJS-focused alternative to pre-commit.
Installation
Projects generated with GOTem come with the .pre-commit-config.yaml
file already configured as described below. If you would like to create your own, you can follow the instructions here.
Install the hooks with:
pre-commit install
And now you’ll see the hooks run after every commit to your repo.
Useful Commands:
# Test hooks without committing
pre-commit run --all-files
# Commit without running hooks
git commit --no-verify
Note: Use caution with pre-commit autoupdate on untrusted hooks due to potential security risks.
Hooks
This collection prioritizes best-in-class tools without redundancy. Rather than using multiple overlapping tools, I’ve selected the most effective option for each task. For example:
- Python linting uses only Ruff instead of multiple separate linters
- JSON/YAML/TOML validation uses specialized schema validators
- Security scanning uses a single comprehensive tool
All hooks labeled with # STRICT
are commented out by default and not recommended for every project. (Ex: Code style linters are typically desired for production-grade software but not research.)
01 🔒 Security
GitLeaks is a fast, lightweight scanner that prevents secrets (passwords, API keys, tokens) from being committed to your repository.
Note: TruffleHog is a more comprehensive but complex alternative to GitLeaks.
- repo: <https://github.com/gitleaks/gitleaks>
rev: v8.22.1
hooks:
- id: gitleaks
name: "🔒 security · Detect hardcoded secrets"
02 🔍 Code Quality
This section covers tools for code formatting, linting, type checking, and schema validation across different languages and file types. Best-in-class tools were chosen, avoiding redundant functionality. I opted for remote hook downloads over local commands to make the file more portable and self-updating.
🐍 python
Ruff is a fast, comprehensive Python formatter and linter that replaces multiple traditional tools (Black, Flake8, isort, pyupgrade, bandit, pydoclint, mccabe complexity, and more.) While it’s not yet at 100% parity with all these tools, its speed and broad coverage make it an excellent choice as the only Python linter/formatter:
- VSCode extension
- While Ruff does many things, type checking it does not… yet.
Note: Before Ruff, projects used multiple tools like Black, isort, Flake8, etc. Ruff consolidates these with better speed and modern defaults.
- repo: <https://github.com/astral-sh/ruff-pre-commit>
rev: v0.9.1
hooks:
- id: ruff-format
name: "🐍 python · Format with Ruff"
# STRICT
- id: ruff
args: [ --fix ]
Microsoft’s Pyright handles Python type checking:
- VSCode Extension, but Pylance, the default extension for Python, has it built-in.
- This is a community supported pre-commit hook, endorsed by microsoft
Note: MyPy is the original type checker, but Pyright offers better speed and features.
# STRICT
- repo: <https://github.com/RobertCraigie/pyright-python>
rev: v1.1.391
hooks:
- id: pyright
name: "🐍 python · Check types"
validate-pyproject specifically handles pyproject.toml validation. In the future, I may have check-jsonschema do this as well.
- repo: <https://github.com/abravalheri/validate-pyproject>
rev: v0.23
hooks:
- id: validate-pyproject
name: "🐍 python · Validate pyproject.toml"
additional_dependencies: ["validate-pyproject-schema-store[all]"]
🟨 JavaScript & Web Tools
Biome is a modern, fast formatter and linter for JS/TS ecosystems (JS[X], TS[X], JSON[C], CSS, GraphQL). It provides better defaults than ESLint.
Note: ESLint/Prettier are more established but slower; use them if you need specific plugins or framework support.
- repo: <https://github.com/biomejs/pre-commit>
rev: "v0.6.1"
hooks:
- id: biome-check
name: "🟨 javascript · Lint, format, and safe fixes with Biome"
additional_dependencies: ["@biomejs/biome@1.9.4"]
✅ Data & Config Validation
check-jsonschema validates various configuration files using JSON Schema. It supports JSON, YAML, and TOML files, and includes specialized validators like the TaskFile and GitHub Actions checker.
- Additional JSON schema available on Schema Store
- VSCode automatically provides intellisense and validation for JSON files with schema
- GitHub Actions VSCode Extension provides action YAML file intellisense and validation.
- repo: <https://github.com/python-jsonschema/check-jsonschema>
rev: 0.31.0
hooks:
- id: check-github-workflows
name: "🐙 github-actions · Validate gh workflow files"
args: ["--verbose"]
- id: check-taskfile
name: "✅ taskfile · Validate Task configuration"
📝 Markdown
mdformat for Markdown formatting with additional plugins for GitHub-Flavored Markdown, Ruff-style code formatting, and frontmatter support:
- repo: <https://github.com/hukkin/mdformat>
rev: 0.7.21
hooks:
- id: mdformat
name: "📝 markdown · Format markdown"
additional_dependencies:
- mdformat-gfm # GitHub-Flavored Markdown support
- mdformat-ruff # Python code formatting
- mdformat-frontmatter # YAML frontmatter support
- ruff # Required for mdformat-ruff
Markdownlint for Markdown linting.
- repo: <https://github.com/markdownlint/markdownlint>
rev: v0.12.0
hooks:
- id: markdownlint
name: "📝 markdown · Lint markdown"
🐚 Shell
ShellCheck lints your shell scripts.
# STRICT
- repo: <https://github.com/shellcheck-py/shellcheck-py>
rev: v0.10.0.1
hooks:
- id: shellcheck
name: "🐚 shell · Lint shell scripts"
bashate checks your shell script code style.
# STRICT
- repo: <https://github.com/openstack/bashate>
rev: 2.1.1
hooks:
- id: bashate
name: "🐚 shell · Check shell script code style"
🐮 Makefile
Checkmake for linting your Makefile.
- repo: <https://github.com/mrtazz/checkmake.git>
rev: 0.2.2
hooks:
- id: checkmake
name: "🐮 Makefile · Lint Makefile"
📊 SQL Code
SQLFluff can be used to lint and attempt to auto-fix any of your *.sql
files automatically.
- repo: <https://github.com/sqlfluff/sqlfluff>
rev: 3.3.0
hooks:
- id: sqlfluff-fix
name: "📊 SQL · Attempts to fix rule violations."
# STRICT
- id: sqlfluff-lint
name: "📊 SQL · Lint SQL code files"
📓 Notebooks
nbQA for Jupyter notebook quality assurance, allowing us to use our standard Python tools on notebooks.
Note: Ruff has native Jupyter Notebook support, making some nbQA features redundant.
- repo: <https://github.com/nbQA-dev/nbQA>
rev: 1.9.1
hooks:
- id: nbqa
entry: nbqa mdformat
name: "📓 notebook · Format markdown cells"
args: ["--nbqa-md"]
types: [jupyter]
additional_dependencies:
- mdformat
- mdformat-gfm
- mdformat-ruff
- mdformat-frontmatter
- ruff
# STRICT
# TODO: Convert to pyright
- id: nbqa-mypy
name: "📓 notebook · Type-check cells"
🖼️ Image Optimization
oxipng is a PNG optimizer written in Rust with lossy and lossless options. (The selection of arguments below are slightly lossy):
- repo: <https://github.com/shssoichiro/oxipng>
rev: v9.1.3
hooks:
- id: oxipng
name: "🖼️ images · Optimize PNG files"
args: [
"-o", "4",
"--strip", "safe",
"--alpha"
]
✨ Additional File Types
Prettier (HTML, YAML, CSS) handles formatting for various file types not covered by other tools. While it can be slow, sometimes produces code-breaking formatting, and I personally dislike it — it remains the standard for these file types.
Note: Future plans include replacing Prettier with more focused tools for specific file types.
- repo: <https://github.com/pre-commit/mirrors-prettier>
rev: v4.0.0-alpha.8
hooks:
- id: prettier
name: "✨ misc-files · Format misc web files"
types_or: [yaml, html, scss]
additional_dependencies:
- prettier@3.4.2
03 📁 Filesystem
Pre-commit hooks are collection of hooks managed by the pre-commit team. These hooks help maintain repository hygiene by preventing common file-related issues:
check-case-conflict
- Prevents issues on case-insensitive filesystems (Windows/MacOS)check-symlinks
&destroyed-symlinks
- Maintains symlink integritycheck-executables-have-shebangs
- Ensures scripts are properly configuredcheck-illegal-windows-names
- Check for files that cannot be created on Windows.
- repo: <https://github.com/pre-commit/pre-commit-hooks>
rev: v5.0.0
hooks:
- id: check-executables-have-shebangs
name: "📁 filesystem/⚙️ exec · Verify shebang presence"
- id: check-shebang-scripts-are-executable
name: "📁 filesystem/⚙️ exec · Verify script permissions"
- id: check-case-conflict
name: "📁 filesystem/📝 names · Check case sensitivity"
- id: check-illegal-windows-names
name: "📁 filesystem/📝 names · Validate Windows filenames"
- id: check-symlinks
name: "📁 filesystem/🔗 symlink · Check symlink validity"
- id: destroyed-symlinks
name: "📁 filesystem/🔗 symlink · Detect broken symlinks"
# ... More Below ...
04 🌳 Git Quality
🪵 Repo Constraints
Pre-commit hooks again, this time for branch protection restricting unwanted actions.
forbid-new-submodules
- Prevent addition of new git submodules (repo-in-a-repo). (Imo, Git submodules are a perfectly find practice, butcheck-merge-conflict
- Prevents committing unresolved merge conflictsno-commit-to-branch
- Protects main branches from direct commits (GitHub branch protections are for enterprise members only (sad))check-added-large-files
- Prevents committing files larger than 5000KB (Git Large File Storage (LFS) or Data Version Control (DVC) should instead be used)
- repo: <https://github.com/pre-commit/pre-commit-hooks>
rev: v5.0.0
hooks:
# ... More Above ...
- id: check-merge-conflict
name: "🌳 git · Detect conflict markers"
- id: forbid-new-submodules
name: "🌳 git · Prevent submodule creation"
- id: no-commit-to-branch
name: "🌳 git · Protect main branches"
args: ["--branch", "main", "--branch", "master"]
- id: check-added-large-files
name: "🌳 git · Block large file commits"
args: ['--maxkb=5000']
🗒️ Commit Message Standards
Commitizen enforces high-quality standardized commit messages that enable automatic changelog generation and semantic versioning
Note: Commitizen provides a CLI interface for standardized commits, with alternatives like czg for AI-generated commits.
default_install_hook_types:
- pre-commit
- commit-msg
repos:
# ... other hooks ...
- repo: <https://github.com/commitizen-tools/commitizen>
rev: v4.1.0
hooks:
- id: commitizen
name: "🌳 git · Validate commit message"
stages: [commit-msg]
If you would like to use commitizen with the cz-conventional-gitmoji preset, add additional_dependencies: [cz-conventional-gitmoji]
to your config:
default_install_hook_types:
- pre-commit
- commit-msg
repos:
# ... other hooks ...
- repo: <https://github.com/commitizen-tools/commitizen>
rev: v4.1.0
hooks:
- id: commitizen
name: "🌳 git · Validate commit message"
stages: [commit-msg]
additional_dependencies: [cz-conventional-gitmoji] # NEW
And make sure tell commitizen to use this preset in your pyproject.toml
file:
[tool.commitizen]
name = "cz_gitmoji"
05 🧪 Fast Tests (Local)
While extensive tests may be too time consuming for a pre-commit hook, it can be helpful to run fast local tests to detect unexpected failures in your code before you are 10 commits in and unsure which one broke your code.
The example below uses pytest
. The first hook checks that the tests don’t contain any syntax errors and can be successfully collected. This should always pass.
The second hook runs all fast pytests using my custom pytest option which leverages the pytest-timeout feature you can read about here
- repo: local
hooks:
- id: pytest-collect
name: 🧪 test · Validate test formatting
entry: ./.venv/bin/pytest tests
language: system
types: [python]
args: ["--collect-only"]
pass_filenames: false
always_run: true
# STRICT
- id: pytest-fast
name: 🧪 test · Run fast tests
entry: ./.venv/bin/pytest tests
language: system
types: [python]
args: ["--max-timeout=3"]
pass_filenames: false
always_run: true
Conclusion
This is by no means an exhaustive list of great hooks. Your encouraged to pick-and-choose as desired. Hooks don’t exist for all tools, so if you want to run those you can always use a local hook:
- repo: local
hooks:
- id: make-lint
name: Run 'make lint'
entry: make
args: ["lint"]
language: system
final .pre-commit-config.yaml
file
exclude: |
(?x)^(
.*\{\{.*\}\}.*| # Exclude any files with cookiecutter variables
docs/site/.*| # Exclude mkdocs compiled files
\.history/.*| # Exclude history files
.*cache.*/.*| # Exclude cache directories
.*venv.*/.*| # Exclude virtual environment directories
)$
fail_fast: true
default_language_version:
python: python3.12
default_install_hook_types:
- pre-commit
- commit-msg
repos:
#
# Documentation Here:
# https://gatlenculp.github.io/gatlens-opinionated-template/precommit/
#
# ---------------------------------------------------------------------------- #
# 🔄 Pre-Commit Hooks #
# ---------------------------------------------------------------------------- #
# ----------------------------- 🔒 Security Tools ---------------------------- #
- repo: https://github.com/gitleaks/gitleaks
rev: v8.22.1
hooks:
- id: gitleaks
name: "🔒 security · Detect hardcoded secrets"
# --------------------------- 🔍 Code Quality Tools -------------------------- #
### Python Tools ###
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.9.1
hooks:
- id: ruff-format
name: "🐍 python · Format with Ruff"
# STRICT
# - id: ruff
# name: "🐍 python · Lint and perform safe auto-fixes with Ruff"
# args: [--fix]
# STRICT
# - repo: https://github.com/RobertCraigie/pyright-python
# rev: v1.1.391
# hooks:
# - id: pyright
# name: "🐍 python · Check types"
- repo: https://github.com/abravalheri/validate-pyproject
rev: v0.23
hooks:
- id: validate-pyproject
name: "🐍 python · Validate pyproject.toml"
additional_dependencies: ["validate-pyproject-schema-store[all]"]
### Javascript & Web Tools ###
- repo: https://github.com/biomejs/pre-commit
rev: "v0.6.1"
hooks:
- id: biome-check
name: "🟨 javascript · Lint, format, and safe fixes with Biome"
additional_dependencies: ["@biomejs/biome@1.9.4"]
### Data & Config Validation ###
- repo: https://github.com/python-jsonschema/check-jsonschema
rev: 0.31.0
hooks:
- id: check-github-workflows
name: "🐙 github-actions · Validate gh workflow files"
args: ["--verbose"]
- id: check-taskfile
name: "✅ taskfile · Validate Task configuration"
### Markdown ###
- repo: https://github.com/hukkin/mdformat
rev: 0.7.21
hooks:
- id: mdformat
name: "📝 markdown · Format markdown"
additional_dependencies:
- mdformat-gfm
- mdformat-ruff
- mdformat-frontmatter
- ruff
# STRICT
# - repo: https://github.com/markdownlint/markdownlint
# rev: v0.12.0
# hooks:
# - id: markdownlint
# name: "📝 markdown · Lint markdown"
### Shell ###
# STRICT
# - repo: https://github.com/shellcheck-py/shellcheck-py
# rev: v0.10.0.1
# hooks:
# - id: shellcheck
# name: "🐚 shell · Lint shell scripts"
# STRICT
# - repo: https://github.com/openstack/bashate
# rev: 2.1.1
# hooks:
# - id: bashate
# name: "🐚 shell · Check shell script code style"
### Makefile ###
- repo: https://github.com/mrtazz/checkmake.git
rev: 0.2.2
hooks:
- id: checkmake
name: "🐮 Makefile · Lint Makefile"
### SQL ###
- repo: https://github.com/sqlfluff/sqlfluff
rev: 3.3.0
hooks:
- id: sqlfluff-fix
name: "📊 SQL · Attempts to fix rule violations."
# STRICT
# - id: sqlfluff-lint
# name: "📊 SQL · Lint SQL code files"
### Notebooks ###
- repo: https://github.com/nbQA-dev/nbQA
rev: 1.9.1
hooks:
- id: nbqa
entry: nbqa mdformat
name: "📓 notebook · Format markdown cells"
args: ["--nbqa-md"]
types: [jupyter]
additional_dependencies:
- mdformat
- mdformat-gfm
- mdformat-ruff
- mdformat-frontmatter
- ruff
# STRICT
# TODO: Convert to pyright
- id: nbqa-mypy
name: "📓 notebook · Type-check cells"
### PNG Images ###
- repo: https://github.com/shssoichiro/oxipng
rev: v9.1.3
hooks:
- id: oxipng
name: "🖼️ images · Optimize PNG files"
args: ["-o", "4", "--strip", "safe", "--alpha"]
### Additional File Types ###
- repo: https://github.com/pre-commit/mirrors-prettier
rev: v4.0.0-alpha.8
hooks:
- id: prettier
name: "✨ misc-files · Format misc web files"
types_or: [yaml, html, scss]
additional_dependencies:
- prettier@3.4.2
# ---------------------------- 📁 Filesystem Tools --------------------------- #
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
hooks:
# Filesystem Checks
- id: check-executables-have-shebangs
name: "📁 filesystem/⚙️ exec · Verify shebang presence"
- id: check-shebang-scripts-are-executable
name: "📁 filesystem/⚙️ exec · Verify script permissions"
- id: check-case-conflict
name: "📁 filesystem/📝 names · Check case sensitivity"
- id: check-illegal-windows-names
name: "📁 filesystem/📝 names · Validate Windows filenames"
- id: check-symlinks
name: "📁 filesystem/🔗 symlink · Check symlink validity"
- id: destroyed-symlinks
name: "📁 filesystem/🔗 symlink · Detect broken symlinks"
# ------------------------------- 🌳 Git Tools ------------------------------- #
- id: check-merge-conflict
name: "🌳 git · Detect conflict markers"
- id: forbid-new-submodules
name: "🌳 git · Prevent submodule creation"
- id: no-commit-to-branch
name: "🌳 git · Protect main branches"
args: ["--branch", "main", "--branch", "master"]
- id: check-added-large-files
name: "🌳 git · Block large file commits"
args: ["--maxkb=5000"]
# ---------------------------------------------------------------------------- #
# 📝 Commit Message Hooks #
# ---------------------------------------------------------------------------- #
#
# --------------------------- ✍️ Git Commit Quality -------------------------- #
### Commit Message Standards ###
- repo: https://github.com/commitizen-tools/commitizen
rev: v4.1.0
hooks:
- id: commitizen
name: "🌳 git · Validate commit message"
stages: [commit-msg]
additional_dependencies: [cz-conventional-gitmoji]
# ---------------------------------------------------------------------------- #
# 🧪 Fast Tests (Local) #
# ---------------------------------------------------------------------------- #
- repo: local
hooks:
- id: pytest-collect
name: 🧪 test · Validate test formatting
entry: ./.venv/bin/pytest tests
language: system
types: [python]
args: ["--collect-only"]
pass_filenames: false
always_run: true
# STRICT
- id: pytest-fast
name: 🧪 test · Run fast tests
entry: ./.venv/bin/pytest tests
language: system
types: [python]
args: ["--max-timeout=3"]
pass_filenames: false
always_run: true
Other hooks to consider
Here are some other hooks I haven’t added but would consider adding!
Individual Hooks
- buildbuf — Protobuf Linter
- Clang — C++ linter
- gitlint — Alternative to commitlint, may actually be preferred.
- typos or codespell — Finds common misspellings in code and documentation. One blog post preferred Typos over codespell because it found more typos (and apparently has VSCode support?)
- yamllint — YAML linter
- yamlfmt — YAML formatter by Google
- actionlint — Lints github action files, may be a better checker than the currently selected one.
- uv pre-commits — A collection of pre-commits for uv by Astral
- Vulture or Deadcode — Detect unused code in Python
- sync-pre-commit-deps — Sync pre-commit hook dependencies based on other installed hooks (to avoid installing multiple versions I assume).
Hook lists
- featured hooks by the pre-commit team
- first-party hooks by the pre-commit team (some used in this guide).
- Megalinter’s Supported Linters — Not all of these may provide pre-commits but regardless it’s a great collection of QA tools!
- (More but you’ll have to find them yourself :P)
What’s Missing?
- File optimizers for more than just PNGs (JPG, GIF, etc.)
- Profanity checker (You’ll be surprised with the amount of profanity in projects with friends). This can possibly be done with GitLeaks
- … This list could go on and on …
Ensemble Hooks (Linters, Formatters, etc.)
I went a bit overboard with cherry picking my favorite formatters, linters, etc. This may lead to maintaining more hooks than is worthwhile. It’s on my todo list to look at ensemble linters and formatters such as Megalinter and Superlinter which would GREATLY reduce the amount of overhead for code QA and provide support to languages without hunting down multiple hooks for each of them. These also have the added benefit of better integration to other CI/CD tools, pre-built container images, and security scanning.
🦙 MegaLinter analyzes 50 languages, 22 formats, 21 tooling formats, excessive copy-pastes, spelling mistakes and security issues in your repository sources with a GitHub Action, other CI tools or locally.
I’m slightly concerned that these ensemble linters might be an additional annoying piece of software to learn and configure that sets off small teams from using them entirely. Additionally, this software may not support your favorite linters — some of which mentioned in this guide include Biomejs and mdformat. I’m sure the list of available tools is extendable although I’m unsure how much effort is needed to do so.