Add AI agent workflows to auto-comment on issues and PRs#95
Add AI agent workflows to auto-comment on issues and PRs#95
Conversation
Co-authored-by: blackboxprogramming <118287761+blackboxprogramming@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Adds two GitHub Actions workflows that use the GitHub Models API to auto-generate and post structured, “agent-style” comments on issue and pull request activity to improve triage and review throughput.
Changes:
- Introduces an
Issue Agentworkflow to comment on issue events with a structured analysis (and a non-AI fallback). - Introduces a
PR Agentworkflow to comment on PR events, including basic PR metadata and a changed-files summary (and a non-AI fallback). - Adds GitHub Models API usage (
models: read) from withinactions/github-script.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 8 comments.
| File | Description |
|---|---|
| .github/workflows/issue-agent.yml | New workflow that calls Models API and posts a structured issue analysis comment (with fallback). |
| .github/workflows/pr-agent.yml | New workflow that gathers changed files, calls Models API, and posts a structured PR review comment (with fallback). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| on: | ||
| pull_request: | ||
| types: [opened, edited, synchronize, reopened] | ||
|
|
||
| permissions: | ||
| pull-requests: write | ||
| contents: read | ||
| models: read |
There was a problem hiding this comment.
Using the pull_request event with pull-requests: write permissions will not be able to create comments for PRs opened from forks (the token is read-only in that case). If you need this to work for forked contributions, consider switching to pull_request_target with appropriate safeguards (avoid checking out/executing untrusted PR code), or document/accept that the agent only comments on same-repo branches.
| const response = await fetch('https://models.inference.ai.azure.com/chat/completions', { | ||
| method: 'POST', | ||
| headers: { | ||
| 'Content-Type': 'application/json', | ||
| 'Authorization': `Bearer ${process.env.GITHUB_TOKEN}` | ||
| }, |
There was a problem hiding this comment.
The Authorization header for the Models API call is built from process.env.GITHUB_TOKEN, but this workflow never sets GITHUB_TOKEN in env:. As written, the bearer token can end up empty and the Models API request will fail (falling back every time). Set env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} (or ${{ github.token }}) for this step, or pass the token via an explicit env var and reference that instead of process.env.GITHUB_TOKEN.
| const response = await fetch('https://models.inference.ai.azure.com/chat/completions', { | ||
| method: 'POST', | ||
| headers: { | ||
| 'Content-Type': 'application/json', | ||
| 'Authorization': `Bearer ${process.env.GITHUB_TOKEN}` | ||
| }, | ||
| body: JSON.stringify({ | ||
| model: 'gpt-4o-mini', | ||
| messages: [ | ||
| { | ||
| role: 'system', | ||
| content: `You are a rigorous technical agent reviewing GitHub issues for the "simulation-theory" repository — a research project on simulation theory, mathematics, quantum mechanics, and philosophy. Your job is to carefully read each issue and provide a thorough, structured analysis. | ||
|
|
||
| For each issue produce: | ||
| 1. **Summary** — a concise one-paragraph summary of what the issue is about. | ||
| 2. **Key Points** — bullet list of the most important observations or questions raised. | ||
| 3. **Relevance to Simulation Theory** — how this issue connects to the project's themes. | ||
| 4. **Suggested Actions** — concrete next steps or questions for the author. | ||
|
|
||
| Be rigorous, thoughtful, and constructive. Keep the tone academic and helpful.` | ||
| }, | ||
| { | ||
| role: 'user', | ||
| content: `Please analyze this GitHub issue:\n\n**Title:** ${issueTitle}\n**Author:** ${issueUser}\n**Labels:** ${labels}\n\n**Description:**\n${issueBody}` | ||
| } | ||
| ], | ||
| max_tokens: 1500, | ||
| temperature: 0.4 | ||
| }) | ||
| }); | ||
|
|
||
| let analysisText; | ||
| if (response.ok) { | ||
| const data = await response.json(); | ||
| if (data.choices && data.choices.length > 0 && data.choices[0].message) { | ||
| analysisText = data.choices[0].message.content; | ||
| } else { | ||
| console.log('Unexpected response structure from GitHub Models API:', JSON.stringify(data)); | ||
| } | ||
| } else { | ||
| console.log(`GitHub Models API returned ${response.status}: ${await response.text()}`); | ||
| } |
There was a problem hiding this comment.
The Models API call / JSON parsing is not wrapped in try/catch. If fetch() throws (network/DNS) or response.json() throws (non-JSON error body), the action will fail and no fallback comment will be posted. Wrap the request + parsing in a try/catch and fall back to the templated analysis on any exception; also consider adding a request timeout via AbortController so the job can't hang on a stalled API call.
.github/workflows/pr-agent.yml
Outdated
| const response = await fetch('https://models.inference.ai.azure.com/chat/completions', { | ||
| method: 'POST', | ||
| headers: { | ||
| 'Content-Type': 'application/json', | ||
| 'Authorization': `Bearer ${process.env.GITHUB_TOKEN}` | ||
| }, |
There was a problem hiding this comment.
The Authorization header for the Models API call is built from process.env.GITHUB_TOKEN, but this workflow never sets GITHUB_TOKEN in env:. As written, the bearer token can end up empty and the Models API request will fail (falling back every time). Set env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} (or ${{ github.token }}) for this step, or pass the token via an explicit env var and reference that instead of process.env.GITHUB_TOKEN.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated 11 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
.github/workflows/issue-agent.yml
Outdated
| analysisText = `**Summary**\nIssue #${issueNumber} titled *"${issueTitle}"* was submitted by @${issueUser}. ${issueBody.length > 0 ? 'It contains a description that may include images or text.' : 'No description was provided.'}\n\n**Labels:** ${labels}\n\n**Suggested Actions**\n- Review the content of this issue and add appropriate labels if missing.\n- Respond to the author with any clarifying questions.\n- Link related issues or pull requests if applicable.`; | ||
| } | ||
|
|
||
| const comment = `## 🤖 Agent Analysis\n\n${analysisText}\n\n---\n*This comment was generated automatically by the Issue Agent workflow.*`; |
There was a problem hiding this comment.
The AI-generated response is directly interpolated into the GitHub comment without any sanitization or validation of the content. While GitHub's comment system handles markdown safely, a malicious or malformed AI response could:
- Include excessive content that makes the comment unusable
- Include markdown that creates broken formatting
- Potentially include HTML/JavaScript if the AI is manipulated via prompt injection
Consider:
- Adding a maximum length check on the AI response before posting (e.g., truncate if > 5000 characters)
- Stripping or escaping potentially dangerous content
- Validating that the response is structured as expected before posting
This is a defense-in-depth measure to handle cases where the AI model behaves unexpectedly or is manipulated through prompt injection (see related security comment).
| role: 'user', | ||
| content: `Please analyze this pull request:\n\n**Title:** ${prTitle}\n**Author:** ${prUser}\n**Base branch:** ${baseBranch} ← **Head branch:** ${headBranch}\n**Changes:** +${additions} / -${deletions} lines\n**Changed files:** ${changedFiles}\n\n**Description:**\n${prBody}` |
There was a problem hiding this comment.
User-provided content (PR title, description, branch names) is directly interpolated into the AI prompt without sanitization. While the content is only sent to the GitHub Models API and not rendered directly in the comment, this could still lead to prompt injection attacks where malicious users craft PR content designed to manipulate the AI's response.
Consider:
- Adding a content length limit before sending to the API (especially for PR body which can be very long)
- Sanitizing or escaping special characters that could be used for prompt injection
- Adding a disclaimer in the workflow documentation about the risks of AI-generated content
Note: This is partially mitigated by the fact that the AI's response is wrapped in a clearly marked bot comment, but prompt injection could still cause the bot to generate misleading or inappropriate analysis.
| jobs: | ||
| analyze-pr: | ||
| runs-on: ubuntu-latest |
There was a problem hiding this comment.
There's no rate limiting or concurrency control for the GitHub Models API calls. If multiple PRs are opened/synchronized rapidly (e.g., during batch updates or automated dependency PRs), this could:
- Exceed GitHub Models API rate limits, causing failures
- Incur unexpected costs if the API has usage-based pricing
- Create a spam flood of bot comments (especially with
synchronizetrigger firing on every push)
Consider adding:
- A concurrency limit in the workflow (e.g.,
concurrency: group: "pr-agent"withcancel-in-progress: trueorgroup: ${{ github.event.pull_request.number }}) - Rate limiting checks before making API calls
- A filter to skip bot-created PRs (check if
context.payload.pull_request.user.type === 'Bot')
| jobs: | ||
| analyze-issue: | ||
| runs-on: ubuntu-latest | ||
| steps: |
There was a problem hiding this comment.
There's no rate limiting or concurrency control for the GitHub Models API calls. If multiple issues are opened/edited rapidly (e.g., during a bulk import or bot activity), this could:
- Exceed GitHub Models API rate limits, causing failures
- Incur unexpected costs if the API has usage-based pricing
- Create a spam flood of bot comments
Consider adding:
- A concurrency limit in the workflow (e.g.,
concurrency: group: "issue-agent"withcancel-in-progress: true) - Rate limiting checks before making API calls
- A filter to skip bot-created issues (check if
context.payload.issue.user.type === 'Bot')
| on: | ||
| issues: | ||
| types: [opened, edited, reopened, labeled] |
There was a problem hiding this comment.
The workflow will post a new comment every time it's triggered, which could lead to spam on issues that are edited multiple times. For the edited trigger, consider either:
- Editing the existing bot comment instead of creating a new one (find the previous comment by filtering comments where the author is the GitHub Actions bot)
- Adding a check to only comment once per issue
- Removing the
editedtrigger if re-analysis on edits isn't necessary
The same pattern applies to issues that are repeatedly labeled/unlabeled.
| { | ||
| role: 'user', | ||
| content: `Please analyze this GitHub issue:\n\n**Title:** ${issueTitle}\n**Author:** ${issueUser}\n**Labels:** ${labels}\n\n**Description:**\n${issueBody}` |
There was a problem hiding this comment.
User-provided content (issue title, body, labels) is directly interpolated into the AI prompt without sanitization. While the content is only sent to the GitHub Models API and not rendered directly in the comment, this could still lead to prompt injection attacks where malicious users craft issue content designed to manipulate the AI's response.
Consider:
- Adding a content length limit before sending to the API
- Sanitizing or escaping special characters that could be used for prompt injection
- Adding a disclaimer in the workflow documentation about the risks of AI-generated content
Note: This is partially mitigated by the fact that the AI's response is wrapped in a clearly marked bot comment, but prompt injection could still cause the bot to generate misleading or inappropriate analysis.
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated 7 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| max_tokens: 1500, | ||
| temperature: 0.4 | ||
| }) |
There was a problem hiding this comment.
With active PRs mentioned in the description, triggering this workflow on 'opened', 'edited', 'synchronize', and 'reopened' events could result in significant API usage costs. Each workflow run makes an API call to the GitHub Models service with max_tokens set to 1500. The 'synchronize' event in particular fires on every new commit, which could lead to many API calls for actively developed PRs. Consider implementing rate limiting, cooldown periods, or restricting which events trigger the workflow to control costs and avoid hitting potential API rate limits.
| const response = await fetch('https://models.inference.ai.azure.com/chat/completions', { | ||
| method: 'POST', | ||
| headers: { | ||
| 'Content-Type': 'application/json', | ||
| 'Authorization': `Bearer ${process.env.GITHUB_TOKEN}` | ||
| }, | ||
| body: JSON.stringify({ | ||
| model: 'gpt-4o-mini', | ||
| messages: [ | ||
| { | ||
| role: 'system', | ||
| content: `You are a rigorous technical agent reviewing GitHub issues for the "simulation-theory" repository — a research project on simulation theory, mathematics, quantum mechanics, and philosophy. Your job is to carefully read each issue and provide a thorough, structured analysis. | ||
|
|
||
| For each issue produce: | ||
| 1. **Summary** — a concise one-paragraph summary of what the issue is about. | ||
| 2. **Key Points** — bullet list of the most important observations or questions raised. | ||
| 3. **Relevance to Simulation Theory** — how this issue connects to the project's themes. | ||
| 4. **Suggested Actions** — concrete next steps or questions for the author. | ||
|
|
||
| Be rigorous, thoughtful, and constructive. Keep the tone academic and helpful.` | ||
| }, | ||
| { | ||
| role: 'user', | ||
| content: `Please analyze this GitHub issue:\n\n**Title:** ${issueTitle}\n**Author:** ${issueUser}\n**Labels:** ${labels}\n\n**Description:**\n${issueBody}` | ||
| } | ||
| ], | ||
| max_tokens: 1500, | ||
| temperature: 0.4 | ||
| }) | ||
| }); |
There was a problem hiding this comment.
The API call to GitHub Models does not include timeout configuration. If the API becomes unresponsive, the workflow could hang for an extended period before timing out with the default GitHub Actions job timeout (360 minutes). Consider adding a timeout to the fetch call or implementing a reasonable timeout mechanism to fail fast if the API doesn't respond promptly.
| // Call GitHub Models API for AI-powered analysis | ||
| const response = await fetch('https://models.inference.ai.azure.com/chat/completions', { | ||
| method: 'POST', | ||
| headers: { | ||
| 'Content-Type': 'application/json', | ||
| 'Authorization': `Bearer ${process.env.GITHUB_TOKEN}` | ||
| }, | ||
| body: JSON.stringify({ | ||
| model: 'gpt-4o-mini', | ||
| messages: [ | ||
| { | ||
| role: 'system', | ||
| content: `You are a rigorous code and content review agent for the "simulation-theory" repository — a research project on simulation theory, mathematics, quantum mechanics, and philosophy. Your job is to carefully examine each pull request and provide a thorough, structured review. | ||
|
|
||
| For each PR produce: | ||
| 1. **Summary** — a concise one-paragraph summary of the proposed changes. | ||
| 2. **Changed Files Analysis** — observations about the files being modified and why they matter. | ||
| 3. **Potential Concerns** — any risks, conflicts, or issues the reviewer should check. | ||
| 4. **Relevance to Project Goals** — how these changes align with (or diverge from) simulation-theory research. | ||
| 5. **Suggested Actions** — specific things the PR author or reviewers should do before merging. | ||
|
|
||
| Be rigorous, constructive, and precise. Keep the tone academic and professional.` | ||
| }, | ||
| { | ||
| role: 'user', | ||
| content: `Please analyze this pull request:\n\n**Title:** ${prTitle}\n**Author:** ${prUser}\n**Base branch:** ${baseBranch} ← **Head branch:** ${headBranch}\n**Changes:** +${additions} / -${deletions} lines\n**Changed files:** ${changedFiles}\n\n**Description:**\n${prBody}` | ||
| } | ||
| ], | ||
| max_tokens: 1500, | ||
| temperature: 0.4 | ||
| }) | ||
| }); |
There was a problem hiding this comment.
The GITHUB_TOKEN is being exposed in the workflow script environment and used directly in the API call. While this is the intended use for GitHub Models API, there's a risk that the token could be logged or exposed in error messages. The current error handling at lines 91 and 94 uses console.log which could potentially expose sensitive information. Consider sanitizing error messages to ensure tokens are not leaked in logs.
|
|
||
| // Fallback: structured analysis without AI | ||
| if (!analysisText) { | ||
| analysisText = `**Summary**\nIssue #${issueNumber} titled *"${issueTitle}"* was submitted by @${issueUser}. ${issueBody.length > 0 ? 'It contains a description that may include images or text.' : 'No description was provided.'}\n\n**Labels:** ${labels}\n\n**Suggested Actions**\n- Review the content of this issue and add appropriate labels if missing.\n- Respond to the author with any clarifying questions.\n- Link related issues or pull requests if applicable.`; |
There was a problem hiding this comment.
The fallback message construction uses issueBody.length to check if a description exists, but issueBody is already defined as an empty string fallback at line 22 when body is null/undefined. This means issueBody.length will always be greater than 0 (even if it's just the fallback text), making the conditional logic misleading. The condition should check the original body value or compare against the fallback string, not just the length.
.github/workflows/pr-agent.yml
Outdated
| const response = await fetch('https://models.inference.ai.azure.com/chat/completions', { | ||
| method: 'POST', | ||
| headers: { | ||
| 'Content-Type': 'application/json', | ||
| 'Authorization': `Bearer ${process.env.GITHUB_TOKEN}` | ||
| }, | ||
| body: JSON.stringify({ | ||
| model: 'gpt-4o-mini', | ||
| messages: [ | ||
| { | ||
| role: 'system', | ||
| content: `You are a rigorous code and content review agent for the "simulation-theory" repository — a research project on simulation theory, mathematics, quantum mechanics, and philosophy. Your job is to carefully examine each pull request and provide a thorough, structured review. | ||
|
|
||
| For each PR produce: | ||
| 1. **Summary** — a concise one-paragraph summary of the proposed changes. | ||
| 2. **Changed Files Analysis** — observations about the files being modified and why they matter. | ||
| 3. **Potential Concerns** — any risks, conflicts, or issues the reviewer should check. | ||
| 4. **Relevance to Project Goals** — how these changes align with (or diverge from) simulation-theory research. | ||
| 5. **Suggested Actions** — specific things the PR author or reviewers should do before merging. | ||
|
|
||
| Be rigorous, constructive, and precise. Keep the tone academic and professional.` | ||
| }, | ||
| { | ||
| role: 'user', | ||
| content: `Please analyze this pull request:\n\n**Title:** ${prTitle}\n**Author:** ${prUser}\n**Base branch:** ${baseBranch} ← **Head branch:** ${headBranch}\n**Changes:** +${additions} / -${deletions} lines\n**Changed files:** ${changedFiles}\n\n**Description:**\n${prBody}` | ||
| } | ||
| ], | ||
| max_tokens: 1500, | ||
| temperature: 0.4 | ||
| }) | ||
| }); |
There was a problem hiding this comment.
The API call to GitHub Models does not include timeout configuration. If the API becomes unresponsive, the workflow could hang for an extended period before timing out with the default GitHub Actions job timeout (360 minutes). Consider adding a timeout to the fetch call or implementing a reasonable timeout mechanism to fail fast if the API doesn't respond promptly.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated 7 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| on: | ||
| issues: | ||
| types: [opened, edited, reopened, labeled] | ||
|
|
There was a problem hiding this comment.
Triggering on edited and labeled will post a new agent comment every time an issue is tweaked or labels change, which can quickly flood threads. Consider updating an existing bot comment instead of always creating a new one, or restrict triggers to fewer event types.
| const response = await fetch('https://models.inference.ai.azure.com/chat/completions', { | ||
| method: 'POST', | ||
| headers: { | ||
| 'Content-Type': 'application/json', | ||
| 'Authorization': `Bearer ${process.env.GITHUB_TOKEN}` |
There was a problem hiding this comment.
The Models API call isn’t protected against network/transport failures. If fetch(...) throws (DNS/timeout/etc.), the script will error out before reaching the fallback comment. Wrap the request in try/catch and proceed with the non-AI fallback on any exception.
.github/workflows/pr-agent.yml
Outdated
|
|
||
| on: | ||
| pull_request: | ||
| types: [opened, edited, synchronize, reopened] |
There was a problem hiding this comment.
Triggering on edited and synchronize will post a new agent comment on every title/body edit and every push, which can quickly flood PR threads. Consider updating an existing bot comment (find by marker text) instead of always creating a new one, or restrict triggers to fewer event types.
| types: [opened, edited, synchronize, reopened] | |
| types: [opened, reopened] |
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated 6 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| try { | ||
| if (response.ok) { | ||
| const data = await response.json(); | ||
| if (data.choices && data.choices.length > 0 && data.choices[0].message) { | ||
| analysisText = data.choices[0].message.content; | ||
| } else { | ||
| console.log('Unexpected response structure from GitHub Models API:', JSON.stringify(data)); | ||
| } | ||
| } else { | ||
| console.log(`GitHub Models API returned ${response.status}: ${await response.text()}`); | ||
| } | ||
| } catch (error) { | ||
| console.log('Error while calling or parsing response from GitHub Models API, falling back to templated analysis:', error); |
There was a problem hiding this comment.
Duplicate code block detected. Lines 106-120 duplicate the logic from lines 102-120, creating a nested try-catch structure that will never work correctly. The outer try block (starting at line 104) is opened but never closed, and the inner try block (starting at line 107) creates duplicate logic. This will cause the workflow to fail with a syntax error when executed. The duplicate try-catch block starting at line 107 should be removed entirely.
| try { | |
| if (response.ok) { | |
| const data = await response.json(); | |
| if (data.choices && data.choices.length > 0 && data.choices[0].message) { | |
| analysisText = data.choices[0].message.content; | |
| } else { | |
| console.log('Unexpected response structure from GitHub Models API:', JSON.stringify(data)); | |
| } | |
| } else { | |
| console.log(`GitHub Models API returned ${response.status}: ${await response.text()}`); | |
| } | |
| } catch (error) { | |
| console.log('Error while calling or parsing response from GitHub Models API, falling back to templated analysis:', error); | |
| analysisText = data.choices[0].message.content; | |
| } else { | |
| console.log('Unexpected response structure from GitHub Models API:', JSON.stringify(data)); | |
| } | |
| } catch (error) { | |
| console.log('Error while calling or parsing response from GitHub Models API, falling back to templated analysis:', error); | |
| } | |
| } else { | |
| console.log(`GitHub Models API returned ${response.status}: ${await response.text()}`); |
| pull_request_target: | ||
| types: [opened, reopened] | ||
|
|
||
| permissions: | ||
| pull-requests: write | ||
| contents: read | ||
| models: read | ||
|
|
||
| jobs: | ||
| analyze-pr: | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - name: Checkout repository | ||
| uses: actions/checkout@v4 | ||
| with: | ||
| fetch-depth: 0 |
There was a problem hiding this comment.
Using 'pull_request_target' trigger introduces security risks when combined with code checkout. This trigger runs in the context of the base repository with write permissions, even for PRs from forks. When combined with checking out code at line 17 (especially from untrusted forks), this creates a potential security vulnerability where malicious code from a fork could be executed with write permissions to the repository. Consider using the standard 'pull_request' trigger instead, or if 'pull_request_target' is necessary for API access, remove the checkout step since it's not being used for any file operations in this workflow.
| if (!analysisText) { | ||
| analysisText = `**Summary**\nIssue #${issueNumber} titled *"${issueTitle}"* was submitted by @${issueUser}. ${issueBody.length > 0 ? 'It contains a description that may include images or text.' : 'No description was provided.'}\n\n**Labels:** ${labels}\n\n**Suggested Actions**\n- Review the content of this issue and add appropriate labels if missing.\n- Respond to the author with any clarifying questions.\n- Link related issues or pull requests if applicable.`; |
There was a problem hiding this comment.
Fallback comment may expose sensitive information. The fallback analysis at line 76 includes the full issue body in the comment without any filtering or sanitization. If the issue body contains sensitive information (tokens, credentials, personal data), this will be publicly exposed in the agent's comment. Consider truncating or summarizing the issue body reference, or at minimum applying the same sanitization that should be used for AI-generated content.
| let analysisText; | ||
| if (response.ok) { | ||
| try { | ||
| const data = await response.json(); | ||
| if (data.choices && data.choices.length > 0 && data.choices[0].message) { | ||
| try { | ||
| if (response.ok) { | ||
| const data = await response.json(); | ||
| if (data.choices && data.choices.length > 0 && data.choices[0].message) { | ||
| analysisText = data.choices[0].message.content; | ||
| } else { | ||
| console.log('Unexpected response structure from GitHub Models API:', JSON.stringify(data)); | ||
| } | ||
| } else { | ||
| console.log(`GitHub Models API returned ${response.status}: ${await response.text()}`); | ||
| } | ||
| } catch (error) { | ||
| console.log('Error while calling or parsing response from GitHub Models API, falling back to templated analysis:', error); | ||
| } |
There was a problem hiding this comment.
Missing closing brace for the try block that starts at line 104. The code has a try statement at line 104 but no corresponding closing brace, which will cause a JavaScript syntax error when the workflow runs. After the duplicate code block is removed, ensure there's a proper closing brace for the try block and a corresponding catch block to handle any exceptions.
| - name: Checkout repository | ||
| uses: actions/checkout@v4 | ||
| with: | ||
| fetch-depth: 0 | ||
|
|
||
| - name: Collect changed files | ||
| id: changed | ||
| run: | | ||
| # Ensure the base branch ref is available locally (important for fork-based PRs) | ||
| git fetch origin "${{ github.event.pull_request.base.ref }}" --no-tags --prune --depth=1 | ||
|
|
||
| BASE="${{ github.event.pull_request.base.sha }}" | ||
| HEAD="${{ github.event.pull_request.head.sha }}" | ||
|
|
||
| # Compute the list of changed files between base and head; fail explicitly on error | ||
| if ! ALL_FILES=$(git diff --name-only "$BASE" "$HEAD"); then | ||
| echo "Error: failed to compute git diff between $BASE and $HEAD" >&2 | ||
| exit 1 | ||
| fi | ||
|
|
||
| # Count total changed files robustly, even when there are zero files | ||
| TOTAL=$(printf '%s\n' "$ALL_FILES" | sed '/^$/d' | wc -l | tr -d ' ') | ||
| FILES=$(echo "$ALL_FILES" | head -50 | tr '\n' ', ') | ||
| FILES="${FILES%, }" | ||
| if [ "$TOTAL" -gt 50 ]; then | ||
| REMAINING=$(( TOTAL - 50 )) | ||
| FILES="${FILES} (and ${REMAINING} more files)" | ||
| fi | ||
| { | ||
| echo 'files<<EOF' | ||
| echo "$FILES" | ||
| echo 'EOF' | ||
| } >> "$GITHUB_OUTPUT" | ||
|
|
There was a problem hiding this comment.
The checkout step is unnecessary and wasteful. The workflow checks out the entire repository with full history (fetch-depth: 0) but only uses git commands to compute file diffs, which could be obtained more efficiently through the GitHub API. The checkout step also introduces security risks when combined with pull_request_target. Consider removing the checkout step entirely and using github.rest.pulls.listFiles() API to get the list of changed files, which would be safer, faster, and more efficient.
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Collect changed files | |
| id: changed | |
| run: | | |
| # Ensure the base branch ref is available locally (important for fork-based PRs) | |
| git fetch origin "${{ github.event.pull_request.base.ref }}" --no-tags --prune --depth=1 | |
| BASE="${{ github.event.pull_request.base.sha }}" | |
| HEAD="${{ github.event.pull_request.head.sha }}" | |
| # Compute the list of changed files between base and head; fail explicitly on error | |
| if ! ALL_FILES=$(git diff --name-only "$BASE" "$HEAD"); then | |
| echo "Error: failed to compute git diff between $BASE and $HEAD" >&2 | |
| exit 1 | |
| fi | |
| # Count total changed files robustly, even when there are zero files | |
| TOTAL=$(printf '%s\n' "$ALL_FILES" | sed '/^$/d' | wc -l | tr -d ' ') | |
| FILES=$(echo "$ALL_FILES" | head -50 | tr '\n' ', ') | |
| FILES="${FILES%, }" | |
| if [ "$TOTAL" -gt 50 ]; then | |
| REMAINING=$(( TOTAL - 50 )) | |
| FILES="${FILES} (and ${REMAINING} more files)" | |
| fi | |
| { | |
| echo 'files<<EOF' | |
| echo "$FILES" | |
| echo 'EOF' | |
| } >> "$GITHUB_OUTPUT" | |
| - name: Collect changed files | |
| id: changed | |
| uses: actions/github-script@v7 | |
| with: | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| result-encoding: string | |
| script: | | |
| const prNumber = context.payload.pull_request.number; | |
| const perPage = 100; | |
| let page = 1; | |
| const files = []; | |
| while (true) { | |
| const { data } = await github.rest.pulls.listFiles({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| pull_number: prNumber, | |
| per_page: perPage, | |
| page, | |
| }); | |
| if (!data.length) { | |
| break; | |
| } | |
| for (const file of data) { | |
| files.push(file.filename); | |
| } | |
| if (data.length < perPage) { | |
| break; | |
| } | |
| page += 1; | |
| } | |
| const total = files.length; | |
| const limited = files.slice(0, 50); | |
| let summary = limited.join(', '); | |
| if (total > 50) { | |
| const remaining = total - 50; | |
| summary += ` (and ${remaining} more files)`; | |
| } | |
| core.setOutput('files', summary); |
| - name: Analyze issue and post comment | ||
| uses: actions/github-script@v7 | ||
| with: | ||
| github-token: ${{ secrets.GITHUB_TOKEN }} | ||
| script: | | ||
| const issueNumber = context.issue.number; | ||
| const issueTitle = context.payload.issue.title; | ||
| const issueBody = context.payload.issue.body || '(no description provided)'; | ||
| const issueUser = context.payload.issue.user.login; | ||
| const labels = (context.payload.issue.labels || []).map(l => l.name).join(', ') || 'none'; | ||
|
|
||
| // Call GitHub Models API for AI-powered analysis | ||
| const response = await fetch('https://models.inference.ai.azure.com/chat/completions', { | ||
| method: 'POST', | ||
| headers: { | ||
| 'Content-Type': 'application/json', | ||
| 'Authorization': `Bearer ${process.env.GITHUB_TOKEN}` | ||
| }, | ||
| body: JSON.stringify({ | ||
| model: 'gpt-4o-mini', | ||
| messages: [ | ||
| { | ||
| role: 'system', | ||
| content: `You are a rigorous technical agent reviewing GitHub issues for the "simulation-theory" repository — a research project on simulation theory, mathematics, quantum mechanics, and philosophy. Your job is to carefully read each issue and provide a thorough, structured analysis. | ||
|
|
||
| For each issue produce: | ||
| 1. **Summary** — a concise one-paragraph summary of what the issue is about. | ||
| 2. **Key Points** — bullet list of the most important observations or questions raised. | ||
| 3. **Relevance to Simulation Theory** — how this issue connects to the project's themes. | ||
| 4. **Suggested Actions** — concrete next steps or questions for the author. | ||
|
|
||
| Be rigorous, thoughtful, and constructive. Keep the tone academic and helpful.` | ||
| }, | ||
| { | ||
| role: 'user', | ||
| content: `Please analyze this GitHub issue:\n\n**Title:** ${issueTitle}\n**Author:** ${issueUser}\n**Labels:** ${labels}\n\n**Description:**\n${issueBody}` | ||
| } | ||
| ], | ||
| max_tokens: 1500, | ||
| temperature: 0.4 | ||
| }) | ||
| }); | ||
|
|
||
| let analysisText; | ||
| if (response.ok) { | ||
| let data; | ||
| try { | ||
| data = await response.json(); | ||
| } catch (error) { | ||
| console.log('Failed to parse JSON from GitHub Models API response:', error); | ||
| } | ||
|
|
||
| if (data && data.choices && data.choices.length > 0 && data.choices[0].message) { | ||
| analysisText = data.choices[0].message.content; | ||
| } else if (data) { | ||
| console.log('Unexpected response structure from GitHub Models API:', JSON.stringify(data)); | ||
| } | ||
| } else { | ||
| console.log(`GitHub Models API returned ${response.status}: ${await response.text()}`); | ||
| } | ||
|
|
||
| // Fallback: structured analysis without AI | ||
| if (!analysisText) { | ||
| analysisText = `**Summary**\nIssue #${issueNumber} titled *"${issueTitle}"* was submitted by @${issueUser}. ${issueBody.length > 0 ? 'It contains a description that may include images or text.' : 'No description was provided.'}\n\n**Labels:** ${labels}\n\n**Suggested Actions**\n- Review the content of this issue and add appropriate labels if missing.\n- Respond to the author with any clarifying questions.\n- Link related issues or pull requests if applicable.`; | ||
| } | ||
|
|
||
| const marker = '*This comment was generated automatically by the Issue Agent workflow.*'; | ||
| const commentBody = `## 🤖 Agent Analysis\n\n${analysisText}\n\n---\n${marker}`; | ||
|
|
||
| // Look for an existing Issue Agent comment and update it if found to avoid spamming | ||
| const { data: existingComments } = await github.rest.issues.listComments({ | ||
| owner: context.repo.owner, | ||
| repo: context.repo.repo, | ||
| issue_number: issueNumber, | ||
| per_page: 100 | ||
| }); | ||
|
|
||
| const existingAgentComment = existingComments.find(c => | ||
| c.user && | ||
| c.user.type === 'Bot' && | ||
| typeof c.body === 'string' && | ||
| c.body.includes(marker) | ||
| ); | ||
|
|
||
| if (existingAgentComment) { | ||
| await github.rest.issues.updateComment({ | ||
| owner: context.repo.owner, | ||
| repo: context.repo.repo, | ||
| comment_id: existingAgentComment.id, | ||
| body: commentBody | ||
| }); | ||
| } else { | ||
| await github.rest.issues.createComment({ | ||
| owner: context.repo.owner, | ||
| repo: context.repo.repo, | ||
| issue_number: issueNumber, | ||
| body: commentBody | ||
| }); | ||
| } |
There was a problem hiding this comment.
Code duplication between workflows. Both pr-agent.yml and issue-agent.yml contain similar logic for calling the GitHub Models API, handling responses, and posting comments. This duplication makes maintenance harder and could lead to inconsistencies (as evidenced by the missing sanitization in issue-agent.yml). Consider extracting the common logic into a reusable composite action or a shared script that both workflows can call, which would improve maintainability and ensure consistent behavior.
The repo has 45+ open issues and active PRs with no automated triage or analysis. Two GitHub Actions workflows add AI-powered agents that post structured comments on every issue and PR event.
issue-agent.ymlissues:opened,edited,reopened,labeledgpt-4o-mini) with the issue context (title, body, author, labels)pr-agent.ymlpull_request:opened,edited,synchronize,reopenedBoth workflows
GITHUB_TOKEN— no extra secrets required;models: readpermission addeddata.choicesshape before access; fall back to a structured templated comment if the Models API is unavailable or returns an unexpected responsemax_tokens: 1500to avoid truncated responsesOriginal prompt
✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.