From 7378bc0020f0df4e938ba512839ddb4a882f6de0 Mon Sep 17 00:00:00 2001 From: S4NKALP Date: Wed, 4 Jun 2025 13:50:00 +0545 Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor(generator):=20upd?= =?UTF-8?q?ate=20system=20and=20user=20prompts=20for=20clarity=20and=20con?= =?UTF-8?q?sistency?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- devtools/commitgen/generator.py | 172 +++++++++++++++----------------- 1 file changed, 82 insertions(+), 90 deletions(-) diff --git a/devtools/commitgen/generator.py b/devtools/commitgen/generator.py index cee948a..fe96faa 100644 --- a/devtools/commitgen/generator.py +++ b/devtools/commitgen/generator.py @@ -18,146 +18,138 @@ class CommitGenerator(AIService): 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 clean, conventional commit messages with optional bodies. + system_prompt = """You are an expert Git assistant trained to write highly effective and conventional commit messages. -Follow this format: +Your task is to analyze the provided code diff and generate a commit message in the following format: -(): -[blank line] - - -Where: +Format: type(scope): description - type: One of feat, fix, docs, style, refactor, test, chore -- scope: Optional but recommended (e.g. auth, api, db) -- summary: A short, one-line description of the change -- body: (Optional) Add context, rationale, and details about the change +- 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 must have an emoji: +Each type has an emoji prefix: - ✨ feat: New feature - πŸ› fix: Bug fix -- πŸ“š docs: Documentation -- πŸ’… style: Formatting (no code logic) -- ♻️ refactor: Code restructure without behavior change -- βœ… test: Tests added or modified -- πŸ”§ chore: Maintenance, configs, build scripts, etc. +- πŸ“š 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 -Rules: -- Subject must be a single line, max ~72 characters -- Use imperative mood: β€œadd”, not β€œadded” -- Don’t include file paths, issue numbers, or PR references -- Use body only if extra context is helpful (start with a verb or full sentence) -- Don’t include explanations about what can be written -- DO NOT include β€œcommit message” or meta-language +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 +- ✨ feat(auth): add OAuth2 login support +- πŸ› fix(api): return correct HTTP status for invalid input +- ♻️ refactor(db): simplify user query joins -Adds full OAuth2 login flow using access and refresh tokens. -This improves external authentication and reduces local user management. -""" +Output ONLY the commit message. Do not include any comments or explanations.""" - user_prompt = f"""Generate a conventional commit message for the following code changes: + user_prompt = f"""Generate a single-line conventional commit message for the following code changes: {diff} -- Include a single-line subject -- Add a body section if useful, separated by a blank line -- Output must match the conventional commit format -- Use the correct emoji prefix -""" +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 ) - # Clean and extract subject and optional body - lines = [line.rstrip() for line in message.splitlines()] - subject = "" - body_lines = [] + lines = [line.strip() for line in message.split("\n") if line.strip()] - for i, line in enumerate(lines): - if not subject and line and ":" in line: - subject = 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 subject: - body_lines = lines[i:] - break + 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 subject: - subject = "πŸ”§ chore: update code" - - # Ensure emoji in the subject if not any( - emoji in subject for emoji in ["✨", "πŸ›", "πŸ“š", "πŸ’…", "♻️", "βœ…", "πŸ”§"] + emoji in message for emoji in ["✨", "πŸ›", "πŸ“š", "πŸ’…", "♻️", "βœ…", "πŸ”§"] ): - if subject.startswith("feat"): - subject = "✨ " + subject - elif subject.startswith("fix"): - subject = "πŸ› " + subject - elif subject.startswith("docs"): - subject = "πŸ“š " + subject - elif subject.startswith("style"): - subject = "πŸ’… " + subject - elif subject.startswith("refactor"): - subject = "♻️ " + subject - elif subject.startswith("test"): - subject = "βœ… " + subject - elif subject.startswith("chore"): - subject = "πŸ”§ " + subject + 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: - subject = "πŸ”§ " + subject - - # Final formatted message - message = subject - if body_lines: - body = "\n".join(line.strip() for line in body_lines if line.strip()) - if body: - message += "\n\n" + body + 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. - - Args: - diffs: Dictionary mapping file paths to their diffs - temperature: Optional temperature for AI generation - - Returns: - Dictionary mapping file paths to their commit messages - """ - # If only one file, use the single file method + """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)} - # Combine all diffs with clear file separators combined_diff = "\n\n".join( f"Changes in {file_path}:\n{diff}" for file_path, diff in diffs.items() ) - # Generate a single commit message for all changes message = self.generate_commit_message(combined_diff, temperature) - # Return the same message for all files 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 helpful AI that generates changelogs. + system_prompt = """You are a professional release manager generating changelogs from commit messages. + Follow these rules: -- Group changes by type (Added, Changed, Fixed, etc.) -- Keep descriptions concise but informative -- Use past tense -- Start each entry with a verb -""" +- 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 changelog for version {version} with these commits:\n\n" + f"Generate a clean and structured changelog for version { + version + } using these commits:\n\n" + "\n".join(commits) )