claude-code-actionでissueにラベルを自動で付け、不足情報を問い返す
issueが立ったらclaude-code-actionが起動し、ラベルを付けて再現手順の足りない報告には問い返しを書く設定です。権限とツール許可の切り方、外部報告者で落ちる条件までまとめます。
新しいissueが立った瞬間にラベルを付け、再現手順が足りなければ報告者に問い返す。この仕分けは、anthropics/claude-code-actionにpromptを渡し、トリガーをissuesのopenedにすれば組めます。@claudeと書かれるのを待つ必要はありません。
ただし、プレーンテキストのpromptだけでは、ClaudeはシェルもGitHub APIも使えません。ラベル付けとコメント投稿に使うツールを、許可リストで1つずつ渡す必要があります。この記事は、そのワークフローの全体、権限の切り方、promptの書き方、そして外部の報告者のissueで実行が落ちる条件をまとめます。
issueが立つたびに走るワークフローの全体像
まず全体のYAMLを示します。.github/workflows/issue-triage.ymlとして置く想定です。
name: Issue Triage
on:
issues:
types: [opened]
jobs:
triage:
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: read
issues: write
id-token: write
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 1
- uses: anthropics/claude-code-action@v1
env:
GH_TOKEN: ${{ github.token }}
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
prompt: |
Triage issue #${{ github.event.issue.number }}
in ${{ github.repository }}.
1. Run `gh label list` and choose labels only from that list.
2. Apply them with `gh issue edit`.
3. If the report lacks reproduction steps, the version,
or the expected behavior, post one comment asking for
exactly the missing items with `gh issue comment`.
Do not create labels. Do not close or edit the issue body.
claude_args: |
--max-turns 6
--allowedTools "Bash(gh label list *),Bash(gh issue view *),Bash(gh issue edit *),Bash(gh issue comment *)"ドキュメントにあるのは、promptを渡すと任意のGitHubイベントで自動化モードになること、プレーンテキストのpromptにはツールを許可して初めてシェルやAPIが使えること、の2点です。issuesイベントでラベルを付ける完成形のYAMLは載っていません。上の構成は、その2点とルールの書式を組み合わせた例です。ghコマンドにGH_TOKENを渡す部分も、ドキュメントの例にはありません。手元のリポジトリで動作を確かめてから使ってください。
注目する行は次の4つです。
このワークフローの要になる4行
on.issues.types
[opened]だけにします。labeledやeditedを含めると、人がラベルや本文を変えるたびにワークフローが再実行され、APIと実行分数を使います。permissions
issues: writeがラベル付けとコメントに要る権限です。contents: readはactions/checkout用、id-token: writeはGitHub Appによる既定の認証に必要です。prompt
この入力があるので、Claudeは
@claudeの言及を待たずに動きます。対象のissue番号をpromptに埋め込み、やってよいことと、やってはいけないことを両方書きます。--allowedTools
使えるコマンドを
ghの4つの操作に絞ります。ラベルの作成も、issueのクローズも許可していないので、promptに頼まなくても実行できません。
「権限」と「ツール許可」は別の層にある
ここで混ざりやすいのが、issues: writeと--allowedToolsの役割です。
permissionsは、GitHub側の話です。ワークフローが使うトークンに、issueを書き換える力があるかを決めます。--allowedToolsは、Claude側の話です。その力を、Claudeがどのコマンド経由で使えるかを決めます。どちらか片方が欠けると、ラベルは付きません。
issues: writeが無い: コマンドは許可されていても、GitHubが書き込みを拒否する--allowedToolsが無い: 書き込みの権限はあっても、Claudeがコマンドを実行できない
@claude向けの例は、contents: write・pull-requests: write・issues: writeを並べています。仕分けだけなら、コードもPRも書き換えないので、contents: readまで下げられます。Claude GitHub Appのインストール時に付く権限は、機能全体で共有する大きな集合です。アクションは、その中のContents・Issues・Pull requestsの3つを使うと説明されています。ワークフロー側のpermissionsで、実際に使う分へ絞ります。
許可の書き方は2通りあります。上のYAMLはclaude_argsの--allowedToolsで書きました。ルールを設定として分けたいなら、settings入力にpermissions.allowを渡せます。
settings: |
{"permissions": {"allow": ["Bash(gh label list *)", "Bash(gh issue edit *)"]}}ghの代わりに、GitHub MCPツールを--allowedToolsで許可する経路もあります。毎日のレポート例はmcp__github__list_commitsとmcp__github__list_issuesを許可し、GitHub APIの読み取りをこの経路に任せてチェックアウトを省いています。ラベル付けやコメント投稿に使うMCPツール名は、その例には載っていません。
Bashルールの書式では、*をサブコマンドの後ろに置きます。Bash(gh issue edit *)はgh issue editで始まるコマンドだけを通し、Bash(gh *)のように広げるとghの全サブコマンドを通します。ラベルの付け外しに使うのはeditだけなので、gh *まで広げる理由はありません。
promptに書くのは判断基準と禁止事項
自動で走るpromptは、人が見ていない前提で書きます。効くのは次の3点です。
- ラベルは既存の一覧から選ばせます。
gh label listを先に実行させ、その結果の中からだけ選ばせます。ラベル作成のコマンドは許可していないので、一覧に無いラベルは付けられません。 - 問い返しの条件は具体的に書きます。「情報が足りなければ」だけでは、毎回ぶれます。再現手順・バージョン・期待する動作のどれが欠けたときに書くかを列挙します。
- やらないことも明記します。クローズ、本文の編集、担当者の割り当てなどです。
ラベルの基準は、CLAUDE.mdに書いておく手もあります。リポジトリのルートに置いたCLAUDE.mdにスタイルやレビュー基準を書くと、Claudeがそれに従います。上のワークフローがactions/checkoutを含むのは、このためです。
## issue の仕分け
- crash / error を含む報告は bug
- 「〜できると嬉しい」は enhancement
- 使い方の質問は question(再現手順の問い返しは不要)
- 再現手順の問い返しは bug のときだけ。1 issue につき 1 回最後の行は、無駄なコメントを増やさないための線引きです。質問にまで再現手順を求めると、報告者には的外れな返信になります。
問い返しの文面は、欠けた項目だけを挙げさせる
曖昧なprompt
「情報が足りなければコメントしてください」と書くだけでは、毎回「詳細を教えてください」のような定型文になりがちです。
項目を指定したprompt
「再現手順・バージョン・期待する動作のうち、欠けているものだけを箇条書きで尋ねる」と書くと、報告者は何を足せばよいかが分かります。
外部の報告者のissueでは実行が落ちる
この構成で最初にぶつかりやすいのが、実行者の確認です。
アクションは起動前に、トリガーした人を2つの観点で確認し、どちらかで弾かれると実行が失敗します。
- 書き込み権限: issueやPRのイベントでは、トリガーしたユーザーにリポジトリへの書き込み権限が要ります
- 人間の実行者か: ボットは
allowed_botsに載せない限り拒否されます
自分のチームだけが使うリポジトリなら、issueを立てるのは書き込み権限のある人だけなので問題になりません。公開リポジトリで外部の人が立てるissueを仕分けたい場合、この確認に引っかかります。
落とし穴
外部の人が立てたissueでは、仕分けが走らずに失敗する
書き込み権限のないユーザーを許可するには、allowed_non_write_usersを設定し、あわせて自分で用意したgithub_tokenを渡す必要があります。ドキュメントはこの2つをセットで説明しています。許可する範囲が広がるので、後述の注意を読んでから決めてください。
依存関係の更新ボットなどが立てるissueも、同じ確認に引っかかります。仕分けたい場合は、そのボットをallowed_botsに列挙します。
外部の文面をそのまま読ませるときの注意
仕分けの対象は、第三者が書いた文章です。issueの本文に「これまでの指示を無視して〜」のような一文が混ざっていても、Claudeはそれを読みます。ドキュメントは、プロンプトインジェクションを、指示を上書きしようとする悪意あるテキストの挿入と説明し、権限システムなどの防御を挙げています。ワークフロー側でできることは、被害の上限を決めておくことです。
- 許可するコマンドを、ラベル付けとコメントに必要な分だけにする(
Bash(gh *)やBashの全許可にしない) contents: readに留め、コードやブランチを書き換えられない状態にする- 認証情報はGitHub Secretsに置く。キーをリポジトリにコミットしないことは、公式の注意事項にもある
allowed_non_write_usersを使うなら、許可した範囲で何が起こりうるかを先に見積もる
上のYAMLの許可リストでも、最悪の場合は想定より広く動きます。gh issue edit *はタイトル・本文・担当者・マイルストーンの変更も通し、別のissue番号も指定できます。gh issue comment *は、コメントの複数回の投稿を止めません。一方で、コードやブランチ、リポジトリの設定には、権限の側でも許可の側でも届きません。
範囲を絞りたいなら、Bash(gh issue edit * --add-label *)のように、*を引数の途中にも置けます。ルールはコマンド全体の文字列に照合されるので、--add-labelを含む形に狭められます。ただし末尾の*が残る限り、後ろに別のオプションが続く形までは止められません。
@claudeメンションとの違い
@claudeを使う構成との違いも比べます。導入手順そのものは、Claude CodeをGitHub Actionsに組み込むで扱っています。
| 観点 | @claudeメンション | issue作成で起動する自動化 |
|---|---|---|
| 起動の条件 | @claudeメンション人がトリガーフレーズを書く | issue作成で起動する自動化issueが立つ(opened) |
prompt入力 | @claudeメンション書かない(対話モード) | issue作成で起動する自動化書く(自動化モード) |
| 結果の出る場所 | @claudeメンション同じissueへのコメント | issue作成で起動する自動化実行ログ。コメントはpromptで指示したときだけ |
| 向く用途 | @claudeメンション個別の相談・実装の依頼 | issue作成で起動する自動化全件への同じ仕分け |
対話モードでは、新しく開かれたissueの本文やタイトルにトリガーフレーズがあるときも反応します。つまり「issueが立ったら必ず」ではなく「@claudeと書かれたissueが立ったら」です。全件を仕分けたいなら、自動化モードのほうが条件に合います。
自動化モードの出力は、既定ではワークフローの実行ログに出ます。コメントとして残したいなら、promptで投稿を指示し、投稿に使えるツールを許可しておく必要があります。上のYAMLがgh issue commentを許可しているのは、このためです。
動かないときの切り分け
issueを立てても何も起きない、または実行は走ったのにラベルが付かない場合は、上から順に確かめます。
ラベルが付かないときの順番
- QActionsのタブに実行が出ている?答えが実行が出ていない答えがはい
トリガーの
on.issues.typesと、ワークフローがデフォルトブランチにあるかを確かめます。ワークフローの有効化も見ます。 - Q実行者の確認を通っている?答えが実行者の確認で失敗する答えがはい
外部ユーザーやボットが立てたissueなら、前の節の
allowed_non_write_usersとallowed_botsを確認します。 - Q権限のエラーは出ていない?答えが権限のエラーが出る答えがはい
permissionsにissues: writeがあるかを確かめます。
それでも付かないとき
実行ログでClaudeが許可の無いコマンドを試していないかを見て、--allowedToolsに不足を足します。
認証まわりでは、APIキーまたはOAuthトークンがリポジトリのSecretsに入っているかも見ます。トラブルシューティングでも、最初に確認する項目です。
料金を抑える設定
issueの件数が多いリポジトリでは、1件ごとの実行が積み上がります。1回の実行は、GitHub Actionsの分数とAPIトークンの両方を使います。ドキュメントが挙げる抑え方のうち、この用途に効くのは次の3つです。
--max-turnsで1回の反復を制限する(上のYAMLは6)- ワークフローにタイムアウトを付ける(上のYAMLは10分)
- 同時実行をconcurrencyで絞る
--modelで軽いモデルを選ぶ手もあります。claude_argsに--model claude-sonnet-5のように書くと、既定のモデルの代わりに使えます。仕分けのような短い判断なら、モデルを下げて試す価値があります。CLAUDE.mdを簡潔に保つことも、毎回読む分のコストを抑えます。
まとめ
issue作成で起動する仕分けは、on.issues.types: [opened]・prompt・許可リストの3点で組み立てます。権限はissues: writeまでで足り、コードを書き換える権限は要りません。公開リポジトリで使う場合は、書き込み権限のない報告者が弾かれる条件を、最初に決めておく必要があります。
最初は、ラベル付けだけを許可して数日動かし、付いたラベルを見て基準を直す進め方が現実的です。問い返しのコメントは、基準が安定してから許可に足せます。毎朝の要約のように、時刻で動かす構成はclaude-code-actionをcronで回す記事にあります。
参照した一次ソース
この記事の記述は、次の一次ソースと照合しています。更新時に参照先が変わった場合は一覧も更新します。