diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 487ea65b..fffc07db 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -107,7 +107,7 @@ jobs:
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: "commit-check_docs"
- path: ${{ github.workspace }}/_build/html
+ path: ${{ github.workspace }}/site
- name: Upload docs to github pages
# only publish doc changes from main branch
@@ -115,4 +115,7 @@ jobs:
uses: peaceiris/actions-gh-pages@84c30a85c19949d7eee79c4ff27748b70285e453 # v4.1.0
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
- publish_dir: ./_build/html
+ publish_dir: ./site
+ # Pinned explicitly so the custom domain cannot be lost when the
+ # publish branch is replaced.
+ cname: docs.commit-check.com
diff --git a/.gitignore b/.gitignore
index 3dfd5ade..546ac132 100644
--- a/.gitignore
+++ b/.gitignore
@@ -19,3 +19,7 @@ _build/
docs/_build
docs/cli_args.rst
docs/__pycache__
+
+# MkDocs
+site/
+docs/cli.md
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index efb135f6..50a3a9c8 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -12,6 +12,11 @@ repos:
rev: v6.0.0
hooks:
- id: check-yaml
+ # mkdocs.yml carries the `!!python/name:` tags that Material's emoji
+ # extension requires, which yaml.safe_load cannot construct. MkDocs
+ # validates the file itself on every build (`mkdocs build --strict`),
+ # so it is checked more thoroughly than this hook would manage.
+ exclude: ^mkdocs\.yml$
- id: check-toml
- id: end-of-file-fixer
- id: trailing-whitespace
diff --git a/commit_check/rules_catalog.py b/commit_check/rules_catalog.py
index 71d4eb05..a912cc1c 100644
--- a/commit_check/rules_catalog.py
+++ b/commit_check/rules_catalog.py
@@ -21,7 +21,7 @@
from dataclasses import dataclass
#: Base URL of the rules reference documentation.
-RULES_DOCS_URL = "https://docs.commit-check.com/rules.html"
+RULES_DOCS_URL = "https://docs.commit-check.com/rules/"
@dataclass(frozen=True)
diff --git a/docs/README.rst b/docs/README.rst
deleted file mode 100644
index d2964103..00000000
--- a/docs/README.rst
+++ /dev/null
@@ -1,21 +0,0 @@
-:orphan:
-
-How to build the docs
-=====================
-
-From the root directory of the repository, do the following to steps
-
-1. Install docs' dependencies
-
- .. code-block:: text
-
- pip install nox
-
-2. Build the docs
-
- .. code-block:: text
-
- nox -s docs
-
- Browse the files in /_build/html with your internet browser to see the rendered
- output.
diff --git a/docs/_static/extra_css.css b/docs/_static/extra_css.css
deleted file mode 100644
index cc322201..00000000
--- a/docs/_static/extra_css.css
+++ /dev/null
@@ -1,71 +0,0 @@
-tbody .stub,
-thead {
- background-color: var(--md-accent-bg-color--light);
- color: var(--md-default-bg-color);
-}
-
-.md-header,
-.md-nav--primary .md-nav__title[for="__drawer"] {
- background-color: #2c9ccd;
-}
-
-/* Sidebar section headings ("Getting started", "Reference", ...) */
-.md-nav__item--section > .md-nav__link {
- font-weight: 700;
- color: var(--md-default-fg-color);
-}
-
-/* Fix table header visibility for both light and dark modes */
-.md-content table th {
- color: var(--md-typeset-color) !important;
-}
-
-/* Custom color scheme to match logo */
-:root {
- --md-primary-fg-color: #2c9ccd;
- --md-primary-fg-color--light: #5bb3d9;
- --md-primary-fg-color--dark: #1e85a8;
-}
-
-/* Navigation and links */
-.md-nav__link--active,
-.md-nav__link:hover {
- color: #2c9ccd;
-}
-
-/* Buttons and accent elements */
-.md-button--primary {
- background-color: #2c9ccd;
- border-color: #2c9ccd;
-}
-
-.md-button--primary:hover {
- background-color: #1e85a8;
- border-color: #1e85a8;
-}
-
-/* Code blocks and syntax highlighting accents */
-.md-typeset .codehilite .hll,
-.md-typeset .highlight .hll {
- background-color: rgba(44, 156, 205, 0.1);
-}
-
-/* Admonition titles with your brand color */
-.md-typeset .admonition.note > .admonition-title,
-.md-typeset .admonition.tip > .admonition-title {
- background-color: rgba(44, 156, 205, 0.1);
- border-color: #2c9ccd;
-}
-
-/* Align table cells to the top so short cells line up with a wrapped message. */
-.md-typeset table td,
-.md-typeset table th {
- vertical-align: top;
-}
-
-/* In the rule index tables only, keep rule codes, names, and CLI flags on one
- line so the tables stay scannable. Other tables (such as the configuration
- options table) hold long regexes and lists that must stay wrappable. */
-.md-typeset table.rules-index td code {
- white-space: nowrap;
-}
diff --git a/docs/_static/logo.jpg b/docs/_static/logo.jpg
deleted file mode 100644
index ed4cd723..00000000
Binary files a/docs/_static/logo.jpg and /dev/null differ
diff --git a/docs/assets/extra.css b/docs/assets/extra.css
new file mode 100644
index 00000000..d0ef3cca
--- /dev/null
+++ b/docs/assets/extra.css
@@ -0,0 +1,89 @@
+/* Brand palette -----------------------------------------------------------
+ Material's "custom" primary/accent read these variables. */
+:root {
+ --cc-brand: #2c9ccd;
+ --cc-brand-dark: #1e85a8;
+ --cc-brand-light: #5bb3d9;
+
+ --md-primary-fg-color: var(--cc-brand);
+ --md-primary-fg-color--light: var(--cc-brand-light);
+ --md-primary-fg-color--dark: var(--cc-brand-dark);
+ --md-accent-fg-color: var(--cc-brand-dark);
+}
+
+[data-md-color-scheme="slate"] {
+ --md-primary-fg-color: var(--cc-brand);
+ --md-accent-fg-color: var(--cc-brand-light);
+ --md-typeset-a-color: var(--cc-brand-light);
+}
+
+[data-md-color-scheme="default"] {
+ --md-typeset-a-color: var(--cc-brand-dark);
+}
+
+/* Landing page -------------------------------------------------------------
+ The hero is the first thing a visitor sees, so it carries the value
+ proposition rather than a badge wall. */
+.cc-hero {
+ text-align: center;
+ padding: 3.5rem 1rem 2.5rem;
+}
+
+.cc-hero h1 {
+ font-size: 2.9rem;
+ line-height: 1.15;
+ font-weight: 800;
+ letter-spacing: -0.02em;
+ margin: 0 0 0.6rem;
+}
+
+.cc-hero p {
+ font-size: 1.05rem;
+ max-width: 40rem;
+ margin: 0 auto 1.8rem;
+ color: var(--md-default-fg-color--light);
+}
+
+.cc-hero .md-button {
+ margin: 0.3rem;
+}
+
+/* Hide the redundant H1 that MkDocs renders above a custom hero. */
+.cc-hero + hr {
+ display: none;
+}
+
+/* Feature grid: uses Material's card grid, tightened up a little. */
+.md-typeset .grid.cards > ul > li {
+ border-radius: 0.4rem;
+ transition: border-color 125ms, box-shadow 125ms;
+}
+
+.md-typeset .grid.cards > ul > li:hover {
+ border-color: var(--cc-brand);
+ box-shadow: 0 0 0 1px var(--cc-brand);
+}
+
+.md-typeset .grid.cards > ul > li > hr {
+ margin: 0.6rem 0;
+}
+
+/* Tables -------------------------------------------------------------------
+ Cells align to the top so short values line up with a wrapped message. */
+.md-typeset table:not([class]) td,
+.md-typeset table:not([class]) th {
+ vertical-align: top;
+}
+
+/* In the rule index tables only, keep codes, names and CLI flags on one line
+ so the tables stay scannable. Other tables hold long regexes and lists that
+ must stay wrappable. */
+.md-typeset table.rules-index td code {
+ white-space: nowrap;
+}
+
+/* Sidebar section headings ("Getting started", "Reference", ...) */
+.md-nav__item--section > .md-nav__link {
+ font-weight: 700;
+ color: var(--md-default-fg-color);
+}
diff --git a/docs/assets/favicon.svg b/docs/assets/favicon.svg
new file mode 100644
index 00000000..896e52aa
--- /dev/null
+++ b/docs/assets/favicon.svg
@@ -0,0 +1,8 @@
+
diff --git a/docs/assets/logo.svg b/docs/assets/logo.svg
new file mode 100644
index 00000000..3aee9298
--- /dev/null
+++ b/docs/assets/logo.svg
@@ -0,0 +1,8 @@
+
diff --git a/docs/changelog.md b/docs/changelog.md
new file mode 100644
index 00000000..f1fccb02
--- /dev/null
+++ b/docs/changelog.md
@@ -0,0 +1,187 @@
+# Changelog
+
+All **notable changes** to this project will be documented in this file.
+
+Full changelog available at [GitHub releases](https://github.com/commit-check/commit-check/releases).
+
+## v2.11.0 (2026-07-06)
+
+### New Features
+
+* **AI attribution governance** — Added support for forbidding known AI tool
+ signatures (e.g., `Co-authored-by: Copilot`) in commit messages. New
+ `[commit]` config option `forbid_ai_attribution` (boolean, default
+ `false`) rejects commits co-authored by AI coding agents. See PR [#456](https://github.com/commit-check/commit-check/pull/456).
+
+### Bug Fixes
+
+* Fixed `MergeBaseValidator` branch detection — replaced `git branch -a`
+ regex matching with `git rev-parse --verify` to avoid false positives
+ (e.g., pattern `main` matching `main-staging`). See PR [#451](https://github.com/commit-check/commit-check/pull/451).
+
+### Chores
+
+* Added OpenSSF Scorecard workflow, badge, and pinned dependency SHAs for CI
+* Migrated PyPI publishing to `pypa/gh-action-pypi-publish`
+* Removed OpenSSF Scorecard badge after evaluation (moved to Scorecard dashboard)
+
+## v2.10.1 (2026-06-30)
+
+### Bug Fixes
+
+* **WIP detection case-insensitivity** — `WIP` (`[WIP]`, `WIP:`, `wip:`,
+ etc.) is now recognized regardless of case across all common patterns.
+ See PR [#448](https://github.com/commit-check/commit-check/pull/448).
+* **Conventional commit special characters** — Allowed special characters
+ (parentheses, brackets, etc.) in the description part of conventional commit
+ messages. See PR [#447](https://github.com/commit-check/commit-check/pull/447).
+
+### Refactors
+
+* Extracted `_get_commit_message` to `BaseValidator` to remove code
+ duplication across validators. See PR [#445](https://github.com/commit-check/commit-check/pull/445).
+* Removed legacy YAML config parsing code from `util.py`.
+ See PR [#444](https://github.com/commit-check/commit-check/pull/444).
+
+## v2.10.0 (2026-06-26)
+
+### New Features
+
+* **Dependabot / Renovate as default branch type** — `dependabot/` and
+ `renovate/` branch prefixes are now included in `DEFAULT_BRANCH_TYPES`,
+ so dependency update branches are automatically recognized.
+ See PR [#442](https://github.com/commit-check/commit-check/pull/442).
+
+## v2.9.0 (2026-06-22)
+
+### New Features
+
+* **AI agent branch prefixes (Conventional Branch v1.1.0)** — Added
+ `ai/`, `claude/`, `codex/`, `copilot/`, and `cursor/` to
+ `DEFAULT_BRANCH_TYPES` so branches created by AI coding agents are
+ recognized as valid. See PR [#438](https://github.com/commit-check/commit-check/pull/438).
+
+## v2.8.1 (2026-06-22)
+
+### Chores
+
+* Fixed 27 SonarQube code-quality issues across source and test files,
+ including path traversal vulnerability fix, cognitive complexity
+ reduction, and duplicate branch consolidation. See PR [#436](https://github.com/commit-check/commit-check/pull/436).
+* Added SchemaStore IDE autocompletion support for `cchk.toml`.
+ See PR [#433](https://github.com/commit-check/commit-check/pull/433).
+
+## v2.8.0 (2026-06-13)
+
+### New Features
+
+* **Custom commit message pattern** — New `message_pattern` option in the
+ `[commit]` config section allows replacing the built-in Conventional Commits
+ regex with a user-defined regex pattern. Also supported via the
+ `CCHK_MESSAGE_PATTERN` environment variable. See PR [#427](https://github.com/commit-check/commit-check/pull/427).
+
+### Breaking Changes
+
+* **Dropped Python 3.9 support** — Minimum required Python version is now
+ 3.10. Type annotations have been modernized (PEP 604/585) and the
+ `py.typed` marker added for downstream type checkers.
+ See PR [#424](https://github.com/commit-check/commit-check/pull/424).
+
+## v2.7.1 (2026-06-08)
+
+### Chores
+
+* Added `auto` to the list of imperative verbs. See PR [#417](https://github.com/commit-check/commit-check/pull/417).
+* Added commit-check vs GitHub Rulesets comparison table to the README.
+ See PR [#419](https://github.com/commit-check/commit-check/pull/419).
+
+## v2.7.0 (2026-05-16)
+
+### New Features
+
+* **Force push detection and blocking** — Added `--no-force-push` CLI flag and
+ `check-no-force-push` pre-push hook that inspect pushed ref ancestry via
+ `git merge-base --is-ancestor` to detect and block `git push --force` and
+ `git push -f`. A new `[push]` TOML config section with
+ `allow_force_push` (default `true`) controls the behavior. Environment
+ variable `CCHK_ALLOW_FORCE_PUSH` is also supported.
+
+* **`validate_push()` API** — New `commit_check.api.validate_push()`
+ function for programmatic push safety checks, matching the `--no-force-push`
+ CLI behavior without spawning a subprocess.
+
+* **Standalone mode** — When `--no-force-push` is run outside a pre-push hook
+ (no stdin), it checks whether pushing `HEAD` to its configured upstream
+ would require force, using `git ls-remote` and optional `git fetch` to
+ resolve the remote commit.
+
+* **Expanded imperative verbs** — Added 156 new imperative verbs across 10
+ categories (auth/security, data ops, lifecycle, I/O, debugging, UI/UX,
+ engineering, general), growing the total from 234 to 390.
+ See PR [#414](https://github.com/commit-check/commit-check/pull/414).
+
+## v2.6.0 (2026-04-20)
+
+### New Features
+
+* **Lower-noise CLI failure output** — Added `--no-banner` to suppress the ASCII art header while preserving detailed errors and suggestions.
+* **Compact failure mode** — Added `--compact` to print one `[FAIL]` line per failing check for CI logs and automation-friendly terminal output. This mode also suppresses the banner.
+
+### Bug Fixes
+
+* Fixed `print_error_header` state handling so repeated validations stay consistent when `--compact` is used.
+
+## v2.5.0 (2026-04-03)
+
+### New Features
+
+* **Co-author bypass in `ignore_authors`** — `_should_skip_commit_validation()` now parses `Co-authored-by:` trailers in the commit message body. If any co-author name matches `ignore_authors`, all commit checks are skipped. Useful for AI bots that co-author commits (e.g., `coderabbitai[bot]`).
+* **Organization-level config inheritance via `inherit_from`** — New top-level TOML key that loads a parent config from a GitHub shorthand (`github:owner/repo:path`), a local file path, or an HTTPS URL, then deep-merges it with local settings. HTTP (non-TLS) URLs are rejected to prevent MITM attacks.
+* **Git config author validation** — `AuthorValidator` now checks `git config user.name` / `user.email` first (the identity used for the *next* commit), falling back to `git log` if unset. Previously, a misconfigured identity would pass if the last commit had a valid author.
+
+### Bug Fixes
+
+* Fixed incorrect mock target in `test_main_with_message_empty_string_no_stdin_with_git`: was patching `commit_check.util.get_commit_info` (ineffective) instead of `commit_check.engine.get_commit_info`.
+
+## v2.0.0 (2025-10-01)
+
+.. Attention::
+ This major release introduces significant architectural changes and breaking updates to commit-check. Please review carefully before upgrading.
+
+### What's New
+
+* **TOML Configuration** — Replaces the old `.commit-check.yml` with `cchk.toml` or `commit-check.toml` for clearer syntax.
+* **Simplified CLI & Hooks** — Legacy pre-commit hooks and command-line options have been removed for a cleaner, more consistent interface.
+* **New Validation Engine** — The validation system has been completely redesigned around a new ValidationEngine to improve maintainability and flexibility.
+
+#### Breaking Changes
+
+Configuration Format:
+
+* `.commit-check.yml` has been replaced with `cchk.toml` or `commit-check.toml`.
+* All YAML configurations must be migrated to TOML from this version onward.
+* See the [Migration Guide](migration.md) for step-by-step instructions.
+
+Removed Pre-commit Hooks and CLI Options:
+
+* Several legacy hooks and command-line flags have been removed in favor of a simplified interface.
+* Removed hooks: `check-commit-signoff`, `check-merge-base`, `check-imperative`.
+* Removed CLI options: `--signoff`, `--merge-base`, `--imperative`.
+
+Module Removal:
+
+* The following legacy modules have been removed: `author.py`, `branch.py`, `commit.py`, `error.py`.
+
+Architecture Redesign:
+
+* The validation system has been completely restructured around the new `ValidationEngine`, breaking compatibility with any code or integrations relying on the old module structure.
+
+See PR [#280](https://github.com/commit-check/commit-check/pull/280)
+
+## v0.10.2 (2025-08-26)
+
+Last release before the big v2.0 changes.
+
+## v0.1.0 (2022-11-02)
+
+Initial release of commit-check.
diff --git a/docs/changelog.rst b/docs/changelog.rst
deleted file mode 100644
index a64330e8..00000000
--- a/docs/changelog.rst
+++ /dev/null
@@ -1,227 +0,0 @@
-Changelog
-=========
-
-All **notable changes** to this project will be documented in this file.
-
-Full changelog available at `GitHub releases `_.
-
-v2.11.0 (2026-07-06)
---------------------
-
-New Features
-~~~~~~~~~~~~
-
-* **AI attribution governance** — Added support for forbidding known AI tool
- signatures (e.g., ``Co-authored-by: Copilot``) in commit messages. New
- ``[commit]`` config option ``forbid_ai_attribution`` (boolean, default
- ``false``) rejects commits co-authored by AI coding agents. See PR :pr:`456`.
-
-Bug Fixes
-~~~~~~~~~
-
-* Fixed ``MergeBaseValidator`` branch detection — replaced ``git branch -a``
- regex matching with ``git rev-parse --verify`` to avoid false positives
- (e.g., pattern ``main`` matching ``main-staging``). See PR :pr:`451`.
-
-Chores
-~~~~~~
-
-* Added OpenSSF Scorecard workflow, badge, and pinned dependency SHAs for CI
-* Migrated PyPI publishing to ``pypa/gh-action-pypi-publish``
-* Removed OpenSSF Scorecard badge after evaluation (moved to Scorecard dashboard)
-
-
-v2.10.1 (2026-06-30)
---------------------
-
-Bug Fixes
-~~~~~~~~~
-
-* **WIP detection case-insensitivity** — ``WIP`` (``[WIP]``, ``WIP:``, ``wip:``,
- etc.) is now recognized regardless of case across all common patterns.
- See PR :pr:`448`.
-* **Conventional commit special characters** — Allowed special characters
- (parentheses, brackets, etc.) in the description part of conventional commit
- messages. See PR :pr:`447`.
-
-Refactors
-~~~~~~~~~
-
-* Extracted ``_get_commit_message`` to ``BaseValidator`` to remove code
- duplication across validators. See PR :pr:`445`.
-* Removed legacy YAML config parsing code from ``util.py``.
- See PR :pr:`444`.
-
-
-v2.10.0 (2026-06-26)
---------------------
-
-New Features
-~~~~~~~~~~~~
-
-* **Dependabot / Renovate as default branch type** — ``dependabot/`` and
- ``renovate/`` branch prefixes are now included in ``DEFAULT_BRANCH_TYPES``,
- so dependency update branches are automatically recognized.
- See PR :pr:`442`.
-
-
-v2.9.0 (2026-06-22)
--------------------
-
-New Features
-~~~~~~~~~~~~
-
-* **AI agent branch prefixes (Conventional Branch v1.1.0)** — Added
- ``ai/``, ``claude/``, ``codex/``, ``copilot/``, and ``cursor/`` to
- ``DEFAULT_BRANCH_TYPES`` so branches created by AI coding agents are
- recognized as valid. See PR :pr:`438`.
-
-
-v2.8.1 (2026-06-22)
--------------------
-
-Chores
-~~~~~~
-
-* Fixed 27 SonarQube code-quality issues across source and test files,
- including path traversal vulnerability fix, cognitive complexity
- reduction, and duplicate branch consolidation. See PR :pr:`436`.
-* Added SchemaStore IDE autocompletion support for ``cchk.toml``.
- See PR :pr:`433`.
-
-
-v2.8.0 (2026-06-13)
--------------------
-
-New Features
-~~~~~~~~~~~~
-
-* **Custom commit message pattern** — New ``message_pattern`` option in the
- ``[commit]`` config section allows replacing the built-in Conventional Commits
- regex with a user-defined regex pattern. Also supported via the
- ``CCHK_MESSAGE_PATTERN`` environment variable. See PR :pr:`427`.
-
-Breaking Changes
-~~~~~~~~~~~~~~~~
-
-* **Dropped Python 3.9 support** — Minimum required Python version is now
- 3.10. Type annotations have been modernized (PEP 604/585) and the
- ``py.typed`` marker added for downstream type checkers.
- See PR :pr:`424`.
-
-
-v2.7.1 (2026-06-08)
--------------------
-
-Chores
-~~~~~~
-
-* Added ``auto`` to the list of imperative verbs. See PR :pr:`417`.
-* Added commit-check vs GitHub Rulesets comparison table to the README.
- See PR :pr:`419`.
-
-
-v2.7.0 (2026-05-16)
--------------------
-
-New Features
-~~~~~~~~~~~~
-
-* **Force push detection and blocking** — Added ``--no-force-push`` CLI flag and
- ``check-no-force-push`` pre-push hook that inspect pushed ref ancestry via
- ``git merge-base --is-ancestor`` to detect and block ``git push --force`` and
- ``git push -f``. A new ``[push]`` TOML config section with
- ``allow_force_push`` (default ``true``) controls the behavior. Environment
- variable ``CCHK_ALLOW_FORCE_PUSH`` is also supported.
-
-* **``validate_push()`` API** — New ``commit_check.api.validate_push()``
- function for programmatic push safety checks, matching the ``--no-force-push``
- CLI behavior without spawning a subprocess.
-
-* **Standalone mode** — When ``--no-force-push`` is run outside a pre-push hook
- (no stdin), it checks whether pushing ``HEAD`` to its configured upstream
- would require force, using ``git ls-remote`` and optional ``git fetch`` to
- resolve the remote commit.
-
-* **Expanded imperative verbs** — Added 156 new imperative verbs across 10
- categories (auth/security, data ops, lifecycle, I/O, debugging, UI/UX,
- engineering, general), growing the total from 234 to 390.
- See PR :pr:`414`.
-
-
-v2.6.0 (2026-04-20)
--------------------
-
-New Features
-~~~~~~~~~~~~
-
-* **Lower-noise CLI failure output** — Added ``--no-banner`` to suppress the ASCII art header while preserving detailed errors and suggestions.
-* **Compact failure mode** — Added ``--compact`` to print one ``[FAIL]`` line per failing check for CI logs and automation-friendly terminal output. This mode also suppresses the banner.
-
-Bug Fixes
-~~~~~~~~~
-
-* Fixed ``print_error_header`` state handling so repeated validations stay consistent when ``--compact`` is used.
-
-v2.5.0 (2026-04-03)
--------------------
-
-New Features
-~~~~~~~~~~~~
-
-* **Co-author bypass in ``ignore_authors``** — ``_should_skip_commit_validation()`` now parses ``Co-authored-by:`` trailers in the commit message body. If any co-author name matches ``ignore_authors``, all commit checks are skipped. Useful for AI bots that co-author commits (e.g., ``coderabbitai[bot]``).
-* **Organization-level config inheritance via ``inherit_from``** — New top-level TOML key that loads a parent config from a GitHub shorthand (``github:owner/repo:path``), a local file path, or an HTTPS URL, then deep-merges it with local settings. HTTP (non-TLS) URLs are rejected to prevent MITM attacks.
-* **Git config author validation** — ``AuthorValidator`` now checks ``git config user.name`` / ``user.email`` first (the identity used for the *next* commit), falling back to ``git log`` if unset. Previously, a misconfigured identity would pass if the last commit had a valid author.
-
-Bug Fixes
-~~~~~~~~~
-
-* Fixed incorrect mock target in ``test_main_with_message_empty_string_no_stdin_with_git``: was patching ``commit_check.util.get_commit_info`` (ineffective) instead of ``commit_check.engine.get_commit_info``.
-
-v2.0.0 (2025-10-01)
--------------------
-
-.. Attention::
- This major release introduces significant architectural changes and breaking updates to commit-check. Please review carefully before upgrading.
-
-What's New
-~~~~~~~~~~
-
-* **TOML Configuration** — Replaces the old ``.commit-check.yml`` with ``cchk.toml`` or ``commit-check.toml`` for clearer syntax.
-* **Simplified CLI & Hooks** — Legacy pre-commit hooks and command-line options have been removed for a cleaner, more consistent interface.
-* **New Validation Engine** — The validation system has been completely redesigned around a new ValidationEngine to improve maintainability and flexibility.
-
-Breaking Changes
-^^^^^^^^^^^^^^^^
-
-Configuration Format:
-
-* ``.commit-check.yml`` has been replaced with ``cchk.toml`` or ``commit-check.toml``.
-* All YAML configurations must be migrated to TOML from this version onward.
-* See the `Migration Guide `_ for step-by-step instructions.
-
-Removed Pre-commit Hooks and CLI Options:
-
-* Several legacy hooks and command-line flags have been removed in favor of a simplified interface.
-* Removed hooks: ``check-commit-signoff``, ``check-merge-base``, ``check-imperative``.
-* Removed CLI options: ``--signoff``, ``--merge-base``, ``--imperative``.
-
-Module Removal:
-
-* The following legacy modules have been removed: ``author.py``, ``branch.py``, ``commit.py``, ``error.py``.
-
-Architecture Redesign:
-
-* The validation system has been completely restructured around the new ``ValidationEngine``, breaking compatibility with any code or integrations relying on the old module structure.
-
-See PR :pr:`280`
-
-v0.10.2 (2025-08-26)
---------------------
-
-Last release before the big v2.0 changes.
-
-v0.1.0 (2022-11-02)
--------------------
-
-Initial release of commit-check.
diff --git a/docs/conf.py b/docs/conf.py
deleted file mode 100644
index 405949dc..00000000
--- a/docs/conf.py
+++ /dev/null
@@ -1,161 +0,0 @@
-# pylint: disable=all
-# Configuration file for the Sphinx documentation builder.
-#
-# For the full list of built-in configuration values, see the documentation:
-# https://www.sphinx-doc.org/en/master/usage/configuration.html
-import re
-import datetime
-from pathlib import Path
-import subprocess
-from sphinx.application import Sphinx
-
-# -- Project information -----------------------------------------------------
-# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
-project = "commit-check"
-copyright = f"{datetime.date.today().year}, shenxianpeng"
-author = "shenxianpeng"
-
-# -- General configuration ---------------------------------------------------
-# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
-extensions = [
- "myst_parser",
- "sphinx_immaterial",
- "sphinx.ext.autodoc",
- "sphinx.ext.intersphinx",
- "sphinx.ext.viewcode",
- "sphinx_issues",
-]
-
-source_suffix = {
- ".rst": "restructuredtext",
- ".md": "markdown",
-}
-
-# Treat bare URLs as external links so MyST does not warn about TOC
-# anchor references (e.g. `(#overview)`) being unresolved cross-references.
-myst_all_links_external = True
-
-autodoc_member_order = "bysource"
-
-templates_path = ["_templates"]
-exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
-
-default_role = "any"
-
-# -- Options for sphinx_issues --------------------------------------------------------
-issues_default_group_project = "commit-check/commit-check"
-
-# -- Options for HTML output -------------------------------------------------
-# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
-
-html_theme = "sphinx_immaterial"
-html_static_path = ["_static"]
-# html_logo = "_static/logo.jpg" can not display well in blue background
-# html_favicon = "_static/favicon.ico"
-html_css_files = ["extra_css.css"]
-html_title = "Commit Check"
-
-html_theme_options = {
- "repo_url": "https://github.com/commit-check/commit-check",
- "repo_name": "commit-check",
- "icon": {
- "logo": "material/git",
- },
- "palette": [
- {
- "media": "(prefers-color-scheme: light)",
- "scheme": "default",
- "primary": "blue",
- "accent": "light-blue",
- "toggle": {
- "icon": "material/lightbulb-outline",
- "name": "Switch to dark mode",
- },
- },
- {
- "media": "(prefers-color-scheme: dark)",
- "scheme": "slate",
- "primary": "blue",
- "accent": "light-blue",
- "toggle": {
- "icon": "material/lightbulb",
- "name": "Switch to light mode",
- },
- },
- ],
- # The global navigation lives in the left sidebar (no top tab bar), with
- # each toctree caption rendered as a section heading. This keeps every
- # page one click away and leaves the right-hand column for the page's own
- # table of contents.
- "features": [
- "navigation.sections",
- "navigation.top",
- "navigation.tracking",
- "toc.sticky",
- "toc.follow",
- "search.highlight",
- "search.share",
- ],
- # Keep the sidebar sections expanded rather than collapsing everything but
- # the current page, so the whole documentation set is visible at a glance.
- "globaltoc_collapse": False,
-}
-
-object_description_options = [
- ("py:parameter", {"include_in_toc": False}),
-]
-
-sphinx_immaterial_custom_admonitions = [
- {
- "name": "seealso",
- "color": (215, 59, 205),
- "icon": "octicons/eye-24",
- "override": True,
- },
- {
- "name": "note",
- "icon": "material/file-document-edit-outline",
- "override": True,
- },
-]
-for name in ("hint", "tip", "important"):
- sphinx_immaterial_custom_admonitions.append(
- {"name": name, "icon": "material/school", "override": True}
- )
-
-
-def setup(app: Sphinx):
- """Generate a doc from the executable script's ``--help`` output."""
-
- result = subprocess.run(
- ["commit-check", "--help"],
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE,
- encoding="utf-8",
- )
- doc = "commit-check --help\n==============================\n\n"
- CLI_OPT_NAME = re.compile(r"^\s*(\-\w)(?:\s+[A-Z_\[\]]*)?(?:,\s+(\-\-[a-z\-]+))?")
- in_options_section = False
-
- for line in result.stdout.splitlines():
- # Start processing options when we see the "options:" line
- if line.strip() == "options:":
- in_options_section = True
- doc += line + "\n"
- continue
-
- # Only process option patterns in the options section
- if in_options_section:
- match = CLI_OPT_NAME.search(line)
- if match is not None:
- short_opt = match.group(1)
- long_opt = match.group(2)
- if short_opt and long_opt:
- doc += "\n.. std:option:: " + short_opt + ", " + long_opt + "\n\n"
- elif short_opt:
- doc += "\n.. std:option:: " + short_opt + "\n\n"
-
- doc += line + "\n"
- cli_doc = Path(app.srcdir, "cli_args.rst")
- cli_doc.unlink(missing_ok=True)
- cli_doc.write_text(doc)
diff --git a/docs/configuration.md b/docs/configuration.md
new file mode 100644
index 00000000..ac72ea3f
--- /dev/null
+++ b/docs/configuration.md
@@ -0,0 +1,305 @@
+# Configuration
+
+`commit-check` can be configured in three ways with the following priority (highest to lowest):
+
+1. **Command-line arguments** (`--subject-imperative=true`)
+2. **Environment variables** (`CCHK_SUBJECT_IMPERATIVE=true`)
+3. **Configuration files** (`cchk.toml` or `commit-check.toml`)
+4. **Built-in defaults**
+
+This flexibility allows you to:
+
+* Use configuration files for project-wide settings
+* Override with environment variables in CI/CD pipelines
+* Override specific settings via CLI for one-off checks
+* Use without any configuration files (relies on defaults)
+
+## Configuration Files
+
+`commit-check` configuration files support the TOML format. See `cchk.toml` for an example configuration.
+
+!!! tip "Default Behavior"
+
+ * When no configuration file exists, commit-check uses sensible defaults with minimal restrictions.
+ * Enforced by default: the Conventional Commits format ([CC001](rules.md#cc001)), the Conventional Branch format ([CC201](rules.md#cc201)), the subject length limits of 5–80 characters ([CC004](rules.md#cc004), [CC005](rules.md#cc005)), and the author name and email patterns ([CC101](rules.md#cc101), [CC102](rules.md#cc102)).
+ * **Off** by default: subject capitalization, imperative mood, body and signoff requirements, rebase requirements, and every `allow_*` restriction.
+
+ See [rules](rules.md) for the default state of every rule.
+
+commit-check can be configured via a `cchk.toml` or `commit-check.toml` file.
+
+The file should be placed in the root of your repository or in the `.github` folder.
+
+## Configuration File Locations
+
+commit-check searches for configuration files in the following order (first found is used):
+
+1. `cchk.toml` (root directory)
+2. `commit-check.toml` (root directory)
+3. `.github/cchk.toml`
+4. `.github/commit-check.toml`
+
+!!! tip "GitHub Best Practice"
+
+ Placing configuration files in the `.github` folder helps keep your repository root clean and follows GitHub conventions used by tools like Dependabot and Renovate.
+
+!!! tip "IDE Autocompletion"
+
+ commit-check's TOML schema is published on [SchemaStore](https://www.schemastore.org/),
+ so editors like VS Code (via [Even Better TOML](https://marketplace.visualstudio.com/items?itemName=tamasfe.even-better-toml)),
+ PyCharm, and IntelliJ provide autocompletion, validation, and documentation
+ tooltips for `cchk.toml` out of the box — no manual schema path configuration needed.
+
+## Organization-Level Configuration (inherit_from)
+
+For organizations that want to share a common base configuration across many repositories, commit-check supports an `inherit_from` directive at the top level of your TOML config file.
+
+**How it works:**
+
+1. The `inherit_from` value can be a `github:` shorthand, a local file path, or an HTTPS URL.
+2. The parent (inherited) configuration is loaded first.
+3. Local settings in the current config file **override** the parent values.
+4. The `inherit_from` key itself is not passed to the validation engine.
+
+**Example — inherit from a GitHub repository (recommended):**
+
+```toml
+# .github/cchk.toml
+inherit_from = "github:my-org/.github:cchk.toml"
+
+[commit]
+subject_max_length = 72 # Overrides parent value
+```
+
+**GitHub shorthand format:**
+
+* `github:owner/repo:path/to/cchk.toml` — uses `HEAD` (default branch)
+* `github:owner/repo@main:path/to/cchk.toml` — pins to the `main` branch
+
+**Example — inherit from a local file:**
+
+```toml
+# repo/.github/cchk.toml
+inherit_from = "../../shared/org-cchk.toml"
+
+[commit]
+allow_wip_commits = true # Override for this project only
+```
+
+**Example — inherit from an HTTPS URL:**
+
+```toml
+# .github/cchk.toml
+inherit_from = "https://example.com/shared/cchk.toml"
+```
+
+!!! note
+
+ If the `inherit_from` target is unreachable or the format is unrecognized, commit-check silently ignores the inheritance and uses only the local configuration. HTTP (non-TLS) URLs are rejected for security.
+
+## Example Configuration
+
+```toml
+[commit]
+# https://www.conventionalcommits.org
+conventional_commits = true
+# message_pattern = "" # Optional - custom regex (overrides conventional_commits)
+subject_capitalized = false
+subject_imperative = false
+subject_max_length = 80 # Default - set to your own limit
+subject_min_length = 5 # Default - set to your own minimum
+allow_commit_types = ["feat", "fix", "docs", "style", "refactor", "test", "chore"]
+allow_merge_commits = true
+allow_revert_commits = true
+allow_empty_commits = false
+allow_fixup_commits = true
+allow_wip_commits = false
+require_body = false
+# ignore_authors = [] # Optional - bypass checks for these commit/co-authors
+require_signed_off_by = false
+ai_attribution = "forbid" # "ignore" (default) or "forbid" — rejects AI tool trailers
+
+[push]
+# Block force pushes when used as a pre-push hook or with --no-force-push
+allow_force_push = true # Set to false to block force pushes
+
+[branch]
+# https://conventionalbranch.org
+conventional_branch = true
+# Optional: defaults are a superset of the Conventional Branch spec — the
+# spec types plus Conventional Commit types, AI agent prefixes and bot
+# prefixes (see the Options table below for the full list). Omit this
+# option to use the defaults, or set your own list for a strict subset.
+allow_branch_types = [
+ "feature",
+ "bugfix",
+ "hotfix",
+ "release",
+ "chore",
+ "feat",
+ "fix",
+ "build",
+ "ci",
+ "docs",
+ "perf",
+ "refactor",
+ "style",
+ "test",
+]
+# allow_branch_names = [] # Optional - additional standalone branch names (e.g., ["develop", "staging"])
+# require_rebase_target = "main" # Optional - no rebase requirement by default
+# ignore_authors = [] # Optional - no authors ignored by default
+```
+
+## Command-Line Arguments
+
+All configuration options can be specified via command-line arguments, which take precedence over environment variables and configuration files.
+
+**Syntax:**
+
+* Boolean options: `--option-name=true` or `--option-name=false`
+* Integer options: `--option-name=80`
+* List options: `--option-name=value1,value2,value3` (comma-separated)
+* String options: `--option-name=value`
+
+**Examples:**
+
+```bash
+# Disable imperative mood check
+commit-check --message --subject-imperative=false
+
+# Set custom subject length limit
+commit-check --message --subject-max-length=72
+
+# Restrict allowed commit types
+commit-check --message --allow-commit-types=feat,fix,docs
+
+# Combine multiple options
+commit-check --message --subject-imperative=true --subject-max-length=50 --allow-commit-types=feat,fix
+
+# Branch configuration via CLI
+commit-check --branch --allow-branch-types=feature,bugfix,hotfix
+```
+
+**Pre-commit Hook Usage:**
+
+The primary use case for CLI arguments is configuring commit-check in `.pre-commit-config.yaml` without requiring a TOML file:
+
+```yaml
+repos:
+ - repo: https://github.com/commit-check/commit-check
+ rev: v2.5.0
+ hooks:
+ - id: check-message
+ args:
+ - --subject-imperative=false
+ - --subject-max-length=100
+ - --allow-merge-commits=false
+```
+
+## Environment Variables
+
+Configuration can also be set via environment variables with the `CCHK_` prefix. This is useful for CI/CD pipelines and temporary overrides.
+
+**Naming Convention:**
+
+* Convert option name to uppercase
+* Replace hyphens with underscores
+* Add `CCHK_` prefix
+
+**Examples:**
+
+```bash
+# Set boolean options
+export CCHK_SUBJECT_IMPERATIVE=true
+export CCHK_SUBJECT_CAPITALIZED=false
+
+# Set integer options
+export CCHK_SUBJECT_MAX_LENGTH=72
+export CCHK_SUBJECT_MIN_LENGTH=10
+
+# Set list options (comma-separated)
+export CCHK_ALLOW_COMMIT_TYPES=feat,fix,docs,chore
+export CCHK_ALLOW_BRANCH_TYPES=feature,bugfix,hotfix
+
+# Set string options
+export CCHK_REQUIRE_REBASE_TARGET=main
+
+# Use in CI/CD
+CCHK_SUBJECT_MAX_LENGTH=100 commit-check --message
+```
+
+**Complete Mapping:**
+
+| TOML Config | Environment Variable | CLI Argument |
+|---|---|---|
+| `conventional_commits = true` | `CCHK_CONVENTIONAL_COMMITS=true` | `--conventional-commits=true` |
+| `message_pattern = "^PROJ-\\d+: .+"` | `CCHK_MESSAGE_PATTERN=^PROJ-\\d+: .+` | N/A (config file only) |
+| `subject_capitalized = false` | `CCHK_SUBJECT_CAPITALIZED=false` | `--subject-capitalized=false` |
+| `subject_imperative = true` | `CCHK_SUBJECT_IMPERATIVE=true` | `--subject-imperative=true` |
+| `subject_max_length = 80` | `CCHK_SUBJECT_MAX_LENGTH=80` | `--subject-max-length=80` |
+| `subject_min_length = 5` | `CCHK_SUBJECT_MIN_LENGTH=5` | `--subject-min-length=5` |
+| `allow_commit_types = ["feat", "fix"]` | `CCHK_ALLOW_COMMIT_TYPES=feat,fix` | `--allow-commit-types=feat,fix` |
+| `allow_merge_commits = true` | `CCHK_ALLOW_MERGE_COMMITS=true` | `--allow-merge-commits=true` |
+| `allow_revert_commits = true` | `CCHK_ALLOW_REVERT_COMMITS=true` | `--allow-revert-commits=true` |
+| `allow_empty_commits = false` | `CCHK_ALLOW_EMPTY_COMMITS=false` | `--allow-empty-commits=false` |
+| `allow_fixup_commits = true` | `CCHK_ALLOW_FIXUP_COMMITS=true` | `--allow-fixup-commits=true` |
+| `allow_wip_commits = false` | `CCHK_ALLOW_WIP_COMMITS=false` | `--allow-wip-commits=false` |
+| `require_body = false` | `CCHK_REQUIRE_BODY=false` | `--require-body=false` |
+| `require_signed_off_by = false` | `CCHK_REQUIRE_SIGNED_OFF_BY=false` | `--require-signed-off-by=false` |
+| `ignore_authors = ["bot"]` | `CCHK_IGNORE_AUTHORS=bot,user` | `--ignore-authors=bot,user` |
+| `author_email_pattern=^.+@example\.com$` | `CCHK_AUTHOR_EMAIL_PATTERN=^.+@example\.com$` | `--author-email-pattern=^.+@example\.com$` |
+| `author_name_pattern=^.+ .+$` | `CCHK_AUTHOR_NAME_PATTERN=^.+ .+$` | `--author-name-pattern=^.+ .+$` |
+| `conventional_branch = true` | `CCHK_CONVENTIONAL_BRANCH=true` | `--conventional-branch=true` |
+| `allow_branch_types = ["feature"]` | `CCHK_ALLOW_BRANCH_TYPES=feature,bugfix` | `--allow-branch-types=feature,bugfix` |
+| `allow_branch_names = ["develop"]` | `CCHK_ALLOW_BRANCH_NAMES=develop,staging` | `--allow-branch-names=develop,staging` |
+| `require_rebase_target = "main"` | `CCHK_REQUIRE_REBASE_TARGET=main` | `--require-rebase-target=main` |
+| `allow_force_push = true` | `CCHK_ALLOW_FORCE_PUSH=false` | `--no-force-push` (enable via `--no-force-push` flag) |
+| `ai_attribution = "forbid"` | `CCHK_AI_ATTRIBUTION=forbid` | `--ai-attribution=forbid` |
+| `ignore_authors = ["bot"]` (in branch section) | `CCHK_BRANCH_IGNORE_AUTHORS=bot,user` | `--branch-ignore-authors=bot,user` |
+
+## Configuration Priority Example
+
+When the same option is specified in multiple places, the priority determines which value is used:
+
+```bash
+# In cchk.toml:
+# subject_max_length = 100
+
+# Set via environment:
+export CCHK_SUBJECT_MAX_LENGTH=80
+
+# Override via CLI:
+commit-check --message --subject-max-length=50
+
+# Result: subject_max_length = 50 (CLI wins)
+```
+
+## Options Table Description
+
+| Section | Option | Type | Default | Description |
+|---|---|---|---|---|
+| commit | conventional_commits | bool | true | Enforce Conventional Commits specification. |
+| commit | message_pattern | str | "" (disabled) | Custom regex pattern for commit message validation. When set, this pattern replaces the auto-generated Conventional Commits regex entirely, making it possible to enforce custom formats such as JIRA smart commits (e.g., `"^PROJ-\\d+: .+"`). When `message_pattern` is set (non-empty) it takes precedence over `conventional_commits`. |
+| commit | subject_capitalized | bool | false | Subject must start with a capital letter. |
+| commit | subject_imperative | bool | false | Subject must be in imperative mood. Forms of verbs can be found at [imperatives.py](https://github.com/commit-check/commit-check/blob/main/commit_check/imperatives.py) |
+| commit | subject_max_length | int | 80 | Maximum length of the subject line. |
+| commit | subject_min_length | int | 5 | Minimum length of the subject line. |
+| commit | allow_commit_types | list[str] | ["feat", "fix", "docs", "style", "refactor", "test", "chore", "perf", "build", "ci"] | Allowed commit types when conventional_commits is true. |
+| commit | allow_merge_commits | bool | true | Allow merge commits. |
+| commit | allow_revert_commits | bool | true | Allow revert commits. |
+| commit | allow_empty_commits | bool | true | Allow empty commits. |
+| commit | allow_fixup_commits | bool | true | Allow fixup commits (e.g., "fixup! "). |
+| commit | allow_wip_commits | bool | true | Allow work-in-progress commits (e.g., "WIP: "). |
+| commit | require_body | bool | false | Require a body in the commit message. |
+| commit | ignore_authors | list[str] | [] (none ignored) | List of commit authors **or co-authors** (`Co-authored-by:` lines) to bypass all commit checks. Useful for bots (e.g., `"dependabot[bot]"`, `"coderabbitai[bot]"`). |
+| commit | author_email_pattern | str | `^.+@.+$` | Custom regex for the author email check. When empty, the built-in default pattern is used. This option only takes effect when the author_email check is enabled (`-e` / `--author-email`). |
+| commit | author_name_pattern | str | "" (built-in default) | Custom regex for the author name check. When empty, the built-in default pattern is used (it is not disabled). This option only takes effect when the author_name check is enabled (`-n` / `--author-name`). |
+| commit | require_signed_off_by | bool | false | Require "Signed-off-by" line in the commit message footer. |
+| commit | ai_attribution | str | "ignore" | AI attribution policy. `"forbid"` rejects any commit containing known AI tool signatures (Claude Code, Copilot, Codex, Gemini, Cursor, Devin, Aider, Windsurf, Tabby, and generic AI model patterns). `"ignore"` disables the check. This feature is a response to the industry-wide discussion on AI disclosure in open source (Linux kernel `Assisted-by:` trailer, CPython, VS Code, Apache, Fedora policies). |
+| branch | conventional_branch | bool | true | Enforce Conventional Branch specification. |
+| branch | allow_branch_types | list[str] | ["feature", "bugfix", "hotfix", "release", "chore", "feat", "fix", "build", "ci", "docs", "perf", "refactor", "style", "test", "ai", "claude", "codex", "copilot", "cursor", "dependabot", "renovate"] | Allowed branch types when `conventional_branch` is true. The default is a superset of the [Conventional Branch spec](https://conventionalbranch.org/): the spec types (`feature`, `bugfix`, `hotfix`, `release`, `chore`) plus the Conventional Commit types (`build`, `ci`, `docs`, `perf`, `refactor`, `style`, `test`), AI agent prefixes (`ai`, `claude`, `codex`, `copilot`, `cursor`) and bot prefixes (`dependabot`, `renovate`). For strict spec-only validation, set this option explicitly (e.g. `["feature", "bugfix", "hotfix", "release", "chore"]`). |
+| branch | allow_branch_names | list[str] | [] (empty list) | Additional standalone branch names allowed when conventional_branch is true (e.g., ["develop", "staging"]). By default, master, main, HEAD, and PR-* are always allowed. |
+| branch | require_rebase_target | str | "" (no requirement) | Target branch for rebase requirement. If not set, no rebase validation is performed. |
+| push | allow_force_push | bool | true | Allow force pushes. Set to `false` to block force pushes when used as a pre-push hook or with `--no-force-push`. |
+| branch | ignore_authors | list[str] | [] (none ignored) | List of authors to ignore (i.e., always allow). |
diff --git a/docs/configuration.rst b/docs/configuration.rst
deleted file mode 100644
index 27e05772..00000000
--- a/docs/configuration.rst
+++ /dev/null
@@ -1,476 +0,0 @@
-Configuration
-=============
-
-``commit-check`` can be configured in three ways with the following priority (highest to lowest):
-
-1. **Command-line arguments** (``--subject-imperative=true``)
-2. **Environment variables** (``CCHK_SUBJECT_IMPERATIVE=true``)
-3. **Configuration files** (``cchk.toml`` or ``commit-check.toml``)
-4. **Built-in defaults**
-
-This flexibility allows you to:
-
-* Use configuration files for project-wide settings
-* Override with environment variables in CI/CD pipelines
-* Override specific settings via CLI for one-off checks
-* Use without any configuration files (relies on defaults)
-
-Configuration Files
--------------------
-
-``commit-check`` configuration files support the TOML format. See ``cchk.toml`` for an example configuration.
-
-.. tip::
- **Default Behavior**
-
- * When no configuration file exists, commit-check uses sensible defaults with minimal restrictions.
- * Enforced by default: the Conventional Commits format (:ref:`CC001 `), the Conventional Branch format (:ref:`CC201 `), the subject length limits of 5–80 characters (:ref:`CC004 `, :ref:`CC005 `), and the author name and email patterns (:ref:`CC101 `, :ref:`CC102 `).
- * **Off** by default: subject capitalization, imperative mood, body and signoff requirements, rebase requirements, and every ``allow_*`` restriction.
-
- See :doc:`rules` for the default state of every rule.
-
-commit-check can be configured via a ``cchk.toml`` or ``commit-check.toml`` file.
-
-The file should be placed in the root of your repository or in the ``.github`` folder.
-
-Configuration File Locations
------------------------------
-
-commit-check searches for configuration files in the following order (first found is used):
-
-1. ``cchk.toml`` (root directory)
-2. ``commit-check.toml`` (root directory)
-3. ``.github/cchk.toml``
-4. ``.github/commit-check.toml``
-
-.. tip::
- **GitHub Best Practice**
-
- Placing configuration files in the ``.github`` folder helps keep your repository root clean and follows GitHub conventions used by tools like Dependabot and Renovate.
-
-.. tip::
- **IDE Autocompletion**
-
- commit-check's TOML schema is published on `SchemaStore `_,
- so editors like VS Code (via `Even Better TOML `_),
- PyCharm, and IntelliJ provide autocompletion, validation, and documentation
- tooltips for ``cchk.toml`` out of the box — no manual schema path configuration needed.
-
-Organization-Level Configuration (inherit_from)
--------------------------------------------------
-
-For organizations that want to share a common base configuration across many repositories, commit-check supports an ``inherit_from`` directive at the top level of your TOML config file.
-
-**How it works:**
-
-1. The ``inherit_from`` value can be a ``github:`` shorthand, a local file path, or an HTTPS URL.
-2. The parent (inherited) configuration is loaded first.
-3. Local settings in the current config file **override** the parent values.
-4. The ``inherit_from`` key itself is not passed to the validation engine.
-
-**Example — inherit from a GitHub repository (recommended):**
-
-.. code-block:: toml
-
- # .github/cchk.toml
- inherit_from = "github:my-org/.github:cchk.toml"
-
- [commit]
- subject_max_length = 72 # Overrides parent value
-
-**GitHub shorthand format:**
-
-* ``github:owner/repo:path/to/cchk.toml`` — uses ``HEAD`` (default branch)
-* ``github:owner/repo@main:path/to/cchk.toml`` — pins to the ``main`` branch
-
-**Example — inherit from a local file:**
-
-.. code-block:: toml
-
- # repo/.github/cchk.toml
- inherit_from = "../../shared/org-cchk.toml"
-
- [commit]
- allow_wip_commits = true # Override for this project only
-
-**Example — inherit from an HTTPS URL:**
-
-.. code-block:: toml
-
- # .github/cchk.toml
- inherit_from = "https://example.com/shared/cchk.toml"
-
-.. note::
- If the ``inherit_from`` target is unreachable or the format is unrecognized, commit-check silently ignores the inheritance and uses only the local configuration. HTTP (non-TLS) URLs are rejected for security.
-
-Example Configuration
----------------------
-
-.. code-block:: toml
- :class: copy
-
- [commit]
- # https://www.conventionalcommits.org
- conventional_commits = true
- # message_pattern = "" # Optional - custom regex (overrides conventional_commits)
- subject_capitalized = false
- subject_imperative = false
- subject_max_length = 80 # Default - set to your own limit
- subject_min_length = 5 # Default - set to your own minimum
- allow_commit_types = ["feat", "fix", "docs", "style", "refactor", "test", "chore"]
- allow_merge_commits = true
- allow_revert_commits = true
- allow_empty_commits = false
- allow_fixup_commits = true
- allow_wip_commits = false
- require_body = false
- # ignore_authors = [] # Optional - bypass checks for these commit/co-authors
- require_signed_off_by = false
- ai_attribution = "forbid" # "ignore" (default) or "forbid" — rejects AI tool trailers
-
- [push]
- # Block force pushes when used as a pre-push hook or with --no-force-push
- allow_force_push = true # Set to false to block force pushes
-
- [branch]
- # https://conventionalbranch.org
- conventional_branch = true
- # Optional: defaults are a superset of the Conventional Branch spec — the
- # spec types plus Conventional Commit types, AI agent prefixes and bot
- # prefixes (see the Options table below for the full list). Omit this
- # option to use the defaults, or set your own list for a strict subset.
- allow_branch_types = [
- "feature",
- "bugfix",
- "hotfix",
- "release",
- "chore",
- "feat",
- "fix",
- "build",
- "ci",
- "docs",
- "perf",
- "refactor",
- "style",
- "test",
- ]
- # allow_branch_names = [] # Optional - additional standalone branch names (e.g., ["develop", "staging"])
- # require_rebase_target = "main" # Optional - no rebase requirement by default
- # ignore_authors = [] # Optional - no authors ignored by default
-
-
-Command-Line Arguments
-----------------------
-
-All configuration options can be specified via command-line arguments, which take precedence over environment variables and configuration files.
-
-**Syntax:**
-
-* Boolean options: ``--option-name=true`` or ``--option-name=false``
-* Integer options: ``--option-name=80``
-* List options: ``--option-name=value1,value2,value3`` (comma-separated)
-* String options: ``--option-name=value``
-
-**Examples:**
-
-.. code-block:: bash
-
- # Disable imperative mood check
- commit-check --message --subject-imperative=false
-
- # Set custom subject length limit
- commit-check --message --subject-max-length=72
-
- # Restrict allowed commit types
- commit-check --message --allow-commit-types=feat,fix,docs
-
- # Combine multiple options
- commit-check --message --subject-imperative=true --subject-max-length=50 --allow-commit-types=feat,fix
-
- # Branch configuration via CLI
- commit-check --branch --allow-branch-types=feature,bugfix,hotfix
-
-**Pre-commit Hook Usage:**
-
-The primary use case for CLI arguments is configuring commit-check in ``.pre-commit-config.yaml`` without requiring a TOML file:
-
-.. code-block:: yaml
-
- repos:
- - repo: https://github.com/commit-check/commit-check
- rev: v2.5.0
- hooks:
- - id: check-message
- args:
- - --subject-imperative=false
- - --subject-max-length=100
- - --allow-merge-commits=false
-
-Environment Variables
----------------------
-
-Configuration can also be set via environment variables with the ``CCHK_`` prefix. This is useful for CI/CD pipelines and temporary overrides.
-
-**Naming Convention:**
-
-* Convert option name to uppercase
-* Replace hyphens with underscores
-* Add ``CCHK_`` prefix
-
-**Examples:**
-
-.. code-block:: bash
-
- # Set boolean options
- export CCHK_SUBJECT_IMPERATIVE=true
- export CCHK_SUBJECT_CAPITALIZED=false
-
- # Set integer options
- export CCHK_SUBJECT_MAX_LENGTH=72
- export CCHK_SUBJECT_MIN_LENGTH=10
-
- # Set list options (comma-separated)
- export CCHK_ALLOW_COMMIT_TYPES=feat,fix,docs,chore
- export CCHK_ALLOW_BRANCH_TYPES=feature,bugfix,hotfix
-
- # Set string options
- export CCHK_REQUIRE_REBASE_TARGET=main
-
- # Use in CI/CD
- CCHK_SUBJECT_MAX_LENGTH=100 commit-check --message
-
-**Complete Mapping:**
-
-.. list-table::
- :header-rows: 1
-
- * - TOML Config
- - Environment Variable
- - CLI Argument
- * - ``conventional_commits = true``
- - ``CCHK_CONVENTIONAL_COMMITS=true``
- - ``--conventional-commits=true``
- * - ``message_pattern = "^PROJ-\\d+: .+"``
- - ``CCHK_MESSAGE_PATTERN=^PROJ-\\d+: .+``
- - N/A (config file only)
- * - ``subject_capitalized = false``
- - ``CCHK_SUBJECT_CAPITALIZED=false``
- - ``--subject-capitalized=false``
- * - ``subject_imperative = true``
- - ``CCHK_SUBJECT_IMPERATIVE=true``
- - ``--subject-imperative=true``
- * - ``subject_max_length = 80``
- - ``CCHK_SUBJECT_MAX_LENGTH=80``
- - ``--subject-max-length=80``
- * - ``subject_min_length = 5``
- - ``CCHK_SUBJECT_MIN_LENGTH=5``
- - ``--subject-min-length=5``
- * - ``allow_commit_types = ["feat", "fix"]``
- - ``CCHK_ALLOW_COMMIT_TYPES=feat,fix``
- - ``--allow-commit-types=feat,fix``
- * - ``allow_merge_commits = true``
- - ``CCHK_ALLOW_MERGE_COMMITS=true``
- - ``--allow-merge-commits=true``
- * - ``allow_revert_commits = true``
- - ``CCHK_ALLOW_REVERT_COMMITS=true``
- - ``--allow-revert-commits=true``
- * - ``allow_empty_commits = false``
- - ``CCHK_ALLOW_EMPTY_COMMITS=false``
- - ``--allow-empty-commits=false``
- * - ``allow_fixup_commits = true``
- - ``CCHK_ALLOW_FIXUP_COMMITS=true``
- - ``--allow-fixup-commits=true``
- * - ``allow_wip_commits = false``
- - ``CCHK_ALLOW_WIP_COMMITS=false``
- - ``--allow-wip-commits=false``
- * - ``require_body = false``
- - ``CCHK_REQUIRE_BODY=false``
- - ``--require-body=false``
- * - ``require_signed_off_by = false``
- - ``CCHK_REQUIRE_SIGNED_OFF_BY=false``
- - ``--require-signed-off-by=false``
- * - ``ignore_authors = ["bot"]``
- - ``CCHK_IGNORE_AUTHORS=bot,user``
- - ``--ignore-authors=bot,user``
- * - ``author_email_pattern=^.+@example\.com$``
- - ``CCHK_AUTHOR_EMAIL_PATTERN=^.+@example\.com$``
- - ``--author-email-pattern=^.+@example\.com$``
- * - ``author_name_pattern=^.+ .+$``
- - ``CCHK_AUTHOR_NAME_PATTERN=^.+ .+$``
- - ``--author-name-pattern=^.+ .+$``
- * - ``conventional_branch = true``
- - ``CCHK_CONVENTIONAL_BRANCH=true``
- - ``--conventional-branch=true``
- * - ``allow_branch_types = ["feature"]``
- - ``CCHK_ALLOW_BRANCH_TYPES=feature,bugfix``
- - ``--allow-branch-types=feature,bugfix``
- * - ``allow_branch_names = ["develop"]``
- - ``CCHK_ALLOW_BRANCH_NAMES=develop,staging``
- - ``--allow-branch-names=develop,staging``
- * - ``require_rebase_target = "main"``
- - ``CCHK_REQUIRE_REBASE_TARGET=main``
- - ``--require-rebase-target=main``
- * - ``allow_force_push = true``
- - ``CCHK_ALLOW_FORCE_PUSH=false``
- - ``--no-force-push`` (enable via ``--no-force-push`` flag)
- * - ``ai_attribution = "forbid"``
- - ``CCHK_AI_ATTRIBUTION=forbid``
- - ``--ai-attribution=forbid``
- * - ``ignore_authors = ["bot"]`` (in branch section)
- - ``CCHK_BRANCH_IGNORE_AUTHORS=bot,user``
- - ``--branch-ignore-authors=bot,user``
-
-
-Configuration Priority Example
--------------------------------
-
-When the same option is specified in multiple places, the priority determines which value is used:
-
-.. code-block:: bash
-
- # In cchk.toml:
- # subject_max_length = 100
-
- # Set via environment:
- export CCHK_SUBJECT_MAX_LENGTH=80
-
- # Override via CLI:
- commit-check --message --subject-max-length=50
-
- # Result: subject_max_length = 50 (CLI wins)
-
-
-Options Table Description
--------------------------
-
-.. list-table::
- :header-rows: 1
-
- * - Section
- - Option
- - Type
- - Default
- - Description
- * - commit
- - conventional_commits
- - bool
- - true
- - Enforce Conventional Commits specification.
- * - commit
- - message_pattern
- - str
- - "" (disabled)
- - Custom regex pattern for commit message validation. When set, this pattern replaces the auto-generated Conventional Commits regex entirely, making it possible to enforce custom formats such as JIRA smart commits (e.g., ``"^PROJ-\\d+: .+"``). When ``message_pattern`` is set (non-empty) it takes precedence over ``conventional_commits``.
- * - commit
- - subject_capitalized
- - bool
- - false
- - Subject must start with a capital letter.
- * - commit
- - subject_imperative
- - bool
- - false
- - Subject must be in imperative mood. Forms of verbs can be found at `imperatives.py `_
- * - commit
- - subject_max_length
- - int
- - 80
- - Maximum length of the subject line.
- * - commit
- - subject_min_length
- - int
- - 5
- - Minimum length of the subject line.
- * - commit
- - allow_commit_types
- - list[str]
- - ["feat", "fix", "docs", "style", "refactor", "test", "chore", "perf", "build", "ci"]
- - Allowed commit types when conventional_commits is true.
- * - commit
- - allow_merge_commits
- - bool
- - true
- - Allow merge commits.
- * - commit
- - allow_revert_commits
- - bool
- - true
- - Allow revert commits.
- * - commit
- - allow_empty_commits
- - bool
- - true
- - Allow empty commits.
- * - commit
- - allow_fixup_commits
- - bool
- - true
- - Allow fixup commits (e.g., "fixup! ").
- * - commit
- - allow_wip_commits
- - bool
- - true
- - Allow work-in-progress commits (e.g., "WIP: ").
- * - commit
- - require_body
- - bool
- - false
- - Require a body in the commit message.
- * - commit
- - ignore_authors
- - list[str]
- - [] (none ignored)
- - List of commit authors **or co-authors** (``Co-authored-by:`` lines) to bypass all commit checks. Useful for bots (e.g., ``"dependabot[bot]"``, ``"coderabbitai[bot]"``).
- * - commit
- - author_email_pattern
- - str
- - ``^.+@.+$``
- - Custom regex for the author email check. When empty, the built-in default pattern is used.
- This option only takes effect when the author_email check is enabled (``-e`` / ``--author-email``).
- * - commit
- - author_name_pattern
- - str
- - "" (built-in default)
- - Custom regex for the author name check. When empty, the built-in default pattern is used (it is not disabled).
- This option only takes effect when the author_name check is enabled (``-n`` / ``--author-name``).
- * - commit
- - require_signed_off_by
- - bool
- - false
- - Require "Signed-off-by" line in the commit message footer.
- * - commit
- - ai_attribution
- - str
- - "ignore"
- - AI attribution policy. ``"forbid"`` rejects any commit containing known AI tool signatures (Claude Code, Copilot, Codex, Gemini, Cursor, Devin, Aider, Windsurf, Tabby, and generic AI model patterns). ``"ignore"`` disables the check. This feature is a response to the industry-wide discussion on AI disclosure in open source (Linux kernel ``Assisted-by:`` trailer, CPython, VS Code, Apache, Fedora policies).
- * - branch
- - conventional_branch
- - bool
- - true
- - Enforce Conventional Branch specification.
- * - branch
- - allow_branch_types
- - list[str]
- - ["feature", "bugfix", "hotfix", "release", "chore", "feat", "fix", "build", "ci", "docs", "perf", "refactor", "style", "test", "ai", "claude", "codex", "copilot", "cursor", "dependabot", "renovate"]
- - Allowed branch types when ``conventional_branch`` is true. The default is a superset of the `Conventional Branch spec `_: the spec types (``feature``, ``bugfix``, ``hotfix``, ``release``, ``chore``) plus the Conventional Commit types (``build``, ``ci``, ``docs``, ``perf``, ``refactor``, ``style``, ``test``), AI agent prefixes (``ai``, ``claude``, ``codex``, ``copilot``, ``cursor``) and bot prefixes (``dependabot``, ``renovate``). For strict spec-only validation, set this option explicitly (e.g. ``["feature", "bugfix", "hotfix", "release", "chore"]``).
- * - branch
- - allow_branch_names
- - list[str]
- - [] (empty list)
- - Additional standalone branch names allowed when conventional_branch is true (e.g., ["develop", "staging"]). By default, master, main, HEAD, and PR-* are always allowed.
- * - branch
- - require_rebase_target
- - str
- - "" (no requirement)
- - Target branch for rebase requirement. If not set, no rebase validation is performed.
- * - push
- - allow_force_push
- - bool
- - true
- - Allow force pushes. Set to ``false`` to block force pushes when used as a pre-push hook or with ``--no-force-push``.
- * - branch
- - ignore_authors
- - list[str]
- - [] (none ignored)
- - List of authors to ignore (i.e., always allow).
diff --git a/docs/example.md b/docs/example.md
new file mode 100644
index 00000000..5d90ade3
--- /dev/null
+++ b/docs/example.md
@@ -0,0 +1,374 @@
+# Usage Examples
+
+This guide demonstrates how to use commit-check to validate commit messages, branch names, and author information.
+
+There are several ways to use commit-check: as a pre-commit hook, via STDIN, or directly with files.
+
+## Running as GitHub Action
+
+Please see [commit-check/commit-check-action](https://github.com/commit-check/commit-check-action)
+
+## Running as pre-commit hook
+
+1. **Install pre-commit:**
+
+!!! tip
+
+ Make sure `pre-commit` is [installed](https://pre-commit.com/#install).
+
+```bash
+pip install pre-commit
+```
+
+2. **Create .pre-commit-config.yaml:**
+
+```yaml
+- repo: https://github.com/commit-check/commit-check
+ rev: the tag or revision
+ hooks:
+ - id: check-message
+ stages: [commit-msg]
+ - id: check-branch
+ - id: check-author-name
+ - id: check-author-email
+```
+
+3. **Install the hooks:**
+
+```bash
+pre-commit install --hook-type pre-commit --hook-type commit-msg
+```
+
+4. **Test the integration:**
+
+```bash
+# This will trigger validation automatically
+git commit -m "feat: add new user authentication system"
+```
+
+### Pre-commit Validation Examples
+
+**✅ Successful Validation:**
+
+```text
+$ git commit -m "feat: add user authentication system"
+
+check commit message.....................................................Passed
+check committer name.....................................................Passed
+check committer email....................................................Passed
+[main abc1234] feat: add user authentication system
+```
+
+**❌ Failed Validation:**
+
+```text
+$ git commit -m "bad commit message"
+
+check commit message.....................................................Failed
+- hook id: check-message
+- exit code: 1
+
+Commit rejected by Commit-Check.
+
+ (c).-.(c) (c).-.(c) (c).-.(c) (c).-.(c) (c).-.(c)
+ / ._. \ / ._. \ / ._. \ / ._. \ / ._. \
+ __\( C )/__ __\( H )/__ __\( E )/__ __\( C )/__ __\( K )/__
+(_.-/'-'\-._)(_.-/'-'\-._)(_.-/'-'\-._)(_.-/'-'\-._)(_.-/'-'\-._)
+ || E || || R || || R || || O || || R ||
+ _.' '-' '._ _.' '-' '._ _.' '-' '._ _.' '-' '._ _.' '-' '._
+(.-./`-´\.-.)(.-./`-´\.-.)(.-./`-´\.-.)(.-./`-´\.-.)(.-./`-´\.-.)
+ `-´ `-´ `-´ `-´ `-´ `-´ `-´ `-´ `-´ `-´
+
+Commit rejected.
+
+Type message check failed ==> bad commit message
+It doesn't match regex: ^(feat|fix|docs|style|refactor|test|chore)(\(.+\))?: .+
+The commit message should follow Conventional Commits. See https://www.conventionalcommits.org
+Suggest: Use (): with allowed types
+```
+
+## Running as CLI
+
+Commit-check provides several command-line options for different validation scenarios. via options or STDIN
+
+.. tip ::
+ Validate commit messages by piping them through STDIN. This is useful for testing or scripting.
+
+Available Commands see [commit-check --help](cli.md)
+
+### Message Validation Examples
+
+```bash
+# Validate message from STDIN
+echo "feat: new feature" | commit-check -m
+
+# Validate message from file
+commit-check -m commit_message.txt
+
+# Validate current git commit message (from git log)
+commit-check -m
+```
+
+**Reading from file:**
+
+```bash
+# Create a commit message file
+cat > commit_message.txt << EOF
+fix(auth): resolve login timeout issue
+
+Users were experiencing timeouts during login.
+Increased session timeout and improved error handling.
+
+Fixes #123
+EOF
+
+# Validate from file
+commit-check -m commit_message.txt
+
+# Or pipe file content
+cat commit_message.txt | commit-check -m
+```
+
+### Branch Validation Examples
+
+```bash
+# Check current branch name
+commit-check --branch
+
+# Example valid branch names:
+# - feature/user-auth
+# - fix/login-bug
+# - hotfix/security-patch
+# - release/v1.2.0
+```
+
+### Push Validation Examples
+
+```bash
+# Check whether pushing HEAD to its configured upstream would require force
+commit-check --no-force-push
+```
+
+```yaml
+# Configure the dedicated pre-push hook
+- repo: https://github.com/commit-check/commit-check
+ rev: the tag or revision
+ hooks:
+ - id: check-no-force-push
+ stages: [pre-push]
+```
+
+`git push | commit-check --no-force-push` is not a prevention mechanism. The
+push has already started, and normal `git push` output does not include the
+pre-push ref lines that Git provides to hooks.
+
+### Author Validation Examples
+
+```bash
+# Check author name
+commit-check --author-name
+
+# Check author email
+commit-check --author-email
+
+# Check both author name and email
+commit-check --author-name --author-email
+```
+
+### Configuration Examples
+
+```bash
+# Use custom configuration file
+echo "feat: test" | commit-check --config my-config.toml -m
+
+# Use configuration from different directory
+commit-check --config /path/to/config/cchk.toml -m
+```
+
+### Valid Commit Message Examples
+
+```bash
+# Basic feature
+echo "feat: add user registration" | commit-check -m
+
+# Feature with scope
+echo "feat(auth): implement OAuth2 login" | commit-check -m
+
+# Bug fix
+echo "fix: resolve memory leak in parser" | commit-check -m
+
+# Documentation update
+echo "docs: add installation guide" | commit-check -m
+
+# Breaking change
+echo "feat!: redesign API endpoints" | commit-check -m
+
+# Merge commit (automatically allowed)
+echo "Merge pull request #123 from feature/new-api" | commit-check -m
+```
+
+### Invalid Commit Message Examples
+
+```bash
+# No type prefix
+echo "added new feature" | commit-check -m
+
+# Capitalized (if configured to disallow)
+echo "feat: Add new feature" | commit-check -m
+
+# Too short
+echo "fix" | commit-check -m
+
+# Non-imperative mood
+echo "feat: added login functionality" | commit-check -m
+
+# Unknown type
+echo "unknown: some changes" | commit-check -m
+```
+
+### Error Output Examples
+
+**Commit Message Validation Failure:**
+
+```text
+Commit rejected by Commit-Check.
+
+ (c).-.(c) (c).-.(c) (c).-.(c) (c).-.(c) (c).-.(c)
+ / ._. \ / ._. \ / ._. \ / ._. \ / ._. \
+ __\( C )/__ __\( H )/__ __\( E )/__ __\( C )/__ __\( K )/__
+(_.-/'-'\-._)(_.-/'-'\-._)(_.-/'-'\-._)(_.-/'-'\-._)(_.-/'-'\-._)
+ || E || || R || || R || || O || || R ||
+ _.' '-' '._ _.' '-' '._ _.' '-' '._ _.' '-' '._ _.' '-' '._
+(.-./`-´\.-.)(.-./`-´\.-.)(.-./`-´\.-.)(.-./`-´\.-.)(.-./`-´\.-.)
+ `-´ `-´ `-´ `-´ `-´ `-´ `-´ `-´ `-´ `-´
+
+Commit rejected.
+
+Type message check failed ==> test commit message check
+It doesn't match regex: ^(chore|ci|docs|feat|fix|refactor|style|test){1}(\([\w\-\.]+\))?(!)?: ([\w ])+([\s\S]*)|(Merge).*|(fixup!.*)
+The commit message should follow Conventional Commits. See https://www.conventionalcommits.org
+Suggest: Use (): with allowed types
+```
+
+**Branch Name Validation Failure:**
+
+```text
+Commit rejected by Commit-Check.
+
+ (c).-.(c) (c).-.(c) (c).-.(c) (c).-.(c) (c).-.(c)
+ / ._. \ / ._. \ / ._. \ / ._. \ / ._. \
+ __\( C )/__ __\( H )/__ __\( E )/__ __\( C )/__ __\( K )/__
+(_.-/'-'\-._)(_.-/'-'\-._)(_.-/'-'\-._)(_.-/'-'\-._)(_.-/'-'\-._)
+ || E || || R || || R || || O || || R ||
+ _.' '-' '._ _.' '-' '._ _.' '-' '._ _.' '-' '._ _.' '-' '._
+(.-./`-´\.-.)(.-./`-´\.-.)(.-./`-´\.-.)(.-./`-´\.-.)(.-./`-´\.-.)
+ `-´ `-´ `-´ `-´ `-´ `-´ `-´ `-´ `-´ `-´
+
+Commit rejected.
+
+Type branch check failed ==> test-branch
+It doesn't match regex: ^(feature|bugfix|hotfix|release|chore|feat|fix)\/.+|(master)|(main)|(HEAD)|(PR-.+)
+The branch should follow Conventional Branch. See https://conventionalbranch.org
+Suggest: Use / with allowed types or ignore_authors in config branch section to bypass
+```
+
+**Commit Signature Validation Failure:**
+
+```text
+Commit rejected by Commit-Check.
+
+ (c).-.(c) (c).-.(c) (c).-.(c) (c).-.(c) (c).-.(c)
+ / ._. \ / ._. \ / ._. \ / ._. \ / ._. \
+ __\( C )/__ __\( H )/__ __\( E )/__ __\( C )/__ __\( K )/__
+(_.-/'-'\-._)(_.-/'-'\-._)(_.-/'-'\-._)(_.-/'-'\-._)(_.-/'-'\-._)
+ || E || || R || || R || || O || || R ||
+ _.' '-' '._ _.' '-' '._ _.' '-' '._ _.' '-' '._ _.' '-' '._
+(.-./`-´\.-.)(.-./`-´\.-.)(.-./`-´\.-.)(.-./`-´\.-.)(.-./`-´\.-.)
+ `-´ `-´ `-´ `-´ `-´ `-´ `-´ `-´ `-´ `-´
+
+Commit rejected.
+
+Type require_signed_off_by check failed ==> fix: add missing file
+It doesn't match regex: Signed-off-by:.*[A-Za-z0-9]\s+<.+@.+>
+Signed-off-by not found in latest commit
+Suggest: git commit --amend --signoff or use --signoff on commit
+```
+
+**Commit Message Validation Failure without ASCII Banner (`--no-banner`):**
+
+```text
+Type message check failed ==> test commit message check
+It doesn't match regex: ^(chore|ci|docs|feat|fix|refactor|style|test){1}(\([\w\-\.]+\))?(!)?: ([\w ])+([\s\S]*)|(Merge).*|(fixup!.*)
+The commit message should follow Conventional Commits. See https://www.conventionalcommits.org
+Suggest: Use (): with allowed types
+```
+
+**Compact Failure Output (`--compact`):**
+
+```text
+[FAIL] message: test commit message check
+```
+
+**Imperative Mood Validation Failure:**
+
+```text
+Commit rejected by Commit-Check.
+
+ (c).-.(c) (c).-.(c) (c).-.(c) (c).-.(c) (c).-.(c)
+ / ._. \ / ._. \ / ._. \ / ._. \ / ._. \
+ __\( C )/__ __\( H )/__ __\( E )/__ __\( C )/__ __\( K )/__
+(_.-/'-'\-._)(_.-/'-'\-._)(_.-/'-'\-._)(_.-/'-'\-._)(_.-/'-'\-._)
+ || E || || R || || R || || O || || R ||
+ _.' '-' '._ _.' '-' '._ _.' '-' '._ _.' '-' '._ _.' '-' '._
+(.-./`-´\.-.)(.-./`-´\.-.)(.-./`-´\.-.)(.-./`-´\.-.)(.-./`-´\.-.)
+ `-´ `-´ `-´ `-´ `-´ `-´ `-´ `-´ `-´ `-´
+
+Commit rejected.
+
+Type imperative check failed ==> fix: added missing file
+It doesn't match regex:
+Commit message should use imperative mood (e.g., 'Add feature' not 'Added feature')
+Suggest: Use imperative mood in the subject line
+```
+
+## Integration Tips
+
+### CI/CD Integration
+
+You can use commit-check in CI/CD pipelines:
+
+```bash
+# In your CI script
+git log --format="%s" -n 1 | commit-check -m
+
+# or just
+commit-check -m
+
+# Keep plain-text output but remove the ASCII art banner
+git log --format="%s" -n 1 | commit-check -m --no-banner
+
+# Emit one machine-friendly line per failure without switching to JSON
+git log --format="%s" -n 1 | commit-check -m --compact
+```
+
+### Scripting
+
+Use commit-check in scripts to validate commit messages programmatically:
+
+```bash
+#!/bin/bash
+# validate-commits.sh
+
+# Get all commit messages from last 10 commits
+for i in {0..9}; do
+ msg=$(git log --format="%s" -n 1 --skip=$i)
+ if [ -n "$msg" ]; then
+ echo "Validating: $msg"
+ echo "$msg" | commit-check -m || exit 1
+ fi
+done
+
+echo "All commits are valid!"
+```
+
+For more configuration options, see the [Configuration Documentation](configuration.md).
diff --git a/docs/example.rst b/docs/example.rst
deleted file mode 100644
index 4ac73653..00000000
--- a/docs/example.rst
+++ /dev/null
@@ -1,397 +0,0 @@
-Usage Examples
-==============
-
-This guide demonstrates how to use commit-check to validate commit messages, branch names, and author information.
-
-There are several ways to use commit-check: as a pre-commit hook, via STDIN, or directly with files.
-
-Running as GitHub Action
-------------------------
-
-Please see `commit-check/commit-check-action `_
-
-Running as pre-commit hook
----------------------------
-
-1. **Install pre-commit:**
-
-.. tip::
-
- Make sure ``pre-commit`` is `installed `_.
-
-.. code-block:: bash
-
- pip install pre-commit
-
-2. **Create .pre-commit-config.yaml:**
-
-.. code-block:: yaml
-
- - repo: https://github.com/commit-check/commit-check
- rev: the tag or revision
- hooks:
- - id: check-message
- stages: [commit-msg]
- - id: check-branch
- - id: check-author-name
- - id: check-author-email
-
-3. **Install the hooks:**
-
-.. code-block:: bash
-
- pre-commit install --hook-type pre-commit --hook-type commit-msg
-
-4. **Test the integration:**
-
-.. code-block:: bash
-
- # This will trigger validation automatically
- git commit -m "feat: add new user authentication system"
-
-
-Pre-commit Validation Examples
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-**✅ Successful Validation:**
-
-.. code-block:: text
-
- $ git commit -m "feat: add user authentication system"
-
- check commit message.....................................................Passed
- check committer name.....................................................Passed
- check committer email....................................................Passed
- [main abc1234] feat: add user authentication system
-
-**❌ Failed Validation:**
-
-.. code-block:: text
-
- $ git commit -m "bad commit message"
-
- check commit message.....................................................Failed
- - hook id: check-message
- - exit code: 1
-
- Commit rejected by Commit-Check.
-
- (c).-.(c) (c).-.(c) (c).-.(c) (c).-.(c) (c).-.(c)
- / ._. \ / ._. \ / ._. \ / ._. \ / ._. \
- __\( C )/__ __\( H )/__ __\( E )/__ __\( C )/__ __\( K )/__
- (_.-/'-'\-._)(_.-/'-'\-._)(_.-/'-'\-._)(_.-/'-'\-._)(_.-/'-'\-._)
- || E || || R || || R || || O || || R ||
- _.' '-' '._ _.' '-' '._ _.' '-' '._ _.' '-' '._ _.' '-' '._
- (.-./`-´\.-.)(.-./`-´\.-.)(.-./`-´\.-.)(.-./`-´\.-.)(.-./`-´\.-.)
- `-´ `-´ `-´ `-´ `-´ `-´ `-´ `-´ `-´ `-´
-
- Commit rejected.
-
- Type message check failed ==> bad commit message
- It doesn't match regex: ^(feat|fix|docs|style|refactor|test|chore)(\(.+\))?: .+
- The commit message should follow Conventional Commits. See https://www.conventionalcommits.org
- Suggest: Use (): with allowed types
-
-
-Running as CLI
---------------
-
-Commit-check provides several command-line options for different validation scenarios. via options or STDIN
-
-.. tip ::
- Validate commit messages by piping them through STDIN. This is useful for testing or scripting.
-
-Available Commands see `commit-check --help `_
-
-Message Validation Examples
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-.. code-block:: bash
-
- # Validate message from STDIN
- echo "feat: new feature" | commit-check -m
-
- # Validate message from file
- commit-check -m commit_message.txt
-
- # Validate current git commit message (from git log)
- commit-check -m
-
-
-**Reading from file:**
-
-.. code-block:: bash
-
- # Create a commit message file
- cat > commit_message.txt << EOF
- fix(auth): resolve login timeout issue
-
- Users were experiencing timeouts during login.
- Increased session timeout and improved error handling.
-
- Fixes #123
- EOF
-
- # Validate from file
- commit-check -m commit_message.txt
-
- # Or pipe file content
- cat commit_message.txt | commit-check -m
-
-
-Branch Validation Examples
-~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-.. code-block:: bash
-
- # Check current branch name
- commit-check --branch
-
- # Example valid branch names:
- # - feature/user-auth
- # - fix/login-bug
- # - hotfix/security-patch
- # - release/v1.2.0
-
-Push Validation Examples
-~~~~~~~~~~~~~~~~~~~~~~~~
-
-.. code-block:: bash
-
- # Check whether pushing HEAD to its configured upstream would require force
- commit-check --no-force-push
-
-.. code-block:: yaml
-
- # Configure the dedicated pre-push hook
- - repo: https://github.com/commit-check/commit-check
- rev: the tag or revision
- hooks:
- - id: check-no-force-push
- stages: [pre-push]
-
-``git push | commit-check --no-force-push`` is not a prevention mechanism. The
-push has already started, and normal ``git push`` output does not include the
-pre-push ref lines that Git provides to hooks.
-
-Author Validation Examples
-~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-.. code-block:: bash
-
- # Check author name
- commit-check --author-name
-
- # Check author email
- commit-check --author-email
-
- # Check both author name and email
- commit-check --author-name --author-email
-
-
-Configuration Examples
-~~~~~~~~~~~~~~~~~~~~~~
-
-.. code-block:: bash
-
- # Use custom configuration file
- echo "feat: test" | commit-check --config my-config.toml -m
-
- # Use configuration from different directory
- commit-check --config /path/to/config/cchk.toml -m
-
-
-Valid Commit Message Examples
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-.. code-block:: bash
-
- # Basic feature
- echo "feat: add user registration" | commit-check -m
-
- # Feature with scope
- echo "feat(auth): implement OAuth2 login" | commit-check -m
-
- # Bug fix
- echo "fix: resolve memory leak in parser" | commit-check -m
-
- # Documentation update
- echo "docs: add installation guide" | commit-check -m
-
- # Breaking change
- echo "feat!: redesign API endpoints" | commit-check -m
-
- # Merge commit (automatically allowed)
- echo "Merge pull request #123 from feature/new-api" | commit-check -m
-
-Invalid Commit Message Examples
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-.. code-block:: bash
-
- # No type prefix
- echo "added new feature" | commit-check -m
-
- # Capitalized (if configured to disallow)
- echo "feat: Add new feature" | commit-check -m
-
- # Too short
- echo "fix" | commit-check -m
-
- # Non-imperative mood
- echo "feat: added login functionality" | commit-check -m
-
- # Unknown type
- echo "unknown: some changes" | commit-check -m
-
-Error Output Examples
-~~~~~~~~~~~~~~~~~~~~~
-
-**Commit Message Validation Failure:**
-
-.. code-block:: text
-
- Commit rejected by Commit-Check.
-
- (c).-.(c) (c).-.(c) (c).-.(c) (c).-.(c) (c).-.(c)
- / ._. \ / ._. \ / ._. \ / ._. \ / ._. \
- __\( C )/__ __\( H )/__ __\( E )/__ __\( C )/__ __\( K )/__
- (_.-/'-'\-._)(_.-/'-'\-._)(_.-/'-'\-._)(_.-/'-'\-._)(_.-/'-'\-._)
- || E || || R || || R || || O || || R ||
- _.' '-' '._ _.' '-' '._ _.' '-' '._ _.' '-' '._ _.' '-' '._
- (.-./`-´\.-.)(.-./`-´\.-.)(.-./`-´\.-.)(.-./`-´\.-.)(.-./`-´\.-.)
- `-´ `-´ `-´ `-´ `-´ `-´ `-´ `-´ `-´ `-´
-
- Commit rejected.
-
- Type message check failed ==> test commit message check
- It doesn't match regex: ^(chore|ci|docs|feat|fix|refactor|style|test){1}(\([\w\-\.]+\))?(!)?: ([\w ])+([\s\S]*)|(Merge).*|(fixup!.*)
- The commit message should follow Conventional Commits. See https://www.conventionalcommits.org
- Suggest: Use (): with allowed types
-
-**Branch Name Validation Failure:**
-
-.. code-block:: text
-
- Commit rejected by Commit-Check.
-
- (c).-.(c) (c).-.(c) (c).-.(c) (c).-.(c) (c).-.(c)
- / ._. \ / ._. \ / ._. \ / ._. \ / ._. \
- __\( C )/__ __\( H )/__ __\( E )/__ __\( C )/__ __\( K )/__
- (_.-/'-'\-._)(_.-/'-'\-._)(_.-/'-'\-._)(_.-/'-'\-._)(_.-/'-'\-._)
- || E || || R || || R || || O || || R ||
- _.' '-' '._ _.' '-' '._ _.' '-' '._ _.' '-' '._ _.' '-' '._
- (.-./`-´\.-.)(.-./`-´\.-.)(.-./`-´\.-.)(.-./`-´\.-.)(.-./`-´\.-.)
- `-´ `-´ `-´ `-´ `-´ `-´ `-´ `-´ `-´ `-´
-
- Commit rejected.
-
- Type branch check failed ==> test-branch
- It doesn't match regex: ^(feature|bugfix|hotfix|release|chore|feat|fix)\/.+|(master)|(main)|(HEAD)|(PR-.+)
- The branch should follow Conventional Branch. See https://conventionalbranch.org
- Suggest: Use / with allowed types or ignore_authors in config branch section to bypass
-
-**Commit Signature Validation Failure:**
-
-.. code-block:: text
-
- Commit rejected by Commit-Check.
-
- (c).-.(c) (c).-.(c) (c).-.(c) (c).-.(c) (c).-.(c)
- / ._. \ / ._. \ / ._. \ / ._. \ / ._. \
- __\( C )/__ __\( H )/__ __\( E )/__ __\( C )/__ __\( K )/__
- (_.-/'-'\-._)(_.-/'-'\-._)(_.-/'-'\-._)(_.-/'-'\-._)(_.-/'-'\-._)
- || E || || R || || R || || O || || R ||
- _.' '-' '._ _.' '-' '._ _.' '-' '._ _.' '-' '._ _.' '-' '._
- (.-./`-´\.-.)(.-./`-´\.-.)(.-./`-´\.-.)(.-./`-´\.-.)(.-./`-´\.-.)
- `-´ `-´ `-´ `-´ `-´ `-´ `-´ `-´ `-´ `-´
-
- Commit rejected.
-
- Type require_signed_off_by check failed ==> fix: add missing file
- It doesn't match regex: Signed-off-by:.*[A-Za-z0-9]\s+<.+@.+>
- Signed-off-by not found in latest commit
- Suggest: git commit --amend --signoff or use --signoff on commit
-
-**Commit Message Validation Failure without ASCII Banner (``--no-banner``):**
-
-.. code-block:: text
-
- Type message check failed ==> test commit message check
- It doesn't match regex: ^(chore|ci|docs|feat|fix|refactor|style|test){1}(\([\w\-\.]+\))?(!)?: ([\w ])+([\s\S]*)|(Merge).*|(fixup!.*)
- The commit message should follow Conventional Commits. See https://www.conventionalcommits.org
- Suggest: Use (): with allowed types
-
-**Compact Failure Output (``--compact``):**
-
-.. code-block:: text
-
- [FAIL] message: test commit message check
-
-**Imperative Mood Validation Failure:**
-
-.. code-block:: text
-
- Commit rejected by Commit-Check.
-
- (c).-.(c) (c).-.(c) (c).-.(c) (c).-.(c) (c).-.(c)
- / ._. \ / ._. \ / ._. \ / ._. \ / ._. \
- __\( C )/__ __\( H )/__ __\( E )/__ __\( C )/__ __\( K )/__
- (_.-/'-'\-._)(_.-/'-'\-._)(_.-/'-'\-._)(_.-/'-'\-._)(_.-/'-'\-._)
- || E || || R || || R || || O || || R ||
- _.' '-' '._ _.' '-' '._ _.' '-' '._ _.' '-' '._ _.' '-' '._
- (.-./`-´\.-.)(.-./`-´\.-.)(.-./`-´\.-.)(.-./`-´\.-.)(.-./`-´\.-.)
- `-´ `-´ `-´ `-´ `-´ `-´ `-´ `-´ `-´ `-´
-
- Commit rejected.
-
- Type imperative check failed ==> fix: added missing file
- It doesn't match regex:
- Commit message should use imperative mood (e.g., 'Add feature' not 'Added feature')
- Suggest: Use imperative mood in the subject line
-
-
-Integration Tips
-----------------
-
-CI/CD Integration
-~~~~~~~~~~~~~~~~~
-
-You can use commit-check in CI/CD pipelines:
-
-.. code-block:: bash
-
- # In your CI script
- git log --format="%s" -n 1 | commit-check -m
-
- # or just
- commit-check -m
-
- # Keep plain-text output but remove the ASCII art banner
- git log --format="%s" -n 1 | commit-check -m --no-banner
-
- # Emit one machine-friendly line per failure without switching to JSON
- git log --format="%s" -n 1 | commit-check -m --compact
-
-Scripting
-~~~~~~~~~
-
-Use commit-check in scripts to validate commit messages programmatically:
-
-.. code-block:: bash
-
- #!/bin/bash
- # validate-commits.sh
-
- # Get all commit messages from last 10 commits
- for i in {0..9}; do
- msg=$(git log --format="%s" -n 1 --skip=$i)
- if [ -n "$msg" ]; then
- echo "Validating: $msg"
- echo "$msg" | commit-check -m || exit 1
- fi
- done
-
- echo "All commits are valid!"
-
-For more configuration options, see the `Configuration Documentation `_.
diff --git a/docs/getting-started/installation.md b/docs/getting-started/installation.md
new file mode 100644
index 00000000..c915fc6a
--- /dev/null
+++ b/docs/getting-started/installation.md
@@ -0,0 +1,78 @@
+# Installation
+
+Commit Check runs anywhere Python does, and ships as a GitHub Action and an MCP
+server for the places it doesn't.
+
+## Command line
+
+=== "pip"
+
+ ```console
+ $ pip install commit-check
+ ```
+
+=== "uv"
+
+ ```console
+ $ uv tool install commit-check
+ ```
+
+=== "pipx"
+
+ ```console
+ $ pipx install commit-check
+ ```
+
+Verify the install:
+
+```console
+$ commit-check --version
+```
+
+The CLI is also available as `cchk`, which is the same program under a shorter
+name.
+
+!!! tip "Supported Python versions"
+
+ Commit Check supports Python 3.10 through 3.14, on Linux, macOS and Windows.
+
+## As a pre-commit hook
+
+No installation step — [pre-commit](https://pre-commit.com) fetches it for you.
+See the [pre-commit guide](../guides/pre-commit.md).
+
+## As a GitHub Action
+
+No installation step. See the
+[GitHub Actions guide](../guides/github-actions.md).
+
+## Verifying the download
+
+Releases are built with [SLSA Level 3](https://slsa.dev) provenance. To verify a
+release artifact came from this repository's build pipeline:
+
+```console
+$ gh attestation verify commit_check-*.whl --repo commit-check/commit-check
+```
+
+## Next steps
+
+
+
+- :material-rocket-launch-outline:{ .lg .middle } __Quick start__
+
+ ---
+
+ Catch your first bad commit in five minutes.
+
+ [:octicons-arrow-right-24: Quick start](quickstart.md)
+
+- :material-book-open-variant:{ .lg .middle } __Rules reference__
+
+ ---
+
+ Every rule, what it does, and why it matters.
+
+ [:octicons-arrow-right-24: Rules](../rules.md)
+
+
diff --git a/docs/getting-started/quickstart.md b/docs/getting-started/quickstart.md
new file mode 100644
index 00000000..11eef029
--- /dev/null
+++ b/docs/getting-started/quickstart.md
@@ -0,0 +1,148 @@
+# Quick start
+
+By the end of this page you will have Commit Check rejecting a bad commit
+message on your machine, and you will understand what it is telling you.
+
+It takes about five minutes and needs nothing but a Git repository.
+
+## 1. Install
+
+```console
+$ pip install commit-check
+```
+
+## 2. Watch it reject something
+
+Commit Check works with no configuration at all. Make a deliberately bad commit
+in a scratch repository:
+
+```console
+$ git init demo && cd demo
+$ git commit --allow-empty -m "updated the parser"
+```
+
+Now check it:
+
+```console
+$ commit-check --message
+```
+
+```text
+CC001 message check failed ==> updated the parser
+The commit message should follow Conventional Commits. See https://www.conventionalcommits.org
+Suggest: Use (): , where is one of: feat, fix, docs, ...
+Docs: https://docs.commit-check.com/rules/#cc001
+```
+
+Four things are happening in that output, and each is deliberate:
+
+| Part | What it gives you |
+|---|---|
+| `CC001` | A stable rule ID. It will mean the same thing in five years. |
+| `==> updated the parser` | The exact value that failed, not just "invalid message". |
+| `Suggest:` | What to do about it. |
+| `Docs:` | Why the rule exists, and how to configure or disable it. |
+
+## 3. Fix it
+
+```console
+$ git commit --amend -m "fix(parser): handle empty input"
+$ commit-check --message
+```
+
+No output and an exit code of `0`. Commit Check is quiet when it is happy.
+
+## 4. Check the branch too
+
+```console
+$ git switch -c my-changes
+$ commit-check --branch
+```
+
+```text
+CC201 branch check failed ==> my-changes
+The branch should follow Conventional Branch. See https://conventionalbranch.org
+Suggest: Use / with allowed types
+Docs: https://docs.commit-check.com/rules/#cc201
+```
+
+Rename it to something structured and it passes:
+
+```console
+$ git branch -m fix/empty-input
+$ commit-check --branch
+```
+
+!!! tip "Checks are opt-in per run"
+
+ `commit-check --message` never evaluates branch rules, and vice versa. Each
+ check is selected by its own flag, so you can run exactly what a given hook
+ or CI job needs. The
+ [rules reference](../rules.md) lists which flag activates each rule.
+
+## 5. Write down your policy
+
+So far you have been running the defaults. Create a `cchk.toml` in the
+repository root — or in `.github/` — to make the policy explicit:
+
+```toml title="cchk.toml"
+[commit]
+conventional_commits = true
+subject_imperative = true # (1)!
+subject_max_length = 72
+allow_wip_commits = false # (2)!
+
+[branch]
+conventional_branch = true
+```
+
+1. Off by default. Turning it on rejects `fixed a bug` in favour of `fix a bug`.
+2. `allow_*` options describe what is *permitted*. Set to `false` to enforce.
+
+Run it again and the new rules apply:
+
+```console
+$ commit-check --message
+```
+
+!!! warning "Defaults are not "nothing""
+
+ Even with no config file, Conventional Commits, Conventional Branch, subject
+ length limits of 5–80 characters, and author name/email patterns are
+ enforced. Check the *Default* column in the
+ [rules reference](../rules.md) before assuming a rule is off.
+
+## 6. Make it automatic
+
+Running the command by hand does not scale. Wire it into the two places it
+belongs:
+
+
+
+- :material-git:{ .lg .middle } __Before the commit lands__
+
+ ---
+
+ A pre-commit hook rejects the message as you write it, so nothing bad
+ reaches the branch in the first place.
+
+ [:octicons-arrow-right-24: Pre-commit guide](../guides/pre-commit.md)
+
+- :material-github:{ .lg .middle } __On every pull request__
+
+ ---
+
+ A GitHub Action checks every commit in the PR and can comment on the PR
+ with what needs fixing.
+
+ [:octicons-arrow-right-24: GitHub Actions guide](../guides/github-actions.md)
+
+
+
+## Where to go next
+
+- **[Rules reference](../rules.md)** — every rule, what it does, why it matters,
+ and how to configure it.
+- **[Configuration](../configuration.md)** — every option, its type and default,
+ plus the environment variable and CLI flag that override it.
+- **[Why Commit Check](why.md)** — the reasoning behind the tool.
diff --git a/docs/getting-started/why.md b/docs/getting-started/why.md
new file mode 100644
index 00000000..f1143126
--- /dev/null
+++ b/docs/getting-started/why.md
@@ -0,0 +1,56 @@
+# Why Commit Check
+
+## The problem
+
+Git history is a database that every team writes to and almost nobody validates.
+
+The cost shows up later, and indirectly. Release notes get written by hand
+because commit subjects cannot be grouped. `git bisect` walks through merge
+commits that record nothing but a sync. A commit is attributed to `ec2-user`
+because a build box had no `user.name`. A contribution has to be rejected
+months after the fact because it never carried a `Signed-off-by` trailer.
+
+None of these are caught by a linter, a type checker, or a test suite. They are
+all caught by review, which means they are caught inconsistently, by whoever
+happens to be looking, and only after the work is done.
+
+## The approach
+
+Commit Check treats commit metadata the way linters treat code: a policy written
+down once, enforced identically everywhere, with a stable identifier for every
+diagnostic so that findings can be discussed, suppressed, and tracked.
+
+**One config.** A single `cchk.toml` drives the CLI, the pre-commit hook, the
+GitHub Action, and the MCP server. There is no second place where the rules can
+disagree with themselves.
+
+**Fails where it is cheap.** The same check that runs in CI runs in your
+`commit-msg` hook. Finding out that a subject is malformed takes a second
+locally and a full CI cycle plus a force-push remotely.
+
+**Stable rule IDs.** Every rule has an ID like [CC003](../rules.md#cc003) that
+never changes once released. You can cite it in a review comment, link to its
+documentation, and eventually suppress it per-rule.
+
+**Explains itself.** A failure names the rule, quotes the offending value, says
+how to fix it, and links to the reasoning.
+
+## Where it fits
+
+Commit Check is deliberately narrow: it validates *metadata*, not code. It is a
+lightweight, open alternative to
+[GitHub Enterprise metadata restrictions](https://docs.github.com/en/enterprise-server@3.11/repositories/configuring-branches-and-merges-in-your-repository/managing-rulesets/available-rules-for-rulesets#metadata-restrictions)
+and Bitbucket's paid
+[Yet Another Commit Checker](https://marketplace.atlassian.com/apps/1211854/yet-another-commit-checker),
+without requiring a particular forge or an enterprise plan.
+
+If you already run `ruff`, `eslint`, or `golangci-lint` on your source, Commit
+Check is the equivalent for the commits that carry it.
+
+## What it is not
+
+- **Not a code linter.** It never reads your source files.
+- **Not a replacement for review.** It enforces the mechanical rules so review
+ can spend its attention on the change itself.
+- **Not opinionated by default.** Most rules are off until you turn them on. See
+ the [rules reference](../rules.md) for what applies out of the box.
diff --git a/docs/guides/ai-attribution.md b/docs/guides/ai-attribution.md
new file mode 100644
index 00000000..3f627e16
--- /dev/null
+++ b/docs/guides/ai-attribution.md
@@ -0,0 +1,75 @@
+# Set an AI attribution policy
+
+AI coding tools add trailers to commit messages identifying themselves. Whether
+that is welcome, required, or unacceptable is a decision each project makes for
+itself — and the industry has landed in different places:
+
+- The **Linux kernel** added an `Assisted-by:` trailer, treating AI assistance
+ as something to disclose.
+- **Some projects disallow AI-assisted contributions outright**, usually over
+ provenance and licensing.
+- **Most projects have no stated position**, which means the question resurfaces
+ in every code review.
+
+Commit Check does not take a side. It gives you a way to enforce whichever
+position your project has already taken, so it stops being relitigated.
+
+## The default: no opinion
+
+```toml
+[commit]
+ai_attribution = "ignore" # the default
+```
+
+[CC013](../rules.md#cc013) is off. Commits carrying AI trailers pass, and so do
+commits without them.
+
+## Forbidding AI-attributed commits
+
+```toml title="cchk.toml"
+[commit]
+ai_attribution = "forbid"
+```
+
+Commits carrying a recognised AI signature now fail:
+
+```text
+CC013 ai_attribution check failed ==> feat: add caching layer
+AI attribution policy violation
+Suggest: This project forbids AI-assisted commits. Remove AI trailers and re-commit.
+Docs: https://docs.commit-check.com/rules/#cc013
+```
+
+### What counts as a signature
+
+Trailers and co-author lines naming Claude Code, GitHub Copilot, Codex, Gemini,
+Cursor, Devin, Aider, Windsurf and Tabby, plus generic AI model patterns.
+
+!!! warning "This checks disclosure, not authorship"
+
+ CC013 reads commit metadata. It detects a commit that *says* it was
+ AI-assisted; it cannot detect one that was AI-assisted and did not say so.
+
+ Set against a policy of "no AI contributions", it is an honesty check on
+ contributors who are already following the rules — not an enforcement
+ mechanism against those who aren't. Be clear with yourself about which of
+ those you are buying.
+
+## Exempting automation
+
+Bots that legitimately carry AI trailers can be excluded:
+
+```toml title="cchk.toml"
+[commit]
+ai_attribution = "forbid"
+ignore_authors = ["dependabot[bot]", "renovate[bot]"]
+```
+
+## Documenting the decision
+
+Whichever way you go, the config file is not where contributors look. State the
+policy where they will see it — `CONTRIBUTING.md`, the pull request template —
+and let Commit Check be the mechanism rather than the announcement.
+
+Enforcing an undocumented policy produces a confusing failure for somebody
+acting in good faith.
diff --git a/docs/guides/github-actions.md b/docs/guides/github-actions.md
new file mode 100644
index 00000000..23c9a9e0
--- /dev/null
+++ b/docs/guides/github-actions.md
@@ -0,0 +1,81 @@
+# Run in GitHub Actions
+
+Local hooks can be skipped with `--no-verify`. A CI check cannot, which makes
+GitHub Actions the place where your policy is actually a policy.
+
+## Minimal setup
+
+```yaml title=".github/workflows/commit-check.yml"
+name: Commit Check
+
+on:
+ push:
+ pull_request:
+ branches: [main]
+
+jobs:
+ commit-check:
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read
+ steps:
+ - uses: actions/checkout@v5
+ with:
+ ref: ${{ github.event.pull_request.head.sha }}
+ fetch-depth: 0 # (1)!
+ - uses: commit-check/commit-check-action@v1
+ with:
+ message: true
+ branch: true
+ author-name: true
+ author-email: true
+```
+
+1. Commit Check needs the full history to inspect every commit in the pull
+ request. Without this it only sees the most recent one.
+
+## Commenting on the pull request
+
+Instead of making contributors open the job log, have the Action post what
+needs fixing directly on the PR:
+
+```yaml
+ - uses: commit-check/commit-check-action@v1
+ with:
+ message: true
+ branch: true
+ pr-comments: ${{ github.event_name == 'pull_request' }}
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+```
+
+This needs extra permissions on the job:
+
+```yaml
+ permissions:
+ contents: read
+ pull-requests: write
+```
+
+## Reporting without failing
+
+While a team is adopting the policy, it is often better to report problems
+without blocking merges. `dry-run` always exits `0`:
+
+```yaml
+ - uses: commit-check/commit-check-action@v1
+ with:
+ message: true
+ dry-run: true
+```
+
+Turn it off once the history is clean.
+
+## Sharing config with local hooks
+
+The Action reads the same `cchk.toml` as the CLI, so a repository that already
+has one needs no Action-specific configuration. That is the point: the rules
+cannot drift between what a developer sees locally and what CI enforces.
+
+See [Configuration](../configuration.md) for where the file may live, and
+[Organization-wide policy](organization.md) for sharing one across repositories.
diff --git a/docs/guides/organization.md b/docs/guides/organization.md
new file mode 100644
index 00000000..38b69956
--- /dev/null
+++ b/docs/guides/organization.md
@@ -0,0 +1,100 @@
+# Enforce one policy across an organization
+
+Copying `cchk.toml` into forty repositories works until the day you want to
+change it. `inherit_from` lets each repository pull a shared base config and
+override only what it genuinely needs.
+
+## The shared config
+
+Put the policy in a repository every project can read — GitHub's `.github`
+repository is the conventional home:
+
+```toml title="my-org/.github → cchk.toml"
+[commit]
+conventional_commits = true
+subject_imperative = true
+subject_max_length = 72
+allow_merge_commits = false
+
+[branch]
+conventional_branch = true
+allow_branch_types = ["feature", "bugfix", "hotfix", "release", "chore"]
+```
+
+## Inheriting it
+
+Each repository then needs three lines:
+
+```toml title="any-repo → .github/cchk.toml"
+inherit_from = "github:my-org/.github:cchk.toml"
+```
+
+Local settings win, so a project with a different constraint overrides just that
+one option:
+
+```toml title="a-repo-with-longer-subjects → .github/cchk.toml"
+inherit_from = "github:my-org/.github:cchk.toml"
+
+[commit]
+subject_max_length = 100 # everything else comes from the org config
+```
+
+## Pinning the version
+
+By default the shorthand resolves to the parent repository's default branch,
+which means a change to the org config takes effect everywhere on the next run.
+That is usually what you want. When it isn't, pin to a ref:
+
+```toml
+inherit_from = "github:my-org/.github@v1:cchk.toml"
+```
+
+## Other sources
+
+=== "GitHub shorthand"
+
+ ```toml
+ inherit_from = "github:my-org/.github:cchk.toml"
+ ```
+
+=== "Local path"
+
+ ```toml
+ inherit_from = "../../shared/org-cchk.toml"
+ ```
+
+ Useful in a monorepo, where the shared config is already checked out.
+
+=== "HTTPS URL"
+
+ ```toml
+ inherit_from = "https://example.com/shared/cchk.toml"
+ ```
+
+ Plain HTTP is rejected.
+
+!!! warning "Inheritance fails open"
+
+ If the parent config is unreachable — a network blip, a renamed file, a
+ private repository — Commit Check silently falls back to the local config
+ rather than failing the build. This keeps CI green during an outage, but it
+ also means a typo in `inherit_from` is easy to miss. Verify the merged
+ result when you first set it up:
+
+ ```console
+ $ commit-check --message --format json
+ ```
+
+## Rolling it out
+
+Turning on a strict policy across an organization at once produces a wall of
+red. A gentler sequence:
+
+1. Ship the org config with `dry-run` enabled in CI, so violations are reported
+ but nothing blocks.
+2. Look at what actually fails. Some rules will turn out to be wrong for some
+ teams — that is information, not an obstacle.
+3. Turn off `dry-run` for repositories whose history is already clean.
+4. Tighten the shared config over time.
+
+See the [GitHub Actions guide](github-actions.md) for the `dry-run` input.
diff --git a/docs/guides/pre-commit.md b/docs/guides/pre-commit.md
new file mode 100644
index 00000000..193f0f97
--- /dev/null
+++ b/docs/guides/pre-commit.md
@@ -0,0 +1,89 @@
+# Run as a pre-commit hook
+
+A pre-commit hook is the cheapest place to enforce commit policy: the developer
+finds out while they are still writing the message, not after a CI round trip.
+
+## Setup
+
+Add Commit Check to `.pre-commit-config.yaml`:
+
+```yaml title=".pre-commit-config.yaml"
+repos:
+ - repo: https://github.com/commit-check/commit-check
+ rev: v2.11.0
+ hooks:
+ - id: check-message
+ - id: check-branch
+ - id: check-author-name
+ - id: check-author-email
+```
+
+Then install the hooks. `check-message` runs at the `commit-msg` stage, so it
+needs its own install step:
+
+```console
+$ pre-commit install --hook-type commit-msg
+$ pre-commit install
+```
+
+That is it. The next malformed commit message is rejected before it exists.
+
+## Available hooks
+
+| Hook ID | Stage | Rules |
+|---|---|---|
+| `check-message` | `commit-msg` | [CC001–CC013](../rules.md#commit-message-rules) |
+| `check-branch` | `pre-commit` | [CC201–CC202](../rules.md#branch-rules) |
+| `check-author-name` | `pre-commit` | [CC101](../rules.md#cc101) |
+| `check-author-email` | `pre-commit` | [CC102](../rules.md#cc102) |
+| `check-no-force-push` | `pre-push` | [CC301](../rules.md#cc301) |
+
+`check-no-force-push` also needs its own install:
+
+```console
+$ pre-commit install --hook-type pre-push
+```
+
+## Configuring without a TOML file
+
+Options can be passed as hook arguments, which keeps everything in one file:
+
+```yaml title=".pre-commit-config.yaml"
+repos:
+ - repo: https://github.com/commit-check/commit-check
+ rev: v2.11.0
+ hooks:
+ - id: check-message
+ args:
+ - --subject-imperative=true
+ - --subject-max-length=72
+ - --allow-merge-commits=false
+```
+
+A `cchk.toml` is usually the better choice once you have more than a couple of
+options, because CI and the CLI read it too. See
+[Configuration](../configuration.md) for the precedence rules.
+
+## Skipping a hook
+
+Occasionally you need to get a commit through — a mid-rebase fixup, an
+automated migration. `pre-commit` supports this natively:
+
+```console
+$ SKIP=check-message git commit -m "wip"
+```
+
+!!! warning "Local hooks are not a policy boundary"
+
+ Anyone can pass `--no-verify`. Hooks exist to give fast feedback to people
+ who want to follow the policy, not to stop people who don't. Pair them with
+ the [GitHub Action](github-actions.md), which runs where it cannot be
+ skipped.
+
+## Troubleshooting
+
+If `check-message` never seems to run, it is almost always because
+`pre-commit install --hook-type commit-msg` was not run — a plain
+`pre-commit install` only wires up the `pre-commit` stage.
+
+More in [Troubleshooting](../troubleshoot.md).
diff --git a/docs/guides/signoff.md b/docs/guides/signoff.md
new file mode 100644
index 00000000..578c37fb
--- /dev/null
+++ b/docs/guides/signoff.md
@@ -0,0 +1,81 @@
+# Require signoff (DCO)
+
+Projects that use the [Developer Certificate of Origin](https://developercertificate.org/)
+require every commit to carry a `Signed-off-by` trailer. The Linux kernel and
+much of the CNCF work this way.
+
+A DCO bot rejecting a pull request after the fact is a poor experience: the
+contributor has to rewrite history for every commit in the branch. Checking
+locally fixes it before it becomes a problem.
+
+## Turn it on
+
+```toml title="cchk.toml"
+[commit]
+require_signed_off_by = true
+```
+
+This enables [CC012](../rules.md#cc012), which is off by default.
+
+## Signing off
+
+```console
+$ git commit --signoff -m "fix: handle an empty config file"
+```
+
+The trailer is appended automatically from your `user.name` and `user.email`:
+
+```text
+fix: handle an empty config file
+
+Signed-off-by: Your Name
+```
+
+Forgot it? Fix the last commit in place:
+
+```console
+$ git commit --amend --signoff --no-edit
+```
+
+Fix a whole branch:
+
+```console
+$ git rebase --signoff main
+```
+
+!!! tip "Make it automatic"
+
+ Signing off is easy to forget. Combine this rule with the
+ [pre-commit hook](pre-commit.md) so a missing trailer is caught at commit
+ time, not at review time.
+
+## Identity matters
+
+The DCO is a statement about who wrote the code, so it only means something if
+the identity is real. [CC101](../rules.md#cc101) and
+[CC102](../rules.md#cc102) check the committer name and email, and are enabled
+by default when their check runs:
+
+```console
+$ commit-check --author-name --author-email
+```
+
+To require a company address:
+
+```toml title="cchk.toml"
+[commit]
+author_email_pattern = "^.+@example\\.com$"
+```
+
+## Bots
+
+Automation cannot meaningfully sign the DCO, and forcing it to produces
+meaningless trailers. Exempt bots instead:
+
+```toml title="cchk.toml"
+[commit]
+require_signed_off_by = true
+ignore_authors = ["dependabot[bot]", "renovate[bot]"]
+```
+
+`ignore_authors` matches the commit author and any `Co-authored-by:` trailers.
diff --git a/docs/index.md b/docs/index.md
index b7b74a4f..6be368cc 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -1,31 +1,168 @@
-```{include} ../README.md
-```
+---
+title: Commit Check
+description: Enforce commit message, branch naming, author and signoff standards across your CLI, pre-commit hooks, CI, and AI agents.
+hide:
+ - navigation
+ - toc
+---
-```{toctree}
-:hidden:
-:caption: Getting started
-self
-what-is-new
-example
-```
+
-```{toctree}
-:hidden:
-:caption: Configuring
-configuration
-migration
-```
+# Clean commits. Clear standards.
-```{toctree}
-:hidden:
-:caption: Reference
-rules
-cli_args
-```
+Commit Check enforces the rules your Git history already depends on — commit
+messages, branch names, committer identity, signoff — from one config, in every
+place your team writes code.
+
+[Get started :octicons-arrow-right-24:](getting-started/quickstart.md){ .md-button .md-button--primary }
+[Browse the rules](rules.md){ .md-button }
+
+
+
+---
+
+## One config, enforced everywhere
+
+Write the policy once. The same rules run on a developer's laptop, in CI, and in
+whatever your AI agent is committing on your behalf.
+
+=== "Command line"
+
+ ```console
+ $ commit-check --message --branch
+ CC003 subject_imperative check failed ==> docs: revamped the profile
+ Commit message should use imperative mood (e.g., 'fix bug' not 'fixed bug')
+ Suggest: Change the first verb to imperative form
+ Docs: https://docs.commit-check.com/rules/#cc003
+ ```
+
+=== "pre-commit"
+
+ ```yaml title=".pre-commit-config.yaml"
+ repos:
+ - repo: https://github.com/commit-check/commit-check
+ rev: v2.11.0
+ hooks:
+ - id: check-message
+ - id: check-branch
+ - id: check-author-email
+ ```
+
+=== "GitHub Actions"
+
+ ```yaml title=".github/workflows/commit-check.yml"
+ - uses: commit-check/commit-check-action@v1
+ with:
+ message: true
+ branch: true
+ pr-comments: ${{ github.event_name == 'pull_request' }}
+ ```
+
+=== "AI agents"
+
+ ```json title="MCP server"
+ {
+ "mcpServers": {
+ "commit-check": { "command": "commit-check-mcp" }
+ }
+ }
+ ```
+
+## What it checks
+
+
+
+- :material-message-text-outline:{ .lg .middle } __Commit messages__
+
+ ---
+
+ Conventional Commits by default, or your own pattern. Subject length, mood,
+ capitalisation, required body, forbidden merge/fixup/WIP commits.
+
+ [:octicons-arrow-right-24: CC001–CC013](rules.md#commit-message-rules)
+
+- :material-source-branch:{ .lg .middle } __Branch names__
+
+ ---
+
+ Conventional Branch naming, plus rebase checks that catch a branch drifting
+ behind its target before CI wastes a run on stale code.
+
+ [:octicons-arrow-right-24: CC201–CC202](rules.md#branch-rules)
+
+- :material-account-check-outline:{ .lg .middle } __Committer identity__
+
+ ---
+
+ Catch commits authored by `ec2-user` on a build box, or require everyone to
+ contribute from a company address.
-```{toctree}
-:hidden:
-:caption: About
-troubleshoot
-changelog
+ [:octicons-arrow-right-24: CC101–CC102](rules.md#author-rules)
+
+- :material-file-sign:{ .lg .middle } __Signoff and DCO__
+
+ ---
+
+ Require the `Signed-off-by` trailer locally, so contributors find out before
+ CI rejects the pull request.
+
+ [:octicons-arrow-right-24: Signoff guide](guides/signoff.md)
+
+- :material-robot-outline:{ .lg .middle } __AI attribution__
+
+ ---
+
+ Whatever your project has decided about AI-assisted commits, enforce it
+ mechanically instead of relitigating it in review.
+
+ [:octicons-arrow-right-24: AI attribution guide](guides/ai-attribution.md)
+
+- :material-office-building-outline:{ .lg .middle } __Org-wide policy__
+
+ ---
+
+ Inherit a base config from a shared repository, then let each project
+ override only what it needs.
+
+ [:octicons-arrow-right-24: Organization guide](guides/organization.md)
+
+
+
+## Built to be trusted
+
+
+
+- :material-shield-check:{ .lg .middle } __SLSA Level 3__
+
+ ---
+
+ Build provenance with artifact attestation verified at install time.
+
+- :material-tag-outline:{ .lg .middle } __Stable rule IDs__
+
+ ---
+
+ Every diagnostic carries an ID like `CC003` that never changes, so you can
+ cite it in review, suppress it, or feed it to tooling.
+
+- :material-source-commit:{ .lg .middle } __Used in production__
+
+ ---
+
+ Running at Apache, Texas Instruments, Mila, and
+ [many more](https://github.com/commit-check/commit-check-action/network/dependents).
+
+