diff --git a/git_commit_ai/prompts/__init__.py b/git_commit_ai/prompts/__init__.py new file mode 100644 index 0000000..7837ea2 --- /dev/null +++ b/git_commit_ai/prompts/__init__.py @@ -0,0 +1,109 @@ +"""Prompt management for Git Commit AI.""" + +from pathlib import Path +from typing import Optional + +from git_commit_ai.core.config import Config, get_config + + +class PromptBuilder: + """Builder for commit message prompts.""" + + DEFAULT_PROMPT = """You are a helpful assistant that generates git commit messages. + +Analyze the following git diff and generate a concise, descriptive commit message. +The message should: +- Be clear and descriptive +- Explain what changed and why +- Be in present tense +- Not exceed 72 characters for the first line if possible + +Git diff: +``` +{diff} +``` + +{few_shot} + +Generate 3 different commit message suggestions, one per line. +Format: Just the commit messages, one per line, nothing else. + +Suggestions: +""" + + CONVENTIONAL_PROMPT = """You are a helpful assistant that generates conventional git commit messages. + +Generate a commit message in the conventional commit format: +- type(scope): description +- Examples: feat(auth): add login, fix: resolve memory leak + +Valid types: feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert + +Analyze the following git diff and generate commit messages. +Git diff: +``` +{diff} +``` + +{few_shot} + +Generate 3 different commit message suggestions, one per line. +Format: Just the commit messages, one per line, nothing else. + +Suggestions: +""" + + SYSTEM_DEFAULT = "You are a helpful assistant that generates clear and concise git commit messages." + + SYSTEM_CONVENTIONAL = "You are a helpful assistant that generates conventional git commit messages. Always use the format: type(scope): description. Valid types: feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert" + + def __init__(self, config: Optional[Config] = None): + self.config = config or get_config() + self._prompt_dir = Path(self.config.prompt_directory) + + def build_prompt(self, diff: str, context: Optional[str] = None, conventional: bool = False) -> str: + few_shot = self._build_few_shot(context) + + if conventional: + template = self._get_conventional_template() + else: + template = self._get_default_template() + + prompt = template.format(diff=diff[:10000] if len(diff) > 10000 else diff, few_shot=few_shot) + return prompt + + def _get_default_template(self) -> str: + custom_path = self._prompt_dir / "default.txt" + if custom_path.exists(): + return custom_path.read_text() + return self.DEFAULT_PROMPT + + def _get_conventional_template(self) -> str: + custom_path = self._prompt_dir / "conventional.txt" + if custom_path.exists(): + return custom_path.read_text() + return self.CONVENTIONAL_PROMPT + + def _build_few_shot(self, context: Optional[str]) -> str: + if not context: + return "" + return f"\n\nRecent commit history for context:\n{context}" + + def get_system_prompt(self, conventional: bool = False) -> str: + if conventional: + custom_path = self._prompt_dir / "system_conventional.txt" + if custom_path.exists(): + return custom_path.read_text() + return self.SYSTEM_CONVENTIONAL + + custom_path = self._prompt_dir / "system_default.txt" + if custom_path.exists(): + return custom_path.read_text() + return self.SYSTEM_DEFAULT + + def get_supported_languages(self) -> list[str]: + return ["Python", "JavaScript", "TypeScript", "Java", "Go", "Rust", "Ruby", "PHP", "C", "C++", "C#", "Swift", "Kotlin", "Scala", "HTML", "CSS", "SQL", "Shell"] + + +def get_prompt_builder(config: Optional[Config] = None) -> PromptBuilder: + return PromptBuilder(config)