commitmessagegenerator 2.2.0__tar.gz → 2.5.1__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 (16) hide show
  1. {commitmessagegenerator-2.2.0 → commitmessagegenerator-2.5.1}/PKG-INFO +25 -8
  2. {commitmessagegenerator-2.2.0 → commitmessagegenerator-2.5.1}/README.md +120 -103
  3. {commitmessagegenerator-2.2.0 → commitmessagegenerator-2.5.1}/commitmessagegenerator/cli.py +52 -28
  4. commitmessagegenerator-2.5.1/commitmessagegenerator/configure.py +196 -0
  5. {commitmessagegenerator-2.2.0 → commitmessagegenerator-2.5.1}/commitmessagegenerator/generator.py +2 -3
  6. {commitmessagegenerator-2.2.0 → commitmessagegenerator-2.5.1}/commitmessagegenerator.egg-info/PKG-INFO +25 -8
  7. {commitmessagegenerator-2.2.0 → commitmessagegenerator-2.5.1}/pyproject.toml +21 -21
  8. commitmessagegenerator-2.2.0/commitmessagegenerator/configure.py +0 -103
  9. {commitmessagegenerator-2.2.0 → commitmessagegenerator-2.5.1}/LICENSE +0 -0
  10. {commitmessagegenerator-2.2.0 → commitmessagegenerator-2.5.1}/commitmessagegenerator/__init__.py +0 -0
  11. {commitmessagegenerator-2.2.0 → commitmessagegenerator-2.5.1}/commitmessagegenerator.egg-info/SOURCES.txt +0 -0
  12. {commitmessagegenerator-2.2.0 → commitmessagegenerator-2.5.1}/commitmessagegenerator.egg-info/dependency_links.txt +0 -0
  13. {commitmessagegenerator-2.2.0 → commitmessagegenerator-2.5.1}/commitmessagegenerator.egg-info/entry_points.txt +0 -0
  14. {commitmessagegenerator-2.2.0 → commitmessagegenerator-2.5.1}/commitmessagegenerator.egg-info/requires.txt +0 -0
  15. {commitmessagegenerator-2.2.0 → commitmessagegenerator-2.5.1}/commitmessagegenerator.egg-info/top_level.txt +0 -0
  16. {commitmessagegenerator-2.2.0 → commitmessagegenerator-2.5.1}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: commitmessagegenerator
3
- Version: 2.2.0
3
+ Version: 2.5.1
4
4
  Summary: Generate commit messages with AI (Google Gemini) automatically using `git diff`.
5
5
  Author-email: Gabriel Terceiro <gcarolinoterceiro@gmail.com>
6
6
  License: MIT
@@ -35,6 +35,14 @@ pip install commitmessagegenerator
35
35
  commitgen -cf
36
36
  ```
37
37
 
38
+ You can explicitly choose where configuration is written:
39
+
40
+ ```bash
41
+ commitgen -cf --config-scope auto # default behavior
42
+ commitgen -cf --config-scope local # always write .env in current directory
43
+ commitgen -cf --config-scope global # always write ~/.commitgen/.env
44
+ ```
45
+
38
46
  This opens an interactive configuration menu where you can:
39
47
 
40
48
  1. Set or update your Gemini API key
@@ -53,10 +61,18 @@ Create a `.env` file in the directory where you will run commitgen (usually the
53
61
 
54
62
  ```
55
63
  GEMINI_API_KEY=your-gemini-api-key
56
- AI_MODEL=gemini-2.0-flash
64
+ AI_MODEL=gemini-2.5-flash
57
65
  AUTO_ADD_ALL=true
