mirror of
https://github.com/qodo-ai/pr-agent.git
synced 2025-07-02 20:00:41 +08:00
feat: add TODO comments to PR review output
This commit is contained in:
@ -98,6 +98,11 @@ extra_instructions = "..."
|
||||
<tr>
|
||||
<td><b>require_security_review</b></td>
|
||||
<td>If set to true, the tool will add a section that checks if the PR contains a possible security or vulnerability issue. Default is true.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>require_todo_scan</b></td>
|
||||
<td>If set to true, the tool will add a section that lists TODO comments found in the PR code changes. Default is true.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>require_ticket_analysis_review</b></td>
|
||||
|
@ -131,6 +131,7 @@ def convert_to_markdown_v2(output_data: dict,
|
||||
"Focused PR": "✨",
|
||||
"Relevant ticket": "🎫",
|
||||
"Security concerns": "🔒",
|
||||
"Todo sections": "✅",
|
||||
"Insights from user's answers": "📝",
|
||||
"Code feedback": "🤖",
|
||||
"Estimated effort to review [1-5]": "⏱️",
|
||||
@ -153,7 +154,7 @@ def convert_to_markdown_v2(output_data: dict,
|
||||
|
||||
for key, value in output_data['review'].items():
|
||||
if value is None or value == '' or value == {} or value == []:
|
||||
if key.lower() not in ['can_be_split', 'key_issues_to_review']:
|
||||
if key.lower() not in ['can_be_split', 'key_issues_to_review', 'todo_sections']:
|
||||
continue
|
||||
key_nice = key.replace('_', ' ').capitalize()
|
||||
emoji = emojis.get(key_nice, "")
|
||||
@ -209,6 +210,50 @@ def convert_to_markdown_v2(output_data: dict,
|
||||
markdown_text += f"### {emoji} Security concerns\n\n"
|
||||
value = emphasize_header(value.strip(), only_markdown=True)
|
||||
markdown_text += f"{value}\n\n"
|
||||
elif 'todo sections' in key_nice.lower():
|
||||
if gfm_supported:
|
||||
markdown_text += f"<tr><td>"
|
||||
if is_value_no(value):
|
||||
markdown_text += f"{emoji} <strong>No ToDo sections</strong>"
|
||||
else:
|
||||
markdown_text += f"{emoji} <strong>ToDo sections</strong><br><br>\n\n"
|
||||
if isinstance(value, list):
|
||||
markdown_text += "<ul>\n"
|
||||
for todo_item in value:
|
||||
relevant_file = todo_item.get('relevant_file', '')
|
||||
line_number = todo_item.get('line_number', '')
|
||||
content = todo_item.get('content', '')
|
||||
reference_link = None
|
||||
if git_provider and relevant_file and line_number:
|
||||
reference_link = git_provider.get_line_link(relevant_file, line_number, line_number)
|
||||
file_line = f"{relevant_file} [{line_number}]"
|
||||
if reference_link:
|
||||
file_line = f"<a href='{reference_link}'>{file_line}</a>"
|
||||
if content.strip():
|
||||
markdown_text += f"<li>{file_line}: {content}</li>\n"
|
||||
else:
|
||||
markdown_text += f"<li>{file_line}</li>\n"
|
||||
markdown_text += "</ul>\n"
|
||||
markdown_text += f"</td></tr>\n"
|
||||
else:
|
||||
if is_value_no(value):
|
||||
markdown_text += f"### {emoji} No ToDo sections\n\n"
|
||||
else:
|
||||
markdown_text += f"### {emoji} ToDo sections\n\n"
|
||||
if isinstance(value, list):
|
||||
for todo_item in value:
|
||||
relevant_file = todo_item.get('relevant_file', '')
|
||||
line_number = todo_item.get('line_number', '')
|
||||
content = todo_item.get('content', '')
|
||||
if git_provider and relevant_file and line_number:
|
||||
reference_link = git_provider.get_line_link(relevant_file, line_number, line_number)
|
||||
file_line = f"[{relevant_file} [{line_number}]]({reference_link})"
|
||||
else:
|
||||
file_line = f"{relevant_file} [{line_number}]"
|
||||
if content.strip():
|
||||
markdown_text += f"- {file_line}: {content}\n"
|
||||
else:
|
||||
markdown_text += f"- {file_line}\n"
|
||||
elif 'can be split' in key_nice.lower():
|
||||
if gfm_supported:
|
||||
markdown_text += f"<tr><td>"
|
||||
|
@ -77,6 +77,7 @@ require_tests_review=true
|
||||
require_estimate_effort_to_review=true
|
||||
require_can_be_split_review=false
|
||||
require_security_review=true
|
||||
require_todo_scan=true
|
||||
require_ticket_analysis_review=true
|
||||
# general options
|
||||
persistent_comment=true
|
||||
|
@ -72,6 +72,11 @@ class KeyIssuesComponentLink(BaseModel):
|
||||
start_line: int = Field(description="The start line that corresponds to this issue in the relevant file")
|
||||
end_line: int = Field(description="The end line that corresponds to this issue in the relevant file")
|
||||
|
||||
class TodoSection(BaseModel):
|
||||
relevant_file: str = Field(description="The file containing the TODO comment")
|
||||
line_number: int = Field(description="The line number of the TODO comment")
|
||||
content: str = Field(description="The content of the TODO comment. Only include items that are actual TODO comments inside code comments (e.g., lines starting with #, //, /*, <!--, etc). Remove any leading 'TODO' or similar prefix from the content, but do include lines like '# TODO' even if they result in empty content after prefix removal. Do not include TODOs that are not in comments or not prefixed appropriately.")
|
||||
|
||||
{%- if related_tickets %}
|
||||
|
||||
class TicketCompliance(BaseModel):
|
||||
@ -102,6 +107,9 @@ class Review(BaseModel):
|
||||
{%- if require_security_review %}
|
||||
security_concerns: str = Field(description="Does this PR code introduce possible 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 %}
|
||||
{%- if require_todo_scan %}
|
||||
todo_sections: List[TodoSection] = Field(description="A list of TODO comments found in the code. Return an empty list if there are no TODO comments.")
|
||||
{%- endif %}
|
||||
{%- if require_can_be_split_review %}
|
||||
can_be_split: List[SubPR] = Field(min_items=0, max_items=3, description="Can this PR, which contains {{ num_pr_files }} changed files in total, be divided into smaller sub-PRs with distinct tasks that can be reviewed and merged independently, regardless of the order ? Make sure that the sub-PRs are indeed independent, with no code dependencies between them, and that each sub-PR represent a meaningful independent task. Output an empty list if the PR code does not need to be split.")
|
||||
{%- endif %}
|
||||
@ -148,6 +156,12 @@ review:
|
||||
- ...
|
||||
security_concerns: |
|
||||
No
|
||||
todo_sections:
|
||||
- relevant_file: |
|
||||
src/file1.py
|
||||
line_number: 13
|
||||
content: |
|
||||
...
|
||||
{%- if require_can_be_split_review %}
|
||||
can_be_split:
|
||||
- relevant_files:
|
||||
@ -266,6 +280,12 @@ review:
|
||||
- ...
|
||||
security_concerns: |
|
||||
No
|
||||
todo_sections:
|
||||
- relevant_file: |
|
||||
...
|
||||
line_number: ...
|
||||
content: |
|
||||
...
|
||||
{%- if require_can_be_split_review %}
|
||||
can_be_split:
|
||||
- relevant_files:
|
||||
|
@ -87,6 +87,7 @@ class PRReviewer:
|
||||
"require_estimate_effort_to_review": get_settings().pr_reviewer.require_estimate_effort_to_review,
|
||||
'require_can_be_split_review': get_settings().pr_reviewer.require_can_be_split_review,
|
||||
'require_security_review': get_settings().pr_reviewer.require_security_review,
|
||||
'require_todo_scan': get_settings().pr_reviewer.require_todo_scan,
|
||||
'question_str': question_str,
|
||||
'answer_str': answer_str,
|
||||
"extra_instructions": get_settings().pr_reviewer.extra_instructions,
|
||||
|
Reference in New Issue
Block a user