From 9f94164177fe5ee1f01f34870140374b664550e8 Mon Sep 17 00:00:00 2001 From: Defame1297 Date: Sat, 27 Jun 2026 21:57:34 +0000 Subject: [PATCH] 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 Claude-Session: https://claude.ai/code/session_01P87CiC58Ru2PPWYTeXtjHT --- .../research/docs/pre-commit/cli-reference.md | 152 ++++++++++++ .../research/docs/pre-commit/configuration.md | 206 ++++++++++++++++ .../docs/research/docs/pre-commit/examples.md | 220 ++++++++++++++++++ .../docs/pre-commit/hook-authoring.md | 156 +++++++++++++ .../docs/pre-commit/hooks-reference.md | 174 ++++++++++++++ .../docs/research/docs/pre-commit/overview.md | 49 ++++ .../docs/research/docs/pre-commit/sources.md | 29 +++ .../docs/pre-commit/troubleshooting.md | 110 +++++++++ 8 files changed, 1096 insertions(+) create mode 100644 plugins/kyberforge/docs/research/docs/pre-commit/cli-reference.md create mode 100644 plugins/kyberforge/docs/research/docs/pre-commit/configuration.md create mode 100644 plugins/kyberforge/docs/research/docs/pre-commit/examples.md create mode 100644 plugins/kyberforge/docs/research/docs/pre-commit/hook-authoring.md create mode 100644 plugins/kyberforge/docs/research/docs/pre-commit/hooks-reference.md create mode 100644 plugins/kyberforge/docs/research/docs/pre-commit/overview.md create mode 100644 plugins/kyberforge/docs/research/docs/pre-commit/sources.md create mode 100644 plugins/kyberforge/docs/research/docs/pre-commit/troubleshooting.md diff --git a/plugins/kyberforge/docs/research/docs/pre-commit/cli-reference.md b/plugins/kyberforge/docs/research/docs/pre-commit/cli-reference.md new file mode 100644 index 0000000..0003f5d --- /dev/null +++ b/plugins/kyberforge/docs/research/docs/pre-commit/cli-reference.md @@ -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 +``` diff --git a/plugins/kyberforge/docs/research/docs/pre-commit/configuration.md b/plugins/kyberforge/docs/research/docs/pre-commit/configuration.md new file mode 100644 index 0000000..d000b44 --- /dev/null +++ b/plugins/kyberforge/docs/research/docs/pre-commit/configuration.md @@ -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 ` | +| `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 `. + +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 ` 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 -- +``` diff --git a/plugins/kyberforge/docs/research/docs/pre-commit/examples.md b/plugins/kyberforge/docs/research/docs/pre-commit/examples.md new file mode 100644 index 0000000..db731e4 --- /dev/null +++ b/plugins/kyberforge/docs/research/docs/pre-commit/examples.md @@ -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 +``` diff --git a/plugins/kyberforge/docs/research/docs/pre-commit/hook-authoring.md b/plugins/kyberforge/docs/research/docs/pre-commit/hook-authoring.md new file mode 100644 index 0000000..ef1a0a1 --- /dev/null +++ b/plugins/kyberforge/docs/research/docs/pre-commit/hook-authoring.md @@ -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 ... +``` + +When `pass_filenames: false`, pre-commit calls: +``` +entry +``` + +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,