Claude Media
Claude Codeでgh extensionを自作する — ghのサブコマンドでチーム用ツールを配る

Claude Codeでgh extensionを自作する — ghのサブコマンドでチーム用ツールを配る

gh extensionの条件、bashでの雛形、認証をghに任せる書き方、gh extension installでの配布までを、Claude Codeに書かせて検証させる流れでたどります。

gh extensionは、gh-で始まる名前のリポジトリに実行ファイルを1本置くだけで、ghのサブコマンドを増やせる仕組みです。チーム専用の定型作業をgh team-prのような1コマンドにして配れます。

Claude Codeには、雛形の肉付けと動作の検証を任せます。GitHubへの認証はgh本体に預けます。この分担にすると、スクリプトの中にトークンを書く場面がなくなります。

ghの拡張が成り立つ条件

GitHub CLIの拡張とは、リポジトリ名がgh-で始まり、ルートに同名の実行ファイルを持つリポジトリのことです。拡張名はリポジトリ名からgh-を除いた部分になります。gh-team-prなら、呼び出しはgh team-prです。

条件は次のとおりです。

  • リポジトリ名がgh-で始まる
  • ルートに、リポジトリ名と同じ名前の実行ファイルがある。または、プリコンパイル済みのバイナリがリリースに添付されている
  • ghの組み込みコマンドは上書きできない。名前がぶつかるときはgh extension exec <拡張名>で呼ぶ
  • インストールはユーザー単位。別のマシンや別のユーザーには引き継がれない

実行ファイルをスクリプトで用意する場合、ドキュメントはbashを勧めています。bash以外でも書けますが、利用者の環境にそのインタプリタが入っていることが前提になります。インタプリタに依存したくないときはプリコンパイル型を選びます。

型配布物利用者側の前提向く場面
スクリプト型配布物ルートの実行ファイル利用者側の前提インタプリタ(bash推奨)向く場面数十行の定型作業
プリコンパイル型配布物OSとCPUごとのバイナリ利用者側の前提なし向く場面処理が重い、依存が多い

プリコンパイル型のバイナリはgh-whoami-windows-amd64.exeのように、末尾をOSとアーキテクチャで終える名前にします。Windows向けは.exeが必須です。この記事ではスクリプト型だけを扱います。

gh 2.90.0で雛形を作ると何ができるか

雛形はgh extension createが作ります。ログイン済みでないと、このコマンドはgh auth loginを促して止まります。以下はgh 2.90.0で、設定ディレクトリを空にしたうえでダミーのトークンを渡して試した結果です。

gh extension create team-pr
cd gh-team-pr
gh extension install .
gh team-pr

createが作るのは、gh-team-prディレクトリです。中身はgit init済みのリポジトリ(コミットはinitial commitの1つ)と、実行権限付きのgh-team-prファイルでした。ファイルの本体はecho "Hello gh-team-pr!"だけで、残りはコメントアウトされた書き方の見本です。見本には、引数の素通しとgh api graphqlのテンプレート出力が入っています。

gh extension install .の後、gh team-prはHello gh-team-pr!を表示します。インストール先のディレクトリを見ると、実体のコピーではなくリポジトリ内のファイルへのシンボリックリンクでした。ローカルインストールでは、スクリプトを書き換えれば次の実行から反映されます。

gh extension install --helpにも、ローカル版は「リポジトリ直下の、リポジトリ名と同名の実行ファイル」へのリンクとして管理されると書かれています。ファイル名を変えると、リンク先が見つからず実行に失敗します。

Claude Codeに書かせて、3つの経路を実行させる

雛形ができたら、中身をClaude Codeに書かせます。ここで効くのは、検証の経路まで依頼文に入れておくことです。

gh-team-pr を実装してください。
- 雛形の構成(直下に実行ファイル1本)を崩さない
- 引数は --limit N と --help だけ。未知のフラグは標準エラーに出して終了コード2
- 認証は gh に任せる。トークンを読み書きしない。未ログインなら案内して終了コード1
- 終わったら gh extension install . で入れ、
  --help / 未知のフラグ / 未ログイン の3経路を実際に実行して終了コードを報告

出来上がるスクリプトの一例が次です。ドキュメントのフラグ処理の例(caseで--helpと値付きフラグを読む形)に沿っています。

#!/usr/bin/env bash
set -euo pipefail
 
