Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
70e9068082 |
No files matched your search
+3
-3
@@ -85,14 +85,14 @@ def setup_config() -> None:
|
||||
raise typer.Exit(code=130)
|
||||
ollama_host = ollama_host_input.strip() or ollama_host
|
||||
|
||||
# Save Config
|
||||
new_config = {
|
||||
# Save Config — merge with existing to preserve extra keys
|
||||
new_config = {**current_config, **{
|
||||
"provider": provider,
|
||||
"model": model,
|
||||
"api_key": api_key,
|
||||
"emoji": emoji,
|
||||
"ollama_host": ollama_host,
|
||||
}
|
||||
}}
|
||||
|
||||
try:
|
||||
with config_path.open("w", encoding="utf-8") as f:
|
||||
|
||||
@@ -145,7 +145,7 @@ class ChangelogGenerator:
|
||||
old = path.read_text(encoding="utf-8")
|
||||
if old.strip().startswith("# CHANGELOG"):
|
||||
header, _, rest = old.partition("\n")
|
||||
new_content = f"{header}\n\n{md_content}\n{rest.lstrip()}"
|
||||
new_content = f"{header}\n\n{md_content}\n{rest}"
|
||||
else:
|
||||
new_content = f"# CHANGELOG\n\n{md_content}\n\n{old}"
|
||||
else:
|
||||
|
||||
@@ -48,6 +48,7 @@ class CacheManager:
|
||||
|
||||
def __init__(self, path: Path, max_age_minutes: int = 120) -> None:
|
||||
self.path = path
|
||||
self.max_age_minutes = max_age_minutes
|
||||
|
||||
def init_dry_run(self) -> None:
|
||||
self.path.parent.mkdir(parents=True, exist_ok=True)
|
||||
@@ -59,7 +60,7 @@ class CacheManager:
|
||||
if dry_run:
|
||||
self.init_dry_run()
|
||||
return {}
|
||||
if not force_rebuild and is_file_recent(self.path):
|
||||
if not force_rebuild and is_file_recent(self.path, self.max_age_minutes):
|
||||
return extract_commit_messages(self.path)
|
||||
return {}
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ class DiffBuilder:
|
||||
def _partition(self, files: List[str]) -> tuple[list[str], list[str]]:
|
||||
summary, to_diff = [], []
|
||||
for f in files:
|
||||
if any(p in f for p in IGNORED_DIFF_PATTERNS):
|
||||
if Path(f).name in IGNORED_DIFF_PATTERNS:
|
||||
summary.append(f"[METADATA UPDATED] {f}")
|
||||
else:
|
||||
to_diff.append(f)
|
||||
@@ -128,7 +128,11 @@ class FileGrouper:
|
||||
deepest = max(candidates, key=lambda p: len(Path(p).parts))
|
||||
parent_path = str(Path(deepest).parent)
|
||||
new_key = "root" if parent_path == "." else parent_path
|
||||
groups.setdefault(new_key, []).extend(groups.pop(deepest))
|
||||
files = groups.pop(deepest)
|
||||
if new_key in groups:
|
||||
groups[new_key].extend(files)
|
||||
else:
|
||||
groups["root"].extend(files)
|
||||
return groups
|
||||
|
||||
|
||||
|
||||
@@ -115,8 +115,11 @@ class GitOperator:
|
||||
self._run(["git", "add", *existing])
|
||||
if deleted:
|
||||
self._run(["git", "rm", *deleted])
|
||||
except GitError as e:
|
||||
raise GitError(f"Could not stage files: {e}") from e
|
||||
except GitError:
|
||||
# Fallback: file state may have changed between check and command.
|
||||
# Try a single git add --all for the whole set; git will handle
|
||||
# both additions and removals correctly.
|
||||
self._run(["git", "add", "--all", "--", *files])
|
||||
|
||||
def commit(self, message: str) -> None:
|
||||
"""``git commit -m`` with the given message."""
|
||||
|
||||
@@ -16,4 +16,4 @@ class AnthropicProvider(BaseProvider):
|
||||
max_tokens=1024,
|
||||
messages=[{"role": "user", "content": prompt}],
|
||||
)
|
||||
return message.content[0].text
|
||||
return message.content[0].text or ""
|
||||
@@ -53,7 +53,8 @@ class BaseProvider(ABC):
|
||||
)
|
||||
chosen_model = model or self.DEFAULT_MODEL
|
||||
try:
|
||||
return self._generate(prompt, api_key, chosen_model, **kwargs).strip()
|
||||
text = self._generate(prompt, api_key, chosen_model, **kwargs)
|
||||
return (text or "").strip()
|
||||
except Exception as e:
|
||||
self._handle_error(e)
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ class GeminiProvider(BaseProvider):
|
||||
max_output_tokens=kwargs.get("max_output_tokens", 2048),
|
||||
),
|
||||
)
|
||||
return response.text
|
||||
return response.text or ""
|
||||
except Exception as e:
|
||||
if is_token_limit_error(e):
|
||||
raise # base class handles with friendly message
|
||||
|
||||
@@ -36,7 +36,7 @@ class OllamaProvider(BaseProvider):
|
||||
f"Ollama returned no choices for model {model!r}. "
|
||||
"The model may not be installed locally."
|
||||
)
|
||||
return response.choices[0].message.content
|
||||
return response.choices[0].message.content or ""
|
||||
|
||||
def _handle_error(self, error):
|
||||
"""Override default wrapping — Ollama has no API key to check."""
|
||||
|
||||
@@ -17,4 +17,4 @@ class OpenaiProvider(BaseProvider):
|
||||
messages=[{"role": "user", "content": prompt}],
|
||||
**kwargs,
|
||||
)
|
||||
return response.choices[0].message.content
|
||||
return response.choices[0].message.content or ""
|
||||
@@ -209,6 +209,11 @@ def delete_file(filepath: Path | str) -> bool:
|
||||
|
||||
def load_template_env(sub_dir: str) -> Environment:
|
||||
template_dir = Path(__file__).parent / "templates" / sub_dir
|
||||
if not template_dir.is_dir():
|
||||
raise RuntimeError(
|
||||
f"Template directory not found: {template_dir}. "
|
||||
"The installation may be corrupted."
|
||||
)
|
||||
return Environment(loader=FileSystemLoader(template_dir))
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "devgen-cli"
|
||||
version = "0.2.7"
|
||||
version = "0.2.8"
|
||||
description = "A collection of developer tools"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.10"
|
||||
|
||||
@@ -6,7 +6,7 @@ from setuptools import find_packages, setup
|
||||
|
||||
setup(
|
||||
name="devgen-cli",
|
||||
version="0.2.7",
|
||||
version="0.2.8",
|
||||
packages=find_packages(),
|
||||
include_package_data=True,
|
||||
install_requires=[
|
||||
|
||||
Reference in new issue
Block a user