gac 2.2.0__py3-none-any.whl → 2.3.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/ai.py +26 -0
- gac/ai_utils.py +28 -13
- gac/cli.py +4 -0
- gac/constants.py +1 -1
- gac/git.py +42 -0
- gac/init_cli.py +174 -61
- gac/language_cli.py +8 -8
- gac/main.py +505 -169
- gac/prompt.py +101 -15
- gac/security.py +1 -1
- gac/workflow_utils.py +131 -0
- {gac-2.2.0.dist-info → gac-2.3.0.dist-info}/METADATA +5 -1
- {gac-2.2.0.dist-info → gac-2.3.0.dist-info}/RECORD +17 -16
- {gac-2.2.0.dist-info → gac-2.3.0.dist-info}/WHEEL +0 -0
- {gac-2.2.0.dist-info → gac-2.3.0.dist-info}/entry_points.txt +0 -0
- {gac-2.2.0.dist-info → gac-2.3.0.dist-info}/licenses/LICENSE +0 -0
gac/prompt.py
CHANGED
|
@@ -246,30 +246,30 @@ DEFAULT_USER_TEMPLATE = """<hint>
|
|
|
246
246
|
Additional context provided by the user: <hint_text></hint_text>
|
|
247
247
|
</hint>
|
|
248
248
|
|
|
249
|
-
<
|
|
250
|
-
<
|
|
251
|
-
</
|
|
249
|
+
<git_diff>
|
|
250
|
+
<diff></diff>
|
|
251
|
+
</git_diff>
|
|
252
252
|
|
|
253
253
|
<git_diff_stat>
|
|
254
254
|
<diff_stat></diff_stat>
|
|
255
255
|
</git_diff_stat>
|
|
256
256
|
|
|
257
|
-
<
|
|
258
|
-
<
|
|
259
|
-
</
|
|
257
|
+
<git_status>
|
|
258
|
+
<status></status>
|
|
259
|
+
</git_status>
|
|
260
|
+
|
|
261
|
+
<language_instructions>
|
|
262
|
+
IMPORTANT: You MUST write the entire commit message in <language_name></language_name>.
|
|
263
|
+
All text in the commit message, including the summary line and body, must be in <language_name></language_name>.
|
|
264
|
+
<prefix_instruction></prefix_instruction>
|
|
265
|
+
</language_instructions>
|
|
260
266
|
|
|
261
|
-
<
|
|
267
|
+
<format_instructions>
|
|
262
268
|
IMMEDIATELY AFTER ANALYZING THE CHANGES, RESPOND WITH ONLY THE COMMIT MESSAGE.
|
|
263
269
|
DO NOT include any preamble, reasoning, explanations or anything other than the commit message itself.
|
|
264
270
|
DO NOT use markdown formatting, headers, or code blocks.
|
|
265
271
|
The entire response will be passed directly to 'git commit -m'.
|
|
266
|
-
|
|
267
|
-
<language>
|
|
268
|
-
IMPORTANT: You MUST write the entire commit message in <language_name></language_name>.
|
|
269
|
-
All text in the commit message, including the summary line and body, must be in <language_name></language_name>.
|
|
270
|
-
<prefix_instruction></prefix_instruction>
|
|
271
|
-
</language>
|
|
272
|
-
</instructions>"""
|
|
272
|
+
</format_instructions>"""
|
|
273
273
|
|
|
274
274
|
|
|
275
275
|
# ============================================================================
|
|
@@ -518,7 +518,7 @@ The ENTIRE commit message, including the prefix, must be in {language}."""
|
|
|
518
518
|
|
|
519
519
|
user_template = user_template.replace("<prefix_instruction></prefix_instruction>", prefix_instruction)
|
|
520
520
|
else:
|
|
521
|
-
user_template = _remove_template_section(user_template, "
|
|
521
|
+
user_template = _remove_template_section(user_template, "language_instructions")
|
|
522
522
|
logger.debug("Using default language (English)")
|
|
523
523
|
|
|
524
524
|
user_template = re.sub(r"\n(?:[ \t]*\n){2,}", "\n\n", user_template)
|
|
@@ -526,6 +526,92 @@ The ENTIRE commit message, including the prefix, must be in {language}."""
|
|
|
526
526
|
return system_template.strip(), user_template.strip()
|
|
527
527
|
|
|
528
528
|
|
|
529
|
+
def build_group_prompt(
|
|
530
|
+
status: str,
|
|
531
|
+
processed_diff: str,
|
|
532
|
+
diff_stat: str,
|
|
533
|
+
one_liner: bool,
|
|
534
|
+
hint: str,
|
|
535
|
+
infer_scope: bool,
|
|
536
|
+
verbose: bool,
|
|
537
|
+
system_template_path: str | None,
|
|
538
|
+
language: str | None,
|
|
539
|
+
translate_prefixes: bool,
|
|
540
|
+
) -> tuple[str, str]:
|
|
541
|
+
"""Build prompt for grouped commit generation (JSON output with multiple commits)."""
|
|
542
|
+
system_prompt, user_prompt = build_prompt(
|
|
543
|
+
status=status,
|
|
544
|
+
processed_diff=processed_diff,
|
|
545
|
+
diff_stat=diff_stat,
|
|
546
|
+
one_liner=one_liner,
|
|
547
|
+
hint=hint,
|
|
548
|
+
infer_scope=infer_scope,
|
|
549
|
+
verbose=verbose,
|
|
550
|
+
system_template_path=system_template_path,
|
|
551
|
+
language=language,
|
|
552
|
+
translate_prefixes=translate_prefixes,
|
|
553
|
+
)
|
|
554
|
+
|
|
555
|
+
user_prompt = _remove_template_section(user_prompt, "format_instructions")
|
|
556
|
+
|
|
557
|
+
grouping_instructions = """
|
|
558
|
+
<format_instructions>
|
|
559
|
+
Your task is to split the changed files into separate, logical commits. Think of this like sorting files into different folders where each file belongs in exactly one folder.
|
|
560
|
+
|
|
561
|
+
CRITICAL REQUIREMENT - Every File Used Exactly Once:
|
|
562
|
+
You must assign EVERY file from the diff to exactly ONE commit.
|
|
563
|
+
- NO file should be left out
|
|
564
|
+
- NO file should appear in multiple commits
|
|
565
|
+
- EVERY file must be used once and ONLY once
|
|
566
|
+
|
|
567
|
+
Think of it like dealing cards: Once you've dealt a card to a player, that card cannot be dealt to another player.
|
|
568
|
+
|
|
569
|
+
HOW TO SPLIT THE FILES:
|
|
570
|
+
1. Review all changed files in the diff
|
|
571
|
+
2. Group files by logical relationship (e.g., related features, bug fixes, documentation)
|
|
572
|
+
3. Assign each file to exactly one commit based on what makes the most sense
|
|
573
|
+
4. If a file could fit in multiple commits, pick the best fit and move on - do NOT duplicate it
|
|
574
|
+
5. Continue until every single file has been assigned to a commit
|
|
575
|
+
|
|
576
|
+
ORDERING:
|
|
577
|
+
Order the commits in a logical sequence considering dependencies, natural progression, and overall workflow.
|
|
578
|
+
|
|
579
|
+
YOUR RESPONSE FORMAT:
|
|
580
|
+
Respond with valid JSON following this structure:
|
|
581
|
+
```json
|
|
582
|
+
{
|
|
583
|
+
"commits": [
|
|
584
|
+
{
|
|
585
|
+
"files": ["src/auth/login.ts", "src/auth/logout.ts"],
|
|
586
|
+
"message": "<commit_message_conforming_to_prescribed_structure_and_format>"
|
|
587
|
+
},
|
|
588
|
+
{
|
|
589
|
+
"files": ["src/db/schema.sql", "src/db/migrations/001.sql"],
|
|
590
|
+
"message": "<commit_message_conforming_to_prescribed_structure_and_format>"
|
|
591
|
+
},
|
|
592
|
+
{
|
|
593
|
+
"files": ["tests/auth.test.ts", "tests/db.test.ts", "README.md"],
|
|
594
|
+
"message": "<commit_message_conforming_to_prescribed_structure_and_format>"
|
|
595
|
+
}
|
|
596
|
+
]
|
|
597
|
+
}
|
|
598
|
+
```
|
|
599
|
+
|
|
600
|
+
☝️ Notice how EVERY file path in the example above appears exactly ONCE across all commits. "src/auth/login.ts" appears once. "tests/auth.test.ts" appears once. No file is repeated.
|
|
601
|
+
|
|
602
|
+
VALIDATION CHECKLIST - Before responding, verify:
|
|
603
|
+
□ Total files across all commits = Total files in the diff
|
|
604
|
+
□ Each file appears in exactly 1 commit (no duplicates, no omissions)
|
|
605
|
+
□ Every commit has at least one file
|
|
606
|
+
□ If you list all files from all commits and count them, you get the same count as unique files in the diff
|
|
607
|
+
</format_instructions>
|
|
608
|
+
"""
|
|
609
|
+
|
|
610
|
+
user_prompt = user_prompt + grouping_instructions
|
|
611
|
+
|
|
612
|
+
return system_prompt, user_prompt
|
|
613
|
+
|
|
614
|
+
|
|
529
615
|
# ============================================================================
|
|
530
616
|
# Message Cleaning Helpers
|
|
531
617
|
# ============================================================================
|
gac/security.py
CHANGED
|
@@ -54,7 +54,7 @@ class SecretPatterns:
|
|
|
54
54
|
PRIVATE_KEY = re.compile(r"-----BEGIN (?:RSA |EC |OPENSSH )?PRIVATE KEY-----")
|
|
55
55
|
|
|
56
56
|
# Bearer Tokens (require actual token with specific characteristics)
|
|
57
|
-
BEARER_TOKEN = re.compile(r"Bearer\s+[A-Za-z0-9]{20,}[/=]
|
|
57
|
+
BEARER_TOKEN = re.compile(r"Bearer\s+[A-Za-z0-9]{20,}[/=]*(?:\s|$)", re.IGNORECASE)
|
|
58
58
|
|
|
59
59
|
# JWT Tokens
|
|
60
60
|
JWT_TOKEN = re.compile(r"eyJ[A-Za-z0-9_-]+\.eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+")
|
gac/workflow_utils.py
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import tempfile
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
import click
|
|
6
|
+
from rich.console import Console
|
|
7
|
+
|
|
8
|
+
logger = logging.getLogger(__name__)
|
|
9
|
+
console = Console()
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def handle_confirmation_loop(
|
|
13
|
+
commit_message: str,
|
|
14
|
+
conversation_messages: list[dict[str, str]],
|
|
15
|
+
quiet: bool,
|
|
16
|
+
model: str,
|
|
17
|
+
) -> tuple[str, str, list[dict[str, str]]]:
|
|
18
|
+
from rich.panel import Panel
|
|
19
|
+
|
|
20
|
+
from gac.utils import edit_commit_message_inplace
|
|
21
|
+
|
|
22
|
+
while True:
|
|
23
|
+
response = click.prompt(
|
|
24
|
+
"Proceed with commit above? [y/n/r/e/<feedback>]",
|
|
25
|
+
type=str,
|
|
26
|
+
show_default=False,
|
|
27
|
+
).strip()
|
|
28
|
+
response_lower = response.lower()
|
|
29
|
+
|
|
30
|
+
if response_lower in ["y", "yes"]:
|
|
31
|
+
return ("yes", commit_message, conversation_messages)
|
|
32
|
+
if response_lower in ["n", "no"]:
|
|
33
|
+
return ("no", commit_message, conversation_messages)
|
|
34
|
+
if response == "":
|
|
35
|
+
continue
|
|
36
|
+
if response_lower in ["e", "edit"]:
|
|
37
|
+
edited_message = edit_commit_message_inplace(commit_message)
|
|
38
|
+
if edited_message:
|
|
39
|
+
commit_message = edited_message
|
|
40
|
+
conversation_messages[-1] = {"role": "assistant", "content": commit_message}
|
|
41
|
+
logger.info("Commit message edited by user")
|
|
42
|
+
console.print("\n[bold green]Edited commit message:[/bold green]")
|
|
43
|
+
console.print(Panel(commit_message, title="Commit Message", border_style="cyan"))
|
|
44
|
+
else:
|
|
45
|
+
console.print("[yellow]Using previous message.[/yellow]")
|
|
46
|
+
console.print(Panel(commit_message, title="Commit Message", border_style="cyan"))
|
|
47
|
+
continue
|
|
48
|
+
if response_lower in ["r", "reroll"]:
|
|
49
|
+
msg = "Please provide an alternative commit message using the same repository context."
|
|
50
|
+
conversation_messages.append({"role": "user", "content": msg})
|
|
51
|
+
console.print("[cyan]Regenerating commit message...[/cyan]")
|
|
52
|
+
return ("regenerate", commit_message, conversation_messages)
|
|
53
|
+
|
|
54
|
+
msg = f"Please revise the commit message based on this feedback: {response}"
|
|
55
|
+
conversation_messages.append({"role": "user", "content": msg})
|
|
56
|
+
console.print(f"[cyan]Regenerating commit message with feedback: {response}[/cyan]")
|
|
57
|
+
return ("regenerate", commit_message, conversation_messages)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def execute_commit(commit_message: str, no_verify: bool) -> None:
|
|
61
|
+
from gac.git import run_git_command
|
|
62
|
+
|
|
63
|
+
commit_args = ["commit", "-m", commit_message]
|
|
64
|
+
if no_verify:
|
|
65
|
+
commit_args.append("--no-verify")
|
|
66
|
+
run_git_command(commit_args)
|
|
67
|
+
logger.info("Commit created successfully")
|
|
68
|
+
console.print("[green]Commit created successfully[/green]")
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def check_token_warning(
|
|
72
|
+
prompt_tokens: int,
|
|
73
|
+
warning_limit: int,
|
|
74
|
+
require_confirmation: bool,
|
|
75
|
+
) -> bool:
|
|
76
|
+
if warning_limit and prompt_tokens > warning_limit:
|
|
77
|
+
console.print(f"[yellow]⚠️ WARNING: Prompt has {prompt_tokens} tokens (limit: {warning_limit})[/yellow]")
|
|
78
|
+
if require_confirmation:
|
|
79
|
+
proceed = click.confirm("Do you want to continue anyway?", default=True)
|
|
80
|
+
if not proceed:
|
|
81
|
+
console.print("[yellow]Aborted due to token limit.[/yellow]")
|
|
82
|
+
return False
|
|
83
|
+
return True
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def display_commit_message(commit_message: str, prompt_tokens: int, model: str, quiet: bool) -> None:
|
|
87
|
+
from rich.panel import Panel
|
|
88
|
+
|
|
89
|
+
from gac.ai_utils import count_tokens
|
|
90
|
+
|
|
91
|
+
console.print("[bold green]Generated commit message:[/bold green]")
|
|
92
|
+
console.print(Panel(commit_message, title="Commit Message", border_style="cyan"))
|
|
93
|
+
|
|
94
|
+
if not quiet:
|
|
95
|
+
completion_tokens = count_tokens(commit_message, model)
|
|
96
|
+
total_tokens = prompt_tokens + completion_tokens
|
|
97
|
+
console.print(
|
|
98
|
+
f"[dim]Token usage: {prompt_tokens} prompt + {completion_tokens} completion = {total_tokens} total[/dim]"
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def restore_staging(staged_files: list[str], staged_diff: str | None = None) -> None:
|
|
103
|
+
"""Restore the git staging area to a previous state.
|
|
104
|
+
|
|
105
|
+
Args:
|
|
106
|
+
staged_files: List of file paths that should be staged
|
|
107
|
+
staged_diff: Optional staged diff to reapply for partial staging
|
|
108
|
+
"""
|
|
109
|
+
from gac.git import run_git_command
|
|
110
|
+
|
|
111
|
+
run_git_command(["reset", "HEAD"])
|
|
112
|
+
|
|
113
|
+
if staged_diff:
|
|
114
|
+
temp_path: Path | None = None
|
|
115
|
+
try:
|
|
116
|
+
with tempfile.NamedTemporaryFile("w", delete=False) as tmp:
|
|
117
|
+
tmp.write(staged_diff)
|
|
118
|
+
temp_path = Path(tmp.name)
|
|
119
|
+
run_git_command(["apply", "--cached", str(temp_path)])
|
|
120
|
+
return
|
|
121
|
+
except Exception as e:
|
|
122
|
+
logger.warning(f"Failed to reapply staged diff, falling back to file list: {e}")
|
|
123
|
+
finally:
|
|
124
|
+
if temp_path:
|
|
125
|
+
temp_path.unlink(missing_ok=True)
|
|
126
|
+
|
|
127
|
+
for file_path in staged_files:
|
|
128
|
+
try:
|
|
129
|
+
run_git_command(["add", file_path])
|
|
130
|
+
except Exception as e:
|
|
131
|
+
logger.warning(f"Failed to restore staging for {file_path}: {e}")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gac
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.3.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
|
|
@@ -108,6 +108,7 @@ uv tool upgrade gac
|
|
|
108
108
|
- **Understands intent**: Analyzes code structure, logic, and patterns to understand the "why" behind your changes, not just what changed
|
|
109
109
|
- **Semantic awareness**: Recognizes refactoring, bug fixes, features, and breaking changes to generate contextually appropriate messages
|
|
110
110
|
- **Intelligent filtering**: Prioritizes meaningful changes while ignoring generated files, dependencies, and artifacts
|
|
111
|
+
- **Intelligent commit grouping** - Automatically group related changes into multiple logical commits with `--group`
|
|
111
112
|
|
|
112
113
|
### 📝 **Multiple Message Formats**
|
|
113
114
|
|
|
@@ -175,6 +176,9 @@ gac -v -s
|
|
|
175
176
|
# Quick one-liner for small changes
|
|
176
177
|
gac -o
|
|
177
178
|
|
|
179
|
+
# Group changes into logically related commits
|
|
180
|
+
gac -ag
|
|
181
|
+
|
|
178
182
|
# Debug what the LLM sees
|
|
179
183
|
gac --show-prompt
|
|
180
184
|
|
|
@@ -1,21 +1,22 @@
|
|
|
1
1
|
gac/__init__.py,sha256=z9yGInqtycFIT3g1ca24r-A3699hKVaRqGUI79wsmMc,415
|
|
2
|
-
gac/__version__.py,sha256=
|
|
3
|
-
gac/ai.py,sha256=
|
|
4
|
-
gac/ai_utils.py,sha256=
|
|
5
|
-
gac/cli.py,sha256=
|
|
2
|
+
gac/__version__.py,sha256=KHcXxHr9uQK6amU8n-h2x8FjOXkwzFkaiUL62tdw07w,66
|
|
3
|
+
gac/ai.py,sha256=6SQK4axBE0uEbF3eKVTvQtGL9X1TbxoBOrY7NuYIfiM,5009
|
|
4
|
+
gac/ai_utils.py,sha256=reJINfsKlX0HAg5HPlH4ImO6FvibzgRZ0ruG9u1cxa8,8312
|
|
5
|
+
gac/cli.py,sha256=i4VEQeq5k2Rmy8DzqVuJCUyYxSq87x_wLTmH3VgOkyg,5986
|
|
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=yyvYycIfRJ9SZZIMIhwn1s6yohjcaNM-fGXl_R9w1dI,9586
|
|
9
9
|
gac/diff_cli.py,sha256=wnVQ9OFGnM0d2Pj9WVjWbo0jxqIuRHVAwmb8wU9Pa3E,5676
|
|
10
10
|
gac/errors.py,sha256=ysDIVRCd0YQVTOW3Q6YzdolxCdtkoQCAFf3_jrqbjUY,7916
|
|
11
|
-
gac/git.py,sha256=
|
|
12
|
-
gac/init_cli.py,sha256=
|
|
13
|
-
gac/language_cli.py,sha256=
|
|
14
|
-
gac/main.py,sha256=
|
|
11
|
+
gac/git.py,sha256=m7EqMYYRNRfD68HbxZD3TUY3DZlzZSdqa38SeLSNb6A,9347
|
|
12
|
+
gac/init_cli.py,sha256=3n6A9FX0atPpO8XMMDypxLTIPxXv-cg_YtRl4yAKUOc,14449
|
|
13
|
+
gac/language_cli.py,sha256=Nl1WKR-o7APOKTg_7T5LoJmg4GaiXnGUzR5XFJayYwI,3050
|
|
14
|
+
gac/main.py,sha256=r5zwAYBBn-qz9kix2Zn5yJ-ac3-42u6HaIoirybOaTQ,28784
|
|
15
15
|
gac/preprocess.py,sha256=hk2p2X4-xVDvuy-T1VMzMa9k5fTUbhlWDyw89DCf81Q,15379
|
|
16
|
-
gac/prompt.py,sha256=
|
|
17
|
-
gac/security.py,sha256=
|
|
16
|
+
gac/prompt.py,sha256=ofumb6DmxJceqZLUlUyLE9b7Mwx9BpIEHweKEV9eicw,31841
|
|
17
|
+
gac/security.py,sha256=QT91mBEo2Y7la-aXvKuF2zhWuoOSXb6PWKLJ93kSy2k,9926
|
|
18
18
|
gac/utils.py,sha256=owkUzwJBX8mi0VrP3HKxku5vJj_JlaShzTYwjjsHn-4,8126
|
|
19
|
+
gac/workflow_utils.py,sha256=fc0yPRBeA-7P2WiVPFa7A_NjXTW68UlbUv6_AXVYAzA,5023
|
|
19
20
|
gac/providers/__init__.py,sha256=pT1xcKoZkPm6BWaxcAQ299-Ca5zZ31kf4DeQYAim9Tw,1367
|
|
20
21
|
gac/providers/anthropic.py,sha256=VK5d1s1PmBNDwh_x7illQ2CIZIHNIYU28btVfizwQPs,2036
|
|
21
22
|
gac/providers/cerebras.py,sha256=Ik8lhlsliGJVkgDgqlThfpra9tqbdYQZkaC4eNxRd9w,1648
|
|
@@ -36,8 +37,8 @@ gac/providers/streamlake.py,sha256=KAA2ZnpuEI5imzvdWVWUhEBHSP0BMnprKXte6CbwBWY,2
|
|
|
36
37
|
gac/providers/synthetic.py,sha256=sRMIJTS9LpcXd9A7qp_ZjZxdqtTKRn9fl1W4YwJZP4c,1855
|
|
37
38
|
gac/providers/together.py,sha256=1bUIVHfYzcEDw4hQPE8qV6hjc2JNHPv_khVgpk2IJxI,1667
|
|
38
39
|
gac/providers/zai.py,sha256=kywhhrCfPBu0rElZyb-iENxQxxpVGykvePuL4xrXlaU,2739
|
|
39
|
-
gac-2.
|
|
40
|
-
gac-2.
|
|
41
|
-
gac-2.
|
|
42
|
-
gac-2.
|
|
43
|
-
gac-2.
|
|
40
|
+
gac-2.3.0.dist-info/METADATA,sha256=5VIkxFRlLC4NPEGIuZyRUo253XYHdQYt_8uX3gbvjjg,9782
|
|
41
|
+
gac-2.3.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
42
|
+
gac-2.3.0.dist-info/entry_points.txt,sha256=tdjN-XMmcWfL92swuRAjT62bFLOAwk9bTMRLGP5Z4aI,36
|
|
43
|
+
gac-2.3.0.dist-info/licenses/LICENSE,sha256=vOab37NouL1PNs5BswnPayrMCqaN2sqLfMQfqPDrpZg,1103
|
|
44
|
+
gac-2.3.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|