Claude Code は claude -p
でユーザーとの対話なしに一度だけクエリを実行することができる。
これを ANTHROPIC_API_KEY
を設定した環境で実行することで CI として動作させることが可能になる(直近でこの部分にバグがあったが修正された)。
以下は GitHub Actions で Issue にラベルをつけると解決を試みてくれるサンプル:
name: Claude Issue Solver
on:
issues:
types: [opened, edited, labeled]
permissions:
contents: write
issues: write
pull-requests: write
jobs:
analyze-issue:
runs-on: ubuntu-latest
# Only run when issues have the 'claude-solve' label
if: contains(github.event.issue.labels.*.name, 'claude-solve')
steps:
- name: Checkout repository
uses: actions/checkout@v3
with:
token: ${{ secrets.GITHUB_TOKEN }}
fetch-depth: 0
- name: Setup Git
run: |
git config --global user.name "GitHub Action"
git config --global user.email "[email protected]"
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20.x
- name: Setup PNPM
uses: pnpm/action-setup@v3
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Install Claude Code
run: npm install -g @anthropic-ai/claude-code
- name: Create new branch
run: |
BRANCH_NAME="claude-fix-issue-${{ github.event.issue.number }}"
git checkout -b "$BRANCH_NAME"
- name: Save issue details
env:
ISSUE_TITLE: ${{ github.event.issue.title }}
ISSUE_BODY: ${{ github.event.issue.body }}
ISSUE_NUMBER: ${{ github.event.issue.number }}
run: |
# Save issue details to file for Claude to analyze
cat > issue.txt << EOF
Issue #${ISSUE_NUMBER}: ${ISSUE_TITLE}
${ISSUE_BODY}
EOF
- name: Evaluate issue solvability
id: evaluate
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
run: |
export ANTHROPIC_API_KEY=${{ secrets.ANTHROPIC_API_KEY }}
echo $ANTHROPIC_API_KEY
# Use Claude Code to evaluate if the issue can be solved
EVALUATION_JSON=$(cat issue.txt | claude -p "あなたはソフトウェアエンジニアとして、このIssueを解決できるかどうか評価してください。このIssueを解決するために十分な情報が含まれていれば「[Yes]」と返してください。そうでない場合は「[No]」と返して、その次の行に理由を述べてください。" --json)
echo "EVALUATION_JSON: $EVALUATION_JSON"
# Extract cost information
COST_USD=$(echo "$EVALUATION_JSON" | jq -r '.cost_usd')
echo "評価コスト: $COST_USD USD"
# Extract the result
EVALUATION=$(echo "$EVALUATION_JSON" | jq -r '.result')
# Extract yes/no response and reason
if echo "$EVALUATION" | grep -q "\[Yes\]"; then
echo "solvable=true" >> $GITHUB_OUTPUT
echo "reason=Issueには解決するための十分な情報が含まれています。" >> $GITHUB_OUTPUT
else
echo "solvable=false" >> $GITHUB_OUTPUT
REASON=$(echo "$EVALUATION" | sed -n '/\[No\]/,/^$/p' | tail -n +2)
echo "reason<<EOF" >> $GITHUB_OUTPUT
echo "$REASON" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
fi
# Save total cost for reporting
echo "cost_usd=$COST_USD" >> $GITHUB_OUTPUT
- name: Comment on unsolvable issue
if: steps.evaluate.outputs.solvable != 'true'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ISSUE_NUMBER: ${{ github.event.issue.number }}
COST_USD: ${{ steps.evaluate.outputs.cost_usd }}
run: |
COMMENT="## Claude 評価結果
このIssueは自動的に解決できません。
**理由:** ${{ steps.evaluate.outputs.reason }}
より詳細な情報を提供するか、要件を明確にしてください。
**コスト:** ${COST_USD} USD"
gh issue comment "$ISSUE_NUMBER" --body "$COMMENT"
- name: Solve issue
if: steps.evaluate.outputs.solvable == 'true'
id: solve
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
run: |
# Use Claude Code to solve the issue
SOLUTION_JSON=$(cat issue.txt | claude -p "このIssueを解決してください。リポジトリのコードを変更するために必要な作業を行ってください。" --json --allowedTools "Bash(git diff:*)" "Bash(git log:*)" Edit)
# Extract cost and result information
COST_USD=$(echo "$SOLUTION_JSON" | jq -r '.cost_usd')
RESULT=$(echo "$SOLUTION_JSON" | jq -r '.result')
echo "解決コスト: $COST_USD USD"
echo "解決結果: $RESULT"
# Save for reporting
echo "cost_usd=$COST_USD" >> $GITHUB_OUTPUT
echo "result<<EOF" >> $GITHUB_OUTPUT
echo "$RESULT" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
- name: Apply auto-fixes
if: steps.evaluate.outputs.solvable == 'true'
run: |
pnpm format || true
pnpm lint:fix || true
- name: Create PR
if: steps.evaluate.outputs.solvable == 'true'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ISSUE_NUMBER: ${{ github.event.issue.number }}
ISSUE_TITLE: ${{ github.event.issue.title }}
SOLVE_COST: ${{ steps.solve.outputs.cost_usd }}
EVAL_COST: ${{ steps.evaluate.outputs.cost_usd }}
SOLVE_RESULT: ${{ steps.solve.outputs.result }}
run: |
BRANCH_NAME="claude-fix-issue-${ISSUE_NUMBER}"
# Calculate total cost
TOTAL_COST=$(echo "$EVAL_COST + $SOLVE_COST" | bc)
rm issue.txt
git add .
git commit -m "Fix: Issue #$ISSUE_NUMBER: $ISSUE_TITLE"
git push -u origin "$BRANCH_NAME"
PR_BODY="## Claude による自動修正
このPRはIssue #${ISSUE_NUMBER} を修正します。
**解決結果:**
${SOLVE_RESULT}
**Claude AI コスト:** ${TOTAL_COST} USD
Claude AI 🤖 によって実装・テストされました"
gh pr create --title "Fix: ${ISSUE_TITLE}" --body "$PR_BODY" --base main
gh issue comment "$ISSUE_NUMBER" --body "修正用のPRを作成しました! 🎉 確認をお願いします。Claude AIのコスト: ${TOTAL_COST} USD"
Claude Code で編集したあと eslint と prettier を実行して PR を作らせている、情報が不足していたら Issue にコメントが来るはずだが、いまのところかなり適当な内容でも解決しようとする。 テストや eslint、prettier に失敗したらそれをもとに更に修正させることもしたかったが、yaml として書くのが面倒で今回はそこまではやっていない。
Roo Mode のような役割を与えるプロンプトを別途用意しておくと精度がよくなるかもしれない。