fix: make agent:claude autopilot implementation-only #5
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: Org Agent Autopilot | ||
| on: | ||
| workflow_call: | ||
| inputs: | ||
| issue_number: | ||
| description: 'Issue number to work on' | ||
| required: true | ||
| type: number | ||
| action: | ||
| description: 'Label action (labeled or unlabeled)' | ||
| required: true | ||
| type: string | ||
| label_name: | ||
| description: 'Label that triggered this workflow' | ||
| required: true | ||
| type: string | ||
| project_id: | ||
| description: 'GitHub Projects board ID' | ||
| required: false | ||
| type: string | ||
| default: '' | ||
| status_field_id: | ||
| description: 'Status field ID in project' | ||
| required: false | ||
| type: string | ||
| default: '' | ||
| status_in_progress_id: | ||
| description: 'Status option ID for "In Progress"' | ||
| required: false | ||
| type: string | ||
| default: '' | ||
| status_review_id: | ||
| description: 'Status option ID for "Review"' | ||
| required: false | ||
| type: string | ||
| default: '' | ||
| secrets: | ||
| CLAUDE_CODE_OAUTH_TOKEN: | ||
| description: 'Claude Code OAuth token' | ||
| required: true | ||
| GH_TOKEN: | ||
| description: 'GitHub token for API access' | ||
| required: true | ||
| permissions: | ||
| contents: write | ||
| pull-requests: write | ||
| issues: write | ||
| actions: read | ||
| id-token: write | ||
| concurrency: | ||
| group: autopilot-${{ github.repository }}-${{ inputs.issue_number }} | ||
| cancel-in-progress: false # Don't cancel ongoing work | ||
| jobs: | ||
| # Check if issue is ready for implementation | ||
| check-readiness: | ||
| if: inputs.action == 'labeled' && inputs.label_name == 'agent: claude' | ||
| runs-on: arc-happyvertical | ||
| timeout-minutes: 5 | ||
| outputs: | ||
| ready: ${{ steps.check.outputs.ready }} | ||
| missing: ${{ steps.check.outputs.missing }} | ||
| steps: | ||
| - name: Check Definition of Ready | ||
| id: check | ||
| env: | ||
| GH_TOKEN: ${{ secrets.GH_TOKEN }} | ||
| run: | | ||
| echo "Checking Definition of Ready for issue #${{ inputs.issue_number }}..." | ||
| ISSUE=$(gh issue view ${{ inputs.issue_number }} --json labels,body,title,state) | ||
| STATE=$(echo "$ISSUE" | jq -r '.state') | ||
| if [ "$STATE" != "OPEN" ]; then | ||
| echo "ready=false" >> $GITHUB_OUTPUT | ||
| echo "missing=Issue is closed" >> $GITHUB_OUTPUT | ||
| exit 0 | ||
| fi | ||
| LABELS=$(echo "$ISSUE" | jq -r '.labels[].name' 2>/dev/null || echo "") | ||
| HAS_TYPE=$(echo "$LABELS" | grep -c "^type:" || true) | ||
| HAS_PRIORITY=$(echo "$LABELS" | grep -c "^priority:" || true) | ||
| HAS_SIZE=$(echo "$LABELS" | grep -c "^size:" || true) | ||
| BODY_LENGTH=$(echo "$ISSUE" | jq '.body | length // 0') | ||
| MISSING="" | ||
| if [ "$HAS_TYPE" -eq 0 ]; then | ||
| MISSING="${MISSING}type label, " | ||
| fi | ||
| if [ "$HAS_PRIORITY" -eq 0 ]; then | ||
| MISSING="${MISSING}priority label, " | ||
| fi | ||
| if [ "$HAS_SIZE" -eq 0 ]; then | ||
| MISSING="${MISSING}size label, " | ||
| fi | ||
| if [ "$BODY_LENGTH" -lt 50 ]; then | ||
| MISSING="${MISSING}detailed description, " | ||
| fi | ||
| if [ -n "$MISSING" ]; then | ||
| echo "ready=false" >> $GITHUB_OUTPUT | ||
| echo "missing=${MISSING%, }" >> $GITHUB_OUTPUT | ||
| else | ||
| echo "ready=true" >> $GITHUB_OUTPUT | ||
| echo "missing=" >> $GITHUB_OUTPUT | ||
| fi | ||
| # NOT READY: Reject and remove label | ||
| not-ready: | ||
| needs: check-readiness | ||
| if: needs.check-readiness.outputs.ready == 'false' | ||
| runs-on: arc-happyvertical | ||
| timeout-minutes: 5 | ||
| steps: | ||
| - name: Post Not Ready Comment | ||
| env: | ||
| GH_TOKEN: ${{ secrets.GH_TOKEN }} | ||
| run: | | ||
| gh issue comment ${{ inputs.issue_number }} --body "## Issue Not Ready for Autopilot | ||
| The \`agent: claude\` label is for **fully autonomous implementation only**. | ||
| This issue is missing: **${{ needs.check-readiness.outputs.missing }}** | ||
| ### To use autopilot: | ||
| 1. Ensure the issue has \`type:\`, \`priority:\`, and \`size:\` labels | ||
| 2. Provide a detailed description (50+ characters) | ||
| 3. Re-apply the \`agent: claude\` label | ||
| ### Alternatives: | ||
| - **Run Issue Checkup** workflow to have Claude triage/prepare issues | ||
| - **Use \`@claude\`** in a comment for interactive assistance | ||
| - **Manually prepare** the issue, then re-apply the label | ||
| --- | ||
| *Removing \`agent: claude\` label*" | ||
| - name: Remove Label | ||
| env: | ||
| GH_TOKEN: ${{ secrets.GH_TOKEN }} | ||
| run: | | ||
| gh issue edit ${{ inputs.issue_number }} --remove-label "agent: claude" | ||
| # IMPLEMENT: If ready, Claude implements the issue | ||
| implement: | ||
| needs: check-readiness | ||
| if: needs.check-readiness.outputs.ready == 'true' | ||
| runs-on: arc-happyvertical | ||
| timeout-minutes: 60 | ||
| steps: | ||
| - name: Checkout repository | ||
| uses: actions/checkout@v4 | ||
| with: | ||
| fetch-depth: 0 | ||
| token: ${{ secrets.GH_TOKEN }} | ||
| - name: Configure Git | ||
| run: | | ||
| git config user.name "claude[bot]" | ||
| git config user.email "claude[bot]@users.noreply.github.com" | ||
| - name: Setup Node.js | ||
| uses: actions/setup-node@v4 | ||
| with: | ||
| node-version: '24' | ||
| - name: Setup pnpm | ||
| uses: pnpm/action-setup@v4 | ||
| with: | ||
| version: 9 | ||
| - name: Install dependencies | ||
| run: | | ||
| if [ -f "pnpm-lock.yaml" ]; then | ||
| pnpm install --frozen-lockfile | ||
| elif [ -f "package-lock.json" ]; then | ||
| npm ci | ||
| elif [ -f "package.json" ]; then | ||
| pnpm install || npm install | ||
| fi | ||
| continue-on-error: true | ||
| - name: Post Start Comment | ||
| env: | ||
| GH_TOKEN: ${{ secrets.GH_TOKEN }} | ||
| run: | | ||
| gh issue comment ${{ inputs.issue_number }} --body "## Implementation Started | ||
| This issue is ready and I'm beginning implementation. | ||
| **Status**: In Progress | ||
| **Started**: $(date -u +"%Y-%m-%dT%H:%M:%SZ") | ||
| I'll post updates as I progress. | ||
| --- | ||
| *Automated implementation by Claude*" | ||
| - name: Update Project Board to In Progress | ||
| if: inputs.project_id != '' && inputs.status_in_progress_id != '' | ||
| env: | ||
| GH_TOKEN: ${{ secrets.GH_TOKEN }} | ||
| run: | | ||
| ISSUE_NODE_ID=$(gh api graphql -f query=' | ||
| query($owner: String!, $repo: String!, $number: Int!) { | ||
| repository(owner: $owner, name: $repo) { | ||
| issue(number: $number) { | ||
| id | ||
| projectItems(first: 10) { | ||
| nodes { | ||
| id | ||
| project { id } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| ' -f owner="${{ github.repository_owner }}" -f repo="${{ github.event.repository.name }}" -F number=${{ inputs.issue_number }} --jq '.data.repository.issue') | ||
| ITEM_ID=$(echo "$ISSUE_NODE_ID" | jq -r ".projectItems.nodes[] | select(.project.id == \"${{ inputs.project_id }}\") | .id" 2>/dev/null || echo "") | ||
| if [ -n "$ITEM_ID" ]; then | ||
| gh api graphql -f query=' | ||
| mutation($projectId: ID!, $itemId: ID!, $fieldId: ID!, $optionId: String!) { | ||
| updateProjectV2ItemFieldValue(input: { | ||
| projectId: $projectId | ||
| itemId: $itemId | ||
| fieldId: $fieldId | ||
| value: { singleSelectOptionId: $optionId } | ||
| }) { | ||
| projectV2Item { id } | ||
| } | ||
| } | ||
| ' -f projectId="${{ inputs.project_id }}" -f itemId="$ITEM_ID" -f fieldId="${{ inputs.status_field_id }}" -f optionId="${{ inputs.status_in_progress_id }}" || true | ||
| fi | ||
| - name: Run Claude Implementation | ||
| id: implement | ||
| uses: anthropics/claude-code-action@v1 | ||
| with: | ||
| claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} | ||
| timeout_minutes: 55 | ||
| track_progress: true | ||
| prompt: | | ||
| REPO: ${{ github.repository }} | ||
| ISSUE_NUMBER: ${{ inputs.issue_number }} | ||
| You are implementing issue #${{ inputs.issue_number }} autonomously. | ||
| ## Workflow | ||
| 1. **Read the issue**: `gh issue view ${{ inputs.issue_number }}` | ||
| 2. **Read CLAUDE.md** for repository guidelines | ||
| 3. **Create branch**: `git checkout -b feat/issue-${{ inputs.issue_number }}-[description]` | ||
| 4. **Implement** following existing patterns | ||
| 5. **Write/update tests** | ||
| 6. **Quality checks**: `pnpm typecheck && pnpm lint && pnpm test && pnpm build` | ||
| 7. **Commit**: `git commit -m "feat(scope): description\n\nCloses #${{ inputs.issue_number }}"` | ||
| 8. **Push**: `git push -u origin [branch]` | ||
| 9. **Create PR**: `gh pr create --title "..." --body "..."` | ||
| 10. **Post completion** comment on the issue | ||
| If blocked, post a comment explaining why. | ||
| allowed_tools: | | ||
| Bash(git:*),Bash(gh pr:*),Bash(gh issue:*),Bash(pnpm:*),Bash(npm:*),Bash(npx:*),Bash(node:*),Bash(bun:*),Read,Write,Edit,Glob,Grep,Bash(ls:*),Bash(cat:*),Bash(mkdir:*),Bash(cp:*),Bash(mv:*),Bash(rm:*) | ||
| - name: Post Failure Comment | ||
| if: failure() | ||
| env: | ||
| GH_TOKEN: ${{ secrets.GH_TOKEN }} | ||
| run: | | ||
| gh issue comment ${{ inputs.issue_number }} --body "## Implementation Failed | ||
| I encountered an error while implementing this issue. | ||
| [View workflow run](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}) | ||
| The \`agent: claude\` label remains - remove it if you want to stop automated attempts. | ||
| --- | ||
| *Automated implementation by Claude*" | ||