diff --git a/devgen/modules/commit_generator.py b/devgen/modules/commit_generator.py index be9f4d7..057c190 100644 --- a/devgen/modules/commit_generator.py +++ b/devgen/modules/commit_generator.py @@ -33,8 +33,8 @@ from devgen.utils import ( extract_commit_messages, get_commit_dry_run_path, is_file_recent, - load_template_env, - render_custom_template, + load_template, + render_template, sanitize_ai_commit_message, ) @@ -74,13 +74,13 @@ class CommitMessageBuilder: def __init__( self, - template_env, + template_str: str, config: Dict[str, Any], provider: str, model: str, debug: bool = False, ) -> None: - self.template_env = template_env + self.template_str = template_str self.config = config self.provider = provider self.model = model @@ -97,25 +97,14 @@ class CommitMessageBuilder: use_emoji = self.config.get("emoji", True) ollama_host = self.config.get("ollama_host") - if custom_template: - prompt = render_custom_template( - custom_template, - group_name=group, - diff_text=diff, - use_emoji=use_emoji, - context=manifest_context, - ) - else: - prompt = ( - self.template_env.get_template("commit_message.tpl") - .render( - group_name=group, - diff_text=diff, - use_emoji=use_emoji, - context=manifest_context, - ) - .strip() - ) + template = custom_template or self.template_str + prompt = render_template( + template, + group_name=group, + diff_text=diff, + use_emoji=use_emoji, + context=manifest_context, + ).strip() provider = ( extra_kwargs.get("provider") or self.config.get("provider") or self.provider @@ -198,10 +187,10 @@ class CommitEngine: ), logger=self.logger, ) - self.template_env = load_template_env("commit") + self.template_str = load_template("commit", "commit_message.tpl") self.cache = CacheManager(get_commit_dry_run_path()) self.message_builder = CommitMessageBuilder( - self.template_env, + self.template_str, self.config, provider=self.provider, model=self.model, diff --git a/devgen/utils.py b/devgen/utils.py index c482a97..b5e7da4 100644 --- a/devgen/utils.py +++ b/devgen/utils.py @@ -6,7 +6,6 @@ from pathlib import Path from typing import Any, Dict, Optional import yaml -from jinja2 import Environment, FileSystemLoader def ensure_log_directory() -> Path: @@ -216,21 +215,61 @@ def delete_file(filepath: Path | str) -> bool: return False -def load_template_env(sub_dir: str) -> Environment: +def load_template(sub_dir: str, name: str) -> str: + """Load a template file from the templates directory.""" template_dir = Path(__file__).parent / "templates" / sub_dir - if not template_dir.is_dir(): + template_path = template_dir / name + if not template_path.is_file(): raise RuntimeError( - f"Template directory not found: {template_dir}. " + f"Template not found: {template_path}. " "The installation may be corrupted." ) - return Environment(loader=FileSystemLoader(template_dir)) + return template_path.read_text(encoding="utf-8") -def render_custom_template(template_str: str, **context) -> str: - """Renders a Jinja2 template from a string.""" - env = Environment() - template = env.from_string(template_str) - return template.render(**context) +def render_template(template_str: str, **context) -> str: + """Render a simple template with variable substitution and conditionals. + + Supports: + - {{ variable }} - variable substitution + - {% if condition %}...{% endif %} - conditional blocks + - {% if condition %}...{% else %}...{% endif %} - conditional with else + """ + result = template_str + + # Process conditional blocks: {% if var %}...{% endif %} + # and {% if var %}...{% else %}...{% endif %} + if_pattern = re.compile( + r"\{%\s*if\s+(\w+)\s*%\}(.*?)\{%\s*endif\s*%\}", + re.DOTALL, + ) + + def replace_if(match): + var_name = match.group(1) + content = match.group(2) + value = context.get(var_name) + if value: + # Check for else block + else_pattern = re.compile(r"\{%\s*else\s*%\}", re.DOTALL) + parts = else_pattern.split(content, 1) + return parts[0] + else: + else_pattern = re.compile(r"\{%\s*else\s*%\}", re.DOTALL) + parts = else_pattern.split(content, 1) + return parts[1] if len(parts) > 1 else "" + + result = if_pattern.sub(replace_if, result) + + # Process variable substitutions: {{ variable }} + var_pattern = re.compile(r"\{\{\s*(\w+)\s*\}\}") + + def replace_var(match): + var_name = match.group(1) + return str(context.get(var_name, "")) + + result = var_pattern.sub(replace_var, result) + + return result def load_config() -> Dict[str, Any]: @@ -338,7 +377,8 @@ __all__ = [ "get_git_staged_files", "read_file_content", "delete_file", - "load_template_env", + "load_template", + "render_template", "load_config", "get_questionary_style", "is_token_limit_error", diff --git a/pyproject.toml b/pyproject.toml index 9593f00..13fe31f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,7 +27,6 @@ classifiers = [ dependencies = [ "anthropic>=0.75.0", "google-genai>=0.1.0", - "jinja2>=3.1.6", "openai>=2.11.0", "pyyaml>=6.0.3", "questionary>=2.1.1", diff --git a/requirements.txt b/requirements.txt index ac70dca..369b7d2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,5 @@ anthropic==0.109.1 google-genai==2.8.0 -jinja2==3.1.6 openai==2.41.1 pre-commit==4.6.0 pyyaml==6.0.3