feat(devgen/modules): enhance commit generation with manifest context and custom templates

- New Features:
  - Added `_get_manifest_context()` to extract project metadata from manifest files (pyproject.toml, package.json, etc.) for richer commit message context.
  - Added support for custom commit message templates via `custom_template` config.
  - Added emoji instruction toggle based on global `use_emoji` setting.

- Enhancements:
  - Improved error handling for git push failures with specific hints for "no upstream branch" scenarios.
  - Optimized manifest context extraction by truncating large files to avoid token bloat.
  - Refactored prompt generation to dynamically include manifest context and custom templates.
```
This commit is contained in:
sankalp committed 2025-12-25 00:04:54 +05:45
1 parent 1219c1ad81
commit 525aebadf4
1 file changed
+70 -2
+70 -2
View File
@@ -13,6 +13,7 @@ from devgen.utils import (
load_template_env,
run_git_command,
sanitize_ai_commit_message,
render_custom_template,
)
from rich.console import Console
from rich.panel import Panel
@@ -214,9 +215,52 @@ class CommitEngine:
f"Git command failed: {' '.join(e.cmd)}\nError: {e.stderr.strip()}"
)
self.logger.error(msg)
# Check for "no upstream branch" specifically to give a hint?
if "no upstream branch" in msg.lower():
self.console.print(
"[warning]No upstream branch. Skipping push.[/warning]"
)
return
raise CommitEngineError(msg) from e
self.console.print("[bold green]Push successful.[/bold green]")
def _get_manifest_context(self) -> str:
"""Finds and reads manifest files to provide context."""
manifests = [
"pyproject.toml",
"package.json",
"go.mod",
"Cargo.toml",
"Gemfile",
"requirements.txt",
"uv.lock",
"package-lock.json",
"pnpm-lock.yaml",
"yarn.lock",
"composer.lock",
"Gemfile.lock",
"poetry.lock",
]
found = []
for m in manifests:
path = Path(m)
if path.exists():
try:
content = path.read_text(encoding="utf-8")
# Take only first 100 lines to avoid token bloat
lines = content.splitlines()
summary = "\n".join(lines[:100])
if len(lines) > 100:
summary += "\n... (truncated)"
found.append(f"File: {m}\n---\n{summary}\n---")
except Exception:
continue
if not found:
return ""
return "\n\n### Project Context (Manifests)\n" + "\n".join(found)
def generate_message(self, group: str, diff: str, cache: Dict[str, str]) -> str:
"""Generates a commit message using AI or cache."""
if not self.force_rebuild and group in cache:
@@ -230,9 +274,33 @@ class CommitEngine:
model = self.kwargs.get("model") or self.config.get("model") or self.model
api_key = self.kwargs.get("api_key") or self.config.get("api_key")
use_emoji = self.config.get("emoji", True)
custom_template = self.config.get("custom_template")
template = self.template_env.get_template("commit_message.j2")
prompt = template.render(group_name=group, diff_text=diff, use_emoji=use_emoji)
manifest_context = self._get_manifest_context()
if manifest_context:
self.logger.info("Including manifest context in prompt")
if custom_template:
self.logger.info("Using custom template from config")
prompt = render_custom_template(
custom_template,
group_name=group,
diff_text=diff,
use_emoji=use_emoji,
context=manifest_context,
)
else:
template = self.template_env.get_template("commit_message.j2")
prompt = template.render(
group_name=group,
diff_text=diff,
use_emoji=use_emoji,
context=manifest_context,
)
# Automatically append emoji instruction based on global setting
emoji_instr = "Use emojis (🚀, 🐛)." if use_emoji else "No emojis."
prompt = f"{prompt.strip()}\n\n- {emoji_instr}"
with self.console.status("[bold blue]Generating commit message...[/bold blue]"):
raw = generate_with_ai(