Spaces:
Paused
Paused
| name: Issue Analysis | |
| on: | |
| issues: | |
| types: [opened] | |
| workflow_dispatch: | |
| inputs: | |
| issueNumber: | |
| description: 'The number of the issue to analyze manually' | |
| required: true | |
| type: string | |
| jobs: | |
| check-issue: | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| issues: write | |
| env: | |
| # If triggered by 'issues', it uses github.event.issue.number. | |
| # If triggered by 'workflow_dispatch', it uses the number you provided in the form. | |
| ISSUE_NUMBER: ${{ github.event.issue.number || inputs.issueNumber }} | |
| IGNORE_BOT_NAMES_JSON: '["ellipsis-dev"]' | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Bot Setup | |
| id: setup | |
| uses: ./.github/actions/bot-setup | |
| with: | |
| bot-app-id: ${{ secrets.BOT_APP_ID }} | |
| bot-private-key: ${{ secrets.BOT_PRIVATE_KEY }} | |
| opencode-api-key: ${{ secrets.OPENCODE_API_KEY }} | |
| opencode-model: ${{ secrets.OPENCODE_MODEL }} | |
| opencode-fast-model: ${{ secrets.OPENCODE_FAST_MODEL }} | |
| custom-providers-json: ${{ secrets.CUSTOM_PROVIDERS_JSON }} | |
| - name: Add reaction to issue | |
| env: | |
| GH_TOKEN: ${{ steps.setup.outputs.token }} | |
| run: | | |
| gh api \ | |
| --method POST \ | |
| -H "Accept: application/vnd.github+json" \ | |
| /repos/${{ github.repository }}/issues/${{ env.ISSUE_NUMBER }}/reactions \ | |
| -f content='eyes' | |
| - name: Save secure prompt from base branch | |
| run: cp .github/prompts/issue-comment.md /tmp/issue-comment.md | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| with: | |
| token: ${{ steps.setup.outputs.token }} | |
| fetch-depth: 0 # Full history needed for git log, git blame, and other investigation commands | |
| - name: Fetch and Format Full Issue Context | |
| id: issue_details | |
| env: | |
| GH_TOKEN: ${{ steps.setup.outputs.token }} | |
| run: | | |
| # Fetch all necessary data in one call | |
| issue_data=$(gh issue view ${{ env.ISSUE_NUMBER }} --json author,title,body,createdAt,state,comments) | |
| timeline_data=$(gh api "/repos/${{ github.repository }}/issues/${{ env.ISSUE_NUMBER }}/timeline") | |
| # Debug: Output issue_data and timeline_data for inspection | |
| echo "$issue_data" > issue_data.txt | |
| echo "$timeline_data" > timeline_data.txt | |
| # Prepare metadata | |
| author=$(echo "$issue_data" | jq -r .author.login) | |
| created_at=$(echo "$issue_data" | jq -r .createdAt) | |
| state=$(echo "$issue_data" | jq -r .state) | |
| title=$(echo "$issue_data" | jq -r .title) | |
| body=$(echo "$issue_data" | jq -r '.body // "(No description provided)"') | |
| # Prepare comments (exclude ignored bots) | |
| total_issue_comments=$(echo "$issue_data" | jq '((.comments // []) | length)') | |
| echo "Debug: total issue comments before filtering = $total_issue_comments" | |
| comments_filter_err=$(mktemp 2>/dev/null || echo "/tmp/issue_comments_filter_err.log") | |
| if comments=$(echo "$issue_data" | jq -r --argjson ignored "$IGNORE_BOT_NAMES_JSON" 'if (((.comments // []) | length) > 0) then ((.comments[]? | select((.author.login as $login | $ignored | index($login)) | not)) | "- " + (.author.login // "unknown") + " at " + (.createdAt // "N/A") + ":\n" + ((.body // "") | tostring) + "\n") else "No comments have been posted yet." end' 2>"$comments_filter_err"); then | |
| filtered_comments=$(echo "$comments" | grep -c "^- " || true) | |
| filtered_comments=${filtered_comments//[^0-9]/} | |
| [ -z "$filtered_comments" ] && filtered_comments=0 | |
| total_issue_comments=${total_issue_comments//[^0-9]/} | |
| [ -z "$total_issue_comments" ] && total_issue_comments=0 | |
| excluded_comments=$(( total_issue_comments - filtered_comments )) || excluded_comments=0 | |
| echo "✓ Filtered comments: $filtered_comments included, $excluded_comments excluded (ignored bots)" | |
| if [ -s "$comments_filter_err" ]; then | |
| echo "::debug::jq stderr (issue comments) emitted output:" | |
| cat "$comments_filter_err" | |
| fi | |
| else | |
| jq_status=$? | |
| echo "::warning::Issue comment filtering failed (exit $jq_status), using unfiltered data" | |
| if [ -s "$comments_filter_err" ]; then | |
| echo "::warning::jq stderr (issue comments):" | |
| cat "$comments_filter_err" | |
| else | |
| echo "::warning::jq returned no stderr for issue comment filter" | |
| fi | |
| comments=$(echo "$issue_data" | jq -r 'if (((.comments // []) | length) > 0) then ((.comments[]?) | "- " + (.author.login // "unknown") + " at " + (.createdAt // "N/A") + ":\n" + ((.body // "") | tostring) + "\n") else "No comments have been posted yet." end') | |
| excluded_comments=0 | |
| echo "FILTER_ERROR_COMMENTS=true" >> $GITHUB_ENV | |
| fi | |
| rm -f "$comments_filter_err" || true | |
| # Prepare cross-references | |
| references=$(echo "$timeline_data" | jq -r '.[] | select(.event == "cross-referenced") | .source.issue | "- Mentioned in \(.html_url | if contains("/pull/") then "PR" else "Issue" end): #\(.number) - \(.title)"') | |
| if [ -z "$references" ]; then | |
| references="No other issues or PRs have mentioned this thread." | |
| fi | |
| # Define a unique, random delimiter for the main context block | |
| CONTEXT_DELIMITER="GH_ISSUE_CONTEXT_DELIMITER_$(openssl rand -hex 8)" | |
| # Assemble the final context block directly into the environment file line by line | |
| echo "ISSUE_CONTEXT<<$CONTEXT_DELIMITER" >> "$GITHUB_ENV" | |
| echo "Issue: #${{ env.ISSUE_NUMBER }}" >> "$GITHUB_ENV" | |
| echo "Title: $title" >> "$GITHUB_ENV" | |
| echo "Author: $author" >> "$GITHUB_ENV" | |
| echo "Created At: $created_at" >> "$GITHUB_ENV" | |
| echo "State: $state" >> "$GITHUB_ENV" | |
| echo "<issue_body>" >> "$GITHUB_ENV" | |
| echo "$body" >> "$GITHUB_ENV" | |
| echo "</issue_body>" >> "$GITHUB_ENV" | |
| echo "<issue_comments>" >> "$GITHUB_ENV" | |
| echo "$comments" >> "$GITHUB_ENV" | |
| echo "</issue_comments>" >> "$GITHUB_ENV" | |
| echo "<cross_references>" >> "$GITHUB_ENV" | |
| echo "$references" >> "$GITHUB_ENV" | |
| echo "</cross_references>" >> "$GITHUB_ENV" | |
| echo "$CONTEXT_DELIMITER" >> "$GITHUB_ENV" | |
| # Also export author for the acknowledgment comment | |
| echo "ISSUE_AUTHOR=$author" >> $GITHUB_ENV | |
| - name: Analyze issue and suggest resolution | |
| env: | |
| GITHUB_TOKEN: ${{ steps.setup.outputs.token }} | |
| ISSUE_CONTEXT: ${{ env.ISSUE_CONTEXT }} | |
| ISSUE_NUMBER: ${{ env.ISSUE_NUMBER }} | |
| ISSUE_AUTHOR: ${{ env.ISSUE_AUTHOR }} | |
| OPENCODE_PERMISSION: | | |
| { | |
| "bash": { | |
| "gh*": "allow", | |
| "git*": "allow", | |
| "jq*": "allow" | |
| }, | |
| "webfetch": "deny" | |
| } | |
| run: | | |
| # Only substitute the variables we intend; leave example $vars and secrets intact | |
| VARS='${ISSUE_CONTEXT} ${ISSUE_NUMBER} ${ISSUE_AUTHOR}' | |
| envsubst "$VARS" < /tmp/issue-comment.md | opencode run --share - |