fix(runner): add timestamps to Gemini CLI messages in UI #186
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: PR Fixer | |
| on: | |
| # Immediate: when a PR gets the agent-managed label | |
| pull_request: | |
| types: [labeled] | |
| # Immediate: when someone comments @ambient-fix on a PR | |
| issue_comment: | |
| types: [created] | |
| # Cadence: every 4 hours including overnight | |
| schedule: | |
| - cron: '0 */4 * * 1-5' | |
| # Manual: for one-off fixes | |
| workflow_dispatch: | |
| inputs: | |
| pr_number: | |
| description: 'PR number to fix' | |
| required: true | |
| type: number | |
| permissions: | |
| contents: read | |
| pull-requests: read | |
| jobs: | |
| # -- Single PR: triggered by label, comment, or manual dispatch -- | |
| fix-single: | |
| if: >- | |
| (github.event_name == 'pull_request' | |
| && github.event.label.name == 'agent-managed' | |
| && !github.event.pull_request.head.repo.fork) | |
| || github.event_name == 'workflow_dispatch' | |
| || (github.event_name == 'issue_comment' | |
| && github.event.issue.pull_request | |
| && contains(github.event.comment.body, '@ambient-fix') | |
| && (github.event.comment.author_association == 'MEMBER' | |
| || github.event.comment.author_association == 'OWNER' | |
| || github.event.comment.author_association == 'COLLABORATOR')) | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| steps: | |
| - name: Resolve PR number | |
| id: pr | |
| run: | | |
| if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then | |
| echo "number=${{ inputs.pr_number }}" >> $GITHUB_OUTPUT | |
| elif [ "${{ github.event_name }}" = "issue_comment" ]; then | |
| echo "number=${{ github.event.issue.number }}" >> $GITHUB_OUTPUT | |
| else | |
| echo "number=${{ github.event.pull_request.number }}" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Check PR is not a fork (issue_comment) | |
| if: github.event_name == 'issue_comment' | |
| id: fork_check | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| IS_FORK=$(gh pr view ${{ steps.pr.outputs.number }} --repo "${{ github.repository }}" --json isCrossRepository --jq '.isCrossRepository') | |
| if [ "$IS_FORK" = "true" ]; then | |
| echo "Skipping fork PR" | |
| echo "skip=true" >> $GITHUB_OUTPUT | |
| else | |
| echo "skip=false" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Fix PR | |
| if: steps.fork_check.outputs.skip != 'true' | |
| id: session | |
| uses: ambient-code/ambient-action@v0.0.2 | |
| with: | |
| api-url: ${{ secrets.AMBIENT_API_URL }} | |
| api-token: ${{ secrets.AMBIENT_BOT_TOKEN }} | |
| project: ${{ secrets.AMBIENT_PROJECT }} | |
| prompt: >- | |
| Fix PR #${{ steps.pr.outputs.number }} in https://github.com/${{ github.repository }}. | |
| Fetch all PR data, rebase to resolve conflicts, evaluate reviewer | |
| comments (fix valid issues, respond to invalid ones), run lints | |
| and tests, and push the fixes. | |
| repos: >- | |
| [{"url": "https://github.com/${{ github.repository }}", "branch": "main"}] | |
| workflow: >- | |
| {"gitUrl": "https://github.com/ambient-code/workflows", "branch": "main", "path": "internal-workflows/pr-fixer"} | |
| model: claude-sonnet-4-5 | |
| wait: 'true' | |
| timeout: '25' | |
| - name: Session summary | |
| if: always() && steps.fork_check.outputs.skip != 'true' | |
| env: | |
| SESSION_NAME: ${{ steps.session.outputs.session-name }} | |
| SESSION_UID: ${{ steps.session.outputs.session-uid }} | |
| SESSION_PHASE: ${{ steps.session.outputs.session-phase }} | |
| run: | | |
| echo "### PR Fixer — #${{ steps.pr.outputs.number }}" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| if [ -n "$SESSION_NAME" ]; then | |
| echo "- **Session**: \`$SESSION_NAME\`" >> $GITHUB_STEP_SUMMARY | |
| echo "- **UID**: \`$SESSION_UID\`" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Phase**: \`$SESSION_PHASE\`" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "- **Status**: Failed to create session" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| # -- Batch: scheduled cadence for all agent-managed PRs -- | |
| fix-batch: | |
| if: github.event_name == 'schedule' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| outputs: | |
| pr_numbers: ${{ steps.find.outputs.pr_numbers }} | |
| steps: | |
| - name: Find agent-managed PRs that need work | |
| id: find | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| # Get all open PRs with agent-managed label, including last committer | |
| PRS=$(gh pr list --repo "${{ github.repository }}" \ | |
| --label "agent-managed" \ | |
| --state open \ | |
| --json number,updatedAt,isCrossRepository \ | |
| --limit 20) | |
| # Filter: updated in the last 4 hours, not a fork, | |
| # and last update wasn't by the fixer bot itself | |
| NEEDS_WORK=$(echo "$PRS" | python3 -c " | |
| import json, sys | |
| from datetime import datetime, timezone, timedelta | |
| prs = json.load(sys.stdin) | |
| cutoff = datetime.now(timezone.utc) - timedelta(hours=4) | |
| active = [] | |
| for pr in prs: | |
| # Skip fork PRs | |
| if pr.get('isCrossRepository', False): | |
| continue | |
| updated = pr.get('updatedAt', '') | |
| if updated: | |
| try: | |
| dt = datetime.fromisoformat(updated.replace('Z', '+00:00')) | |
| if dt > cutoff: | |
| active.append(pr['number']) | |
| except Exception: | |
| active.append(pr['number']) | |
| print(json.dumps(active)) | |
| ") | |
| echo "pr_numbers=$NEEDS_WORK" >> $GITHUB_OUTPUT | |
| COUNT=$(echo "$NEEDS_WORK" | python3 -c "import json,sys; print(len(json.load(sys.stdin)))") | |
| echo "Found $COUNT agent-managed PRs with recent activity" | |
| fix-each: | |
| needs: fix-batch | |
| if: needs.fix-batch.outputs.pr_numbers != '[]' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| strategy: | |
| matrix: | |
| pr_number: ${{ fromJson(needs.fix-batch.outputs.pr_numbers) }} | |
| steps: | |
| - name: Skip if last update was by the fixer | |
| id: churn_check | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| # Check if the most recent comment is from the fixer bot | |
| LAST_AUTHOR=$(gh api "repos/${{ github.repository }}/issues/${{ matrix.pr_number }}/comments" \ | |
| --jq 'last | .body // ""' 2>/dev/null || echo "") | |
| if echo "$LAST_AUTHOR" | grep -q '<!-- pr-fixer-bot -->'; then | |
| echo "Last activity was fixer bot — skipping" | |
| echo "skip=true" >> $GITHUB_OUTPUT | |
| else | |
| echo "skip=false" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Fix PR #${{ matrix.pr_number }} | |
| if: steps.churn_check.outputs.skip != 'true' | |
| id: session | |
| uses: ambient-code/ambient-action@v0.0.2 | |
| with: | |
| api-url: ${{ secrets.AMBIENT_API_URL }} | |
| api-token: ${{ secrets.AMBIENT_BOT_TOKEN }} | |
| project: ${{ secrets.AMBIENT_PROJECT }} | |
| prompt: >- | |
| Fix PR #${{ matrix.pr_number }} in https://github.com/${{ github.repository }}. | |
| Fetch all PR data, rebase to resolve conflicts, evaluate reviewer | |
| comments (fix valid issues, respond to invalid ones), run lints | |
| and tests, and push the fixes. | |
| repos: >- | |
| [{"url": "https://github.com/${{ github.repository }}", "branch": "main"}] | |
| workflow: >- | |
| {"gitUrl": "https://github.com/ambient-code/workflows", "branch": "main", "path": "internal-workflows/pr-fixer"} | |
| model: claude-sonnet-4-5 | |
| wait: 'false' | |
| - name: Session summary | |
| if: always() && steps.churn_check.outputs.skip != 'true' | |
| env: | |
| SESSION_NAME: ${{ steps.session.outputs.session-name }} | |
| SESSION_UID: ${{ steps.session.outputs.session-uid }} | |
| SESSION_PHASE: ${{ steps.session.outputs.session-phase }} | |
| run: | | |
| echo "### PR Fixer — #${{ matrix.pr_number }}" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| if [ -n "$SESSION_NAME" ]; then | |
| echo "- **Session**: \`$SESSION_NAME\`" >> $GITHUB_STEP_SUMMARY | |
| echo "- **UID**: \`$SESSION_UID\`" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Phase**: \`$SESSION_PHASE\`" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "- **Status**: Failed to create session" >> $GITHUB_STEP_SUMMARY | |
| fi |