feat(automation): Add Amber issue-to-PR automation system #43
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: Amber Issue-to-PR Handler | |
| on: | |
| issues: | |
| types: [labeled, opened] | |
| issue_comment: | |
| types: [created] | |
| permissions: | |
| contents: write | |
| issues: write | |
| pull-requests: write | |
| jobs: | |
| amber-handler: | |
| runs-on: ubuntu-latest | |
| # Only run for specific labels or commands | |
| if: | | |
| (github.event.label.name == 'amber:auto-fix' || | |
| github.event.label.name == 'amber:refactor' || | |
| github.event.label.name == 'amber:test-coverage' || | |
| contains(github.event.comment.body, '/amber execute')) | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Determine Amber action type | |
| id: action-type | |
| env: | |
| LABEL_NAME: ${{ github.event.label.name }} | |
| COMMENT_BODY: ${{ github.event.comment.body }} | |
| run: | | |
| # Parse label or comment to determine action | |
| if [[ "$LABEL_NAME" == "amber:auto-fix" ]]; then | |
| echo "type=auto-fix" >> $GITHUB_OUTPUT | |
| echo "severity=low" >> $GITHUB_OUTPUT | |
| elif [[ "$LABEL_NAME" == "amber:refactor" ]]; then | |
| echo "type=refactor" >> $GITHUB_OUTPUT | |
| echo "severity=medium" >> $GITHUB_OUTPUT | |
| elif [[ "$LABEL_NAME" == "amber:test-coverage" ]]; then | |
| echo "type=test-coverage" >> $GITHUB_OUTPUT | |
| echo "severity=medium" >> $GITHUB_OUTPUT | |
| elif [[ "$COMMENT_BODY" == *"/amber execute"* ]]; then | |
| echo "type=execute-proposal" >> $GITHUB_OUTPUT | |
| echo "severity=medium" >> $GITHUB_OUTPUT | |
| else | |
| echo "type=unknown" >> $GITHUB_OUTPUT | |
| exit 1 | |
| fi | |
| - name: Extract issue details | |
| id: issue-details | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const issue = context.payload.issue; | |
| // Parse issue body for Amber-compatible context | |
| const body = issue.body || ''; | |
| // Extract file paths mentioned in issue (safely) | |
| const filePattern = /(?:File|Path):\s*`?([^\s`]+)`?/gi; | |
| const files = [...body.matchAll(filePattern)].map(m => m[1]); | |
| // Extract specific instructions (safely) | |
| const instructionPattern = /(?:Instructions?|Task):\s*\n([\s\S]*?)(?:\n#{2,}|\n---|\n\*\*|$)/i; | |
| const instructionMatch = body.match(instructionPattern); | |
| const instructions = instructionMatch ? instructionMatch[1].trim() : ''; | |
| // Set outputs (all values are safe - no command injection risk) | |
| core.setOutput('issue_number', issue.number); | |
| core.setOutput('issue_title', issue.title); | |
| core.setOutput('issue_body', body); | |
| core.setOutput('files', JSON.stringify(files)); | |
| core.setOutput('instructions', instructions || issue.title); | |
| console.log('Parsed issue:', { | |
| number: issue.number, | |
| title: issue.title, | |
| files: files, | |
| instructions: instructions || issue.title | |
| }); | |
| - name: Create Amber agent prompt | |
| id: create-prompt | |
| env: | |
| ISSUE_NUMBER: ${{ steps.issue-details.outputs.issue_number }} | |
| ISSUE_TITLE: ${{ steps.issue-details.outputs.issue_title }} | |
| ISSUE_INSTRUCTIONS: ${{ steps.issue-details.outputs.instructions }} | |
| ISSUE_FILES: ${{ steps.issue-details.outputs.files }} | |
| ACTION_TYPE: ${{ steps.action-type.outputs.type }} | |
| ACTION_SEVERITY: ${{ steps.action-type.outputs.severity }} | |
| run: | | |
| # Create prompt file using environment variables (safe from injection) | |
| cat > /tmp/amber-prompt.md <<'EOF' | |
| # Amber Agent Task | |
| You are Amber, an automated background agent. Execute the following task based on issue context. | |
| ## Issue Details | |
| **Issue Number:** #${ISSUE_NUMBER} | |
| **Title:** ${ISSUE_TITLE} | |
| **Action Type:** ${ACTION_TYPE} | |
| **Severity:** ${ACTION_SEVERITY} | |
| ## Instructions | |
| ${ISSUE_INSTRUCTIONS} | |
| ## Files (if specified) | |
| ${ISSUE_FILES} | |
| ## Your Mission | |
| Based on the action type, perform the following: | |
| ### For `auto-fix` type: | |
| 1. Identify the specific linting/formatting issues mentioned | |
| 2. Run appropriate formatters (gofmt, black, prettier, etc.) | |
| 3. Fix any trivial issues (unused imports, spacing, etc.) | |
| 4. Ensure all changes pass existing tests | |
| 5. Create a clean commit with conventional format | |
| ### For `refactor` type: | |
| 1. Analyze the current code structure | |
| 2. Implement the refactoring as described in the issue | |
| 3. Ensure backward compatibility (no breaking changes) | |
| 4. Add/update tests to cover refactored code | |
| 5. Verify all existing tests still pass | |
| ### For `test-coverage` type: | |
| 1. Analyze current test coverage for specified files | |
| 2. Identify untested code paths | |
| 3. Write contract tests following project standards (see CLAUDE.md) | |
| 4. Ensure tests follow table-driven test pattern (Go) or pytest patterns (Python) | |
| 5. Verify all new tests pass | |
| ### For `execute-proposal` type: | |
| 1. Read the full issue body for the proposed implementation | |
| 2. Execute the changes as specified in the proposal | |
| 3. Follow the risk assessment and rollback plan provided | |
| 4. Ensure all testing strategies are implemented | |
| ## Requirements | |
| - Follow all standards in CLAUDE.md | |
| - Use conventional commit format: `type(scope): message` | |
| - Run all linters BEFORE committing: | |
| - Go: `gofmt -w .`, `golangci-lint run` | |
| - Python: `black .`, `isort .`, `flake8` | |
| - TypeScript: `npm run lint` | |
| - Ensure ALL tests pass: `make test` (if Makefile exists) | |
| - Create focused, minimal changes (no scope creep) | |
| ## Success Criteria | |
| - All linters pass with 0 warnings | |
| - All existing tests pass | |
| - New code follows project conventions | |
| - Commit message is clear and follows conventional format | |
| - Changes are focused on issue scope only | |
| ## Output Format | |
| After completing the work: | |
| 1. Commit changes with conventional commit format | |
| 2. Include reference to issue number in commit body | |
| 3. Run all validation checks | |
| Execute this task now. | |
| EOF | |
| # Substitute environment variables into the template | |
| envsubst < /tmp/amber-prompt.md > /tmp/amber-prompt-final.md | |
| echo "prompt_file=/tmp/amber-prompt-final.md" >> $GITHUB_OUTPUT | |
| # Show prompt for debugging (safe - no secrets) | |
| echo "=== Generated Prompt ===" | |
| cat /tmp/amber-prompt-final.md | |
| - name: Execute Amber agent via Claude Code | |
| id: amber-execute | |
| env: | |
| ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} | |
| ISSUE_NUMBER: ${{ steps.issue-details.outputs.issue_number }} | |
| ISSUE_TITLE: ${{ steps.issue-details.outputs.issue_title }} | |
| ACTION_TYPE: ${{ steps.action-type.outputs.type }} | |
| PROMPT_FILE: ${{ steps.create-prompt.outputs.prompt_file }} | |
| uses: anthropics/claude-code-action@v1 | |
| with: | |
| anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} | |
| prompt_file: /tmp/amber-prompt-final.md | |
| auto_commit: true | |
| commit_message: | | |
| fix(amber): address issue #${{ steps.issue-details.outputs.issue_number }} | |
| Automated fix by Amber agent for: ${{ steps.issue-details.outputs.issue_title }} | |
| Action type: ${{ steps.action-type.outputs.type }} | |
| 🤖 Generated with Amber Background Agent | |
| Resolves: #${{ steps.issue-details.outputs.issue_number }} | |
| - name: Create Pull Request | |
| if: success() | |
| env: | |
| ISSUE_NUMBER: ${{ steps.issue-details.outputs.issue_number }} | |
| ISSUE_TITLE: ${{ steps.issue-details.outputs.issue_title }} | |
| ACTION_TYPE: ${{ steps.action-type.outputs.type }} | |
| GITHUB_REPOSITORY: ${{ github.repository }} | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| # Configure git | |
| git config user.name "Amber Agent" | |
| git config user.email "amber@ambient-code.ai" | |
| # Check if changes were made | |
| if git diff --quiet HEAD; then | |
| echo "No changes made by Amber" | |
| echo "has_changes=false" >> $GITHUB_OUTPUT | |
| exit 0 | |
| fi | |
| # Create feature branch (sanitize title for branch name) | |
| SANITIZED_TITLE=$(echo "$ISSUE_TITLE" | tr '[:upper:]' '[:lower:]' | tr -cd '[:alnum:]-' | cut -c1-50) | |
| BRANCH_NAME="amber/issue-${ISSUE_NUMBER}-${SANITIZED_TITLE}" | |
| # Create and push branch | |
| git checkout -b "$BRANCH_NAME" | |
| git push -u origin "$BRANCH_NAME" | |
| # Create PR using gh CLI | |
| gh pr create \ | |
| --title "[Amber] Fix: ${ISSUE_TITLE}" \ | |
| --body "## Automated Fix by Amber Agent | |
| This PR addresses issue #${ISSUE_NUMBER} using the Amber background agent. | |
| ### Changes Summary | |
| - **Action Type:** ${ACTION_TYPE} | |
| - **Triggered by:** Issue label/command | |
| ### Pre-merge Checklist | |
| - [ ] All linters pass | |
| - [ ] All tests pass | |
| - [ ] Changes follow project conventions (CLAUDE.md) | |
| - [ ] No scope creep beyond issue description | |
| ### Reviewer Notes | |
| This PR was automatically generated. Please review: | |
| 1. Code quality and adherence to standards | |
| 2. Test coverage for changes | |
| 3. No unintended side effects | |
| --- | |
| 🤖 Generated with [Amber Background Agent](https://github.com/${GITHUB_REPOSITORY}/blob/main/.claude/amber-config.yml) | |
| Closes #${ISSUE_NUMBER}" \ | |
| --label "amber-generated,auto-fix,${ACTION_TYPE}" \ | |
| --base main | |
| # Get PR number and comment on issue | |
| PR_NUMBER=$(gh pr list --head "$BRANCH_NAME" --json number --jq '.[0].number') | |
| gh issue comment "${ISSUE_NUMBER}" --body "🤖 Amber has created a pull request to address this issue: #${PR_NUMBER} | |
| The changes are ready for review. All automated checks will run on the PR." | |
| - name: Report failure | |
| if: failure() | |
| env: | |
| ISSUE_NUMBER: ${{ steps.issue-details.outputs.issue_number }} | |
| ACTION_TYPE: ${{ steps.action-type.outputs.type }} | |
| RUN_ID: ${{ github.run_id }} | |
| GITHUB_SERVER_URL: ${{ github.server_url }} | |
| GITHUB_REPOSITORY: ${{ github.repository }} | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const issueNumber = parseInt(process.env.ISSUE_NUMBER); | |
| const actionType = process.env.ACTION_TYPE; | |
| const runId = process.env.RUN_ID; | |
| const serverUrl = process.env.GITHUB_SERVER_URL; | |
| const repository = process.env.GITHUB_REPOSITORY; | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: issueNumber, | |
| body: `⚠️ Amber encountered an error while processing this issue. | |
| **Action Type:** ${actionType} | |
| **Workflow Run:** ${serverUrl}/${repository}/actions/runs/${runId} | |
| Please review the workflow logs for details. You may need to: | |
| 1. Check if the issue description provides sufficient context | |
| 2. Verify the specified files exist | |
| 3. Ensure the changes are feasible for automation | |
| Manual intervention may be required for complex changes.` | |
| }); |