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 +1 -1
- gac/constants.py +1 -1
- gac/main.py +14 -1
- gac/utils.py +138 -0
- {gac-2.1.0.dist-info → gac-2.2.0.dist-info}/METADATA +17 -4
- {gac-2.1.0.dist-info → gac-2.2.0.dist-info}/RECORD +9 -9
- {gac-2.1.0.dist-info → gac-2.2.0.dist-info}/WHEEL +0 -0
- {gac-2.1.0.dist-info → gac-2.2.0.dist-info}/entry_points.txt +0 -0
- {gac-2.1.0.dist-info → gac-2.2.0.dist-info}/licenses/LICENSE +0 -0
gac/__version__.py
CHANGED
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 =
|
|
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.
|
|
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
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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.
|
|
40
|
-
gac-2.
|
|
41
|
-
gac-2.
|
|
42
|
-
gac-2.
|
|
43
|
-
gac-2.
|
|
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
|
|
File without changes
|
|
File without changes
|