mod を作成する
Claude に説明から Claude Code mod を書かせるか、ツール呼び出しをカウントしてコマンドを追加する mod を自分で書きます。リロードと検証ループについて学びます。
mod は Claude Code プラグインで、hooks module と呼ばれるエントリファイルを持っています。hooks module は JavaScript または TypeScript ファイルで、イベントが発生したときに Claude Code が呼び出す関数を含みます。mod を作成する方法は 2 つあります。
- Claude に書かせる: Claude Code セッションで何をしたいかを説明します
- 自分で書く: チュートリアルに従ってmod のコードがどのように機能するかを学びます。Node.js、バンドラー、またはビルドステップは必要ありません。Claude Code は
.jsファイルと.tsファイルを直接読み込むためです。
mod が適切なツールかどうかまだ決めていない場合は、まず概要のページで比較を読んでください。
Mod には Claude Code v2.1.287 以降が必要です。シェルで claude --version を実行してチェックしてください。mod がロードできるかどうかを確認するには、mod がロードできるかどうかを確認するを参照してください。
Claude に mod を書かせる
インタラクティブな Claude Code セッションで、作成したい mod について説明すると、Claude がそれを書きます。Claude は plugin-authoring という組み込みスキルから動作します。このスキルは、mod をどこに書くか、バージョンにどのイベントとメソッドがあるか、mod がどのようにロードされるかを Claude に伝えます。Claude は mod を要求するときにスキルをロードできます。または、Claude Code プロンプトで /plugin-authoring を実行して自分でロードできます。
mod は承認すると実行されます。ただし、mod Claude が書いたものがロードできないセッションでは実行されません。
mod について説明する
自分の言葉で mod を要求します。たとえば、make a mod that shows the current git branch above the prompt のように。Claude は、セッションの mods フォルダ内の独自のディレクトリに mod を書きます。これは ~/.claude/dev-mods/ の後にセッションの ID が続きます。mod の完全なパスは ~/.claude/dev-mods/3f2a9c1e-5b7d-4e8a-9c21-6d0f4b8a7e13/git-branch/ のようになります。
default および acceptEdits 権限モードでは、~/.claude は保護されたパスであるため、Claude Code は Claude が mod の各ファイルを作成する前に確認を求めます。各ファイルが表示されたら承認してください。
mod を承認する
Claude が最初のファイルを保存すると、Claude Code はセッションのホットリロードを有効にするかどうかを尋ねます。ホットリロードは、このセッションで Claude が書いた mod を実行し、後で変更されるたびにそれを取得します。
次のいずれかの答えを選択してください。
- このセッションで有効にする: セッションの mods フォルダ内の mod はターンの終了時にロードされ、それらを変更するターンの終了時に再ロードされます。答えはセッション全体に対して有効です。再開後も含まれます。
- 今はしない: 今のところ何もロードされません。ファイルは Claude が書いた場所に留まり、mod は次回そのセッションが開始されるときにロードされます。mod がロードされないようにするには、そのディレクトリを削除してください。
mod がロードされたことを確認する
Claude Code プロンプトで /plugin を実行し、Tab キーを押して Installed タブが選択されるまで続けます。mod が一覧表示され、そこでオフにできます。
mod を試す
要求したものを使用します。例のプロンプトの場合、現在のブランチ名がプロンプトボックスの上に表示されます。mod が期待したことをしない場合は、Claude に何を変更するかを伝えてください。mod は、そのファイルを変更するターンの終了時に再ロードされるため、Claude が終了するとすぐに変更を試すことができます。
他のセッションで mod を使用する
Claude が書いた mod は、それを作成したセッションでのみロードされます。Claude Code は、そのセッションの mods フォルダを cleanupPeriodDays より古くなると削除します。mod を保持するには、mods フォルダからそのディレクトリを ~/mods/git-branch などの自分の場所にコピーしてください。次に、ロード方法を選択します。
- 開始するセッションで: シェルで
claude --plugin-dir ~/mods/git-branchを実行します - 他の人向け: マーケットプレイスに追加して、インストールできるようにします
mod Claude が書いたものがロードできないセッション
Claude が書いた mod は、信頼できるワークスペースで承認後にのみロードされます。このワークスペースでは mod の実行が許可されています。これらのセッションではロードされません。
- 誰も承認する人がいない:
claude -p実行やdontAskモードのように、セッションはプロンプトを表示できません - ワークスペースが信頼されていない: ディレクトリの信頼プロンプトを受け入れていません
- Mod が停止している:
--safe-modeまたは--bareで開始した、disableAllHooksを設定した、または組織の管理設定がそれをブロックしている
自分で mod を書く
このチュートリアルでは、first-mod という名前の mod を構築します。この mod は Claude が行うツール呼び出しをカウントし、Claude が動作している間にスピナーの横にカウントを表示し、/tally コマンドを追加して印刷します。その後、Claude Code がモジュールの横に書いた型宣言を読み、claude plugin validate を実行します。これらは、バージョンが提供するイベントとメソッド、および Claude Code がコードから読み取るものを示します。
この記録は完成した mod を示しています。スピナーはツール呼び出しをカウントし、/tally はカウントを印刷し、セッションの実行中にコードの編集が有効になります。
3 つのファイルを書きます。
first-mod/
├── .claude-plugin/
│ └── plugin.json
└── hooks/
├── hooks.json
└── register.js
plugin.json: プラグインのマニフェストhooks.json: コードファイルを指しますregister.js: コード。hooks module と呼ばれます
プラグインディレクトリを作成する
ファイルを保持する 2 つのディレクトリを作成します。
mkdir -p first-mod/.claude-plugin first-mod/hooks
New-Item -ItemType Directory -Force first-mod\.claude-plugin, first-mod\hooks
マニフェストを書く
mod はプラグインで、mod にはマニフェストが必要です。この mod のマニフェストには特別なフィールドはありません。これを first-mod/.claude-plugin/plugin.json として保存します。
{
"name": "first-mod",
"version": "0.1.0",
"description": "Counts Claude's tool calls, shows the count beside the spinner, and adds a /tally command",
"author": { "name": "Your Name" }
}
Claude Code にコードの場所を伝える
Claude Code がプラグインをロードするとき、プラグインの hooks/hooks.json を読みます。そのファイルの modules キーはコードへのパスを提供し、それを持つことがプラグインを mod にします。1 つのパスをリストします。これは hooks.json に相対的です。ここでは、次のステップで書く register.js を指します。
これを first-mod/hooks/hooks.json として保存します。
{
"description": "The first-mod hooks module",
"modules": ["./register.js"]
}
コードを書く
このファイルは mod のコード。hooks module と呼ばれます。mod がロードされると、Claude Code はファイルがエクスポートする register 関数を呼び出し、on という関数を渡します。on への各呼び出しは、イベントハンドラー(hook と呼ばれる)をそれが名前を付けるイベントに登録します。
これを first-mod/hooks/register.js として保存します。
// The count, shared by the hooks below
let calls = 0
// Claude Code calls this once when the mod loads
export function register(on) {
// Runs when the session starts, before your first prompt
on('session.start', async ($, e, next) => {
// Add the /tally command
await $.command.register({
name: 'tally',
description: 'Show how many tool calls Claude has made',
})
// Let the session start as usual
return next(e)
})
// Runs each time Claude is about to use a tool
on('tool.call', async ($, e, next) => {
calls += 1
// Ask Claude Code to draw the interface again, so the new count shows
$.ui.invalidate('ui.render')
// Let the tool run as usual
return next(e)
})
// Runs when you type /tally, and only then, because of the matcher
on('command.run', { command: 'tally' }, async () => {
// The text to print in the transcript
return { text: 'Claude has made ' + calls + ' tool calls since this mod loaded' }
})
// Runs each time Claude Code draws the spinner
on('ui.render', { component: 'Spinner' }, async ($, e, next) => {
// Keep Claude Code's spinner, with the count added after its word
return next({ ...e, props: { ...e.props, suffix: ' · tool calls: ' + calls + '…' } })
})
}
ファイルは calls にカウントを保持し、4 つの hook を登録します。
session.startはセッションが開始されるときに実行されます。最初のプロンプトの前に、mod が再ロードされるたびに実行されます。Claude Code に/tallyコマンドを追加します。tool.callは Claude がツールを使用しようとするたびに実行されます。callsに 1 を追加し、Claude Code にインターフェイスを再度描画するよう要求します。command.runは/tallyを入力するときに実行されます。印刷するテキストを返します。ui.renderは Claude Code がスピナーを描画するたびに実行されます。スピナーの単語の後にカウントを追加します。
例の mod がどのように機能するかは、各 hook が取る 3 つの引数と各引数が返すものについて説明しています。
mod をロードする
--plugin-dir フラグで Claude Code を開始します。これはプラグインディレクトリを 1 つのセッションにロードします。インストールしません。
claude --plugin-dir ./first-mod
mod を試す
Claude に、いくつかのツール呼び出しを必要とするものを実行するよう要求します。たとえば、list the files here and read the README のように。Claude が動作している間、スピナーの単語の後に、Thinking · tool calls: 2… のように上昇するカウントが続きます。Claude が終了したら、/tally を入力して Enter キーを押します。トランスクリプトは first-mod: Claude has made 2 tool calls since this mod loaded を表示します。独自のカウント付きです。Claude Code はプラグインの名前をコマンドのテキストの前に置きます。
インタラクティブセッションなしでコマンドをチェックするには、非インタラクティブモードで実行します。
claude -p "/tally" --plugin-dir ./first-mod
first-mod: Claude has made 0 tool calls since this mod loaded
/tally がコマンドリストにない場合、モジュールはロードされませんでした。mod が何もしない理由を見つけるを参照してください。
セッションの実行中にコードを変更する
セッションを開いたままにします。register.js で、ui.render hook の ' · tool calls: ' を ' · tools used: ' に変更して保存します。強調表示された行は変更される行です。
// Runs each time Claude Code draws the spinner
on('ui.render', { component: 'Spinner' }, async ($, e, next) => {
// Keep Claude Code's spinner, with the count added after its word
return next({ ...e, props: { ...e.props, suffix: ' · tools used: ' + calls + '…' } })
})
トランスクリプトの行は first-mod が再ロードされたことを示し、その hook をリストします。次のスピナーは新しいテキストを使用します。たとえば、Thinking · tools used: 1… のように。
例の mod がどのように機能するか
on に渡す各関数は hook で、イベントハンドラーです。Claude Code はすべての hook に同じ 3 つの引数を渡します。
- mods API。
$という名前です。mod が自身の外に到達するために呼び出すことができるすべてのメソッド。$.uiや$.commandなどの名前空間内です - イベント。
eという名前です。ツール呼び出しの名前と引数などのイベントの入力。プレーンデータとして - 次のハンドラー。
nextという名前です。イベントを他の mod に渡し、次に Claude Code 独自の動作に渡す関数。結果を返します
first-mod の hook は、hook ができる 3 つの方法でイベントを処理します。
- 観察:
session.starthook はコマンドを登録し、tool.callhook はコールをカウントして再描画を要求します。どちらもnext(e)を返すため、セッションが開始され、ツールが通常どおり実行されます。 - 回答:
command.runhook は独自の結果を返し、nextを呼び出しません。onへの 2 番目の引数{ command: 'tally' }はフィルターで、matcher と呼ばれます。hook は/tallyに対してのみ実行されます。 - 書き直す:
ui.renderhook はeのコピーでnextを呼び出します。そのsuffixはカウントを保持します。Claude Code は通常のスピナーを描画し、単語の後にテキストを描画します
Claude Code は --plugin-dir でロードされたディレクトリを監視し、ファイルが変更されると hooks module をホットリロードします。各リロードは register を再度実行するため、calls は 0 に戻り、/tally は再度カウントを開始します。リロード全体で値を保持するには、状態を保持するを参照してください。
mod の作業を続ける
mod がロードされたら、Claude に変更させたり、コードをバージョンの型定義と照合したり、Claude Code が見つけたイベントと呼び出しをリストしたり、テストしたりできます。
Claude で mod を変更する
既に持っている mod を変更するには、--plugin-dir を mod のディレクトリに向けてセッションを開始します。Claude が書いたものが同じセッションでロードされるようにします。
claude --plugin-dir ./first-mod
次に、変更を要求します。たとえば、add a /tally-reset command to this mod that sets the tally back to zero のように。Claude は hooks module を編集し、claude plugin validate を実行し、報告されたものを修正します。--plugin-dir でロードするディレクトリは保護されたパスであるため、default および acceptEdits モードでは、Claude の mod への各編集を承認するよう求められます。保護されたパステーブルは他の権限モードの結果を示します。
Claude がターンの終了時に保存したファイルは、ターンの終了時に再ロードされるため、Claude が終了するとすぐに /tally-reset を試すことができます。
バージョンの型定義を取得する
Claude Code が --plugin-dir に渡すディレクトリから mod をロードまたは再ロードするたびに、または Claude が書いた mod の場合、TypeScript 宣言ファイル(.d.ts で終わる)を mod のディレクトリ内の .claude-plugin/types/ に書き込みます。実行している Claude Code バージョンの正確なイベント、mods API メソッド、および要素について説明しているため、エディターは hook を自動補完および型チェックできます。宣言をオンラインで参照するには、Claude Code リポジトリの mods/types/claude-code.d.ts を読んでください。その最初の行は、それを書いたバージョンに名前を付けます。ディレクトリには次のファイルが含まれます。
| パス | 宣言内容 |
|---|---|
claude-code/index.d.ts |
すべてのイベントとその入力と結果、すべての mods API 名前空間とメソッド、各サーフェスが描画できる要素 |
claude-code-tools/index.d.ts |
組み込みツールの入力と結果。e.tool === 'Bash' をチェックすると e が絞り込まれます |
claude-code-mcp/index.d.ts |
mod のファイルを最後に保存したときに接続された MCP ツールの入力 |
プラグイン用に名前が付けられたディレクトリ内の index.d.ts |
そのプラグインが mods API に追加するもの。plugin.json の dependencies の下にリストされている各プラグイン用に 1 つのディレクトリがあります |
tsconfig.json |
hooks module に適したコンパイラオプション |
mod に独自の tsconfig.json がない場合、Claude Code は mod のルートに生成されたものを拡張する tsconfig.json を追加します。エディターと tsc -p ./first-mod は、さらにセットアップなしで mod を型チェックできます。
イベントとメソッドはリリース間で変更される可能性があるため、意見が異なる場合は、このページを含むすべてのページよりもこれらのファイルを信頼してください。
claude-code/index.d.ts はビルドの最も完全なリファレンスで、すべての mods API メソッドにコメントと例があります。何かを検索するには、ファイルで名前を検索します。たとえば、'tool.call' のように。
Claude Code がモジュールから読み取るものを確認する
セッションを実行したり、コードを実行したりせずに、Claude Code がモジュールをどのように見るかを確認するには、claude plugin validate を使用します。マニフェストをチェックし、Claude Code が mod をロードするときに hooks module のソースで実行するのと同じ静的分析を実行します。シェルで、mod のディレクトリで実行します。
claude plugin validate ./first-mod
first-mod の場合、出力には次の行が含まれます。
❯ ./register.js hooks: session.start, tool.call, command.run{command=tally}, ui.render{component=Spinner}
❯ ./register.js calls: $.command.register, $.ui.invalidate
✔ Validation passed
hooks: 行は、モジュールが hook するイベントをリストします。各イベントは、中括弧内のフィルター付きです。calls: 行は、呼び出すすべての mods API メソッドをリストします。環境変数を読み取るまたは設定するモジュールは、env reads: および env writes: 行も取得します。$.state を使用するモジュールは、state reads: および state writes: を取得します。
hook するつもりだったイベントが最初の行から欠落している場合、Claude Code はその hook も呼び出しません。通常の原因は、イベント名のスペルミスです。コマンドは "tool.calls" is not an event などのエラーとして報告します。
静的分析がすべての hook と呼び出しを見つけることができるように、これらのルールに従ってください。
- 各 mods API 呼び出しを完全に綴ります。
$、名前空間、メソッド。$.store.get('notes')のように。$を同じファイルの最上位で宣言された関数に渡すことができます。loadNotesという名前の関数の場合、calls:行は$.store.get (via loadNotes)を読みます。$をメソッド、hook 内で定義された関数、または別のファイルからインポートした関数に渡すと、検証が失敗します。$.stateが使用するreadおよびupdate関数は、それを取ることができるインポートです。$またはその名前空間の 1 つを変数に割り当てたり、分割したり、計算された名前でインデックスを付けたりしないでください。const ui = $.uiは$.ui is used as a valueで失敗します。 - 各
on呼び出しでイベント名を文字列リテラルとして書きます。'tool.call'のように。変数、または名前のリストのループは、the event name passed to on() is not a string literalで失敗します。 register内で、onという名前の 2 番目の変数またはパラメーターを宣言しないでください。検証は"on" is declared again (shadowed)で失敗します。- プラグインディレクトリ内のファイルからのみインポートします。相対パスで。許可される唯一の裸のインポートは、型といくつかのヘルパーの
claude-codeです。 - ファイルの最上部で
import宣言を使用します。import { name } from './file.js'のように。動的なimport()はa dynamic import(); a hooks module imports its own files with an import declarationで失敗します。 - すべてのファイルを ES モジュールとして書きます。
importを使用し、requireは使用しません。リファレンスは Claude Code がロードするファイル拡張子をリストします。
mod をテストする
mod の自動テストを書き、シェルから claude plugin test で実行できます。セッション、サインイン、またはネットワークはありません。テストは hook が処理するイベントを発生させ、hook が何をしたかをチェックします。
このテストは 2 つのツール呼び出しを発生させ、/tally を実行し、hook が両方をカウントしたことをチェックします。これを first-mod/tests/first-mod.test.ts として保存します。
import { expect, test } from 'claude-code/testing'
test('/tally reports the tool calls the mod has seen', async ($, on) => {
// Answer each tool call in Claude Code's place, so no tool runs
on('tool.call', () => ({ result: 'ok' }))
// Raise two tool calls, which the mod's tool.call hook counts
await $.tool.call({ tool: 'Bash', command: 'ls' })
await $.tool.call({ tool: 'Read', file_path: 'README.md' })
// Run /tally and check the text its hook returns
const answer = await $.command.run({ command: 'tally', args: '' })
expect(answer.text).toBe('Claude has made 2 tool calls since this mod loaded')
})
シェルで、first-mod ディレクトリからテストを実行します。
claude plugin test
出力は各テストと合格したかどうかを名前で示します。タイミングは実行ごとに異なります。
tests/first-mod.test.ts:
(pass) /tally reports the tool calls the mod has seen [22.87ms]
1 pass
0 fail
Ran 1 test across 1 file. [0.19s]
mod をテストするは、モデル呼び出しまたはストアをスタブ化し、タイマーと描画をテストすることをカバーしています。
mod を共有する
mod はプラグインであるため、マニフェストでバージョン管理し、人々は /plugin コマンドでインストールおよび更新します。他の人に提供するには、マーケットプレイスに追加してください。
その前に、プラグインの name をチェックしてください。claude plugin validate は、Anthropic 独自のように見える名前で失敗します。たとえば、claude- で始まる名前。イベントとメソッドはリリース間で変更される可能性があるため、README はテストした Claude Code バージョンを示す場所です。
インストールされたコピーに対してではなく、--plugin-dir を使用してディレクトリに対して開発を続けます。Claude Code はインストールされたプラグインをバージョンでキャッシュするため、バージョンを上げてもう一度インストールするまで、編集はインストールされたコピーに到達しません。
次のステップ
- インターフェイスに描画する: ペインを開き、プロンプトの上に描画し、ボタンとテキストフィールドを追加します
- イベントに反応する: ツール呼び出し、プロンプト、ターンをフック化します
- mods API を使用する: コマンドとツールを追加し、モデルを呼び出し、タイマーで作業を実行します
- mod をテストする: Claude Code が答えるものをスタブ化し、タイマーと描画をテストします
- mod をトラブルシューティングする: mod が何もしない理由とデバッグログ
- 組み込み mod のソースを読む: 完全なプラグイン。各 hooks module とテスト付き