commit-check 2.18.0__py3-none-any.whl → 2.18.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
commit_check/api.py CHANGED
@@ -206,7 +206,8 @@ def validate_push(
206
206
 
207
207
  :param push_refs: Push ref information in the format produced by git's
208
208
  pre-push hook: ``<local ref> <local sha1> <remote ref> <remote sha1>``,
209
- one entry per line. If *None*, the check is skipped (returns pass).
209
+ one entry per line. If *None* or empty, or no line names a ref,
210
+ there is nothing to compare and the check reports ``"skip"``.
210
211
  :param config: Optional configuration override dict. The push check is
211
212
  always enabled when calling this function; a detected force push fails
212
213
  the result unless ``no_force_push`` is listed under ``warn``, in which
@@ -221,6 +222,8 @@ def validate_push(
221
222
  >>> result = validate_push(f"refs/heads/main abc123 refs/heads/main {zero}")
222
223
  >>> result["status"]
223
224
  'pass'
225
+ >>> validate_push(None)["status"]
226
+ 'skip'
224
227
  """
225
228
  cfg = _merge_config(config)
226
229
  # Enable force push blocking in the config so the rule is built
commit_check/engine.py CHANGED
@@ -93,6 +93,14 @@ class ValidationContext:
93
93
  files_cache: dict[str, list[tuple[str, int]]] = field(
94
94
  default_factory=dict, repr=False, compare=False
95
95
  )
96
+ # Set when stdin_text is a piped commit message: checks with a value of
97
+ # their own (branch, author, tags, push refs) then read git instead.
98
+ stdin_is_message: bool = False
99
+
100
+ @property
101
+ def piped_value(self) -> str | None:
102
+ """stdin_text as a check's own value, or None when it is the message."""
103
+ return None if self.stdin_is_message else self.stdin_text
96
104
 
97
105
 
98
106
  @dataclass
@@ -334,14 +342,14 @@ class BaseValidator(ABC):
334
342
  Determine if branch validation should be skipped.
335
343
 
336
344
  Skip if the current author is in the ignore_authors list for branches,
337
- or if no stdin_text and no commits exist.
345
+ or if no branch name was supplied and no commits exist.
338
346
  """
339
347
  ignore_authors = context.config.get("branch", {}).get("ignore_authors", [])
340
348
  if ignore_authors:
341
349
  current_author = self._resolve_current_author(context)
342
350
  if current_author and current_author in ignore_authors:
343
351
  return True
344
- return context.stdin_text is None and not has_commits()
352
+ return context.piped_value is None and not has_commits()
345
353
 
346
354
  def _print_failure(
347
355
  self,
@@ -614,8 +622,9 @@ class AuthorValidator(BaseValidator):
614
622
  Checks git config first (for pre-commit validation of the configured identity),
615
623
  then falls back to the last commit's author info.
616
624
  """
617
- if context.stdin_text is not None:
618
- return context.stdin_text.strip()
625
+ supplied = context.piped_value
626
+ if supplied is not None:
627
+ return supplied.strip()
619
628
 
620
629
  git_config_map = {
621
630
  "author_name": "user.name",
@@ -669,11 +678,8 @@ class BranchValidator(BaseValidator):
669
678
  def validate(self, context: ValidationContext) -> ValidationResult:
670
679
  if self._should_skip_branch_validation(context):
671
680
  return ValidationResult.SKIP
672
- branch_name = (
673
- context.stdin_text.strip()
674
- if context.stdin_text is not None
675
- else get_branch_name()
676
- )
681
+ supplied = context.piped_value
682
+ branch_name = supplied.strip() if supplied is not None else get_branch_name()
677
683
  self._checked_value = branch_name
678
684
 
679
685
  if not self.rule.regex:
@@ -730,8 +736,9 @@ class TagValidator(BaseValidator):
730
736
  return lines
731
737
 
732
738
  def validate(self, context: ValidationContext) -> ValidationResult:
733
- if context.stdin_text is not None:
734
- tags = self._tags_from_stdin(context.stdin_text)
739
+ supplied = context.piped_value
740
+ if supplied is not None:
741
+ tags = self._tags_from_stdin(supplied)
735
742
  else:
736
743
  tags = get_tags_at(context.rev or "HEAD")
737
744
 
@@ -814,8 +821,9 @@ class FilesValidator(BaseValidator):
814
821
  or commits the remote already has — which the caller reports as a
815
822
  skip rather than a pass.
816
823
  """
817
- if context.stdin_text is not None:
818
- revs = self._push_revs_from_stdin(context.stdin_text)
824
+ supplied = context.piped_value
825
+ if supplied is not None:
826
+ revs = self._push_revs_from_stdin(supplied)
819
827
  if revs is not None:
820
828
  return revs or None
821
829
  return [context.rev or "HEAD"]
@@ -1108,6 +1116,10 @@ class ForcePushValidator(BaseValidator):
1108
1116
 
1109
1117
  A force push is detected when the remote SHA is not an ancestor of the
1110
1118
  local SHA, meaning local history would overwrite the remote.
1119
+
1120
+ With nothing to compare -- no ref lines, only blank or malformed ones, or
1121
+ no upstream to fall back on -- the check reports SKIP rather than PASS:
1122
+ it examined nothing, and a pass would read as a push that was judged safe.
1111
1123
  """
1112
1124
 
1113
1125
  ZERO_SHA = "0000000000000000000000000000000000000000"
@@ -1117,23 +1129,30 @@ class ForcePushValidator(BaseValidator):
1117
1129
  # branch name, stdin_text carries a *list* of refs, and no refs means
1118
1130
  # there is nothing to check either way. So this one stays a truth test
1119
1131
  # while the single-value readers above distinguish "" from None.
1120
- if not context.stdin_text:
1132
+ push_refs = context.piped_value
1133
+ if not push_refs:
1121
1134
  if context.push_upstream_fallback:
1122
1135
  return self._check_current_branch_against_upstream()
1123
- return ValidationResult.PASS
1136
+ return ValidationResult.SKIP
1124
1137
 
1125
- for line in context.stdin_text.splitlines():
1126
- result = self._check_push_line(line.strip())
1127
- if result == ValidationResult.FAIL:
1138
+ checked_any = False
1139
+ for line in push_refs.splitlines():
1140
+ line = line.strip()
1141
+ # A blank or malformed line names no ref, so it judges nothing.
1142
+ if len(line.split()) < 4:
1143
+ continue
1144
+ checked_any = True
1145
+ if self._check_push_line(line) == ValidationResult.FAIL:
1128
1146
  return ValidationResult.FAIL
1129
1147
 
1130
- return ValidationResult.PASS
1148
+ return ValidationResult.PASS if checked_any else ValidationResult.SKIP
1131
1149
 
1132
1150
  def _check_current_branch_against_upstream(self) -> ValidationResult:
1133
1151
  """Check whether pushing HEAD to its upstream would require force."""
1134
1152
  upstream_ref = get_upstream_branch()
1135
1153
  if not upstream_ref:
1136
- return ValidationResult.PASS
1154
+ # No upstream means no remote history to compare HEAD with.
1155
+ return ValidationResult.SKIP
1137
1156
 
1138
1157
  if self._collect_value:
1139
1158
  branch = get_branch_name()
commit_check/main.py CHANGED
@@ -711,6 +711,9 @@ def main() -> int:
711
711
  if not args.message:
712
712
  stdin_content = _resolve_stdin_for_non_message(args, stdin_reader)
713
713
 
714
+ # Next to --message, piped stdin is the message; other checks read git.
715
+ stdin_is_message = args.message and stdin_content is not None
716
+
714
717
  # Reset banner state for this run
715
718
  print_error_header.has_been_called = False
716
719
 
@@ -721,7 +724,9 @@ def main() -> int:
721
724
  config=config_data,
722
725
  no_banner=getattr(args, "no_banner", False),
723
726
  compact=getattr(args, "compact", False),
724
- push_upstream_fallback=args.no_force_push and stdin_content is None,
727
+ push_upstream_fallback=args.no_force_push
728
+ and (stdin_content is None or stdin_is_message),
729
+ stdin_is_message=stdin_is_message,
725
730
  )
726
731
 
727
732
  # Run validation – choose output mode based on --format
@@ -0,0 +1,256 @@
1
+ Metadata-Version: 2.4
2
+ Name: commit-check
3
+ Version: 2.18.2
4
+ Summary: Check commit message formatting, branch naming, commit author, email, and more.
5
+ Author-email: Xianpeng Shen <xianpeng.shen@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: documentation, https://commit-check.com/
8
+ Project-URL: source, https://github.com/commit-check/commit-check
9
+ Project-URL: tracker, https://github.com/commit-check/commit-check/issues
10
+ Keywords: commit conventions,conventional commits,conventional branch,branch naming,commit-check,message,lint message,devops
11
+ Classifier: Development Status :: 5 - Production/Stable
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Natural Language :: English
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Programming Language :: Python :: 3.14
20
+ Classifier: Programming Language :: Python :: 3.15
21
+ Classifier: Topic :: Utilities
22
+ Classifier: Topic :: Software Development :: Build Tools
23
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
24
+ Requires-Python: >=3.10
25
+ Description-Content-Type: text/markdown
26
+ License-File: LICENSE
27
+ Requires-Dist: tomli>=1.1.0; python_version < "3.11"
28
+ Provides-Extra: dev
29
+ Requires-Dist: nox==2026.8.17; extra == "dev"
30
+ Provides-Extra: test
31
+ Requires-Dist: coverage; extra == "test"
32
+ Requires-Dist: pytest; extra == "test"
33
+ Requires-Dist: pytest-mock; extra == "test"
34
+ Requires-Dist: pytest-codspeed; extra == "test"
35
+ Provides-Extra: ci
36
+ Requires-Dist: twine==7.0.0; extra == "ci"
37
+ Dynamic: license-file
38
+
39
+ <div align="center">
40
+
41
+ <picture>
42
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/commit-check/.github/main/branding/banner-dark.png">
43
+ <img src="https://raw.githubusercontent.com/commit-check/.github/main/branding/banner-light.png" alt="Commit Check">
44
+ </picture>
45
+
46
+ **Catch bad commits before they merge.**
47
+
48
+ [![PyPI](https://img.shields.io/pypi/v/commit-check?labelColor=0b1620&logo=pypi&logoColor=white&color=2c9ccd)](https://pypi.org/project/commit-check/)
49
+ [![Downloads](https://img.shields.io/pepy/dt/commit-check?labelColor=0b1620&color=2c9ccd)](https://pepy.tech/projects/commit-check)
50
+ [![CI](https://img.shields.io/github/actions/workflow/status/commit-check/commit-check/main.yml?branch=main&labelColor=0b1620&label=CI)](https://github.com/commit-check/commit-check/actions/workflows/main.yml)
51
+ [![Coverage](https://img.shields.io/codecov/c/github/commit-check/commit-check?labelColor=0b1620&color=2c9ccd&label=coverage)](https://codecov.io/gh/commit-check/commit-check)
52
+ [![OpenSSF Scorecard](https://img.shields.io/ossf-scorecard/github.com/commit-check/commit-check?labelColor=0b1620&color=2c9ccd&label=OpenSSF%20Scorecard)](https://scorecard.dev/viewer/?uri=github.com/commit-check/commit-check)
53
+
54
+ [Docs](https://commit-check.com/getting-started/) ·
55
+ [Rules](https://commit-check.com/rules/) ·
56
+ [GitHub Action](https://github.com/commit-check/commit-check-action) ·
57
+ [GitHub App](https://github.com/apps/commit-check) ·
58
+ [MCP server](https://github.com/commit-check/commit-check-mcp)
59
+
60
+ </div>
61
+
62
+ **Commit Check** validates commit messages, branch names, authors, sign-offs,
63
+ tags, committed files and AI attribution against one `cchk.toml` — in your
64
+ commit hook, in CI, on every pull request and inside your AI agent. When the
65
+ fix is obvious, it hands you the line.
66
+
67
+ ![commit-check demo](https://github.com/commit-check/commit-check/raw/main/assets/demo.gif)
68
+
69
+ ## Quick start
70
+
71
+ ```bash
72
+ pip install commit-check # Python 3.10+
73
+ commit-check --message --branch
74
+ ```
75
+
76
+ No config needed: out of the box it checks [Conventional Commits](https://www.conventionalcommits.org)
77
+ and [Conventional Branch](https://conventionalbranch.org).
78
+
79
+ ### Use with pre-commit
80
+
81
+ Stop bad commits before they exist. In `.pre-commit-config.yaml`:
82
+
83
+ ```yaml
84
+ repos:
85
+ - repo: https://github.com/commit-check/commit-check
86
+ rev: v2.18.1
87
+ hooks:
88
+ - id: check-message
89
+ - id: check-branch
90
+ ```
91
+
92
+ The hooks for the other checks (`check-author-name`, `check-author-email`,
93
+ `check-no-force-push`, `check-tag`, `check-files`) are listed in the
94
+ [pre-commit guide](https://commit-check.com/guides/pre-commit/).
95
+
96
+ ### Everywhere else
97
+
98
+ The same `cchk.toml` drives all of them:
99
+
100
+ | Run it in | How |
101
+ |---|---|
102
+ | GitHub Actions | `uses: commit-check/commit-check-action@v2` — see [commit-check-action](https://github.com/commit-check/commit-check-action) |
103
+ | GitHub, no workflow | [Install the GitHub App](https://github.com/apps/commit-check) — one check run per commit |
104
+ | AI agents | `--format json`, the [Python API](#ai-native-usage), or the [MCP server](https://github.com/commit-check/commit-check-mcp) |
105
+
106
+ ## What it checks
107
+
108
+ | Check | Flag | Rules |
109
+ |---|---|---|
110
+ | Commit message — Conventional Commits, subject length, case and mood, WIP and fixup commits, body, sign-off | `--message` | CC001–CC012 |
111
+ | AI attribution — forbid it, or require an `Assisted-by:` disclosure | `--message` | CC013–CC016 |
112
+ | Author name and email | `--author-name` `--author-email` | CC101–CC102 |
113
+ | Branch name — Conventional Branch, rebased onto a target | `--branch` | CC201–CC202 |
114
+ | Force push | `--no-force-push` | CC301 |
115
+ | Committed files — size, forbidden paths, path length | `--files` | CC302–CC304 |
116
+ | Tag names — SemVer or your own pattern | `--tag` | CC401 |
117
+
118
+ Every rule, with examples: [commit-check.com/rules](https://commit-check.com/rules/)
119
+
120
+ ## Configure
121
+
122
+ Put a `cchk.toml` (or `commit-check.toml`) in the repository root or in `.github/`:
123
+
124
+ ```toml
125
+ [commit]
126
+ subject_max_length = 72
127
+ require_signed_off_by = true
128
+ ai_attribution = "disclose"
129
+ ignore_authors = ["dependabot[bot]", "renovate[bot]"]
130
+
131
+ [branch]
132
+ conventional_branch = true
133
+ ```
134
+
135
+ - **Roll out gently:** `warn = ["CC003"]` reports a rule in full without failing the run.
136
+ - **One policy per organization:** `inherit_from = "github:my-org/.github:cchk.toml"` — see [organization config](https://commit-check.com/guides/organization/).
137
+ - **Override anywhere:** every key has a CLI flag and a `CCHK_*` environment variable.
138
+ - **Editor support:** the schema is on [SchemaStore](https://www.schemastore.org/), so VS Code and JetBrains autocomplete `cchk.toml`.
139
+
140
+ <details>
141
+ <summary><b>Configuration reference</b> — every key, its default, CLI flag and environment variable</summary>
142
+
143
+ Priority: CLI > env > TOML > default. Booleans accept `true/false`, `yes/no`,
144
+ `1/0`; lists are comma-separated on the CLI and in env vars.
145
+
146
+ | Key | Default | CLI flag | Env var | Meaning |
147
+ |-----|---------|----------|---------|---------|
148
+ | `warn` (top level) | `[]` | — | — | Checks or rule IDs to report without failing the run (see "Report a rule without enforcing it") |
149
+ | `commit.conventional_commits` | `true` | `--conventional-commits` | `CCHK_CONVENTIONAL_COMMITS` | Enforce the Conventional Commits format (CC001) |
150
+ | `commit.message_pattern` | `""` | — | `CCHK_MESSAGE_PATTERN` | Custom regex the whole message must match; when set it replaces the Conventional Commits check (still reported as CC001) |
151
+ | `commit.subject_capitalized` | `false` | `--subject-capitalized` | `CCHK_SUBJECT_CAPITALIZED` | Require the subject to start with a capital letter (CC002) |
152
+ | `commit.subject_imperative` | `false` | `--subject-imperative` | `CCHK_SUBJECT_IMPERATIVE` | Require the subject to use the imperative mood (CC003) |
153
+ | `commit.subject_max_length` | `80` | `--subject-max-length` | `CCHK_SUBJECT_MAX_LENGTH` | Maximum subject length (CC004) |
154
+ | `commit.subject_min_length` | `5` | `--subject-min-length` | `CCHK_SUBJECT_MIN_LENGTH` | Minimum subject length (CC005) |
155
+ | `commit.allow_commit_types` | `feat, fix, docs, style, refactor, test, chore, perf, build, ci` | `--allow-commit-types` | `CCHK_ALLOW_COMMIT_TYPES` | Allowed `<type>` values in the subject |
156
+ | `commit.allow_merge_commits` | `true` | `--allow-merge-commits` | `CCHK_ALLOW_MERGE_COMMITS` | Allow merge commits (CC006) |
157
+ | `commit.allow_revert_commits` | `true` | `--allow-revert-commits` | `CCHK_ALLOW_REVERT_COMMITS` | Allow revert commits (CC007) |
158
+ | `commit.allow_empty_commits` | `true` | `--allow-empty-commits` | `CCHK_ALLOW_EMPTY_COMMITS` | Allow empty commit messages (CC008) |
159
+ | `commit.allow_fixup_commits` | `true` | `--allow-fixup-commits` | `CCHK_ALLOW_FIXUP_COMMITS` | Allow `fixup!` commits (CC009) |
160
+ | `commit.allow_wip_commits` | `true` | `--allow-wip-commits` | `CCHK_ALLOW_WIP_COMMITS` | Allow WIP commits (CC010) |
161
+ | `commit.require_body` | `false` | `--require-body` | `CCHK_REQUIRE_BODY` | Require a commit body (CC011) |
162
+ | `commit.require_signed_off_by` | `false` | `--require-signed-off-by` | `CCHK_REQUIRE_SIGNED_OFF_BY` | Require a `Signed-off-by:` trailer (CC012) |
163
+ | `commit.ignore_authors` | `[]` | `--ignore-authors` | `CCHK_IGNORE_AUTHORS` | Authors and co-authors whose commits skip the commit checks |
164
+ | `commit.ai_attribution` | `"ignore"` | `--ai-attribution` | `CCHK_AI_ATTRIBUTION` | `ignore`, `forbid` or `disclose`. `forbid` rejects commits carrying known AI tool signatures (CC013); `disclose` accepts AI assistance disclosed with one of `ai_disclosure_trailers` (CC014) and rejects the tool as a co-author (CC015) or as a sign-off (CC016) |
165
+ | `commit.ai_disclosure_trailers` | `Assisted-by, Generated-by` | `--ai-disclosure-trailers` | `CCHK_AI_DISCLOSURE_TRAILERS` | Trailers that disclose AI assistance under `disclose`; the first is the one a fix is written with. List `Co-authored-by` to accept the tool as a co-author |
166
+ | `commit.ai_disclosure_pattern` | `""` | `--ai-disclosure-pattern` | `CCHK_AI_DISCLOSURE_PATTERN` | Regex the disclosure's value must match under `disclose`, e.g. `^\S+/\S+` for `agent/model`; empty accepts any value, but a trailer with no value at all is always reported |
167
+ | `commit.author_email_pattern` | `"^.+@.+$"` | `--author-email-pattern` | `CCHK_AUTHOR_EMAIL_PATTERN` | Regex the author email must match (CC102, with `--author-email`) |
168
+ | `commit.author_name_pattern` | `""` | `--author-name-pattern` | `CCHK_AUTHOR_NAME_PATTERN` | Regex the author name must match (CC101, with `--author-name`) |
169
+ | `branch.conventional_branch` | `true` | `--conventional-branch` | `CCHK_CONVENTIONAL_BRANCH` | Enforce `<type>/<description>` branch names (CC201) |
170
+ | `branch.allow_branch_types` | `feature, bugfix, hotfix, release, chore, feat, fix, build, ci, docs, perf, refactor, test, style, ai, claude, codex, copilot, cursor, dependabot, renovate` | `--allow-branch-types` | `CCHK_ALLOW_BRANCH_TYPES` | Allowed branch `<type>` prefixes; each entry is a regex matched against the whole type |
171
+ | `branch.allow_branch_names` | `[]` | `--allow-branch-names` | `CCHK_ALLOW_BRANCH_NAMES` | Extra branch names allowed besides `main`, `master`, `HEAD`, `PR-.+`; each entry is a regex matched against the whole branch name, so `create-pull-request/.+` allows a family of them |
172
+ | `branch.require_rebase_target` | `""` | `--require-rebase-target` | `CCHK_REQUIRE_REBASE_TARGET` | Branch the current branch must be rebased onto (CC202); empty disables |
173
+ | `branch.ignore_authors` | `[]` | `--branch-ignore-authors` | `CCHK_BRANCH_IGNORE_AUTHORS` | Authors whose branches skip the branch checks |
174
+ | `push.allow_force_push` | `true` | — (`--no-force-push` runs the check) | `CCHK_ALLOW_FORCE_PUSH` | Reserved; has no effect today. CC301 runs only with `--no-force-push`, which always rejects a non-fast-forward push |
175
+ | `tag.regex` | SemVer with optional `v` | `--tag-regex` | `CCHK_TAG_REGEX` | Pattern tag names must match (CC401); empty disables |
176
+ | `files.max_size` | `""` (off) | `--files-max-size` | `CCHK_FILES_MAX_SIZE` | Largest committed file, in bytes or with `KB`/`MB`/`GB` (CC302) |
177
+ | `files.prohibited_patterns` | `[]` | `--files-prohibited-patterns` | `CCHK_FILES_PROHIBITED_PATTERNS` | fnmatch patterns committed paths must not match (CC303) |
178
+ | `files.max_path_length` | `0` (off) | `--files-max-path-length` | `CCHK_FILES_MAX_PATH_LENGTH` | Longest allowed committed path, in characters (CC304) |
179
+
180
+ Output and appearance are per-run flags, not configuration: `--format json`,
181
+ `--no-banner`, `--compact`, `--dry-run`, `--rev`, `--config`. Color follows
182
+ `NO_COLOR` / `FORCE_COLOR`. Long-form descriptions: [commit-check.com/configuration](https://commit-check.com/configuration/)
183
+
184
+ </details>
185
+
186
+ ## AI-native usage
187
+
188
+ `--format json` gives each check a stable rule ID, the value it saw and a
189
+ suggestion — plus the corrected value itself when the correction is
190
+ mechanical, so an agent can apply it without interpreting anything:
191
+
192
+ ```bash
193
+ echo "Fix: add streaming support" | commit-check -m --format json
194
+ ```
195
+
196
+ The failing entry of its `checks` array:
197
+
198
+ ```json
199
+ {
200
+ "rule_id": "CC001",
201
+ "check": "message",
202
+ "status": "fail",
203
+ "value": "Fix: add streaming support",
204
+ "error": "The commit message should follow Conventional Commits. See https://www.conventionalcommits.org",
205
+ "suggest": "Use \"fix: add streaming support\"",
206
+ "fix": "fix: add streaming support",
207
+ "docs_url": "https://commit-check.com/rules/#cc001"
208
+ }
209
+ ```
210
+
211
+ The same result, without a subprocess:
212
+
213
+ ```python
214
+ from commit_check.api import validate_message
215
+
216
+ validate_message("feat: add streaming support")["status"] # "pass"
217
+ ```
218
+
219
+ `commit_check.api` also has `validate_branch`, `validate_author`,
220
+ `validate_tag`, `validate_push` and `validate_all`, all returning the same
221
+ shape. Unlike the CLI they do not read `cchk.toml`: pass the policy as a
222
+ `config` dict. The [Python API](https://commit-check.com/guides/python-api/)
223
+ page covers each function and how to load a repository's file.
224
+
225
+ Exit codes: `0` passed · `1` a check failed · `2` nothing was checked — bad
226
+ usage, or a config that is missing, is not valid TOML, names an unknown rule or
227
+ holds a regex that does not compile. `--dry-run` reports everything and exits `0`. More output formats: [examples](https://commit-check.com/example/).
228
+
229
+ ## Why Commit Check
230
+
231
+ | | Commit Check | commitlint | GitHub Rulesets |
232
+ |---|:-:|:-:|:-:|
233
+ | Conventional Commits | ✅ | ✅ | regex only |
234
+ | Branch, tag, author and file rules | ✅ | ❌ | ✅\* |
235
+ | Feedback before the push | ✅ | ✅ | ❌ |
236
+ | Works with zero config | ✅ | ❌ | ❌ |
237
+ | No Node.js | ✅ | ❌ | ✅ |
238
+ | JSON output, Python API, MCP | ✅ | ❌ | ❌ |
239
+
240
+ \*Push rulesets need a Team or Enterprise plan for private repositories.
241
+ The full comparison, including YACC and custom hooks: [commit-check.com/compare](https://commit-check.com/compare/tools/)
242
+
243
+ ## Show that you use it
244
+
245
+ [![commit-check](https://commit-check.com/badge.svg)](https://commit-check.com)
246
+
247
+ ```text
248
+ [![commit-check](https://commit-check.com/badge.svg)](https://commit-check.com)
249
+ ```
250
+
251
+ ## Community
252
+
253
+ Questions and ideas go to [Discussions](https://github.com/commit-check/commit-check/discussions),
254
+ bugs to [Issues](https://github.com/commit-check/commit-check/issues).
255
+ Releases follow [Semantic Versioning](https://semver.org/). Released under the
256
+ [MIT License](https://github.com/commit-check/commit-check/blob/main/LICENSE).
@@ -2,20 +2,20 @@ commit_check/__init__.py,sha256=--TSQBRscNxJtSNl74Mm6OGxjX0SDh_Gqicuu2AoEYU,4420
2
2
  commit_check/ai_policy.py,sha256=gYC3ROLgGL1TDyhPkFXkV-NiYbvMt80Qt0MFrJcbJY4,8131
3
3
  commit_check/ai_signatures.py,sha256=XTfHh8zD2EbHjuCBBbukQllTJWqO7EoehVc7bADMwQY,9437
4
4
  commit_check/ai_signatures_data.py,sha256=33uiiAieL1wB6isgU-dgkz10IrQub7lVCGh8hf7XiN8,11213
5
- commit_check/api.py,sha256=dHwdCl25BWxIUJvk9SWIyKWHHuX1kbYtuMOdDgo_Y70,12695
5
+ commit_check/api.py,sha256=RlLGWfpEu9iW-7zIWJ7xDHTYHdH7w4a-0AE7yJH6jU0,12818
6
6
  commit_check/config.py,sha256=3KAoCU3sREyT0llePdKE6YrM79tmGka3a2j8LqF27WA,9815
7
7
  commit_check/config_merger.py,sha256=k3heMtKEMCSca-LtAntfyvNdPaB18iusfPjt_BOe6j8,11758
8
- commit_check/engine.py,sha256=LocjM15ks26g_ae0EIJIpXK0mRaHo1scyJkRDmPNuaw,67646
8
+ commit_check/engine.py,sha256=vjMcrKpjQR9e_HZoHYkmCUJqcmOw8iOckxpCBqUUvXk,68640
9
9
  commit_check/fixes.py,sha256=AsE9ChMXoOCV61pgxbZZNkAzmQS9_ARTWvvox4HkgEg,7088
10
10
  commit_check/imperatives.py,sha256=dEd_ssu4G0j6Cann6jqPdB97lw5Adua43K0_Odg_FyA,9518
11
- commit_check/main.py,sha256=5flwZ-2b4BT2BwZewU8QLegtVl63oYl6vGji8juR5dY,24649
11
+ commit_check/main.py,sha256=dyvEHptwOdGZA01E3nxbNwNmUvxfgT-vIgfXzvLtqBM,24881
12
12
  commit_check/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
13
  commit_check/rule_builder.py,sha256=f42vN3X_hYU47YgJ0xCniwKW9CwlDwt-4Q_WC7_Y-6E,29591
14
14
  commit_check/rules_catalog.py,sha256=Sa4jsyxWJ8g6fX2qJTdIMnd6513in5GmSxHYL5nTwws,11557
15
15
  commit_check/util.py,sha256=o0PuiiRhSgIVBmBF_20VytiHj74VlKb3nCMJ8KxB2Ds,25749
16
- commit_check-2.18.0.dist-info/licenses/LICENSE,sha256=VAJ9TE1ov8aUKmeoBRYqciMs0CXag1TeDCoLhwbeQmA,1095
17
- commit_check-2.18.0.dist-info/METADATA,sha256=R0JbRtBhlSYSPDmBZKzjB7L-37LV2kBvm0HS3hFfEUI,40619
18
- commit_check-2.18.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
19
- commit_check-2.18.0.dist-info/entry_points.txt,sha256=YMrkFGeub-3-IanV1d9qnopXVaRQM7MA9foXKW9iZ88,86
20
- commit_check-2.18.0.dist-info/top_level.txt,sha256=Wf46u-ooHBMJNHbhfrBNQw3wC5_m8wt-o_Lfbc4QpRg,13
21
- commit_check-2.18.0.dist-info/RECORD,,
16
+ commit_check-2.18.2.dist-info/licenses/LICENSE,sha256=VAJ9TE1ov8aUKmeoBRYqciMs0CXag1TeDCoLhwbeQmA,1095
17
+ commit_check-2.18.2.dist-info/METADATA,sha256=9LtV66iFvvvt72VtMZo07FUtJCB78s5hBUfqRt-YsNU,15077
18
+ commit_check-2.18.2.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
19
+ commit_check-2.18.2.dist-info/entry_points.txt,sha256=YMrkFGeub-3-IanV1d9qnopXVaRQM7MA9foXKW9iZ88,86
20
+ commit_check-2.18.2.dist-info/top_level.txt,sha256=Wf46u-ooHBMJNHbhfrBNQw3wC5_m8wt-o_Lfbc4QpRg,13
21
+ commit_check-2.18.2.dist-info/RECORD,,