commit-check-mcp 0.1.2__tar.gz → 0.1.4__tar.gz

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.
Files changed (22) hide show
  1. commit_check_mcp-0.1.4/.github/dependabot.yml +23 -0
  2. {commit_check_mcp-0.1.2 → commit_check_mcp-0.1.4}/.github/workflows/main.yml +2 -2
  3. {commit_check_mcp-0.1.2 → commit_check_mcp-0.1.4}/.github/workflows/publish.yml +3 -3
  4. {commit_check_mcp-0.1.2/src/commit_check_mcp.egg-info → commit_check_mcp-0.1.4}/PKG-INFO +32 -8
  5. commit_check_mcp-0.1.2/PKG-INFO → commit_check_mcp-0.1.4/README.md +17 -20
  6. {commit_check_mcp-0.1.2 → commit_check_mcp-0.1.4}/pyproject.toml +17 -4
  7. {commit_check_mcp-0.1.2 → commit_check_mcp-0.1.4}/src/commit_check_mcp/server.py +58 -6
  8. commit_check_mcp-0.1.2/README.md → commit_check_mcp-0.1.4/src/commit_check_mcp.egg-info/PKG-INFO +44 -4
  9. {commit_check_mcp-0.1.2 → commit_check_mcp-0.1.4}/src/commit_check_mcp.egg-info/SOURCES.txt +2 -0
  10. commit_check_mcp-0.1.4/src/commit_check_mcp.egg-info/requires.txt +6 -0
  11. {commit_check_mcp-0.1.2 → commit_check_mcp-0.1.4}/tests/test_server.py +95 -1
  12. commit_check_mcp-0.1.4/uv.lock +1073 -0
  13. commit_check_mcp-0.1.2/src/commit_check_mcp.egg-info/requires.txt +0 -6
  14. {commit_check_mcp-0.1.2 → commit_check_mcp-0.1.4}/.github/workflows/labeler.yml +0 -0
  15. {commit_check_mcp-0.1.2 → commit_check_mcp-0.1.4}/.github/workflows/release-drafter.yml +0 -0
  16. {commit_check_mcp-0.1.2 → commit_check_mcp-0.1.4}/.gitignore +0 -0
  17. {commit_check_mcp-0.1.2 → commit_check_mcp-0.1.4}/LICENSE +0 -0
  18. {commit_check_mcp-0.1.2 → commit_check_mcp-0.1.4}/setup.cfg +0 -0
  19. {commit_check_mcp-0.1.2 → commit_check_mcp-0.1.4}/src/commit_check_mcp/__init__.py +0 -0
  20. {commit_check_mcp-0.1.2 → commit_check_mcp-0.1.4}/src/commit_check_mcp.egg-info/dependency_links.txt +0 -0
  21. {commit_check_mcp-0.1.2 → commit_check_mcp-0.1.4}/src/commit_check_mcp.egg-info/entry_points.txt +0 -0
  22. {commit_check_mcp-0.1.2 → commit_check_mcp-0.1.4}/src/commit_check_mcp.egg-info/top_level.txt +0 -0
@@ -0,0 +1,23 @@
1
+ # To get started with Dependabot version updates, you'll need to specify which
2
+ # package ecosystems to update and where the package manifests are located.
3
+ # Please see the documentation for all configuration options:
4
+ # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
5
+
6
+ version: 2
7
+ updates:
8
+ - package-ecosystem: github-actions
9
+ directory: /
10
+ schedule:
11
+ interval: "monthly"
12
+ groups:
13
+ github-actions:
14
+ patterns:
15
+ - "*"
16
+ - package-ecosystem: pip
17
+ directory: /
18
+ schedule:
19
+ interval: "monthly"
20
+ groups:
21
+ pip:
22
+ patterns:
23
+ - "*"
@@ -39,7 +39,7 @@ jobs:
39
39
 
40
40
  - name: Upload coverage reports to Codecov
41
41
  if: matrix.python-version == '3.12'
42
- uses: codecov/codecov-action@v5
42
+ uses: codecov/codecov-action@v6
43
43
  with:
