Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions AUTO1_PATCHES.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ Keep this list minimal to ease upstream rebases.

- Upstream repo: qodo-ai/pr-agent
- Upstream tag: main
- Upstream commit: 1b0609a013f53694c36d457149bde70abf50c048
- Synced on: 2026-02-23
- Upstream commit: 42d55d4182a4839820b11b6c4d06fce855970301
- Synced on: 2026-03-23

## Local patches

Expand Down
5 changes: 1 addition & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

Try the free version on our website.

👉[Get Started Now](www.qodo.ai/get-started/)
👉[Get Started Now](https://www.qodo.ai/get-started/)

PR-Agent is an open-source, AI-powered code review agent and a community-maintained legacy project of Qodo. It is distinct from Qodo’s primary AI code review offering, which provides a feature-rich, context-aware experience. Qodo now offers a free tier that integrates seamlessly with GitHub, GitLab, Bitbucket, and Azure DevOps for high-quality automated reviews.

Expand Down Expand Up @@ -221,9 +221,6 @@ ___
</p>
</div>

<div align="left">

</div>
<hr>

## Try It Now
Expand Down
5 changes: 5 additions & 0 deletions docs/docs/core-abilities/fetching_ticket_context.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

`Supported Git Platforms: GitHub, GitLab, Bitbucket`

!!! note "Branch-name issue linking: GitHub only (for now)"
Extracting issue links from the **branch name** (and the optional `branch_issue_regex` setting) is currently implemented for **GitHub only**. Support for GitLab, Bitbucket, and other platforms is planned for a later release. The GitHub flow was the most relevant to implement first; other providers will follow.

## Overview

PR-Agent streamlines code review workflows by seamlessly connecting with multiple ticket management systems.
Expand Down Expand Up @@ -85,6 +88,8 @@ Examples of valid GitHub/Gitlab issue references:
Branch names can also be used to link issues, for example:
- `123-fix-bug` (where `123` is the issue number)

This branch-name detection applies **only when the git provider is GitHub**. Support for other platforms is planned for later.

Since PR-Agent is integrated with GitHub, it doesn't require any additional configuration to fetch GitHub issues.

## Jira Integration
Expand Down
4 changes: 2 additions & 2 deletions docs/docs/tools/review.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,8 +159,8 @@ extra_instructions = "..."

The `review` can tool automatically add labels to your Pull Requests:

- **`possible security issue`**: This label is applied if the tool detects a potential [security vulnerability](https://github.com/qodo-ai/pr-agent/blob/main/pr_agent/settings/pr_reviewer_prompts.toml#L121) in the PR's code. This feedback is controlled by the 'enable_review_labels_security' flag (default is true).
- **`review effort [x/5]`**: This label estimates the [effort](https://github.com/qodo-ai/pr-agent/blob/main/pr_agent/settings/pr_reviewer_prompts.toml#L105) required to review the PR on a relative scale of 1 to 5, where 'x' represents the assessed effort. This feedback is controlled by the 'enable_review_labels_effort' flag (default is true).
- **`possible security issue`**: This label is applied if the tool detects a potential [security vulnerability](https://github.com/qodo-ai/pr-agent/blob/main/pr_agent/settings/pr_reviewer_prompts.toml#L134) in the PR's code. This feedback is controlled by the 'enable_review_labels_security' flag (default is true).
- **`review effort [x/5]`**: This label estimates the [effort](https://github.com/qodo-ai/pr-agent/blob/main/pr_agent/settings/pr_reviewer_prompts.toml#L118) required to review the PR on a relative scale of 1 to 5, where 'x' represents the assessed effort. This feedback is controlled by the 'enable_review_labels_effort' flag (default is true).
- **`ticket compliance`**: Adds a label indicating code compliance level ("Fully compliant" | "PR Code Verified" | "Partially compliant" | "Not compliant") to any GitHub/Jira/Linea ticket linked in the PR. Controlled by the 'require_ticket_labels' flag (default: false). If 'require_no_ticket_labels' is also enabled, PRs without ticket links will receive a "No ticket found" label.


Expand Down
7 changes: 7 additions & 0 deletions pr_agent/algo/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,13 @@
'gpt-5.2-chat-latest': 128000, # 128K, but may be limited by config.max_model_tokens
'gpt-5.2-codex': 400000, # 400K, but may be limited by config.max_model_tokens
'gpt-5.3-codex': 400000, # 400K, but may be limited by config.max_model_tokens
'gpt-5.3-chat': 128000, # 128K, but may be limited by config.max_model_tokens
'gpt-5.4': 272000, # 272K safe default without opt-in 1M context parameters
'gpt-5.4-2026-03-05': 272000, # 272K safe default without opt-in 1M context parameters
'gpt-5.4-mini': 400000, # 400K, but may be limited by config.max_model_tokens
'gpt-5.4-mini-2026-03-17': 400000, # 400K, but may be limited by config.max_model_tokens
'gpt-5.4-nano': 400000, # 400K, but may be limited by config.max_model_tokens
'gpt-5.4-nano-2026-03-17': 400000, # 400K, but may be limited by config.max_model_tokens
'o1-mini': 128000, # 128K, but may be limited by config.max_model_tokens
'o1-mini-2024-09-12': 128000, # 128K, but may be limited by config.max_model_tokens
'o1-preview': 128000, # 128K, but may be limited by config.max_model_tokens
Expand Down Expand Up @@ -94,6 +99,7 @@
'vertex_ai/gemini-2.5-flash': 1048576,
'vertex_ai/gemini-3-flash-preview': 1048576,
'vertex_ai/gemini-3-pro-preview': 1048576,
'vertex_ai/gemini-3.1-flash-lite-preview': 1048576,
'vertex_ai/gemini-3.1-pro-preview': 1048576,
'vertex_ai/gemma2': 8200,
'gemini/gemini-1.5-pro': 1048576,
Expand All @@ -108,6 +114,7 @@
'gemini/gemini-2.5-pro': 1048576,
'gemini/gemini-3-flash-preview': 1048576,
'gemini/gemini-3-pro-preview': 1048576,
'gemini/gemini-3.1-flash-lite-preview': 1048576,
'gemini/gemini-3.1-pro-preview': 1048576,
'codechat-bison': 6144,
'codechat-bison-32k': 32000,
Expand Down
4 changes: 3 additions & 1 deletion pr_agent/custom_merge_loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ def load(obj, env=None, silent=True, key=None, filename=None):
- Replaces list and dict fields instead of appending/updating (non-default Dynaconf behavior).
- Enforces several security checks (e.g., disallows includes/preloads and enforces .toml files).
- Supports optional single-key loading.
- Supports Dynaconf's fresh_vars feature for dynamic reloading.
Args:
obj: The Dynaconf settings instance to update.
env: The current environment name (upper case). Defaults to 'DEVELOPMENT'. Note: currently unused.
Expand Down Expand Up @@ -93,7 +94,8 @@ def load(obj, env=None, silent=True, key=None, filename=None):

# Update the settings object
for k, v in accumulated_data.items():
if key is None or key == k:
# For fresh_vars support: key parameter is uppercase, but accumulated_data keys are lowercase
if key is None or key.upper() == k.upper():
obj.set(k, v)

def validate_file_security(file_data, filename):
Expand Down
15 changes: 13 additions & 2 deletions pr_agent/settings/configuration.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

[config]
# models
model="gpt-5.2-2025-12-11"
model="gpt-5.4-2026-03-05"
fallback_models=["o4-mini"]
#model_reasoning="o4-mini" # dedicated reasoning model for self-reflection
#model_weak="gpt-4o" # optional, a weaker model to use for some easier tasks
Expand Down Expand Up @@ -61,6 +61,14 @@ reasoning_effort = "medium" # "low", "medium", "high"
enable_claude_extended_thinking = false # Set to true to enable extended thinking feature
extended_thinking_budget_tokens = 2048
extended_thinking_max_output_tokens = 4096
# Extract issue number from PR source branch name (e.g. feature/1-auth-google -> issue #1). When true, branch-derived
# issue URLs are merged with tickets from the PR description for compliance. Set to false to restore description-only behaviour.
# Note: Branch-name extraction is GitHub-only for now; other providers planned for later.
extract_issue_from_branch = true
# Optional: custom regex with exactly one capturing group for the issue number (validated at runtime; falls back
# to default if missing). If empty, uses default pattern: first 1-6 digits at start of branch or after a slash,
# followed by hyphen or end (e.g. feature/1-test, 123-fix). GitHub only; other providers planned for later.
branch_issue_regex = ""


[pr_reviewer] # /review #
Expand Down Expand Up @@ -113,11 +121,14 @@ publish_description_as_comment_persistent=true
enable_semantic_files_types=true
collapsible_file_list='adaptive' # true, false, 'adaptive'
collapsible_file_list_threshold=6
inline_file_summary=false # false, true, 'table'
# markers
use_description_markers=false
enable_large_pr_handling=true
include_generated_by_header=true
#custom_labels = ['Bug fix', 'Tests', 'Bug fix with tests', 'Enhancement', 'Documentation', 'Other']

max_ai_calls=4
async_ai_calls=true
[pr_questions] # /ask #
enable_help_text=false
use_conversation_history=true
Expand Down
23 changes: 18 additions & 5 deletions pr_agent/settings/pr_reviewer_prompts.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[pr_review_prompt]
system="""You are PR-Reviewer, a language model designed to review a Git Pull Request (PR).
Your task is to provide constructive and concise feedback for the PR.
The review should focus on new code added in the PR code diff (lines starting with '+')
The review should focus on new code added in the PR code diff (lines starting with '+'), and only on issues introduced by this PR.


The format we will use to present the PR code diff:
Expand Down Expand Up @@ -38,8 +38,7 @@ __new hunk__

- In the format above, the diff is organized into separate '__new hunk__' and '__old hunk__' sections for each code chunk. '__new hunk__' contains the updated code, while '__old hunk__' shows the removed code. If no code was removed in a specific chunk, the __old hunk__ section will be omitted.
- We also added line numbers for the '__new hunk__' code, to help you refer to the code lines in your suggestions. These line numbers are not part of the actual code, and should only be used for reference.
- Code lines are prefixed with symbols ('+', '-', ' '). The '+' symbol indicates new code added in the PR, the '-' symbol indicates code removed in the PR, and the ' ' symbol indicates unchanged code. \
The review should address new code added in the PR code diff (lines starting with '+').
- Code lines are prefixed with symbols ('+', '-', ' '). The '+' symbol indicates new code added in the PR, the '-' symbol indicates code removed in the PR, and the ' ' symbol indicates unchanged code.
{%- if is_ai_metadata %}
- If available, an AI-generated summary will appear and provide a high-level overview of the file changes. Note that this summary may not be fully accurate or complete.
{%- endif %}
Expand All @@ -54,6 +53,20 @@ __new hunk__
- Set `evidence_type` to `diff` when changed lines directly support the issue, `ticket` for a clear ticket mismatch, or `inferred` when the issue depends on indirect clues.
{%- endif %}

Determining what to flag:
- For clear bugs and security issues, be thorough. Do not skip a genuine problem just because the trigger scenario is narrow.
- For lower-severity concerns, be certain before flagging. If you cannot confidently explain why something is a problem with a concrete scenario, do not flag it.
- Each issue must be discrete and actionable, not a vague concern about the codebase in general.
- Do not speculate that a change might break other code unless you can identify the specific affected code path from the diff context.
- Do not flag intentional design choices or stylistic preferences unless they introduce a clear defect.
- When confidence is limited but the potential impact is high (e.g., data loss, security), report it with an explicit note on what remains uncertain. Otherwise, prefer not reporting over guessing.

Constructing comments:
- Be direct about why something is a problem and the realistic scenario where it manifests.
- Communicate severity accurately. Do not overstate impact. If an issue only arises under specific inputs or environments, say so upfront.
- Keep each issue description concise. Write so the reader grasps the point immediately without close reading.
- Use a matter-of-fact, helpful tone. Avoid accusatory language, excessive praise, or filler phrases like 'Great job', 'Thanks for'.

{%- if extra_instructions %}


Expand All @@ -75,7 +88,7 @@ class SubPR(BaseModel):
class KeyIssuesComponentLink(BaseModel):
relevant_file: str = Field(description="The full file path of the relevant file")
issue_header: str = Field(description="One or two word title for the issue. For example: 'Possible Bug', etc.")
issue_content: str = Field(description="A short and concise summary of what should be further inspected and validated during the PR review process for this issue. Do not mention line numbers in this field.")
issue_content: str = Field(description="A short and concise description of the issue, why it matters, and the specific scenario or input that triggers it. Do not mention line numbers in this field.")
{%- if findings_metadata %}
confidence: str = Field(description="Your confidence in this issue based only on the visible evidence. Allowed values: high, medium, low")
evidence_type: str = Field(description="What directly supports this issue. Allowed values: diff, ticket, inferred")
Expand Down Expand Up @@ -127,7 +140,7 @@ class Review(BaseModel):
{%- if question_str %}
insights_from_user_answers: str = Field(description="shortly summarize the insights you gained from the user's answers to the questions")
{%- endif %}
key_issues_to_review: List[KeyIssuesComponentLink] = Field("A short and diverse list (0-{{ num_max_findings }} issues) of high-priority bugs, problems or performance concerns introduced in the PR code, which the PR reviewer should further focus on and validate during the review process.")
key_issues_to_review: List[KeyIssuesComponentLink] = Field("A concise list (0-{{ num_max_findings }} issues) of bugs, security vulnerabilities, or significant performance concerns introduced in this PR. Only include issues you are confident about. If confidence is limited but the potential impact is high (e.g., data loss, security), you may include it only if you explicitly note what remains uncertain. Each issue must identify a concrete problem with a realistic trigger scenario. An empty list is acceptable if no clear issues are found.")
{%- if require_security_review %}
security_concerns: str = Field(description="Does this PR code introduce vulnerabilities such as exposure of sensitive information (e.g., API keys, secrets, passwords), or security concerns like SQL injection, XSS, CSRF, and others ? Answer 'No' (without explaining why) if there are no possible issues. If there are security concerns or issues, start your answer with a short header, such as: 'Sensitive information exposure: ...', 'SQL injection: ...', etc. Explain your answer. Be specific and give examples if possible")
{%- endif %}
Expand Down
60 changes: 59 additions & 1 deletion pr_agent/tools/ticket_pr_compliance_check.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
GITHUB_TICKET_PATTERN = re.compile(
r'(https://github[^/]+/[^/]+/[^/]+/issues/\d+)|(\b(\w+)/(\w+)#(\d+)\b)|(#\d+)'
)
# Option A: issue number at start of branch or after /, followed by - or end (e.g. feature/1-test-issue, 123-fix)
BRANCH_ISSUE_PATTERN = re.compile(r"(?:^|/)(\d{1,6})(?=-|$)")

TICKET_PROMPT_FIELDS = ("ticket_id", "ticket_url", "title", "body", "labels", "requirements")

Expand Down Expand Up @@ -116,6 +118,45 @@ def extract_ticket_links_from_pr_description(pr_description, repo_path, base_url

return list(github_tickets)

def extract_ticket_links_from_branch_name(branch_name, repo_path, base_url_html="https://github.com"):
"""
Extract GitHub issue URLs from branch name. Numbers are matched at start of branch or after /,
followed by - or end (e.g. feature/1-test-issue -> #1). Respects extract_issue_from_branch
and optional branch_issue_regex (may be under [config] in TOML).
"""
if not branch_name or not repo_path:
return []
if not isinstance(branch_name, str):
return []
settings = get_settings()
if not settings.get("extract_issue_from_branch", settings.get("config.extract_issue_from_branch", True)):
return []
github_tickets = set()
custom_regex_str = settings.get("branch_issue_regex") or settings.get("config.branch_issue_regex", "") or ""
if custom_regex_str:
try:
pattern = re.compile(custom_regex_str)
if pattern.groups < 1:
get_logger().error(
"branch_issue_regex must contain at least one capturing group for the issue number; using default pattern."
)
pattern = BRANCH_ISSUE_PATTERN
except re.error as e:
get_logger().error(f"Invalid custom regex for branch issue extraction: {e}")
return []
else:
pattern = BRANCH_ISSUE_PATTERN
for match in pattern.finditer(branch_name):
try:
issue_number = match.group(1)
except IndexError:
continue
if issue_number and issue_number.isdigit():
github_tickets.add(
f"{base_url_html.strip('/')}/{repo_path}/issues/{issue_number}"
)
return list(github_tickets)


async def extract_tickets(git_provider) -> tuple[list | None, str]:
MAX_TICKET_CHARACTERS = 10000
Expand All @@ -142,7 +183,24 @@ async def extract_tickets(git_provider) -> tuple[list | None, str]:

if isinstance(git_provider, GithubProvider):
user_description = git_provider.get_user_description()
tickets = extract_ticket_links_from_pr_description(user_description, git_provider.repo, git_provider.base_url_html)
description_tickets = extract_ticket_links_from_pr_description(
user_description, git_provider.repo, git_provider.base_url_html
)
branch_name = git_provider.get_pr_branch()
branch_tickets = extract_ticket_links_from_branch_name(
branch_name, git_provider.repo, git_provider.base_url_html
)
seen = set()
merged = []
for link in description_tickets + branch_tickets:
if link not in seen:
seen.add(link)
merged.append(link)
if len(merged) > 3:
get_logger().info(f"Too many tickets (description + branch): {len(merged)}")
tickets = merged[:3]
else:
tickets = merged

if tickets:

Expand Down
Loading