Skip to content

Commit 71546b6

Browse files
committed
feat: initial version
1 parent 5a8fe3b commit 71546b6

29 files changed

Lines changed: 1017 additions & 0 deletions

.github/workflows/ci.yml

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
name: Continuous Integration
2+
3+
on:
4+
push:
5+
paths:
6+
- .github/workflows/ci.yml
7+
- poetry.lock
8+
- pyproject.toml
9+
- src/**
10+
- tests/**
11+
pull_request:
12+
paths:
13+
- .github/workflows/ci.yml
14+
- poetry.lock
15+
- pyproject.toml
16+
- src/**
17+
- tests/**
18+
19+
jobs:
20+
build:
21+
runs-on: ubuntu-24.04
22+
strategy:
23+
matrix:
24+
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
25+
26+
steps:
27+
- name: Checkout
28+
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
29+
30+
- uses: CVector-Energy/python-test@main
31+
with:
32+
python-version: ${{ matrix.python-version }}
33+
src-dirs: .

CLAUDE.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
- Name files for their main export, transforming Python names from PascalCase to underscore_case. For example, the file that defines the CollectEditedFilesHook class should be named collect_edited_files.py.

README.md

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
# python-claude
2+
3+
Python hooks for Claude Code.
4+
5+
## Installation
6+
7+
```bash
8+
poetry add python-claude
9+
```
10+
11+
## Usage
12+
13+
This package provides hooks that can be used with Claude Code's hook system.
14+
15+
### Available Commands
16+
17+
- `edited` - Tracks edited Python files for deferred processing
18+
- `git status` - Shows git status
19+
- `mypy` - Runs mypy type checking on edited files
20+
- `pytest` - Runs pytest
21+
- `ruff check` - Runs ruff check on collected files with auto-fix
22+
- `ruff format` - Runs ruff format on edited files
23+
24+
### Claude Code Settings
25+
26+
Add hooks to your Claude Code settings.json:
27+
28+
```json
29+
{
30+
"hooks": {
31+
"PostToolUse": [
32+
{
33+
"matcher": "Edit|Write|MultiEdit",
34+
"hooks": [
35+
{
36+
"type": "command",
37+
"command": "poetry run python-claude ruff format"
38+
},
39+
{
40+
"type": "command",
41+
"command": "poetry run python-claude edited"
42+
}
43+
]
44+
}
45+
],
46+
"Stop": [
47+
{
48+
"hooks": [
49+
{
50+
"type": "command",
51+
"command": "poetry run python-claude ruff check"
52+
},
53+
{
54+
"type": "command",
55+
"command": "poetry run python-claude pytest"
56+
}
57+
]
58+
}
59+
]
60+
}
61+
}
62+
```
63+
64+
## Development
65+
66+
```bash
67+
poetry install
68+
poetry run pytest
69+
poetry run ruff check
70+
poetry run mypy src
71+
```

poetry.lock

Lines changed: 382 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
[project]
2+
name = "python-claude"
3+
version = "0.1.0"
4+
description = "Python hooks for Claude Code"
5+
authors = [{name = "CVector", email = "support@cvector.com"}]
6+
readme = "README.md"
7+
requires-python = ">=3.10"
8+
dependencies = []
9+
10+
[project.scripts]
11+
python-claude = "python_claude.cli:main"
12+
13+
[tool.poetry]
14+
packages = [{ include = "python_claude", from = "src" }]
15+
16+
[tool.poetry.group.dev.dependencies]
17+
pytest = "^8.3.5"
18+
mypy = "^1.15.0"
19+
ruff = "^0.11.10"
20+
21+
[build-system]
22+
requires = ["poetry-core>=2.0.0,<3.0.0"]
23+
build-backend = "poetry.core.masonry.api"
24+
25+
[tool.mypy]
26+
strict = true

src/python_claude/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
"""Python hooks for Claude Code."""
2+
3+
__version__ = "0.1.0"
237 Bytes
Binary file not shown.
2.29 KB
Binary file not shown.

src/python_claude/cli.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
"""Command-line interface for python-claude hooks."""
2+
3+
import sys
4+
5+
from python_claude.hooks import (
6+
CollectEditedFilesHook,
7+
GitStatusHook,
8+
MypyHook,
9+
PytestHook,
10+
RuffCheckHook,
11+
RuffFormatHook,
12+
)
13+
from python_claude.hooks.base import Hook
14+
15+
HOOKS: dict[str, type[Hook]] = {
16+
"edited": CollectEditedFilesHook,
17+
"git status": GitStatusHook,
18+
"mypy": MypyHook,
19+
"pytest": PytestHook,
20+
"ruff check": RuffCheckHook,
21+
"ruff format": RuffFormatHook,
22+
}
23+
24+
25+
def _print_available_hooks() -> None:
26+
"""Print available hooks to stderr."""
27+
hooks = ", ".join(sorted(HOOKS.keys()))
28+
print(f"Available hooks: {hooks}", file=sys.stderr)
29+
30+
31+
def main() -> None:
32+
"""Main entry point for the CLI."""
33+
if len(sys.argv) < 2:
34+
print("Usage: python-claude <command>", file=sys.stderr)
35+
_print_available_hooks()
36+
sys.exit(1)
37+
38+
# Try two-word command first, then single word
39+
if len(sys.argv) >= 3:
40+
hook_name = f"{sys.argv[1]} {sys.argv[2]}"
41+
if hook_name not in HOOKS:
42+
hook_name = sys.argv[1]
43+
else:
44+
hook_name = sys.argv[1]
45+
46+
if hook_name not in HOOKS:
47+
print(f"Unknown command: {hook_name}", file=sys.stderr)
48+
_print_available_hooks()
49+
sys.exit(1)
50+
51+
hook_class = HOOKS[hook_name]
52+
hook = hook_class()
53+
exit_code = hook.run()
54+
sys.exit(exit_code)
55+
56+
57+
if __name__ == "__main__":
58+
main()
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
"""Claude Code hook implementations."""
2+
3+
from python_claude.hooks.collect_edited_files import CollectEditedFilesHook
4+
from python_claude.hooks.git_status import GitStatusHook
5+
from python_claude.hooks.mypy import MypyHook
6+
from python_claude.hooks.pytest import PytestHook
7+
from python_claude.hooks.ruff_check import RuffCheckHook
8+
from python_claude.hooks.ruff_format import RuffFormatHook
9+
10+
__all__ = [
11+
"CollectEditedFilesHook",
12+
"GitStatusHook",
13+
"MypyHook",
14+
"PytestHook",
15+
"RuffCheckHook",
16+
"RuffFormatHook",
17+
]

0 commit comments

Comments
 (0)