58
66
  ```
59
67
 
68
+ Config discovery order:
69
+
70
+ 1. `.env` in current directory
71
+ 2. `.env` in parent directories (useful when running from subfolders)
72
+ 3. Global config in `~/.commitgen/.env` (created automatically by `commitgen -cf --config-scope auto` when no local `.env` exists)
73
+
74
+ Environment variables already set in your shell (e.g. `GEMINI_API_KEY`) are also respected.
75
+
60
76
  ## 🚀 Usage
61
77
 
62
78
  With the terminal, inside any Git repository with pending changes, run:
@@ -77,18 +93,19 @@ The command will:
77
93
  - `commitgen -c` - Generate and commit with the message
78
94
  - `commitgen -cp` - Generate, commit, and push
79
95
  - `commitgen -cf` - Configure API key, model, and file staging behavior
96
+ - `commitgen -cf --config-scope [auto|local|global]` - Choose where `.env` is created/updated
80
97
  - `commitgen -s` - Show current configuration status
81
98
 
82
99
  ### Available Models
83
100
 
84
101
  When configuring with `-cf`, you can choose from:
85
102
 
86
- 1. **gemini-2.0-flash** (default) - Fast and efficient
87
- 2. **gemini-1.5-flash** - Good balance of speed and quality
88
- 3. **gemini-1.5-pro** - Highest quality, slower
89
- 4. **gemini-2.0-flash-exp** - Experimental version
90
- 5. **gemini-2.5-flash** - Latest version, fast and efficient
91
- 6. **gemini-2.5-pro** - Latest version, highest quality
103
+ 1. **gemini-2.5-flash** (default) - Fast and efficient, best price-performance
104
+ 2. **gemini-2.5-flash-lite** - Fastest and most economical
105
+ 3. **gemini-2.5-pro** - Advanced reasoning and coding
106
+ 4. **gemini-3-flash** - Frontier performance (preview)
107
+ 5. **gemini-3.5-flash** - Latest stable model, best for coding
108
+ 6. **gemini-3.1-pro** - Highest quality (preview)
92
109
 
93
110
  ### File Staging Behavior
94
111
 
@@ -1,103 +1,120 @@
1
- # commitmessagegenerator
2
-
3
- Generate objective and technical commit messages with AI (Google Gemini) automatically using your `git diff`.
4
-
5
- ## 📦 Install
6
-
7
- ```bash
8
- pip install commitmessagegenerator
9
- ```
10
-
11
- Or, if you're using a `venv`:
12
-
13
- ```bash
14
- python -m venv venv
15
- source venv/bin/activate # or .\venv\Scripts\activate in Windows
16
- pip install commitmessagegenerator
17
- ```
18
-
19
- ## ⚙️ Configuring
20
-
21
- ```bash
22
- commitgen -cf
23
- ```
24
-
25
- This opens an interactive configuration menu where you can:
26
-
27
- 1. Set or update your Gemini API key
28
- 2. Change the AI model
29
- 3. Configure file staging behavior
30
-
31
- Each option can be configured independently, and you can exit at any time without saving changes.
32
-
33
- ## Run this and type you API key to the terminal so the package creates the .env file and automatically adds it to the .gitignore
34
-
35
- Or do it manually:
36
-
37
- ## IMPORTANT - BEFORE CREATING THIS FILE ADD '.venv' TO YOUR .gitignore SO YOUR API KEY ISN'T EXPOSED
38
-
39
- Create a `.env` file in the directory where you will run commitgen (usually the root of your Git project):
40
-
41
- ```
42
- GEMINI_API_KEY=your-gemini-api-key
43
- AI_MODEL=gemini-2.0-flash
44
- AUTO_ADD_ALL=true
45
- ```
46
-
47
- ## 🚀 Usage
48
-
49
- With the terminal, inside any Git repository with pending changes, run:
50
-
51
- ```bash
52
- commitgen (-c/-cp)
53
- ```
54
-
55
- The command will:
56
-
57
- - Read the git diff;
58
- - Send it to the Google Gemini API using your configured model;
59
- - Return a commit message suggestion directly in your terminal.
60
-
61
- ### Available Commands
62
-
63
- - `commitgen` - Generate commit message only
64
- - `commitgen -c` - Generate and commit with the message
65
- - `commitgen -cp` - Generate, commit, and push
66
- - `commitgen -cf` - Configure API key, model, and file staging behavior
67
- - `commitgen -s` - Show current configuration status
68
-
69
- ### Available Models
70
-
71
- When configuring with `-cf`, you can choose from:
72
-
73
- 1. **gemini-2.0-flash** (default) - Fast and efficient
74
- 2. **gemini-1.5-flash** - Good balance of speed and quality
75
- 3. **gemini-1.5-pro** - Highest quality, slower
76
- 4. **gemini-2.0-flash-exp** - Experimental version
77
- 5. **gemini-2.5-flash** - Latest version, fast and efficient
78
- 6. **gemini-2.5-pro** - Latest version, highest quality
79
-
80
- ### File Staging Behavior
81
-
82
- When configuring with `-cf`, you can choose how files are staged:
83
-
84
- 1. **Auto-add all files** (default) - Automatically runs `git add --all` before generating the commit message
85
- 2. **Staged only** - Only reads the diff from files you've already staged with `git add`
86
-
87
- The "staged only" option gives you more control over which changes are included in the commit message.
88
-
89
- ## 🧩 Requisites
90
-
91
- - Python 3.8 or higher
92
- - Gemini API Key (Google Generative AI, free at: https://aistudio.google.com/app/apikey)
93
- - Initialized Git repository
94
- - Python dependencies (Automatically installed with the package):
95
- - `GitPython`
96
- - `google-generativeai`
97
- - `python-dotenv`
98
-
99
- ## 📄 License
100
-
101
- ```
102
- MIT License
103
- ```
1
+ # commitmessagegenerator
2
+
3
+ Generate objective and technical commit messages with AI (Google Gemini) automatically using your `git diff`.
4
+
5
+ ## 📦 Install
6
+
7
+ ```bash
8
+ pip install commitmessagegenerator
9
+ ```
10
+
11
+ Or, if you're using a `venv`:
12
+
13
+ ```bash
14
+ python -m venv venv
15
+ source venv/bin/activate # or .\venv\Scripts\activate in Windows
16
+ pip install commitmessagegenerator
17
+ ```
18
+
19
+ ## ⚙️ Configuring
20
+
21
+ ```bash
22
+ commitgen -cf
23
+ ```
24
+
25
+ You can explicitly choose where configuration is written:
26
+
27
+ ```bash
28
+ commitgen -cf --config-scope auto # default behavior
29
+ commitgen -cf --config-scope local # always write .env in current directory
30
+ commitgen -cf --config-scope global # always write ~/.commitgen/.env
31
+ ```
32
+
33
+ This opens an interactive configuration menu where you can:
34
+
35
+ 1. Set or update your Gemini API key
36
+ 2. Change the AI model
37
+ 3. Configure file staging behavior
38
+
39
+ Each option can be configured independently, and you can exit at any time without saving changes.
40
+
41
+ ## Run this and type you API key to the terminal so the package creates the .env file and automatically adds it to the .gitignore
42
+
43
+ Or do it manually:
44
+
45
+ ## IMPORTANT - BEFORE CREATING THIS FILE ADD '.venv' TO YOUR .gitignore SO YOUR API KEY ISN'T EXPOSED
46
+
47
+ Create a `.env` file in the directory where you will run commitgen (usually the root of your Git project):
48
+
49
+ ```
50
+ GEMINI_API_KEY=your-gemini-api-key
51
+ AI_MODEL=gemini-2.5-flash
52
+ AUTO_ADD_ALL=true
53
+ ```
54
+
55
+ Config discovery order:
56
+
57
+ 1. `.env` in current directory
58
+ 2. `.env` in parent directories (useful when running from subfolders)
59
+ 3. Global config in `~/.commitgen/.env` (created automatically by `commitgen -cf --config-scope auto` when no local `.env` exists)
60
+
61
+ Environment variables already set in your shell (e.g. `GEMINI_API_KEY`) are also respected.
62
+
63
+ ## 🚀 Usage
64
+
65
+ With the terminal, inside any Git repository with pending changes, run:
66
+
67
+ ```bash
68
+ commitgen (-c/-cp)
69
+ ```
70
+
71
+ The command will:
72
+
73
+ - Read the git diff;
74
+ - Send it to the Google Gemini API using your configured model;
75
+ - Return a commit message suggestion directly in your terminal.
76
+
77
+ ### Available Commands
78
+
79
+ - `commitgen` - Generate commit message only
80
+ - `commitgen -c` - Generate and commit with the message
81
+ - `commitgen -cp` - Generate, commit, and push
82
+ - `commitgen -cf` - Configure API key, model, and file staging behavior
83
+ - `commitgen -cf --config-scope [auto|local|global]` - Choose where `.env` is created/updated
84
+ - `commitgen -s` - Show current configuration status
85
+
86
+ ### Available Models
87
+
88
+ When configuring with `-cf`, you can choose from:
89
+
90
+ 1. **gemini-2.5-flash** (default) - Fast and efficient, best price-performance
91
+ 2. **gemini-2.5-flash-lite** - Fastest and most economical
92
+ 3. **gemini-2.5-pro** - Advanced reasoning and coding
93
+ 4. **gemini-3-flash** - Frontier performance (preview)
94
+ 5. **gemini-3.5-flash** - Latest stable model, best for coding
95
+ 6. **gemini-3.1-pro** - Highest quality (preview)
96
+
97
+ ### File Staging Behavior
98
+
99
+ When configuring with `-cf`, you can choose how files are staged:
100
+
101
+ 1. **Auto-add all files** (default) - Automatically runs `git add --all` before generating the commit message
102
+ 2. **Staged only** - Only reads the diff from files you've already staged with `git add`
103
+
104
+ The "staged only" option gives you more control over which changes are included in the commit message.
105
+
106
+ ## 🧩 Requisites
107
+
108
+ - Python 3.8 or higher
109
+ - Gemini API Key (Google Generative AI, free at: https://aistudio.google.com/app/apikey)
110
+ - Initialized Git repository
111
+ - Python dependencies (Automatically installed with the package):
112
+ - `GitPython`
113
+ - `google-generativeai`
114
+ - `python-dotenv`
115
+
116
+ ## 📄 License
117
+
118
+ ```
119
+ MIT License
120
+ ```
@@ -1,21 +1,34 @@
1
1
  import argparse
2
2
  import subprocess
3
3
  from .generator import gerar_mensagem_commit
4
- from .configure import api_key, get_configured_model, get_auto_add_setting, update_setting, get_api_key_status
4
+ from .configure import (
5
+ api_key,
6
+ get_configured_model,
7
+ get_auto_add_setting,
8
+ update_setting,
9
+ get_api_key_status,
10
+ load_config_env,
11
+ resolve_env_path,
12
+ )
5
13
  import sys
6
14
  import getpass
7
15
 
8
16
  MODEL_MAP = {
9
- "1": "gemini-2.0-flash",
10
- "2": "gemini-1.5-flash",
11
- "3": "gemini-1.5-pro",
12
- "4": "gemini-2.0-flash-exp",
13
- "5": "gemini-2.5-flash",
14
- "6": "gemini-2.5-pro"
17
+ "1": "gemini-2.5-flash",
18
+ "2": "gemini-2.5-flash-lite",
19
+ "3": "gemini-2.5-pro",
20
+ "4": "gemini-3-flash",
21
+ "5": "gemini-3.5-flash",
22
+ "6": "gemini-3.1-pro",
15
23
  }
16
24
 
17
25
  def configure_menu():
18
26
  """Interactive configuration menu"""
27
+ configure_menu_with_scope("auto")
28
+
29
+
30
+ def configure_menu_with_scope(config_scope):
31
+ """Interactive configuration menu with target scope."""
19
32
  while True:
20
33
  # Get current settings
21
34
  api_set = get_api_key_status()
@@ -37,15 +50,15 @@ def configure_menu():
37
50
  print("\nExiting configuration.\n")
38
51
  break
39
52
  elif choice == "1":
40
- configure_api_key()
53
+ configure_api_key(config_scope)
41
54
  elif choice == "2":
42
- configure_model()
55
+ configure_model(config_scope)
43
56
  elif choice == "3":
44
- configure_staging()
57
+ configure_staging(config_scope)
45
58
  else:
46
59
  print("\nInvalid option. Please try again.")
47
60
 
48
- def configure_api_key():
61
+ def configure_api_key(config_scope):
49
62
  """Configure the API key"""
50
63
  print("\n" + "-"*40)
51
64
  print("API KEY CONFIGURATION")
@@ -59,10 +72,10 @@ def configure_api_key():
59
72
  print("\nNo changes made.")
60
73
  return
61
74
 
62
- update_setting("GEMINI_API_KEY", key)
75
+ update_setting("GEMINI_API_KEY", key, scope=config_scope)
63
76
  print("\n✓ API Key saved successfully!")
64
77
 
65
- def configure_model():
78
+ def configure_model(config_scope):
66
79
  """Configure the AI model"""
67
80
  current_model = get_configured_model()
68
81
 
@@ -71,12 +84,12 @@ def configure_model():
71
84
  print("-"*40)
72
85
  print(f"Current model: {current_model}\n")
73
86
  print("Available models:")
74
- print("1. gemini-2.0-flash (fast and efficient)")
75
- print("2. gemini-1.5-flash (good balance)")
76
- print("3. gemini-1.5-pro (highest quality, slower)")
77
- print("4. gemini-2.0-flash-exp (experimental)")
78
- print("5. gemini-2.5-flash (latest, fast)")
79
- print("6. gemini-2.5-pro (latest, highest quality)")
87
+ print("1. gemini-2.5-flash (default, fast and efficient)")
88
+ print("2. gemini-2.5-flash-lite (fastest, most economical)")
89
+ print("3. gemini-2.5-pro (advanced reasoning and coding)")
90
+ print("4. gemini-3-flash (frontier performance, preview)")
91
+ print("5. gemini-3.5-flash (latest stable, best for coding)")
92
+ print("6. gemini-3.1-pro (highest quality, preview)")
80
93
  print("\n0. Cancel")
81
94
 
82
95
  choice = input("\nSelect model (0-6): ").strip()
@@ -87,12 +100,12 @@ def configure_model():
87
100
 
88
101
  if choice in MODEL_MAP:
89
102
  selected_model = MODEL_MAP[choice]
90
- update_setting("AI_MODEL", selected_model)
103
+ update_setting("AI_MODEL", selected_model, scope=config_scope)
91
104
  print(f"\n✓ Model changed to: {selected_model}")
92
105
  else:
93
106
  print("\nInvalid option. No changes made.")
94
107
 
95
- def configure_staging():
108
+ def configure_staging(config_scope):
96
109
  """Configure file staging behavior"""
97
110
  auto_add = get_auto_add_setting()
98
111
 
@@ -111,10 +124,10 @@ def configure_staging():
111
124
  return
112
125
 
113
126
  if choice == "1":
114
- update_setting("AUTO_ADD_ALL", "true")
127
+ update_setting("AUTO_ADD_ALL", "true", scope=config_scope)
115
128
  print("\n✓ Set to: Auto-add all files")
116
129
  elif choice == "2":
117
- update_setting("AUTO_ADD_ALL", "false")
130
+ update_setting("AUTO_ADD_ALL", "false", scope=config_scope)
118
131
  print("\n✓ Set to: Staged only")
119
132
  else:
120
133
  print("\nInvalid option. No changes made.")
@@ -125,14 +138,18 @@ def main():
125
138
  parser.add_argument("-cp", "--commitpush", action="store_true", help="Commits and pushes with the generated message")
126
139
  parser.add_argument("-cf", "--configure", action="store_true", help="Configures the GEMINI_API_KEY environment variable")
127
140
  parser.add_argument("-s", "--status", action="store_true", help="Shows current configuration status")
141
+ parser.add_argument(
142
+ "--config-scope",
143
+ choices=["auto", "local", "global"],
144
+ default="auto",
145
+ help="Where -cf writes configuration (.env): auto, local, or global",
146
+ )
128
147
  args = parser.parse_args()
129
148
 
130
149
  if args.status:
131
- from .configure import get_configured_model, get_auto_add_setting
132
150
  import os
133
- from dotenv import load_dotenv
134
-
135
- load_dotenv()
151
+
152
+ env_path = load_config_env()
136
153
  key = os.getenv("GEMINI_API_KEY")
137
154
  model = get_configured_model()
138
155
  auto_add = get_auto_add_setting()
@@ -141,6 +158,10 @@ def main():
141
158
  print(f"API Key: {'✓ Set' if key else '✗ Not set'}")
142
159
  print(f"Model: {model}")
143
160
  print(f"Auto-add all files: {'✓ Yes' if auto_add else '✗ No (staged only)'}")
161
+ if env_path is not None:
162
+ print(f"Config file: {env_path}")
163
+ else:
164
+ print("Config file: not found (.env not discovered)")
144
165
  return
145
166
 
146
167
  if not args.configure:
@@ -161,7 +182,10 @@ def main():
161
182
  subprocess.run(["git", "push"])
162
183
 
163
184
  if args.configure:
164
- configure_menu()
185
+ configure_menu_with_scope(args.config_scope)
186
+ configured_path = resolve_env_path()
187
+ if configured_path is not None:
188
+ print(f"\nConfiguration file in use: {configured_path}")
165
189
 
166
190
  if len(sys.argv) == 1:
167
191
  print("\nRemoving staged changes (git reset)...")
@@ -0,0 +1,196 @@
1
+ import os
2
+ from pathlib import Path
3
+ from dotenv import load_dotenv
4
+
5
+
6
+ GLOBAL_ENV_PATH = Path.home() / ".commitgen" / ".env"
7
+ CONFIG_SCOPES = {"auto", "local", "global"}
8
+
9
+
10
+ def _iter_local_env_candidates():
11
+ """Yield .env paths from cwd up to filesystem root."""
12
+ current = Path.cwd().resolve()
13
+ for directory in (current, *current.parents):
14
+ yield directory / ".env"
15
+
16
+
17
+ def resolve_local_env_path():
18
+ """Resolve an existing local .env from cwd up to filesystem root."""
19
+ for candidate in _iter_local_env_candidates():
20
+ if candidate.exists():
21
+ return candidate
22
+ return None
23
+
24
+
25
+ def resolve_env_path():
26
+ """Resolve the best available config file path."""
27
+ local_path = resolve_local_env_path()
28
+ if local_path is not None:
29
+ return local_path
30
+ if GLOBAL_ENV_PATH.exists():
31
+ return GLOBAL_ENV_PATH
32
+ return None
33
+
34
+
35
+ def resolve_writable_env_path(scope="auto"):
36
+ """
37
+ Resolve where settings should be written.
38
+
39
+ scope:
40
+ - auto: existing local .env, then existing global .env, else new global .env
41
+ - local: .env in current working directory
42
+ - global: ~/.commitgen/.env
43
+ """
44
+ if scope not in CONFIG_SCOPES:
45
+ raise ValueError(f"Invalid config scope: {scope}")
46
+
47
+ if scope == "local":
48
+ return Path.cwd().resolve() / ".env"
49
+
50
+ if scope == "global":
51
+ return GLOBAL_ENV_PATH
52
+
53
+ existing_path = resolve_env_path()
54
+ if existing_path is not None:
55
+ return existing_path
56
+ return GLOBAL_ENV_PATH
57
+
58
+
59
+ def load_config_env():
60
+ """
61
+ Load configuration from the resolved .env path (if found).
62
+ Returns the loaded path or None.
63
+ """
64
+ env_path = resolve_env_path()
65
+ if env_path is not None:
66
+ load_dotenv(dotenv_path=env_path, override=False)
67
+ return env_path
68
+ load_dotenv(override=False)
69
+ return None
70
+
71
+ def update_setting(setting_name, value, scope="auto"):
72
+ """Update a single setting in the .env file"""
73
+ env_path = resolve_writable_env_path(scope=scope)
74
+ env_path.parent.mkdir(parents=True, exist_ok=True)
75
+
76
+ if env_path.exists():
77
+ with open(env_path, "r", encoding="utf-8") as f:
78
+ lines = f.readlines()
79
+
80
+ found = False
81
+ new_lines = []
82
+ for line in lines:
83
+ key_name = line.split("=")[0] if "=" in line else None
84
+ if key_name == setting_name:
85
+ new_lines.append(f"{setting_name}={value}\n")
86
+ found = True
87
+ else:
88
+ new_lines.append(line)
89
+
90
+ if not found:
91
+ new_lines.append(f"{setting_name}={value}\n")
92
+
93
+ with open(env_path, "w", encoding="utf-8") as f:
94
+ f.writelines(new_lines)
95
+ else:
96
+ with open(env_path, "w", encoding="utf-8") as f:
97
+ f.write(f"{setting_name}={value}\n")
98
+
99
+ _ensure_gitignore(env_path)
100
+
101
+ def _ensure_gitignore(env_path):
102
+ """Ensure .env is in .gitignore for local (non-global) config files."""
103
+ if env_path == GLOBAL_ENV_PATH:
104
+ return
105
+
106
+ gitignore_path = env_path.parent / ".gitignore"
107
+ if gitignore_path.exists():
108
+ with open(gitignore_path, "r+", encoding="utf-8") as outfile:
109
+ lines = outfile.readlines()
110
+ env_in_gitignore = next((line for line in lines if line.strip() == ".env"), None)
111
+ if not env_in_gitignore:
112
+ outfile.write("\n.env")
113
+
114
+ def api_key(key, model="gemini-2.5-flash", auto_add_all=True, scope="auto"):
115
+ """Set all configuration at once (legacy function)"""
116
+ config = {
117
+ "GEMINI_API_KEY": key,
118
+ "AI_MODEL": model,
119
+ "AUTO_ADD_ALL": str(auto_add_all).lower()
120
+ }
121
+
122
+ env_path = resolve_writable_env_path(scope=scope)
123
+ env_path.parent.mkdir(parents=True, exist_ok=True)
124
+
125
+ if env_path.exists():
126
+ with open(env_path, "r", encoding="utf-8") as f:
127
+ lines = f.readlines()
128
+
129
+ existing_keys = set()
130
+ new_lines = []
131
+ for line in lines:
132
+ key_name = line.split("=")[0] if "=" in line else None
133
+ if key_name in config:
134
+ new_lines.append(f"{key_name}={config[key_name]}\n")
135
+ existing_keys.add(key_name)
136
+ else:
137
+ new_lines.append(line)
138
+
139
+ for key_name, value in config.items():
140
+ if key_name not in existing_keys:
141
+ new_lines.append(f"{key_name}={value}\n")
142
+
143
+ with open(env_path, "w", encoding="utf-8") as f:
144
+ f.writelines(new_lines)
145
+ else:
146
+ with open(env_path, "w", encoding="utf-8") as f:
147
+ for key_name, value in config.items():
148
+ f.write(f"{key_name}={value}\n")
149
+
150
+ _ensure_gitignore(env_path)
151
+
152
+ def get_api_key_status():
153
+ """Check if API key is set"""
154
+ env_key = os.getenv("GEMINI_API_KEY")
155
+ if env_key:
156
+ return True
157
+
158
+ env_path = resolve_env_path()
159
+ if env_path is not None:
160
+ with open(env_path, "r", encoding="utf-8") as f:
161
+ lines = f.readlines()
162
+ api_line = next((line for line in lines if line.startswith("GEMINI_API_KEY=")), None)
163
+ if api_line:
164
+ value = api_line.split("=", 1)[1].strip()
165
+ return bool(value)
166
+ return False
167
+
168
+ def get_configured_model():
169
+ """Get the currently configured AI model from .env file"""
170
+ env_model = os.getenv("AI_MODEL")
171
+ if env_model:
172
+ return env_model
173
+
174
+ env_path = resolve_env_path()
175
+ if env_path is not None:
176
+ with open(env_path, "r", encoding="utf-8") as outfile:
177
+ lines = outfile.readlines()
178
+ model_line = next((line for line in lines if line.startswith("AI_MODEL=")), None)
179
+ if model_line:
180
+ return model_line.split("=", 1)[1].strip()
181
+ return "gemini-2.5-flash" # Default fallback
182
+
183
+ def get_auto_add_setting():
184
+ """Get the auto-add all files setting from .env file"""
185
+ env_auto_add = os.getenv("AUTO_ADD_ALL")
186
+ if env_auto_add is not None:
187
+ return env_auto_add.strip().lower() == "true"
188
+
189
+ env_path = resolve_env_path()
190
+ if env_path is not None:
191
+ with open(env_path, "r", encoding="utf-8") as outfile:
192
+ lines = outfile.readlines()
193
+ auto_add_line = next((line for line in lines if line.startswith("AUTO_ADD_ALL=")), None)
194
+ if auto_add_line:
195
+ return auto_add_line.split("=", 1)[1].strip().lower() == "true"
196
+ return True # Default: auto-add all files (current behavior)
@@ -1,11 +1,10 @@
1
1
  import os
2
- from dotenv import load_dotenv
3
2
  from google import genai
4
3
  from git import Repo
5
- from .configure import get_configured_model, get_auto_add_setting
4
+ from .configure import get_configured_model, get_auto_add_setting, load_config_env
6
5
 
7
6
  def gerar_mensagem_commit():
8
- load_dotenv()
7
+ load_config_env()
9
8
  key = os.getenv("GEMINI_API_KEY")
10
9
  if not key:
11
10
  raise RuntimeError("The GEMINI_API_KEY environment variable is not set.")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: commitmessagegenerator
3
- Version: 2.2.0
3
+ Version: 2.5.1
4
4
  Summary: Generate commit messages with AI (Google Gemini) automatically using `git diff`.
5
5
  Author-email: Gabriel Terceiro <gcarolinoterceiro@gmail.com>
6
6
  License: MIT
@@ -35,6 +35,14 @@ pip install commitmessagegenerator
35
35
  commitgen -cf
36
36
  ```
