gitai-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.
- gitai/__init__.py +1 -0
- gitai/ai.py +52 -0
- gitai/cli.py +132 -0
- gitai/config.py +27 -0
- gitai/git.py +24 -0
- gitai/prompt.py +73 -0
- gitai_cli-0.1.0.dist-info/METADATA +139 -0
- gitai_cli-0.1.0.dist-info/RECORD +11 -0
- gitai_cli-0.1.0.dist-info/WHEEL +4 -0
- gitai_cli-0.1.0.dist-info/entry_points.txt +2 -0
- gitai_cli-0.1.0.dist-info/licenses/LICENSE +21 -0
gitai/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.0"
|
gitai/ai.py
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import litellm
|
|
2
|
+
from litellm.exceptions import APIConnectionError, AuthenticationError
|
|
3
|
+
from gitai.config import load_config
|
|
4
|
+
|
|
5
|
+
def get_commit_suggestions(prompt: str) -> list[str]:
|
|
6
|
+
config = load_config()
|
|
7
|
+
|
|
8
|
+
provider = config["provider"].lower()
|
|
9
|
+
model = config["model"]
|
|
10
|
+
model_string = f"{provider}/{model}"
|
|
11
|
+
|
|
12
|
+
kwargs = {
|
|
13
|
+
"model": model_string,
|
|
14
|
+
"messages": [{"role": "user", "content": prompt}],
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
if provider == "ollama":
|
|
18
|
+
kwargs["api_base"] = config["ollama_url"]
|
|
19
|
+
|
|
20
|
+
try:
|
|
21
|
+
response = litellm.completion(**kwargs)
|
|
22
|
+
except APIConnectionError:
|
|
23
|
+
if provider == "ollama":
|
|
24
|
+
raise SystemExit(
|
|
25
|
+
f"[gitai] Could not connect to Ollama at {config['ollama_url']}. "
|
|
26
|
+
"Is Ollama running? Try: ollama serve"
|
|
27
|
+
)
|
|
28
|
+
raise SystemExit(
|
|
29
|
+
f"[gitai] Could not connect to the {provider} API. "
|
|
30
|
+
"Check your network connection and try again."
|
|
31
|
+
)
|
|
32
|
+
except AuthenticationError:
|
|
33
|
+
key_map = {
|
|
34
|
+
"openai": "OPENAI_API_KEY",
|
|
35
|
+
"anthropic": "ANTHROPIC_API_KEY",
|
|
36
|
+
"gemini": "GEMINI_API_KEY",
|
|
37
|
+
}
|
|
38
|
+
env_var = key_map.get(provider, f"{provider.upper()}_API_KEY")
|
|
39
|
+
raise SystemExit(
|
|
40
|
+
f"[gitai] Authentication failed for {provider}. "
|
|
41
|
+
f"Set the {env_var} environment variable and try again."
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
text = response.choices[0].message.content.strip()
|
|
45
|
+
|
|
46
|
+
suggestions = []
|
|
47
|
+
for line in text.splitlines():
|
|
48
|
+
line = line.strip()
|
|
49
|
+
if line and line[0].isdigit() and "." in line:
|
|
50
|
+
suggestions.append(line)
|
|
51
|
+
|
|
52
|
+
return suggestions
|
gitai/cli.py
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
import typer
|
|
3
|
+
import questionary
|
|
4
|
+
import subprocess
|
|
5
|
+
from gitai.config import load_config, save_config
|
|
6
|
+
from gitai.git import get_staged_diff, get_repo_name, is_diff_meaningful
|
|
7
|
+
from gitai.prompt import build_commit_prompt
|
|
8
|
+
from gitai.ai import get_commit_suggestions
|
|
9
|
+
from gitai import __version__
|
|
10
|
+
|
|
11
|
+
VALID_PROVIDERS = {"ollama", "openai", "anthropic", "gemini"}
|
|
12
|
+
VALID_COMMIT_STYLES = {"conventional", "free-form"}
|
|
13
|
+
|
|
14
|
+
app = typer.Typer()
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _version_callback(value: bool):
|
|
18
|
+
if value:
|
|
19
|
+
typer.echo(f"gitai {__version__}")
|
|
20
|
+
raise typer.Exit()
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@app.callback()
|
|
24
|
+
def main(
|
|
25
|
+
version: Optional[bool] = typer.Option(
|
|
26
|
+
None, "--version", "-V",
|
|
27
|
+
callback=_version_callback,
|
|
28
|
+
is_eager=True,
|
|
29
|
+
help="Show version and exit.",
|
|
30
|
+
),
|
|
31
|
+
):
|
|
32
|
+
"""AI-powered git commit message generator."""
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@app.command()
|
|
36
|
+
def commit():
|
|
37
|
+
"""Generate AI-powered commit message suggestions for staged changes."""
|
|
38
|
+
typer.echo("🔍 Reading your git diff...")
|
|
39
|
+
|
|
40
|
+
diff = get_staged_diff()
|
|
41
|
+
if not diff:
|
|
42
|
+
typer.echo("No staged changes found. Please stage your changes before committing.")
|
|
43
|
+
raise typer.Exit(code=1)
|
|
44
|
+
|
|
45
|
+
if not is_diff_meaningful(diff):
|
|
46
|
+
typer.echo("Staged file appears to be empty or has no content changes.")
|
|
47
|
+
chosen = typer.prompt("Enter your commit message")
|
|
48
|
+
subprocess.run(["git", "commit", "-m", chosen])
|
|
49
|
+
typer.echo(f"\n Committed: {chosen}")
|
|
50
|
+
raise typer.Exit()
|
|
51
|
+
|
|
52
|
+
config = load_config()
|
|
53
|
+
repo_name = get_repo_name()
|
|
54
|
+
prompt = build_commit_prompt(diff, repo_name, emoji=config["emoji"], commit_style=config["commit_style"])
|
|
55
|
+
|
|
56
|
+
typer.echo("Generating commit message suggestions...")
|
|
57
|
+
suggestions = get_commit_suggestions(prompt)
|
|
58
|
+
|
|
59
|
+
if not suggestions:
|
|
60
|
+
typer.echo("No suggestions generated. Please try again.")
|
|
61
|
+
raise typer.Exit()
|
|
62
|
+
|
|
63
|
+
clean = [s.split(". ", 1)[1] if ". " in s else s for s in suggestions]
|
|
64
|
+
clean.append("Write my own")
|
|
65
|
+
|
|
66
|
+
chosen = questionary.select(
|
|
67
|
+
"Choose a commit message:",
|
|
68
|
+
choices=clean
|
|
69
|
+
).ask()
|
|
70
|
+
|
|
71
|
+
if chosen is None:
|
|
72
|
+
typer.echo("Aborted.")
|
|
73
|
+
raise typer.Exit()
|
|
74
|
+
|
|
75
|
+
if chosen == "Write my own":
|
|
76
|
+
chosen = typer.prompt("Enter your custom commit message")
|
|
77
|
+
|
|
78
|
+
subprocess.run(["git", "commit", "-m", chosen])
|
|
79
|
+
typer.echo(f"\n Committed: {chosen}")
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
@app.command()
|
|
83
|
+
def config():
|
|
84
|
+
"""View and update gitai settings."""
|
|
85
|
+
current = load_config()
|
|
86
|
+
|
|
87
|
+
typer.echo("Current configuration:\n")
|
|
88
|
+
for key, value in current.items():
|
|
89
|
+
typer.echo(f" {key}: {value}")
|
|
90
|
+
|
|
91
|
+
typer.echo("")
|
|
92
|
+
if not typer.confirm("Do you want to change any settings?"):
|
|
93
|
+
raise typer.Exit()
|
|
94
|
+
|
|
95
|
+
provider = typer.prompt(
|
|
96
|
+
"Provider (ollama, openai, anthropic, gemini)",
|
|
97
|
+
default=current["provider"],
|
|
98
|
+
)
|
|
99
|
+
if provider not in VALID_PROVIDERS:
|
|
100
|
+
typer.echo(f"[gitai] Unknown provider '{provider}'. Choose from: {', '.join(sorted(VALID_PROVIDERS))}")
|
|
101
|
+
raise typer.Exit(code=1)
|
|
102
|
+
|
|
103
|
+
model = typer.prompt("Model name", default=current["model"])
|
|
104
|
+
|
|
105
|
+
ollama_url = current["ollama_url"]
|
|
106
|
+
if provider == "ollama":
|
|
107
|
+
ollama_url = typer.prompt("Ollama URL", default=current["ollama_url"])
|
|
108
|
+
|
|
109
|
+
commit_style = typer.prompt(
|
|
110
|
+
"Commit style (conventional, free-form)",
|
|
111
|
+
default=current["commit_style"],
|
|
112
|
+
)
|
|
113
|
+
if commit_style not in VALID_COMMIT_STYLES:
|
|
114
|
+
typer.echo(f"[gitai] Unknown commit style '{commit_style}'. Choose from: {', '.join(sorted(VALID_COMMIT_STYLES))}")
|
|
115
|
+
raise typer.Exit(code=1)
|
|
116
|
+
|
|
117
|
+
emoji = typer.confirm("Use emojis in commit messages?", default=current["emoji"])
|
|
118
|
+
|
|
119
|
+
new_config = {
|
|
120
|
+
"model": model,
|
|
121
|
+
"provider": provider,
|
|
122
|
+
"ollama_url": ollama_url,
|
|
123
|
+
"commit_style": commit_style,
|
|
124
|
+
"emoji": emoji,
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
save_config(new_config)
|
|
128
|
+
typer.echo("\n✅ Config saved to ~/.gitai.toml")
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
if __name__ == "__main__":
|
|
132
|
+
app()
|
gitai/config.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import tomllib
|
|
2
|
+
import tomli_w
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
CONFIG_PATH = Path.home() / ".gitai.toml"
|
|
6
|
+
|
|
7
|
+
DEFAULT_CONFIG = {
|
|
8
|
+
"model": "llama3.2",
|
|
9
|
+
"provider": "ollama",
|
|
10
|
+
"ollama_url": "http://localhost:11434",
|
|
11
|
+
"commit_style": "conventional",
|
|
12
|
+
"emoji": False,
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
def load_config() -> dict:
|
|
16
|
+
if not CONFIG_PATH.exists():
|
|
17
|
+
save_config(DEFAULT_CONFIG)
|
|
18
|
+
return DEFAULT_CONFIG.copy()
|
|
19
|
+
|
|
20
|
+
with open(CONFIG_PATH, "rb") as f:
|
|
21
|
+
user_config = tomllib.load(f)
|
|
22
|
+
|
|
23
|
+
return {**DEFAULT_CONFIG, **user_config}
|
|
24
|
+
|
|
25
|
+
def save_config(config: dict) -> None:
|
|
26
|
+
with open(CONFIG_PATH, "wb") as f:
|
|
27
|
+
tomli_w.dump(config, f)
|
gitai/git.py
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import subprocess
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
def get_staged_diff() -> str:
|
|
5
|
+
result = subprocess.run(
|
|
6
|
+
["git", "diff", "--cached"],
|
|
7
|
+
capture_output=True,
|
|
8
|
+
text=True,
|
|
9
|
+
encoding="utf-8",
|
|
10
|
+
)
|
|
11
|
+
return result.stdout
|
|
12
|
+
|
|
13
|
+
def get_repo_name() -> str:
|
|
14
|
+
path = Path.cwd()
|
|
15
|
+
return path.name
|
|
16
|
+
|
|
17
|
+
def is_diff_meaningful(diff: str) -> bool:
|
|
18
|
+
meaningful_lines = [
|
|
19
|
+
line for line in diff.splitlines()
|
|
20
|
+
if line.startswith(("+", "-"))
|
|
21
|
+
and not line.startswith(("+++", "---"))
|
|
22
|
+
and line.strip() not in ("+", "-", "")
|
|
23
|
+
]
|
|
24
|
+
return len(meaningful_lines) > 0
|
gitai/prompt.py
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
def build_commit_prompt(diff: str, repo_name: str, emoji: bool = False, commit_style: str = "conventional") -> str:
|
|
2
|
+
format_rules = _build_format_rules(commit_style, emoji)
|
|
3
|
+
example = _build_example(commit_style, emoji)
|
|
4
|
+
|
|
5
|
+
return f"""You are an expert developer generating git commit messages.
|
|
6
|
+
|
|
7
|
+
Repository: {repo_name}
|
|
8
|
+
|
|
9
|
+
Analyze the git diff below and return exactly 3 commit message suggestions.
|
|
10
|
+
|
|
11
|
+
Rules:
|
|
12
|
+
{format_rules}
|
|
13
|
+
- Each suggestion must offer a meaningfully different angle — vary the type, scope, or emphasis
|
|
14
|
+
- Base suggestions strictly on what you see in the diff — never invent context
|
|
15
|
+
- Output: a numbered list of exactly 3 lines, no intro, no explanation, nothing else
|
|
16
|
+
|
|
17
|
+
Example output:
|
|
18
|
+
{example}
|
|
19
|
+
|
|
20
|
+
Git diff:
|
|
21
|
+
{diff}
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _build_format_rules(commit_style: str, emoji: bool) -> str:
|
|
26
|
+
if commit_style == "free-form":
|
|
27
|
+
rules = (
|
|
28
|
+
"- Format: a single clear imperative sentence, no type prefix required\n"
|
|
29
|
+
"- Keep the message under 72 characters\n"
|
|
30
|
+
'- Imperative mood ("add" not "added"), lowercase, no trailing period'
|
|
31
|
+
)
|
|
32
|
+
if emoji:
|
|
33
|
+
rules += "\n- Prefix each message with a relevant emoji that reflects the nature of the change"
|
|
34
|
+
else: # conventional (default)
|
|
35
|
+
rules = (
|
|
36
|
+
"- Format: type(scope): description\n"
|
|
37
|
+
"- Types: feat, fix, refactor, chore, docs, style, test, perf, ci, build\n"
|
|
38
|
+
"- Scope: infer from the file paths or module names changed; omit if unclear\n"
|
|
39
|
+
'- Description: imperative mood ("add" not "added"), lowercase, no trailing period, under 72 characters'
|
|
40
|
+
)
|
|
41
|
+
if emoji:
|
|
42
|
+
rules += (
|
|
43
|
+
"\n- Prefix each message with the matching gitmoji before the type: "
|
|
44
|
+
"✨ feat, 🐛 fix, ♻️ refactor, 🔧 chore, 📝 docs, 🎨 style, ✅ test, ⚡ perf, 👷 ci, 📦 build"
|
|
45
|
+
)
|
|
46
|
+
return rules
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _build_example(commit_style: str, emoji: bool) -> str:
|
|
50
|
+
if commit_style == "free-form":
|
|
51
|
+
if emoji:
|
|
52
|
+
return (
|
|
53
|
+
"1. ✨ add refresh token rotation on session expiry\n"
|
|
54
|
+
"2. 🐛 prevent reuse of invalidated tokens\n"
|
|
55
|
+
"3. ♻️ extract token validation into dedicated service"
|
|
56
|
+
)
|
|
57
|
+
return (
|
|
58
|
+
"1. add refresh token rotation on session expiry\n"
|
|
59
|
+
"2. prevent reuse of invalidated tokens\n"
|
|
60
|
+
"3. extract token validation into dedicated service"
|
|
61
|
+
)
|
|
62
|
+
else: # conventional
|
|
63
|
+
if emoji:
|
|
64
|
+
return (
|
|
65
|
+
"1. ✨ feat(auth): add refresh token rotation on expiry\n"
|
|
66
|
+
"2. 🐛 fix(auth): prevent reuse of invalidated tokens\n"
|
|
67
|
+
"3. ♻️ refactor(auth): extract token validation into dedicated service"
|
|
68
|
+
)
|
|
69
|
+
return (
|
|
70
|
+
"1. feat(auth): add refresh token rotation on expiry\n"
|
|
71
|
+
"2. fix(auth): prevent reuse of invalidated tokens\n"
|
|
72
|
+
"3. refactor(auth): extract token validation into dedicated service"
|
|
73
|
+
)
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: gitai-cli
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: AI-powered git commit message generator
|
|
5
|
+
Project-URL: Homepage, https://github.com/Jeranguz/gitai
|
|
6
|
+
Project-URL: Repository, https://github.com/Jeranguz/gitai
|
|
7
|
+
Project-URL: Issues, https://github.com/Jeranguz/gitai/issues
|
|
8
|
+
Author: Jeranguz
|
|
9
|
+
License: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: ai,cli,commit,conventional-commits,git,llm
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Topic :: Software Development :: Version Control :: Git
|
|
21
|
+
Classifier: Topic :: Utilities
|
|
22
|
+
Requires-Python: >=3.11
|
|
23
|
+
Requires-Dist: gitpython
|
|
24
|
+
Requires-Dist: litellm
|
|
25
|
+
Requires-Dist: questionary
|
|
26
|
+
Requires-Dist: rich
|
|
27
|
+
Requires-Dist: tomli-w
|
|
28
|
+
Requires-Dist: typer
|
|
29
|
+
Provides-Extra: dev
|
|
30
|
+
Requires-Dist: pytest; extra == 'dev'
|
|
31
|
+
Description-Content-Type: text/markdown
|
|
32
|
+
|
|
33
|
+
# gitai
|
|
34
|
+
|
|
35
|
+
<p align="center">
|
|
36
|
+
<img src="assets/commit-genie.png" alt="The Commit Genie" width="200"/>
|
|
37
|
+
</p>
|
|
38
|
+
|
|
39
|
+
<p align="center">
|
|
40
|
+
<a href="https://pypi.org/project/gitai"><img src="https://img.shields.io/pypi/v/gitai" alt="PyPI version"/></a>
|
|
41
|
+
<a href="https://pypi.org/project/gitai"><img src="https://img.shields.io/pypi/pyversions/gitai" alt="Python versions"/></a>
|
|
42
|
+
<a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue" alt="MIT license"/></a>
|
|
43
|
+
</p>
|
|
44
|
+
|
|
45
|
+
AI-powered git commit message generator. Analyzes your staged changes and suggests meaningful commit messages — using any LLM you already have access to.
|
|
46
|
+
|
|
47
|
+
## Features
|
|
48
|
+
|
|
49
|
+
- Reads your staged `git diff` and generates 3 commit message suggestions
|
|
50
|
+
- Interactive selection: pick a suggestion or write your own
|
|
51
|
+
- Supports multiple providers: Ollama (local), OpenAI, Anthropic, Gemini, and [more](https://docs.litellm.ai/docs/providers)
|
|
52
|
+
- Two commit styles: [Conventional Commits](https://www.conventionalcommits.org/) or free-form
|
|
53
|
+
- Optional emoji (gitmoji) support
|
|
54
|
+
|
|
55
|
+
## Installation
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
pip install gitai
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Requires Python 3.11+.
|
|
62
|
+
|
|
63
|
+
## Quick start
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
# 1. Stage your changes
|
|
67
|
+
git add .
|
|
68
|
+
|
|
69
|
+
# 2. Run gitai
|
|
70
|
+
gitai commit
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
gitai reads the diff, calls your configured LLM, and presents 3 suggestions to choose from.
|
|
74
|
+
|
|
75
|
+
## Usage
|
|
76
|
+
|
|
77
|
+
```
|
|
78
|
+
gitai commit Generate commit message suggestions for staged changes
|
|
79
|
+
gitai config View and update settings
|
|
80
|
+
gitai --version Show version
|
|
81
|
+
gitai --help Show help
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Configuration
|
|
85
|
+
|
|
86
|
+
Run `gitai config` to update settings interactively. Settings are stored in `~/.gitai.toml`.
|
|
87
|
+
|
|
88
|
+
| Key | Default | Description |
|
|
89
|
+
|---|---|---|
|
|
90
|
+
| `provider` | `ollama` | LLM provider |
|
|
91
|
+
| `model` | `llama3.2` | Model name |
|
|
92
|
+
| `ollama_url` | `http://localhost:11434` | Ollama API base URL (Ollama only) |
|
|
93
|
+
| `commit_style` | `conventional` | `conventional` or `free-form` |
|
|
94
|
+
| `emoji` | `false` | Prefix suggestions with gitmoji |
|
|
95
|
+
|
|
96
|
+
### Supported providers
|
|
97
|
+
|
|
98
|
+
| Provider | `provider` value | Example `model` value | API key env var |
|
|
99
|
+
|---|---|---|---|
|
|
100
|
+
| Ollama (local) | `ollama` | `llama3.2`, `mistral` | — |
|
|
101
|
+
| Anthropic | `anthropic` | `claude-sonnet-4-6`, `claude-haiku-4-5-20251001` | `ANTHROPIC_API_KEY` |
|
|
102
|
+
| OpenAI | `openai` | `gpt-4o`, `gpt-4o-mini` | `OPENAI_API_KEY` |
|
|
103
|
+
| Gemini | `gemini` | `gemini-2.0-flash` | `GEMINI_API_KEY` |
|
|
104
|
+
|
|
105
|
+
For cloud providers, set the API key in your shell profile:
|
|
106
|
+
|
|
107
|
+
**bash/zsh** (`~/.bashrc` or `~/.zshrc`):
|
|
108
|
+
```bash
|
|
109
|
+
export ANTHROPIC_API_KEY=sk-ant-...
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
**PowerShell** (`$PROFILE`):
|
|
113
|
+
```powershell
|
|
114
|
+
$env:ANTHROPIC_API_KEY="sk-ant-..."
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Example `~/.gitai.toml`
|
|
118
|
+
|
|
119
|
+
```toml
|
|
120
|
+
provider = "anthropic"
|
|
121
|
+
model = "claude-haiku-4-5-20251001"
|
|
122
|
+
commit_style = "conventional"
|
|
123
|
+
emoji = false
|
|
124
|
+
ollama_url = "http://localhost:11434"
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Local setup (Ollama)
|
|
128
|
+
|
|
129
|
+
If you want to run fully offline with Ollama:
|
|
130
|
+
|
|
131
|
+
1. Install [Ollama](https://ollama.com/)
|
|
132
|
+
2. Pull a model: `ollama pull llama3.2`
|
|
133
|
+
3. Run `gitai commit` — no API key needed
|
|
134
|
+
|
|
135
|
+
## TODO
|
|
136
|
+
|
|
137
|
+
- [ ] Allow configuring the number of suggestions generated
|
|
138
|
+
- [ ] Add `--push` flag to commit and push in one step
|
|
139
|
+
- [ ] Support unstaged changes with an optional `--all` flag
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
gitai/__init__.py,sha256=kUR5RAFc7HCeiqdlX36dZOHkUI5wI6V_43RpEcD8b-0,22
|
|
2
|
+
gitai/ai.py,sha256=wx_yYA865VsQB8SsrzkEKd1hLF7SmdsWf1j-cbWeemU,1656
|
|
3
|
+
gitai/cli.py,sha256=pyiJS7jm9IRgeYdsJX4-v77zramPo_BXF9hghchmLi4,3949
|
|
4
|
+
gitai/config.py,sha256=LqmaFN9JBpHL0MGbn476sHBGUgfhfmEeQYD-7J60yxE,669
|
|
5
|
+
gitai/git.py,sha256=iDv09764q21uRBVJk784-qo9Gl3Nrpk0rpW31ha9_p0,628
|
|
6
|
+
gitai/prompt.py,sha256=Hj61wFDOrDztmnQxLNWagjCJ6OXfqWreS93z0KfTE_o,3057
|
|
7
|
+
gitai_cli-0.1.0.dist-info/METADATA,sha256=HS5B9TjSQ8OTbx_Yfbn6BXM16ew3H7KQPadtmtJB-Go,4257
|
|
8
|
+
gitai_cli-0.1.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
9
|
+
gitai_cli-0.1.0.dist-info/entry_points.txt,sha256=Cv684a3aG7n1URVEr6skYx27B37CJoev2fZx2irdUiw,40
|
|
10
|
+
gitai_cli-0.1.0.dist-info/licenses/LICENSE,sha256=RjOVI-Vu4fAe5qNurmnVBMVROuK_xPePzpCSEEEtKq4,1065
|
|
11
|
+
gitai_cli-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Jeranguz
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|