polyglot-git-hooks について
polyglot-git-hooks は、多言語リポジトリで Lefthook を設定するスキルです。ステージ済みファイルやプッシュ前のローカル Git チェックを構成し、CI を補完します。
主な機能
- 01リポジトリ直下の lefthook.yml
- 02コミット前のステージ済みファイル検査
- 03プッシュ前の型チェックとテスト
- 04サブディレクトリ別の言語コマンド
- 05並列実行と自動修正のステージング
活用シーン
- 01リポジトリへのローカル整形・lint ゲート追加
- 02TypeScript と Python のサブディレクトリでのフック設定
- 03多言語モノレポのフック設定の集約
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ bm629/ agent-skills --skill polyglot-git-hooksインストール後、Claude Code で/polyglot-git-hooksを入力して読み込まれたか確認します。
同梱ファイルreferences/lefthook-reference.mdreferences/sources.md
詳細
polyglot-git-hooks — SKILL.md
バリアント: 標準 · 使用するタイミング: このスキルが呼び出されると、
lefthook.ymlを生成して設定し、呼び出し元に制御を戻します。このスキルで扱うツールは Lefthook(Git フックマネージャー)です。このスキル名はその特徴に由来しており、1 つの設定から 複数の言語 にまたがるチェックを実行します。
概要
このスキルでは、Lefthook を使って複数言語のリポジトリに Git フックを設定します。Lefthook は、高速な Go 製の言語非依存フックマネージャーです。目標は、リポジトリのルートに 1 つの lefthook.yml を置き、複数の言語が混在するリポジトリ(たとえば、TypeScript と Python のサブツリー)で、ステージ済みファイルに対する 適切な高速チェック(フォーマットとリント)を pre-commit で、時間のかかるチェック(型チェックとテスト)を pre-push で並行して実行することです。これは「コミット/プッシュ前にローカルで早期に失敗を検出する」ためのレイヤーであり、CI を補完するものであって、置き換えるものではありません。このスキルが担当するのはフックの設定です。各ツールの詳細(Biome/Ruff/tsc/ty の使い方)はそれぞれのスキルに属し、このスキルでは参照のみ行います。
有効にするタイミング
- ✅ リポジトリの Git フック(新しい
lefthook.yml)を設定する場合、またはpre-commit/pre-pushゲートを追加する場合。 - ✅ コミット前にステージ済みファイルを対象として、またはプッシュ前にプッシュ対象のファイルを対象として、リント/フォーマット/型チェックを実行するよう設定する場合。
- ✅ サブツリーごとに異なるツールチェーンを使う複数言語対応またはモノレポのフックを設定する場合。
- ✅ husky+lint-staged、または Python の
pre-commitフレームワークから、単一設定による複数言語対応の構成に移行する場合。
次の場合は有効にしないでください:
- 特定のリンター/フォーマッター/型チェッカー自体を学習または設定する場合は、そのツールのスキル(
biome、ruff、ty、typescript-typecheck)を使用してください。 - CI パイプライン(サーバー上で実行する完全かつ正式なチェック)を設計する場合は、フックはローカルのゲートであり、CI は別のものです(ステップ 5 と
turborepoスキルを参照してください)。 - 組織のポリシーとして
--no-verifyの使用を禁止する場合は、フックの設定ではなく、限定的なポリシーフックに関する問題です(注意事項を参照してください)。
ワークフロー
ステップ 1:Lefthook を選び、その理由を把握する
Lefthook は、あらゆる言語の Git フックを管理する、依存関係のない単一のバイナリ(Go 製)です。複数言語のリポジトリでは、次の理由から他の選択肢より Lefthook を選んでください。
- husky + lint-staged との比較 — husky は JS/Node 専用で、ステージ済みファイルのフィルタリングには
lint-stagedを追加する必要があります。その結果、設定が.husky/pre-commit、package.json内のlint-stagedキー、package.jsonのスクリプトに分散し、node_modulesに大きな依存関係ツリーが追加されます。Lefthook なら、lefthook.yml1 つ、バイナリ 1 つ、ステージ済みファイルのネイティブなフィルタリングで済み、追加のランタイム依存関係もありません。フロントエンドとバックエンドが混在するチームでは、husky+lint-staged を使うとセットアップが 2 つに分かれがちです。 - Python の
pre-commitフレームワークとの比較 —pre-commitは Python 中心で(フックごとに専用のツール環境を管理します)、TS や複数言語のリポジトリにはあまり適していません。Lefthook はコマンドを並列実行し、Python スクリプトではなくコンパイル済みの単一バイナリであり、言語を問わず大規模なモノレポでも同様に動作します。(ただし、pre-commitの方がすぐに使えるフックのカタログは充実しており、小規模なプロジェクトや Python のみのプロジェクトではより簡単です。これは認めるべき妥当なトレードオフですが、ここでは選択しません。)
ステップ 2:インストールと有効化
Lefthook を開発依存関係またはバイナリとしてインストールし、lefthook install で .git/hooks に組み込みます。
- npm:
npm install lefthook --save-dev(@evilmartians/lefthookとしても公開されています)。lefthooknpm パッケージは独自のpostinstallを実行してlefthook installを呼び出すため、新規に実行するnpm installでフックが自動的に組み込まれます。これにより、npm ユーザーは新規クローン時の有効化の問題を最初から解決できます。(以下の新規クローンに関する注記を参照してください。) - バイナリ/npm 以外: Homebrew、
go install、pipx install lefthook、gem install lefthook、apt、snap、winget でインストールし、その後lefthook installを一度実行します。
lefthook.yml は リポジトリのルートに 1 つだけ配置します。lefthook install を実行して組み込みを確認します(冪等なので、再実行しても安全です)。
新規クローン時の有効化(重要)。 フックは各開発者のローカル .git/hooks にあり、これはコミットされません。そのため、リポジトリをクローンしたばかりのチームメイトは、lefthook install が実行されるまでフックが ありません。クローン時に自動で組み込む方法は 2 つあります。
- npm プロジェクト:
lefthookパッケージのpostinstall(npm install時に自動実行)を利用するか、インストール後に実行されるようpackage.jsonに"prepare": "lefthook install"スクリプトを明示的に追加します。(pnpmの場合は、pnpm.onlyBuiltDependencies/pnpm-workspace.yamlのonlyBuiltDependenciesにlefthookを追加してください。追加しないとpostinstallがスキップされ、フックが組み込まれません。) - npm 以外のプロジェクト: README に
lefthook installを必須のセットアップ手順として記載するか、ブートストラップ/セットアップスクリプトから実行します。
CI では通常、postinstall によるフックのインストールは避けたいものです。スキップするには CI=true を設定します(多くの CI ではすでに設定されています)。必要な場合は LEFTHOOK=1 を使って有効にしてください。
ステップ 3:lefthook.yml スキーマを作成する
トップレベルのキーはGit フック名です。pre-commit、pre-push(任意のフック名を使用できます:commit-msg、post-merge、…)。各フックの下で、作業を次のいずれかとして定義します。
commands:— 名前付きコマンドのマップ(従来の形式)、またはjobs:— ジョブのリスト(柔軟な形式。コマンド、スクリプト、独自のparallel/pipedフローを持つネストされたグループをサポートします)。グループに設定したglob/root/excludeは、すべてのネストされたジョブに適用されます。
読みやすい方を使用してください。ここでは commands: を使用した例を示します。フックレベルおよびコマンドレベルで、よく使用するオプションは次のとおりです。
| オプション | 設定箇所 | 動作 |
|---|---|---|
run: | command | シェルコマンド(sh 経由で実行)。必須です。 |
glob: | command | "*.{ts,tsx}" のように、一致するファイルに絞り込みます。glob のリストも使用できます(Lefthook ≥ 1.10.10)。run でファイルテンプレートが省略されている場合も、Lefthook は {staged_files}(pre-commit)/{push_files}(pre-push)を引き続き glob で絞り込み、一致するファイルがない場合はコマンドをスキップします。 |
root: | command | コマンドの作業ディレクトリをサブツリーに変更します(例:apps/web/)。サブディレクトリに package.json/設定ファイルがある場合に便利です。glob は引き続き Git リポジトリのルートを基準に計算されるため、root は glob の照合方法を変更しません。ただし、コマンドで使用するために Lefthook が {staged_files} に代入するファイルパスは root からの相対パスに変換されるため、ツールにはサブツリーからの相対パスが渡されます。 |
exclude: | command | glob と組み合わせて、正規表現(またはリスト)に一致するファイルを除外します。 |
stage_fixed: | command | コマンドの実行後、(絞り込まれた)ファイルを git add し、自動修正されたファイルを再度ステージングします。pre-commit でのみ機能します。 |
parallel: | hook | true → フックのコマンドを並行して実行します。 |
piped: | hook/group | true → 順次実行し、最初に失敗した時点で停止します。 |
skip:/only: | hook または command | 条件:true(常に)、merge/rebase/merge-commit、ref: main(ref: dev/* のような glob 形式の参照)、または run: <shell>(コマンドが 0 で終了した場合にスキップ)。 |
tags:/priority: | command | コマンドをグループ化/並べ替えます。 |
run: で使用できるファイルテンプレート変数。実行時に置換されます。
{staged_files}— コミット用にステージングされたファイル(pre-commitで設定されます)。{push_files}— コミット済みで、まだプッシュされていないファイル(pre-pushで設定されます)。{all_files}— Git で追跡されているすべてのファイル。{files}— カスタムのfiles:コマンドの結果。{cmd}—lefthook.ymlに記述されたコマンド(例:Docker 内で実行するようラップする場合に使用)。
ファイルリストが OS のコマンドラインの上限を超えるほど長い場合、Lefthook はリストを分割し、各チャンクに対してコマンドを順次実行します。
Step 4: 多言語構成の実例
TypeScript のサブツリーとPython のサブツリーがあるリポジトリ向けの、完全なルート lefthook.yml です。pre-commit では、高速で自動修正を行うステージ済みファイルのチェックを、言語ごとに対象を絞って並列に実行します。pre-push では、コードがマシンから出る前に、時間のかかる型チェックとテストのゲートを実行します。
# lefthook.yml — at the repo root
min_version: 2.0.0 # pin the major version; warn on older Lefthook
pre-commit:
parallel: true
commands:
# --- TypeScript / JavaScript: format + lint staged files, re-stage fixes ---
biome:
glob: "*.{js,jsx,ts,tsx}"
run: biome check --write {staged_files}
stage_fixed: true
# --- Python: lint-fix + format staged files in the python subtree ---
ruff:
root: "services/api/" # CWD for the command (a python subtree)
glob: "*.py" # computed from the repo root, not from `root`
run: ruff check --fix {staged_files} && ruff format {staged_files}
stage_fixed: true
pre-push:
parallel: true
commands:
# --- TypeScript: whole-project type-check (no file template; checks the project) ---
tsc:
glob: "*.{ts,tsx}"
run: tsc --noEmit
# --- Python: type-check the subtree ---
ty:
root: "services/api/"
glob: "*.py"
run: ty check
# --- Tests: run the suites (heavier; pre-push, not pre-commit) ---
test-ts:
glob: "*.{ts,tsx}"
run: npm test
test-py:
root: "services/api/"
glob: "*.py"
run: pytest
この構成にする理由:
pre-commit= 高速 + ステージ済みファイルのみ。biome check --writeとruff check --fix/ruff formatは{staged_files}のみを対象とするため、フックは高速で、コミットするファイルだけを編集します。stage_fixed: trueは自動修正を再ステージするため、修正済みの結果がコミットに含まれます(なお、stage_fixedが有効なのはここ、pre-commitのみです)。- サブツリーごとに対象を限定。 各コマンドは
glob(言語)と、Python 側ではroot(サブツリーのディレクトリ)によって対象を限定し、適切なファイルに適切なツールを実行します。rootが設定されていても、globはリポジトリルートからの相対パスに対して照合されることに注意してください。 pre-push= 低速 + 徹底的。 型チェック(tsc --noEmit、ty check)とテストはプロジェクト全体を対象とし、すべてのコミットで実行するには時間がかかりすぎるため、代わりにプッシュのゲートとして実行します。ここでの glob はコマンドを実行するかどうかだけを判断します(該当する言語の変更ファイルがなければスキップします)。型チェッカーとテストランナーは、変更されたファイルだけではなく、プロジェクト全体をチェックします。プッシュ時のコマンドを変更ファイルに限定したい場合は、{push_files}を利用できます。- 並列実行。
parallel: trueにすると、言語ごとの処理を並行して実行します。TS と Python のチェックは、互いの完了を待ちません。
各ツールの呼び出し(biome check、ruff check / ruff format、tsc --noEmit、ty check)は、それぞれのスキルが担当します。## Related を参照してください。このファイルが担当するのは、接続設定のみです。
Step 5: CI とフックの役割を分ける(重複させない)
フックと CI は異なるレイヤーです。コピーではなく、役割分担として接続してください。
- フック = ローカル、ステージ済み、高速。 コミットやプッシュがマシンから出る前に、フォーマット、lint、プッシュ時の簡易な型チェック/テストのゲートなど、明らかな問題を検出します。すべてのコミットで実行されるため、速度が重要です。
pre-commitはステージ済みファイルの高速チェックにとどめ、時間のかかる処理はpre-pushに移してください。 - CI = 完全、権威あるチェック。 CI はサーバー上でリポジトリ全体を対象に完全なテストスイートを実行し、マージ要件となる強制ゲートを担います。フックはローカルで実行され、回避可能だからです(Step 6)。実際にチェックが実行されたことを保証するのは CI です。
CI のパイプラインを lefthook.yml に重ねて記述しないでください。リポジトリでタスクランナーを使っている場合、フックのコマンドからパイプラインの各ステップを再列挙するのではなく、タスク(例:turbo run lint)を呼び出すことができます。ただし、パイプラインの定義自体はフック設定ではなく、タスクランナー側に置きます。タスクグラフについては turborepo スキルを参照してください。このスキルでは重複して定義しません。
CI がまだない場合の注意点(重要)。 リポジトリに CI パイプラインがまだない場合、フックが自動チェックの主なゲートとなります。フックを支えるサーバー側の網がないためです。その状況では、pre-push の基準を引き上げてください。プッシュ時に型チェックとテストの完全なスイートを実行し、未検証のものがマシンから出ないようにします。CI が導入されるまでは、フック一式を重要な支えとして扱ってください。CI が導入されたら、pre-push を高速なスモークテストのゲートに近づけ、網羅的な実行は CI に任せられます。
ステップ 6:--no-verify によるバイパスを文書化する
フックはローカルにあり、スキップできます。それが正当な状況と、黙って見過ごされる抜け穴ではないことをチームに周知してください。
git commit --no-verify(-n)とgit push --no-verifyは、Git フックを完全にスキップします。gitコマンドの前にLEFTHOOK=0(またはLEFTHOOK=false)を設定すると、そのコマンドの実行時に限り Lefthook が無効になります。LEFTHOOK_EXCLUDEはexclude_tags設定を上書きし、特定のタグやコマンドを除外します。
正当な用途:個人ブランチに未完成であることが明らかな WIP をコミットする場合や、フックが処理を妨げており、いずれにせよ CI で再チェックされる緊急のホットフィックスの場合です。バイパスが存在するため、強制されるゲートはフックではなく CI です(ステップ 5)。バイパスを意図的な操作にするため、--no-verify をブロックするポリシーフックを追加するチームもあります。公開されている例は wshobson/agents@block-no-verify-hook です。これは別の強制に関する事項であり、このスキルでは設定せず、参照先としてのみ記載しています。
ルール
必須ルール(決して破らないでください):
- リポジトリのルートに
lefthook.ymlを 1 つだけ置きます。 言語に依存しない単一の設定にすることが要点です。言語ごとに分割しないでください。 pre-commitは高速で、ステージ済みファイルのみを対象にします。{staged_files}に対してフォーマットと lint を実行し、stage_fixed: trueを指定します。リポジトリ全体の型チェックやテストスイート全体をpre-commitに配置しないでください。- 時間のかかるゲートは
pre-pushに配置します。 型チェックとテストはこちらで実行し、pre-commitには配置しません。 - すべてのコマンドの対象範囲を指定します。 各コマンドに
glob(サブツリーの場合はrootも)を指定して、適切なツールが適切なファイルに対して実行されるようにします。Python のツールを TS ファイルに実行したり、その逆を行ったりしないでください。 - このスキルが担当するのは配線であり、ツールではありません。 ツールのフラグについては
biome/ruff/ty/typescript-typecheckを参照し、CI/タスクグラフについてはturborepoを参照してください。ツールの説明を重ねて行ったり、複製したりしないでください。 min_version:でメジャーバージョンを固定します(現在のメジャーバージョン:Lefthook 2.x)。古いバイナリを使っているチームメイトに、動作がおかしくなっても気付かれないのではなく、警告が表示されるようにします。
推奨事項(必要に応じて変更できます):
- 言語ごとの単純なレーンには
commands:を優先し、ネストしたpiped/parallelのフロー制御が必要な場合はjobs:とグループを使用します。 - 独立した言語レーンを並行して実行するには、
parallel: trueを使用します。 - マージ/リベース中に実行すべきでないコマンドには、
skip: [merge, rebase]を追加します。
注意点
- クローン直後はフックがありません。 フックはコミット対象外の
.git/hooksに保存されるため、クローンしたチームメンバーはlefthook installを実行するまでフックを利用できません。「その人にはフックが実行されなかった」という症状が発生します。対処方法:npm のpostinstall(npm プロジェクトの場合)を利用するか、prepareスクリプトを追加するか、lefthook installを実行するようにドキュメントに記載してください(ステップ 2)。pnpmでは、lefthookがonlyBuiltDependenciesに含まれていない限り、postinstall はスキップされます。 rootを設定しても glob の照合基準は変わりません。root: "services/api/"を設定するとコマンドの CWD は変わりますが、globパターンは引き続きリポジトリルートを基準とする相対パスに対して評価されます。"services/api/**/*.py"(フルパス)や単純な"*.py"(任意の階層に一致)のような glob は使えますが、rootからの相対パスとして glob を記述しないでください。stage_fixedはpre-commit専用です。pre-pushやその他のフックでは無視されます。フォーマッターによる修正がコミットされない場合は、コマンドがpre-commitの下にあることと、そのコマンドにstage_fixed: trueが設定されていることを確認してください。- 一致するファイルがない場合、コマンドはスキップされます(通常は望ましい動作です)。
globを指定していて、ステージ済みファイルに一致するものがない場合、Lefthook はファイル数ゼロでコマンドを実行するのではなく、そのコマンドをスキップします。そのため、TS のみを含むコミットで Python の処理が実行されることはありません。「ファイルがない場合」の手動チェックは追加しないでください。glob がすでに処理しています。 - 古いバージョンの設定キーが残っている場合があります。 現行バージョンは Lefthook 2.x です。非常に古い設定ファイルやブログ記事では、非推奨のキー(例:
run:の代わりにrunner:)が使われていることがあります。現行のスキーマに合わせて記述し、古すぎるバイナリはmin_version:で検出してください。v2 の Go モジュールパスも.../lefthook/v2です。 lefthook installと worktree。lefthook installを実行すると、インストール時の絶対パスがフックの shim に埋め込まれる場合があり、これにより git worktrees で問題が発生することが知られています。worktree でフックが正しく動作しない場合は、そこでlefthook installを再実行してください。- フックは強制手段ではありません。 誰でも
--no-verifyを指定できます。フックはローカルで素早く利用できる便利な仕組みとして扱い、マージを実際にブロックするゲートは CI に任せてください(ステップ 5/6)。
アンチパターン
- 「すべてを見逃さないよう、テストスイート全体を
pre-commitに置く。」 いいえ。そうするとすべてのコミットが遅くなり、チームが--no-verifyを使うようになります。pre-commitではステージ済みファイルに対する高速チェックを、pre-pushでは時間のかかるゲートを実行し、網羅的な実行は CI で行ってください。 - 「コミットのたびにリポジトリ全体を lint するだけでよい。」 いいえ。フックの対象をコミットするファイルのみにするため、
{staged_files}を使ってください。リポジトリ全体の実行は CI で行います。 - 「CI パイプラインのステップを
lefthook.ymlにコピーして、内容を一致させる。」 いいえ。定義が重複して乖離する原因になります。タスクランナー(turborepo)を参照し、パイプライン定義は一か所にまとめてください。 - 「
globとrootを省略して、すべてに単一のコマンドを使う。」 いいえ。対象を絞らないコマンドは、複数の言語が混在するリポジトリで、誤ったファイルに誤ったツールを実行してしまいます。言語やサブツリーごとに対象を絞ってください。 - 「セットアップのドキュメントでは
lefthook installを省略してもよい。各自で分かるはず。」 いいえ。これが、新しいコントリビューターの環境でフックが気づかれないまま実行されない最大の原因です。有効化を自動化するか、ドキュメントに記載してください。
出力
このスキルは、リポジトリルートに単一の lefthook.yml を生成し、さらに有効化の設定(npm プロジェクトの場合は prepare/postinstall フック、またはドキュメントに記載した lefthook install の手順)も用意します。この設定は複数の言語が混在するリポジトリ向けで、pre-commit では stage_fixed を使って言語ごとにステージ済みファイルのフォーマットと lint を実行し、pre-push では型チェックとテストのゲートを実行します。成果物の利用者は、各コントリビューターのローカル Git(コミット/プッシュ時にフックが実行されます)と、ひいてはフックが事前に対処するレビュー/CI レイヤーです。各ツールのコマンドと CI パイプラインは参照するだけで、設定内には埋め込みません。
関連項目
biome— TS/JS のフォーマットとリントを行います。pre-commitの例で使用するbiome check --writeの呼び出しを提供します。ruff— Python のリントとフォーマットを行います。ruff check --fix/ruff formatの呼び出しを提供します。ty— Python の型チェッカーです。pre-pushの Python ゲートです。typescript-typecheck—pre-pushの TS レーンで使用するtsc --noEmitの型チェックゲートです。turborepo— モノレポのタスクグラフと CI パイプラインです。フックから turbo タスクを呼び出すことはできますが、パイプラインはlefthook.ymlではなく、ここにあります。references/lefthook-reference.md—lefthook.ymlのスキーマに関する詳しい説明、jobs:形式、バージョンと移行の詳細です。この SKILL.md で扱っていないオプションが必要な場合に読み込んでください。references/sources.md— 調査の出典です。
段階的な開示
references/lefthook-reference.md— スキーマの詳細なリファレンス(オプション一覧、jobs:/ グループ形式、グローバルキー、バージョンに関する注記)。読み込み条件: 上記のワークフローで扱っていないlefthook.ymlのオプションや動作が必要な場合。references/sources.md— このスキルの記述の出典です。読み込み条件: 記述の検証または更新を行う場合。
このスキルには scripts/ や assets/ は含まれていません。上記の例をもとに作成する lefthook.yml を出力するものであり、ジェネレーターではありません。
本文の上限
descriptionは 1,024 文字以下(agentskills.io の上限)。- 本文は約 500 行 / 5,000 トークン以下です。毎ターンのコンテキストに含まれます。
- リファレンスファイルごとに、10,000 トークン超で警告、25,000 トークン超でエラーとします。リファレンス全体では、25,000 トークン超で警告、50,000 トークン超でエラーとします。