37
37
 
38
+ You can explicitly choose where configuration is written:
39
+
40
+ ```bash
41
+ commitgen -cf --config-scope auto # default behavior
42
+ commitgen -cf --config-scope local # always write .env in current directory
43
+ commitgen -cf --config-scope global # always write ~/.commitgen/.env
44
+ ```
45
+
38
46
  This opens an interactive configuration menu where you can:
39
47
 
40
48
  1. Set or update your Gemini API key
@@ -53,10 +61,18 @@ Create a `.env` file in the directory where you will run commitgen (usually the
53
61
 
54
62
  ```
55
63
  GEMINI_API_KEY=your-gemini-api-key
56
- AI_MODEL=gemini-2.0-flash
64
+ AI_MODEL=gemini-2.5-flash
57
65
  AUTO_ADD_ALL=true
58
66
  ```
59
67
 
68
+ Config discovery order:
69
+
70
+ 1. `.env` in current directory
71
+ 2. `.env` in parent directories (useful when running from subfolders)
72
+ 3. Global config in `~/.commitgen/.env` (created automatically by `commitgen -cf --config-scope auto` when no local `.env` exists)
73
+
74
+ Environment variables already set in your shell (e.g. `GEMINI_API_KEY`) are also respected.
75
+
60
76
  ## 🚀 Usage
61
77
 
62
78
  With the terminal, inside any Git repository with pending changes, run:
@@ -77,18 +93,19 @@ The command will:
77
93
  - `commitgen -c` - Generate and commit with the message
78
94
  - `commitgen -cp` - Generate, commit, and push
79
95
  - `commitgen -cf` - Configure API key, model, and file staging behavior
96
+ - `commitgen -cf --config-scope [auto|local|global]` - Choose where `.env` is created/updated
80
97
  - `commitgen -s` - Show current configuration status
81
98
 
82
99
  ### Available Models
83
100
 
84
101
  When configuring with `-cf`, you can choose from:
85
102
 
86
- 1. **gemini-2.0-flash** (default) - Fast and efficient
87
- 2. **gemini-1.5-flash** - Good balance of speed and quality
88
- 3. **gemini-1.5-pro** - Highest quality, slower
89
- 4. **gemini-2.0-flash-exp** - Experimental version
90
- 5. **gemini-2.5-flash** - Latest version, fast and efficient
91
- 6. **gemini-2.5-pro** - Latest version, highest quality
103
+ 1. **gemini-2.5-flash** (default) - Fast and efficient, best price-performance
104
+ 2. **gemini-2.5-flash-lite** - Fastest and most economical
105
+ 3. **gemini-2.5-pro** - Advanced reasoning and coding
106
+ 4. **gemini-3-flash** - Frontier performance (preview)
107
+ 5. **gemini-3.5-flash** - Latest stable model, best for coding
108
+ 6. **gemini-3.1-pro** - Highest quality (preview)
92
109
 
93
110
  ### File Staging Behavior
94
111
 
@@ -1,21 +1,21 @@
1
- [build-system]
2
- requires = ["setuptools>=61.0"]
3
- build-backend = "setuptools.build_meta"
4
-
5
- [project]
6
- name = "commitmessagegenerator"
7
- version = "2.2.0"
8
- description = "Generate commit messages with AI (Google Gemini) automatically using `git diff`."
9
- readme = "README.md"
10
- license = {text = "MIT"}
11
- authors = [
12
- {name = "Gabriel Terceiro", email = "gcarolinoterceiro@gmail.com"}
13
- ]
14
- dependencies = [
15
- "google-genai==1.20.0",
16
- "GitPython==3.1.44",
17
- "python-dotenv==1.1.0"
18
- ]
19
-
20
- [project.scripts]
21
- commitgen = "commitmessagegenerator.cli:main"
1
+ [build-system]
2
+ requires = ["setuptools>=61.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "commitmessagegenerator"
7
+ version = "2.5.1"
8
+ description = "Generate commit messages with AI (Google Gemini) automatically using `git diff`."
9
+ readme = "README.md"
10
+ license = {text = "MIT"}
11
+ authors = [
12
+ {name = "Gabriel Terceiro", email = "gcarolinoterceiro@gmail.com"}
13
+ ]
14
+ dependencies = [
15
+ "google-genai==1.20.0",
16
+ "GitPython==3.1.44",
17
+ "python-dotenv==1.1.0"
18
+ ]
19
+
20
+ [project.scripts]
21
+ commitgen = "commitmessagegenerator.cli:main"
@@ -1,103 +0,0 @@
1
- import os
2
-
3
- def update_setting(setting_name, value):
4
- """Update a single setting in the .env file"""
5
- if os.path.exists(".env"):
6
- with open(".env", "r") as f:
7
- lines = f.readlines()
8
-
9
- found = False
10
- new_lines = []
11
- for line in lines:
12
- key_name = line.split("=")[0] if "=" in line else None
13
- if key_name == setting_name:
14
- new_lines.append(f"{setting_name}={value}\n")
15
- found = True
16
- else:
17
- new_lines.append(line)
18
-
19
- if not found:
20
- new_lines.append(f"{setting_name}={value}\n")
21
-
22
- with open(".env", "w") as f:
23
- f.writelines(new_lines)
24
- else:
25
- with open(".env", "w") as f:
26
- f.write(f"{setting_name}={value}\n")
27
-
28
- _ensure_gitignore()
29
-
30
- def _ensure_gitignore():
31
- """Ensure .env is in .gitignore"""
32
- if os.path.exists(".gitignore"):
33
- with open(".gitignore", "r+") as outfile:
34
- lines = outfile.readlines()
35
- env_in_gitignore = next((line for line in lines if line.strip() == ".env"), None)
36
- if not env_in_gitignore:
37
- outfile.write("\n.env")
38
-
39
- def api_key(key, model="gemini-2.0-flash", auto_add_all=True):
40
- """Set all configuration at once (legacy function)"""
41
- config = {
42
- "GEMINI_API_KEY": key,
43
- "AI_MODEL": model,
44
- "AUTO_ADD_ALL": str(auto_add_all).lower()
45
- }
46
-
47
- if os.path.exists(".env"):
48
- with open(".env", "r") as f:
49
- lines = f.readlines()
50
-
51
- existing_keys = set()
52
- new_lines = []
53
- for line in lines:
54
- key_name = line.split("=")[0] if "=" in line else None
55
- if key_name in config:
56
- new_lines.append(f"{key_name}={config[key_name]}\n")
57
- existing_keys.add(key_name)
58
- else:
59
- new_lines.append(line)
60
-
61
- for key_name, value in config.items():
62
- if key_name not in existing_keys:
63
- new_lines.append(f"{key_name}={value}\n")
64
-
65
- with open(".env", "w") as f:
66
- f.writelines(new_lines)
67
- else:
68
- with open(".env", "w") as f:
69
- for key_name, value in config.items():
70
- f.write(f"{key_name}={value}\n")
71
-
72
- _ensure_gitignore()
73
-
74
- def get_api_key_status():
75
- """Check if API key is set"""
76
- if os.path.exists(".env"):
77
- with open(".env", "r") as f:
78
- lines = f.readlines()
79
- api_line = next((line for line in lines if line.startswith("GEMINI_API_KEY=")), None)
80
- if api_line:
81
- value = api_line.split("=", 1)[1].strip()
82
- return bool(value)
83
- return False
84
-
85
- def get_configured_model():
86
- """Get the currently configured AI model from .env file"""
87
- if os.path.exists(".env"):
88
- with open(".env", "r") as outfile:
89
- lines = outfile.readlines()
90
- model_line = next((line for line in lines if line.startswith("AI_MODEL=")), None)
91
- if model_line:
92
- return model_line.split("=", 1)[1].strip()
93
- return "gemini-2.0-flash" # Default fallback
94
-
95
- def get_auto_add_setting():
96
- """Get the auto-add all files setting from .env file"""
97
- if os.path.exists(".env"):
98
- with open(".env", "r") as outfile:
99
- lines = outfile.readlines()
100
- auto_add_line = next((line for line in lines if line.startswith("AUTO_ADD_ALL=")), None)
101
- if auto_add_line:
102
- return auto_add_line.split("=", 1)[1].strip().lower() == "true"
103
- return True # Default: auto-add all files (current behavior)