GitHub 自動化 — AI を活用したスケーラブルな Issue 管理
前提知識
- ›第 1 回:アーキテクチャ概要
- ›GitHub Actions の基礎知識(ワークフロー・イベント・パーミッション)
- ›TypeScript / シェルスクリプトの基礎知識
GitHub 自動化 — AI を活用したスケーラブルな Issue 管理
claude-code リポジトリは、Claude Code プラグインを収録しているだけでなく、Claude Code を使ってリポジトリ自身を管理しています。この「自分自身を使って自分を管理する」という再帰的なアプローチは、12 本の GitHub Actions ワークフロー、カスタムスラッシュコマンド、サンドボックス化された CLI ラッパー、そして TypeScript のライフサイクルスクリプトにわたって実装されています。新しい Issue が開かれると Claude Code がトリアージし、重複が見つかると Claude Code が検出します。Issue が放置されると、スクリプトが猶予期間と作者による拒否権の仕組みを通じて丁寧にクローズします。
この記事では、Issue が辿るライフサイクルの全体像を追いながら、AI エージェントを GitHub リポジトリ上で本番運用するために設計された 3 層のセキュリティモデルを詳しく解説します。
Issue ライフサイクルパイプライン
claude-code リポジトリの Issue は、それぞれ異なるワークフローやスクリプトが担当する最大 7 つのステージを経由します。
stateDiagram-v2
[*] --> Opened: Issue created
Opened --> Triaged: claude-issue-triage.yml
Triaged --> DupeChecked: claude-dedupe-issues.yml
DupeChecked --> Active: No duplicates found
DupeChecked --> DupeCommented: Duplicate detected
DupeCommented --> AutoClosed: 3-day grace + no dispute
DupeCommented --> Active: Author disputes (👎 reaction)
Active --> Stale: sweep.ts (14d inactive)
Stale --> Closed: sweep.ts (14d after stale label)
Active --> NeedsInfo: Triage labels needs-info/needs-repro
NeedsInfo --> Closed: sweep.ts (7d no response)
NeedsInfo --> Active: Info provided → re-triage
Closed --> Locked: lock-closed-issues.yml
ライフサイクルラベルとそのタイムアウト設定は、scripts/issue-lifecycle.ts を唯一の情報源として一元管理されています。
export const lifecycle = [
{ label: "invalid", days: 3, reason: "this doesn't appear to be about Claude Code" },
{ label: "needs-repro", days: 7, reason: "we still need reproduction steps" },
{ label: "needs-info", days: 7, reason: "we still need more information" },
{ label: "stale", days: 14, reason: "inactive for too long" },
{ label: "autoclose", days: 14, reason: "inactive for too long" },
] as const;
タイムアウト情報が必要なすべてのスクリプトとワークフローは、このファイルからインポートしています。定数 STALE_UPVOTE_THRESHOLD(👍 リアクション 10 件)は安全弁として機能し、長期間更新がなくても人気の高い Issue はスタレ判定から除外されます。
AI によるトリアージと重複排除
Issue が開かれると、2 つのワークフローが並行して起動します。
claude-issue-triage.yml(/.github/workflows/claude-issue-triage.yml)は、anthropics/claude-code-action@v1 経由で /triage-issue コマンドを実行します。Claude Opus(claude-opus-4-6)を使用し、タイムアウトは 5 分です。concurrency グループにより、同一 Issue に対して同時に複数のトリアージが走ることを防いでいます。
/.claude/commands/triage-issue.md のトリアージコマンドは、動作範囲を厳密に絞り込んでいます。
- ラベルの追加・削除のみ — コメントや Issue 編集は行わない
- 既存ラベルのみ使用 — まずラベル一覧を取得し、新しいラベルを勝手に作らない
- ライフサイクルラベルは慎重に — 「誤検知のほうが見逃しより有害」
- 新規 Issue とコメントで挙動を分ける — 新規 Issue は全カテゴリを評価し、コメントはライフサイクルラベルの更新のみ行う
コマンドは issues イベント(フルトリアージ)と issue_comment イベント(ライフサイクルラベル管理)を区別します。コメントイベントでは、新しい活動があったことを示すため stale/autoclose ラベルを削除し、needs-repro/needs-info ラベルについては必要な情報が提供されたかどうかを判断して削除を検討します。
flowchart TD
EVENT{"Event type?"} --> NEW["issues (new issue)"]
EVENT --> COMMENT["issue_comment"]
NEW --> LABELS["Fetch available labels"]
LABELS --> READ["Read issue details"]
READ --> VALID{"About<br/>Claude Code?"}
VALID -->|No| INVALID["Label: invalid"]
VALID -->|Yes| CATEGORIZE["Apply category labels<br/>(type, area, platform)"]
CATEGORIZE --> DUPECHECK["Search for duplicates"]
DUPECHECK --> LIFECYCLE["Evaluate lifecycle labels<br/>(needs-repro, needs-info)"]
LIFECYCLE --> APPLY["Apply all labels"]
COMMENT --> READ_CONV["Read full conversation"]
READ_CONV --> STALE{"Has stale/<br/>autoclose?"}
STALE -->|Yes| REMOVE["Remove stale labels"]
STALE -->|No| NEEDS{"Has needs-repro/<br/>needs-info?"}
NEEDS -->|Yes| PROVIDED{"Info<br/>provided?"}
PROVIDED -->|Yes| REMOVE_NEEDS["Remove needs-* label"]
PROVIDED -->|No| KEEP["Keep label"]
style INVALID fill:#E53935,color:#fff
claude-dedupe-issues.yml(/.github/workflows/claude-dedupe-issues.yml)は Sonnet を使って /dedupe コマンドを実行します。/.claude/commands/dedupe.md の dedupe コマンドは5 つの並列エージェントを起動し、それぞれ異なるキーワードとアプローチで重複候補を幅広く検索します。最後に絞り込みエージェントが誤検知を除去し、コメントスクリプト経由で結果を投稿します。このワークフローは分析用に Statsig へのイベントログも記録します。
ヒント: トリアージコマンドのツール制限(
Bash(./scripts/gh.sh:*)とBash(./scripts/edit-issue-labels.sh:*))は、第 2 回で紹介したallowed-toolsと同じパターンです。AI エージェントの操作を特定の監査可能な処理に限定することは、パブリックリポジトリで AI を運用する際に欠かせない考え方です。
猶予期間付きの自動クローズ
scripts/auto-close-duplicates.ts の自動クローズスクリプトは、精巧な猶予期間の仕組みを実装しています。dedupe コマンドが「重複の可能性あり」というコメントを投稿した時点でカウントダウンが始まります。3 日後、このスクリプトが Issue をクローズすべきかどうかを判断します。
flowchart TD
START["Fetch open issues<br/>created >3 days ago"] --> SCAN["For each issue"]
SCAN --> DUPE{"Has bot 'possible<br/>duplicate' comment?"}
DUPE -->|No| SKIP["Skip"]
DUPE -->|Yes| AGE{"Comment older<br/>than 3 days?"}
AGE -->|No| SKIP
AGE -->|Yes| ACTIVITY{"Any comments<br/>after dupe comment?"}
ACTIVITY -->|Yes| SKIP
ACTIVITY -->|No| REACTION{"Author gave 👎<br/>on dupe comment?"}
REACTION -->|Yes| SKIP
REACTION -->|No| EXTRACT["Extract duplicate<br/>issue number"]
EXTRACT --> CLOSE["Close as duplicate"]
style CLOSE fill:#E53935,color:#fff
style SKIP fill:#4CAF50,color:#fff
設計の要となるのが、作者による拒否権の仕組みです。スクリプトは重複検出コメントへのリアクションを確認し、Issue の作者本人(reaction.user.id === issue.user.id で照合)からの 👎 があるかどうかをチェックします。
scripts/auto-close-duplicates.ts#L228-L241
作者が異議を唱えた場合、Issue はオープンのまま維持されます。3 日以内に何も反応がなければ、経緯を説明して再オープンを促すコメントとともに Issue がクローズされます。
@claude メンションハンドラー
claude.yml ワークフローは、GitHub の Issue や PR 上で Claude Code と自然言語でやり取りできるようにします。Issue コメント、PR レビューコメント、PR レビュー本文、Issue 本文に @claude が含まれているときにトリガーされます。
if: |
(github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
(github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) ||
...
これにより、すべての Issue と PR が Claude Code との対話ポイントになります。ワークフローは Sonnet を使い、読み取り専用のパーミッションで anthropics/claude-code-action@v1 を実行します — コードの読み取りと返答はできますが、変更のプッシュはできません。
sequenceDiagram
participant U as User
participant GH as GitHub
participant WF as claude.yml
participant CC as Claude Code Action
U->>GH: Comment: "@claude explain this error"
GH->>WF: issue_comment event
WF->>WF: Check: contains '@claude'?
WF->>CC: Run claude-code-action
CC->>GH: Read repository context
CC->>GH: Post response comment
セキュリティのサンドボックス化:3 層の防御
パブリックリポジトリで AI エージェントを運用するには、セキュリティへの真剣な取り組みが必要です。claude-code リポジトリは 3 層の防御を実装しています。
第 1 層:gh.sh — サンドボックス化された CLI ラッパー
scripts/gh.sh のスクリプトは、gh CLI を読み取り系の 4 つのサブコマンドに制限します。
case "$CMD" in
"issue view"|"issue list"|"search issues"|"label list")
;;
*)
echo "Error: only 'issue view', 'issue list', 'search issues', 'label list' are allowed"
exit 1
;;
esac
フラグは許可リスト方式(--comments、--state、--limit、--label)で管理されています。search コマンドでは repo:、org:、user: 修飾子を明示的にブロックし、クロスリポジトリアクセスを防いでいます。Issue 番号は数値のみ受け付け、リポジトリは常に GH_REPO でスコープされ、他のリポジトリへの抜け道を塞いでいます。
これは多重防御の考え方です。Claude Code が予期しない gh コマンドを生成したとしても、ラッパーが確実に拒否します。
第 2 層:DevContainer ファイアウォール
.devcontainer/devcontainer.json の DevContainer 設定は NET_ADMIN と NET_RAW ケーパビリティを要求します。
"runArgs": [
"--cap-add=NET_ADMIN",
"--cap-add=NET_RAW"
]
これらのケーパビリティにより、.devcontainer/init-firewall.sh のファイアウォールスクリプトが iptables ルールを設定できます。ファイアウォールはデフォルト拒否ポリシーを実装しています。
iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT DROP
その上で、以下の宛先への通信のみを許可します。
- GitHub(
api.github.com/metaから IP アドレスを取得) - npm レジストリ(
registry.npmjs.org) - Anthropic API(
api.anthropic.com) - Statsig(
statsig.anthropic.com、statsig.com) - VS Code マーケットプレイスおよびアップデートサーバー
- Sentry(エラーレポート用)
ファイアウォールは自己検証も行います。example.com に到達できないこと、api.github.com に到達できることを確認し、どちらかの確認に失敗した場合はスクリプトがエラーで終了します。
第 3 層:エンタープライズ管理設定
examples/settings/ のサンプルは、組織がポリシーレベルで Claude Code をロックダウンする方法を示しています。
flowchart TD
subgraph "Layer 1: CLI Wrapper"
GH["gh.sh<br/>4 allowed subcommands<br/>Flag allowlist<br/>Injection prevention"]
end
subgraph "Layer 2: Network Firewall"
FW["init-firewall.sh<br/>Default-deny iptables<br/>Allowlisted domains only<br/>Self-verification"]
end
subgraph "Layer 3: Enterprise Settings"
ES["managed-settings.json<br/>Permission lockdown<br/>Hook restrictions<br/>Tool deny lists"]
end
GH --> FW --> ES
style GH fill:#4CAF50,color:#fff
style FW fill:#2196F3,color:#fff
style ES fill:#9C27B0,color:#fff
エンタープライズ設定プロファイル
リポジトリには段階的な制限強度を示す 3 つのデプロイプロファイルが含まれています。
| 設定 | Lax | Strict | Bash-Sandbox |
|---|---|---|---|
--dangerously-skip-permissions を無効化 |
✅ | ✅ | |
| プラグインマーケットプレイスをブロック | ✅ | ✅ | |
| ユーザー/プロジェクトのパーミッションルールをブロック | ✅ | ✅ | |
| ユーザー/プロジェクトのフックをブロック | ✅ | ||
| WebSearch/WebFetch ツールを拒否 | ✅ | ||
| Bash に承認を要求 | ✅ | ||
| Bash をサンドボックスで実行 | ✅ |
examples/settings/settings-strict.json の strict プロファイルは完全なロックダウンの例を示しています。
{
"permissions": {
"disableBypassPermissionsMode": "disable",
"ask": ["Bash"],
"deny": ["WebSearch", "WebFetch"]
},
"allowManagedPermissionRulesOnly": true,
"allowManagedHooksOnly": true,
"strictKnownMarketplaces": []
}
主要なプロパティの意味は次のとおりです。
allowManagedPermissionRulesOnly:ユーザーおよびプロジェクトレベルのパーミッションルールを無視し、エンタープライズ管理のルールのみを適用するallowManagedHooksOnly:ユーザー設定やプロジェクト設定からのフックをブロックするstrictKnownMarketplaces: []:空配列にすることですべてのプラグインマーケットプレイスをブロックするdisableBypassPermissionsMode: "disable":ユーザーが--dangerously-skip-permissionsフラグを使えないようにする
bash-sandbox プロファイルのサンドボックス設定はさらに踏み込んでおり、bash コマンドがアクセスできるドメインを制限し、ローカルソケットへのアクセスもブロックします。
ヒント: これらの設定ファイルは設定階層のどのレベルでも適用できますが、
strictKnownMarketplaces、allowManagedHooksOnly、allowManagedPermissionRulesOnlyなどのプロパティはエンタープライズ設定でのみ有効になります。組織に展開する前に、managed-settings.jsonに適用してローカルで動作確認しておきましょう。
スタレネスのスイープ
scripts/sweep.ts のスイープスクリプトはスケジュールで実行され、2 つの処理を担います。非アクティブな Issue をスタレとしてマークすること、そしてライフサイクルラベルの期限が切れた Issue をクローズすることです。
markStale 関数は、更新日時の古い順にオープンな Issue をページネーションで処理します。プルリクエスト、ロック済み Issue、担当者がアサインされた Issue、すでにスタレの Issue、および 👍 が 10 件以上ある Issue はスキップします。それ以外でスタレウィンドウ内に更新のなかった Issue にラベルを付与します。
closeExpired 関数は各ライフサイクルラベルを順に処理し、ラベルが付与されてからタイムアウトを超えた Issue を特定して、ラベル付与後に人間のアクティビティがあるかどうかを確認します。これが最後の安全網です。スクリプトが本来クローズしようとしていても、ラベル付与後に実際のユーザー(ボット以外)がコメントしていれば、その Issue はクローズを免れます。
スクリプトは --dry-run モードもサポートしており、実際には変更を加えずに実行結果をログ出力します。ライフサイクル設定の変更をテストする際に欠かせない機能です。
シリーズのまとめ
この 5 回のシリーズを通じて、anthropics/claude-code リポジトリをハイレベルなアーキテクチャから順に読み解いてきました。プラグインのコンポーネントモデル、マルチエージェントのオーケストレーションパターン、フックの実装、そして GitHub 自動化のインフラストラクチャまで、その全体像を網羅しました。このリポジトリは Claude Code の拡張方法と、Claude Code による自己管理の方法を示すリファレンス実装です。
ここで取り上げたパターン — 設定より規約を優先するプラグイン探索、モデルのティアに合わせたエージェント設計、フェイルオープン vs. フェイルクローズのフック哲学、そして多層のセキュリティサンドボックス化 — は、このリポジトリに限らず広く応用できる考え方です。AI エージェントを開発ワークフローに統合しようとするあらゆるチームにとって、基礎となるビルディングブロックです。
Claude Code プラグインを構築するなら、plugin-dev スキルのドキュメントから始めましょう。エンタープライズ展開を検討しているなら、設定サンプルが出発点になります。そして GitHub 上で AI エージェントをスケールさせることに興味があるなら、ここで紹介した自動化インフラが現時点で最良のリファレンス実装です。