44
44
  token: ${{ secrets.CODECOV_TOKEN }}
45
45
  slug: commit-check/commit-check-mcp
@@ -65,7 +65,7 @@ jobs:
65
65
  run: python -m twine check dist/*
66
66
 
67
67
  - name: Upload distributions
68
- uses: actions/upload-artifact@v4
68
+ uses: actions/upload-artifact@v7
69
69
  with:
70
70
  name: dist
71
71
  path: dist/*
@@ -45,13 +45,13 @@ jobs:
45
45
  commit-check-mcp --help
46
46
 
47
47
  - name: Upload distributions
48
- uses: actions/upload-artifact@v4
48
+ uses: actions/upload-artifact@v7
49
49
  with:
50
50
  name: python-package-distributions
51
51
  path: dist/*
52
52
 
53
53
  - name: Create attestations
54
- uses: actions/attest-build-provenance@v3
54
+ uses: actions/attest-build-provenance@v4
55
55
  with:
56
56
  subject-path: dist/*
57
57
 
@@ -65,7 +65,7 @@ jobs:
65
65
  id-token: write
66
66
  steps:
67
67
  - name: Download distributions
68
- uses: actions/download-artifact@v4
68
+ uses: actions/download-artifact@v8
69
69
  with:
70
70
  name: python-package-distributions
71
71
  path: dist
@@ -1,17 +1,28 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: commit-check-mcp
3
- Version: 0.1.2
3
+ Version: 0.1.4
4
4
  Summary: MCP server exposing commit-check validation tools
5
- Author: commit-check
5
+ Author-email: Xianpeng Shen <xianpeng.shen@gmail.com>
6
6
  License: MIT
7
+ Classifier: Development Status :: 4 - Beta
8
+ Classifier: Environment :: Console
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Operating System :: OS Independent
12
+ Classifier: Programming Language :: Python :: 3.10
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Programming Language :: Python :: 3.14
17
+ Classifier: Topic :: Software Development
7
18
  Requires-Python: >=3.10
8
19
  Description-Content-Type: text/markdown
9
20
  License-File: LICENSE
10
- Requires-Dist: commit-check<3,>=2.5.0
21
+ Requires-Dist: commit-check<3,>=2.7.0
11
22
  Requires-Dist: mcp<2,>=1.27.0
12
23
  Provides-Extra: dev
13
24
  Requires-Dist: pytest<10,>=9.0.0; extra == "dev"
14
- Requires-Dist: pytest-cov<7,>=6.0.0; extra == "dev"
25
+ Requires-Dist: pytest-cov<8,>=6.0.0; extra == "dev"
15
26
  Dynamic: license-file
16
27
 
17
28
  # commit-check-mcp
@@ -24,7 +35,7 @@ Dynamic: license-file
24
35
 
25
36
  Model Context Protocol (MCP) server for [commit-check](https://github.com/commit-check/commit-check).
26
37
 
27
- `commit-check-mcp` exposes `commit-check` as local MCP tools so an MCP client can validate commit messages, branch names, author info, and repository state.
38
+ `commit-check-mcp` exposes `commit-check` as local MCP tools so an MCP client can validate commit messages, branch names, author info, push safety, and repository state.
28
39
 
29
40
  ## Features
30
41
 
@@ -33,9 +44,10 @@ This MCP server exposes commit-check validations as MCP tools:
33
44
  - `server_health` — returns server/sdk versions
34
45
  - `validate_commit_message` — validates a commit message
35
46
  - `validate_branch_name` — validates a branch name or the current repo branch
47
+ - `validate_push_safety` — validates that a push is not a force push
36
48
  - `validate_author_info` — validates author name/email or the repo's git author config
37
49
  - `validate_commit_context` — runs combined checks in one call
38
- - `validate_repository_state` — validates latest commit, current branch, and author state for a repo
50
+ - `validate_repository_state` — validates latest commit, current branch, author state, and optional push safety for a repo
39
51
  - `describe_validation_rules` — returns the effective config and enabled rules after merging defaults and repo config
40
52
 
41
53
  All validation tools return the same structured commit-check result shape:
@@ -122,9 +134,10 @@ After the client starts the server, it will expose these tools:
122
134
  - `server_health`: returns server, SDK, and dependency versions
123
135
  - `validate_commit_message(message, config?, repo_path?, config_path?)`
124
136
  - `validate_branch_name(branch?, config?, repo_path?, config_path?)`
137
+ - `validate_push_safety(push_refs?, config?, repo_path?, config_path?)`
125
138
  - `validate_author_info(author_name?, author_email?, config?, repo_path?, config_path?)`
126
139
  - `validate_commit_context(message?, branch?, author_name?, author_email?, config?, repo_path?, config_path?)`
127
- - `validate_repository_state(repo_path?, config?, config_path?, include_message?, include_branch?, include_author?)`
140
+ - `validate_repository_state(repo_path?, config?, config_path?, include_message?, include_branch?, include_author?, include_push?)`
128
141
  - `describe_validation_rules(config?, repo_path?, config_path?)`
129
142
 
130
143
  The common optional arguments are:
@@ -164,6 +177,15 @@ Validate the full repository state:
164
177
  }
165
178
  ```
166
179
 
180
+ Validate push safety from git pre-push hook ref metadata:
181
+
182
+ ```json
183
+ {
184
+ "repo_path": "/path/to/repo",
185
+ "push_refs": "refs/heads/main abc123 refs/heads/main def456"
186
+ }
187
+ ```
188
+
167
189
  Inspect the final merged rules that will be applied:
168
190
 
169
191
  ```json
@@ -189,6 +211,7 @@ Typical patterns:
189
211
 
190
212
  - Validate an explicit message with a repository's rules
191
213
  - Validate the current repository state without passing message/branch/author values manually
214
+ - Validate push safety using pre-push ref metadata, or check the current branch against its upstream
192
215
  - Inspect which rules are actually enabled after config merging
193
216
 
194
217
  Example payload for a repository-wide validation:
@@ -198,7 +221,8 @@ Example payload for a repository-wide validation:
198
221
  "repo_path": "/path/to/repo",
199
222
  "include_message": true,
200
223
  "include_branch": true,
201
- "include_author": true
224
+ "include_author": true,
225
+ "include_push": true
202
226
  }
203
227
  ```
204
228
 
@@ -1,19 +1,3 @@
1
- Metadata-Version: 2.4
2
- Name: commit-check-mcp
3
- Version: 0.1.2
4
- Summary: MCP server exposing commit-check validation tools
5
- Author: commit-check
6
- License: MIT
7
- Requires-Python: >=3.10
8
- Description-Content-Type: text/markdown
9
- License-File: LICENSE
10
- Requires-Dist: commit-check<3,>=2.5.0
11
- Requires-Dist: mcp<2,>=1.27.0
12
- Provides-Extra: dev
13
- Requires-Dist: pytest<10,>=9.0.0; extra == "dev"
14
- Requires-Dist: pytest-cov<7,>=6.0.0; extra == "dev"
15
- Dynamic: license-file
16
-
17
1
  # commit-check-mcp
18
2
 
19
3
  [![PyPI version](https://img.shields.io/pypi/v/commit-check-mcp)](https://pypi.org/project/commit-check-mcp/)
@@ -24,7 +8,7 @@ Dynamic: license-file
24
8
 
25
9
  Model Context Protocol (MCP) server for [commit-check](https://github.com/commit-check/commit-check).
26
10
 
27
- `commit-check-mcp` exposes `commit-check` as local MCP tools so an MCP client can validate commit messages, branch names, author info, and repository state.
11
+ `commit-check-mcp` exposes `commit-check` as local MCP tools so an MCP client can validate commit messages, branch names, author info, push safety, and repository state.
28
12
 
29
13
  ## Features
30
14
 
@@ -33,9 +17,10 @@ This MCP server exposes commit-check validations as MCP tools:
33
17
  - `server_health` — returns server/sdk versions
34
18
  - `validate_commit_message` — validates a commit message
35
19
  - `validate_branch_name` — validates a branch name or the current repo branch
20
+ - `validate_push_safety` — validates that a push is not a force push
36
21
  - `validate_author_info` — validates author name/email or the repo's git author config
37
22
  - `validate_commit_context` — runs combined checks in one call
38
- - `validate_repository_state` — validates latest commit, current branch, and author state for a repo
23
+ - `validate_repository_state` — validates latest commit, current branch, author state, and optional push safety for a repo
39
24
  - `describe_validation_rules` — returns the effective config and enabled rules after merging defaults and repo config
40
25
 
41
26
  All validation tools return the same structured commit-check result shape:
@@ -122,9 +107,10 @@ After the client starts the server, it will expose these tools:
122
107
  - `server_health`: returns server, SDK, and dependency versions
123
108
  - `validate_commit_message(message, config?, repo_path?, config_path?)`
124
109
  - `validate_branch_name(branch?, config?, repo_path?, config_path?)`
110
+ - `validate_push_safety(push_refs?, config?, repo_path?, config_path?)`
125
111
  - `validate_author_info(author_name?, author_email?, config?, repo_path?, config_path?)`
126
112
  - `validate_commit_context(message?, branch?, author_name?, author_email?, config?, repo_path?, config_path?)`
127
- - `validate_repository_state(repo_path?, config?, config_path?, include_message?, include_branch?, include_author?)`
113
+ - `validate_repository_state(repo_path?, config?, config_path?, include_message?, include_branch?, include_author?, include_push?)`
128
114
  - `describe_validation_rules(config?, repo_path?, config_path?)`
129
115
 
130
116
  The common optional arguments are:
@@ -164,6 +150,15 @@ Validate the full repository state:
164
150
  }
165
151
  ```
166
152
 
153
+ Validate push safety from git pre-push hook ref metadata:
154
+
155
+ ```json
156
+ {
157
+ "repo_path": "/path/to/repo",
158
+ "push_refs": "refs/heads/main abc123 refs/heads/main def456"
159
+ }
160
+ ```
161
+
167
162
  Inspect the final merged rules that will be applied:
168
163
 
169
164
  ```json
@@ -189,6 +184,7 @@ Typical patterns:
189
184
 
190
185
  - Validate an explicit message with a repository's rules
191
186
  - Validate the current repository state without passing message/branch/author values manually
187
+ - Validate push safety using pre-push ref metadata, or check the current branch against its upstream
192
188
  - Inspect which rules are actually enabled after config merging
193
189
 
194
190
  Example payload for a repository-wide validation:
@@ -198,7 +194,8 @@ Example payload for a repository-wide validation:
198
194
  "repo_path": "/path/to/repo",
199
195
  "include_message": true,
200
196
  "include_branch": true,
201
- "include_author": true
197
+ "include_author": true,
198
+ "include_push": true
202
199
  }
203
200
  ```
204
201
 
@@ -1,5 +1,5 @@
1
1
  [build-system]
2
- requires = ["setuptools>=77", "setuptools-scm", "wheel"]
2
+ requires = ["setuptools>=77", "setuptools-scm"]
3
3
  build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
@@ -9,10 +9,23 @@ readme = "README.md"
9
9
  requires-python = ">=3.10"
10
10
  license = { text = "MIT" }
11
11
  authors = [
12
- { name = "commit-check" }
12
+ { name = "Xianpeng Shen", email = "xianpeng.shen@gmail.com" }
13
+ ]
14
+ classifiers = [
15
+ "Development Status :: 4 - Beta",
16
+ "Environment :: Console",
17
+ "Intended Audience :: Developers",
18
+ "License :: OSI Approved :: MIT License",
19
+ "Operating System :: OS Independent",
20
+ "Programming Language :: Python :: 3.10",
21
+ "Programming Language :: Python :: 3.11",
22
+ "Programming Language :: Python :: 3.12",
23
+ "Programming Language :: Python :: 3.13",
24
+ "Programming Language :: Python :: 3.14",
25
+ "Topic :: Software Development",
13
26
  ]
14
27
  dependencies = [
15
- "commit-check>=2.5.0,<3",
28
+ "commit-check>=2.7.0,<3",
16
29
  "mcp>=1.27.0,<2"
17
30
  ]
18
31
  dynamic = ["version"]
@@ -20,7 +33,7 @@ dynamic = ["version"]
20
33
  [project.optional-dependencies]
21
34
  dev = [
22
35
  "pytest>=9.0.0,<10",
23
- "pytest-cov>=6.0.0,<7"
36
+ "pytest-cov>=6.0.0,<8"
24
37
  ]
25
38
 
26
39
  [project.scripts]
@@ -14,7 +14,7 @@ from commit_check import __version__ as commit_check_version
14
14
  from commit_check.config_merger import deep_merge, get_default_config, load_toml_config
15
15
  from commit_check.engine import ValidationContext, ValidationEngine, ValidationResult
16
16
  from commit_check.rule_builder import RuleBuilder, ValidationRule
17
- from commit_check.rules_catalog import BRANCH_RULES, COMMIT_RULES
17
+ from commit_check.rules_catalog import BRANCH_RULES, COMMIT_RULES, PUSH_RULES
18
18
  from mcp.server.fastmcp import FastMCP
19
19
 
20
20
  from . import __version__
@@ -22,8 +22,8 @@ from . import __version__
22
22
  mcp = FastMCP(
23
23
  "commit-check-mcp",
24
24
  instructions=(
25
- "Use these tools to validate commit messages, branch names, and author metadata "
26
- "with commit-check."
25
+ "Use these tools to validate commit messages, branch names, author metadata, "
26
+ "and push safety with commit-check."
27
27
  ),
28
28
  )
29
29
 
@@ -185,6 +185,28 @@ def _validate_branch(
185
185
  )
186
186
 
187
187
 
188
+ def _validate_push(
189
+ push_refs: str | None = None,
190
+ *,
191
+ config: dict[str, Any] | None = None,
192
+ repo_path: Path | None = None,
193
+ config_path: str | None = None,
194
+ ) -> dict[str, Any]:
195
+ """Validate push ref updates against commit-check force-push protection."""
196
+ cfg = _merge_config(config, repo_path=repo_path, config_path=config_path)
197
+ cfg.setdefault("push", {})["allow_force_push"] = False
198
+ with _working_directory(repo_path):
199
+ return _run_checks(
200
+ ["no_force_push"],
201
+ ValidationContext(
202
+ stdin_text=push_refs,
203
+ config=cfg,
204
+ push_upstream_fallback=push_refs is None,
205
+ ),
206
+ cfg,
207
+ )
208
+
209
+
188
210
  def _validate_author(
189
211
  name: str | None = None,
190
212
  email: str | None = None,
@@ -373,6 +395,24 @@ def validate_author_info(
373
395
  )
374
396
 
375
397
 
398
+ @mcp.tool()
399
+ def validate_push_safety(
400
+ push_refs: str | None = None,
401
+ config: dict[str, Any] | None = None,
402
+ repo_path: str | None = None,
403
+ config_path: str | None = None,
404
+ ) -> dict[str, Any]:
405
+ """Validate that a push is not a force push."""
406
+ normalized_push_refs = push_refs.strip() if isinstance(push_refs, str) else None
407
+ normalized_repo_path = _normalize_repo_path(repo_path)
408
+ return _validate_push(
409
+ normalized_push_refs,
410
+ config=_normalize_config(config),
411
+ repo_path=normalized_repo_path,
412
+ config_path=_normalize_config_path(config_path, normalized_repo_path),
413
+ )
414
+
415
+
376
416
  @mcp.tool()
377
417
  def validate_commit_context(
378
418
  message: str | None = None,
@@ -423,9 +463,10 @@ def validate_repository_state(
423
463
  include_message: bool = True,
424
464
  include_branch: bool = True,
425
465
  include_author: bool = True,
466
+ include_push: bool = False,
426
467
  ) -> dict[str, Any]:
427
- """Validate the latest commit, current branch, and git author state of a repository."""
428
- if not any([include_message, include_branch, include_author]):
468
+ """Validate the latest commit, branch, author, and optional push safety state."""
469
+ if not any([include_message, include_branch, include_author, include_push]):
429
470
  raise ValueError("At least one validation target must be enabled")
430
471
 
431
472
  normalized_repo_path = _normalize_repo_path(repo_path)
@@ -461,6 +502,15 @@ def validate_repository_state(
461
502
  config_path=normalized_config_path,
462
503
  )["checks"]
463
504
  )
505
+ if include_push:
506
+ checks.extend(
507
+ _validate_push(
508
+ None,
509
+ config=normalized_config,
510
+ repo_path=normalized_repo_path,
511
+ config_path=normalized_config_path,
512
+ )["checks"]
513
+ )
464
514
 
465
515
  return {
466
516
  "status": "fail" if any(c["status"] == "fail" for c in checks) else "pass",
@@ -489,7 +539,9 @@ def describe_validation_rules(
489
539
  "commit_check_version": commit_check_version,
490
540
  "config": merged_config,
491
541
  "supported_checks": list(
492
- dict.fromkeys(entry.check for entry in COMMIT_RULES + BRANCH_RULES)
542
+ dict.fromkeys(
543
+ entry.check for entry in COMMIT_RULES + BRANCH_RULES + PUSH_RULES
544
+ )
493
545
  ),
494
546
  "enabled_rules": rules,
495
547
  }
@@ -1,3 +1,30 @@
1
+ Metadata-Version: 2.4
2
+ Name: commit-check-mcp
3
+ Version: 0.1.4
4
+ Summary: MCP server exposing commit-check validation tools
5
+ Author-email: Xianpeng Shen <xianpeng.shen@gmail.com>
6
+ License: MIT
7
+ Classifier: Development Status :: 4 - Beta
8
+ Classifier: Environment :: Console
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Operating System :: OS Independent
12
+ Classifier: Programming Language :: Python :: 3.10
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Programming Language :: Python :: 3.14
17
+ Classifier: Topic :: Software Development
18
+ Requires-Python: >=3.10
19
+ Description-Content-Type: text/markdown
20
+ License-File: LICENSE
21
+ Requires-Dist: commit-check<3,>=2.7.0
22
+ Requires-Dist: mcp<2,>=1.27.0
23
+ Provides-Extra: dev
24
+ Requires-Dist: pytest<10,>=9.0.0; extra == "dev"
25
+ Requires-Dist: pytest-cov<8,>=6.0.0; extra == "dev"
26
+ Dynamic: license-file
27
+
1
28
  # commit-check-mcp
2
29
 
3
30
  [![PyPI version](https://img.shields.io/pypi/v/commit-check-mcp)](https://pypi.org/project/commit-check-mcp/)
@@ -8,7 +35,7 @@
8
35
 
9
36
  Model Context Protocol (MCP) server for [commit-check](https://github.com/commit-check/commit-check).
10
37
 
11
- `commit-check-mcp` exposes `commit-check` as local MCP tools so an MCP client can validate commit messages, branch names, author info, and repository state.
38
+ `commit-check-mcp` exposes `commit-check` as local MCP tools so an MCP client can validate commit messages, branch names, author info, push safety, and repository state.
12
39
 
13
40
  ## Features
14
41
 
@@ -17,9 +44,10 @@ This MCP server exposes commit-check validations as MCP tools:
17
44
  - `server_health` — returns server/sdk versions
18
45
  - `validate_commit_message` — validates a commit message
19
46
  - `validate_branch_name` — validates a branch name or the current repo branch
47
+ - `validate_push_safety` — validates that a push is not a force push
20
48
  - `validate_author_info` — validates author name/email or the repo's git author config
21
49
  - `validate_commit_context` — runs combined checks in one call
22
- - `validate_repository_state` — validates latest commit, current branch, and author state for a repo
50
+ - `validate_repository_state` — validates latest commit, current branch, author state, and optional push safety for a repo
23
51
  - `describe_validation_rules` — returns the effective config and enabled rules after merging defaults and repo config
24
52
 
25
53
  All validation tools return the same structured commit-check result shape:
@@ -106,9 +134,10 @@ After the client starts the server, it will expose these tools:
106
134
  - `server_health`: returns server, SDK, and dependency versions
107
135
  - `validate_commit_message(message, config?, repo_path?, config_path?)`
108
136
  - `validate_branch_name(branch?, config?, repo_path?, config_path?)`
137
+ - `validate_push_safety(push_refs?, config?, repo_path?, config_path?)`
109
138
  - `validate_author_info(author_name?, author_email?, config?, repo_path?, config_path?)`
110
139
  - `validate_commit_context(message?, branch?, author_name?, author_email?, config?, repo_path?, config_path?)`
111
- - `validate_repository_state(repo_path?, config?, config_path?, include_message?, include_branch?, include_author?)`
140
+ - `validate_repository_state(repo_path?, config?, config_path?, include_message?, include_branch?, include_author?, include_push?)`
112
141
  - `describe_validation_rules(config?, repo_path?, config_path?)`
113
142
 
114
143
  The common optional arguments are:
@@ -148,6 +177,15 @@ Validate the full repository state:
148
177
  }
149
178
  ```
150
179
 
180
+ Validate push safety from git pre-push hook ref metadata:
181
+
182
+ ```json
183
+ {
184
+ "repo_path": "/path/to/repo",
185
+ "push_refs": "refs/heads/main abc123 refs/heads/main def456"
186
+ }
187
+ ```
188
+
151
189
  Inspect the final merged rules that will be applied:
152
190
 
153
191
  ```json
@@ -173,6 +211,7 @@ Typical patterns:
173
211
 
174
212
  - Validate an explicit message with a repository's rules
175
213
  - Validate the current repository state without passing message/branch/author values manually
214
+ - Validate push safety using pre-push ref metadata, or check the current branch against its upstream
176
215
  - Inspect which rules are actually enabled after config merging
177
216
 
178
217
  Example payload for a repository-wide validation:
@@ -182,7 +221,8 @@ Example payload for a repository-wide validation:
182
221
  "repo_path": "/path/to/repo",
183
222
  "include_message": true,
184
223
  "include_branch": true,
185
- "include_author": true
224
+ "include_author": true,
225
+ "include_push": true
186
226
  }
187
227
  ```
188
228
 
@@ -2,6 +2,8 @@
2
2
  LICENSE
3
3
  README.md
4
4
  pyproject.toml
5
+ uv.lock
6
+ .github/dependabot.yml
5
7
  .github/workflows/labeler.yml
6
8
  .github/workflows/main.yml
7
9
  .github/workflows/publish.yml
@@ -0,0 +1,6 @@
1
+ commit-check<3,>=2.7.0
2
+ mcp<2,>=1.27.0
3
+
4
+ [dev]
5
+ pytest<10,>=9.0.0
6
+ pytest-cov<8,>=6.0.0
@@ -50,6 +50,83 @@ def test_validate_commit_context_requires_at_least_one_field() -> None:
50
50
  server.validate_commit_context()
51
51
 
52
52
 
53
+ def test_validate_push_safety_forwards_normalized_values(
54
+ monkeypatch: pytest.MonkeyPatch,
55
+ ) -> None:
56
+ captured: dict[str, object] = {}
57
+
58
+ def fake_validate_push(
59
+ push_refs: str | None,
60
+ *,
61
+ config: dict | None,
62
+ repo_path: Path | None,
63
+ config_path: str | None,
64
+ ):
65
+ captured["push_refs"] = push_refs
66
+ captured["config"] = config
67
+ captured["repo_path"] = repo_path
68
+ captured["config_path"] = config_path
69
+ return {"status": "pass", "checks": []}
70
+
71
+ monkeypatch.setattr(server, "_validate_push", fake_validate_push)
72
+
73
+ result = server.validate_push_safety(
74
+ " refs/heads/main abc refs/heads/main def ",
75
+ {"push": {"allow_force_push": True}},
76
+ repo_path=".",
77
+ )
78
+
79
+ assert result["status"] == "pass"
80
+ assert captured == {
81
+ "push_refs": "refs/heads/main abc refs/heads/main def",
82
+ "config": {"push": {"allow_force_push": True}},
83
+ "repo_path": Path.cwd().resolve(),
84
+ "config_path": None,
85
+ }
86
+
87
+
88
+ def test_validate_push_forces_no_force_push_rule(
89
+ monkeypatch: pytest.MonkeyPatch,
90
+ ) -> None:
91
+ captured: list[dict[str, object]] = []
92
+
93
+ def fake_run_checks(check_names, context, config):
94
+ captured.append(
95
+ {
96
+ "check_names": check_names,
97
+ "stdin_text": context.stdin_text,
98
+ "push_upstream_fallback": context.push_upstream_fallback,
99
+ "allow_force_push": config["push"]["allow_force_push"],
100
+ }
101
+ )
102
+ return {"status": "pass", "checks": []}
103
+
104
+ monkeypatch.setattr(server, "_run_checks", fake_run_checks)
105
+
106
+ result = server._validate_push(None, config={"push": {"allow_force_push": True}})
107
+ empty_refs_result = server._validate_push(
108
+ "",
109
+ config={"push": {"allow_force_push": True}},
110
+ )
111
+
112
+ assert result["status"] == "pass"
113
+ assert empty_refs_result["status"] == "pass"
114
+ assert captured == [
115
+ {
116
+ "check_names": ["no_force_push"],
117
+ "stdin_text": None,
118
+ "push_upstream_fallback": True,
119
+ "allow_force_push": False,
120
+ },
121
+ {
122
+ "check_names": ["no_force_push"],
123
+ "stdin_text": "",
124
+ "push_upstream_fallback": False,
125
+ "allow_force_push": False,
126
+ },
127
+ ]
128
+
129
+
53
130
  def test_validate_author_info_forwards_normalized_values(
54
131
  monkeypatch: pytest.MonkeyPatch,
55
132
  ) -> None:
@@ -132,6 +209,7 @@ def test_validate_repository_state_requires_one_enabled_target() -> None:
132
209
  include_message=False,
133
210
  include_branch=False,
134
211
  include_author=False,
212
+ include_push=False,
135
213
  )
136
214
 
137
215
 
@@ -158,16 +236,31 @@ def test_validate_repository_state_combines_requested_checks(
158
236
  assert branch is None
159
237
  return {"status": "pass", "checks": [{"check": "branch", "status": "pass"}]}
160
238
 
239
+ def fake_validate_push(
240
+ push_refs: str | None,
241
+ *,
242
+ config: dict | None,
243
+ repo_path: Path | None,
244
+ config_path: str | None,
245
+ ):
246
+ assert push_refs is None
247
+ return {
248
+ "status": "pass",
249
+ "checks": [{"check": "no_force_push", "status": "pass"}],
250
+ }
251
+
161
252
  monkeypatch.setattr(server, "_validate_message", fake_validate_message)
162
253
  monkeypatch.setattr(server, "_validate_branch", fake_validate_branch)
254
+ monkeypatch.setattr(server, "_validate_push", fake_validate_push)
163
255
 
164
- result = server.validate_repository_state(include_author=False)
256
+ result = server.validate_repository_state(include_author=False, include_push=True)
165
257
 
166
258
  assert result == {
167
259
  "status": "pass",
168
260
  "checks": [
169
261
  {"check": "message", "status": "pass"},
170
262
  {"check": "branch", "status": "pass"},
263
+ {"check": "no_force_push", "status": "pass"},
171
264
  ],
172
265
  }
173
266
 
@@ -192,5 +285,6 @@ allow_branch_names = ["develop"]
192
285
  assert result["config"]["commit"]["require_body"] is True
193
286
  assert result["config"]["branch"]["allow_branch_names"] == ["develop"]
194
287
  assert "message" in result["supported_checks"]
288
+ assert "no_force_push" in result["supported_checks"]
195
289
  assert result["supported_checks"].count("ignore_authors") == 1
196
290
  assert any(rule["check"] == "require_body" for rule in result["enabled_rules"])