gac 3.10.3__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/__init__.py +15 -0
- gac/__version__.py +3 -0
- gac/ai.py +109 -0
- gac/ai_utils.py +246 -0
- gac/auth_cli.py +214 -0
- gac/cli.py +218 -0
- gac/commit_executor.py +62 -0
- gac/config.py +125 -0
- gac/config_cli.py +95 -0
- gac/constants.py +328 -0
- gac/diff_cli.py +159 -0
- gac/errors.py +231 -0
- gac/git.py +372 -0
- gac/git_state_validator.py +184 -0
- gac/grouped_commit_workflow.py +423 -0
- gac/init_cli.py +70 -0
- gac/interactive_mode.py +182 -0
- gac/language_cli.py +377 -0
- gac/main.py +476 -0
- gac/model_cli.py +430 -0
- gac/oauth/__init__.py +27 -0
- gac/oauth/claude_code.py +464 -0
- gac/oauth/qwen_oauth.py +327 -0
- gac/oauth/token_store.py +81 -0
- gac/preprocess.py +511 -0
- gac/prompt.py +878 -0
- gac/prompt_builder.py +88 -0
- gac/providers/README.md +437 -0
- gac/providers/__init__.py +80 -0
- gac/providers/anthropic.py +17 -0
- gac/providers/azure_openai.py +57 -0
- gac/providers/base.py +329 -0
- gac/providers/cerebras.py +15 -0
- gac/providers/chutes.py +25 -0
- gac/providers/claude_code.py +79 -0
- gac/providers/custom_anthropic.py +103 -0
- gac/providers/custom_openai.py +44 -0
- gac/providers/deepseek.py +15 -0
- gac/providers/error_handler.py +139 -0
- gac/providers/fireworks.py +15 -0
- gac/providers/gemini.py +90 -0
- gac/providers/groq.py +15 -0
- gac/providers/kimi_coding.py +27 -0
- gac/providers/lmstudio.py +80 -0
- gac/providers/minimax.py +15 -0
- gac/providers/mistral.py +15 -0
- gac/providers/moonshot.py +15 -0
- gac/providers/ollama.py +73 -0
- gac/providers/openai.py +32 -0
- gac/providers/openrouter.py +21 -0
- gac/providers/protocol.py +71 -0
- gac/providers/qwen.py +64 -0
- gac/providers/registry.py +58 -0
- gac/providers/replicate.py +156 -0
- gac/providers/streamlake.py +31 -0
- gac/providers/synthetic.py +40 -0
- gac/providers/together.py +15 -0
- gac/providers/zai.py +31 -0
- gac/py.typed +0 -0
- gac/security.py +293 -0
- gac/utils.py +401 -0
- gac/workflow_utils.py +217 -0
- gac-3.10.3.dist-info/METADATA +283 -0
- gac-3.10.3.dist-info/RECORD +67 -0
- gac-3.10.3.dist-info/WHEEL +4 -0
- gac-3.10.3.dist-info/entry_points.txt +2 -0
- gac-3.10.3.dist-info/licenses/LICENSE +16 -0
gac/constants.py
ADDED
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
"""Constants for the Git Auto Commit (gac) project."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
from enum import Enum
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class FileStatus(Enum):
|
|
8
|
+
"""File status for Git operations."""
|
|
9
|
+
|
|
10
|
+
MODIFIED = "M"
|
|
11
|
+
ADDED = "A"
|
|
12
|
+
DELETED = "D"
|
|
13
|
+
RENAMED = "R"
|
|
14
|
+
COPIED = "C"
|
|
15
|
+
UNTRACKED = "?"
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class EnvDefaults:
|
|
19
|
+
"""Default values for environment variables."""
|
|
20
|
+
|
|
21
|
+
MAX_RETRIES: int = 3
|
|
22
|
+
TEMPERATURE: float = 1
|
|
23
|
+
MAX_OUTPUT_TOKENS: int = 4096 # includes reasoning tokens
|
|
24
|
+
WARNING_LIMIT_TOKENS: int = 32768
|
|
25
|
+
ALWAYS_INCLUDE_SCOPE: bool = False
|
|
26
|
+
SKIP_SECRET_SCAN: bool = False
|
|
27
|
+
VERBOSE: bool = False
|
|
28
|
+
NO_TIKTOKEN: bool = False
|
|
29
|
+
NO_VERIFY_SSL: bool = False # Skip SSL certificate verification (for corporate proxies)
|
|
30
|
+
HOOK_TIMEOUT: int = 120 # Timeout for pre-commit and lefthook hooks in seconds
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class ProviderDefaults:
|
|
34
|
+
"""Default values for provider configurations."""
|
|
35
|
+
|
|
36
|
+
HTTP_TIMEOUT: int = 120 # seconds - timeout for HTTP requests to LLM providers
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class Logging:
|
|
40
|
+
"""Logging configuration constants."""
|
|
41
|
+
|
|
42
|
+
DEFAULT_LEVEL: str = "WARNING"
|
|
43
|
+
LEVELS: list[str] = ["DEBUG", "INFO", "WARNING", "ERROR"]
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class Utility:
|
|
47
|
+
"""General utility constants."""
|
|
48
|
+
|
|
49
|
+
DEFAULT_ENCODING: str = "cl100k_base" # llm encoding
|
|
50
|
+
DEFAULT_DIFF_TOKEN_LIMIT: int = 15000 # Maximum tokens for diff processing
|
|
51
|
+
MAX_WORKERS: int = os.cpu_count() or 4 # Maximum number of parallel workers
|
|
52
|
+
MAX_DISPLAYED_SECRET_LENGTH: int = 50 # Maximum length for displaying secrets
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class FilePatterns:
|
|
56
|
+
"""Patterns for identifying special file types."""
|
|
57
|
+
|
|
58
|
+
# Regex patterns to detect binary file changes in git diffs (e.g., images or other non-text files)
|
|
59
|
+
BINARY: list[str] = [
|
|
60
|
+
r"Binary files .* differ",
|
|
61
|
+
r"GIT binary patch",
|
|
62
|
+
]
|
|
63
|
+
|
|
64
|
+
# Regex patterns to detect minified files in git diffs (e.g., JavaScript or CSS files)
|
|
65
|
+
MINIFIED_EXTENSIONS: list[str] = [
|
|
66
|
+
".min.js",
|
|
67
|
+
".min.css",
|
|
68
|
+
".bundle.js",
|
|
69
|
+
".bundle.css",
|
|
70
|
+
".compressed.js",
|
|
71
|
+
".compressed.css",
|
|
72
|
+
".opt.js",
|
|
73
|
+
".opt.css",
|
|
74
|
+
]
|
|
75
|
+
|
|
76
|
+
# Regex patterns to detect build directories in git diffs (e.g., dist, build, vendor, etc.)
|
|
77
|
+
BUILD_DIRECTORIES: list[str] = [
|
|
78
|
+
"/dist/",
|
|
79
|
+
"/build/",
|
|
80
|
+
"/vendor/",
|
|
81
|
+
"/node_modules/",
|
|
82
|
+
"/assets/vendor/",
|
|
83
|
+
"/public/build/",
|
|
84
|
+
"/static/dist/",
|
|
85
|
+
]
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class FileTypeImportance:
|
|
89
|
+
"""Importance multipliers for different file types."""
|
|
90
|
+
|
|
91
|
+
EXTENSIONS: dict[str, float] = {
|
|
92
|
+
# Programming languages
|
|
93
|
+
".py": 5.0, # Python
|
|
94
|
+
".js": 4.5, # JavaScript
|
|
95
|
+
".ts": 4.5, # TypeScript
|
|
96
|
+
".jsx": 4.8, # React JS
|
|
97
|
+
".tsx": 4.8, # React TS
|
|
98
|
+
".go": 4.5, # Go
|
|
99
|
+
".rs": 4.5, # Rust
|
|
100
|
+
".java": 4.2, # Java
|
|
101
|
+
".c": 4.2, # C
|
|
102
|
+
".h": 4.2, # C/C++ header
|
|
103
|
+
".cpp": 4.2, # C++
|
|
104
|
+
".rb": 4.2, # Ruby
|
|
105
|
+
".php": 4.0, # PHP
|
|
106
|
+
".scala": 4.0, # Scala
|
|
107
|
+
".swift": 4.0, # Swift
|
|
108
|
+
".kt": 4.0, # Kotlin
|
|
109
|
+
# Configuration
|
|
110
|
+
".json": 3.5, # JSON config
|
|
111
|
+
".yaml": 3.8, # YAML config
|
|
112
|
+
".yml": 3.8, # YAML config
|
|
113
|
+
".toml": 3.8, # TOML config
|
|
114
|
+
".ini": 3.5, # INI config
|
|
115
|
+
".env": 3.5, # Environment variables
|
|
116
|
+
# Documentation
|
|
117
|
+
".md": 2.5, # Markdown (reduced to prioritize code changes)
|
|
118
|
+
".rst": 2.5, # reStructuredText (reduced to prioritize code changes)
|
|
119
|
+
# Web
|
|
120
|
+
".html": 3.5, # HTML
|
|
121
|
+
".css": 3.5, # CSS
|
|
122
|
+
".scss": 3.5, # SCSS
|
|
123
|
+
".svg": 2.5, # SVG graphics
|
|
124
|
+
# Build & CI
|
|
125
|
+
"Dockerfile": 4.0, # Docker
|
|
126
|
+
".github/workflows": 4.0, # GitHub Actions
|
|
127
|
+
"CMakeLists.txt": 3.8, # CMake
|
|
128
|
+
"Makefile": 3.8, # Make
|
|
129
|
+
"package.json": 4.2, # NPM package
|
|
130
|
+
"pyproject.toml": 4.2, # Python project
|
|
131
|
+
"requirements.txt": 4.0, # Python requirements
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
class CodePatternImportance:
|
|
136
|
+
"""Importance multipliers for different code patterns."""
|
|
137
|
+
|
|
138
|
+
# Regex patterns to detect code structure changes in git diffs (e.g., class, function, import)
|
|
139
|
+
# Note: The patterns are prefixed with "+" to match only added and modified lines
|
|
140
|
+
PATTERNS: dict[str, float] = {
|
|
141
|
+
# Structure changes
|
|
142
|
+
r"\+\s*(class|interface|enum)\s+\w+": 1.8, # Class/interface/enum definitions
|
|
143
|
+
r"\+\s*(def|function|func)\s+\w+\s*\(": 1.5, # Function definitions
|
|
144
|
+
r"\+\s*(import|from .* import)": 1.3, # Imports
|
|
145
|
+
r"\+\s*(public|private|protected)\s+\w+": 1.2, # Access modifiers
|
|
146
|
+
# Configuration changes
|
|
147
|
+
r"\+\s*\"(dependencies|devDependencies)\"": 1.4, # Package dependencies
|
|
148
|
+
r"\+\s*version[\"\s:=]+[0-9.]+": 1.3, # Version changes
|
|
149
|
+
# Logic changes
|
|
150
|
+
r"\+\s*(if|else|elif|switch|case|for|while)[\s(]": 1.2, # Control structures
|
|
151
|
+
r"\+\s*(try|catch|except|finally)[\s:]": 1.2, # Exception handling
|
|
152
|
+
r"\+\s*return\s+": 1.1, # Return statements
|
|
153
|
+
r"\+\s*await\s+": 1.1, # Async/await
|
|
154
|
+
# Comments & docs
|
|
155
|
+
r"\+\s*(//|#|/\*|\*\*)\s*TODO": 1.2, # TODOs
|
|
156
|
+
r"\+\s*(//|#|/\*|\*\*)\s*FIX": 1.3, # FIXes
|
|
157
|
+
r"\+\s*(\"\"\"|\'\'\')": 1.1, # Docstrings
|
|
158
|
+
# Test code
|
|
159
|
+
r"\+\s*(test|describe|it|should)\s*\(": 1.1, # Test definitions
|
|
160
|
+
r"\+\s*(assert|expect)": 1.0, # Assertions
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
class Languages:
|
|
165
|
+
"""Language code mappings and utilities."""
|
|
166
|
+
|
|
167
|
+
# Language code to full name mapping
|
|
168
|
+
# Supports ISO 639-1 codes and common variants
|
|
169
|
+
CODE_MAP: dict[str, str] = {
|
|
170
|
+
# English
|
|
171
|
+
"en": "English",
|
|
172
|
+
# Chinese
|
|
173
|
+
"zh": "Simplified Chinese",
|
|
174
|
+
"zh-cn": "Simplified Chinese",
|
|
175
|
+
"zh-hans": "Simplified Chinese",
|
|
176
|
+
"zh-tw": "Traditional Chinese",
|
|
177
|
+
"zh-hant": "Traditional Chinese",
|
|
178
|
+
# Japanese
|
|
179
|
+
"ja": "Japanese",
|
|
180
|
+
# Korean
|
|
181
|
+
"ko": "Korean",
|
|
182
|
+
# Spanish
|
|
183
|
+
"es": "Spanish",
|
|
184
|
+
# Portuguese
|
|
185
|
+
"pt": "Portuguese",
|
|
186
|
+
# French
|
|
187
|
+
"fr": "French",
|
|
188
|
+
# German
|
|
189
|
+
"de": "German",
|
|
190
|
+
# Russian
|
|
191
|
+
"ru": "Russian",
|
|
192
|
+
# Hindi
|
|
193
|
+
"hi": "Hindi",
|
|
194
|
+
# Italian
|
|
195
|
+
"it": "Italian",
|
|
196
|
+
# Polish
|
|
197
|
+
"pl": "Polish",
|
|
198
|
+
# Turkish
|
|
199
|
+
"tr": "Turkish",
|
|
200
|
+
# Dutch
|
|
201
|
+
"nl": "Dutch",
|
|
202
|
+
# Vietnamese
|
|
203
|
+
"vi": "Vietnamese",
|
|
204
|
+
# Thai
|
|
205
|
+
"th": "Thai",
|
|
206
|
+
# Indonesian
|
|
207
|
+
"id": "Indonesian",
|
|
208
|
+
# Swedish
|
|
209
|
+
"sv": "Swedish",
|
|
210
|
+
# Arabic
|
|
211
|
+
"ar": "Arabic",
|
|
212
|
+
# Hebrew
|
|
213
|
+
"he": "Hebrew",
|
|
214
|
+
# Greek
|
|
215
|
+
"el": "Greek",
|
|
216
|
+
# Danish
|
|
217
|
+
"da": "Danish",
|
|
218
|
+
# Norwegian
|
|
219
|
+
"no": "Norwegian",
|
|
220
|
+
"nb": "Norwegian",
|
|
221
|
+
"nn": "Norwegian",
|
|
222
|
+
# Finnish
|
|
223
|
+
"fi": "Finnish",
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
# List of languages with display names and English names for CLI selection
|
|
227
|
+
# Format: (display_name, english_name)
|
|
228
|
+
LANGUAGES: list[tuple[str, str]] = [
|
|
229
|
+
("English", "English"),
|
|
230
|
+
("简体中文", "Simplified Chinese"),
|
|
231
|
+
("繁體中文", "Traditional Chinese"),
|
|
232
|
+
("日本語", "Japanese"),
|
|
233
|
+
("한국어", "Korean"),
|
|
234
|
+
("Español", "Spanish"),
|
|
235
|
+
("Português", "Portuguese"),
|
|
236
|
+
("Français", "French"),
|
|
237
|
+
("Deutsch", "German"),
|
|
238
|
+
("Русский", "Russian"),
|
|
239
|
+
("हिन्दी", "Hindi"),
|
|
240
|
+
("Italiano", "Italian"),
|
|
241
|
+
("Polski", "Polish"),
|
|
242
|
+
("Türkçe", "Turkish"),
|
|
243
|
+
("Nederlands", "Dutch"),
|
|
244
|
+
("Tiếng Việt", "Vietnamese"),
|
|
245
|
+
("ไทย", "Thai"),
|
|
246
|
+
("Bahasa Indonesia", "Indonesian"),
|
|
247
|
+
("Svenska", "Swedish"),
|
|
248
|
+
("العربية", "Arabic"),
|
|
249
|
+
("עברית", "Hebrew"),
|
|
250
|
+
("Ελληνικά", "Greek"),
|
|
251
|
+
("Dansk", "Danish"),
|
|
252
|
+
("Norsk", "Norwegian"),
|
|
253
|
+
("Suomi", "Finnish"),
|
|
254
|
+
("Custom", "Custom"),
|
|
255
|
+
]
|
|
256
|
+
|
|
257
|
+
@staticmethod
|
|
258
|
+
def resolve_code(language: str) -> str:
|
|
259
|
+
"""Resolve a language code to its full name.
|
|
260
|
+
|
|
261
|
+
Args:
|
|
262
|
+
language: Language name or code (e.g., 'Spanish', 'es', 'zh-CN')
|
|
263
|
+
|
|
264
|
+
Returns:
|
|
265
|
+
Full language name (e.g., 'Spanish', 'Simplified Chinese')
|
|
266
|
+
|
|
267
|
+
If the input is already a full language name, it's returned as-is.
|
|
268
|
+
If it's a recognized code, it's converted to the full name.
|
|
269
|
+
Otherwise, the input is returned unchanged (for custom languages).
|
|
270
|
+
"""
|
|
271
|
+
# Normalize the code to lowercase for lookup
|
|
272
|
+
code_lower = language.lower().strip()
|
|
273
|
+
|
|
274
|
+
# Check if it's a recognized code
|
|
275
|
+
if code_lower in Languages.CODE_MAP:
|
|
276
|
+
return Languages.CODE_MAP[code_lower]
|
|
277
|
+
|
|
278
|
+
# Return as-is (could be a full name or custom language)
|
|
279
|
+
return language
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
class CommitMessageConstants:
|
|
283
|
+
"""Constants for commit message generation and cleaning."""
|
|
284
|
+
|
|
285
|
+
# Conventional commit type prefixes
|
|
286
|
+
CONVENTIONAL_PREFIXES: list[str] = [
|
|
287
|
+
"feat",
|
|
288
|
+
"fix",
|
|
289
|
+
"docs",
|
|
290
|
+
"style",
|
|
291
|
+
"refactor",
|
|
292
|
+
"perf",
|
|
293
|
+
"test",
|
|
294
|
+
"build",
|
|
295
|
+
"ci",
|
|
296
|
+
"chore",
|
|
297
|
+
]
|
|
298
|
+
|
|
299
|
+
# XML tags that may leak from prompt templates into AI responses
|
|
300
|
+
XML_TAGS_TO_REMOVE: list[str] = [
|
|
301
|
+
"<git-status>",
|
|
302
|
+
"</git-status>",
|
|
303
|
+
"<git_status>",
|
|
304
|
+
"</git_status>",
|
|
305
|
+
"<git-diff>",
|
|
306
|
+
"</git-diff>",
|
|
307
|
+
"<git_diff>",
|
|
308
|
+
"</git_diff>",
|
|
309
|
+
"<repository_context>",
|
|
310
|
+
"</repository_context>",
|
|
311
|
+
"<instructions>",
|
|
312
|
+
"</instructions>",
|
|
313
|
+
"<format>",
|
|
314
|
+
"</format>",
|
|
315
|
+
"<conventions>",
|
|
316
|
+
"</conventions>",
|
|
317
|
+
]
|
|
318
|
+
|
|
319
|
+
# Indicators that mark the start of the actual commit message in AI responses
|
|
320
|
+
COMMIT_INDICATORS: list[str] = [
|
|
321
|
+
"# Your commit message:",
|
|
322
|
+
"Your commit message:",
|
|
323
|
+
"The commit message is:",
|
|
324
|
+
"Here's the commit message:",
|
|
325
|
+
"Commit message:",
|
|
326
|
+
"Final commit message:",
|
|
327
|
+
"# Commit Message",
|
|
328
|
+
]
|
gac/diff_cli.py
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# flake8: noqa: E304
|
|
2
|
+
|
|
3
|
+
"""Git diff display command for gac.
|
|
4
|
+
|
|
5
|
+
This module implements the 'gac diff' subcommand which displays git diffs with various
|
|
6
|
+
filtering and formatting options. It provides a convenient way to view staged or unstaged
|
|
7
|
+
changes, compare commits, and apply smart filtering to focus on meaningful code changes.
|
|
8
|
+
|
|
9
|
+
Key features:
|
|
10
|
+
- Display staged or unstaged changes
|
|
11
|
+
- Compare specific commits or branches
|
|
12
|
+
- Filter out binary files, minified files, and lockfiles
|
|
13
|
+
- Smart truncation of large diffs based on token limits
|
|
14
|
+
- Colored output support for better readability
|
|
15
|
+
- Integration with gac's preprocessing logic for cleaner diffs
|
|
16
|
+
|
|
17
|
+
The diff command is particularly useful for:
|
|
18
|
+
- Previewing what changes will be included in the commit message
|
|
19
|
+
- Reviewing filtered diffs before committing
|
|
20
|
+
- Comparing code changes between branches or commits
|
|
21
|
+
- Understanding what files have been modified in the staging area
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
import logging
|
|
25
|
+
import sys
|
|
26
|
+
|
|
27
|
+
import click
|
|
28
|
+
|
|
29
|
+
from gac.errors import GitError, with_error_handling
|
|
30
|
+
from gac.git import get_diff, get_staged_files
|
|
31
|
+
from gac.preprocess import (
|
|
32
|
+
filter_binary_and_minified,
|
|
33
|
+
smart_truncate_diff,
|
|
34
|
+
split_diff_into_sections,
|
|
35
|
+
)
|
|
36
|
+
from gac.utils import print_message, setup_logging
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _diff_implementation(
|
|
40
|
+
filter: bool,
|
|
41
|
+
truncate: bool,
|
|
42
|
+
max_tokens: int | None,
|
|
43
|
+
staged: bool,
|
|
44
|
+
color: bool,
|
|
45
|
+
commit1: str | None = None,
|
|
46
|
+
commit2: str | None = None,
|
|
47
|
+
) -> None:
|
|
48
|
+
"""Implementation of the diff command logic for easier testing."""
|
|
49
|
+
setup_logging()
|
|
50
|
+
# Get a logger for this module instead of using the return value of setup_logging
|
|
51
|
+
logger = logging.getLogger(__name__)
|
|
52
|
+
logger.debug("Running diff command")
|
|
53
|
+
|
|
54
|
+
# If we're comparing specific commits, don't need to check for staged changes
|
|
55
|
+
if not (commit1 or commit2):
|
|
56
|
+
# Check if there are staged changes
|
|
57
|
+
staged_files = get_staged_files()
|
|
58
|
+
if not staged_files and staged:
|
|
59
|
+
print_message("No staged changes found. Use 'git add' to stage changes.", level="error")
|
|
60
|
+
sys.exit(1)
|
|
61
|
+
|
|
62
|
+
try:
|
|
63
|
+
diff_text = get_diff(staged=staged, color=color, commit1=commit1, commit2=commit2)
|
|
64
|
+
if not diff_text:
|
|
65
|
+
print_message("No changes to display.", level="error")
|
|
66
|
+
sys.exit(1)
|
|
67
|
+
except GitError as e:
|
|
68
|
+
print_message(f"Error getting diff: {str(e)}", level="error")
|
|
69
|
+
sys.exit(1)
|
|
70
|
+
|
|
71
|
+
if filter:
|
|
72
|
+
diff_text = filter_binary_and_minified(diff_text)
|
|
73
|
+
if not diff_text:
|
|
74
|
+
print_message("No changes to display after filtering.", level="error")
|
|
75
|
+
sys.exit(1)
|
|
76
|
+
|
|
77
|
+
if truncate:
|
|
78
|
+
# Convert the diff text to the format expected by smart_truncate_diff
|
|
79
|
+
# (list of tuples with (section, score))
|
|
80
|
+
if isinstance(diff_text, str):
|
|
81
|
+
sections = split_diff_into_sections(diff_text)
|
|
82
|
+
scored_sections = [(section, 1.0) for section in sections]
|
|
83
|
+
diff_text = smart_truncate_diff(scored_sections, max_tokens or 1000, "anthropic:claude-3-haiku-latest")
|
|
84
|
+
|
|
85
|
+
if color:
|
|
86
|
+
# Use git's colored diff output
|
|
87
|
+
print(diff_text)
|
|
88
|
+
else:
|
|
89
|
+
# Strip ANSI color codes if color is disabled
|
|
90
|
+
# This is a simple approach - a more robust solution would use a library like 'strip-ansi'
|
|
91
|
+
import re
|
|
92
|
+
|
|
93
|
+
ansi_escape = re.compile(r"\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])")
|
|
94
|
+
print(ansi_escape.sub("", diff_text))
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
@click.command(name="diff")
|
|
98
|
+
# Content filtering options
|
|
99
|
+
@click.option(
|
|
100
|
+
"--filter/--no-filter",
|
|
101
|
+
default=True,
|
|
102
|
+
help="Filter out binary files, minified files, and lockfiles",
|
|
103
|
+
)
|
|
104
|
+
# Display options
|
|
105
|
+
@click.option(
|
|
106
|
+
"--color/--no-color",
|
|
107
|
+
default=True,
|
|
108
|
+
help="Show colored diff output",
|
|
109
|
+
)
|
|
110
|
+
# Diff source options
|
|
111
|
+
@click.option(
|
|
112
|
+
"--staged/--unstaged",
|
|
113
|
+
default=True,
|
|
114
|
+
help="Show staged changes (default) or unstaged changes",
|
|
115
|
+
)
|
|
116
|
+
# Size control options
|
|
117
|
+
@click.option(
|
|
118
|
+
"--truncate/--no-truncate",
|
|
119
|
+
default=True,
|
|
120
|
+
help="Truncate large diffs to a reasonable size",
|
|
121
|
+
)
|
|
122
|
+
@click.option(
|
|
123
|
+
"--max-tokens",
|
|
124
|
+
default=None,
|
|
125
|
+
type=int,
|
|
126
|
+
help="Maximum number of tokens to include in the diff",
|
|
127
|
+
)
|
|
128
|
+
@click.argument("commit1", required=False)
|
|
129
|
+
@click.argument("commit2", required=False)
|
|
130
|
+
@with_error_handling(GitError, "Failed to display diff")
|
|
131
|
+
def diff(
|
|
132
|
+
filter: bool,
|
|
133
|
+
truncate: bool,
|
|
134
|
+
max_tokens: int | None,
|
|
135
|
+
staged: bool,
|
|
136
|
+
color: bool,
|
|
137
|
+
commit1: str | None = None,
|
|
138
|
+
commit2: str | None = None,
|
|
139
|
+
) -> None:
|
|
140
|
+
"""
|
|
141
|
+
Display the diff of staged or unstaged changes.
|
|
142
|
+
|
|
143
|
+
This command shows the raw diff without generating a commit message.
|
|
144
|
+
|
|
145
|
+
You can also compare specific commits or branches by providing one or two arguments:
|
|
146
|
+
gac diff <commit1> - Shows diff between working tree and <commit1>
|
|
147
|
+
gac diff <commit1> <commit2> - Shows diff between <commit1> and <commit2>
|
|
148
|
+
|
|
149
|
+
Commit references can be commit hashes, branch names, or other Git references.
|
|
150
|
+
"""
|
|
151
|
+
_diff_implementation(
|
|
152
|
+
filter=filter,
|
|
153
|
+
truncate=truncate,
|
|
154
|
+
max_tokens=max_tokens,
|
|
155
|
+
staged=staged,
|
|
156
|
+
color=color,
|
|
157
|
+
commit1=commit1,
|
|
158
|
+
commit2=commit2,
|
|
159
|
+
)
|