- GitHub Issue/PR のコメントで
/codeまたは🤖を書くと、devcontainer 内で Claude Code を実行 - 自動的にブランチ checkout、main merge、conflict 解決、コミット&プッシュ
- 実装は約 350 行のシェルスクリプト + 200 行のシステムプロンプト
- 技術スタック非依存で、どんなプロジェクトでも再利用可能
本記事では、GitHub Actions + devcontainer + Claude Code を組み合わせた「Claude Bot」の実装を詳しく解説します。
このボットの特徴:
- Issue/PR のコメントで起動:
/codeまたは🤖を書くだけ - devcontainer で実行: ローカル開発環境と完全に一致
- 自動ブランチ管理: checkout、main merge、conflict 解決を自動化
- リアルタイム進捗: 10 秒ごとにコメント更新
- 技術非依存: システムプロンプトに技術名を含まない汎用設計
┌─────────────────────────────────────────────────┐
│ GitHub (Issue/PR Comment) │
│ User: "/code fix the bug" │
└──────────────────┬──────────────────────────────┘
│ Webhook
↓
┌─────────────────────────────────────────────────┐
│ GitHub Actions Workflow │
│ (.github/workflows/claude-bot.yml) │
│ │
│ 1. Add 👀 reaction (immediate feedback) │
│ 2. Checkout code │
│ 3. Login to GHCR │
└──────────────────┬──────────────────────────────┘
│
↓
┌─────────────────────────────────────────────────┐
│ devcontainer (via devcontainers/ci) │
│ (.devcontainer/ci/devcontainer.json) │
│ │
│ ┌───────────────────────────────────────────┐ │
│ │ run-action.sh │ │
│ │ │ │
│ │ 1. Git checkout branch │ │
│ │ - Issue: claude-bot/issue-N │ │
│ │ - PR: head branch │ │
│ │ │ │
│ │ 2. Merge origin/main │ │
│ │ - Detect conflicts │ │
│ │ │ │
│ │ 3. Build prompt │ │
│ │ - system.md │ │
│ │ - Issue/PR context │ │
│ │ - Conflict info (if any) │ │
│ │ │ │
│ │ 4. Execute Claude Code CLI │ │
│ │ ↓ │ │
│ │ ┌────────────────────────────────────┐ │ │
│ │ │ Claude Code │ │ │
│ │ │ - Read CLAUDE.md │ │ │
│ │ │ - Resolve conflicts (if any) │ │ │
│ │ │ - Execute user's task │ │ │
│ │ │ - Commit & push │ │ │
│ │ └────────────────────────────────────┘ │ │
│ │ │ │
│ │ 5. Post result to GitHub │ │
│ │ - Update progress comment │ │
│ │ - Add 👍 reaction │ │
│ └───────────────────────────────────────────┘ │
└─────────────────────────────────────────────────┘
│
↓
┌─────────────────────────────────────────────────┐
│ GitHub (Updated) │
│ - Comment with results │
│ - Branch pushed │
│ - 👍 reaction added │
└─────────────────────────────────────────────────┘
.github/
├── workflows/
│ └── claude-bot.yml # GitHub Actions ワークフロー
└── claude/
├── run-action.sh # メインスクリプト(350行)
└── system.md # システムプロンプト(200行)
.claude/
└── CLAUDE.md # プロジェクト固有の設定
.devcontainer/
└── ci/
└── devcontainer.json # CI 用 devcontainer 設定
ファイル: .github/workflows/claude-bot.yml
name: Claude Bot
on:
issue_comment:
types: [created]
pull_request_review_comment:
types: [created]
pull_request_review:
types: [submitted]
jobs:
claude:
# トリガー条件: /code または 🤖 を含むコメント
if: |
(github.event_name == 'issue_comment' &&
(contains(github.event.comment.body, '/code') ||
contains(github.event.comment.body, '🤖')))
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
issues: write
steps:
# ステップ1: 即座に 👀 リアクションを追加
- name: Add eyes reaction to comment
run: |
COMMENT_ID="${{ github.event.comment.id }}"
curl -X POST \
-H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
"https://api.github.com/repos/${{ github.repository }}/issues/comments/$COMMENT_ID/reactions" \
-d '{"content":"eyes"}'
# ステップ2: コードチェックアウト
- name: Checkout
uses: actions/checkout@v4
# ステップ3: devcontainer で実行
- name: Run Claude Bot in devcontainer
uses: devcontainers/ci@v0.3
with:
configFile: .devcontainer/ci/devcontainer.json
env: |
CLAUDE_CODE_OAUTH_TOKEN=${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }}
ISSUE_NUMBER=${{ github.event.issue.number }}
EVENT_TYPE=${{ github.event_name }}
COMMENT_ID=${{ github.event.comment.id }}
runCmd: ./.github/claude/run-action.shポイント:
devcontainers/ciを使って devcontainer 内で実行- 👀 リアクションは devcontainer 起動前に追加(即座にフィードバック)
- 環境変数で Issue/PR 情報を渡す
ファイル: .github/claude/run-action.sh
if [ "$EVENT_TYPE" = "pull_request" ]; then
# PR の場合
PR_DATA=$(gh pr view $ISSUE_NUMBER \
--json title,body,comments,commits,files \
--repo $GITHUB_REPOSITORY)
ISSUE_TITLE=$(echo "$PR_DATA" | jq -r '.title')
COMMENTS=$(echo "$PR_DATA" | jq -r '.comments[]')
PR_DIFF=$(gh pr diff $ISSUE_NUMBER --repo $GITHUB_REPOSITORY)
else
# Issue の場合
ISSUE_DATA=$(gh issue view $ISSUE_NUMBER \
--json title,body,comments \
--repo $GITHUB_REPOSITORY)
ISSUE_TITLE=$(echo "$ISSUE_DATA" | jq -r '.title')
COMMENTS=$(echo "$ISSUE_DATA" | jq -r '.comments[]')
fi# Git 設定
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
# 最新の状態を取得
git fetch origin
# ブランチ名決定(Issue と PR で異なる)
if [ "$EVENT_TYPE" = "pull_request" ]; then
# PR の場合: head ブランチ名を取得
BRANCH_NAME=$(gh pr view $ISSUE_NUMBER \
--json headRefName --jq '.headRefName' \
--repo $GITHUB_REPOSITORY)
echo "📌 PR head branch: $BRANCH_NAME"
git checkout "$BRANCH_NAME"
git pull origin "$BRANCH_NAME" || true
else
# Issue の場合: 新しいブランチ名を作成
BRANCH_NAME="claude-bot/issue-${ISSUE_NUMBER}"
echo "📌 Issue branch: $BRANCH_NAME"
if git ls-remote --heads origin "$BRANCH_NAME" | grep -q "$BRANCH_NAME"; then
# ブランチが存在 → checkout
git checkout "$BRANCH_NAME"
git pull origin "$BRANCH_NAME" || true
else
# ブランチが存在しない → 作成
git checkout -b "$BRANCH_NAME"
fi
fi
# main を merge
echo "🔀 Merging origin/main into $BRANCH_NAME..."
MERGE_OUTPUT=$(git merge origin/main --no-edit 2>&1)
MERGE_EXIT_CODE=$?
if [ $MERGE_EXIT_CODE -ne 0 ]; then
echo "⚠️ Merge conflict detected!"
# conflict があれば、Claude に解決させる
CONFLICT_FILES=$(git diff --name-only --diff-filter=U)
FULL_PROMPT="$FULL_PROMPT
---
# 🚨 IMPORTANT: Git Merge Conflict Detected
**You MUST resolve the merge conflicts BEFORE starting the user's task.**
## Conflicted Files:
$CONFLICT_FILES
## Steps to Resolve:
1. Read each conflicted file
2. Understand both changes (current branch vs main)
3. Resolve conflicts by editing files (remove conflict markers <<<<<<, =======, >>>>>>>)
4. Stage resolved files: \`git add <file>\`
5. Commit the merge: \`git commit -m \"Merge main into $BRANCH_NAME\"\`
6. Verify: \`git status\` should show no conflicts
**After resolving conflicts, proceed with the user's original request.**
"
else
echo "✅ Merge successful (no conflicts)"
fiポイント:
- Issue の場合:
claude-bot/issue-Nという新しいブランチを作成 - PR の場合: PR の head ブランチに checkout
- 必ず origin/main を merge → 常に最新の状態で作業
- conflict があれば Claude に最優先タスクとして渡す
# システムプロンプト読み込み
SYSTEM_PROMPT=$(cat .github/claude/system.md | \
sed "s|{DEVCONTAINER_CONFIG_PATH}|$DEVCONTAINER_CONFIG_PATH|g")
# フルプロンプト構築
FULL_PROMPT="
$SYSTEM_PROMPT
---
# Issue/PR Context
**Type**: $EVENT_TYPE
**Number**: #$ISSUE_NUMBER
**Title**: $ISSUE_TITLE
## Description
$ISSUE_BODY
## Recent Comments
$COMMENTS
## Latest Request
$USER_REQUEST
---
# Environment Variables Available
- ISSUE_NUMBER: $ISSUE_NUMBER
- GITHUB_REPOSITORY: $GITHUB_REPOSITORY
"
# プロンプトをファイルに保存
echo "$FULL_PROMPT" > "/tmp/claude-prompt-$ISSUE_NUMBER.txt"# 初期コメント投稿
PROGRESS_COMMENT_ID=$(gh api repos/$GITHUB_REPOSITORY/issues/$ISSUE_NUMBER/comments \
-f body="🤖 **作業中...**" --jq '.id')
# Claude CLI をバックグラウンドで実行
OUTPUT_FILE="/tmp/claude-output-$ISSUE_NUMBER.txt"
timeout 1800 claude -p --dangerously-skip-permissions \
< "/tmp/claude-prompt-$ISSUE_NUMBER.txt" \
> "$OUTPUT_FILE" 2>&1 &
CLAUDE_PID=$!
# 進捗を定期的に更新(10秒ごと)
LAST_SIZE=0
UPDATE_COUNT=0
while kill -0 $CLAUDE_PID 2>/dev/null; do
sleep 10
UPDATE_COUNT=$((UPDATE_COUNT + 1))
CURRENT_SIZE=$(wc -c < "$OUTPUT_FILE" 2>/dev/null || echo "0")
# サイズが変わった場合のみ更新
if [ "$CURRENT_SIZE" -gt "$LAST_SIZE" ]; then
LAST_SIZE=$CURRENT_SIZE
# 出力の最後の部分を取得(最大500行)
CURRENT_OUTPUT=$(tail -500 "$OUTPUT_FILE")
# コメントを更新
gh api -X PATCH repos/$GITHUB_REPOSITORY/issues/comments/$PROGRESS_COMMENT_ID \
-f body="🤖 **作業中...** (更新 #$UPDATE_COUNT)
\`\`\`
${CURRENT_OUTPUT}
\`\`\`
⏱️ 作業継続中..."
fi
done
# 完了後、最終結果を投稿
wait $CLAUDE_PID
CLAUDE_EXIT_CODE=$?
if [ $CLAUDE_EXIT_CODE -eq 0 ]; then
# 成功: 👀 を削除して 👍 を追加
gh api -X DELETE repos/$GITHUB_REPOSITORY/issues/comments/$COMMENT_ID/reactions/$REACTION_ID
gh api -X POST repos/$GITHUB_REPOSITORY/issues/comments/$COMMENT_ID/reactions \
-f content="+1"
# 最終結果を投稿
CLAUDE_OUTPUT=$(cat "$OUTPUT_FILE")
gh api -X PATCH repos/$GITHUB_REPOSITORY/issues/comments/$PROGRESS_COMMENT_ID \
-f body="## ✅ タスク完了
$CLAUDE_OUTPUT"
fiポイント:
- Claude CLI をバックグラウンドで実行
- 10 秒ごとに出力をチェックしてコメント更新
- ユーザーはリアルタイムで進捗を確認できる
- 完了後、👀 → 👍 に変更
ファイル: .github/claude/system.md
# Your Role
You are an autonomous development assistant running on GitHub Actions.
You are running inside a devcontainer.
Configuration: {DEVCONTAINER_CONFIG_PATH}
Read the devcontainer configuration and `.claude/CLAUDE.md` to understand:
- Available tools and versions (languages, databases, runtime environments)
- Project-specific commands (test, build, server start, etc.)
- Git workflows and branching conventions
- Code style and testing requirements
# Git Workflow
**NOTE**: The branch has already been checked out and main has been merged by the automation script.
You are already on the correct branch (`claude-bot/issue-N` or PR head branch).
When you make code changes, follow these steps:
## Step 1: Resolve Merge Conflicts (if any)
If you see a "Git Merge Conflict Detected" message in the prompt:
1. Read each conflicted file
2. Understand both changes (current branch vs main)
3. Resolve conflicts by editing files (remove conflict markers)
4. Stage resolved files: `git add <resolved-files>`
5. Commit the merge: `git commit -m "Merge main into <branch-name>"`
6. Verify: `git status` should show no conflicts
After resolving conflicts, proceed with the user's original request.
## Step 2: Stage Changes
```bash
git add .git commit -m "<type>: <brief description>
<optional detailed description>
Co-Authored-By: Claude Bot <noreply@anthropic.com>"Common commit types: feat, fix, refactor, test, docs, chore
CURRENT_BRANCH=$(git branch --show-current)
git push origin "$CURRENT_BRANCH"Follow these steps for each task:
- If the prompt contains "Git Merge Conflict Detected":
- STOP and resolve conflicts FIRST
- Do NOT proceed with the user's request until conflicts are resolved
- Read Issue/PR title, description, and comments
- Review PR diff if available
- Identify the user's request
- Use Read tool to read relevant files
- Use Glob to find files by pattern
- Use Grep to search for keywords
- Read related files first
- Make necessary changes using Edit or Write tools
- Follow existing code style
- Check
.claude/CLAUDE.mdfor the test command - Execute tests using Bash
- Fix issues if tests fail
- Stage, commit, and push changes
- You are already on the correct branch
- Output structured results to stdout
- Include what changed, test results, and git operations
**設計のポイント**:
- **技術非依存**: Rails、Python、port 3333 などの具体的な技術名を含まない
- **環境検出**: CLAUDE.md と devcontainer 設定から自動検出
- **conflict 最優先**: 最初に conflict をチェックして解決
- **簡潔**: ブランチ操作はスクリプトで済んでいるため、Git Workflow は 4 ステップのみ
---
## 動作フロー(Issue の場合)
### 1. ユーザーアクション
Issue #123 にコメント: /code README.md に新しいセクションを追加して
### 2. GitHub Actions 起動
1. `issue_comment` イベントをトリガー
2. コメントに `/code` が含まれているか確認
3. 👀 リアクションを追加(2秒以内)
### 3. devcontainer 起動
1. `.devcontainer/ci/devcontainer.json` を読み込み
2. Docker イメージをビルド/pull(初回は数分、以降はキャッシュ利用)
3. コンテナ内で `run-action.sh` を実行
### 4. ブランチ準備
```bash
# ブランチ作成または checkout
git checkout -b claude-bot/issue-123 # 初回
# or
git checkout claude-bot/issue-123 # 2回目以降
# main を merge
git merge origin/main --no-edit
# プロンプト構築(system.md + Issue コンテキスト)
claude -p < /tmp/claude-prompt-123.txtClaude Code が実行すること:
.claude/CLAUDE.mdを自動読み込み- README.md を読む
- 新しいセクションを追加
- 変更をコミット:
git commit -m "docs: add new section to README" - プッシュ:
git push origin claude-bot/issue-123
## ✅ タスク完了
### 実施内容
- README.md に「## インストール」セクションを追加
### 変更ファイル
- `README.md`
### テスト結果
- テスト不要(ドキュメント変更のみ)
### Git 操作
- **ブランチ**: `claude-bot/issue-123`
- **コミット**: `docs: add new section to README`
- **プッシュ**: ✅ 完了
### 備考
- ブランチから PR を作成できます- Issue #123 に結果がコメントされる
- コメントに 👍 リアクションが追加される
- ブランチ
claude-bot/issue-123が push されている - GitHub UI で「Compare & pull request」ボタンが表示される
PR #45 (head: feature/add-auth) にコメント:
/code tests を追加して
# PR の head ブランチに checkout
git checkout feature/add-auth
git pull origin feature/add-auth
# main を merge
git merge origin/main --no-editmain が更新されていて conflict が発生した場合:
Auto-merging src/auth.js
CONFLICT (content): Merge conflict in src/auth.js
Automatic merge failed; fix conflicts and then commit the result.スクリプトが conflict を検出し、Claude にタスクとして渡す:
# 🚨 IMPORTANT: Git Merge Conflict Detected
## Conflicted Files:
src/auth.js
## Steps to Resolve:
1. Read src/auth.js
2. Understand both changes
3. Resolve conflicts by editing the file
4. Stage: `git add src/auth.js`
5. Commit: `git commit -m "Merge main into feature/add-auth"`
**After resolving conflicts, proceed with the user's original request.**Claude Code が実行:
src/auth.jsを読む- conflict markers を理解
- 適切に解決(両方の変更を保持)
git add src/auth.jsgit commit -m "Merge main into feature/add-auth"- ユーザーの元のタスク(tests を追加)を実行
ファイル: .claude/CLAUDE.md
# 開発ガイド
## テストコマンド
```bash
bundle exec rake test:all- Feature:
feature/<name> - Bugfix:
bugfix/<name> - Claude Bot:
claude-bot/issue-<number>
<type>: <subject>
<body>
Co-Authored-By: Claude Bot <noreply@anthropic.com>
Types: feat, fix, refactor, test, docs, chore
このファイルは **Claude Code が自動で読み込む** ため、スクリプトやプロンプトの変更は不要。
### 2. トリガーキーワードの変更
**ファイル**: `.github/workflows/claude-bot.yml`
```yaml
if: |
contains(github.event.comment.body, '/code') ||
contains(github.event.comment.body, '🤖') ||
contains(github.event.comment.body, '@claude') # 追加
ファイル: .github/workflows/claude-bot.yml
env: |
CLAUDE_TIMEOUT=3600 # 60分に変更(デフォルト: 1800秒 = 30分)ファイル: .github/claude/run-action.sh
while kill -0 $CLAUDE_PID 2>/dev/null; do
sleep 30 # 30秒に変更(デフォルト: 10秒)
...
done原因: ワークフローのトリガー条件が満たされていない
確認:
# コメントに /code または 🤖 が含まれているか
grep -E "(/code|🤖)" <<< "your comment"解決:
- コメントに
/codeを明示的に含める .github/workflows/claude-bot.ymlのトリガー条件を確認
原因: Docker イメージのビルドに時間がかかる
解決:
- キャッシュを活用
- name: Run Claude Bot in devcontainer
uses: devcontainers/ci@v0.3
with:
push: always # イメージを push してキャッシュ
cacheFrom: |
ghcr.io/${{ github.repository }}/devcontainer-ci:latest- ベースイメージを軽量化
{
"image": "mcr.microsoft.com/devcontainers/base:ubuntu"
}原因: Claude が conflict markers を正しく理解できていない
解決:
- system.md の conflict 解決手順を詳細化
- CLAUDE.md に conflict 解決の例を追加
## Conflict 解決の例
```diff
<<<<<<< HEAD
const API_URL = "https://api.example.com/v1"
=======
const API_URL = "https://api.example.com/v2"
>>>>>>> main↓ 解決後
const API_URL = "https://api.example.com/v2" // main の変更を採用
### 問題4: API レート制限
**原因**: GitHub API の呼び出しが多すぎる
**解決**:
1. gh CLI のキャッシュを活用
2. API 呼び出しを最小化
```bash
# 1回の呼び出しで複数フィールドを取得
gh pr view $ISSUE_NUMBER \
--json title,body,comments,commits,files \
--repo $GITHUB_REPOSITORY
- name: Run Claude Bot in devcontainer
uses: devcontainers/ci@v0.3
with:
imageName: ghcr.io/${{ github.repository }}/devcontainer-ci
push: always
cacheFrom: |
ghcr.io/${{ github.repository }}/devcontainer-ci:latest効果:
- 初回: 5-10 分
- 2 回目以降: 1-2 分
# サイズが変わった場合のみ更新
if [ "$CURRENT_SIZE" -gt "$LAST_SIZE" ]; then
LAST_SIZE=$CURRENT_SIZE
# コメント更新
fi効果:
- API 呼び出しを 90% 削減
- レート制限を回避
# PR の diff を制限
PR_DIFF=$(gh pr diff $ISSUE_NUMBER --repo $GITHUB_REPOSITORY | head -1000)
# コメントを最新 5 件のみ
COMMENTS=$(echo "$PR_DATA" | jq -r '.comments[]' | tail -5)効果:
- プロンプトサイズを 50% 削減
- Claude の応答が高速化
env: |
CLAUDE_CODE_OAUTH_TOKEN=${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }}重要:
CLAUDE_CODE_OAUTH_TOKENは Repository Secrets に保存GITHUB_TOKENは自動で提供される- スクリプト内でシークレットをログ出力しない
permissions:
contents: write # コミット&プッシュに必要
pull-requests: write # PR コメントに必要
issues: write # Issue コメントに必要# ユーザー入力をそのまま実行しない
USER_REQUEST=$(echo "$LATEST_COMMENT" | sed -E 's/\/code\b//gi')
# Claude に渡すだけで、bash で eval しないon:
pull_request:
types: [opened, synchronize]
jobs:
review:
steps:
- name: Review code
run: |
gh pr comment $PR_NUMBER --body "/code このPRをレビューして、改善点を教えて"on:
schedule:
- cron: '0 0 * * 0' # 毎週日曜日
jobs:
maintenance:
steps:
- name: Create maintenance issue
run: |
ISSUE_NUMBER=$(gh issue create \
--title "Weekly maintenance" \
--body "依存関係を更新して、テストを実行してください" \
--label "maintenance" \
--repo $GITHUB_REPOSITORY \
--jq '.number')
gh issue comment $ISSUE_NUMBER \
--body "/code 依存関係を更新してください"jobs:
claude-frontend:
steps:
- run: ./.github/claude/run-action.sh
env:
CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_FRONTEND_TOKEN }}
SYSTEM_PROMPT_FILE: .github/claude/frontend.md
claude-backend:
steps:
- run: ./.github/claude/run-action.sh
env:
CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_BACKEND_TOKEN }}
SYSTEM_PROMPT_FILE: .github/claude/backend.mdこの実装の特徴:
- シンプル: 約 550 行のコードで完結
- 汎用的: 技術スタック非依存で、どんなプロジェクトでも使える
- 信頼性: ブランチ操作を bash で確実に実行
- UX: リアルタイム進捗、👀/👍 リアクション
- 柔軟性: CLAUDE.md でプロジェクト固有の設定を簡単に追加
GitHub Actions + devcontainer + Claude Code の組み合わせにより、ローカル開発環境と完全に一致した環境で AI ボットを実行できます。
実装例: https://github.com/bloom-and-co/manual-maker-2025
以上、Claude Bot の実装解説でした。質問や改善提案があれば、お気軽にどうぞ!