git-commit-msg-ai 1.4.2__py3-none-any.whl → 1.5.1__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.
@@ -1,3 +1,4 @@
1
+ import logging
1
2
  import textwrap
2
3
  from typing import Final, cast
3
4
 
@@ -5,6 +6,8 @@ import anthropic
5
6
 
6
7
  from git_commit_msg_ai.exceptions import AIError
7
8
 
9
+ logger = logging.getLogger(__name__)
10
+
8
11
  SYSTEM_PROMPT: Final[str] = textwrap.dedent("""\
9
12
  You are a Git commit message generator. Output only the commit message, nothing else. Follow the Conventional Commits specification:
10
13
  - First line: <type>(<optional scope>)<!>: <short subject> (50 chars max); append ! before the colon if the commit introduces a breaking change
@@ -13,7 +16,7 @@ SYSTEM_PROMPT: Final[str] = textwrap.dedent("""\
13
16
  - If there is a breaking change, add a blank line after the body followed by "BREAKING CHANGE: <description of what breaks and why>
14
17
  Types: feat, fix, docs, style, refactor, test, chore""")
15
18
 
16
- MODEL: Final[str] = 'claude-haiku-4-5-20251001'
19
+ MODEL: Final[str] = "claude-haiku-4-5-20251001"
17
20
  MAX_TOKENS: Final[int] = 1024
18
21
 
19
22
 
@@ -21,22 +24,29 @@ def generate_commit_message(diff: str) -> str:
21
24
  try:
22
25
  anthropic_client = anthropic.Anthropic()
23
26
 
27
+ logger.debug("Calling Anthropic API: model=%s max_tokens=%d", MODEL, MAX_TOKENS)
24
28
  anthropic_api_response = anthropic_client.messages.create(
25
29
  model=MODEL,
26
30
  max_tokens=MAX_TOKENS,
27
31
  system=SYSTEM_PROMPT,
28
- messages=[{'role': 'user', 'content': diff}],
32
+ messages=[{"role": "user", "content": diff}],
29
33
  )
30
34
  except anthropic.AuthenticationError:
31
- raise AIError('Anthropic API key is missing or invalid. Set the ANTHROPIC_API_KEY environment variable.')
35
+ logger.error("Anthropic authentication error")
36
+ raise AIError("Anthropic API key is missing or invalid. Set the ANTHROPIC_API_KEY environment variable.")
32
37
  except anthropic.RateLimitError:
33
- raise AIError('Anthropic API rate limit reached. Wait a moment and try again.')
38
+ logger.error("Anthropic rate limit error")
39
+ raise AIError("Anthropic API rate limit reached. Wait a moment and try again.")
34
40
  except anthropic.APIConnectionError:
35
- raise AIError('Could not reach the Anthropic API. Check your network connection.')
41
+ logger.error("Anthropic API connection error")
42
+ raise AIError("Could not reach the Anthropic API. Check your network connection.")
36
43
  except anthropic.APIStatusError as error:
37
- raise AIError(f'Anthropic API returned an error: {error.status_code}.')
44
+ logger.error("Anthropic API status error: %s", error.status_code)
45
+ raise AIError(f"Anthropic API returned an error: {error.status_code}.")
38
46
 
39
47
  anthropic_api_response_message = anthropic_api_response.content[0]
40
48
  commit_message = cast(anthropic.types.TextBlock, anthropic_api_response_message).text.strip()
41
49
 
50
+ logger.info("Commit message generated: %d chars", len(commit_message))
51
+
42
52
  return commit_message
git_commit_msg_ai/cli.py CHANGED
@@ -1,35 +1,51 @@
1
+ import logging
2
+ import os
1
3
  import sys
4
+ from typing import Final
2
5
 
3
6
  from git_commit_msg_ai import ai_client, editor, git_ops
4
7
  from git_commit_msg_ai.exceptions import GitCommitAIError
5
8
 
9
+ LOG_LEVEL_ENV_VAR: Final[str] = "GIT_COMMIT_AI_LOG_LEVEL"
10
+
6
11
 
7
12
  def main() -> None:
13
+ log_level_name = os.environ.get(LOG_LEVEL_ENV_VAR, "WARNING").upper()
14
+ numeric_log_level = getattr(logging, log_level_name, None)
15
+
16
+ if not isinstance(numeric_log_level, int):
17
+ numeric_log_level = logging.WARNING
18
+
19
+ logging.basicConfig(level=numeric_log_level, format="%(levelname)s:%(name)s:%(message)s", stream=sys.stderr)
20
+
21
+ if not isinstance(getattr(logging, log_level_name, None), int):
22
+ logging.warning("Invalid %s value %r, defaulting to WARNING", LOG_LEVEL_ENV_VAR, log_level_name)
23
+
8
24
  try:
9
25
  diff = git_ops.get_staged_diff()
10
26
  commit_message = ai_client.generate_commit_message(diff)
11
27
  print(commit_message)
12
28
 
13
29
  print()
14
- user_selection = input('[a]ccept / [e]dit / [r]eject: ').strip().lower()
30
+ user_selection = input("[a]ccept / [e]dit / [r]eject: ").strip().lower()
15
31
 
16
- if user_selection == 'a':
32
+ if user_selection == "a":
17
33
  git_ops.commit(commit_message)
18
- elif user_selection == 'e':
34
+ elif user_selection == "e":
19
35
  updated_commit_message = editor.open_in_editor(commit_message)
20
36
  git_ops.commit(updated_commit_message)
21
- elif user_selection == 'r':
22
- print('User rejected the generated commit message. No commit made.')
37
+ elif user_selection == "r":
38
+ print("User rejected the generated commit message. No commit made.")
23
39
  else:
24
- print('Invalid selection.')
40
+ print("Invalid selection.")
25
41
  sys.exit(1)
26
42
  except GitCommitAIError as error:
27
43
  print(str(error))
28
44
  sys.exit(1)
29
45
  except (KeyboardInterrupt, EOFError):
30
- print('Aborted.')
46
+ print("Aborted.")
31
47
  sys.exit(1)
32
48
 
33
49
 
34
- if __name__ == '__main__': # pragma: no cover
50
+ if __name__ == "__main__": # pragma: no cover
35
51
  main()
@@ -1,3 +1,4 @@
1
+ import logging
1
2
  import os
2
3
  import platform
3
4
  import subprocess
@@ -5,39 +6,54 @@ import tempfile
5
6
 
6
7
  from git_commit_msg_ai.exceptions import EditorError
7
8
 
9
+ logger = logging.getLogger(__name__)
10
+
8
11
 
9
12
  def get_default_editor() -> str:
10
13
  current_platform = platform.system()
11
- is_windows = current_platform == 'Windows'
14
+ is_windows = current_platform == "Windows"
12
15
 
13
- return 'notepad' if is_windows else 'vi'
16
+ return "notepad" if is_windows else "vi"
14
17
 
15
18
 
16
19
  def open_in_editor(initial_text: str) -> str:
17
20
  try:
18
- with tempfile.NamedTemporaryFile(suffix='.txt', mode='w', delete=False) as temp_file:
21
+ with tempfile.NamedTemporaryFile(suffix=".txt", mode="w", delete=False) as temp_file:
19
22
  temp_file.write(initial_text)
20
23
  temp_file_path = temp_file.name
21
24
  except OSError:
22
- raise EditorError('Could not create a temporary file.')
25
+ logger.error("Could not create temporary file")
26
+ raise EditorError("Could not create a temporary file.")
27
+
28
+ logger.debug("Temporary file created: %s", temp_file_path)
23
29
 
24
30
  platform_default_editor = get_default_editor()
25
- editor_command = os.environ.get('EDITOR', platform_default_editor)
31
+ editor_command = os.environ.get("EDITOR", platform_default_editor)
32
+
33
+ logger.debug("Selected editor: %s", editor_command)
26
34
 
27
35
  try:
28
36
  try:
37
+ logger.debug("Opening editor: %s %s", editor_command, temp_file_path)
29
38
  subprocess.run([editor_command, temp_file_path], check=True)
30
39
  except FileNotFoundError:
40
+ logger.error("Editor not found: %s", editor_command)
31
41
  raise EditorError(f'Editor "{editor_command}" was not found. Set the EDITOR environment variable to a valid editor.')
32
42
  except subprocess.CalledProcessError:
43
+ logger.error("Editor exited with error: %s", editor_command)
33
44
  raise EditorError(f'Editor "{editor_command}" exited with an error.')
34
45
 
46
+ logger.debug("Editor closed, reading edited content")
47
+
35
48
  try:
36
49
  with open(temp_file_path) as temp_file:
37
50
  edited_text = temp_file.read().strip()
38
51
  except OSError:
39
- raise EditorError('Could not read the edited commit message from the temporary file.')
52
+ logger.error("Could not read temporary file: %s", temp_file_path)
53
+ raise EditorError("Could not read the edited commit message from the temporary file.")
40
54
  finally:
41
55
  os.unlink(temp_file_path)
42
56
 
57
+ logger.debug("Edited text read: %d chars", len(edited_text))
58
+
43
59
  return edited_text
@@ -1,36 +1,52 @@
1
+ import logging
1
2
  import subprocess
2
3
  from typing import Final
3
4
 
4
5
  from git_commit_msg_ai.exceptions import GitError
5
6
 
6
- GIT_COMMAND: Final[str] = 'git'
7
- DIFF_SUBCOMMAND: Final[str] = 'diff'
8
- CACHED_FLAG: Final[str] = '--cached'
9
- COMMIT_SUBCOMMAND: Final[str] = 'commit'
10
- MESSAGE_FLAG: Final[str] = '-m'
11
- UTF8_ENCODING: Final[str] = 'utf-8'
7
+ GIT_COMMAND: Final[str] = "git"
8
+ DIFF_SUBCOMMAND: Final[str] = "diff"
9
+ CACHED_FLAG: Final[str] = "--cached"
10
+ COMMIT_SUBCOMMAND: Final[str] = "commit"
11
+ MESSAGE_FLAG: Final[str] = "-m"
12
+ UTF8_ENCODING: Final[str] = "utf-8"
13
+
14
+ logger = logging.getLogger(__name__)
12
15
 
13
16
 
14
17
  def get_staged_diff() -> str:
15
18
  try:
16
- staged_diff = subprocess.check_output([GIT_COMMAND, DIFF_SUBCOMMAND, CACHED_FLAG]).decode(UTF8_ENCODING)
19
+ logger.debug("Running: %s %s %s", GIT_COMMAND, DIFF_SUBCOMMAND, CACHED_FLAG)
20
+ raw_diff_bytes = subprocess.check_output([GIT_COMMAND, DIFF_SUBCOMMAND, CACHED_FLAG])
21
+ staged_diff = raw_diff_bytes.decode(UTF8_ENCODING)
17
22
  except FileNotFoundError:
18
- raise GitError('git is not installed or not on PATH.')
23
+ logger.error("git not found on PATH")
24
+ raise GitError("git is not installed or not on PATH.")
19
25
  except subprocess.CalledProcessError:
20
- raise GitError('Failed to get staged diff. Are you inside a git repository?')
26
+ logger.error("git diff --cached exited non-zero")
27
+ raise GitError("Failed to get staged diff. Are you inside a git repository?")
21
28
  except UnicodeDecodeError:
22
- raise GitError('Staged diff contains bytes that could not be decoded as UTF-8.')
29
+ logger.error("Staged diff could not be decoded as UTF-8")
30
+ raise GitError("Staged diff contains bytes that could not be decoded as UTF-8.")
31
+
32
+ logger.debug("Staged diff received: %d chars", len(staged_diff))
23
33
 
24
34
  if not staged_diff:
25
- raise GitError('No staged changes found. Stage files with git add before running.')
35
+ logger.warning("No staged changes found")
36
+ raise GitError("No staged changes found. Stage files with git add before running.")
26
37
 
27
38
  return staged_diff
28
39
 
29
40
 
30
41
  def commit(message: str) -> None:
31
42
  try:
43
+ logger.debug("Running: %s %s %s <message>", GIT_COMMAND, COMMIT_SUBCOMMAND, MESSAGE_FLAG)
32
44
  subprocess.run([GIT_COMMAND, COMMIT_SUBCOMMAND, MESSAGE_FLAG, message], check=True)
33
45
  except FileNotFoundError:
34
- raise GitError('git is not installed or not on PATH.')
46
+ logger.error("git not found on PATH")
47
+ raise GitError("git is not installed or not on PATH.")
35
48
  except subprocess.CalledProcessError:
36
- raise GitError('git commit failed.')
49
+ logger.error("git commit exited non-zero")
50
+ raise GitError("git commit failed.")
51
+
52
+ logger.info("Commit created successfully")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: git-commit-msg-ai
3
- Version: 1.4.2
3
+ Version: 1.5.1
4
4
  Summary: AI-powered git commit message generator following Conventional Commits
5
5
  License-Expression: MIT
6
6
  Requires-Python: >=3.10
@@ -113,3 +113,25 @@ BREAKING CHANGE: /v1/legacy is no longer available
113
113
  ```
114
114
 
115
115
  Supported types: `feat`, `fix`, `docs`, `style`, `refactor`, `test`, `chore`
116
+
117
+ ## Debugging
118
+
119
+ 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**.
120
+
121
+ Valid values: `DEBUG`, `INFO`, `WARNING`, `ERROR`, `CRITICAL`
122
+
123
+ ```sh
124
+ # macOS/Linux - show all internal diagnostic messages
125
+ GIT_COMMIT_AI_LOG_LEVEL=DEBUG git-commit-msg-ai
126
+
127
+ # Windows PowerShell
128
+ $env:GIT_COMMIT_AI_LOG_LEVEL = 'DEBUG'
129
+ git-commit-msg-ai
130
+ ```
131
+
132
+ | Level | What you see |
133
+ |---|---|
134
+ | `DEBUG` | git commands run, API model/token params, temp file paths, char counts |
135
+ | `INFO` | commit message generated, commit created |
136
+ | `WARNING` | no staged changes found |
137
+ | `ERROR` | git not found, API failures, editor errors |
@@ -0,0 +1,11 @@
1
+ git_commit_msg_ai/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ git_commit_msg_ai/ai_client.py,sha256=ET7wWfgiIPzXsuGi1bV8bV8UWOThPXn5JzRgcnh3yNI,2340
3
+ git_commit_msg_ai/cli.py,sha256=IBU_BhBbd5azbTI6Rso92LtabLHlVUsO3ZUYA2Zr9wE,1686
4
+ git_commit_msg_ai/editor.py,sha256=PycIvOuvHIAx8fSl6_L8AkzAWOS5PRNGTHb6yE11KN0,2087
5
+ git_commit_msg_ai/exceptions.py,sha256=7Hwluf3zHMjs4lpGktWS-Lwgo8y_4Xbb1WqzPQHkkUA,352
6
+ git_commit_msg_ai/git_ops.py,sha256=jJJ_8E_tOIl-SQ5Nf51XawO767P-5VMVXQ7gNNMLDyM,1974
7
+ git_commit_msg_ai-1.5.1.dist-info/METADATA,sha256=VqmEAniNF6yKUNU7VP0-T2plMIDuJWQCkWyDlW3hnHE,3586
8
+ git_commit_msg_ai-1.5.1.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
9
+ git_commit_msg_ai-1.5.1.dist-info/entry_points.txt,sha256=KTu6wUhl0p3nf27k8L4vpSH_hpeRQpwzMPSmKv7K5Cs,65
10
+ git_commit_msg_ai-1.5.1.dist-info/top_level.txt,sha256=XYQC2BXvrcREGKEG7sm9nbwO7ifqcUSVgU7SW8BTURs,18
11
+ git_commit_msg_ai-1.5.1.dist-info/RECORD,,
@@ -1,11 +0,0 @@
1
- git_commit_msg_ai/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- git_commit_msg_ai/ai_client.py,sha256=RnzgIx1ieuiiH6w2VijA6_F-cs6tIQR57W1x3avg8lE,1887
3
- git_commit_msg_ai/cli.py,sha256=s94orvxin-38vqUYWAO-j5_JZTzxBXbZSVOHYZ5KO_k,1059
4
- git_commit_msg_ai/editor.py,sha256=JnVm5SZwkunyfOjisDKvTuPm3_NPbijVk_ho_yQy-3o,1430
5
- git_commit_msg_ai/exceptions.py,sha256=7Hwluf3zHMjs4lpGktWS-Lwgo8y_4Xbb1WqzPQHkkUA,352
6
- git_commit_msg_ai/git_ops.py,sha256=k2kjwcvj0Ac-WAttSsMP8pIl4-WXUKK47thgjaB62Og,1263
7
- git_commit_msg_ai-1.4.2.dist-info/METADATA,sha256=BzSJx7Vdzjo9CrRi2-88_HvbR1aQjJnci2deJQvtthQ,2768
8
- git_commit_msg_ai-1.4.2.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
9
- git_commit_msg_ai-1.4.2.dist-info/entry_points.txt,sha256=KTu6wUhl0p3nf27k8L4vpSH_hpeRQpwzMPSmKv7K5Cs,65
10
- git_commit_msg_ai-1.4.2.dist-info/top_level.txt,sha256=XYQC2BXvrcREGKEG7sm9nbwO7ifqcUSVgU7SW8BTURs,18
11
- git_commit_msg_ai-1.4.2.dist-info/RECORD,,