Skip to content

feat(automation): Add Amber issue-to-PR automation system #43

feat(automation): Add Amber issue-to-PR automation system

feat(automation): Add Amber issue-to-PR automation system #43

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.`
});