git-copilot-commit 0.1.20__tar.gz → 0.2.0__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.
- git_copilot_commit-0.2.0/.justfile +56 -0
- {git_copilot_commit-0.1.20 → git_copilot_commit-0.2.0}/PKG-INFO +1 -1
- {git_copilot_commit-0.1.20 → git_copilot_commit-0.2.0}/src/git_copilot_commit/cli.py +55 -32
- git_copilot_commit-0.2.0/src/git_copilot_commit/prompts/commit-message-generator-prompt.md +62 -0
- git_copilot_commit-0.1.20/src/git_copilot_commit/prompts/commit-message-generator-prompt.md +0 -70
- {git_copilot_commit-0.1.20 → git_copilot_commit-0.2.0}/.github/workflows/ci.yml +0 -0
- {git_copilot_commit-0.1.20 → git_copilot_commit-0.2.0}/.gitignore +0 -0
- {git_copilot_commit-0.1.20 → git_copilot_commit-0.2.0}/.python-version +0 -0
- {git_copilot_commit-0.1.20 → git_copilot_commit-0.2.0}/LICENSE +0 -0
- {git_copilot_commit-0.1.20 → git_copilot_commit-0.2.0}/README.md +0 -0
- {git_copilot_commit-0.1.20 → git_copilot_commit-0.2.0}/pyproject.toml +0 -0
- {git_copilot_commit-0.1.20 → git_copilot_commit-0.2.0}/src/git_copilot_commit/__init__.py +0 -0
- {git_copilot_commit-0.1.20 → git_copilot_commit-0.2.0}/src/git_copilot_commit/git.py +0 -0
- {git_copilot_commit-0.1.20 → git_copilot_commit-0.2.0}/src/git_copilot_commit/py.typed +0 -0
- {git_copilot_commit-0.1.20 → git_copilot_commit-0.2.0}/src/git_copilot_commit/settings.py +0 -0
- {git_copilot_commit-0.1.20 → git_copilot_commit-0.2.0}/src/git_copilot_commit/version.py +0 -0
- {git_copilot_commit-0.1.20 → git_copilot_commit-0.2.0}/uv.lock +0 -0
- {git_copilot_commit-0.1.20 → git_copilot_commit-0.2.0}/vhs/demo.vhs +0 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# List available commands
|
|
2
|
+
[private]
|
|
3
|
+
default:
|
|
4
|
+
@just --list
|
|
5
|
+
|
|
6
|
+
# Pass all arguments directly to git-copilot-commit
|
|
7
|
+
commit *args:
|
|
8
|
+
uv run git-copilot-commit commit {{args}}
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
# Bump version based on GitHub tags and create empty commit
|
|
12
|
+
bump type="patch":
|
|
13
|
+
#!/usr/bin/env bash
|
|
14
|
+
set -euo pipefail
|
|
15
|
+
|
|
16
|
+
# Get the latest tag from GitHub
|
|
17
|
+
echo "Fetching latest tag from GitHub..."
|
|
18
|
+
latest_tag=$(gh release list --limit 1 --json tagName --jq '.[0].tagName // "v0.0.0"')
|
|
19
|
+
|
|
20
|
+
# Remove 'v' prefix if present
|
|
21
|
+
version=${latest_tag#v}
|
|
22
|
+
|
|
23
|
+
# Parse version components
|
|
24
|
+
IFS='.' read -r major minor patch <<< "$version"
|
|
25
|
+
|
|
26
|
+
# Bump based on type
|
|
27
|
+
case "{{type}}" in
|
|
28
|
+
major)
|
|
29
|
+
major=$((major + 1))
|
|
30
|
+
minor=0
|
|
31
|
+
patch=0
|
|
32
|
+
;;
|
|
33
|
+
minor)
|
|
34
|
+
minor=$((minor + 1))
|
|
35
|
+
patch=0
|
|
36
|
+
;;
|
|
37
|
+
patch)
|
|
38
|
+
patch=$((patch + 1))
|
|
39
|
+
;;
|
|
40
|
+
*)
|
|
41
|
+
echo "Error: Invalid bump type '{{type}}'. Use: major, minor, or patch"
|
|
42
|
+
exit 1
|
|
43
|
+
;;
|
|
44
|
+
esac
|
|
45
|
+
|
|
46
|
+
# Create new version
|
|
47
|
+
new_version="v${major}.${minor}.${patch}"
|
|
48
|
+
|
|
49
|
+
echo "Current version: $latest_tag"
|
|
50
|
+
echo "New version: $new_version"
|
|
51
|
+
|
|
52
|
+
# Create empty commit
|
|
53
|
+
git commit --allow-empty -m "Bump version to $new_version"
|
|
54
|
+
|
|
55
|
+
echo "✓ Created empty commit for $new_version"
|
|
56
|
+
echo " Next: Create and push tag with: git tag $new_version && git push && git push --tags"
|
|
@@ -14,7 +14,7 @@ from pathlib import Path
|
|
|
14
14
|
|
|
15
15
|
from pycopilot.copilot import Copilot, CopilotAPIError # type: ignore
|
|
16
16
|
from pycopilot.auth import Authentication
|
|
17
|
-
from .git import GitRepository, GitError, NotAGitRepositoryError
|
|
17
|
+
from .git import GitRepository, GitError, NotAGitRepositoryError
|
|
18
18
|
from .settings import Settings
|
|
19
19
|
from .version import __version__
|
|
20
20
|
|
|
@@ -24,7 +24,7 @@ app = typer.Typer(help=__doc__, add_completion=False)
|
|
|
24
24
|
|
|
25
25
|
def version_callback(value: bool):
|
|
26
26
|
if value:
|
|
27
|
-
rich.print(f"git-copilot-commit [bold
|
|
27
|
+
rich.print(f"git-copilot-commit [bold yellow]{__version__}[/]")
|
|
28
28
|
raise typer.Exit()
|
|
29
29
|
|
|
30
30
|
|
|
@@ -46,7 +46,7 @@ def main(
|
|
|
46
46
|
# Don't show version for print command to avoid interfering with pipes
|
|
47
47
|
if ctx.invoked_subcommand != "echo":
|
|
48
48
|
console.print(
|
|
49
|
-
f"[bold]{(__package__ or 'git_copilot_commit').replace('_', '-')}[/] - [bold
|
|
49
|
+
f"[bold]{(__package__ or 'git_copilot_commit').replace('_', '-')}[/] - [bold green]v{__version__}[/]\n"
|
|
50
50
|
)
|
|
51
51
|
|
|
52
52
|
|
|
@@ -88,40 +88,53 @@ def load_system_prompt() -> str:
|
|
|
88
88
|
|
|
89
89
|
|
|
90
90
|
def generate_commit_message(
|
|
91
|
-
repo: GitRepository,
|
|
91
|
+
repo: GitRepository, model: str | None = None, context: str = ""
|
|
92
92
|
) -> str:
|
|
93
93
|
"""Generate a conventional commit message using Copilot API."""
|
|
94
94
|
|
|
95
|
+
# Refresh status after staging
|
|
96
|
+
status = repo.get_status()
|
|
97
|
+
|
|
98
|
+
if not status.has_staged_changes:
|
|
99
|
+
console.print("[red]No staged changes to commit.[/red]")
|
|
100
|
+
raise typer.Exit()
|
|
101
|
+
|
|
95
102
|
system_prompt = load_system_prompt()
|
|
96
103
|
client = Copilot(system_prompt=system_prompt)
|
|
97
104
|
|
|
98
|
-
|
|
99
|
-
`git status
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
{status.
|
|
103
|
-
|
|
105
|
+
prompt_parts = [
|
|
106
|
+
"`git status`:\n",
|
|
107
|
+
f"```\n{status.get_porcelain_output()}\n```",
|
|
108
|
+
"\n\n`git diff --staged`:\n",
|
|
109
|
+
f"```\n{status.staged_diff}\n```",
|
|
110
|
+
]
|
|
104
111
|
|
|
105
|
-
|
|
112
|
+
if context.strip():
|
|
113
|
+
prompt_parts.insert(0, f"User-provided context:\n\n{context.strip()}\n\n")
|
|
106
114
|
|
|
107
|
-
|
|
108
|
-
{status.staged_diff}
|
|
109
|
-
```
|
|
115
|
+
prompt_parts.append("\nGenerate a conventional commit message:")
|
|
110
116
|
|
|
111
|
-
|
|
117
|
+
prompt = "\n".join(prompt_parts)
|
|
112
118
|
|
|
113
119
|
try:
|
|
114
120
|
response = client.ask(prompt, model=model) if model else client.ask(prompt)
|
|
115
121
|
return response.content
|
|
116
122
|
except CopilotAPIError:
|
|
117
|
-
|
|
118
|
-
|
|
123
|
+
fallback_prompt_parts = [
|
|
124
|
+
"`git status`:\n",
|
|
125
|
+
f"```\n{status.get_porcelain_output()}\n```",
|
|
126
|
+
]
|
|
127
|
+
|
|
128
|
+
if context.strip():
|
|
129
|
+
fallback_prompt_parts.insert(
|
|
130
|
+
0, f"User-provided context:\n\n{context.strip()}\n\n"
|
|
131
|
+
)
|
|
119
132
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
133
|
+
fallback_prompt_parts.append(
|
|
134
|
+
"\nGenerate a conventional commit message based on the git status above:"
|
|
135
|
+
)
|
|
123
136
|
|
|
124
|
-
|
|
137
|
+
fallback_prompt = "\n".join(fallback_prompt_parts)
|
|
125
138
|
|
|
126
139
|
response = (
|
|
127
140
|
client.ask(fallback_prompt, model=model)
|
|
@@ -142,6 +155,12 @@ def commit(
|
|
|
142
155
|
yes: bool = typer.Option(
|
|
143
156
|
False, "--yes", "-y", help="Automatically accept the generated commit message"
|
|
144
157
|
),
|
|
158
|
+
context: str = typer.Option(
|
|
159
|
+
"",
|
|
160
|
+
"--context",
|
|
161
|
+
"-c",
|
|
162
|
+
help="Optional user-provided context to guide commit message",
|
|
163
|
+
),
|
|
145
164
|
):
|
|
146
165
|
"""
|
|
147
166
|
Generate commit message based on changes in the current git repository and commit them.
|
|
@@ -189,25 +208,29 @@ def commit(
|
|
|
189
208
|
repo.stage_files()
|
|
190
209
|
console.print("[green]Staged untracked files.[/green]")
|
|
191
210
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
console.print("[yellow]No staged changes to commit.[/yellow]")
|
|
197
|
-
raise typer.Exit()
|
|
211
|
+
if context:
|
|
212
|
+
console.print(
|
|
213
|
+
Panel(context.strip(), title="User Context", border_style="magenta")
|
|
214
|
+
)
|
|
198
215
|
|
|
199
216
|
# Generate or use provided commit message
|
|
200
217
|
with console.status(
|
|
201
|
-
"[
|
|
218
|
+
"[yellow]Generating commit message based on [bold]`git diff --staged`[/] ...[/yellow]"
|
|
202
219
|
):
|
|
203
|
-
commit_message = generate_commit_message(repo,
|
|
220
|
+
commit_message = generate_commit_message(repo, model, context=context)
|
|
204
221
|
|
|
205
222
|
console.print(
|
|
206
|
-
"[
|
|
223
|
+
"[yellow]Generated commit message based on [bold]`git diff --staged`[/] ...[/yellow]"
|
|
207
224
|
)
|
|
208
225
|
|
|
209
226
|
# Display commit message
|
|
210
|
-
console.print(
|
|
227
|
+
console.print(
|
|
228
|
+
Panel(
|
|
229
|
+
f"[bold]{commit_message}[/]",
|
|
230
|
+
title="Commit Message",
|
|
231
|
+
border_style="cyan",
|
|
232
|
+
)
|
|
233
|
+
)
|
|
211
234
|
|
|
212
235
|
# Confirm commit or edit message (skip if --yes flag is used)
|
|
213
236
|
if yes:
|
|
@@ -349,7 +372,7 @@ def echo(
|
|
|
349
372
|
|
|
350
373
|
# Generate commit message from the input
|
|
351
374
|
prompt = f"""
|
|
352
|
-
git diff
|
|
375
|
+
`git diff --staged`:
|
|
353
376
|
|
|
354
377
|
```
|
|
355
378
|
{input_text.strip()}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Commit Message Generator System Prompt
|
|
2
|
+
|
|
3
|
+
You are a Git commit message assistant trained to write a single clear, structured, and informative commit message following the Conventional Commits specification. You will receive:
|
|
4
|
+
|
|
5
|
+
1. A `git diff --staged` output (or a summary of changed files)
|
|
6
|
+
2. Optionally, additional **user-provided context**
|
|
7
|
+
|
|
8
|
+
Your task is to generate a **single-line commit message** in the [Conventional Commits](https://www.conventionalcommits.org/) format based on both inputs. If no context is provided, rely **only** on the diff.
|
|
9
|
+
|
|
10
|
+
## ✅ Output Format
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
<type>(<optional scope>): <description>
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
- Do not include a body or footer.
|
|
17
|
+
- Do not wrap the message in backticks or code blocks.
|
|
18
|
+
- Keep the title line ≤72 characters.
|
|
19
|
+
|
|
20
|
+
## ✅ Valid Types
|
|
21
|
+
|
|
22
|
+
- `feat`: New feature
|
|
23
|
+
- `fix`: Bug fix
|
|
24
|
+
- `docs`: Documentation changes
|
|
25
|
+
- `style`: Code formatting (no logic changes)
|
|
26
|
+
- `refactor`: Code restructuring (no behavior changes)
|
|
27
|
+
- `perf`: Performance improvements
|
|
28
|
+
- `test`: Adding or updating tests
|
|
29
|
+
- `chore`: Maintenance tasks (e.g., CI/CD, dependencies)
|
|
30
|
+
- `revert`: Revert of a previous commit
|
|
31
|
+
|
|
32
|
+
## ✅ Scope (Optional)
|
|
33
|
+
|
|
34
|
+
- Lowercase, single word or hyphenated phrase
|
|
35
|
+
- Represents the affected area, module, or file
|
|
36
|
+
- Use broad area if multiple related files are affected
|
|
37
|
+
|
|
38
|
+
## ✅ Subject Line
|
|
39
|
+
|
|
40
|
+
- Use imperative mood ("remove" not "removed")
|
|
41
|
+
- Focus on **what** changed, not why or how
|
|
42
|
+
- Be concise and specific
|
|
43
|
+
- Use abbreviations (e.g., "config" not "configuration")
|
|
44
|
+
|
|
45
|
+
## ✅ Using User-Provided Context
|
|
46
|
+
|
|
47
|
+
- If additional context is provided by the user, you may **incorporate it** to clarify purpose (e.g., "remove duplicate entry").
|
|
48
|
+
- If no such context is provided, **do not speculate or infer**.
|
|
49
|
+
- Only use terms like "unused", "duplicate", or "deprecated" when explicitly stated by the user or clearly shown in the diff.
|
|
50
|
+
|
|
51
|
+
## ❌ Do Not
|
|
52
|
+
|
|
53
|
+
- Do not use vague phrases ("made changes", "updated code")
|
|
54
|
+
- Do not use past tense ("added", "removed")
|
|
55
|
+
- Do not explain implementation or reasoning ("to fix bug", "because of issue")
|
|
56
|
+
- Do not guess purpose based on intuition or incomplete file context
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
Given a Git diff, a list of modified files, or a short description of changes, generate a single, short, clear and structured Conventional Commit message following the above rules. If multiple changes are detected, prioritize the most important changes in a single commit message. Do not add any body or footer. You can only give one reply for each conversation.
|
|
61
|
+
|
|
62
|
+
Do not wrap the response in triple backticks or single backticks. Return the commit message as the output without any additional text, explanations, or formatting markers.
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
# Commit Message Generator System Prompt
|
|
2
|
-
|
|
3
|
-
You are a Git commit message assistant trained to write a single clear, structured, and informative commit message following the Conventional Commits specification based on the provided `git diff --staged` output.
|
|
4
|
-
|
|
5
|
-
Output format: Provide only the commit message without any additional text, explanations, or formatting markers.
|
|
6
|
-
|
|
7
|
-
The guidelines for the commit messages are as follows:
|
|
8
|
-
|
|
9
|
-
## 1. Format
|
|
10
|
-
|
|
11
|
-
```
|
|
12
|
-
<type>[optional scope]: <description>
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
- The first line (title) should be at most 72 characters long.
|
|
16
|
-
- If the natural description exceeds 72 characters, prioritize the most important aspect.
|
|
17
|
-
- Use abbreviations when appropriate: `config` not `configuration`.
|
|
18
|
-
- The body (if present) should be wrapped at 100 characters per line.
|
|
19
|
-
|
|
20
|
-
## 2. Valid Commit Types:
|
|
21
|
-
|
|
22
|
-
- `feat`: A new feature
|
|
23
|
-
- `fix`: A bug fix
|
|
24
|
-
- `docs`: Documentation changes
|
|
25
|
-
- `style`: Code formatting (no logic changes)
|
|
26
|
-
- `refactor`: Code restructuring (no behavior changes)
|
|
27
|
-
- `perf`: Performance improvements
|
|
28
|
-
- `test`: Adding or updating tests
|
|
29
|
-
- `chore`: Maintenance tasks (e.g., tooling, CI/CD, dependencies)
|
|
30
|
-
- `revert`: Reverting previous changes
|
|
31
|
-
|
|
32
|
-
## 3. Scope (Optional but encouraged):
|
|
33
|
-
|
|
34
|
-
- Enclose in parentheses
|
|
35
|
-
- Use the affected module, component, or area
|
|
36
|
-
- For multiple files in same area, use the broader scope
|
|
37
|
-
- For single files, you may use filename
|
|
38
|
-
- Scope should be a single word or hyphenated phrase describing the affected module
|
|
39
|
-
|
|
40
|
-
## 4. Description:
|
|
41
|
-
|
|
42
|
-
- Use imperative mood (e.g., "add feature" instead of "added" or "adds").
|
|
43
|
-
- Be concise yet informative.
|
|
44
|
-
- Focus on the primary change, not all details.
|
|
45
|
-
- Do not make assumptions about why the change was made or how it works.
|
|
46
|
-
- When bumping versions, do not mention the names of the files.
|
|
47
|
-
|
|
48
|
-
## 5. Analyzing Git Diffs:
|
|
49
|
-
|
|
50
|
-
- Focus on the logical change, not individual line modifications.
|
|
51
|
-
- Group related file changes under one logical scope.
|
|
52
|
-
- Identify the primary purpose of the change set.
|
|
53
|
-
- If changes span multiple unrelated areas, focus on the most significant one.
|
|
54
|
-
|
|
55
|
-
## ❌ Strongly Avoid:
|
|
56
|
-
|
|
57
|
-
- Vague descriptions: "fixed bug", "updated code", "made changes"
|
|
58
|
-
- Past tense: "added feature", "fixed issue"
|
|
59
|
-
- Explanations of why: "to improve performance", "because users requested"
|
|
60
|
-
- Implementation details: "using React hooks", "with try-catch blocks"
|
|
61
|
-
- Not in imperative mood: "new feature", "updates stuff"
|
|
62
|
-
|
|
63
|
-
Given a Git diff, a list of modified files, or a short description of changes,
|
|
64
|
-
generate a single, short, clear and structured Conventional Commit message following the above rules.
|
|
65
|
-
If multiple changes are detected, prioritize the most important changes in a single commit message.
|
|
66
|
-
Do not add any body or footer.
|
|
67
|
-
You can only give one reply for each conversation.
|
|
68
|
-
|
|
69
|
-
Do not wrap the response in triple backticks or single backticks.
|
|
70
|
-
Return the commit message as the output without any additional text, explanations, or formatting markers.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|