gac 2.1.0__py3-none-any.whl → 2.2.0__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.

Potentially problematic release.


This version of gac might be problematic. Click here for more details.

gac/__version__.py CHANGED
@@ -1,3 +1,3 @@
1
1
  """Version information for gac package."""
2
2
 
3
- __version__ = "2.1.0"
3
+ __version__ = "2.2.0"
gac/constants.py CHANGED
@@ -21,7 +21,7 @@ class EnvDefaults:
21
21
  MAX_RETRIES: int = 3
22
22
  TEMPERATURE: float = 1
23
23
  MAX_OUTPUT_TOKENS: int = 1024 # includes reasoning tokens
24
- WARNING_LIMIT_TOKENS: int = 16384
24
+ WARNING_LIMIT_TOKENS: int = 32768
25
25
  ALWAYS_INCLUDE_SCOPE: bool = False
26
26
  SKIP_SECRET_SCAN: bool = False
27
27
  VERBOSE: bool = False
gac/main.py CHANGED
@@ -25,6 +25,7 @@ from gac.git import (
25
25
  from gac.preprocess import preprocess_diff
26
26
  from gac.prompt import build_prompt, clean_commit_message
27
27
  from gac.security import get_affected_files, scan_staged_diff
28
+ from gac.utils import edit_commit_message_inplace
28
29
 
29
30
  logger = logging.getLogger(__name__)
30
31
 
@@ -277,7 +278,7 @@ def main(
277
278
  if require_confirmation:
278
279
  while True:
279
280
  response = click.prompt(
280
- "Proceed with commit above? [y/n/r/<feedback>]",
281
+ "Proceed with commit above? [y/n/r/e/<feedback>]",
281
282
  type=str,
282
283
  show_default=False,
283
284
  ).strip()
@@ -290,6 +291,18 @@ def main(
290
291
  sys.exit(0)
291
292
  if response == "":
292
293
  continue
294
+ if response_lower in ["e", "edit"]:
295
+ edited_message = edit_commit_message_inplace(commit_message)
296
+ if edited_message:
297
+ commit_message = edited_message
298
+ conversation_messages[-1] = {"role": "assistant", "content": commit_message}
299
+ logger.info("Commit message edited by user")
300
+ console.print("\n[bold green]Edited commit message:[/bold green]")
301
+ console.print(Panel(commit_message, title="Commit Message", border_style="cyan"))
302
+ else:
303
+ console.print("[yellow]Using previous message.[/yellow]")
304
+ console.print(Panel(commit_message, title="Commit Message", border_style="cyan"))
305
+ continue
293
306
  if response_lower in ["r", "reroll"]:
294
307
  feedback_message = (
295
308
  "Please provide an alternative commit message using the same repository context."
gac/utils.py CHANGED
@@ -130,3 +130,141 @@ def run_subprocess(
130
130
  # Convert generic exceptions to CalledProcessError for consistency
131
131
  raise subprocess.CalledProcessError(1, command, "", str(e)) from e
132
132
  return ""
133
+
134
+
135
+ def edit_commit_message_inplace(message: str) -> str | None:
136
+ """Edit commit message in-place using rich terminal editing.
137
+
138
+ Uses prompt_toolkit to provide a rich editing experience with:
139
+ - Multi-line editing
140
+ - Vi/Emacs key bindings
141
+ - Line editing capabilities
142
+ - Esc+Enter or Ctrl+S to submit
143
+ - Ctrl+C to cancel
144
+
145
+ Args:
146
+ message: The initial commit message
147
+
148
+ Returns:
149
+ The edited commit message, or None if editing was cancelled
150
+
151
+ Example:
152
+ >>> edited = edit_commit_message_inplace("feat: add feature")
153
+ >>> # User can edit the message using vi/emacs key bindings
154
+ >>> # Press Esc+Enter or Ctrl+S to submit
155
+ """
156
+ from prompt_toolkit import Application
157
+ from prompt_toolkit.buffer import Buffer
158
+ from prompt_toolkit.document import Document
159
+ from prompt_toolkit.enums import EditingMode
160
+ from prompt_toolkit.key_binding import KeyBindings
161
+ from prompt_toolkit.layout import HSplit, Layout, Window
162
+ from prompt_toolkit.layout.controls import BufferControl, FormattedTextControl
163
+ from prompt_toolkit.layout.margins import ScrollbarMargin
164
+ from prompt_toolkit.styles import Style
165
+
166
+ try:
167
+ console.print("\n[info]Edit commit message:[/info]")
168
+ console.print()
169
+
170
+ # Create buffer for text editing
171
+ text_buffer = Buffer(
172
+ document=Document(text=message, cursor_position=0),
173
+ multiline=True,
174
+ enable_history_search=False,
175
+ )
176
+
177
+ # Track submission state
178
+ cancelled = {"value": False}
179
+ submitted = {"value": False}
180
+
181
+ # Create text editor window
182
+ text_window = Window(
183
+ content=BufferControl(
184
+ buffer=text_buffer,
185
+ focus_on_click=True,
186
+ ),
187
+ height=lambda: max(5, message.count("\n") + 3),
188
+ wrap_lines=True,
189
+ right_margins=[ScrollbarMargin()],
190
+ )
191
+
192
+ # Create hint window
193
+ hint_window = Window(
194
+ content=FormattedTextControl(
195
+ text=[("class:hint", " Esc+Enter or Ctrl+S to submit | Ctrl+C to cancel ")],
196
+ ),
197
+ height=1,
198
+ dont_extend_height=True,
199
+ )
200
+
201
+ # Create layout
202
+ root_container = HSplit(
203
+ [
204
+ text_window,
205
+ hint_window,
206
+ ]
207
+ )
208
+
209
+ layout = Layout(root_container, focused_element=text_window)
210
+
211
+ # Create key bindings
212
+ kb = KeyBindings()
213
+
214
+ @kb.add("c-s")
215
+ def _(event):
216
+ """Submit with Ctrl+S."""
217
+ submitted["value"] = True
218
+ event.app.exit()
219
+
220
+ @kb.add("c-c")
221
+ def _(event):
222
+ """Cancel editing."""
223
+ cancelled["value"] = True
224
+ event.app.exit()
225
+
226
+ @kb.add("escape", "enter")
227
+ def _(event):
228
+ """Submit with Esc+Enter."""
229
+ submitted["value"] = True
230
+ event.app.exit()
231
+
232
+ # Create and run application
233
+ custom_style = Style.from_dict(
234
+ {
235
+ "hint": "#888888",
236
+ }
237
+ )
238
+
239
+ app: Application[None] = Application(
240
+ layout=layout,
241
+ key_bindings=kb,
242
+ full_screen=False,
243
+ mouse_support=False,
244
+ editing_mode=EditingMode.VI, # Enable vi key bindings
245
+ style=custom_style,
246
+ )
247
+
248
+ app.run()
249
+
250
+ # Handle result
251
+ if cancelled["value"]:
252
+ console.print("\n[yellow]Edit cancelled.[/yellow]")
253
+ return None
254
+
255
+ if submitted["value"]:
256
+ edited_message = text_buffer.text.strip()
257
+ if not edited_message:
258
+ console.print("[yellow]Commit message cannot be empty. Edit cancelled.[/yellow]")
259
+ return None
260
+ return edited_message
261
+
262
+ return None
263
+
264
+ except (EOFError, KeyboardInterrupt):
265
+ console.print("\n[yellow]Edit cancelled.[/yellow]")
266
+ return None
267
+ except Exception as e:
268
+ logger.error(f"Error during in-place editing: {e}")
269
+ console.print(f"[error]Failed to edit commit message: {e}[/error]")
270
+ return None
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gac
3
- Version: 2.1.0
3
+ Version: 2.2.0
4
4
  Summary: LLM-powered Git commit message generator with multi-provider support
5
5
  Project-URL: Homepage, https://github.com/cellwebb/gac
6
6
  Project-URL: Documentation, https://github.com/cellwebb/gac#readme
@@ -25,6 +25,7 @@ Requires-Dist: click>=8.3.0
25
25
  Requires-Dist: halo
26
26
  Requires-Dist: httpcore>=1.0.9
27
27
  Requires-Dist: httpx>=0.28.0
28
+ Requires-Dist: prompt-toolkit>=3.0.36
28
29
  Requires-Dist: pydantic>=2.12.0
29
30
  Requires-Dist: python-dotenv>=1.1.1
30
31
  Requires-Dist: questionary
@@ -123,7 +124,7 @@ uv tool upgrade gac
123
124
 
124
125
  ### 💻 **Developer Experience**
125
126
 
126
- - **Interactive feedback**: Type `r` to reroll, or directly type your feedback like `make it shorter` or `focus on the bug fix`
127
+ - **Interactive feedback**: Type `r` to reroll, `e` to edit in-place with vi/emacs keybindings, or directly type your feedback like `make it shorter` or `focus on the bug fix`
127
128
  - **One-command workflows**: Complete workflows with flags like `gac -ayp` (stage all, auto-confirm, push)
128
129
  - **Git integration**: Respects pre-commit and lefthook hooks, running them before expensive LLM operations
129
130
 
@@ -146,7 +147,7 @@ git add .
146
147
  # Generate and commit with LLM
147
148
  gac
148
149
 
149
- # Review → y (commit) | n (cancel) | r (reroll) | or type feedback
150
+ # Review → y (commit) | n (cancel) | r (reroll) | e (edit) | or type feedback
150
151
  ```
151
152
 
152
153
  ### Common Commands
@@ -183,12 +184,17 @@ gac --skip-secret-scan
183
184
 
184
185
  ### Interactive Feedback System
185
186
 
186
- Not happy with the result? You have two options:
187
+ Not happy with the result? You have several options:
187
188
 
188
189
  ```bash
189
190
  # Simple reroll (no feedback)
190
191
  r
191
192
 
193
+ # Edit in-place with rich terminal editing
194
+ e
195
+ # Uses prompt_toolkit for multi-line editing with vi/emacs keybindings
196
+ # Press Esc+Enter or Ctrl+S to submit, Ctrl+C to cancel
197
+
192
198
  # Or just type your feedback directly!
193
199
  make it shorter and focus on the performance improvement
194
200
  use conventional commit format with scope
@@ -197,6 +203,13 @@ explain the security implications
197
203
  # Press Enter on empty input to see the prompt again
198
204
  ```
199
205
 
206
+ The edit feature (`e`) provides rich in-place terminal editing, allowing you to:
207
+
208
+ - **Edit naturally**: Multi-line editing with familiar vi/emacs key bindings
209
+ - **Make quick fixes**: Correct typos, adjust wording, or refine formatting
210
+ - **Add details**: Include information the LLM might have missed
211
+ - **Restructure**: Reorganize bullet points or change the message structure
212
+
200
213
  ---
201
214
 
202
215
  ## Configuration
@@ -1,21 +1,21 @@
1
1
  gac/__init__.py,sha256=z9yGInqtycFIT3g1ca24r-A3699hKVaRqGUI79wsmMc,415
2
- gac/__version__.py,sha256=bTn7Tm6xpc6WrZ2ANt-S3NGI6Rng6eG5VxYTYs-UWqM,66
2
+ gac/__version__.py,sha256=r4l_6jWyV87srDy0RVRKwXgzwJscJzmR4SM825CjGh0,66
3
3
  gac/ai.py,sha256=iBHeLsqe6iyFj86wbvEosyy4vkjAN1BlLQeqtb_rfmo,4303
4
4
  gac/ai_utils.py,sha256=094ujZVlbDnHM3HPxiBSCbGi_5MD6bOKCj2SjKVDDK0,7445
5
5
  gac/cli.py,sha256=SOrSfrlku99O7O8zev5hRVmADAmJ7AIkM7Z0dquuCbQ,5807
6
6
  gac/config.py,sha256=O9n09-sFOqlkf47vieEP7fI5I7uhu1cXn9PUZ5yiYkw,1974
7
7
  gac/config_cli.py,sha256=v9nFHZO1RvK9fzHyuUS6SG-BCLHMsdOMDwWamBhVVh4,1608
8
- gac/constants.py,sha256=Spuqa1ntae0CtNGu-Emxzc_Zz1CipikO3h5BWcUlUW0,9586
8
+ gac/constants.py,sha256=zgylsiKnpBunoNzVT6RpAVe9m8cgxZrZ55kRN6ZP_cM,9586
9
9
  gac/diff_cli.py,sha256=wnVQ9OFGnM0d2Pj9WVjWbo0jxqIuRHVAwmb8wU9Pa3E,5676
10
10
  gac/errors.py,sha256=ysDIVRCd0YQVTOW3Q6YzdolxCdtkoQCAFf3_jrqbjUY,7916
11
11
  gac/git.py,sha256=g6tvph50zV-wrTWrxARYXEpl0NeI8-ffFwHoqhp3fSE,8033
12
12
  gac/init_cli.py,sha256=KvFOvjjyMpdJ3MhJFvXSuYjdfulPA6hCP11YXwjHrqw,8849
13
13
  gac/language_cli.py,sha256=J4xZNNrMvHamsjK4TCsOVj0lrjYDtLMuHlnTqN0-N_w,3024
14
- gac/main.py,sha256=LJhONOP_w09TE7HOd4T_NiAzmiqemY2N7EZ6biqwVQE,15221
14
+ gac/main.py,sha256=fK48fGHeJ4qGsttbKDoMXs4Gj3NncFHz_F_cJZI70IQ,16159
15
15
  gac/preprocess.py,sha256=hk2p2X4-xVDvuy-T1VMzMa9k5fTUbhlWDyw89DCf81Q,15379
16
16
  gac/prompt.py,sha256=HLvsW3YQLdTfw2N9UgjZ0jWckUc1x76V7Kcqjcl8Fsk,28633
17
17
  gac/security.py,sha256=15Yp6YR8QC4eECJi1BUCkMteh_veZXUbLL6W8qGcDm4,9920
18
- gac/utils.py,sha256=nV42-brIHW_fBg7x855GM8nRrqEBbRzTSweg-GTyGE8,3971
18
+ gac/utils.py,sha256=owkUzwJBX8mi0VrP3HKxku5vJj_JlaShzTYwjjsHn-4,8126
19
19
  gac/providers/__init__.py,sha256=pT1xcKoZkPm6BWaxcAQ299-Ca5zZ31kf4DeQYAim9Tw,1367
20
20
  gac/providers/anthropic.py,sha256=VK5d1s1PmBNDwh_x7illQ2CIZIHNIYU28btVfizwQPs,2036
21
21
  gac/providers/cerebras.py,sha256=Ik8lhlsliGJVkgDgqlThfpra9tqbdYQZkaC4eNxRd9w,1648
@@ -36,8 +36,8 @@ gac/providers/streamlake.py,sha256=KAA2ZnpuEI5imzvdWVWUhEBHSP0BMnprKXte6CbwBWY,2
36
36
  gac/providers/synthetic.py,sha256=sRMIJTS9LpcXd9A7qp_ZjZxdqtTKRn9fl1W4YwJZP4c,1855
37
37
  gac/providers/together.py,sha256=1bUIVHfYzcEDw4hQPE8qV6hjc2JNHPv_khVgpk2IJxI,1667
38
38
  gac/providers/zai.py,sha256=kywhhrCfPBu0rElZyb-iENxQxxpVGykvePuL4xrXlaU,2739
39
- gac-2.1.0.dist-info/METADATA,sha256=VYrOHxHKI_6jbenHBBKt3F8IfBNf-n4Vlnf_LUYBuGM,8958
40
- gac-2.1.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
41
- gac-2.1.0.dist-info/entry_points.txt,sha256=tdjN-XMmcWfL92swuRAjT62bFLOAwk9bTMRLGP5Z4aI,36
42
- gac-2.1.0.dist-info/licenses/LICENSE,sha256=vOab37NouL1PNs5BswnPayrMCqaN2sqLfMQfqPDrpZg,1103
43
- gac-2.1.0.dist-info/RECORD,,
39
+ gac-2.2.0.dist-info/METADATA,sha256=Z5Vv7oBzqWKVr7lDmk_HFjq3kF13DNnCkMnXKZDKRtA,9609
40
+ gac-2.2.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
41
+ gac-2.2.0.dist-info/entry_points.txt,sha256=tdjN-XMmcWfL92swuRAjT62bFLOAwk9bTMRLGP5Z4aI,36
42
+ gac-2.2.0.dist-info/licenses/LICENSE,sha256=vOab37NouL1PNs5BswnPayrMCqaN2sqLfMQfqPDrpZg,1103
43
+ gac-2.2.0.dist-info/RECORD,,
File without changes