diff --git a/.github/workflows/claude-dispatcher-sync.yml b/.github/workflows/claude-dispatcher-sync.yml new file mode 100644 index 0000000000..854be78b1a --- /dev/null +++ b/.github/workflows/claude-dispatcher-sync.yml @@ -0,0 +1,57 @@ +name: Push Claude Dispatcher to Subrepos + +on: + push: + branches: [main] + paths: + - '.github/workflows/claude-dispatcher.yml' + workflow_dispatch: + inputs: + repos: + description: 'Comma-separated list of repos to sync (e.g., opral/inlang-paraglide-js,opral/inlang-sdk)' + required: false + +jobs: + push-to-repos: + runs-on: ubuntu-latest + strategy: + matrix: + repo: + # Add your issue tracker repos here + - opral/inlang-paraglide-js + - opral/inlang-sdk + # Add more repos as needed + steps: + - name: Checkout monorepo + uses: actions/checkout@v4 + with: + path: monorepo + + - name: Checkout target repo + uses: actions/checkout@v4 + with: + repository: ${{ matrix.repo }} + token: ${{ secrets.MONOREPO_DISPATCH_TOKEN }} + path: target-repo + + - name: Copy dispatcher and push + run: | + # Copy the dispatcher + cp monorepo/.github/workflows/claude-dispatcher.yml target-repo/.github/workflows/ + + cd target-repo + + # Configure git + git config user.name "GitHub Actions" + git config user.email "actions@github.com" + + # Check if there are changes + if git diff --quiet; then + echo "No changes in ${{ matrix.repo }}" + exit 0 + fi + + # Commit and push + git add .github/workflows/claude-dispatcher.yml + git commit -m "chore: sync Claude dispatcher from monorepo" + git push origin main \ No newline at end of file diff --git a/.github/workflows/claude-dispatcher.yml b/.github/workflows/claude-dispatcher.yml new file mode 100644 index 0000000000..0c8feb844f --- /dev/null +++ b/.github/workflows/claude-dispatcher.yml @@ -0,0 +1,49 @@ +name: Claude Dispatcher + +on: + issue_comment: + types: [created] + issues: + types: [opened] + pull_request_review_comment: + types: [created] + pull_request_review: + types: [submitted] + +jobs: + dispatch-to-monorepo: + if: | + (contains(github.event.comment.body || github.event.issue.body || github.event.review.body || '', '@claude')) + runs-on: ubuntu-latest + steps: + - name: Post initial comment + id: comment + uses: actions/github-script@v7 + with: + script: | + const comment = await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + body: '🤖 Claude is processing your request...' + }); + return comment.data.id; + + - name: Dispatch to Monorepo + uses: peter-evans/repository-dispatch@v3 + with: + token: ${{ secrets.MONOREPO_DISPATCH_TOKEN || secrets.GITHUB_TOKEN }} + repository: ${{ github.repository == 'opral/monorepo' && 'opral/monorepo' || 'opral/monorepo' }} + event-type: claude-request + client-payload: | + { + "issue_number": "${{ github.event.issue.number }}", + "issue_title": "${{ github.event.issue.title }}", + "issue_body": "${{ github.event.issue.body }}", + "comment_body": "${{ github.event.comment.body || github.event.review.body }}", + "issue_url": "${{ github.event.issue.html_url }}", + "repository": "${{ github.repository }}", + "sender": "${{ github.event.sender.login }}", + "comment_id": "${{ steps.comment.outputs.result }}", + "is_monorepo": "${{ github.repository == 'opral/monorepo' }}" + } \ No newline at end of file diff --git a/.github/workflows/claude-receiver.yml b/.github/workflows/claude-receiver.yml new file mode 100644 index 0000000000..34cf9b3b1e --- /dev/null +++ b/.github/workflows/claude-receiver.yml @@ -0,0 +1,59 @@ +name: Claude Unified Handler + +on: + # Handle dispatched requests from all repos (including monorepo itself) + repository_dispatch: + types: [claude-request] + +jobs: + claude: + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + issues: write + id-token: write + actions: read + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 1 + + - name: Run Claude Code + id: claude + uses: anthropics/claude-code-action@beta + with: + anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} + + # Direct prompt with the request from the subrepo + direct_prompt: | + Handle this request from ${{ github.event.client_payload.repository }}#${{ github.event.client_payload.issue_number }}: + + ${{ github.event.client_payload.comment_body || github.event.client_payload.issue_body }} + + IMPORTANT: When creating a PR, you MUST include "Closes ${{ github.event.client_payload.issue_url }}" in the PR description. + The original issue is at: ${{ github.event.client_payload.issue_url }} + Focus on changes that relate to the specific subrepo package mentioned in the request. + + - name: Update comment in source repo + if: always() && github.event.client_payload.comment_id + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.MONOREPO_DISPATCH_TOKEN }} + script: | + const [owner, repo] = '${{ github.event.client_payload.repository }}'.split('/'); + const commentId = '${{ github.event.client_payload.comment_id }}'; + + let body = '❌ Claude encountered an error processing your request.'; + + if ('${{ steps.claude.outcome }}' === 'success') { + body = '✅ Claude has processed your request. Check the monorepo for the PR.'; + } + + await github.rest.issues.updateComment({ + owner: owner, + repo: repo, + comment_id: parseInt(commentId), + body: body + }); \ No newline at end of file diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000000..8c917d1d26 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,5 @@ +# Opral Monorepo Claude Instructions + +- Read the [./contributing.md](./contributing.md) for navigating the monorepo. +- The monorepo uses `pnpm`. Don't use `npm` or `yarn`. +- If you want to test a specific code file, it's easiest to cwd into the package directory and run `pnpm exec vitest run `.