Claude Continuous Work #2
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: Claude Continuous Work | |
| on: | |
| schedule: | |
| # Run every 2 hours during work hours (UTC) | |
| - cron: '0 */2 * * *' | |
| workflow_dispatch: | |
| inputs: | |
| max_issues: | |
| description: 'Maximum number of issues to process' | |
| required: false | |
| default: '3' | |
| concurrency: | |
| group: claude-continuous | |
| cancel-in-progress: false | |
| jobs: | |
| find-work: | |
| runs-on: ubuntu-latest | |
| outputs: | |
| issues: ${{ steps.find.outputs.issues }} | |
| has_work: ${{ steps.find.outputs.has_work }} | |
| steps: | |
| - name: Find open Claude issues | |
| id: find | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| MAX_ISSUES="${{ github.event.inputs.max_issues || '3' }}" | |
| # Find open issues with 'claude' label that don't have linked PRs | |
| ISSUES=$(gh issue list \ | |
| --repo ${{ github.repository }} \ | |
| --label "claude" \ | |
| --state open \ | |
| --limit "$MAX_ISSUES" \ | |
| --json number,title,labels,updatedAt \ | |
| --jq '[.[] | select(.labels | map(.name) | any(. == "in-progress" or . == "blocked") | not)] | .[0:'"$MAX_ISSUES"']') | |
| echo "Found issues: $ISSUES" | |
| # Check if any issues found | |
| ISSUE_COUNT=$(echo "$ISSUES" | jq 'length') | |
| if [ "$ISSUE_COUNT" -gt 0 ]; then | |
| echo "has_work=true" >> $GITHUB_OUTPUT | |
| echo "issues=$ISSUES" >> $GITHUB_OUTPUT | |
| echo "✅ Found $ISSUE_COUNT issue(s) to work on" | |
| else | |
| echo "has_work=false" >> $GITHUB_OUTPUT | |
| echo "issues=[]" >> $GITHUB_OUTPUT | |
| echo "ℹ️ No open claude-labeled issues found" | |
| fi | |
| process-issue: | |
| needs: find-work | |
| if: needs.find-work.outputs.has_work == 'true' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 45 | |
| permissions: | |
| contents: write | |
| issues: write | |
| pull-requests: write | |
| actions: read | |
| id-token: write | |
| strategy: | |
| matrix: | |
| issue: ${{ fromJson(needs.find-work.outputs.issues) }} | |
| max-parallel: 1 # Process one issue at a time | |
| fail-fast: false | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Check if issue already has PR | |
| id: check-pr | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| ISSUE_NUM="${{ matrix.issue.number }}" | |
| # Check for linked PRs | |
| PR_COUNT=$(gh pr list \ | |
| --repo ${{ github.repository }} \ | |
| --search "closes #$ISSUE_NUM OR fixes #$ISSUE_NUM OR resolves #$ISSUE_NUM" \ | |
| --json number \ | |
| --jq 'length') | |
| if [ "$PR_COUNT" -gt 0 ]; then | |
| echo "skip=true" >> $GITHUB_OUTPUT | |
| echo "Issue #$ISSUE_NUM already has PR, skipping" | |
| else | |
| echo "skip=false" >> $GITHUB_OUTPUT | |
| echo "Issue #$ISSUE_NUM ready for work" | |
| fi | |
| - name: Mark issue as in-progress | |
| if: steps.check-pr.outputs.skip != 'true' | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| ISSUE_NUM="${{ matrix.issue.number }}" | |
| gh issue edit "$ISSUE_NUM" --add-label "in-progress" | |
| gh issue comment "$ISSUE_NUM" --body "🤖 Claude is now working on this issue automatically." | |
| - name: Determine complexity and model | |
| id: complexity | |
| if: steps.check-pr.outputs.skip != 'true' | |
| run: | | |
| MODEL="claude-sonnet-4-5" | |
| # Check labels for complexity | |
| LABELS='${{ toJson(matrix.issue.labels.*.name) }}' | |
| if echo "$LABELS" | grep -qE "complex|architecture|refactor|performance"; then | |
| MODEL="claude-opus-4-6" | |
| echo "🔍 Complex issue detected, using Opus" | |
| elif echo "$LABELS" | grep -qE "bug|hotfix|simple|documentation"; then | |
| MODEL="claude-sonnet-4-5" | |
| echo "⚡ Simple issue detected, using Sonnet" | |
| else | |
| # Check issue body length | |
| ISSUE_BODY='${{ matrix.issue.title }} ${{ matrix.issue.body }}' | |
| BODY_LENGTH=$(echo "$ISSUE_BODY" | wc -c) | |
| if [ "$BODY_LENGTH" -gt 1000 ]; then | |
| MODEL="claude-opus-4-6" | |
| echo "📊 Long issue description, using Opus" | |
| else | |
| MODEL="claude-sonnet-4-5" | |
| echo "📊 Standard issue, using Sonnet" | |
| fi | |
| fi | |
| echo "model=$MODEL" >> $GITHUB_OUTPUT | |
| - name: Work on issue with Claude | |
| id: claude | |
| if: steps.check-pr.outputs.skip != 'true' | |
| uses: anthropics/claude-code-action@v1 | |
| with: | |
| claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} | |
| additional_permissions: | | |
| actions: read | |
| prompt: | | |
| # Continuous Work Mode - Issue #${{ matrix.issue.number }} | |
| You are working in **continuous automation mode** on this issue. Your goal is to: | |
| 1. **Understand** the issue completely | |
| 2. **Implement** a complete solution | |
| 3. **Test** your implementation | |
| 4. **Create a PR** when done | |
| ## Issue Details | |
| - **Issue**: #${{ matrix.issue.number }} | |
| - **Title**: ${{ matrix.issue.title }} | |
| - **Repository**: ${{ github.repository }} | |
| - **Model**: ${{ steps.complexity.outputs.model }} | |
| ## Work Instructions | |
| ### Step 1: Analyze | |
| - Read the issue description carefully | |
| - Check CLAUDE.md and project docs for guidelines | |
| - Identify all requirements and acceptance criteria | |
| - Plan your implementation approach | |
| ### Step 2: Implement | |
| - Write clean, well-tested code | |
| - Follow project conventions and patterns | |
| - Add appropriate tests | |
| - Ensure all existing tests still pass | |
| ### Step 3: Verify | |
| - Run all tests: `npm test`, `flutter test`, `go test`, `pytest`, etc. | |
| - Check for linting errors | |
| - Verify the solution meets all requirements | |
| ### Step 4: Deliver | |
| - Commit your changes with a clean commit message | |
| - Create a PR that closes this issue: | |
| ```bash | |
| gh pr create --title "Fix: <descriptive title>" --body "## Summary | |
| <what you implemented> | |
| ## Changes | |
| - <change 1> | |
| - <change 2> | |
| ## Testing | |
| - <how you tested> | |
| Closes #${{ matrix.issue.number }}" | |
| ``` | |
| ## Important Rules | |
| - Work iteratively but aim for completion | |
| - If blocked, add "blocked" label and comment explaining why | |
| - If you need clarification, add "needs-info" label and ask questions | |
| - Do NOT add co-author attributions to commits | |
| - Follow the commit message format: imperative, max 72 chars | |
| ## Context | |
| Check for project-specific guidelines in: | |
| - CLAUDE.md | |
| - .claude/ directory | |
| - docs/ directory | |
| - CONTRIBUTING.md | |
| Now, begin working on this issue systematically. | |
| # track_progress not supported for schedule/workflow_dispatch events | |
| track_progress: false | |
| claude_args: | | |
| --model "${{ steps.complexity.outputs.model }}" | |
| --max-turns ${{ steps.complexity.outputs.model == 'claude-opus-4-6' && '150' || '100' }} | |
| --allowedTools "Bash(git:*),Bash(gh issue:*),Bash(gh pr:*),Bash(gh repo:*),Bash(gh api:*),Bash(npm:*),Bash(npx:*),Bash(flutter:*),Bash(dart:*),Bash(pytest:*),Bash(go:*),Edit,Write,Read,Glob,Grep,LS,WebSearch,WebFetch,Task" | |
| --timeout ${{ steps.complexity.outputs.model == 'claude-opus-4-6' && '2700000' || '1800000' }} | |
| - name: Check completion status | |
| if: always() && steps.check-pr.outputs.skip != 'true' | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| ISSUE_NUM="${{ matrix.issue.number }}" | |
| # Check if PR was created | |
| PR_COUNT=$(gh pr list \ | |
| --repo ${{ github.repository }} \ | |
| --search "closes #$ISSUE_NUM OR fixes #$ISSUE_NUM OR resolves #$ISSUE_NUM" \ | |
| --json number \ | |
| --jq 'length') | |
| if [ "$PR_COUNT" -gt 0 ]; then | |
| echo "✅ PR created successfully for issue #$ISSUE_NUM" | |
| gh issue edit "$ISSUE_NUM" --remove-label "in-progress" | |
| else | |
| echo "⚠️ No PR created yet for issue #$ISSUE_NUM" | |
| # Check if blocked or needs info | |
| LABELS=$(gh issue view "$ISSUE_NUM" --json labels --jq '.labels[].name') | |
| if echo "$LABELS" | grep -qE "blocked|needs-info"; then | |
| echo "ℹ️ Issue is blocked or needs more information" | |
| gh issue edit "$ISSUE_NUM" --remove-label "in-progress" | |
| else | |
| # Keep in-progress label for retry | |
| gh issue comment "$ISSUE_NUM" --body "⚠️ Work in progress. Claude will continue in the next scheduled run." | |
| fi | |
| fi | |
| summary: | |
| needs: [find-work, process-issue] | |
| if: always() | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Report summary | |
| run: | | |
| if [ "${{ needs.find-work.outputs.has_work }}" == "true" ]; then | |
| echo "✅ Continuous work cycle completed" | |
| echo "Issues processed: $(echo '${{ needs.find-work.outputs.issues }}' | jq 'length')" | |
| else | |
| echo "ℹ️ No work to do - all claude-labeled issues are complete or blocked" | |
| fi |