git-commit-msg-ai 1.5.1__tar.gz → 1.7.0__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.
- {git_commit_msg_ai-1.5.1 → git_commit_msg_ai-1.7.0}/PKG-INFO +161 -137
- {git_commit_msg_ai-1.5.1 → git_commit_msg_ai-1.7.0}/README.md +24 -0
- {git_commit_msg_ai-1.5.1 → git_commit_msg_ai-1.7.0}/git_commit_msg_ai.egg-info/PKG-INFO +161 -137
- {git_commit_msg_ai-1.5.1 → git_commit_msg_ai-1.7.0}/git_commit_msg_ai.egg-info/SOURCES.txt +1 -0
- {git_commit_msg_ai-1.5.1 → git_commit_msg_ai-1.7.0}/pyproject.toml +1 -1
- {git_commit_msg_ai-1.5.1 → git_commit_msg_ai-1.7.0}/setup.cfg +4 -4
- git_commit_msg_ai-1.7.0/tests/test_generate_release_notes.py +193 -0
- {git_commit_msg_ai-1.5.1 → git_commit_msg_ai-1.7.0}/git_commit_msg_ai/__init__.py +0 -0
- {git_commit_msg_ai-1.5.1 → git_commit_msg_ai-1.7.0}/git_commit_msg_ai/ai_client.py +0 -0
- {git_commit_msg_ai-1.5.1 → git_commit_msg_ai-1.7.0}/git_commit_msg_ai/cli.py +0 -0
- {git_commit_msg_ai-1.5.1 → git_commit_msg_ai-1.7.0}/git_commit_msg_ai/editor.py +0 -0
- {git_commit_msg_ai-1.5.1 → git_commit_msg_ai-1.7.0}/git_commit_msg_ai/exceptions.py +0 -0
- {git_commit_msg_ai-1.5.1 → git_commit_msg_ai-1.7.0}/git_commit_msg_ai/git_ops.py +0 -0
- {git_commit_msg_ai-1.5.1 → git_commit_msg_ai-1.7.0}/git_commit_msg_ai.egg-info/dependency_links.txt +0 -0
- {git_commit_msg_ai-1.5.1 → git_commit_msg_ai-1.7.0}/git_commit_msg_ai.egg-info/entry_points.txt +0 -0
- {git_commit_msg_ai-1.5.1 → git_commit_msg_ai-1.7.0}/git_commit_msg_ai.egg-info/requires.txt +0 -0
- {git_commit_msg_ai-1.5.1 → git_commit_msg_ai-1.7.0}/git_commit_msg_ai.egg-info/top_level.txt +0 -0
- {git_commit_msg_ai-1.5.1 → git_commit_msg_ai-1.7.0}/tests/test_ai_client.py +0 -0
- {git_commit_msg_ai-1.5.1 → git_commit_msg_ai-1.7.0}/tests/test_cli.py +0 -0
- {git_commit_msg_ai-1.5.1 → git_commit_msg_ai-1.7.0}/tests/test_editor.py +0 -0
- {git_commit_msg_ai-1.5.1 → git_commit_msg_ai-1.7.0}/tests/test_exceptions.py +0 -0
- {git_commit_msg_ai-1.5.1 → git_commit_msg_ai-1.7.0}/tests/test_git_ops.py +0 -0
|
@@ -1,137 +1,161 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: git-commit-msg-ai
|
|
3
|
-
Version: 1.
|
|
4
|
-
Summary: AI-powered git commit message generator following Conventional Commits
|
|
5
|
-
License-Expression: MIT
|
|
6
|
-
Requires-Python: >=3.10
|
|
7
|
-
Description-Content-Type: text/markdown
|
|
8
|
-
Requires-Dist: anthropic
|
|
9
|
-
Provides-Extra: dev
|
|
10
|
-
Requires-Dist: mypy; extra == "dev"
|
|
11
|
-
Requires-Dist: ruff; extra == "dev"
|
|
12
|
-
Requires-Dist: pytest; extra == "dev"
|
|
13
|
-
Requires-Dist: pytest-cov; extra == "dev"
|
|
14
|
-
Requires-Dist: build; extra == "dev"
|
|
15
|
-
Requires-Dist: twine; extra == "dev"
|
|
16
|
-
|
|
17
|
-
# git-commit-msg-ai
|
|
18
|
-
|
|
19
|
-
AI-powered git commit message generator that follows the [Conventional Commits](https://www.conventionalcommits.org/) specification.
|
|
20
|
-
|
|
21
|
-
## Prerequisites
|
|
22
|
-
|
|
23
|
-
- Python 3.10+
|
|
24
|
-
- An Anthropic API key set as an environment variable:
|
|
25
|
-
|
|
26
|
-
```sh
|
|
27
|
-
export ANTHROPIC_API_KEY=sk-ant-... # macOS/Linux
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
```powershell
|
|
31
|
-
[System.Environment]::SetEnvironmentVariable('ANTHROPIC_API_KEY', 'sk-ant-...', 'User') # Windows
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
## Development Setup
|
|
35
|
-
|
|
36
|
-
Clone the repository, create a virtual environment, and install the project in editable mode with all dev dependencies:
|
|
37
|
-
|
|
38
|
-
```sh
|
|
39
|
-
python -m venv .venv
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
Activate the virtual environment:
|
|
43
|
-
|
|
44
|
-
```sh
|
|
45
|
-
# macOS/Linux
|
|
46
|
-
source .venv/bin/activate
|
|
47
|
-
|
|
48
|
-
# Windows PowerShell
|
|
49
|
-
.venv\Scripts\Activate.ps1
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
Install the project and dev dependencies:
|
|
53
|
-
|
|
54
|
-
```sh
|
|
55
|
-
pip install -e ".[dev]"
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
After activation the `git-commit-msg-ai` entry-point is on your PATH. You can also run the dev toolchain:
|
|
59
|
-
|
|
60
|
-
```sh
|
|
61
|
-
pytest # run tests with coverage
|
|
62
|
-
ruff check . # lint
|
|
63
|
-
mypy . # type-check
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
## Installation
|
|
67
|
-
|
|
68
|
-
```sh
|
|
69
|
-
pip install git-commit-msg-ai
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
## Usage
|
|
73
|
-
|
|
74
|
-
Stage your changes, then run the tool from inside any git repository:
|
|
75
|
-
|
|
76
|
-
```sh
|
|
77
|
-
git add <files>
|
|
78
|
-
git-commit-msg-ai
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
The tool will:
|
|
82
|
-
1. Read your staged diff
|
|
83
|
-
2. Generate a commit message using Claude AI
|
|
84
|
-
3. Print the message and prompt you to choose:
|
|
85
|
-
|
|
86
|
-
```
|
|
87
|
-
[a]ccept / [e]dit / [r]eject:
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
- **a** - commits immediately with the generated message
|
|
91
|
-
- **e** - opens the message in your `$EDITOR` (defaults to `notepad` on Windows, `vi` on Linux/macOS), lets you modify it, then commits
|
|
92
|
-
- **r** - exits without committing
|
|
93
|
-
|
|
94
|
-
## Commit message format
|
|
95
|
-
|
|
96
|
-
Generated messages follow the Conventional Commits specification:
|
|
97
|
-
|
|
98
|
-
```
|
|
99
|
-
<type>(<optional scope>): <short subject>
|
|
100
|
-
|
|
101
|
-
- Bullet explaining why this change was made
|
|
102
|
-
- Another reason if applicable
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
For breaking changes, the subject line gets a `!` and a footer is added:
|
|
106
|
-
|
|
107
|
-
```
|
|
108
|
-
feat(api)!: remove deprecated endpoint
|
|
109
|
-
|
|
110
|
-
- Endpoint was unused and blocking the new auth rollout
|
|
111
|
-
|
|
112
|
-
BREAKING CHANGE: /v1/legacy is no longer available
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
Supported types: `feat`, `fix`, `docs`, `style`, `refactor`, `test`, `chore`
|
|
116
|
-
|
|
117
|
-
##
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: git-commit-msg-ai
|
|
3
|
+
Version: 1.7.0
|
|
4
|
+
Summary: AI-powered git commit message generator following Conventional Commits
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
Requires-Python: >=3.10
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
Requires-Dist: anthropic
|
|
9
|
+
Provides-Extra: dev
|
|
10
|
+
Requires-Dist: mypy; extra == "dev"
|
|
11
|
+
Requires-Dist: ruff; extra == "dev"
|
|
12
|
+
Requires-Dist: pytest; extra == "dev"
|
|
13
|
+
Requires-Dist: pytest-cov; extra == "dev"
|
|
14
|
+
Requires-Dist: build; extra == "dev"
|
|
15
|
+
Requires-Dist: twine; extra == "dev"
|
|
16
|
+
|
|
17
|
+
# git-commit-msg-ai
|
|
18
|
+
|
|
19
|
+
AI-powered git commit message generator that follows the [Conventional Commits](https://www.conventionalcommits.org/) specification.
|
|
20
|
+
|
|
21
|
+
## Prerequisites
|
|
22
|
+
|
|
23
|
+
- Python 3.10+
|
|
24
|
+
- An Anthropic API key set as an environment variable:
|
|
25
|
+
|
|
26
|
+
```sh
|
|
27
|
+
export ANTHROPIC_API_KEY=sk-ant-... # macOS/Linux
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
```powershell
|
|
31
|
+
[System.Environment]::SetEnvironmentVariable('ANTHROPIC_API_KEY', 'sk-ant-...', 'User') # Windows
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Development Setup
|
|
35
|
+
|
|
36
|
+
Clone the repository, create a virtual environment, and install the project in editable mode with all dev dependencies:
|
|
37
|
+
|
|
38
|
+
```sh
|
|
39
|
+
python -m venv .venv
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Activate the virtual environment:
|
|
43
|
+
|
|
44
|
+
```sh
|
|
45
|
+
# macOS/Linux
|
|
46
|
+
source .venv/bin/activate
|
|
47
|
+
|
|
48
|
+
# Windows PowerShell
|
|
49
|
+
.venv\Scripts\Activate.ps1
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Install the project and dev dependencies:
|
|
53
|
+
|
|
54
|
+
```sh
|
|
55
|
+
pip install -e ".[dev]"
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
After activation the `git-commit-msg-ai` entry-point is on your PATH. You can also run the dev toolchain:
|
|
59
|
+
|
|
60
|
+
```sh
|
|
61
|
+
pytest # run tests with coverage
|
|
62
|
+
ruff check . # lint
|
|
63
|
+
mypy . # type-check
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Installation
|
|
67
|
+
|
|
68
|
+
```sh
|
|
69
|
+
pip install git-commit-msg-ai
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Usage
|
|
73
|
+
|
|
74
|
+
Stage your changes, then run the tool from inside any git repository:
|
|
75
|
+
|
|
76
|
+
```sh
|
|
77
|
+
git add <files>
|
|
78
|
+
git-commit-msg-ai
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
The tool will:
|
|
82
|
+
1. Read your staged diff
|
|
83
|
+
2. Generate a commit message using Claude AI
|
|
84
|
+
3. Print the message and prompt you to choose:
|
|
85
|
+
|
|
86
|
+
```
|
|
87
|
+
[a]ccept / [e]dit / [r]eject:
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
- **a** - commits immediately with the generated message
|
|
91
|
+
- **e** - opens the message in your `$EDITOR` (defaults to `notepad` on Windows, `vi` on Linux/macOS), lets you modify it, then commits
|
|
92
|
+
- **r** - exits without committing
|
|
93
|
+
|
|
94
|
+
## Commit message format
|
|
95
|
+
|
|
96
|
+
Generated messages follow the Conventional Commits specification:
|
|
97
|
+
|
|
98
|
+
```
|
|
99
|
+
<type>(<optional scope>): <short subject>
|
|
100
|
+
|
|
101
|
+
- Bullet explaining why this change was made
|
|
102
|
+
- Another reason if applicable
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
For breaking changes, the subject line gets a `!` and a footer is added:
|
|
106
|
+
|
|
107
|
+
```
|
|
108
|
+
feat(api)!: remove deprecated endpoint
|
|
109
|
+
|
|
110
|
+
- Endpoint was unused and blocking the new auth rollout
|
|
111
|
+
|
|
112
|
+
BREAKING CHANGE: /v1/legacy is no longer available
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Supported types: `feat`, `fix`, `docs`, `style`, `refactor`, `test`, `chore`
|
|
116
|
+
|
|
117
|
+
## CI/CD
|
|
118
|
+
|
|
119
|
+
Every push to `main` and every pull request runs the full CI pipeline (lint, type-check, tests, build).
|
|
120
|
+
|
|
121
|
+
Pushing a version tag (e.g. `v1.5.2`) triggers the CD pipeline:
|
|
122
|
+
|
|
123
|
+
1. Tests are re-run as a gate
|
|
124
|
+
2. The tag version is verified to match the version in `pyproject.toml`
|
|
125
|
+
3. The package is built and published to [PyPI](https://pypi.org/project/git-commit-msg-ai/)
|
|
126
|
+
4. A GitHub Release is created with release notes generated by Claude Haiku from the commit log
|
|
127
|
+
|
|
128
|
+
**Releasing a new version:**
|
|
129
|
+
|
|
130
|
+
```powershell
|
|
131
|
+
# 1. Bump the version in pyproject.toml
|
|
132
|
+
# 2. Commit and push
|
|
133
|
+
git add pyproject.toml
|
|
134
|
+
git commit -m "chore: bump version to 1.5.2"
|
|
135
|
+
git push origin main
|
|
136
|
+
# 3. Tag and push - this triggers the CD pipeline
|
|
137
|
+
git tag v1.5.2
|
|
138
|
+
git push origin v1.5.2
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## Debugging
|
|
142
|
+
|
|
143
|
+
By default the tool produces no diagnostic output. To enable logging, set the `GIT_COMMIT_AI_LOG_LEVEL` environment variable before running the command. Logs are written to **stderr** and do not interfere with the generated commit message on **stdout**.
|
|
144
|
+
|
|
145
|
+
Valid values: `DEBUG`, `INFO`, `WARNING`, `ERROR`, `CRITICAL`
|
|
146
|
+
|
|
147
|
+
```sh
|
|
148
|
+
# macOS/Linux - show all internal diagnostic messages
|
|
149
|
+
GIT_COMMIT_AI_LOG_LEVEL=DEBUG git-commit-msg-ai
|
|
150
|
+
|
|
151
|
+
# Windows PowerShell
|
|
152
|
+
$env:GIT_COMMIT_AI_LOG_LEVEL = 'DEBUG'
|
|
153
|
+
git-commit-msg-ai
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
| Level | What you see |
|
|
157
|
+
|---|---|
|
|
158
|
+
| `DEBUG` | git commands run, API model/token params, temp file paths, char counts |
|
|
159
|
+
| `INFO` | commit message generated, commit created |
|
|
160
|
+
| `WARNING` | no staged changes found |
|
|
161
|
+
| `ERROR` | git not found, API failures, editor errors |
|
|
@@ -98,6 +98,30 @@ BREAKING CHANGE: /v1/legacy is no longer available
|
|
|
98
98
|
|
|
99
99
|
Supported types: `feat`, `fix`, `docs`, `style`, `refactor`, `test`, `chore`
|
|
100
100
|
|
|
101
|
+
## CI/CD
|
|
102
|
+
|
|
103
|
+
Every push to `main` and every pull request runs the full CI pipeline (lint, type-check, tests, build).
|
|
104
|
+
|
|
105
|
+
Pushing a version tag (e.g. `v1.5.2`) triggers the CD pipeline:
|
|
106
|
+
|
|
107
|
+
1. Tests are re-run as a gate
|
|
108
|
+
2. The tag version is verified to match the version in `pyproject.toml`
|
|
109
|
+
3. The package is built and published to [PyPI](https://pypi.org/project/git-commit-msg-ai/)
|
|
110
|
+
4. A GitHub Release is created with release notes generated by Claude Haiku from the commit log
|
|
111
|
+
|
|
112
|
+
**Releasing a new version:**
|
|
113
|
+
|
|
114
|
+
```powershell
|
|
115
|
+
# 1. Bump the version in pyproject.toml
|
|
116
|
+
# 2. Commit and push
|
|
117
|
+
git add pyproject.toml
|
|
118
|
+
git commit -m "chore: bump version to 1.5.2"
|
|
119
|
+
git push origin main
|
|
120
|
+
# 3. Tag and push - this triggers the CD pipeline
|
|
121
|
+
git tag v1.5.2
|
|
122
|
+
git push origin v1.5.2
|
|
123
|
+
```
|
|
124
|
+
|
|
101
125
|
## Debugging
|
|
102
126
|
|
|
103
127
|
By default the tool produces no diagnostic output. To enable logging, set the `GIT_COMMIT_AI_LOG_LEVEL` environment variable before running the command. Logs are written to **stderr** and do not interfere with the generated commit message on **stdout**.
|
|
@@ -1,137 +1,161 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: git-commit-msg-ai
|
|
3
|
-
Version: 1.
|
|
4
|
-
Summary: AI-powered git commit message generator following Conventional Commits
|
|
5
|
-
License-Expression: MIT
|
|
6
|
-
Requires-Python: >=3.10
|
|
7
|
-
Description-Content-Type: text/markdown
|
|
8
|
-
Requires-Dist: anthropic
|
|
9
|
-
Provides-Extra: dev
|
|
10
|
-
Requires-Dist: mypy; extra == "dev"
|
|
11
|
-
Requires-Dist: ruff; extra == "dev"
|
|
12
|
-
Requires-Dist: pytest; extra == "dev"
|
|
13
|
-
Requires-Dist: pytest-cov; extra == "dev"
|
|
14
|
-
Requires-Dist: build; extra == "dev"
|
|
15
|
-
Requires-Dist: twine; extra == "dev"
|
|
16
|
-
|
|
17
|
-
# git-commit-msg-ai
|
|
18
|
-
|
|
19
|
-
AI-powered git commit message generator that follows the [Conventional Commits](https://www.conventionalcommits.org/) specification.
|
|
20
|
-
|
|
21
|
-
## Prerequisites
|
|
22
|
-
|
|
23
|
-
- Python 3.10+
|
|
24
|
-
- An Anthropic API key set as an environment variable:
|
|
25
|
-
|
|
26
|
-
```sh
|
|
27
|
-
export ANTHROPIC_API_KEY=sk-ant-... # macOS/Linux
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
```powershell
|
|
31
|
-
[System.Environment]::SetEnvironmentVariable('ANTHROPIC_API_KEY', 'sk-ant-...', 'User') # Windows
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
## Development Setup
|
|
35
|
-
|
|
36
|
-
Clone the repository, create a virtual environment, and install the project in editable mode with all dev dependencies:
|
|
37
|
-
|
|
38
|
-
```sh
|
|
39
|
-
python -m venv .venv
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
Activate the virtual environment:
|
|
43
|
-
|
|
44
|
-
```sh
|
|
45
|
-
# macOS/Linux
|
|
46
|
-
source .venv/bin/activate
|
|
47
|
-
|
|
48
|
-
# Windows PowerShell
|
|
49
|
-
.venv\Scripts\Activate.ps1
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
Install the project and dev dependencies:
|
|
53
|
-
|
|
54
|
-
```sh
|
|
55
|
-
pip install -e ".[dev]"
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
After activation the `git-commit-msg-ai` entry-point is on your PATH. You can also run the dev toolchain:
|
|
59
|
-
|
|
60
|
-
```sh
|
|
61
|
-
pytest # run tests with coverage
|
|
62
|
-
ruff check . # lint
|
|
63
|
-
mypy . # type-check
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
## Installation
|
|
67
|
-
|
|
68
|
-
```sh
|
|
69
|
-
pip install git-commit-msg-ai
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
## Usage
|
|
73
|
-
|
|
74
|
-
Stage your changes, then run the tool from inside any git repository:
|
|
75
|
-
|
|
76
|
-
```sh
|
|
77
|
-
git add <files>
|
|
78
|
-
git-commit-msg-ai
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
The tool will:
|
|
82
|
-
1. Read your staged diff
|
|
83
|
-
2. Generate a commit message using Claude AI
|
|
84
|
-
3. Print the message and prompt you to choose:
|
|
85
|
-
|
|
86
|
-
```
|
|
87
|
-
[a]ccept / [e]dit / [r]eject:
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
- **a** - commits immediately with the generated message
|
|
91
|
-
- **e** - opens the message in your `$EDITOR` (defaults to `notepad` on Windows, `vi` on Linux/macOS), lets you modify it, then commits
|
|
92
|
-
- **r** - exits without committing
|
|
93
|
-
|
|
94
|
-
## Commit message format
|
|
95
|
-
|
|
96
|
-
Generated messages follow the Conventional Commits specification:
|
|
97
|
-
|
|
98
|
-
```
|
|
99
|
-
<type>(<optional scope>): <short subject>
|
|
100
|
-
|
|
101
|
-
- Bullet explaining why this change was made
|
|
102
|
-
- Another reason if applicable
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
For breaking changes, the subject line gets a `!` and a footer is added:
|
|
106
|
-
|
|
107
|
-
```
|
|
108
|
-
feat(api)!: remove deprecated endpoint
|
|
109
|
-
|
|
110
|
-
- Endpoint was unused and blocking the new auth rollout
|
|
111
|
-
|
|
112
|
-
BREAKING CHANGE: /v1/legacy is no longer available
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
Supported types: `feat`, `fix`, `docs`, `style`, `refactor`, `test`, `chore`
|
|
116
|
-
|
|
117
|
-
##
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: git-commit-msg-ai
|
|
3
|
+
Version: 1.7.0
|
|
4
|
+
Summary: AI-powered git commit message generator following Conventional Commits
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
Requires-Python: >=3.10
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
Requires-Dist: anthropic
|
|
9
|
+
Provides-Extra: dev
|
|
10
|
+
Requires-Dist: mypy; extra == "dev"
|
|
11
|
+
Requires-Dist: ruff; extra == "dev"
|
|
12
|
+
Requires-Dist: pytest; extra == "dev"
|
|
13
|
+
Requires-Dist: pytest-cov; extra == "dev"
|
|
14
|
+
Requires-Dist: build; extra == "dev"
|
|
15
|
+
Requires-Dist: twine; extra == "dev"
|
|
16
|
+
|
|
17
|
+
# git-commit-msg-ai
|
|
18
|
+
|
|
19
|
+
AI-powered git commit message generator that follows the [Conventional Commits](https://www.conventionalcommits.org/) specification.
|
|
20
|
+
|
|
21
|
+
## Prerequisites
|
|
22
|
+
|
|
23
|
+
- Python 3.10+
|
|
24
|
+
- An Anthropic API key set as an environment variable:
|
|
25
|
+
|
|
26
|
+
```sh
|
|
27
|
+
export ANTHROPIC_API_KEY=sk-ant-... # macOS/Linux
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
```powershell
|
|
31
|
+
[System.Environment]::SetEnvironmentVariable('ANTHROPIC_API_KEY', 'sk-ant-...', 'User') # Windows
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Development Setup
|
|
35
|
+
|
|
36
|
+
Clone the repository, create a virtual environment, and install the project in editable mode with all dev dependencies:
|
|
37
|
+
|
|
38
|
+
```sh
|
|
39
|
+
python -m venv .venv
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Activate the virtual environment:
|
|
43
|
+
|
|
44
|
+
```sh
|
|
45
|
+
# macOS/Linux
|
|
46
|
+
source .venv/bin/activate
|
|
47
|
+
|
|
48
|
+
# Windows PowerShell
|
|
49
|
+
.venv\Scripts\Activate.ps1
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Install the project and dev dependencies:
|
|
53
|
+
|
|
54
|
+
```sh
|
|
55
|
+
pip install -e ".[dev]"
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
After activation the `git-commit-msg-ai` entry-point is on your PATH. You can also run the dev toolchain:
|
|
59
|
+
|
|
60
|
+
```sh
|
|
61
|
+
pytest # run tests with coverage
|
|
62
|
+
ruff check . # lint
|
|
63
|
+
mypy . # type-check
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Installation
|
|
67
|
+
|
|
68
|
+
```sh
|
|
69
|
+
pip install git-commit-msg-ai
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Usage
|
|
73
|
+
|
|
74
|
+
Stage your changes, then run the tool from inside any git repository:
|
|
75
|
+
|
|
76
|
+
```sh
|
|
77
|
+
git add <files>
|
|
78
|
+
git-commit-msg-ai
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
The tool will:
|
|
82
|
+
1. Read your staged diff
|
|
83
|
+
2. Generate a commit message using Claude AI
|
|
84
|
+
3. Print the message and prompt you to choose:
|
|
85
|
+
|
|
86
|
+
```
|
|
87
|
+
[a]ccept / [e]dit / [r]eject:
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
- **a** - commits immediately with the generated message
|
|
91
|
+
- **e** - opens the message in your `$EDITOR` (defaults to `notepad` on Windows, `vi` on Linux/macOS), lets you modify it, then commits
|
|
92
|
+
- **r** - exits without committing
|
|
93
|
+
|
|
94
|
+
## Commit message format
|
|
95
|
+
|
|
96
|
+
Generated messages follow the Conventional Commits specification:
|
|
97
|
+
|
|
98
|
+
```
|
|
99
|
+
<type>(<optional scope>): <short subject>
|
|
100
|
+
|
|
101
|
+
- Bullet explaining why this change was made
|
|
102
|
+
- Another reason if applicable
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
For breaking changes, the subject line gets a `!` and a footer is added:
|
|
106
|
+
|
|
107
|
+
```
|
|
108
|
+
feat(api)!: remove deprecated endpoint
|
|
109
|
+
|
|
110
|
+
- Endpoint was unused and blocking the new auth rollout
|
|
111
|
+
|
|
112
|
+
BREAKING CHANGE: /v1/legacy is no longer available
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Supported types: `feat`, `fix`, `docs`, `style`, `refactor`, `test`, `chore`
|
|
116
|
+
|
|
117
|
+
## CI/CD
|
|
118
|
+
|
|
119
|
+
Every push to `main` and every pull request runs the full CI pipeline (lint, type-check, tests, build).
|
|
120
|
+
|
|
121
|
+
Pushing a version tag (e.g. `v1.5.2`) triggers the CD pipeline:
|
|
122
|
+
|
|
123
|
+
1. Tests are re-run as a gate
|
|
124
|
+
2. The tag version is verified to match the version in `pyproject.toml`
|
|
125
|
+
3. The package is built and published to [PyPI](https://pypi.org/project/git-commit-msg-ai/)
|
|
126
|
+
4. A GitHub Release is created with release notes generated by Claude Haiku from the commit log
|
|
127
|
+
|
|
128
|
+
**Releasing a new version:**
|
|
129
|
+
|
|
130
|
+
```powershell
|
|
131
|
+
# 1. Bump the version in pyproject.toml
|
|
132
|
+
# 2. Commit and push
|
|
133
|
+
git add pyproject.toml
|
|
134
|
+
git commit -m "chore: bump version to 1.5.2"
|
|
135
|
+
git push origin main
|
|
136
|
+
# 3. Tag and push - this triggers the CD pipeline
|
|
137
|
+
git tag v1.5.2
|
|
138
|
+
git push origin v1.5.2
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## Debugging
|
|
142
|
+
|
|
143
|
+
By default the tool produces no diagnostic output. To enable logging, set the `GIT_COMMIT_AI_LOG_LEVEL` environment variable before running the command. Logs are written to **stderr** and do not interfere with the generated commit message on **stdout**.
|
|
144
|
+
|
|
145
|
+
Valid values: `DEBUG`, `INFO`, `WARNING`, `ERROR`, `CRITICAL`
|
|
146
|
+
|
|
147
|
+
```sh
|
|
148
|
+
# macOS/Linux - show all internal diagnostic messages
|
|
149
|
+
GIT_COMMIT_AI_LOG_LEVEL=DEBUG git-commit-msg-ai
|
|
150
|
+
|
|
151
|
+
# Windows PowerShell
|
|
152
|
+
$env:GIT_COMMIT_AI_LOG_LEVEL = 'DEBUG'
|
|
153
|
+
git-commit-msg-ai
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
| Level | What you see |
|
|
157
|
+
|---|---|
|
|
158
|
+
| `DEBUG` | git commands run, API model/token params, temp file paths, char counts |
|
|
159
|
+
| `INFO` | commit message generated, commit created |
|
|
160
|
+
| `WARNING` | no staged changes found |
|
|
161
|
+
| `ERROR` | git not found, API failures, editor errors |
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
[egg_info]
|
|
2
|
-
tag_build =
|
|
3
|
-
tag_date = 0
|
|
4
|
-
|
|
1
|
+
[egg_info]
|
|
2
|
+
tag_build =
|
|
3
|
+
tag_date = 0
|
|
4
|
+
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import subprocess
|
|
2
|
+
import textwrap
|
|
3
|
+
from contextlib import ExitStack
|
|
4
|
+
from unittest.mock import MagicMock, call, patch
|
|
5
|
+
|
|
6
|
+
import anthropic
|
|
7
|
+
import pytest
|
|
8
|
+
|
|
9
|
+
from scripts.generate_release_notes import (
|
|
10
|
+
ANTHROPIC_MODEL,
|
|
11
|
+
GIT_COMMAND,
|
|
12
|
+
generate_release_notes,
|
|
13
|
+
get_commit_log,
|
|
14
|
+
main,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _make_api_response(text: str) -> MagicMock:
|
|
19
|
+
mock_text_block = MagicMock(spec=anthropic.types.TextBlock)
|
|
20
|
+
mock_text_block.text = text
|
|
21
|
+
|
|
22
|
+
mock_response = MagicMock(spec=anthropic.types.Message)
|
|
23
|
+
mock_response.content = [mock_text_block]
|
|
24
|
+
|
|
25
|
+
return mock_response
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class TestGetCommitLog:
|
|
29
|
+
def test_uses_range_when_previous_tag_given(self) -> None:
|
|
30
|
+
git_raw_output = """abc123 feat: add feature
|
|
31
|
+
"""
|
|
32
|
+
with patch("scripts.generate_release_notes.subprocess.run") as mock_run:
|
|
33
|
+
mock_run.return_value = MagicMock(stdout=git_raw_output)
|
|
34
|
+
|
|
35
|
+
result = get_commit_log("v1.5.2", "v1.5.1")
|
|
36
|
+
|
|
37
|
+
expected_call = call(
|
|
38
|
+
[GIT_COMMAND, "log", "v1.5.1..v1.5.2", "--oneline", "--no-merges"],
|
|
39
|
+
capture_output=True,
|
|
40
|
+
text=True,
|
|
41
|
+
check=True,
|
|
42
|
+
)
|
|
43
|
+
mock_run.assert_called_once_with(*expected_call.args, **expected_call.kwargs)
|
|
44
|
+
assert result == "abc123 feat: add feature"
|
|
45
|
+
|
|
46
|
+
def test_uses_single_tag_when_no_previous_tag(self) -> None:
|
|
47
|
+
git_raw_output = """abc123 feat: initial release
|
|
48
|
+
"""
|
|
49
|
+
with patch("scripts.generate_release_notes.subprocess.run") as mock_run:
|
|
50
|
+
mock_run.return_value = MagicMock(stdout=git_raw_output)
|
|
51
|
+
|
|
52
|
+
result = get_commit_log("v1.0.0", "")
|
|
53
|
+
|
|
54
|
+
expected_call = call(
|
|
55
|
+
[GIT_COMMAND, "log", "v1.0.0", "--oneline", "--no-merges"],
|
|
56
|
+
capture_output=True,
|
|
57
|
+
text=True,
|
|
58
|
+
check=True,
|
|
59
|
+
)
|
|
60
|
+
mock_run.assert_called_once_with(*expected_call.args, **expected_call.kwargs)
|
|
61
|
+
assert result == "abc123 feat: initial release"
|
|
62
|
+
|
|
63
|
+
def test_strips_trailing_whitespace_from_output(self) -> None:
|
|
64
|
+
git_raw_output = """abc123 fix: bug
|
|
65
|
+
|
|
66
|
+
"""
|
|
67
|
+
with patch("scripts.generate_release_notes.subprocess.run") as mock_run:
|
|
68
|
+
mock_run.return_value = MagicMock(stdout=git_raw_output)
|
|
69
|
+
|
|
70
|
+
result = get_commit_log("v1.5.2", "v1.5.1")
|
|
71
|
+
|
|
72
|
+
assert result == "abc123 fix: bug"
|
|
73
|
+
|
|
74
|
+
def test_propagates_subprocess_error(self) -> None:
|
|
75
|
+
with patch("scripts.generate_release_notes.subprocess.run") as mock_run:
|
|
76
|
+
mock_run.side_effect = subprocess.CalledProcessError(128, GIT_COMMAND)
|
|
77
|
+
|
|
78
|
+
with pytest.raises(subprocess.CalledProcessError):
|
|
79
|
+
get_commit_log("v1.5.2", "v1.5.1")
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class TestGenerateReleaseNotes:
|
|
83
|
+
def test_returns_response_text(self) -> None:
|
|
84
|
+
expected_notes = textwrap.dedent("""
|
|
85
|
+
## Features
|
|
86
|
+
- Added new feature
|
|
87
|
+
""").strip()
|
|
88
|
+
|
|
89
|
+
with ExitStack() as stack:
|
|
90
|
+
mock_anthropic_class = stack.enter_context(patch("scripts.generate_release_notes.anthropic.Anthropic"))
|
|
91
|
+
mock_client = MagicMock()
|
|
92
|
+
mock_client.messages.create.return_value = _make_api_response(expected_notes)
|
|
93
|
+
mock_anthropic_class.return_value = mock_client
|
|
94
|
+
|
|
95
|
+
result = generate_release_notes("abc123 feat: add feature", "1.5.2")
|
|
96
|
+
|
|
97
|
+
assert result == expected_notes
|
|
98
|
+
|
|
99
|
+
def test_calls_haiku_model(self) -> None:
|
|
100
|
+
with ExitStack() as stack:
|
|
101
|
+
mock_anthropic_class = stack.enter_context(patch("scripts.generate_release_notes.anthropic.Anthropic"))
|
|
102
|
+
mock_client = MagicMock()
|
|
103
|
+
mock_client.messages.create.return_value = _make_api_response("notes")
|
|
104
|
+
mock_anthropic_class.return_value = mock_client
|
|
105
|
+
|
|
106
|
+
generate_release_notes("abc123 feat: add feature", "1.5.2")
|
|
107
|
+
|
|
108
|
+
create_call_kwargs = mock_client.messages.create.call_args.kwargs
|
|
109
|
+
assert create_call_kwargs["model"] == ANTHROPIC_MODEL
|
|
110
|
+
|
|
111
|
+
def test_includes_version_in_prompt(self) -> None:
|
|
112
|
+
with ExitStack() as stack:
|
|
113
|
+
mock_anthropic_class = stack.enter_context(patch("scripts.generate_release_notes.anthropic.Anthropic"))
|
|
114
|
+
mock_client = MagicMock()
|
|
115
|
+
mock_client.messages.create.return_value = _make_api_response("notes")
|
|
116
|
+
mock_anthropic_class.return_value = mock_client
|
|
117
|
+
|
|
118
|
+
generate_release_notes("abc123 feat: add feature", "1.5.2")
|
|
119
|
+
|
|
120
|
+
create_call_kwargs = mock_client.messages.create.call_args.kwargs
|
|
121
|
+
prompt_content = create_call_kwargs["messages"][0]["content"]
|
|
122
|
+
assert "1.5.2" in prompt_content
|
|
123
|
+
|
|
124
|
+
def test_includes_commit_log_in_prompt(self) -> None:
|
|
125
|
+
commit_log = textwrap.dedent("""
|
|
126
|
+
abc123 feat: add feature
|
|
127
|
+
def456 fix: patch bug
|
|
128
|
+
""").strip()
|
|
129
|
+
|
|
130
|
+
with ExitStack() as stack:
|
|
131
|
+
mock_anthropic_class = stack.enter_context(patch("scripts.generate_release_notes.anthropic.Anthropic"))
|
|
132
|
+
mock_client = MagicMock()
|
|
133
|
+
mock_client.messages.create.return_value = _make_api_response("notes")
|
|
134
|
+
mock_anthropic_class.return_value = mock_client
|
|
135
|
+
|
|
136
|
+
generate_release_notes(commit_log, "1.5.2")
|
|
137
|
+
|
|
138
|
+
create_call_kwargs = mock_client.messages.create.call_args.kwargs
|
|
139
|
+
prompt_content = create_call_kwargs["messages"][0]["content"]
|
|
140
|
+
assert commit_log in prompt_content
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
class TestMain:
|
|
144
|
+
def test_prints_generated_notes(self, monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str]) -> None:
|
|
145
|
+
monkeypatch.setenv("CURRENT_TAG", "v1.5.2")
|
|
146
|
+
monkeypatch.setenv("PREVIOUS_TAG", "v1.5.1")
|
|
147
|
+
|
|
148
|
+
with ExitStack() as stack:
|
|
149
|
+
mock_get_commit_log = stack.enter_context(patch("scripts.generate_release_notes.get_commit_log"))
|
|
150
|
+
mock_generate_release_notes = stack.enter_context(patch("scripts.generate_release_notes.generate_release_notes"))
|
|
151
|
+
mock_get_commit_log.return_value = "abc123 feat: add feature"
|
|
152
|
+
mock_generate_release_notes.return_value = textwrap.dedent("""
|
|
153
|
+
## Features
|
|
154
|
+
- Added new feature
|
|
155
|
+
""").strip()
|
|
156
|
+
|
|
157
|
+
main()
|
|
158
|
+
|
|
159
|
+
captured = capsys.readouterr()
|
|
160
|
+
assert "## Features" in captured.out
|
|
161
|
+
assert "Added new feature" in captured.out
|
|
162
|
+
|
|
163
|
+
def test_strips_v_prefix_from_version(self, monkeypatch: pytest.MonkeyPatch) -> None:
|
|
164
|
+
monkeypatch.setenv("CURRENT_TAG", "v1.5.2")
|
|
165
|
+
monkeypatch.setenv("PREVIOUS_TAG", "v1.5.1")
|
|
166
|
+
|
|
167
|
+
with ExitStack() as stack:
|
|
168
|
+
stack.enter_context(patch("scripts.generate_release_notes.get_commit_log", return_value="log"))
|
|
169
|
+
mock_generate_release_notes = stack.enter_context(patch("scripts.generate_release_notes.generate_release_notes", return_value="notes"))
|
|
170
|
+
|
|
171
|
+
main()
|
|
172
|
+
|
|
173
|
+
called_version = mock_generate_release_notes.call_args.args[1]
|
|
174
|
+
assert called_version == "1.5.2"
|
|
175
|
+
|
|
176
|
+
def test_passes_empty_previous_tag_when_env_var_absent(self, monkeypatch: pytest.MonkeyPatch) -> None:
|
|
177
|
+
monkeypatch.setenv("CURRENT_TAG", "v1.0.0")
|
|
178
|
+
monkeypatch.delenv("PREVIOUS_TAG", raising=False)
|
|
179
|
+
|
|
180
|
+
with ExitStack() as stack:
|
|
181
|
+
mock_get_commit_log = stack.enter_context(patch("scripts.generate_release_notes.get_commit_log", return_value="log"))
|
|
182
|
+
stack.enter_context(patch("scripts.generate_release_notes.generate_release_notes", return_value="notes"))
|
|
183
|
+
|
|
184
|
+
main()
|
|
185
|
+
|
|
186
|
+
called_previous_tag = mock_get_commit_log.call_args.args[1]
|
|
187
|
+
assert called_previous_tag == ""
|
|
188
|
+
|
|
189
|
+
def test_raises_when_current_tag_env_var_absent(self, monkeypatch: pytest.MonkeyPatch) -> None:
|
|
190
|
+
monkeypatch.delenv("CURRENT_TAG", raising=False)
|
|
191
|
+
|
|
192
|
+
with pytest.raises(KeyError):
|
|
193
|
+
main()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{git_commit_msg_ai-1.5.1 → git_commit_msg_ai-1.7.0}/git_commit_msg_ai.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{git_commit_msg_ai-1.5.1 → git_commit_msg_ai-1.7.0}/git_commit_msg_ai.egg-info/entry_points.txt
RENAMED
|
File without changes
|
|
File without changes
|
{git_commit_msg_ai-1.5.1 → git_commit_msg_ai-1.7.0}/git_commit_msg_ai.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|