(repository: str, issue: dict[str, Any])
| 140 | |
| 141 | |
| 142 | def build_prompt(repository: str, issue: dict[str, Any]) -> str: |
| 143 | issue_number = issue['number'] |
| 144 | issue_title = issue.get('title', '') |
| 145 | issue_body = issue.get('body') or '' |
| 146 | issue_url = issue.get('html_url', '') |
| 147 | issue_title_json = escape_json_text(issue_title) |
| 148 | issue_body_json = escape_json_text(issue_body) |
| 149 | |
| 150 | return '\n'.join( |
| 151 | [ |
| 152 | 'You are investigating whether a GitHub issue should be redirected ' |
| 153 | 'to an existing issue because it is either:', |
| 154 | '- an exact or near-exact duplicate, or', |
| 155 | '- so overlapping in scope that discussion or fix planning would ' |
| 156 | 'likely be better kept in one canonical issue.', |
| 157 | '', |
| 158 | 'Be conservative about auto-close decisions, but do investigate ' |
| 159 | 'seriously before deciding.', |
| 160 | '', |
| 161 | f'Repository: {repository}', |
| 162 | f'New issue number: #{issue_number}', |
| 163 | f'New issue URL: {issue_url}', |
| 164 | f'New issue title (JSON-escaped string): {issue_title_json}', |
| 165 | f'New issue body (JSON-escaped string): {issue_body_json}', |
| 166 | '', |
| 167 | 'Task:', |
| 168 | '1. Understand the core problem, user-facing outcome, likely root ' |
| 169 | 'cause, and requested fix or behavior.', |
| 170 | "2. Investigate this repository's open issues and issues closed " |
| 171 | 'in the last 90 days for exact duplicates, near-duplicates, or ' |
| 172 | 'strong scope overlap.', |
| 173 | '3. Use multiple search approaches with diverse keywords and ' |
| 174 | 'phrasings rather than a single literal search.', |
| 175 | '4. Ignore pull requests.', |
| 176 | '5. Distinguish carefully between:', |
| 177 | ' - duplicate: essentially the same report, request, or root cause', |
| 178 | ' - overlapping-scope: not identical, but likely to fragment ' |
| 179 | 'discussion or produce competing fixes', |
| 180 | ' - related-but-distinct: similar area, but should stay separate', |
| 181 | ' - no-match: no strong candidate worth redirecting to', |
| 182 | '6. Inspect the strongest 1-3 candidates carefully. If needed, ' |
| 183 | 'inspect comments on the strongest candidates to disambiguate ' |
| 184 | 'false positives.', |
| 185 | '7. Do not post comments, do not modify files, and do not change ' |
| 186 | 'repository state.', |
| 187 | '8. Useful API shapes include:', |
| 188 | f' - GET https://api.github.com/repos/{repository}/issues?state=open&per_page=100', |
| 189 | ' - GET https://api.github.com/repos/' |
| 190 | f'{repository}/issues?state=closed&since=<ISO-8601 timestamp>&per_page=100', |
| 191 | ' - GET https://api.github.com/search/issues?q=<query>', |
| 192 | f' - GET https://api.github.com/repos/{repository}/issues/<number>/comments', |
| 193 | '9. Return exactly one JSON object and nothing else. Do not wrap ' |
| 194 | 'it in markdown fences.', |
| 195 | '', |
| 196 | 'Return schema:', |
| 197 | '{', |
| 198 | f' "issue_number": {issue_number},', |
| 199 | ' "should_comment": true or false,', |
no test coverage detected