dboss-cli 0.1.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.
- dboss/__init__.py +0 -0
- dboss/cli.py +63 -0
- dboss/git_utils.py +41 -0
- dboss/ollama_client.py +37 -0
- dboss/prompts.py +17 -0
- dboss_cli-0.1.0.dist-info/METADATA +9 -0
- dboss_cli-0.1.0.dist-info/RECORD +9 -0
- dboss_cli-0.1.0.dist-info/WHEEL +4 -0
- dboss_cli-0.1.0.dist-info/entry_points.txt +2 -0
dboss/__init__.py
ADDED
|
File without changes
|
dboss/cli.py
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
|
|
3
|
+
import click
|
|
4
|
+
|
|
5
|
+
from dboss.git_utils import GitError, commit as commit_fn, get_staged_diff
|
|
6
|
+
from dboss.ollama_client import OllamaError, generate, strip_code_fences
|
|
7
|
+
from dboss.prompts import build_commit_prompt
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@click.group()
|
|
11
|
+
def main():
|
|
12
|
+
"""dboss — git commit message generator."""
|
|
13
|
+
sys.stdout.reconfigure(encoding="utf-8", errors="replace")
|
|
14
|
+
sys.stderr.reconfigure(encoding="utf-8", errors="replace")
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@main.command()
|
|
18
|
+
def hello():
|
|
19
|
+
"""Smoke test: verify the CLI is installed correctly."""
|
|
20
|
+
click.echo("hello from dboss")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@main.command()
|
|
24
|
+
def commit():
|
|
25
|
+
"""Generate a commit message for staged changes and commit."""
|
|
26
|
+
try:
|
|
27
|
+
diff = get_staged_diff()
|
|
28
|
+
except GitError as e:
|
|
29
|
+
raise click.ClickException(str(e))
|
|
30
|
+
|
|
31
|
+
if not diff:
|
|
32
|
+
click.echo("No staged changes.")
|
|
33
|
+
return
|
|
34
|
+
|
|
35
|
+
prompt = build_commit_prompt(diff)
|
|
36
|
+
|
|
37
|
+
while True:
|
|
38
|
+
try:
|
|
39
|
+
message = generate(prompt)
|
|
40
|
+
except OllamaError as e:
|
|
41
|
+
raise click.ClickException(str(e))
|
|
42
|
+
|
|
43
|
+
message = strip_code_fences(message)
|
|
44
|
+
click.echo(f"\nSuggested commit message:\n\n {message}\n")
|
|
45
|
+
choice = click.prompt(
|
|
46
|
+
"[y] accept [r] regenerate [n] cancel",
|
|
47
|
+
type=click.Choice(["y", "r", "n"]),
|
|
48
|
+
default="y",
|
|
49
|
+
show_choices=False,
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
if choice == "y":
|
|
53
|
+
try:
|
|
54
|
+
commit_fn(message)
|
|
55
|
+
except GitError as e:
|
|
56
|
+
raise click.ClickException(str(e))
|
|
57
|
+
click.echo("Commit created.")
|
|
58
|
+
break
|
|
59
|
+
elif choice == "r":
|
|
60
|
+
continue
|
|
61
|
+
else:
|
|
62
|
+
click.echo("Cancelled.")
|
|
63
|
+
break
|
dboss/git_utils.py
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import subprocess
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class GitError(Exception):
|
|
5
|
+
pass
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def get_staged_diff() -> str:
|
|
9
|
+
try:
|
|
10
|
+
subprocess.run(
|
|
11
|
+
["git", "rev-parse", "--git-dir"],
|
|
12
|
+
capture_output=True,
|
|
13
|
+
check=True,
|
|
14
|
+
)
|
|
15
|
+
except FileNotFoundError:
|
|
16
|
+
raise GitError("git komutu bulunamadı. Git kurulu mu?")
|
|
17
|
+
except subprocess.CalledProcessError:
|
|
18
|
+
raise GitError("Bu dizin bir git reposu değil.")
|
|
19
|
+
|
|
20
|
+
try:
|
|
21
|
+
result = subprocess.run(
|
|
22
|
+
["git", "diff", "--staged"],
|
|
23
|
+
capture_output=True,
|
|
24
|
+
encoding="utf-8",
|
|
25
|
+
check=True,
|
|
26
|
+
)
|
|
27
|
+
return result.stdout
|
|
28
|
+
except subprocess.CalledProcessError as e:
|
|
29
|
+
raise GitError(f"git diff başarısız: {e.stderr.strip()}")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def commit(message: str) -> None:
|
|
33
|
+
try:
|
|
34
|
+
subprocess.run(
|
|
35
|
+
["git", "commit", "-m", message],
|
|
36
|
+
capture_output=True,
|
|
37
|
+
encoding="utf-8",
|
|
38
|
+
check=True,
|
|
39
|
+
)
|
|
40
|
+
except subprocess.CalledProcessError as e:
|
|
41
|
+
raise GitError(f"git commit başarısız: {e.stderr.strip()}")
|
dboss/ollama_client.py
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import re
|
|
3
|
+
|
|
4
|
+
import requests
|
|
5
|
+
|
|
6
|
+
OLLAMA_BASE_URL = "http://localhost:11434"
|
|
7
|
+
DEFAULT_MODEL = os.environ.get("DBOSS_MODEL", "qwen2.5-coder:3b")
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def strip_code_fences(text: str) -> str:
|
|
11
|
+
# Remove opening fence line: ```plaintext, ```bash, ``` etc.
|
|
12
|
+
text = re.sub(r"^```[^\n]*\n", "", text)
|
|
13
|
+
# Remove closing fence line
|
|
14
|
+
text = re.sub(r"\n```\s*$", "", text)
|
|
15
|
+
return text.strip()
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class OllamaError(Exception):
|
|
19
|
+
pass
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def generate(prompt: str, model: str = DEFAULT_MODEL) -> str:
|
|
23
|
+
url = f"{OLLAMA_BASE_URL}/api/generate"
|
|
24
|
+
payload = {"model": model, "prompt": prompt, "stream": False}
|
|
25
|
+
try:
|
|
26
|
+
resp = requests.post(url, json=payload, timeout=120)
|
|
27
|
+
except requests.exceptions.ConnectionError:
|
|
28
|
+
raise OllamaError(
|
|
29
|
+
"Ollama'ya bağlanılamadı. `ollama serve` çalışıyor mu? (localhost:11434)"
|
|
30
|
+
)
|
|
31
|
+
if resp.status_code == 404:
|
|
32
|
+
raise OllamaError(
|
|
33
|
+
f"Model bulunamadı: {model!r}. `ollama pull {model}` çalıştır."
|
|
34
|
+
)
|
|
35
|
+
if not resp.ok:
|
|
36
|
+
raise OllamaError(f"Ollama HTTP {resp.status_code}: {resp.text[:200]}")
|
|
37
|
+
return resp.json()["response"]
|
dboss/prompts.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
def build_commit_prompt(diff: str) -> str:
|
|
2
|
+
return (
|
|
3
|
+
"You are a commit message generator.\n"
|
|
4
|
+
"Rules:\n"
|
|
5
|
+
"- Use Conventional Commits format: feat:, fix:, docs:, refactor:, test:, chore:, etc.\n"
|
|
6
|
+
"- Write in English.\n"
|
|
7
|
+
"- Return ONLY the commit message. No explanation, no markdown, no quotes.\n"
|
|
8
|
+
"- Do NOT wrap the message in code fences or backticks.\n"
|
|
9
|
+
"- Do NOT add a language tag like 'plaintext'. Output raw text only.\n"
|
|
10
|
+
"- The first (summary) line must not exceed 72 characters.\n"
|
|
11
|
+
"- If needed, add a blank line after the summary, then a body.\n"
|
|
12
|
+
"- Output EXACTLY ONE commit message. Do NOT produce a list of multiple commit messages.\n"
|
|
13
|
+
"- Do NOT invent changes that are not in the diff. Only describe what the diff actually shows.\n"
|
|
14
|
+
"- Keep it to a single summary line, optionally followed by a blank line and a short body.\n\n"
|
|
15
|
+
"Git diff to summarize:\n\n"
|
|
16
|
+
f"{diff}"
|
|
17
|
+
)
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
dboss/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
dboss/cli.py,sha256=z4_DrvkGI0dzdT-3jsDtjzCh_0BYpalxPHIcM4xfHlU,1681
|
|
3
|
+
dboss/git_utils.py,sha256=6hq--3J28wR6boIAFS2lLE7RZ21ojJzSRrXYrOjOPjc,1086
|
|
4
|
+
dboss/ollama_client.py,sha256=cu17K92rDhjXDCXzCkkjcbVfa4cVSZahkTyT3terX_Y,1146
|
|
5
|
+
dboss/prompts.py,sha256=ob4PuYWpYQEDJWrZOH1g4ztJWxeVjOtFLxRvLU2gbn8,1001
|
|
6
|
+
dboss_cli-0.1.0.dist-info/METADATA,sha256=QdSBS-1WlXj9ZaMCl6bIdW775KOqzgbsn0lcHfeB_mQ,223
|
|
7
|
+
dboss_cli-0.1.0.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
|
|
8
|
+
dboss_cli-0.1.0.dist-info/entry_points.txt,sha256=hWTF230W7GXohliNjWiUF2AvNcLBYr_TWR6Ec3tzVeI,41
|
|
9
|
+
dboss_cli-0.1.0.dist-info/RECORD,,
|