Claude CodeとGitHub Issueを使った全自動開発について
注意事項 #
現在多数の方に閲覧されていますが、こちらの記事はまだ試験段階であり、改善の余地はたくさんあると考えています。
使用する際は十分ご注意ください。
このコードを使用したことで発生する不利益については、筆者は一切責任を負いません。
ご了承ください。
準備 #
- wikiは使用しない
- docsディレクトリで管理する
- README.mdにプロジェクト概要を書く
- GitHub Issueのtemplateを作成する
- .github/ISSUE_TEMPLATE/配下
- 先にIssueを作成しておく
docs/db-schema.md
からdocs/er.md
を作成- ログイン画面作成
- User一覧作成
- などなど
- ghコマンドをインストールする
出先からの実行専用スクリプト #
Priority & issue番号で並び替えして最初のissueを実行 #
- 優先順位の検索: P0->P1->P2->P3->none labelの順で検索
- 番号順のソート: 同じ優先度内では番号が小さいissueを優先
- 自動モード:
-a
オプションで確認なしで実行 - ラベルフィルタ: 追加のラベルで フィルタリング可能
- 詳細な進捗表示: 各ステップで状況を表示
- Claude Codeの実行履歴を完全保存
#!/bin/bash
# 優先度順で最初のissueを処理し、Claude Codeの履歴も保存するスクリプト
# 使用方法: ./process_issue_with_history.sh [オプション]
set -e
# 色付き出力用の関数
print_info() {
echo -e "\033[0;36m[INFO]\033[0m $1"
}
print_success() {
echo -e "\033[0;32m[SUCCESS]\033[0m $1"
}
print_error() {
echo -e "\033[0;31m[ERROR]\033[0m $1"
}
print_warning() {
echo -e "\033[0;33m[WARNING]\033[0m $1"
}
# ヘルプメッセージ
show_help() {
echo "使用方法: $0 [オプション]"
echo ""
echo "オプション:"
echo " -l, --label <label> 特定のラベルでフィルタリング"
echo " -a, --auto 確認なしで自動実行"
echo " -n, --no-history 履歴を保存しない"
echo " -d, --history-dir 履歴保存ディレクトリ(デフォルト: claude_history)"
echo " -h, --help このヘルプを表示"
echo ""
echo "優先度ラベル:"
echo " P0: 最優先"
echo " P1: 高優先度"
echo " P2: 中優先度"
echo " P3: 低優先度"
echo " (ラベルなし): 最低優先度"
}
# デフォルト値
ADDITIONAL_LABEL=""
AUTO_MODE=false
SAVE_HISTORY=true
HISTORY_DIR="claude_history"
# オプション解析
while span> $; do
case $1 in
-l|--label)
ADDITIONAL_LABEL="$2"
shift 2
;;
-a|--auto)
AUTO_MODE=true
shift
;;
-n|--no-history)
SAVE_HISTORY=false
shift
;;
-d|--history-dir)
HISTORY_DIR="$2"
shift 2
;;
-h|--help)
show_help
exit 0
;;
*)
print_error "不明なオプション: $1"
show_help
exit 1
;;
esac
done
# GitHub CLIの存在確認
if ! command -v gh &> /dev/null; then
print_error "GitHub CLI (gh) がインストールされていません"
echo "インストール方法: https://cli.github.com/"
exit 1
fi
# Claude Codeの存在確認
if ! command -v claude &> /dev/null; then
print_error "Claude Code がインストールされていません"
exit 1
fi
# リポジトリ内かチェック
if ! git rev-parse --git-dir > /dev/null 2>&1; then
print_error "Gitリポジトリ内で実行してください"
exit 1
fi
# jqの存在確認
if ! command -v jq &> /dev/null; then
print_error "jq がインストールされていません"
echo "インストール方法: sudo apt-get install jq (Ubuntu) または brew install jq (Mac)"
exit 1
fi
print_info "優先度順でissueを検索中..."
# 優先度ラベルの配列
PRIORITY_LABELS=("P0" "P1" "P2" "P3" "")
# 最初に見つかったissueを処理
FOUND_ISSUE=""
FOUND_PRIORITY=""
for priority in "${PRIORITY_LABELS[@]}"; do
# クエリの構築
QUERY="--state open"
if [ -n "$priority" ]; then
QUERY="$QUERY --label $priority"
current_priority="$priority"
else
current_priority="ラベルなし"
fi
if [ -n "$ADDITIONAL_LABEL" ]; then
QUERY="$QUERY --label $ADDITIONAL_LABEL"
fi
print_info "優先度 $current_priority のissueを検索中..."
# issueを取得(番号順でソート)
ISSUES=$(gh issue list $QUERY --json number,title,state,labels --limit 100 | jq 'sort_by(.number)')
# 最初のissueを取得
if [ -n "$ISSUES" ] && [ "$ISSUES" != "[]" ]; then
FOUND_ISSUE=$(echo "$ISSUES" | jq -r '.[0]')
FOUND_PRIORITY="$current_priority"
break
fi
done
# issueが見つからなかった場合
if [ -z "$FOUND_ISSUE" ] || [ "$FOUND_ISSUE" = "null" ]; then
print_warning "処理可能なissueが見つかりませんでした"
exit 0
fi
# issue情報を抽出
ISSUE_NUMBER=$(echo "$FOUND_ISSUE" | jq -r '.number')
ISSUE_TITLE=$(echo "$FOUND_ISSUE" | jq -r '.title')
print_success "処理するissueが見つかりました"
echo ""
echo "================================"
echo "優先度: $FOUND_PRIORITY"
echo "Issue #$ISSUE_NUMBER: $ISSUE_TITLE"
echo "================================"
# 詳細情報を取得
ISSUE_BODY=$(gh issue view "$ISSUE_NUMBER" --json body -q .body)
echo "$ISSUE_BODY" | head -n 10
if [ $(echo "$ISSUE_BODY" | wc -l) -gt 10 ]; then
echo "... (以下省略)"
fi
echo "================================"
echo ""
# 自動モードでない場合は確認
if [ "$AUTO_MODE" = false ]; then
read -p "このissueを処理しますか? (y/N): " confirm
if [ "$confirm" != "y" ]; then
print_info "処理をキャンセルしました"
exit 0
fi
fi
# 履歴ディレクトリの準備
if [ "$SAVE_HISTORY" = true ]; then
mkdir -p "$HISTORY_DIR"
SESSION_ID="issue_${ISSUE_NUMBER}_$(date +%Y%m%d_%H%M%S)"
SESSION_DIR="$HISTORY_DIR/$SESSION_ID"
mkdir -p "$SESSION_DIR"
print_info "セッションID: $SESSION_ID"
print_info "履歴保存先: $SESSION_DIR"
# Issue情報を保存
gh issue view "$ISSUE_NUMBER" > "$SESSION_DIR/issue_details.txt" 2>&1
gh issue view "$ISSUE_NUMBER" --json number,title,body,state,labels > "$SESSION_DIR/issue_data.json"
# メタデータの記録
cat > "$SESSION_DIR/metadata.json" << EOF
{
"session_id": "$SESSION_ID",
"issue_number": $ISSUE_NUMBER,
"priority": "$FOUND_PRIORITY",
"start_time": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
"git_branch": "$(git branch --show-current)",
"git_commit": "$(git rev-parse HEAD)",
"user": "$(whoami)",
"hostname": "$(hostname)"
}
EOF
fi
# 現在のブランチを保存
CURRENT_BRANCH=$(git branch --show-current)
# 変更があるかチェック
if ! git diff --quiet || ! git diff --cached --quiet; then
print_error "コミットされていない変更があります"
echo "変更をコミットまたはstashしてから再実行してください"
exit 1
fi
# ブランチ作成
BRANCH_NAME="issue-$ISSUE_NUMBER"
print_info "ブランチ '$BRANCH_NAME' を作成します"
# mainまたはmasterブランチを取得
if git show-ref --verify --quiet refs/heads/main; then
BASE_BRANCH="main"
elif git show-ref --verify --quiet refs/heads/master; then
BASE_BRANCH="master"
else
print_error "mainまたはmasterブランチが見つかりません"
exit 1
fi
# 最新の状態を取得
print_info "最新の変更を取得中..."
git checkout "$BASE_BRANCH"
git pull origin "$BASE_BRANCH"
# ブランチが既に存在するかチェック
if git show-ref --verify --quiet "refs/heads/$BRANCH_NAME"; then
print_warning "ブランチ '$BRANCH_NAME' は既に存在します"
if [ "$AUTO_MODE" = false ]; then
read -p "既存のブランチを使用しますか? (y/N): " use_existing
if [ "$use_existing" = "y" ]; then
git checkout "$BRANCH_NAME"
else
exit 1
fi
else
print_info "既存のブランチをスキップします"
exit 0
fi
else
git checkout -b "$BRANCH_NAME"
fi
# 一時ファイルにissue情報を保存
TEMP_FILE=$(mktemp)
cat > "$TEMP_FILE" << EOF
GitHub Issue #$ISSUE_NUMBER
優先度: $FOUND_PRIORITY
タイトル: $ISSUE_TITLE
説明:
$ISSUE_BODY
---
上記のissueの内容に基づいて、必要な機能を実装してください。
実装が完了したら、変更内容の概要を教えてください。
EOF
# 履歴保存時はプロンプトも保存
if [ "$SAVE_HISTORY" = true ]; then
cp "$TEMP_FILE" "$SESSION_DIR/claude_prompt.txt"
fi
print_info "Claude Codeで実装を開始します..."
echo ""
# Claude Codeで実装(履歴保存の有無で分岐)
if [ "$SAVE_HISTORY" = true ]; then
# scriptコマンドを使用してセッション全体を記録
if span> &; then
# macOS
script -q "$SESSION_DIR/claude_session.log" bash -c "
claude < '$TEMP_FILE' 2>&1 | tee '$SESSION_DIR/claude_output.log'
"
else
# Linux
script -q -c "claude < '$TEMP_FILE' 2>&1 | tee '$SESSION_DIR/claude_output.log'" "$SESSION_DIR/claude_session.log"
fi
else
# 履歴を保存しない場合
claude < "$TEMP_FILE"
fi
# 一時ファイルを削除
rm "$TEMP_FILE"
echo ""
print_info "実装が完了しました"
# 変更の確認
if git diff --quiet; then
print_info "変更はありません"
exit 0
fi
# 変更内容を表示
echo ""
print_info "変更内容:"
git status --short
# 履歴保存(実装後の状態)
if [ "$SAVE_HISTORY" = true ]; then
git diff > "$SESSION_DIR/git_diff.patch"
git diff --name-only > "$SESSION_DIR/changed_files.txt"
git status --short > "$SESSION_DIR/git_status.txt"
fi
# 自動モードの場合は自動的にコミット
if [ "$AUTO_MODE" = true ]; then
commit_confirm="y"
else
echo ""
read -p "変更をコミットしますか? (y/N): " commit_confirm
fi
if [ "$commit_confirm" = "y" ]; then
git add .
# コミットメッセージの作成
COMMIT_MESSAGE="feat: implement #$ISSUE_NUMBER - $ISSUE_TITLE
Priority: $FOUND_PRIORITY"
git commit -m "$COMMIT_MESSAGE"
print_success "コミットが完了しました"
# 履歴にコミット情報を追加
if [ "$SAVE_HISTORY" = true ]; then
git rev-parse HEAD > "$SESSION_DIR/commit_hash.txt"
echo "$COMMIT_MESSAGE" > "$SESSION_DIR/commit_message.txt"
fi
# PRを作成するか確認
if [ "$AUTO_MODE" = true ]; then
pr_confirm="y"
else
read -p "Pull Requestを作成しますか? (y/N): " pr_confirm
fi
if [ "$pr_confirm" = "y" ]; then
print_info "ブランチをプッシュ中..."
git push -u origin "$BRANCH_NAME"
# PR作成
PR_BODY="Closes #$ISSUE_NUMBER
## 優先度
$FOUND_PRIORITY
## 変更内容
Claude Codeを使用してIssue #$ISSUE_NUMBER を実装しました。
## テスト
- [ ] 機能が正常に動作することを確認
- [ ] 既存のテストが通ることを確認
- [ ] 必要に応じて新しいテストを追加"
PR_URL=$(gh pr create \\ --title "Fix #$ISSUE_NUMBER: $ISSUE_TITLE" \\ --body "$PR_BODY" \
--base "$BASE_BRANCH" \\ --head "$BRANCH_NAME" \
--web=false)
print_success "Pull Requestを作成しました"
# 履歴にPR情報を追加
if [ "$SAVE_HISTORY" = true ]; then
echo "$PR_URL" > "$SESSION_DIR/pr_url.txt"
fi
fi
fi
# 履歴保存の最終処理
if [ "$SAVE_HISTORY" = true ]; then
# 終了時刻を記録
END_TIME=$(date -u +%Y-%m-%dT%H:%M:%SZ)
jq --arg end_time "$END_TIME" '. + {end_time: $end_time}' "$SESSION_DIR/metadata.json" > "$SESSION_DIR/metadata_tmp.json"
mv "$SESSION_DIR/metadata_tmp.json" "$SESSION_DIR/metadata.json"
# サマリーレポートの作成
cat > "$SESSION_DIR/summary.md" << EOF
# Claude Code実装セッション サマリー
## セッション情報
- セッションID: $SESSION_ID
- Issue番号: #$ISSUE_NUMBER
- 優先度: $FOUND_PRIORITY
- タイトル: $ISSUE_TITLE
- 開始時刻: $(jq -r .start_time "$SESSION_DIR/metadata.json")
- 終了時刻: $END_TIME
## 実行結果
- ブランチ: $BRANCH_NAME
- コミット: $([ -f "$SESSION_DIR/commit_hash.txt" ] && cat "$SESSION_DIR/commit_hash.txt" || echo "未コミット")
- PR: $([ -f "$SESSION_DIR/pr_url.txt" ] && cat "$SESSION_DIR/pr_url.txt" || echo "未作成")
## 変更ファイル
$([ -f "$SESSION_DIR/changed_files.txt" ] && cat "$SESSION_DIR/changed_files.txt" | sed 's/^/- /' || echo "なし")
## Claude Codeの出力(抜粋)
\`\`\`
$([ -f "$SESSION_DIR/claude_output.log" ] && tail -n 20 "$SESSION_DIR/claude_output.log" || echo "ログなし")
\`\`\`
## 詳細ファイル
- 完全なClaude出力: claude_output.log
- セッション記録: claude_session.log
- Git差分: git_diff.patch
- Issue詳細: issue_details.txt
EOF
fi
# 成功時のサマリー
echo ""
echo "================================"
echo "処理完了サマリー"
echo "================================"
echo "優先度: $FOUND_PRIORITY"
echo "Issue: #$ISSUE_NUMBER"
echo "ブランチ: $BRANCH_NAME"
if [ "$SAVE_HISTORY" = true ]; then
echo "セッションID: $SESSION_ID"
echo "履歴保存先: $SESSION_DIR"
fi
echo "================================"
print_success "処理が完了しました"
実行方法 #
chmod +x process_top_priority_issue.sh
# 基本的な使用(優先度順で最初のissueを処理)
./process_top_priority_issue.sh
# bugラベルがついた中で最優先のissueを処理
./process_top_priority_issue.sh -l bug
# 自動モード(確認なしで実行)
./process_top_priority_issue.sh -a
# 複数のオプションを組み合わせ
./process_top_priority_issue.sh -l enhancement -a
カスタマイズ可能な部分 #
優先度ラベルを変更したい場合は、スクリプト内の PRIORITY_LABELS
配列を編集すること。
# 例:緊急度ベースのラベル
PRIORITY_LABELS=("urgent" "high" "medium" "low" "")
# 例:重要度ベースのラベル
PRIORITY_LABELS=("critical" "important" "normal" "")
自宅で実行するバージョン #
出先と中身はほぼ同じで、コミットせずClaude Codeが実装したものを目視確認していくときようのやーつ。
#!/bin/bash
# Claude Codeの試行履歴を保存しながら実装するスクリプト
# 使用方法: ./implement_with_history.sh <issue番号>
set -e
# 色付き出力用の関数
print_info() {
echo -e "\033[0;36m[INFO]\033[0m $1"
}
print_success() {
echo -e "\033[0;32m[SUCCESS]\033[0m $1"
}
print_error() {
echo -e "\033[0;31m[ERROR]\033[0m $1"
}
# 引数チェック
if [ $# -eq 0 ]; then
print_error "Issue番号を指定してください"
echo "使用方法: $0 <issue番号>"
exit 1
fi
ISSUE_NUMBER=$1
# 履歴ディレクトリの作成
HISTORY_DIR="claude_history"
mkdir -p "$HISTORY_DIR"
# セッション用のディレクトリ作成
SESSION_ID="issue_${ISSUE_NUMBER}_$(date +%Y%m%d_%H%M%S)"
SESSION_DIR="$HISTORY_DIR/$SESSION_ID"
mkdir -p "$SESSION_DIR"
print_info "セッションID: $SESSION_ID"
print_info "履歴保存先: $SESSION_DIR"
# GitHub CLIとClaude Codeの存在確認
if ! command -v gh &> /dev/null; then
print_error "GitHub CLI (gh) がインストールされていません"
exit 1
fi
if ! command -v claude &> /dev/null; then
print_error "Claude Code がインストールされていません"
exit 1
fi
# Issue情報の取得と保存
print_info "Issue #$ISSUE_NUMBER の情報を取得中..."
gh issue view "$ISSUE_NUMBER" > "$SESSION_DIR/issue_details.txt" 2>&1
# Issue情報を構造化して保存
gh issue view "$ISSUE_NUMBER" --json number,title,body,state,labels > "$SESSION_DIR/issue_data.json"
# メタデータの記録
cat > "$SESSION_DIR/metadata.json" << EOF
{
"session_id": "$SESSION_ID",
"issue_number": $ISSUE_NUMBER,
"start_time": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
"git_branch": "$(git branch --show-current)",
"git_commit": "$(git rev-parse HEAD)",
"user": "$(whoami)",
"hostname": "$(hostname)"
}
EOF
# Claudeへの入力を準備
ISSUE_TITLE=$(gh issue view "$ISSUE_NUMBER" --json title -q .title)
ISSUE_BODY=$(gh issue view "$ISSUE_NUMBER" --json body -q .body)
# Claude用のプロンプトを作成
cat > "$SESSION_DIR/claude_prompt.txt" << EOF
GitHub Issue #$ISSUE_NUMBER
タイトル: $ISSUE_TITLE
説明:
$ISSUE_BODY
---
上記のissueの内容に基づいて、必要な機能を実装してください。
実装が完了したら、変更内容の概要を教えてください。
EOF
print_info "Claude Codeで実装を開始します..."
echo "すべての出力は $SESSION_DIR/claude_output.log に保存されます"
echo ""
# scriptコマンドを使用してClaude Codeのセッション全体を記録
if span> &; then
# macOS
script -q "$SESSION_DIR/claude_session.log" bash -c "
claude < '$SESSION_DIR/claude_prompt.txt' 2>&1 | tee '$SESSION_DIR/claude_output.log'
"
else
# Linux
script -q -c "claude < '$SESSION_DIR/claude_prompt.txt' 2>&1 | tee '$SESSION_DIR/claude_output.log'" "$SESSION_DIR/claude_session.log"
fi
# 実装後の変更を記録
print_info "変更内容を記録中..."
# git diffを保存
git diff > "$SESSION_DIR/git_diff.patch"
git diff --name-only > "$SESSION_DIR/changed_files.txt"
git status --short > "$SESSION_DIR/git_status.txt"
# 終了時刻を記録
END_TIME=$(date -u +%Y-%m-%dT%H:%M:%SZ)
jq --arg end_time "$END_TIME" '. + {end_time: $end_time}' "$SESSION_DIR/metadata.json" > "$SESSION_DIR/metadata_tmp.json"
mv "$SESSION_DIR/metadata_tmp.json" "$SESSION_DIR/metadata.json"
# サマリーレポートの作成
cat > "$SESSION_DIR/summary.md" << EOF
# Claude Code実装セッション サマリー
## セッション情報
- セッションID: $SESSION_ID
- Issue番号: #$ISSUE_NUMBER
- タイトル: $ISSUE_TITLE
- 開始時刻: $(date)
- 終了時刻: $(date)
## 変更ファイル
$(cat "$SESSION_DIR/changed_files.txt" | sed 's/^/- /')
## Claude Codeの出力(抜粋)
\`\`\`
$(tail -n 20 "$SESSION_DIR/claude_output.log")
\`\`\`
## 詳細ファイル
- 完全なClaude出力: claude_output.log
- セッション記録: claude_session.log
- Git差分: git_diff.patch
- Issue詳細: issue_details.txt
EOF
print_success "実装が完了しました"
echo ""
echo "================================"
echo "セッションサマリー"
echo "================================"
echo "セッションID: $SESSION_ID"
echo "保存先: $SESSION_DIR"
echo ""
echo "主要ファイル:"
echo " - claude_output.log: Claude Codeの出力"
echo " - claude_session.log: 完全なセッション記録"
echo " - git_diff.patch: 変更内容の差分"
echo " - summary.md: セッションサマリー"
echo "================================"
# 変更がある場合は表示
if [ -s "$SESSION_DIR/git_diff.patch" ]; then
echo ""
print_info "変更されたファイル:"
cat "$SESSION_DIR/changed_files.txt"
echo ""
echo "次のステップ:"
echo " 1. cat $SESSION_DIR/summary.md # サマリーを確認"
echo " 2. git diff # 変更内容を確認"
echo " 3. git add . # ステージング"
echo " 4. git commit -m \"feat: implement #$ISSUE_NUMBER\""
fi
実行方法 #
chmod +x implement_with_history.sh
# 基本的な使用(実装のみ)
./implement_with_history.sh
# ブランチも作成せず、現在のブランチで実装
./implement_with_history.sh -b
# 実装内容をログファイルに保存
./implement_with_history.sh -o implementation_log.md
# 自動モード + ログ保存 + ブランチ作成なし
./implement_with_history.sh -a -b -o log.md
# 特定のラベルでフィルタリング
./implement_with_history.sh -l bug