From 900dd436009f54dfd442cb883a6ce304e0bb6f25 Mon Sep 17 00:00:00 2001 From: Andy <119136210+AndyMik90@users.noreply.github.com> Date: Fri, 30 Jan 2026 11:38:44 +0100 Subject: [PATCH] AI-Powered GitHub PR Template Generation (#1618) * auto-claude: subtask-1-1 - Create PR template filler agent system prompt Add system prompt at apps/backend/prompts/github/pr_template_filler.md that instructs the agent to receive PR template, diff summary, spec overview, commit history, and branch context, then fill every section intelligently with accurate descriptions and appropriate checkboxes. Co-Authored-By: Claude Opus 4.5 * auto-claude: subtask-1-2 - Create pr_template_filler agent module Add apps/backend/agents/pr_template_filler.py with: - detect_pr_template(): finds .github/PULL_REQUEST_TEMPLATE.md or .github/PULL_REQUEST_TEMPLATE/ directory templates - run_pr_template_filler(): async function that gathers change context, truncates large diffs to file-level summaries, builds a prompt with template content and context, and invokes Claude via create_client() + run_agent_session() with agent_type='pr_template_filler' - Helper functions for diff truncation, prompt building, and spec loading Co-Authored-By: Claude Opus 4.5 * auto-claude: subtask-1-3 - Register pr_template_filler agent in AGENT_CONFIGS * auto-claude: subtask-2-1 - Integrate AI PR template filler into create_pull_request() Add AI-powered PR body generation to WorktreeManager.create_pull_request(): - detect_pr_template() checks for .github/PULL_REQUEST_TEMPLATE.md - Gathers diff summary and commit log from git for context - Calls run_pr_template_filler() with 30s timeout via asyncio - Falls back to _extract_spec_summary() on any failure - Handles both sync and async calling contexts gracefully * auto-claude: subtask-3-1 - Add pr_template_filler agent config to AgentTools.tsx Register the pr_template_filler entry in the frontend AGENT_CONFIGS with category='utility', tools=['Read', 'Glob', 'Grep'], and feature settings source matching the existing utility agent pattern. Co-Authored-By: Claude Opus 4.5 * auto-claude: subtask-3-2 - Add i18n translation keys for pr_template_filler agent Add English i18n keys for the PR Template Filler agent under a new 'agents' section in settings.json with label and description entries. Co-Authored-By: Claude Opus 4.5 * auto-claude: subtask-3-3 - Add French i18n translation keys for pr_template_filler agent Co-Authored-By: Claude Opus 4.5 * auto-claude: subtask-4-1 - Fix detect_pr_template to accept str and verify integration - Accept str | Path in detect_pr_template() for robustness - Verified module imports cleanly - Verified AGENT_CONFIGS contains pr_template_filler entry - Verified detect_pr_template() correctly finds/misses templates - All 1969 existing backend tests pass with no regressions Co-Authored-By: Claude Opus 4.5 * fix(pr-template): improve accuracy and markdown rendering - Add _strip_markdown_fences() to remove code block wrappers from AI response so PR body renders correctly on GitHub instead of as raw code - Update prompt with detailed checkbox guidelines: - Distinguish between inferable checkboxes (type, area, base branch) and verification-required checkboxes (tested locally, CI passes, platform tested) - Instruct AI to leave unverifiable checkboxes unchecked - Add guidance for platform/code quality checkboxes - Remove markdown code fence wrapper from template in prompt to reduce likelihood of AI wrapping output in fences Co-Authored-By: Claude Opus 4.5 * fix(pr-template): respect user model config and enhance diff context Address PR review findings: - HIGH: Replace hardcoded model="sonnet" with get_utility_model_config() to respect user configuration via UTILITY_MODEL_ID env var - LOW: Add actual code changes to PR context via git diff -p with 30k char truncation for better AI template generation accuracy Co-Authored-By: Claude Opus 4.5 * fix(pr-template): sort imports per ruff I001 Co-Authored-By: Claude Opus 4.5 * fix(pr-template): format import per ruff I001 Co-Authored-By: Claude Opus 4.5 * style: run ruff format on pr_template_filler and worktree Co-Authored-By: Claude Opus 4.5 * fix(pr-template): unpack all 3 return values from run_agent_session run_agent_session returns (status, response, error_info) tuple. Co-Authored-By: Claude Opus 4.5 --------- Co-authored-by: Claude Opus 4.5 --- apps/backend/agents/pr_template_filler.py | 347 ++++++++++++++++++ apps/backend/agents/tools_pkg/models.py | 6 + apps/backend/core/worktree.py | 183 ++++++++- .../prompts/github/pr_template_filler.md | 138 +++++++ .../src/renderer/components/AgentTools.tsx | 8 + .../src/shared/i18n/locales/en/settings.json | 6 + .../src/shared/i18n/locales/fr/settings.json | 6 + 7 files changed, 692 insertions(+), 2 deletions(-) create mode 100644 apps/backend/agents/pr_template_filler.py create mode 100644 apps/backend/prompts/github/pr_template_filler.md diff --git a/apps/backend/agents/pr_template_filler.py b/apps/backend/agents/pr_template_filler.py new file mode 100644 index 00000000..870c0773 --- /dev/null +++ b/apps/backend/agents/pr_template_filler.py @@ -0,0 +1,347 @@ +""" +PR Template Filler Agent Module +================================ + +Detects GitHub PR templates in a project and uses Claude to intelligently +fill them based on code changes, spec context, commit history, and branch info. +""" + +import logging +from pathlib import Path + +from core.client import create_client +from task_logger import LogPhase, get_task_logger + +from .session import run_agent_session + +logger = logging.getLogger(__name__) + +# Maximum diff size (in characters) before truncating to file-level summaries +MAX_DIFF_CHARS = 30_000 + + +def detect_pr_template(project_dir: Path | str) -> str | None: + """ + Detect a GitHub PR template in the project. + + Searches for: + 1. .github/PULL_REQUEST_TEMPLATE.md (single template) + 2. .github/PULL_REQUEST_TEMPLATE/ directory (picks the first .md file) + + Args: + project_dir: Root directory of the project + + Returns: + The template content as a string, or None if no template is found. + """ + project_dir = Path(project_dir) + # Check for single template file + single_template = project_dir / ".github" / "PULL_REQUEST_TEMPLATE.md" + if single_template.is_file(): + try: + content = single_template.read_text(encoding="utf-8") + if content.strip(): + logger.info(f"Found PR template: {single_template}") + return content + except Exception as e: + logger.warning(f"Failed to read PR template {single_template}: {e}") + + # Check for template directory (pick first .md file alphabetically) + template_dir = project_dir / ".github" / "PULL_REQUEST_TEMPLATE" + if template_dir.is_dir(): + try: + md_files = sorted(template_dir.glob("*.md")) + if md_files: + content = md_files[0].read_text(encoding="utf-8") + if content.strip(): + logger.info(f"Found PR template: {md_files[0]}") + return content + except Exception as e: + logger.warning(f"Failed to read PR template from {template_dir}: {e}") + + logger.info("No GitHub PR template found in project") + return None + + +def _truncate_diff(diff_summary: str) -> str: + """ + Truncate a large diff to file-level summaries to stay within token limits. + + If the diff is within MAX_DIFF_CHARS, return it unchanged. + Otherwise, extract only file-level change summaries (e.g. file names + with insertions/deletions counts) and discard line-level detail. + + Args: + diff_summary: The full diff summary text + + Returns: + The original or truncated diff summary. + """ + if len(diff_summary) <= MAX_DIFF_CHARS: + return diff_summary + + lines = diff_summary.splitlines() + summary_lines: list[str] = [] + summary_lines.append("(Diff truncated to file-level summaries due to size)") + summary_lines.append("") + + for line in lines: + # Keep file-level summary lines (stat lines, file headers, etc.) + stripped = line.strip() + if ( + stripped.startswith("diff --git") + or stripped.startswith("---") + or stripped.startswith("+++") + or "file changed" in stripped.lower() + or "files changed" in stripped.lower() + or "insertion" in stripped.lower() + or "deletion" in stripped.lower() + or stripped.startswith("rename") + or stripped.startswith("new file") + or stripped.startswith("deleted file") + or stripped.startswith("Binary files") + ): + summary_lines.append(line) + + # If we couldn't extract meaningful summaries, take the first chunk + if len(summary_lines) <= 2: + truncated = diff_summary[:MAX_DIFF_CHARS] + return truncated + "\n\n(... diff truncated due to size)" + + return "\n".join(summary_lines) + + +def _strip_markdown_fences(content: str) -> str: + """ + Strip markdown code fences from the response if present. + + The AI sometimes wraps the output in ```markdown ... ``` even when instructed + not to. This ensures the PR body renders correctly on GitHub. + + Args: + content: The response content to clean + + Returns: + The content with markdown fences stripped. + """ + result = content + + # Strip opening fence (```markdown or just ```) + if result.startswith("```markdown"): + result = result[len("```markdown") :].lstrip("\n") + elif result.startswith("```md"): + result = result[len("```md") :].lstrip("\n") + elif result.startswith("```"): + result = result[3:].lstrip("\n") + + # Strip closing fence + if result.endswith("```"): + result = result[:-3].rstrip("\n") + + return result.strip() + + +def _build_prompt( + template_content: str, + diff_summary: str, + spec_overview: str, + commit_log: str, + branch_name: str, + target_branch: str, +) -> str: + """ + Build the prompt for the PR template filler agent. + + Combines the system prompt context variables into a single message + that includes the template and all change context. + + Args: + template_content: The PR template markdown + diff_summary: Git diff summary (possibly truncated) + spec_overview: Spec.md content or summary + commit_log: Git log of commits in the PR + branch_name: Source branch name + target_branch: Target branch name + + Returns: + The assembled prompt string. + """ + return f"""Fill out the following GitHub PR template using the provided context. +Return ONLY the filled template markdown — no preamble, no explanation, no code fences. + +## Checkbox Guidelines + +IMPORTANT: Be accurate and honest about what has and hasn't been verified. + +**Check these based on context (you can infer from the diff/spec):** +- Base Branch targeting — check based on target_branch value +- Type of Change (bug fix, feature, docs, refactor, test) — infer from diff and spec +- Area (Frontend, Backend, Fullstack) — infer from changed file paths +- Feature Toggle "N/A" — if the feature appears complete and not behind a flag +- Breaking Changes "No" — if changes appear backward compatible + +**Leave UNCHECKED (these require human verification you cannot perform):** +- "I've tested my changes locally" — you have not tested anything +- "All CI checks pass" — CI has not run yet +- "Windows/macOS/Linux tested" — requires manual testing on each platform +- "All existing tests pass" — CI has not run yet +- "New features include test coverage" — unless test files are clearly visible in the diff +- "Bug fixes include regression tests" — unless test files are clearly visible in the diff + +**For platform/code quality checkboxes:** +- "Used centralized platform/ module" — leave unchecked unless you can verify from the diff +- "No hardcoded paths" — leave unchecked unless you can verify from the diff +- "PR is small and focused (< 400 lines)" — check only if diff stats show < 400 lines changed + +**For the "I've synced with develop branch" checkbox:** +- Leave unchecked — you cannot verify the sync status + +## PR Template + +{template_content} + +## Change Context + +### Branch Information +- **Source branch:** {branch_name} +- **Target branch:** {target_branch} + +### Git Diff Summary +``` +{diff_summary} +``` + +### Spec Overview +{spec_overview} + +### Commit History +``` +{commit_log} +``` + +Fill every section of the PR template. Follow the checkbox guidelines above carefully. +Output ONLY the completed template — no code fences, no preamble.""" + + +def _load_spec_overview(spec_dir: Path) -> str: + """ + Load the spec.md content for context. Falls back to a brief note if unavailable. + + Args: + spec_dir: Directory containing the spec files + + Returns: + The spec content or a fallback message. + """ + spec_file = spec_dir / "spec.md" + if spec_file.is_file(): + try: + content = spec_file.read_text(encoding="utf-8") + # Truncate very long specs to keep prompt manageable + if len(content) > 8000: + return content[:8000] + "\n\n(... spec truncated for brevity)" + return content + except Exception as e: + logger.warning(f"Failed to read spec.md: {e}") + return "(No spec overview available)" + + +async def run_pr_template_filler( + project_dir: Path, + spec_dir: Path, + model: str, + thinking_budget: int | None = None, + branch_name: str = "", + target_branch: str = "develop", + diff_summary: str = "", + commit_log: str = "", + verbose: bool = False, +) -> str | None: + """ + Run the PR template filler agent to generate a filled PR body. + + Detects the project's PR template, gathers change context, and invokes + Claude to intelligently fill out the template sections. + + Args: + project_dir: Root directory of the project + spec_dir: Directory containing the spec files + model: Claude model to use + thinking_budget: Max thinking tokens (None to disable extended thinking) + branch_name: Source branch name for the PR + target_branch: Target branch name for the PR + diff_summary: Git diff summary of changes + commit_log: Git log of commits included in the PR + verbose: Whether to show detailed output + + Returns: + The filled template markdown string, or None if template detection fails + or the agent encounters an error. + """ + # Detect PR template + template_content = detect_pr_template(project_dir) + if template_content is None: + logger.info("No PR template detected — skipping template filler") + return None + + # Load spec overview + spec_overview = _load_spec_overview(spec_dir) + + # Truncate diff if too large + truncated_diff = _truncate_diff(diff_summary) + + # Build the prompt + prompt = _build_prompt( + template_content=template_content, + diff_summary=truncated_diff, + spec_overview=spec_overview, + commit_log=commit_log, + branch_name=branch_name, + target_branch=target_branch, + ) + + # Initialize task logger + task_logger = get_task_logger(spec_dir) + if task_logger: + task_logger.start_phase(LogPhase.CODING, "PR template filling") + + # Create client following the pattern from planner.py + client = create_client( + project_dir, + spec_dir, + model, + agent_type="pr_template_filler", + max_thinking_tokens=thinking_budget, + ) + + try: + async with client: + status, response, _ = await run_agent_session( + client, prompt, spec_dir, verbose, phase=LogPhase.CODING + ) + + if task_logger: + task_logger.end_phase( + LogPhase.CODING, + success=(status != "error"), + message="PR template filling completed", + ) + + if status == "error": + logger.error("PR template filler agent returned an error") + return None + + # The agent should return only the filled template markdown + if response and response.strip(): + result = _strip_markdown_fences(response.strip()) + logger.info("PR template filled successfully") + return result + + logger.warning("PR template filler returned empty response") + return None + + except Exception as e: + logger.error(f"PR template filler error: {e}") + if task_logger: + task_logger.log_error(f"PR template filler error: {e}", LogPhase.CODING) + return None diff --git a/apps/backend/agents/tools_pkg/models.py b/apps/backend/agents/tools_pkg/models.py index f555af60..fddb5091 100644 --- a/apps/backend/agents/tools_pkg/models.py +++ b/apps/backend/agents/tools_pkg/models.py @@ -263,6 +263,12 @@ AGENT_CONFIGS = { "auto_claude_tools": [], "thinking_default": "low", }, + "pr_template_filler": { + "tools": BASE_READ_TOOLS, # Read-only — reads diff, template, spec + "mcp_servers": [], # No MCP needed, context passed via prompt + "auto_claude_tools": [], + "thinking_default": "low", # Fast utility task for structured fill-in + }, "pr_reviewer": { "tools": BASE_READ_TOOLS + WEB_TOOLS, # Read-only "mcp_servers": ["context7"], diff --git a/apps/backend/core/worktree.py b/apps/backend/core/worktree.py index b1a45afb..8b19ead6 100644 --- a/apps/backend/core/worktree.py +++ b/apps/backend/core/worktree.py @@ -15,6 +15,7 @@ This allows: """ import asyncio +import logging import os import re import shutil @@ -28,8 +29,11 @@ from typing import TypedDict, TypeVar from core.gh_executable import get_gh_executable, invalidate_gh_cache from core.git_executable import get_git_executable, get_isolated_git_env, run_git +from core.model_config import get_utility_model_config from debug import debug_warning +logger = logging.getLogger(__name__) + T = TypeVar("T") @@ -1192,8 +1196,22 @@ class WorktreeManager: target = target_branch or self.base_branch pr_title = title or f"auto-claude: {spec_name}" - # Get PR body from spec.md if available - pr_body = self._extract_spec_summary(spec_name) + # Try AI-powered PR body from project's PR template, fall back to spec summary + pr_body: str | None = None + try: + diff_summary, commit_log = self._gather_pr_context(spec_name, target) + pr_body = self._try_ai_pr_body( + spec_name=spec_name, + target_branch=target, + branch_name=info.branch, + diff_summary=diff_summary, + commit_log=commit_log, + ) + except Exception as e: + logger.warning(f"AI PR body generation encountered an error: {e}") + + if not pr_body: + pr_body = self._extract_spec_summary(spec_name) # Find gh executable before attempting PR creation gh_executable = get_gh_executable() @@ -1315,6 +1333,167 @@ class WorktreeManager: error="gh CLI not found. Install from https://cli.github.com/", ) + def _gather_pr_context(self, spec_name: str, target_branch: str) -> tuple[str, str]: + """ + Gather diff summary and commit log for PR template filling. + + Args: + spec_name: The spec folder name + target_branch: The target branch for the PR + + Returns: + Tuple of (diff_summary, commit_log) + """ + worktree_path = self.get_worktree_path(spec_name) + info = self.get_worktree_info(spec_name) + branch = info.branch if info else self.get_branch_name(spec_name) + + # Get diff summary (stat for overview) + diff_result = self._run_git( + ["diff", "--stat", f"{target_branch}...{branch}"], + cwd=worktree_path, + timeout=30, + ) + diff_summary = diff_result.stdout.strip() if diff_result.returncode == 0 else "" + + # Get shortstat for quick summary + shortstat_result = self._run_git( + ["diff", "--shortstat", f"{target_branch}...{branch}"], + cwd=worktree_path, + timeout=30, + ) + if shortstat_result.returncode == 0 and shortstat_result.stdout.strip(): + diff_summary += "\n\n" + shortstat_result.stdout.strip() + + # Get actual code changes (patch format) for better AI context + # Truncate to 30k chars to avoid token limits while still providing meaningful context + patch_result = self._run_git( + ["diff", "-p", "--stat-width=999", f"{target_branch}...{branch}"], + cwd=worktree_path, + timeout=30, + ) + if patch_result.returncode == 0 and patch_result.stdout.strip(): + patch_content = patch_result.stdout.strip() + MAX_DIFF_CHARS = 30_000 + + if len(patch_content) > MAX_DIFF_CHARS: + # Truncate patch and add notice + truncated_patch = patch_content[:MAX_DIFF_CHARS] + diff_summary += ( + "\n\n" + truncated_patch + "\n\n(... diff truncated due to size)" + ) + else: + diff_summary += "\n\n" + patch_content + + # Get commit log + log_result = self._run_git( + [ + "log", + "--oneline", + "--no-merges", + f"{target_branch}..{branch}", + ], + cwd=worktree_path, + timeout=30, + ) + commit_log = log_result.stdout.strip() if log_result.returncode == 0 else "" + + return diff_summary, commit_log + + def _try_ai_pr_body( + self, + spec_name: str, + target_branch: str, + branch_name: str, + diff_summary: str, + commit_log: str, + ) -> str | None: + """ + Attempt to generate a PR body using the AI template filler agent. + + Runs the async agent synchronously with a 30-second timeout. + Returns None on any failure so the caller can fall back gracefully. + + Args: + spec_name: The spec folder name + target_branch: The target branch for the PR + branch_name: The source branch name + diff_summary: Git diff summary of changes + commit_log: Git log of commits + + Returns: + The AI-generated PR body string, or None if unavailable. + """ + try: + from agents.pr_template_filler import ( + detect_pr_template, + run_pr_template_filler, + ) + except ImportError: + logger.warning( + "PR template filler module not available, skipping AI PR body" + ) + return None + + # Check if a PR template exists before doing any heavy lifting + template = detect_pr_template(self.project_dir) + if template is None: + return None + + # Resolve spec directory + spec_dir = self.project_dir / ".auto-claude" / "specs" / spec_name + if not spec_dir.is_dir(): + # Try worktree-local spec path + worktree_path = self.get_worktree_path(spec_name) + spec_dir = worktree_path / ".auto-claude" / "specs" / spec_name + if not spec_dir.is_dir(): + logger.warning("Spec directory not found for AI PR body generation") + return None + + # Get model configuration from environment (respects user settings) + model, thinking_budget = get_utility_model_config() + + async def _run_with_timeout() -> str | None: + try: + return await asyncio.wait_for( + run_pr_template_filler( + project_dir=self.project_dir, + spec_dir=spec_dir, + model=model, + thinking_budget=thinking_budget, + branch_name=branch_name, + target_branch=target_branch, + diff_summary=diff_summary, + commit_log=commit_log, + verbose=False, + ), + timeout=30.0, + ) + except asyncio.TimeoutError: + logger.warning("PR template filler timed out after 30s") + return None + + try: + # Check if there's already a running event loop + try: + loop = asyncio.get_running_loop() + except RuntimeError: + loop = None + + if loop and loop.is_running(): + # We're already inside an async context — run in a new thread + import concurrent.futures + + with concurrent.futures.ThreadPoolExecutor(max_workers=1) as pool: + future = pool.submit(asyncio.run, _run_with_timeout()) + return future.result(timeout=35) + else: + return asyncio.run(_run_with_timeout()) + + except Exception as e: + logger.warning(f"AI PR body generation failed: {e}") + return None + def _extract_spec_summary(self, spec_name: str) -> str: """Extract a summary from spec.md for PR body.""" worktree_path = self.get_worktree_path(spec_name) diff --git a/apps/backend/prompts/github/pr_template_filler.md b/apps/backend/prompts/github/pr_template_filler.md new file mode 100644 index 00000000..a8511283 --- /dev/null +++ b/apps/backend/prompts/github/pr_template_filler.md @@ -0,0 +1,138 @@ +# PR Template Filler Agent + +## Your Role + +You are an expert developer filling out a GitHub Pull Request template. You receive the repository's PR template along with comprehensive context about the changes — git diff summary, spec overview, commit history, and branch information. Your job is to produce a complete, accurate PR body that matches the template structure exactly, with every section filled intelligently and every relevant checkbox checked. + +## Input Context + +You will receive: + +1. **PR Template** — The repository's `.github/PULL_REQUEST_TEMPLATE.md` content +2. **Git Diff Summary** — A summary of all code changes (files changed, insertions, deletions) +3. **Spec Overview** — The specification document describing the feature/fix being implemented +4. **Commit History** — The list of commits included in this PR +5. **Branch Context** — Source branch name, target branch name + +## Methodology + +### Step 1: Understand the Changes + +Before filling anything: + +1. **Read the spec overview** to understand the purpose and scope of the work +2. **Analyze the diff summary** to identify what files changed and what kind of changes were made +3. **Review the commit history** to understand the progression of work +4. **Note the branch names** to infer the PR target and type of change + +### Step 2: Fill Every Section + +For each section in the template: + +1. **Identify the section type** — Is it a description field, a checkbox list, a free-text area, or a conditional section? +2. **Select the appropriate content** based on the change context +3. **Be specific and accurate** — Reference actual files, components, and behaviors from the diff +4. **Never leave a section empty** — If a section is not applicable, explicitly state "N/A" or "Not applicable" + +### Step 3: Check Appropriate Checkboxes + +For checkbox lists (`- [ ]` items): + +1. **Check boxes that apply** by changing `- [ ]` to `- [x]` +2. **Leave unchecked** boxes that don't apply +3. **Base decisions on evidence** from the diff and spec, not assumptions +4. **When uncertain**, leave unchecked rather than incorrectly checking + +### Step 4: Validate Output + +Before returning: + +1. **Verify markdown structure** matches the template exactly (same headings, same order) +2. **Ensure no template placeholders remain** (no `` left unfilled where content is expected) +3. **Check that descriptions are concise** but informative (2-3 sentences for summaries) +4. **Confirm all checkboxes reflect reality** based on the provided context + +## Section-Specific Guidelines + +### Description Sections + +- Write 2-3 clear sentences explaining what the PR does and why +- Reference the spec or task if available +- Focus on the "what" and "why", not implementation details + +### Type of Change + +- Determine from the spec and diff whether this is a bug fix, feature, refactor, docs, or test change +- Check exactly one type unless the PR genuinely spans multiple types +- Use the spec's `workflow_type` field as a strong signal + +### Area / Service + +- Analyze which directories were modified in the diff +- `frontend` = changes in `apps/frontend/` +- `backend` = changes in `apps/backend/` +- `fullstack` = changes in both + +### Related Issues + +- Extract issue numbers from branch names (e.g., `feature/123-description` → `#123`) +- Extract from spec metadata if available +- Use `Closes #N` format for issues that will be closed by this PR + +### Checklists + +- **Testing checklists**: Check items that the commit history and diff evidence support +- **Platform checklists**: Check platforms that CI covers; note if manual testing is needed +- **Code quality checklists**: Check if the diff shows adherence to the principles mentioned + +### AI Disclosure + +- Always check the AI disclosure box — this PR is generated by Auto Claude +- Set tool to "Auto Claude (Claude Agent SDK)" +- Set testing level based on whether QA was run (check spec context for QA status) +- Always check "I understand what this PR does" — the AI agent analyzed the changes + +### Screenshots + +- If the diff includes UI changes (frontend components, styles), note that screenshots should be added +- If no UI changes, write "N/A - No UI changes" or remove the section if the template allows + +### Breaking Changes + +- Analyze the diff for API changes, removed exports, changed interfaces, or modified database schemas +- If no breaking changes are evident, mark as "No" +- If breaking changes exist, describe what breaks and suggest migration steps + +### Feature Toggle + +- Check the spec for mentions of feature flags, localStorage flags, or environment variables +- If the feature is complete and ready, check "N/A - Feature is complete and ready for all users" + +## Output Format + +Return **only** the filled PR template as valid markdown. Do not include any preamble, explanation, or wrapper — just the completed template content ready to be used as a GitHub PR body. + +## Quality Standards + +1. **Accuracy over completeness** — It's better to leave a checkbox unchecked than to incorrectly check it +2. **Evidence-based** — Every filled section should be traceable to the provided context +3. **Professional tone** — Write as a senior developer would in a real PR +4. **Concise but informative** — Don't pad sections with filler text +5. **Valid markdown** — The output must render correctly on GitHub + +## Anti-Patterns to Avoid + +### DO NOT: + +- **Invent information** not present in the provided context +- **Leave template placeholders** like `` without replacing them with actual content +- **Check every checkbox** — only check those supported by evidence +- **Write vague descriptions** like "This PR makes some changes" — be specific +- **Add sections** not present in the original template +- **Remove sections** from the original template — fill or mark as N/A +- **Hallucinate file names** or components not mentioned in the diff +- **Guess issue numbers** — only reference issues you can confirm from the branch name or spec + +--- + +Remember: Your output becomes the PR body on GitHub. It should be professional, accurate, and immediately useful for reviewers. Every section should help a reviewer understand what changed, why it changed, and what to look for during review. diff --git a/apps/frontend/src/renderer/components/AgentTools.tsx b/apps/frontend/src/renderer/components/AgentTools.tsx index 9dcf22d2..6797edbc 100644 --- a/apps/frontend/src/renderer/components/AgentTools.tsx +++ b/apps/frontend/src/renderer/components/AgentTools.tsx @@ -256,6 +256,14 @@ const AGENT_CONFIGS: Record = { mcp_servers: ['context7'], settingsSource: { type: 'feature', feature: 'roadmap' }, }, + pr_template_filler: { + label: 'PR Template Filler', + description: 'Generates AI-powered PR descriptions from templates', + category: 'utility', + tools: ['Read', 'Glob', 'Grep'], + mcp_servers: [], + settingsSource: { type: 'feature', feature: 'utility' }, + }, }; // MCP Server descriptions - accurate per backend models.py diff --git a/apps/frontend/src/shared/i18n/locales/en/settings.json b/apps/frontend/src/shared/i18n/locales/en/settings.json index fd818000..3730add1 100644 --- a/apps/frontend/src/shared/i18n/locales/en/settings.json +++ b/apps/frontend/src/shared/i18n/locales/en/settings.json @@ -858,5 +858,11 @@ "increase": "Increase {{label}} by {{step}}", "currentValue": "Current value: {{value}}" } + }, + "agents": { + "pr_template_filler": { + "label": "PR Template Filler", + "description": "AI-fills GitHub PR templates from code changes" + } } } diff --git a/apps/frontend/src/shared/i18n/locales/fr/settings.json b/apps/frontend/src/shared/i18n/locales/fr/settings.json index 9850dff8..71fc1c49 100644 --- a/apps/frontend/src/shared/i18n/locales/fr/settings.json +++ b/apps/frontend/src/shared/i18n/locales/fr/settings.json @@ -858,5 +858,11 @@ "increase": "Augmenter {{label}} de {{step}}", "currentValue": "Valeur actuelle : {{value}}" } + }, + "agents": { + "pr_template_filler": { + "label": "Remplisseur de modèle PR", + "description": "Remplit intelligemment les modèles de PR GitHub à partir des changements de code" + } } }