Compare commits
14
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
726370ce43 | ||
|
|
e69de1d99a | ||
|
|
e5f22bca85 | ||
|
|
277f197a71 | ||
|
|
2edbf95ed5 | ||
|
|
109fcd584f | ||
|
|
61e51b5187 | ||
|
|
d4b694e77d | ||
|
|
261d04c56f | ||
|
|
f7001d4243 | ||
|
|
11a9db7986 | ||
|
|
34f5d090ce | ||
|
|
d20a31bf38 | ||
|
|
a43e09e924 |
No files matched your search
@@ -14,7 +14,7 @@ jobs:
|
|||||||
name: Lint & format
|
name: Lint & format
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v7
|
||||||
|
|
||||||
- name: Install uv
|
- name: Install uv
|
||||||
uses: astral-sh/setup-uv@v7
|
uses: astral-sh/setup-uv@v7
|
||||||
@@ -42,7 +42,7 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
python-version: ["3.10", "3.11", "3.12", "3.13"]
|
python-version: ["3.10", "3.11", "3.12", "3.13"]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v7
|
||||||
|
|
||||||
- name: Install uv
|
- name: Install uv
|
||||||
uses: astral-sh/setup-uv@v7
|
uses: astral-sh/setup-uv@v7
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ jobs:
|
|||||||
language: [python]
|
language: [python]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v7
|
||||||
|
|
||||||
- name: Install uv
|
- name: Install uv
|
||||||
uses: astral-sh/setup-uv@v7
|
uses: astral-sh/setup-uv@v7
|
||||||
|
|||||||
@@ -19,4 +19,5 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
target: minor
|
target: minor
|
||||||
|
use-github-auto-merge: true
|
||||||
merge-method: squash
|
merge-method: squash
|
||||||
@@ -13,13 +13,13 @@ jobs:
|
|||||||
name: Audit dependencies
|
name: Audit dependencies
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v7
|
||||||
|
|
||||||
- name: Dependency Review
|
- name: Dependency Review
|
||||||
uses: actions/dependency-review-action@v5
|
uses: actions/dependency-review-action@v5
|
||||||
with:
|
with:
|
||||||
fail-on-severity: high
|
fail-on-severity: high
|
||||||
fail-on-scopes: direct
|
fail-on-scopes: runtime
|
||||||
license-check: true
|
license-check: true
|
||||||
# Project is GPL-3.0-or-later; flag licenses that would taint it.
|
# Project is GPL-3.0-or-later; flag licenses that would taint it.
|
||||||
deny-licenses: AGPL-1.0-only, AGPL-1.0-or-later, AGPL-3.0-only, AGPL-3.0-or-later, SSPL-1.0, BUSL-1.1
|
deny-licenses: AGPL-1.0-only, AGPL-1.0-or-later, AGPL-3.0-only, AGPL-3.0-or-later, SSPL-1.0, BUSL-1.1
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ jobs:
|
|||||||
pre-commit:
|
pre-commit:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v7
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ jobs:
|
|||||||
permissions:
|
permissions:
|
||||||
id-token: write # IMPORTANT: this permission is mandatory for trusted publishing
|
id-token: write # IMPORTANT: this permission is mandatory for trusted publishing
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v7
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v6
|
uses: actions/setup-python@v6
|
||||||
with:
|
with:
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ from devgen.utils import (
|
|||||||
extract_commit_messages,
|
extract_commit_messages,
|
||||||
get_commit_dry_run_path,
|
get_commit_dry_run_path,
|
||||||
is_file_recent,
|
is_file_recent,
|
||||||
|
is_token_limit_error,
|
||||||
|
format_token_limit_error,
|
||||||
load_template,
|
load_template,
|
||||||
render_template,
|
render_template,
|
||||||
sanitize_ai_commit_message,
|
sanitize_ai_commit_message,
|
||||||
@@ -241,9 +243,10 @@ class CommitEngine:
|
|||||||
if not self._process_group(group, group_files, cache_map):
|
if not self._process_group(group, group_files, cache_map):
|
||||||
failed.append(group)
|
failed.append(group)
|
||||||
if len(groups) > 1 and i < len(groups) - 1:
|
if len(groups) > 1 and i < len(groups) - 1:
|
||||||
import time
|
if self.provider != "ollama":
|
||||||
|
import time
|
||||||
|
|
||||||
time.sleep(15) # Conservative 4 RPM guard for free-tier
|
time.sleep(15) # Conservative 4 RPM guard for free-tier
|
||||||
return failed
|
return failed
|
||||||
|
|
||||||
def _process_group(
|
def _process_group(
|
||||||
@@ -265,7 +268,7 @@ class CommitEngine:
|
|||||||
return self._apply_message(group, cache[group], files)
|
return self._apply_message(group, cache[group], files)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
manifest_context = ManifestInspector.summary()
|
manifest_context = ManifestInspector.summary(cwd=self.git.cwd)
|
||||||
if manifest_context:
|
if manifest_context:
|
||||||
self.logger.info("Including manifest context in prompt")
|
self.logger.info("Including manifest context in prompt")
|
||||||
with self.console.status(
|
with self.console.status(
|
||||||
@@ -279,10 +282,15 @@ class CommitEngine:
|
|||||||
)
|
)
|
||||||
message = sanitize_ai_commit_message(raw)
|
message = sanitize_ai_commit_message(raw)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.error(
|
if is_token_limit_error(e):
|
||||||
f"Failed to generate message for group {group!r}: {e}",
|
msg = format_token_limit_error(self.provider, e, group=group)
|
||||||
exc_info=True,
|
self.console.print(f"[bold red]{msg}[/bold red]")
|
||||||
)
|
self.logger.error(msg)
|
||||||
|
else:
|
||||||
|
self.logger.error(
|
||||||
|
f"Failed to generate message for group {group!r}: {e}",
|
||||||
|
exc_info=True,
|
||||||
|
)
|
||||||
self.git.reset(files)
|
self.git.reset(files)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|||||||
@@ -143,13 +143,14 @@ class ManifestInspector:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def summary() -> str:
|
def summary(cwd: Optional[Path] = None) -> str:
|
||||||
|
cwd = cwd or get_git_root() or Path(".")
|
||||||
lines = [
|
lines = [
|
||||||
s
|
s
|
||||||
for s in (
|
for s in (
|
||||||
ManifestInspector._pyproject(),
|
ManifestInspector._pyproject(cwd),
|
||||||
ManifestInspector._package_json(),
|
ManifestInspector._package_json(cwd),
|
||||||
ManifestInspector._requirements(),
|
ManifestInspector._requirements(cwd),
|
||||||
)
|
)
|
||||||
if s
|
if s
|
||||||
]
|
]
|
||||||
@@ -164,8 +165,8 @@ class ManifestInspector:
|
|||||||
return re.split(r"[><=!~\[]", spec, 1)[0].strip()
|
return re.split(r"[><=!~\[]", spec, 1)[0].strip()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _pyproject() -> Optional[str]:
|
def _pyproject(cwd: Path) -> Optional[str]:
|
||||||
path = Path("pyproject.toml")
|
path = cwd / "pyproject.toml"
|
||||||
if not path.exists():
|
if not path.exists():
|
||||||
return None
|
return None
|
||||||
try:
|
try:
|
||||||
@@ -186,8 +187,8 @@ class ManifestInspector:
|
|||||||
return f"[pyproject.toml] {' '.join(parts)}" if parts else None
|
return f"[pyproject.toml] {' '.join(parts)}" if parts else None
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _package_json() -> Optional[str]:
|
def _package_json(cwd: Path) -> Optional[str]:
|
||||||
path = Path("package.json")
|
path = cwd / "package.json"
|
||||||
if not path.exists():
|
if not path.exists():
|
||||||
return None
|
return None
|
||||||
try:
|
try:
|
||||||
@@ -203,8 +204,8 @@ class ManifestInspector:
|
|||||||
return f"[package.json] {' '.join(parts)}" if parts else None
|
return f"[package.json] {' '.join(parts)}" if parts else None
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _requirements() -> Optional[str]:
|
def _requirements(cwd: Path) -> Optional[str]:
|
||||||
path = Path("requirements.txt")
|
path = cwd / "requirements.txt"
|
||||||
if not path.exists():
|
if not path.exists():
|
||||||
return None
|
return None
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -91,15 +91,16 @@ class GitOperator:
|
|||||||
[
|
[
|
||||||
"git",
|
"git",
|
||||||
"ls-files",
|
"ls-files",
|
||||||
|
"-z",
|
||||||
"--deleted",
|
"--deleted",
|
||||||
"--modified",
|
"--modified",
|
||||||
"--others",
|
"--others",
|
||||||
"--exclude-standard",
|
"--exclude-standard",
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
files = {f.strip() for f in out.split("\n") if f.strip()}
|
files = {f for f in out.split("\0") if f}
|
||||||
staged_out = self._run(["git", "diff", "--name-only", "--cached"])
|
staged_out = self._run(["git", "diff", "-z", "--name-only", "--cached"])
|
||||||
files.update(f.strip() for f in staged_out.split("\n") if f.strip())
|
files.update(f for f in staged_out.split("\0") if f)
|
||||||
return sorted(files)
|
return sorted(files)
|
||||||
|
|
||||||
# ------------------------------------------------------------------ actions
|
# ------------------------------------------------------------------ actions
|
||||||
|
|||||||
+43
-20
@@ -30,33 +30,56 @@ class BaseProvider(ABC):
|
|||||||
model: str | None = None,
|
model: str | None = None,
|
||||||
**kwargs: Any,
|
**kwargs: Any,
|
||||||
) -> str:
|
) -> str:
|
||||||
"""Public entry point. Validates input, dispatches, and wraps errors.
|
"""Public entry point. Validates input, dispatches, and wraps errors."""
|
||||||
|
import time
|
||||||
|
|
||||||
Args:
|
|
||||||
prompt: The prompt to send to the model.
|
|
||||||
api_key: Provider API key. May be ``None`` for local providers
|
|
||||||
(see :attr:`REQUIRES_API_KEY`).
|
|
||||||
model: Model id. Falls back to :attr:`DEFAULT_MODEL` when ``None``.
|
|
||||||
**kwargs: Forwarded to :meth:`_generate`.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
The generated text, stripped.
|
|
||||||
|
|
||||||
Raises:
|
|
||||||
ValueError: If the provider requires an API key and none was given.
|
|
||||||
RuntimeError: For any underlying failure, with a friendly message.
|
|
||||||
"""
|
|
||||||
if self.REQUIRES_API_KEY and not api_key:
|
if self.REQUIRES_API_KEY and not api_key:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
f"{self.DISPLAY_NAME} API key is missing. "
|
f"{self.DISPLAY_NAME} API key is missing. "
|
||||||
"Set it via `devgen setup config` or pass --api-key."
|
"Set it via `devgen setup config` or pass --api-key."
|
||||||
)
|
)
|
||||||
chosen_model = model or self.DEFAULT_MODEL
|
chosen_model = model or self.DEFAULT_MODEL
|
||||||
try:
|
|
||||||
text = self._generate(prompt, api_key, chosen_model, **kwargs)
|
max_retries = kwargs.get("max_retries", 5)
|
||||||
return (text or "").strip()
|
base_delay = kwargs.get("retry_delay", 10)
|
||||||
except Exception as e:
|
|
||||||
self._handle_error(e)
|
for attempt in range(max_retries + 1):
|
||||||
|
try:
|
||||||
|
text = self._generate(prompt, api_key, chosen_model, **kwargs)
|
||||||
|
return (text or "").strip()
|
||||||
|
except Exception as e:
|
||||||
|
if is_token_limit_error(e):
|
||||||
|
self._handle_error(e)
|
||||||
|
if self._is_quota_error(e) and attempt < max_retries:
|
||||||
|
delay = base_delay * (2**attempt)
|
||||||
|
print(
|
||||||
|
f"\n[{self.DISPLAY_NAME}] Quota/Rate limit hit (429). "
|
||||||
|
f"Waiting {delay}s and retrying... "
|
||||||
|
f"(Attempt {attempt + 1}/{max_retries})"
|
||||||
|
)
|
||||||
|
time.sleep(delay)
|
||||||
|
continue
|
||||||
|
if self._is_quota_error(e) and attempt >= max_retries:
|
||||||
|
raise RuntimeError(
|
||||||
|
f"{self.DISPLAY_NAME} API quota exhausted (429) after multiple retries."
|
||||||
|
) from e
|
||||||
|
self._handle_error(e)
|
||||||
|
return ""
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _is_quota_error(error: Exception) -> bool:
|
||||||
|
text = str(error).upper()
|
||||||
|
return any(
|
||||||
|
marker in text
|
||||||
|
for marker in (
|
||||||
|
"429",
|
||||||
|
"RESOURCE_EXHAUSTED",
|
||||||
|
"QUOTA_EXCEEDED",
|
||||||
|
"THROTTLED",
|
||||||
|
"RATE_LIMIT_EXCEEDED",
|
||||||
|
"TOO_MANY_REQUESTS",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
def _handle_error(self, error: Exception) -> None:
|
def _handle_error(self, error: Exception) -> None:
|
||||||
"""Translate an internal exception into a user-facing RuntimeError.
|
"""Translate an internal exception into a user-facing RuntimeError.
|
||||||
|
|||||||
+10
-48
@@ -1,10 +1,7 @@
|
|||||||
import time
|
|
||||||
|
|
||||||
from google import genai
|
from google import genai
|
||||||
from google.genai import types
|
from google.genai import types
|
||||||
|
|
||||||
from devgen.providers.base import BaseProvider
|
from devgen.providers.base import BaseProvider
|
||||||
from devgen.utils import is_token_limit_error
|
|
||||||
|
|
||||||
|
|
||||||
class GeminiProvider(BaseProvider):
|
class GeminiProvider(BaseProvider):
|
||||||
@@ -14,50 +11,15 @@ class GeminiProvider(BaseProvider):
|
|||||||
DEFAULT_MODEL = "gemini-2.5-flash"
|
DEFAULT_MODEL = "gemini-2.5-flash"
|
||||||
|
|
||||||
def _generate(self, prompt, api_key, model, **kwargs):
|
def _generate(self, prompt, api_key, model, **kwargs):
|
||||||
max_retries = kwargs.get("max_retries", 5)
|
|
||||||
base_delay = kwargs.get("retry_delay", 10) # 10s base for 429 backoff
|
|
||||||
client = genai.Client(api_key=api_key)
|
client = genai.Client(api_key=api_key)
|
||||||
|
response = client.models.generate_content(
|
||||||
for attempt in range(max_retries + 1):
|
model=model,
|
||||||
try:
|
contents=prompt,
|
||||||
response = client.models.generate_content(
|
config=types.GenerateContentConfig(
|
||||||
model=model,
|
temperature=kwargs.get("temperature", 0.7),
|
||||||
contents=prompt,
|
top_p=kwargs.get("top_p", 0.95),
|
||||||
config=types.GenerateContentConfig(
|
top_k=kwargs.get("top_k", 40),
|
||||||
temperature=kwargs.get("temperature", 0.7),
|
max_output_tokens=kwargs.get("max_output_tokens", 2048),
|
||||||
top_p=kwargs.get("top_p", 0.95),
|
),
|
||||||
top_k=kwargs.get("top_k", 40),
|
|
||||||
max_output_tokens=kwargs.get("max_output_tokens", 2048),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
return response.text or ""
|
|
||||||
except Exception as e:
|
|
||||||
if is_token_limit_error(e):
|
|
||||||
raise # base class handles with friendly message
|
|
||||||
if self._is_quota_error(e) and attempt < max_retries:
|
|
||||||
delay = base_delay * (2**attempt)
|
|
||||||
print(
|
|
||||||
f"\n[Gemini] Quota limit hit (429). This is often a temporary "
|
|
||||||
f"rate limit (RPM). Waiting {delay}s and retrying... "
|
|
||||||
f"(Attempt {attempt + 1}/{max_retries})"
|
|
||||||
)
|
|
||||||
time.sleep(delay)
|
|
||||||
continue
|
|
||||||
if self._is_quota_error(e):
|
|
||||||
raise RuntimeError(
|
|
||||||
"Gemini API quota exhausted (429) after multiple retries.\n\n"
|
|
||||||
"POSSIBLE CAUSES:\n"
|
|
||||||
"1. Rate Limit (RPM/RPD): You are sending requests too fast for the Free Tier.\n"
|
|
||||||
"2. Daily Limit: You have reached the maximum free requests allowed for today (typically 1,500).\n"
|
|
||||||
"3. Credit/Billing: If on a paid plan, your credit may be exhausted.\n\n"
|
|
||||||
"Please check your usage metrics at: https://aistudio.google.com/app/usage"
|
|
||||||
) from e
|
|
||||||
raise
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _is_quota_error(error: Exception) -> bool:
|
|
||||||
text = str(error).upper()
|
|
||||||
return any(
|
|
||||||
marker in text
|
|
||||||
for marker in ("429", "RESOURCE_EXHAUSTED", "QUOTA_EXCEEDED", "THROTTLED")
|
|
||||||
)
|
)
|
||||||
|
return response.text or ""
|
||||||
+21
-6
@@ -1,14 +1,29 @@
|
|||||||
You write Conventional Commits. Output ONLY the message — no preamble, no labels, no quotes, no markdown.
|
You write Conventional Commits. Output ONLY the message — no preamble, no labels, no quotes, no markdown.
|
||||||
|
|
||||||
Format:
|
Format:
|
||||||
<type>(<scope>): <summary>
|
<type>[optional scope][optional !]: <description>
|
||||||
[optional 1-2 line body on WHY]
|
[optional blank line]
|
||||||
|
[optional body]
|
||||||
|
[optional blank line]
|
||||||
|
[optional footer(s)]
|
||||||
|
|
||||||
types: feat fix refactor perf docs test build ci chore style
|
types: feat fix refactor perf docs test build ci chore style
|
||||||
scope: {{ group_name }}{% if use_emoji %} (prefix summary with one matching emoji){% endif %}
|
scope: {% if group_name and group_name != 'root' %}({{ group_name }}){% else %}optional{% endif %}{% if use_emoji %} (prefix description with one matching emoji){% endif %}
|
||||||
|
|
||||||
Example:
|
Examples:
|
||||||
feat(auth): add login rate limit
|
feat: add email notifications on new direct messages
|
||||||
Prevents brute-force attacks.
|
feat(shopping cart): add the amazing button
|
||||||
|
feat!: remove ticket list endpoint
|
||||||
|
refers to JIRA-1337
|
||||||
|
BREAKING CHANGE: ticket endpoints no longer supports list all entities.
|
||||||
|
fix(shopping-cart): prevent order an empty shopping cart
|
||||||
|
fix(api): fix wrong calculation of request body checksum
|
||||||
|
fix: add missing parameter to service call
|
||||||
|
The error occurred due to <reasons>.
|
||||||
|
perf: decrease memory footprint for determine unique visitors by using HyperLogLog
|
||||||
|
build: update dependencies
|
||||||
|
build(release): bump version to 1.0.0
|
||||||
|
refactor: implement fibonacci number calculation as recursion
|
||||||
|
style: remove empty line
|
||||||
|
|
||||||
{{ diff_text }}{{ context }}
|
{{ diff_text }}{{ context }}
|
||||||
+5
-2
@@ -46,7 +46,7 @@ def sanitize_ai_commit_message(raw_text: str) -> str:
|
|||||||
# - **fix: bug fix**
|
# - **fix: bug fix**
|
||||||
# - chore(deps)!: breaking change
|
# - chore(deps)!: breaking change
|
||||||
header_pattern = re.compile(
|
header_pattern = re.compile(
|
||||||
r"^(\*\*)?(feat|fix|chore|refactor|docs|style|test|build|ci)(\(.*\))?!?: .*",
|
r"^(\*\*)?(feat|fix|chore|refactor|docs|style|test|build|ci|perf|revert|deps|wip)(\(.*\))?!?: .*",
|
||||||
re.IGNORECASE,
|
re.IGNORECASE,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -75,6 +75,9 @@ def sanitize_ai_commit_message(raw_text: str) -> str:
|
|||||||
# If we hit another header or a known separator, we stop
|
# If we hit another header or a known separator, we stop
|
||||||
if "**Sponsor**" in line:
|
if "**Sponsor**" in line:
|
||||||
break
|
break
|
||||||
|
# Ignore trailing markdown code block markers
|
||||||
|
if line.strip() in ("```", "```md", "```markdown"):
|
||||||
|
continue
|
||||||
cleaned_lines.append(line)
|
cleaned_lines.append(line)
|
||||||
|
|
||||||
if cleaned_lines:
|
if cleaned_lines:
|
||||||
@@ -196,7 +199,7 @@ def get_git_staged_files() -> list[str]:
|
|||||||
try:
|
try:
|
||||||
output = run_git_command(["git", "diff", "--name-only", "--cached"])
|
output = run_git_command(["git", "diff", "--name-only", "--cached"])
|
||||||
return [f for f in output.splitlines() if f.strip()]
|
return [f for f in output.splitlines() if f.strip()]
|
||||||
except subprocess.CalledProcessError:
|
except (subprocess.CalledProcessError, FileNotFoundError):
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+9
-9
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "devgen-cli"
|
name = "devgen-cli"
|
||||||
version = "0.2.9"
|
version = "0.3.0"
|
||||||
description = "A collection of developer tools"
|
description = "A collection of developer tools"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
requires-python = ">=3.10"
|
requires-python = ">=3.10"
|
||||||
@@ -25,15 +25,15 @@ classifiers = [
|
|||||||
"Programming Language :: Python :: 3.10",
|
"Programming Language :: Python :: 3.10",
|
||||||
]
|
]
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anthropic>=0.75.0",
|
"anthropic>=0.116.0",
|
||||||
"google-genai>=0.1.0",
|
"google-genai>=2.10.0",
|
||||||
"openai>=2.11.0",
|
"openai>=2.44.0",
|
||||||
"pyyaml>=6.0.3",
|
"pyyaml>=6.0.3",
|
||||||
"questionary>=2.1.1",
|
"questionary>=2.1.1",
|
||||||
"requests>=2.32.5",
|
"requests>=2.34.2",
|
||||||
"rich>=14.2.0",
|
"rich>=15.0.0",
|
||||||
"toml>=0.10.2",
|
"toml>=0.10.2",
|
||||||
"typer>=0.20.0",
|
"typer>=0.26.8",
|
||||||
]
|
]
|
||||||
[project.urls]
|
[project.urls]
|
||||||
Homepage = "https://github.com/S4NKALP/devgen"
|
Homepage = "https://github.com/S4NKALP/devgen"
|
||||||
@@ -49,6 +49,6 @@ include = ["devgen*"]
|
|||||||
|
|
||||||
[dependency-groups]
|
[dependency-groups]
|
||||||
dev = [
|
dev = [
|
||||||
"pre-commit>=4.5.0",
|
"pre-commit>=4.6.0",
|
||||||
"ruff>=0.14.9",
|
"ruff>=0.15.20",
|
||||||
]
|
]
|
||||||
+8
-8
@@ -1,11 +1,11 @@
|
|||||||
anthropic==0.76.0
|
anthropic==0.116.0
|
||||||
google-genai==1.60.0
|
google-genai==2.10.0
|
||||||
openai==2.15.0
|
openai==2.44.0
|
||||||
pre-commit==4.5.1
|
pre-commit==4.6.2
|
||||||
pyyaml==6.0.3
|
pyyaml==6.0.3
|
||||||
questionary==2.1.1
|
questionary==2.1.1
|
||||||
requests==2.32.5
|
requests==2.34.2
|
||||||
rich==14.2.0
|
rich==15.0.0
|
||||||
ruff==0.14.13
|
ruff==0.15.20
|
||||||
toml==0.10.2
|
toml==0.10.2
|
||||||
typer==0.21.1
|
typer==0.26.8
|
||||||
@@ -6,7 +6,7 @@ from setuptools import find_packages, setup
|
|||||||
|
|
||||||
setup(
|
setup(
|
||||||
name="devgen-cli",
|
name="devgen-cli",
|
||||||
version="0.2.9",
|
version="0.3.0",
|
||||||
packages=find_packages(),
|
packages=find_packages(),
|
||||||
include_package_data=True,
|
include_package_data=True,
|
||||||
install_requires=[
|
install_requires=[
|
||||||
|
|||||||
Reference in new issue
Block a user