usage() { echo "usage: gh team-pr [--limit N] [--help]"; }
limit=20
while [ $# -gt 0 ]; do
  case "$1" in
    --limit) limit="$2"; shift ;;
    -h|--help) usage; exit 0 ;;
    *) echo "unknown flag: $1" >&2; usage >&2; exit 2 ;;
  esac
  shift
done
 
if ! gh auth status >/dev/null 2>&1; then
  echo "gh auth login を先に実行してください" >&2
  exit 1
fi
 
exec gh pr list --limit "$limit" --json number,title,author \
  --jq '.[] | "#\(.number) \(.title) (@\(.author.login))"'

このスクリプトで3経路を実行すると、結果は次のとおりでした(gh 2.90.0、ログインしていない状態)。

実行終了コード出力
gh team-pr --help終了コード0出力usage: gh team-pr [--limit N] [--help]
gh team-pr --bogus終了コード2出力unknown flag: --bogusと使い方
gh team-pr(未ログイン)終了コード1出力gh auth login を先に実行してください

終了コードを返す設計にしておくと、Claude Codeは失敗の理由を判別できます。「何も出なかった」ではなく「2で止まった」「1で止まった」と読めるためです。未ログインの経路を実際に踏ませておけば、利用者のマシンで初めて試すときの驚きも減ります。

拡張のリポジトリに置くCLAUDE.mdには、次のような規約を書いておくと、後から機能を足すときにも崩れません。

# gh-team-pr
- 実行ファイルはリポジトリ直下の gh-team-pr 1本。拡張子を付けない
- bashで書く。他のインタプリタは使わない
- GitHubとの通信は gh pr / gh api 経由だけにする。curlとトークンの直書きは禁止
- 変更したら gh team-pr --help と未知のフラグの終了コードを実行して確かめる

認証をghに任せる設計

拡張の中で認証を扱わないのが、いちばんの設計方針です。ghは自分のログイン状態を持っているので、スクリプトはgh pr listやgh apiを呼ぶだけで、呼び出した利用者本人の権限で動きます。

ドキュメントが示す書き方は次の3つです。

  • データ取得: --jsonで欲しいフィールドを指定し、--jqで絞り込む。対応するコマンドがなければgh apiでAPIを直接呼ぶ
  • 引数の渡し方: 組み込みコマンドは入力を求めてくることがあるため、gh issue create --title "My Title" --body "Issue description"のように必要な値を引数で渡す
  • 終了処理: 末尾でexec gh ...を使うと、ghの終了コードがそのまま拡張の終了コードになる

2つ目はClaude Codeから呼ぶときに特に重要です。対話プロンプトが出ると、Claudeが走らせたシェルはそこで止まります。

配布: リポジトリを作り、利用者がinstallする

配布の手順は短く済みます。

手順

拡張を配る手順

  1. 1

    リポジトリを作って push する

    ドキュメントの例は、この下に示す3行です。リポジトリ名はgh-付きで作ります。

  2. 2

    トピックを付ける(任意)

    gh-extensionトピックを付けると、トピックページに一覧表示されます。社内向けなら省いて構いません。

  3. 3

    利用者がインストールする

    gh extension install OWNER/gh-team-prを1回実行します。以降はgh team-prで呼べます。

git init -b main
git add . && git commit -m "initial commit"
gh repo create gh-team-pr --source=. --public --push

ドキュメントの例は--publicです。非公開リポジトリから配る場合の挙動は記載がないため、メンバーのアカウントでgh extension installが通るかを先に試してください。

インストール時の挙動はgh extension install --helpに書かれています。リモートのリポジトリでは、まずリリース成果物(プリコンパイル済みバイナリ)を探します。リリースがなければリポジトリ自体をクローンし、ルートの実行ファイルを使います。スクリプト型はこの後者の経路です。

バージョンを固定したいときは--pinにタグかコミットを渡します。

gh extension install OWNER/gh-team-pr --pin v1.0.0
gh extension upgrade --all --dry-run
gh extension upgrade --all

upgrade --allは、入っている拡張をまとめて更新します。--dry-runを付けると更新の有無だけを見られます。拡張を実行すると、ghは24時間に1回、新しい版を調べて更新の案内を出します。

GitHub Enterprise Serverの利用者がgithub.com上の拡張を入れるときは、OWNER/REPOではなく完全なURLを渡します。同名の拡張が入っているとインストールは失敗するため、フォークを入れ直す場合は先にアンインストールします。

スクリプトで足りなくなったらプリコンパイル型へ

