Files
DevGen/devtools/commitgen/generator.py
T

159 lines
5.6 KiB
Python

"""
Commit message and changelog generation using AI.
"""
from typing import List, Dict, Optional
from ..shared.ai import AIService
from ..shared.config import Config
class CommitGenerator(AIService):
"""AI-powered commit message and changelog generator."""
def __init__(self, config: Config):
"""Initialize commit generator."""
super().__init__(config)
def generate_commit_message(
self, diff: str, temperature: Optional[float] = None
) -> str:
"""Generate a commit message from a diff."""
system_prompt = """You are an expert Git assistant trained to write highly effective and conventional commit messages.
Your task is to analyze the provided code diff and generate a commit message in the following format:
Format: type(scope): description
- type: One of feat, fix, docs, style, refactor, test, chore
- scope: Optional but encouraged; represents the part of the codebase affected (e.g. auth, api, db, ui)
- description: A short, clear explanation of the change
Each type has an emoji prefix:
- ✨ feat: New feature
- 🐛 fix: Bug fix
- 📚 docs: Documentation-only change
- 💅 style: Code formatting (no logic change)
- ♻️ refactor: Code changes without affecting behavior
- ✅ test: Test additions/changes
- 🔧 chore: Maintenance, build, or config tasks
Strict rules:
- Write the entire message in a single line
- Use imperative mood (e.g. "add" not "added")
- No file paths, no periods at the end
- Avoid generic or vague descriptions
- Don't mention pull requests, issues, or reviewers
- Focus on the why and impact of the change, not just the what
Examples:
- ✨ feat(auth): add OAuth2 login support
- 🐛 fix(api): return correct HTTP status for invalid input
- ♻️ refactor(db): simplify user query joins
Output ONLY the commit message. Do not include any comments or explanations."""
user_prompt = f"""Generate a single-line conventional commit message for the following code changes:
{diff}
Identify the most relevant type, a concise scope, and the purpose of the change.
Output ONLY the commit message in the correct format (with emoji)."""
message = self.generate_completion(
system_prompt, user_prompt, temperature=temperature
)
lines = [line.strip() for line in message.split("\n") if line.strip()]
for line in lines:
if any(
skip in line.lower()
for skip in [
"based on",
"changes in",
"can be written",
"commit message",
"following",
"these changes",
"the changes",
]
):
continue
if line.startswith("`") or line.startswith("/") or ":" in line:
continue
message = line
break
else:
message = lines[0] if lines else "🔧 chore: update code"
if not any(
emoji in message for emoji in ["✨", "🐛", "📚", "💅", "♻️", "✅", "🔧"]
):
if message.startswith("feat"):
message = "✨ " + message
elif message.startswith("fix"):
message = "🐛 " + message
elif message.startswith("docs"):
message = "📚 " + message
elif message.startswith("style"):
message = "💅 " + message
elif message.startswith("refactor"):
message = "♻️ " + message
elif message.startswith("test"):
message = "✅ " + message
elif message.startswith("chore"):
message = "🔧 " + message
else:
message = "🔧 " + message # fallback
return message
def generate_batch_messages(
self, diffs: Dict[str, str], temperature: Optional[float] = None
) -> Dict[str, str]:
"""Generate commit messages for multiple files."""
if len(diffs) == 1:
file_path, diff = next(iter(diffs.items()))
return {file_path: self.generate_commit_message(diff, temperature)}
combined_diff = "\n\n".join(
f"Changes in {file_path}:\n{diff}" for file_path, diff in diffs.items()
)
message = self.generate_commit_message(combined_diff, temperature)
return {file_path: message for file_path in diffs.keys()}
def generate_changelog(
self, commits: List[str], version: str, temperature: Optional[float] = None
) -> str:
"""Generate a changelog from a list of commits."""
system_prompt = """You are a professional release manager generating changelogs from commit messages.
Follow these rules:
- Group entries under clear sections: Added, Changed, Fixed, Removed, Refactored, Tests, Chore, etc.
- Use past tense (e.g. "Added login flow", not "Add login flow")
- Write concise but informative summaries
- Each bullet point should start with a verb (e.g. "Fixed", "Added")
- Skip commit types like "chore" unless they are user-relevant
- Format sections like this:
## [version]
### Added
- Added support for OAuth2 authentication
### Fixed
- Fixed issue where empty input crashed the API
Do NOT include raw commit messages. Use the commit messages as input and convert them into user-facing changelog entries."""
user_prompt = (
f"Generate a clean and structured changelog for version {
version
} using these commits:\n\n"
+ "\n".join(commits)
)
return self.generate_completion(
system_prompt, user_prompt, temperature=temperature
)