docs(kyberforge): add pre-commit hook research documentation
## Why Captures structured reference material for pre-commit hooks to support skill authoring and hook configuration work in the kyberforge plugin. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01P87CiC58Ru2PPWYTeXtjHT
This commit is contained in:
@@ -0,0 +1,152 @@
|
||||
---
|
||||
topic: cli-reference
|
||||
source_keys:
|
||||
- context7-pre-commit-com
|
||||
- pre-commit-com
|
||||
---
|
||||
|
||||
## Exit codes
|
||||
|
||||
| Code | Meaning |
|
||||
|------|---------|
|
||||
| 0 | All hooks passed |
|
||||
| 1 | Hook(s) failed or files were modified |
|
||||
| 3 | Unexpected internal error |
|
||||
| 130 | Interrupted (Ctrl+C) |
|
||||
|
||||
## `pre-commit run`
|
||||
|
||||
Run hooks against files.
|
||||
|
||||
```bash
|
||||
pre-commit run # staged files only (normal commit flow)
|
||||
pre-commit run --all-files # entire working tree
|
||||
pre-commit run check-yaml # single hook by ID
|
||||
pre-commit run --files path/to/file.py # specific files
|
||||
pre-commit run --from-ref HEAD~3 --to-ref HEAD # diff range (CI use)
|
||||
pre-commit run --hook-stage pre-push # specific lifecycle stage
|
||||
pre-commit run --show-diff-on-failure # print diff when hook fails
|
||||
pre-commit run --verbose # always show hook output
|
||||
```
|
||||
|
||||
For CI, prefer `--from-ref`/`--to-ref` over `--all-files` on large repos. Cache `$PRE_COMMIT_HOME` keyed on `.pre-commit-config.yaml` hash.
|
||||
|
||||
## `pre-commit install`
|
||||
|
||||
Wire pre-commit into `.git/hooks/`.
|
||||
|
||||
```bash
|
||||
pre-commit install # default: pre-commit stage
|
||||
pre-commit install -f # overwrite existing hooks
|
||||
pre-commit install --install-hooks # pre-create all environments now
|
||||
pre-commit install -t pre-commit -t pre-push # multiple stages
|
||||
pre-commit install --allow-missing-config # silently skip if no config file
|
||||
```
|
||||
|
||||
Must be run once per clone. Re-run after changing `default_install_hook_types`.
|
||||
|
||||
## `pre-commit uninstall`
|
||||
|
||||
```bash
|
||||
pre-commit uninstall
|
||||
pre-commit uninstall -t pre-push
|
||||
```
|
||||
|
||||
## `pre-commit autoupdate`
|
||||
|
||||
Update `rev` values in `.pre-commit-config.yaml` to the latest tag.
|
||||
|
||||
```bash
|
||||
pre-commit autoupdate # latest tag for all repos
|
||||
pre-commit autoupdate --bleeding-edge # use default branch HEAD
|
||||
pre-commit autoupdate --freeze # pin to commit SHA (reproducible)
|
||||
pre-commit autoupdate --repo https://github.com/pre-commit/pre-commit-hooks # single repo
|
||||
pre-commit autoupdate -j 4 # parallel fetches (v3.3.0+)
|
||||
```
|
||||
|
||||
This modifies `.pre-commit-config.yaml` in-place. A skill updating an existing config should call this rather than manually editing `rev` values.
|
||||
|
||||
## `pre-commit validate-config`
|
||||
|
||||
Validate `.pre-commit-config.yaml` schema. Non-zero exit means the file is invalid.
|
||||
|
||||
```bash
|
||||
pre-commit validate-config
|
||||
pre-commit validate-config path/to/other-config.yaml
|
||||
```
|
||||
|
||||
A skill must call this after writing or modifying a config before considering the task complete.
|
||||
|
||||
## `pre-commit validate-manifest`
|
||||
|
||||
Validate a `.pre-commit-hooks.yaml` hook definition file.
|
||||
|
||||
```bash
|
||||
pre-commit validate-manifest .pre-commit-hooks.yaml
|
||||
```
|
||||
|
||||
## `pre-commit try-repo`
|
||||
|
||||
Test a hook repo without adding it to the config.
|
||||
|
||||
```bash
|
||||
pre-commit try-repo https://github.com/pre-commit/pre-commit-hooks
|
||||
pre-commit try-repo ../local-hook-repo --all-files --verbose
|
||||
pre-commit try-repo ../hook-repo specific-hook-id --verbose
|
||||
```
|
||||
|
||||
Supports all `run` options. Use this in a skill to smoke-test a hook before writing it to config.
|
||||
|
||||
## `pre-commit sample-config`
|
||||
|
||||
Print a minimal starter config to stdout.
|
||||
|
||||
```bash
|
||||
pre-commit sample-config > .pre-commit-config.yaml
|
||||
```
|
||||
|
||||
## `pre-commit install-hooks`
|
||||
|
||||
Pre-create all hook environments without running any hooks.
|
||||
|
||||
```bash
|
||||
pre-commit install-hooks
|
||||
```
|
||||
|
||||
## `pre-commit gc`
|
||||
|
||||
Remove unused cached hook environments (safe to run at any time).
|
||||
|
||||
```bash
|
||||
pre-commit gc
|
||||
```
|
||||
|
||||
## `pre-commit clean`
|
||||
|
||||
Wipe all cached environments. Forces full rebuild on next run.
|
||||
|
||||
```bash
|
||||
pre-commit clean
|
||||
```
|
||||
|
||||
## `SKIP` environment variable
|
||||
|
||||
Skip specific hooks by ID without modifying config. Comma-separated, exact IDs, no spaces.
|
||||
|
||||
```bash
|
||||
SKIP=flake8,check-yaml git commit -m "wip"
|
||||
```
|
||||
|
||||
## CI recipe
|
||||
|
||||
```yaml
|
||||
# GitHub Actions
|
||||
- name: Cache pre-commit envs
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.cache/pre-commit
|
||||
key: pre-commit|${{ hashFiles('.pre-commit-config.yaml') }}
|
||||
|
||||
- name: Run pre-commit
|
||||
run: pre-commit run --from-ref ${{ github.event.pull_request.base.sha }} --to-ref HEAD
|
||||
```
|
||||
@@ -0,0 +1,206 @@
|
||||
---
|
||||
topic: configuration
|
||||
source_keys:
|
||||
- context7-pre-commit-com
|
||||
- pre-commit-com
|
||||
---
|
||||
|
||||
## `.pre-commit-config.yaml` structure
|
||||
|
||||
### Top-level keys
|
||||
|
||||
| Key | Type | Default | Purpose |
|
||||
|-----|------|---------|---------|
|
||||
| `repos` | List | required | List of repo blocks |
|
||||
| `default_install_hook_types` | List | `[pre-commit]` | Hook types installed when `pre-commit install` is run without `-t` |
|
||||
| `default_language_version` | Dict | `{}` | Maps language name → version string; overrides per-hook defaults |
|
||||
| `default_stages` | List | all stages | Applied to every hook unless the hook specifies `stages` |
|
||||
| `files` | Regex string | `''` | Global include pattern applied before hook-level filters |
|
||||
| `exclude` | Regex string | `^$` | Global exclude pattern |
|
||||
| `fail_fast` | Boolean | `false` | Stop after the first failing hook |
|
||||
| `minimum_pre_commit_version` | String | `'0'` | Minimum pre-commit version required to use this config |
|
||||
|
||||
### Repo block keys
|
||||
|
||||
| Key | Required | Description |
|
||||
|-----|----------|-------------|
|
||||
| `repo` | Yes | Git URL, or the special values `local` or `meta` |
|
||||
| `rev` | Yes (not for `local`/`meta`) | Tag or SHA — must be immutable |
|
||||
| `hooks` | Yes | List of hook override blocks |
|
||||
|
||||
### Hook override block keys
|
||||
|
||||
These keys appear under `hooks:` inside a repo block. All are optional overrides of the hook's upstream manifest values.
|
||||
|
||||
| Key | Type | Description |
|
||||
|-----|------|-------------|
|
||||
| `id` | String (required) | Hook identifier — must match an `id` in the repo's `.pre-commit-hooks.yaml` |
|
||||
| `alias` | String | Extra name for targeting with `pre-commit run <alias>` |
|
||||
| `name` | String | Override the display name |
|
||||
| `language_version` | String | Override language version |
|
||||
| `files` | Regex string | Override file include pattern (appended to upstream with AND logic) |
|
||||
| `exclude` | Regex string | Override file exclude pattern |
|
||||
| `types` | List | AND-logic type filter (all tags must match) |
|
||||
| `types_or` | List | OR-logic type filter (any tag must match) |
|
||||
| `exclude_types` | List | Exclude files matching these types |
|
||||
| `args` | List | Additional CLI arguments prepended before filenames |
|
||||
| `stages` | List | Which git stages trigger this hook |
|
||||
| `additional_dependencies` | List | Extra packages to install into hook environment |
|
||||
| `always_run` | Boolean | Run even if no files match the filter |
|
||||
| `verbose` | Boolean | Always print output (not only on failure) |
|
||||
| `log_file` | String | Write output to this file path on failure |
|
||||
|
||||
### Complete annotated example
|
||||
|
||||
```yaml
|
||||
minimum_pre_commit_version: '3.0.0'
|
||||
fail_fast: false
|
||||
default_language_version:
|
||||
python: python3.11
|
||||
default_stages: [pre-commit, pre-push]
|
||||
exclude: ^vendor/
|
||||
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v4.5.0
|
||||
hooks:
|
||||
- id: end-of-file-fixer
|
||||
- id: check-yaml
|
||||
- id: check-json
|
||||
- id: pretty-format-json
|
||||
args: [--autofix]
|
||||
- id: trailing-whitespace
|
||||
exclude: ^tests/fixtures/
|
||||
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: validate-manifest
|
||||
name: Validate marketplace manifest
|
||||
entry: python scripts/validate_manifest.py
|
||||
language: python
|
||||
files: ^\.claude-plugin/marketplace\.json$
|
||||
always_run: false
|
||||
|
||||
- repo: meta
|
||||
hooks:
|
||||
- id: check-hooks-apply
|
||||
- id: check-useless-excludes
|
||||
```
|
||||
|
||||
### Multi-line exclude pattern (readable for long lists)
|
||||
|
||||
```yaml
|
||||
- id: my-hook
|
||||
exclude: |
|
||||
(?x)^(
|
||||
path/to/file1.py|
|
||||
path/to/file2.py|
|
||||
path/to/generated/.*
|
||||
)$
|
||||
```
|
||||
|
||||
## Stages
|
||||
|
||||
Valid `stages` values: `pre-commit`, `pre-push`, `commit-msg`, `prepare-commit-msg`, `post-checkout`, `post-commit`, `post-merge`, `post-rewrite`, `pre-merge-commit`, `pre-rebase`, `manual`.
|
||||
|
||||
The `manual` stage only runs when explicitly invoked: `pre-commit run --hook-stage manual <hookid>`.
|
||||
|
||||
To install hooks for non-default stages, pass them to install:
|
||||
```bash
|
||||
pre-commit install -t pre-commit -t pre-push -t commit-msg
|
||||
```
|
||||
|
||||
Or declare them in config so they install automatically:
|
||||
```yaml
|
||||
default_install_hook_types: [pre-commit, pre-push, commit-msg]
|
||||
```
|
||||
|
||||
## `types` vs `types_or` vs `files`
|
||||
|
||||
- `types: [json, text]` — file must carry ALL listed tags (AND)
|
||||
- `types_or: [javascript, typescript]` — file must carry ANY listed tag (OR)
|
||||
- `files: \.py$` — regex applied via `re.search()` (not full match) on the file path
|
||||
- `exclude: \.generated\.py$` — regex excludes matching paths after `files` matches
|
||||
|
||||
Both `types` and `files` filters must pass for a file to be processed. Use `identify-cli <file>` to see what tags a file has.
|
||||
|
||||
## Local hooks (`repo: local`)
|
||||
|
||||
No external repository required. Required fields: `id`, `name`, `language`, `entry`.
|
||||
|
||||
```yaml
|
||||
- repo: local
|
||||
hooks:
|
||||
# System tool already installed (don't let pre-commit manage env)
|
||||
- id: shellcheck
|
||||
name: shellcheck
|
||||
entry: shellcheck
|
||||
language: unsupported # formerly "system"
|
||||
types: [shell]
|
||||
|
||||
# Script in the repo
|
||||
- id: run-tests
|
||||
name: Run test suite
|
||||
entry: bash tests/run-tests.sh
|
||||
language: unsupported_script # formerly "script"
|
||||
pass_filenames: false
|
||||
always_run: true
|
||||
stages: [pre-push]
|
||||
|
||||
# Always-fail guard (lightweight, no env needed)
|
||||
- id: no-dotenv
|
||||
name: No .env files
|
||||
entry: .env files must not be committed
|
||||
language: fail
|
||||
files: \.env$
|
||||
|
||||
# Python with managed dependencies
|
||||
- id: my-checker
|
||||
name: My Python Checker
|
||||
entry: python -m mymodule.checker
|
||||
language: python
|
||||
additional_dependencies: [requests==2.28.0]
|
||||
types: [python]
|
||||
```
|
||||
|
||||
### Language choices for local hooks
|
||||
|
||||
| Language | Description |
|
||||
|----------|-------------|
|
||||
| `unsupported` / `system` | Runs executable from system PATH — pre-commit does not manage environment |
|
||||
| `unsupported_script` / `script` | Runs a script path relative to repo root |
|
||||
| `fail` | Always fails; `entry` text becomes the error message — good for forbidden file patterns |
|
||||
| `python` | Creates isolated venv; `additional_dependencies` are pip-installed |
|
||||
| `node` | Creates isolated node env |
|
||||
| `ruby`, `golang`, `rust`, `docker`, `docker_image` | Language-specific isolated envs |
|
||||
|
||||
### `pass_filenames` behavior
|
||||
|
||||
`true` (default): `entry arg1 arg2 file1 file2 file3`
|
||||
`false`: `entry arg1 arg2` — hook gets no filenames; use for repo-wide or stateful checks.
|
||||
|
||||
## Meta hooks (`repo: meta`)
|
||||
|
||||
```yaml
|
||||
- repo: meta
|
||||
hooks:
|
||||
- id: check-hooks-apply # each hook must match ≥1 file — catches dead hooks
|
||||
- id: check-useless-excludes # each exclude must exclude ≥1 file — catches dead excludes
|
||||
- id: identity # debug: prints every filename passed to pre-commit
|
||||
```
|
||||
|
||||
## Hazmat helpers (v4.5.0+)
|
||||
|
||||
Entry-point prefixes for edge cases:
|
||||
|
||||
```yaml
|
||||
# Change directory before running (monorepo)
|
||||
entry: pre-commit hazmat cd subdir my-bin --
|
||||
|
||||
# Treat non-zero exit as warning instead of failure
|
||||
entry: pre-commit hazmat ignore-exit-code my-bin --
|
||||
verbose: true
|
||||
|
||||
# Run hook once per file (not batched)
|
||||
entry: pre-commit hazmat n1 my-bin --
|
||||
```
|
||||
220
plugins/kyberforge/docs/research/docs/pre-commit/examples.md
Normal file
220
plugins/kyberforge/docs/research/docs/pre-commit/examples.md
Normal file
@@ -0,0 +1,220 @@
|
||||
---
|
||||
topic: examples
|
||||
source_keys:
|
||||
- context7-pre-commit-com
|
||||
- pre-commit-com
|
||||
- context7-pre-commit-hooks
|
||||
---
|
||||
|
||||
## Starter config
|
||||
|
||||
```bash
|
||||
pre-commit sample-config > .pre-commit-config.yaml
|
||||
pre-commit validate-config
|
||||
pre-commit install
|
||||
pre-commit run --all-files
|
||||
```
|
||||
|
||||
## Common config patterns
|
||||
|
||||
### File hygiene only
|
||||
|
||||
```yaml
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v4.5.0
|
||||
hooks:
|
||||
- id: end-of-file-fixer
|
||||
- id: trailing-whitespace
|
||||
- id: check-yaml
|
||||
- id: check-json
|
||||
- id: check-toml
|
||||
- id: check-merge-conflict
|
||||
- id: detect-private-key
|
||||
```
|
||||
|
||||
### With auto-formatting
|
||||
|
||||
```yaml
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v4.5.0
|
||||
hooks:
|
||||
- id: end-of-file-fixer
|
||||
- id: trailing-whitespace
|
||||
- id: pretty-format-json
|
||||
args: [--autofix]
|
||||
- id: mixed-line-ending
|
||||
args: [--fix=lf]
|
||||
```
|
||||
|
||||
### With branch protection and secret scanning
|
||||
|
||||
```yaml
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v4.5.0
|
||||
hooks:
|
||||
- id: detect-private-key
|
||||
- id: detect-aws-credentials
|
||||
- id: no-commit-to-branch
|
||||
args: [--branch, main, --branch, master]
|
||||
- id: check-added-large-files
|
||||
args: [--maxkb=500]
|
||||
```
|
||||
|
||||
### Multi-stage config (pre-commit + pre-push + commit-msg)
|
||||
|
||||
```yaml
|
||||
default_install_hook_types: [pre-commit, pre-push, commit-msg]
|
||||
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v4.5.0
|
||||
hooks:
|
||||
- id: end-of-file-fixer
|
||||
stages: [pre-commit]
|
||||
- id: check-yaml
|
||||
stages: [pre-commit]
|
||||
|
||||
- repo: https://github.com/compilerla/conventional-pre-commit
|
||||
rev: v2.4.0
|
||||
hooks:
|
||||
- id: conventional-pre-commit
|
||||
stages: [commit-msg]
|
||||
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: run-tests
|
||||
name: Run test suite
|
||||
entry: bash tests/run-tests.sh
|
||||
language: unsupported_script
|
||||
pass_filenames: false
|
||||
always_run: true
|
||||
stages: [pre-push]
|
||||
```
|
||||
|
||||
### Kubernetes-style YAML (needs --unsafe for custom tags)
|
||||
|
||||
```yaml
|
||||
- id: check-yaml
|
||||
args: ['--unsafe']
|
||||
exclude: ^helm/templates/
|
||||
```
|
||||
|
||||
### Monorepo with subdirectory scope
|
||||
|
||||
```yaml
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: validate-frontend
|
||||
name: Validate frontend
|
||||
entry: pre-commit hazmat cd frontend npm run lint --
|
||||
language: unsupported
|
||||
files: ^frontend/
|
||||
pass_filenames: false
|
||||
```
|
||||
|
||||
## Testing and validation workflow
|
||||
|
||||
```bash
|
||||
# After writing a new config:
|
||||
pre-commit validate-config
|
||||
|
||||
# Test all hooks against entire repo:
|
||||
pre-commit run --all-files
|
||||
|
||||
# Test a single hook:
|
||||
pre-commit run check-yaml --all-files
|
||||
|
||||
# Test a hook repo before adding it to config:
|
||||
pre-commit try-repo https://github.com/psf/black --all-files
|
||||
|
||||
# Update all rev pins to latest tags:
|
||||
pre-commit autoupdate
|
||||
|
||||
# Freeze revs to commit SHAs for reproducibility:
|
||||
pre-commit autoupdate --freeze
|
||||
|
||||
# Skip a hook for one commit:
|
||||
SKIP=check-yaml git commit -m "add yaml with custom tags"
|
||||
|
||||
# Run manually against a diff (CI):
|
||||
pre-commit run --from-ref origin/main --to-ref HEAD
|
||||
```
|
||||
|
||||
## Local hook patterns
|
||||
|
||||
### Forbidden file guard (cheapest possible hook)
|
||||
|
||||
```yaml
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: no-env-files
|
||||
name: No .env files
|
||||
entry: .env files must not be committed — use environment variables
|
||||
language: fail
|
||||
files: \.env(\..+)?$
|
||||
```
|
||||
|
||||
### Validate a generated file
|
||||
|
||||
```yaml
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: validate-manifest
|
||||
name: Validate plugin manifest
|
||||
entry: bash scripts/check-manifests.sh
|
||||
language: unsupported_script
|
||||
files: ^\.claude-plugin/
|
||||
pass_filenames: false
|
||||
always_run: false
|
||||
```
|
||||
|
||||
### Run full test suite pre-push
|
||||
|
||||
```yaml
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: run-tests
|
||||
name: Run test suite
|
||||
entry: bash tests/run-tests.sh
|
||||
language: unsupported_script
|
||||
pass_filenames: false
|
||||
always_run: true
|
||||
stages: [pre-push]
|
||||
```
|
||||
|
||||
### Validate SKILL.md frontmatter (inline bash, as used in this repo)
|
||||
|
||||
```yaml
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: skill-frontmatter
|
||||
name: SKILL.md frontmatter validation
|
||||
entry: bash
|
||||
language: system
|
||||
files: 'SKILL\.md$'
|
||||
args:
|
||||
- -c
|
||||
- |
|
||||
for f in "$@"; do
|
||||
if [[ -f "$f" ]]; then
|
||||
if ! grep -q "^name:" "$f" || ! grep -q "^description:" "$f"; then
|
||||
echo "ERROR: $f missing required frontmatter (name: and description:)"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
done
|
||||
```
|
||||
|
||||
## Adding meta-validation
|
||||
|
||||
Add after all other repos to catch dead hooks/excludes:
|
||||
|
||||
```yaml
|
||||
- repo: meta
|
||||
hooks:
|
||||
- id: check-hooks-apply
|
||||
- id: check-useless-excludes
|
||||
```
|
||||
@@ -0,0 +1,156 @@
|
||||
---
|
||||
topic: hook-authoring
|
||||
source_keys:
|
||||
- context7-pre-commit-com
|
||||
- pre-commit-com
|
||||
---
|
||||
|
||||
## What this covers
|
||||
|
||||
How to author a hook that lives in its own git repo (shareable), vs. a local hook that lives in the consuming repo. A pre-commit skill/agent needs to know both: it may create local hooks inline in the config, or scaffold a proper hook repo.
|
||||
|
||||
## Hook definition file: `.pre-commit-hooks.yaml`
|
||||
|
||||
Required in any git repo that others consume as a pre-commit hook source. Lives at the repo root.
|
||||
|
||||
```yaml
|
||||
- id: my-hook
|
||||
name: My Hook
|
||||
description: One-line description shown in pre-commit output.
|
||||
entry: my-hook-script # executable name on PATH, or path relative to repo root
|
||||
language: python # controls how pre-commit installs the environment
|
||||
types: [text] # file type filter (AND logic)
|
||||
files: '' # regex filter on file path (re.search)
|
||||
exclude: ^$ # regex exclusion on file path
|
||||
args: [] # default arguments
|
||||
pass_filenames: true # append matched filenames after args
|
||||
always_run: false # run even with 0 matched files
|
||||
require_serial: false # run in parallel by default
|
||||
fail_fast: false # stop other hooks if this fails
|
||||
verbose: false # always print stdout/stderr (not only on failure)
|
||||
stages: [pre-commit] # git lifecycle stages that trigger this hook
|
||||
additional_dependencies: [] # packages installed into hook environment
|
||||
minimum_pre_commit_version: '0'
|
||||
language_version: default # e.g. 'python3.11', 'node18'
|
||||
```
|
||||
|
||||
Validate with: `pre-commit validate-manifest .pre-commit-hooks.yaml`
|
||||
|
||||
## Language types and what they control
|
||||
|
||||
| Language | Environment | When to use |
|
||||
|----------|-------------|-------------|
|
||||
| `python` | Isolated venv | Pure Python hook; `additional_dependencies` are pip packages |
|
||||
| `node` | Isolated node_modules | JS/TS hook; `additional_dependencies` are npm packages |
|
||||
| `golang` | Builds from source | Go hook; `additional_dependencies` are Go module paths |
|
||||
| `ruby` | Isolated gem env | Ruby hook; `additional_dependencies` are gems |
|
||||
| `rust` | Cargo build | Rust hook |
|
||||
| `docker` | Docker image built from `entry` | Use when no other language fits |
|
||||
| `docker_image` | Pulls Docker image by `entry` | When image is pre-built |
|
||||
| `unsupported` / `system` | No environment — runs from system PATH | When tool is pre-installed on host |
|
||||
| `unsupported_script` / `script` | No environment — runs repo-relative path | Scripts committed to the hook repo |
|
||||
| `fail` | No environment — always exits 1 | Forbidden file guards |
|
||||
| `conda` | Conda environment | Conda-native hooks |
|
||||
| `coursier` | Coursier (Scala/JVM) | JVM hooks |
|
||||
|
||||
## `entry` field
|
||||
|
||||
`entry` is the executable invoked. It is resolved differently by language:
|
||||
- `python`/`node`/etc.: the installed script name (what's in `scripts:` in `setup.cfg` or `package.json`)
|
||||
- `system`/`unsupported`: resolved from system `PATH`
|
||||
- `script`/`unsupported_script`: path relative to the hook repo root
|
||||
- `fail`: the `entry` string is printed as the error message
|
||||
|
||||
Arguments in `entry` are supported: `entry: python -m mymodule.cli` works.
|
||||
|
||||
## `pass_filenames` and argument ordering
|
||||
|
||||
When `pass_filenames: true` (default), pre-commit calls:
|
||||
```
|
||||
entry <args from manifest> <args from config override> <matched file1> <file2> ...
|
||||
```
|
||||
|
||||
When `pass_filenames: false`, pre-commit calls:
|
||||
```
|
||||
entry <args from manifest> <args from config override>
|
||||
```
|
||||
|
||||
Use `pass_filenames: false` for:
|
||||
- Hooks that check the repo state as a whole (test runners, manifest validators)
|
||||
- Hooks whose tool only accepts one file at a time (combine with `require_serial: true` or use `pre-commit hazmat n1`)
|
||||
|
||||
## `require_serial`
|
||||
|
||||
Default is `false` — pre-commit batches files and runs hook processes in parallel. Set `true` when:
|
||||
- The hook reads/writes shared state (a database, a lock file)
|
||||
- The tool cannot handle concurrent invocations
|
||||
- The tool must process all files in a single process call but `pass_filenames: false` is not appropriate
|
||||
|
||||
## Testing a hook during development
|
||||
|
||||
```bash
|
||||
# Test without adding to any consuming repo's config:
|
||||
pre-commit try-repo . --all-files --verbose
|
||||
pre-commit try-repo . specific-hook-id --verbose
|
||||
|
||||
# Test on a specific file:
|
||||
pre-commit try-repo . my-hook --files path/to/file.py --verbose
|
||||
```
|
||||
|
||||
`try-repo .` runs from the hook repo's own directory. Use a path to the hook repo from the consuming repo.
|
||||
|
||||
## Minimum viable hook repo structure
|
||||
|
||||
```
|
||||
my-hook-repo/
|
||||
.pre-commit-hooks.yaml # hook manifest
|
||||
hooks/
|
||||
my_hook.py # hook script
|
||||
setup.cfg # (if python) declares scripts entry point
|
||||
pyproject.toml # (if python) build config
|
||||
```
|
||||
|
||||
`setup.cfg` entry point example:
|
||||
```ini
|
||||
[options.entry_points]
|
||||
console_scripts =
|
||||
my-hook = hooks.my_hook:main
|
||||
```
|
||||
|
||||
## Local hooks (no separate repo)
|
||||
|
||||
For hooks that belong to the consuming repo and don't need to be shared:
|
||||
|
||||
```yaml
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: my-local-check
|
||||
name: My local check
|
||||
entry: ./scripts/check.sh
|
||||
language: unsupported_script
|
||||
pass_filenames: false
|
||||
always_run: true
|
||||
```
|
||||
|
||||
No `.pre-commit-hooks.yaml` needed. All fields that would normally come from the manifest must be declared inline in the config.
|
||||
|
||||
## Inline bash hook (entry splits args)
|
||||
|
||||
```yaml
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: validate-frontmatter
|
||||
name: Validate frontmatter
|
||||
entry: bash
|
||||
language: system
|
||||
files: 'SKILL\.md$'
|
||||
args:
|
||||
- -c
|
||||
- |
|
||||
for f in "$@"; do
|
||||
grep -q "^name:" "$f" || { echo "Missing name: in $f"; exit 1; }
|
||||
done
|
||||
pass_filenames: true
|
||||
```
|
||||
|
||||
Note: `entry: bash` + `args: [-c, <script>]` means pre-commit calls `bash -c <script> -- file1 file2`. The `--` and `$@` pattern is important — without it, filenames are not available inside the script.
|
||||
@@ -0,0 +1,174 @@
|
||||
---
|
||||
topic: hooks-reference
|
||||
source_keys:
|
||||
- context7-pre-commit-hooks
|
||||
- pre-commit-hooks-github
|
||||
---
|
||||
|
||||
## pre-commit-hooks (official collection)
|
||||
|
||||
Repo: `https://github.com/pre-commit/pre-commit-hooks`
|
||||
Latest version: `v6.0.0`
|
||||
Pinned in this repo: `v4.5.0` — consider running `pre-commit autoupdate`
|
||||
|
||||
```yaml
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v6.0.0
|
||||
hooks:
|
||||
- id: <hook-id>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## File syntax / content checks
|
||||
|
||||
| Hook ID | What it does | Key args |
|
||||
|---------|-------------|----------|
|
||||
| `check-ast` | Validates Python files parse as valid AST | — |
|
||||
| `check-json` | Validates JSON parses correctly | — |
|
||||
| `check-toml` | Validates TOML parses correctly | — |
|
||||
| `check-xml` | Validates XML parses correctly | — |
|
||||
| `check-yaml` | Validates YAML parses correctly | `--allow-multiple-documents`, `--unsafe` (syntax-only, enables custom tags) |
|
||||
| `check-merge-conflict` | Detects unresolved merge markers (`<<<<<<<`) | `--assume-in-merge` |
|
||||
|
||||
---
|
||||
|
||||
## Filesystem / naming / encoding checks
|
||||
|
||||
| Hook ID | What it does | Key args |
|
||||
|---------|-------------|----------|
|
||||
| `check-added-large-files` | Blocks files over size threshold | `--maxkb=N` (default 500), `--enforce-all` |
|
||||
| `check-case-conflict` | Detects filenames differing only by case (macOS/Windows hazard) | — |
|
||||
| `check-executables-have-shebangs` | Ensures executable files have a shebang | — |
|
||||
| `check-shebang-scripts-are-executable` | Ensures files with shebangs are executable | — |
|
||||
| `check-illegal-windows-names` | Detects filenames illegal on Windows | — |
|
||||
| `check-symlinks` | Detects broken symlinks | — |
|
||||
| `destroyed-symlinks` | Catches symlinks converted to regular files (common on Windows) | — |
|
||||
| `fix-byte-order-marker` | Removes UTF-8 BOM | — |
|
||||
|
||||
---
|
||||
|
||||
## Security
|
||||
|
||||
| Hook ID | What it does | Key args |
|
||||
|---------|-------------|----------|
|
||||
| `detect-private-key` | Blocks PEM private key material | — |
|
||||
| `detect-aws-credentials` | Blocks AWS credentials (reads `~/.aws/credentials`) | `--credentials-file PATH` (repeatable), `--allow-missing-credentials` |
|
||||
|
||||
---
|
||||
|
||||
## Git-related
|
||||
|
||||
| Hook ID | What it does | Key args |
|
||||
|---------|-------------|----------|
|
||||
| `no-commit-to-branch` | Blocks commits to protected branches | `-b`/`--branch NAME` (repeatable, default: `main`+`master`), `-p`/`--pattern REGEX` |
|
||||
| `check-vcs-permalinks` | Ensures GitHub links are SHAs not branch refs | `--additional-github-domain DOMAIN` |
|
||||
| `forbid-new-submodules` | Blocks adding new git submodules | — |
|
||||
| `forbid-submodules` | Blocks any submodule in the repo | — |
|
||||
|
||||
Note: `no-commit-to-branch` runs with `always_run: true` by default — it ignores `files`/`types` filters.
|
||||
|
||||
---
|
||||
|
||||
## Whitespace / line-ending fixers
|
||||
|
||||
| Hook ID | What it does | Key args |
|
||||
|---------|-------------|----------|
|
||||
| `end-of-file-fixer` | Ensures files end with exactly one newline | — |
|
||||
| `trailing-whitespace` | Removes trailing whitespace | `--markdown-linebreak-ext=md` (preserve ` ` line breaks), `--chars` |
|
||||
| `mixed-line-ending` | Normalises line endings | `--fix=auto` (default), `--fix=lf`, `--fix=crlf`, `--fix=no` (check only) |
|
||||
|
||||
---
|
||||
|
||||
## Formatters / sorters
|
||||
|
||||
| Hook ID | What it does | Key args |
|
||||
|---------|-------------|----------|
|
||||
| `pretty-format-json` | Formats JSON; fails if not already formatted | `--autofix` (fix in place), `--indent N`, `--no-sort-keys`, `--no-ensure-ascii`, `--top-keys k1,k2` |
|
||||
| `requirements-txt-fixer` | Sorts and deduplicates `requirements.txt` and `constraints.txt` | — |
|
||||
| `file-contents-sorter` | Sorts lines in user-specified files alphabetically | `--ignore-case`, `--unique`; no files matched by default — must set `files:` |
|
||||
| `sort-simple-yaml` | Sorts top-level keys in simple YAML files | No files matched by default — must set `files:` |
|
||||
|
||||
---
|
||||
|
||||
## Python-specific
|
||||
|
||||
| Hook ID | What it does | Key args |
|
||||
|---------|-------------|----------|
|
||||
| `check-builtin-literals` | Requires literal syntax for empty built-ins (`[]` not `list()`) | `--ignore=type1,type2`, `--no-allow-dict-kwargs` |
|
||||
| `debug-statements` | Detects `import pdb`, `breakpoint()`, etc. | — |
|
||||
| `double-quote-string-fixer` | Converts double-quoted strings to single-quoted | — |
|
||||
| `name-tests-test` | Enforces test file naming convention | `--pytest` (default: `.*_test.py`), `--pytest-test-first` (`test_.*.py`), `--django`/`--unittest` |
|
||||
|
||||
---
|
||||
|
||||
## Deprecated hooks
|
||||
|
||||
| Hook ID | Replacement |
|
||||
|---------|------------|
|
||||
| `check-byte-order-marker` | Use `fix-byte-order-marker` |
|
||||
| `fix-encoding-pragma` | Use `pyupgrade` |
|
||||
| `check-docstring-first` | Deprecated without replacement |
|
||||
|
||||
---
|
||||
|
||||
## Argument examples
|
||||
|
||||
### `check-yaml` with Kubernetes manifests
|
||||
|
||||
```yaml
|
||||
- id: check-yaml
|
||||
args: ['--unsafe'] # needed for !Tag syntax used by k8s/Helm
|
||||
```
|
||||
|
||||
### `no-commit-to-branch`
|
||||
|
||||
```yaml
|
||||
- id: no-commit-to-branch
|
||||
args: [--branch, main, --branch, master, --branch, production]
|
||||
```
|
||||
|
||||
### `pretty-format-json` auto-fix
|
||||
|
||||
```yaml
|
||||
- id: pretty-format-json
|
||||
args: [--autofix, --indent, '2', --no-sort-keys]
|
||||
```
|
||||
|
||||
### `sort-simple-yaml` opt-in (no files matched by default)
|
||||
|
||||
```yaml
|
||||
- id: sort-simple-yaml
|
||||
files: ^config/simple/
|
||||
```
|
||||
|
||||
### `trailing-whitespace` preserving Markdown line breaks
|
||||
|
||||
```yaml
|
||||
- id: trailing-whitespace
|
||||
args: ['--markdown-linebreak-ext=md']
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Hooks in this repo (`.pre-commit-config.yaml`)
|
||||
|
||||
From `pre-commit/pre-commit-hooks@v4.5.0` (latest is `v6.0.0`):
|
||||
|
||||
| Hook | Stage | Notes |
|
||||
|------|-------|-------|
|
||||
| `end-of-file-fixer` | pre-commit | |
|
||||
| `check-json` | pre-commit | |
|
||||
| `pretty-format-json` | pre-commit | No `--autofix` — fails on unformatted JSON, does not fix |
|
||||
| `check-yaml` | pre-commit | |
|
||||
| `trailing-whitespace` | pre-commit | |
|
||||
|
||||
Local hooks in this repo:
|
||||
|
||||
| Hook | Stage | Entry |
|
||||
|------|-------|-------|
|
||||
| `run-tests` | pre-push | `bash tests/run-tests.sh` |
|
||||
| `check-manifests` | pre-push | `bash scripts/check-manifests.sh` |
|
||||
| `validate-plugins` | pre-push | `claude plugin validate --strict` per plugin dir |
|
||||
| `validate-marketplace` | pre-push | `claude plugin validate --strict .claude-plugin/marketplace.json` |
|
||||
| `skill-frontmatter` | pre-commit | Validates SKILL.md has `name:` and `description:` |
|
||||
49
plugins/kyberforge/docs/research/docs/pre-commit/overview.md
Normal file
49
plugins/kyberforge/docs/research/docs/pre-commit/overview.md
Normal file
@@ -0,0 +1,49 @@
|
||||
---
|
||||
topic: overview
|
||||
source_keys:
|
||||
- context7-pre-commit-com
|
||||
- pre-commit-com
|
||||
---
|
||||
|
||||
## What pre-commit is
|
||||
|
||||
Pre-commit is a framework for managing and executing git hooks. Hooks run automatically at specific git lifecycle points (pre-commit, pre-push, commit-msg, etc.) against staged or changed files. Each hook is pulled from a versioned external git repo; pre-commit clones and caches it in `~/.cache/pre-commit` (or `$PRE_COMMIT_HOME`) and manages isolated execution environments per hook. No system-wide language runtimes are required for most hooks.
|
||||
|
||||
## Key concepts
|
||||
|
||||
**Config file:** `.pre-commit-config.yaml` at the repo root. This is the single source of truth for all hooks.
|
||||
|
||||
**Hook repos vs local hooks:** Most hooks live in external git repos (pinned by `rev`). Local hooks (`repo: local`) live in the same repo and run system tools or scripts directly.
|
||||
|
||||
**`rev` must be immutable:** Always use a tag or commit SHA, never a branch. `autoupdate` will not work correctly with branches.
|
||||
|
||||
**File targeting:** Hooks receive only the files that match their `files` regex AND `types` filter. `pre-commit run --all-files` bypasses staging and runs against all files in the tree.
|
||||
|
||||
**Staged files only by default:** When run via git hooks, pre-commit passes only staged files. This means fixers (e.g. `trailing-whitespace`) modify files but the commit is blocked — the user must re-stage and recommit.
|
||||
|
||||
**`PRE_COMMIT=1`** is set in the environment whenever a hook is executing (since v2.5.0). Hooks can use this to detect they are running under pre-commit.
|
||||
|
||||
**`identify` library** determines file types. Inspect what tags a file has with `identify-cli <file>`. Types include: `file`, `text`, `binary`, `executable`, `python`, `json`, `yaml`, `shell`, `javascript`, `typescript`, `markdown`, `toml`, `xml`, etc.
|
||||
|
||||
## Mental model for a skill/agent
|
||||
|
||||
A pre-commit agent operates on three artefacts:
|
||||
|
||||
1. `.pre-commit-config.yaml` — the config it creates or modifies
|
||||
2. `.pre-commit-hooks.yaml` — a hook manifest if the agent is also authoring hooks in the repo
|
||||
3. The git hooks in `.git/hooks/` — installed by `pre-commit install`
|
||||
|
||||
The safe workflow for a skill:
|
||||
1. Write or modify `.pre-commit-config.yaml`
|
||||
2. Run `pre-commit validate-config` — abort if non-zero
|
||||
3. Run `pre-commit run --all-files` — surface hook failures
|
||||
4. Run `pre-commit autoupdate` if updating `rev` values
|
||||
5. Run `pre-commit install` once to wire hooks into git
|
||||
|
||||
## Cache and environment
|
||||
|
||||
Default cache: `~/.cache/pre-commit` or `$XDG_CACHE_HOME/pre-commit`.
|
||||
Override: `export PRE_COMMIT_HOME=/path/to/cache`.
|
||||
Pre-create all environments: `pre-commit install-hooks`.
|
||||
Wipe and rebuild: `pre-commit clean`.
|
||||
Remove unused only: `pre-commit gc`.
|
||||
29
plugins/kyberforge/docs/research/docs/pre-commit/sources.md
Normal file
29
plugins/kyberforge/docs/research/docs/pre-commit/sources.md
Normal file
@@ -0,0 +1,29 @@
|
||||
# Sources
|
||||
|
||||
## context7-pre-commit-com
|
||||
|
||||
- **URL:** context7:/pre-commit/pre-commit.com
|
||||
- **Description:** Official pre-commit.com documentation — installation, configuration schema, CLI reference, hook authoring, advanced features, troubleshooting
|
||||
- **Contributing files:** overview.md, configuration.md, cli-reference.md, hook-authoring.md, examples.md, troubleshooting.md
|
||||
- **Status:** `extracted`
|
||||
|
||||
## context7-pre-commit-hooks
|
||||
|
||||
- **URL:** context7:/pre-commit/pre-commit-hooks
|
||||
- **Description:** Official pre-commit-hooks collection — all available hook IDs with options and examples
|
||||
- **Contributing files:** hooks-reference.md, examples.md
|
||||
- **Status:** `extracted`
|
||||
|
||||
## pre-commit-com
|
||||
|
||||
- **URL:** https://pre-commit.com/
|
||||
- **Description:** Pre-commit framework homepage — full docs covering install, config, CLI, hook authoring, stages, local hooks, meta hooks, hazmat helpers, CI integration
|
||||
- **Contributing files:** overview.md, configuration.md, cli-reference.md, hook-authoring.md, examples.md, troubleshooting.md
|
||||
- **Status:** `extracted`
|
||||
|
||||
## pre-commit-hooks-github
|
||||
|
||||
- **URL:** https://raw.githubusercontent.com/pre-commit/pre-commit-hooks/main/README.md
|
||||
- **Description:** Official pre-commit-hooks README — complete hook listing with all args, categories, deprecated hooks, and latest version (v6.0.0)
|
||||
- **Contributing files:** hooks-reference.md
|
||||
- **Status:** `extracted`
|
||||
@@ -0,0 +1,110 @@
|
||||
---
|
||||
topic: troubleshooting
|
||||
source_keys:
|
||||
- context7-pre-commit-com
|
||||
- pre-commit-com
|
||||
---
|
||||
|
||||
## Common issues
|
||||
|
||||
### Hooks don't run on `git commit`
|
||||
|
||||
`pre-commit install` was never run in this clone. Run it. Git hooks are per-clone — they are not committed.
|
||||
|
||||
### Hook modified files but commit was blocked
|
||||
|
||||
Expected behavior. The hook fixed files, so the staged version is now stale. Re-stage the modified files and commit again.
|
||||
|
||||
```bash
|
||||
git add -u
|
||||
git commit -m "same message"
|
||||
```
|
||||
|
||||
### `SKIP` not working
|
||||
|
||||
The value must be the exact `id` field from the hook definition. Comma-separated, no spaces.
|
||||
|
||||
```bash
|
||||
SKIP=check-yaml,trailing-whitespace git commit -m "msg" # correct
|
||||
SKIP=check-yaml, trailing-whitespace git commit -m "msg" # wrong — space after comma
|
||||
```
|
||||
|
||||
### Hook runs but matches wrong files (or no files)
|
||||
|
||||
`files:` uses `re.search()`, not a full-string match. `\.py$` matches any path ending in `.py`. To match only repo root: `^[^/]+\.py$`.
|
||||
|
||||
Use `identify-cli <filename>` to see exactly what type tags a file has, then verify your `types:` filter.
|
||||
|
||||
### Hook environment is stale or broken
|
||||
|
||||
```bash
|
||||
pre-commit clean # wipe all environments
|
||||
pre-commit install-hooks # rebuild everything
|
||||
```
|
||||
|
||||
Or clean just a specific repo:
|
||||
```bash
|
||||
pre-commit gc # remove only unused environments
|
||||
```
|
||||
|
||||
### `rev` is a branch name — `autoupdate` broke it
|
||||
|
||||
Branch refs are mutable; pre-commit resolves them at install time and then they drift. Always use a tag or commit SHA. Fix:
|
||||
|
||||
```bash
|
||||
pre-commit autoupdate # finds the latest tag and rewrites rev in place
|
||||
```
|
||||
|
||||
### `validate-config` returns an error
|
||||
|
||||
Schema violation in the YAML. Common causes:
|
||||
- Missing `id` under a hook block
|
||||
- Missing `rev` under a non-local repo block
|
||||
- `repo: local` hook missing required `language` or `entry` fields
|
||||
- Indentation error (YAML parsed but pre-commit schema rejected it)
|
||||
|
||||
### `check-hooks-apply` fails
|
||||
|
||||
A hook's `files`/`types` filter matches zero files in the repo. Either broaden the filter or remove the hook. This is a sign the hook is dead weight.
|
||||
|
||||
### `check-useless-excludes` fails
|
||||
|
||||
An `exclude` pattern matches no files. Remove or fix it.
|
||||
|
||||
### SSH cloning fails in CI
|
||||
|
||||
Export `SSH_AUTH_SOCK` in the CI environment, or use HTTPS URLs for hook repos.
|
||||
|
||||
### HTTP proxy needed
|
||||
|
||||
```bash
|
||||
export http_proxy=http://proxy.example.com:3128
|
||||
export https_proxy=http://proxy.example.com:3128
|
||||
export no_proxy=localhost,127.0.0.1
|
||||
```
|
||||
|
||||
### Hook too slow in CI
|
||||
|
||||
- Check `require_serial: false` (default) — hooks run in parallel by default
|
||||
- Cache `$PRE_COMMIT_HOME` keyed on the hash of `.pre-commit-config.yaml`
|
||||
- Use `--from-ref`/`--to-ref` instead of `--all-files` to only check changed files
|
||||
|
||||
### `language: system` deprecated warning
|
||||
|
||||
Renamed to `language: unsupported`. Old name still works as an alias but triggers a deprecation warning on newer versions.
|
||||
|
||||
### `pre-commit install -f` wiped my existing hooks
|
||||
|
||||
`-f` overwrites `.git/hooks/pre-commit` unconditionally. Without `-f`, pre-commit migrates the existing hook so both run. Only use `-f` deliberately.
|
||||
|
||||
### `pretty-format-json` fails but doesn't fix
|
||||
|
||||
`pretty-format-json` only fixes in-place when `args: [--autofix]` is passed. Without it, the hook just fails. Add `--autofix` to have it modify the file (the commit will then be blocked until you re-stage).
|
||||
|
||||
## Skill/agent-specific gotchas
|
||||
|
||||
- Always call `pre-commit validate-config` after writing or modifying config — do not assume valid YAML is valid pre-commit schema.
|
||||
- `pre-commit autoupdate` modifies the config file in-place. If a skill calls it, re-read the file to get updated `rev` values for display/logging.
|
||||
- Local hooks with `language: unsupported_script` require the `entry` script to be executable. If the skill creates the script, `chmod +x` it.
|
||||
- The `stages` key in a hook override must match what was set in `default_install_hook_types` (or the `-t` flags passed to `pre-commit install`), otherwise the hook will never run.
|
||||
- `always_run: true` combined with `pass_filenames: false` is the correct pattern for repo-wide validators (test suites, manifest checks) that do not operate on individual files.
|
||||
Reference in New Issue
Block a user