処理が重くなった、依存が増えた、利用者のインタプリタに頼りたくない、といった場面では、同じgh extension createにオプションを付けて雛形を作り直せます。--precompiled=goならGoの雛形とワークフローの雛形、--precompiled=otherならGo以外の言語向けのワークフロー雛形が出ます。otherでは、script/build.shにビルド手順を書いておく必要があります。

リリース時のクロスコンパイルは、gh-extension-precompileというアクションをワークフローに入れると自動化できます。Goで書く場合は、ghの機能をライブラリとして使えるgo-ghも用意されています。ビルドで出るバイナリは、バージョン管理にコミットしないよう注意します。ドキュメントの手順も.gitignoreにバイナリ名を足してからコミットする流れです。

Claude Codeに移植を頼むときは、「スクリプトの終了コードと出力は変えない」と条件を付けます。前節の3経路を再実行させれば、移植前後で挙動が揃っているかを同じ物差しで見られます。

Claude Codeにこの拡張を呼ばせる

配った拡張は、人が打つのと同じコマンドとしてClaude Codeからも呼べます。ここで決めるのは、承認の範囲です。

Bashの許可ルールは、コマンド文字列に*を当てて書きます。サブコマンドの後ろに*を置くのが基本で、Bash(gh team-pr *)ならgh team-prの呼び出しだけを通します。末尾が「空白+*」なら、引数なしのgh team-prにも一致します。Bash(gh *)と書くと、ghのすべてのサブコマンドが対象になる点は注意が要ります。

{
  "permissions": {
    "allow": ["Bash(gh team-pr *)"]
  }
}

&&や;でつないだ複合コマンドでは、すべての部分が許可に当たらない限り承認を求められます。gh team-pr && 別のコマンドは、前半だけでは通りません。

読み取り専用の拡張なら、この許可は気楽に置けます。Issueを作る、ブランチを消すといった書き込みを伴う拡張は、許可に入れず都度承認にしておくほうが安全です。

呼び出しを定型にするなら、プロジェクトのスキルにします。リポジトリの.claude/skills/にコミットすれば、チーム全員に届きます。

---
name: team-prs
description: チームのオープンPRを一覧する
disable-model-invocation: true
allowed-tools: Bash(gh team-pr *)
---

allowed-toolsが効くのは、スキルを呼んだそのターンだけです。次のメッセージを送ると許可は外れます。セッション全体で通したいときは、上のようにsettingsの許可ルールへ入れます。スキルの書き方全般は、自作スキルで/code-reviewを置き換える手順にまとめています。

ghに閉じた作業であれば拡張で足ります。複数のサービスをまたぐ、あるいはツール定義をClaudeに渡したい場合は、MCPサーバーの自作のほうが合います。

手段向く用途人が直接使えるか
gh extension向く用途GitHub操作の定型化人が直接使えるかはい(gh team-pr)
スキル向く用途手順書をClaudeに渡す人が直接使えるかはい(/team-prs)
MCPサーバー向く用途外部サービスをツールとして公開人が直接使えるか通常はClaude経由

つまずきやすい点

  • 名前が組み込みコマンドとぶつかる: 組み込みは上書きできません。gh extension execで呼べますが、配るなら名前を変えるほうが楽です
  • Windowsで実行ビットが立たない: git init -b main、git addの後にgit update-index --chmod=+x <ファイル>で付けます
  • 別のマシンで動かない: インストールはユーザー単位です。新しい環境ごとにgh extension installが要ります
  • ログイン前にcreateやlistが動かない: 前述のとおり、gh auth loginかGH_TOKENが先です
  • bash以外で書いた: 利用者側にそのインタプリタがないと動きません。配る相手の環境が揃わないなら、bashに戻すかプリコンパイルにします

まとめ

チーム用の定型作業を、まずbash数十行のgh-リポジトリにしてみる、という順が手堅い流れです。認証をスクリプトに持ち込まず、終了コードで失敗を区別できるようにしておけば、人にもClaude Codeにも同じコマンドを渡せます。VS Code向けの配布物を作る場合は、VS Code拡張の自作手順が参考になります。

参照した一次ソース

この記事の記述は、次の一次ソースと照合しています。更新時に参照先が変わった場合は一覧も更新します。

  1. docs.github.com/en/github-cli/github-cli/creating-github-cli-extensions
  2. docs.github.com/en/github-cli/github-cli/using-github-cli-extensions
  3. cli.github.com/manual/gh_extension
  4. code.claude.com/docs/en/skills
  5. code.claude.com/docs/en/permissions
この記事を共有:XはてブLinkedIn