Buat sebuah mod
Biarkan Claude menulis mod Claude Code dari deskripsi, atau tulis sendiri yang menghitung panggilan alat dan menambahkan perintah. Pelajari loop reload dan validate.
Mod adalah plugin Claude Code dengan file entry, disebut hooks module: file JavaScript atau TypeScript yang fungsi-fungsinya dipanggil Claude Code ketika peristiwa terjadi. Ada dua cara untuk membuatnya:
- Minta Claude untuk menulisnya: jelaskan apa yang Anda inginkan dalam sesi Claude Code
- Tulis sendiri: ikuti tutorial untuk mempelajari cara kerja kode mod. Anda tidak memerlukan Node.js, bundler, atau build step, karena Claude Code memuat file
.jsdan.tssecara langsung.
Jika Anda belum memutuskan apakah mod adalah alat yang tepat, baca perbandingan di overview terlebih dahulu.
Mod memerlukan Claude Code v2.1.287 atau lebih baru. Di shell Anda, jalankan claude --version untuk memeriksa. Untuk melihat apakah mod dapat dimuat untuk Anda, lihat Periksa apakah mod dapat dimuat.
Minta Claude untuk sebuah mod
Jelaskan mod yang Anda inginkan dalam sesi Claude Code interaktif, dan Claude menulisnya. Claude bekerja dari skill bawaan bernama plugin-authoring, yang memberitahu di mana menulis mod, peristiwa dan metode apa yang dimiliki versi Anda, dan bagaimana mod dimuat. Claude dapat memuat skill ketika Anda meminta mod, atau Anda dapat memuatnya sendiri dengan menjalankan /plugin-authoring di prompt Claude Code.
Mod berjalan setelah Anda menyetujuinya, kecuali dalam sesi di mana mod yang ditulis Claude tidak dapat dimuat.
Jelaskan mod
Minta mod dengan kata-kata Anda sendiri, misalnya buat mod yang menunjukkan cabang git saat ini di atas prompt. Claude menulis mod dalam direktori miliknya sendiri di folder mods sesi, yaitu ~/.claude/dev-mods/ diikuti dengan ID sesi. Jalur lengkap mod terlihat seperti ~/.claude/dev-mods/3f2a9c1e-5b7d-4e8a-9c21-6d0f4b8a7e13/git-branch/.
Dalam mode izin default dan acceptEdits, Claude Code menanyakan sebelum Claude membuat setiap file mod, karena ~/.claude adalah jalur yang dilindungi. Setujui setiap file saat muncul.
Setujui mod
Ketika Claude menyimpan file pertama, Claude Code menanyakan apakah akan mengaktifkan hot reloading untuk sesi. Hot reloading menjalankan mod yang ditulis Claude dalam sesi ini dan mengambil setiap perubahan kemudian.
Pilih salah satu jawaban ini:
- Aktifkan untuk sesi ini: mod di folder mods sesi dimuat ketika giliran berakhir, dan dimuat ulang di akhir setiap giliran yang mengubahnya. Jawaban Anda berlaku untuk sesi, termasuk setelah Anda melanjutkannya.
- Tidak sekarang: tidak ada yang dimuat untuk sekarang. File tetap di tempat Claude menulisnya, dan mod dimuat saat sesi itu dimulai berikutnya. Untuk mencegah mod dimuat selamanya, hapus direktorinya.
Periksa bahwa mod dimuat
Jalankan /plugin di prompt Claude Code dan tekan Tab sampai tab Installed dipilih. Ini mencantumkan mod, dan Anda dapat mematikannya di sana.
Coba mod
Gunakan apa yang Anda minta. Untuk prompt contoh, nama cabang saat ini muncul di atas kotak prompt. Jika mod tidak melakukan apa yang Anda inginkan, beri tahu Claude apa yang harus diubah. Mod dimuat ulang di akhir setiap giliran yang mengubah filenya, sehingga Anda dapat mencoba perubahan segera setelah Claude selesai.
Gunakan mod di sesi lain
Mod yang ditulis Claude hanya dimuat dalam sesi yang membuatnya, dan Claude Code menghapus folder mods sesi itu setelah lebih lama dari cleanupPeriodDays. Untuk menyimpan mod, salin direktorinya keluar dari folder mods ke tempat Anda sendiri, seperti ~/mods/git-branch. Kemudian pilih cara memuatnya:
- Dalam sesi yang Anda mulai: di shell Anda, jalankan
claude --plugin-dir ~/mods/git-branch - Untuk orang lain: tambahkan ke marketplace sehingga mereka dapat menginstalnya
Sesi di mana mod yang ditulis Claude tidak dapat dimuat
Mod yang ditulis Claude hanya dimuat setelah Anda menyetujuinya, dalam workspace terpercaya di mana mod diizinkan untuk berjalan. Dalam sesi ini tidak dimuat:
- Tidak ada yang ada untuk menyetujui: sesi tidak dapat menampilkan prompt kepada Anda, seperti dalam run
claude -patau modedontAsk - Workspace tidak terpercaya: Anda belum menerima prompt kepercayaan untuk direktori
- Mod dihentikan: Anda memulai dengan
--safe-modeatau--bare, Anda menetapkandisableAllHooks, atau pengaturan terkelola organisasi Anda memblokir
Tulis mod sendiri
Dalam tutorial ini Anda membangun mod bernama first-mod yang menghitung panggilan alat yang dibuat Claude, menampilkan hitungan di samping spinner saat Claude bekerja, dan menambahkan perintah /tally yang mencetaknya. Anda kemudian membaca deklarasi tipe yang ditulis Claude Code di samping mod Anda dan menjalankan claude plugin validate. Bersama-sama mereka menunjukkan kepada Anda peristiwa dan metode yang ditawarkan versi Anda dan apa yang dibaca Claude Code dari kode Anda.
Rekaman ini menunjukkan mod yang selesai. Spinner menghitung panggilan alat, /tally mencetak hitungan, dan edit ke kode berlaku saat sesi berjalan:
Anda menulis tiga file:
first-mod/
├── .claude-plugin/
│ └── plugin.json
└── hooks/
├── hooks.json
└── register.js
plugin.json: manifest pluginhooks.json: menunjuk ke file kode Andaregister.js: kode Anda, disebut hooks module
Buat direktori plugin
Buat dua direktori yang menyimpan file:
mkdir -p first-mod/.claude-plugin first-mod/hooks
New-Item -ItemType Directory -Force first-mod\.claude-plugin, first-mod\hooks
Tulis manifest
Mod adalah plugin, dan mod memerlukan manifest. Manifest mod ini tidak memiliki field khusus. Simpan ini sebagai 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" }
}
Beri tahu Claude Code di mana kode Anda berada
Ketika Claude Code memuat plugin, ia membaca hooks/hooks.json plugin. Kunci modules dalam file itu memberikan jalur ke kode Anda, dan memilikinya adalah apa yang membuat plugin menjadi mod. Cantumkan satu jalur, relatif terhadap hooks.json. Di sini menunjuk ke register.js, yang Anda tulis di langkah berikutnya.
Simpan ini sebagai first-mod/hooks/hooks.json:
{
"description": "The first-mod hooks module",
"modules": ["./register.js"]
}
Tulis kode
File ini adalah kode mod, disebut hooks module. Ketika mod dimuat, Claude Code memanggil fungsi register yang diekspor file dan meneruskan fungsi bernama on. Setiap panggilan ke on mendaftarkan event handler, disebut hook, untuk peristiwa yang dinamainya.
Simpan ini sebagai 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 + '…' } })
})
}
File menyimpan hitungan dalam calls dan mendaftarkan empat hooks:
session.startberjalan ketika sesi dimulai, sebelum prompt pertama Anda, dan lagi setiap kali mod dimuat ulang. Ini menambahkan perintah/tallyke Claude Code.tool.callberjalan setiap kali Claude akan menggunakan alat. Ini menambah satu kecallsdan meminta Claude Code untuk menggambar antarmuka lagi.command.runberjalan ketika Anda mengetik/tally. Ini mengembalikan teks untuk dicetak.ui.renderberjalan setiap kali Claude Code menggambar spinner. Ini menambahkan hitungan setelah kata spinner.
Cara kerja mod contoh menjelaskan tiga argumen yang diterima setiap hook dan apa yang dikembalikan masing-masing.
Muat mod
Mulai Claude Code dengan flag --plugin-dir, yang memuat direktori plugin untuk satu sesi tanpa menginstalnya:
claude --plugin-dir ./first-mod
Coba mod
Minta Claude untuk melakukan sesuatu yang memerlukan beberapa panggilan alat, seperti list the files here and read the README. Saat Claude bekerja, kata spinner diikuti oleh hitungan yang naik, seperti Thinking · tool calls: 2…. Ketika Claude selesai, ketik /tally dan tekan Enter. Transkrip menunjukkan first-mod: Claude has made 2 tool calls since this mod loaded, dengan hitungan Anda sendiri. Claude Code menempatkan nama plugin di depan teks perintah.
Untuk memeriksa perintah tanpa sesi interaktif, jalankan dalam mode non-interaktif:
claude -p "/tally" --plugin-dir ./first-mod
first-mod: Claude has made 0 tool calls since this mod loaded
Jika /tally tidak ada dalam daftar perintah, modul tidak dimuat. Lihat Cari tahu mengapa mod tidak melakukan apa pun.
Ubah kode saat sesi berjalan
Biarkan sesi tetap terbuka. Dalam register.js, ubah ' · tool calls: ' menjadi ' · tools used: ' dalam hook ui.render dan simpan. Baris yang disorot adalah yang berubah:
// 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 + '…' } })
})
Sebuah baris dalam transkrip mengatakan first-mod dimuat ulang dan mencantumkan hooks-nya, dan spinner berikutnya menggunakan teks baru, seperti Thinking · tools used: 1….
Cara kerja mod contoh
Setiap fungsi yang Anda teruskan ke on adalah hook, yang merupakan event handler. Claude Code meneruskan setiap hook dengan tiga argumen yang sama:
- Mods API, bernama
$: setiap metode yang dapat dipanggil mod untuk menjangkau di luar dirinya, dalam namespaces seperti$.uidan$.command - Event, bernama
e: input event sebagai data biasa, seperti nama dan argumen panggilan alat - Next handler, bernama
next: fungsi yang meneruskan event ke mod lain dan kemudian ke perilaku Claude Code sendiri, dan mengembalikan hasilnya
Hook dalam first-mod menangani peristiwa mereka dalam tiga cara hook dapat:
- Observe: hook
session.startmendaftarkan perintah, dan hooktool.callmenghitung panggilan dan meminta redraw. Keduanya mengembalikannext(e), sehingga sesi dimulai dan alat berjalan seperti biasa. - Answer: hook
command.runmengembalikan hasilnya sendiri dan tidak pernah memanggilnext. Argumen kedua keon,{ command: 'tally' }, adalah filter, disebut matcher, sehingga hook hanya berjalan untuk/tally. - Rewrite: hook
ui.rendermemanggilnextdengan salinaneyangsuffix-nya menyimpan hitungan, sehingga Claude Code menggambar spinner biasanya dengan teks Anda setelah kata
Claude Code mengawasi direktori yang dimuat dengan --plugin-dir dan hot-reload hooks module ketika file di dalamnya berubah. Setiap reload menjalankan register lagi, sehingga calls kembali ke 0 dan /tally mulai menghitung lagi. Untuk menyimpan nilai di seluruh reload, lihat Simpan state.
Terus bekerja pada mod
Setelah mod dimuat, Anda dapat membuat Claude mengubahnya, memeriksa kode Anda terhadap definisi tipe untuk versi Anda, mencantumkan peristiwa dan panggilan yang ditemukan Claude Code di dalamnya, dan mengujinya.
Ubah mod dengan Claude
Untuk mengubah mod yang sudah Anda miliki, mulai sesi dengan --plugin-dir menunjuk ke direktori mod, sehingga apa yang ditulis Claude dimuat dalam sesi yang sama:
claude --plugin-dir ./first-mod
Kemudian minta perubahan, misalnya add a /tally-reset command to this mod that sets the tally back to zero. Claude mengedit hooks module, menjalankan claude plugin validate, dan memperbaiki apa yang dilaporkannya. Direktori yang Anda muat dengan --plugin-dir adalah jalur yang dilindungi, sehingga dalam mode default dan acceptEdits Anda diminta untuk menyetujui setiap edit Claude ke mod. Tabel jalur yang dilindungi memberikan hasil untuk mode izin lainnya.
File yang disimpan Claude selama gilirannya dimuat ulang ketika giliran berakhir, sehingga Anda dapat mencoba /tally-reset segera setelah Claude selesai.
Dapatkan definisi tipe untuk versi Anda
Setiap kali Claude Code memuat atau memuat ulang mod dari direktori yang Anda teruskan ke --plugin-dir, atau mod yang ditulis Claude untuk Anda, ia menulis file deklarasi TypeScript, berakhir dengan .d.ts, ke dalam .claude-plugin/types/ di dalam direktori mod. Mereka menjelaskan peristiwa yang tepat, metode mods API, dan elemen dalam versi Claude Code yang Anda jalankan, sehingga editor Anda dapat autocomplete dan type-check hooks Anda. Untuk menelusuri deklarasi secara online, baca mods/types/claude-code.d.ts di repositori Claude Code, yang baris pertamanya menamai versi yang menulisnya. Direktori menyimpan file-file ini:
| Path | Apa yang dideklarasikan |
|---|---|
claude-code/index.d.ts |
Setiap event dan input serta hasilnya, setiap namespace dan metode mods API, dan elemen yang dapat digambar setiap surface |
claude-code-tools/index.d.ts |
Input dan hasil alat bawaan, sehingga memeriksa e.tool === 'Bash' mempersempit e |
claude-code-mcp/index.d.ts |
Input alat MCP yang terhubung terakhir kali Anda menyimpan file dalam mod |
index.d.ts dalam direktori bernama untuk plugin |
Apa yang ditambahkan plugin itu ke mods API. Ada satu direktori untuk setiap plugin yang dicantumkan plugin.json Anda di bawah dependencies. |
tsconfig.json |
Opsi compiler yang sesuai untuk hooks module |
Jika mod Anda tidak memiliki tsconfig.json miliknya sendiri, Claude Code menambahkan satu di root mod yang memperluas yang dihasilkan, sehingga editor Anda dan tsc -p ./first-mod type-check mod tanpa setup lebih lanjut.
Peristiwa dan metode dapat berubah antar rilis, jadi percayai file-file ini daripada halaman apa pun, termasuk yang ini, ketika mereka tidak setuju.
claude-code/index.d.ts adalah referensi paling lengkap untuk build Anda, dengan komentar dan contoh untuk setiap metode mods API. Untuk mencari sesuatu, cari file untuk namanya, seperti 'tool.call'.
Periksa apa yang dibaca Claude Code dari mod Anda
Untuk melihat mod Anda seperti yang dilihat Claude Code, tanpa menjalankan kode atau memulai sesi, gunakan claude plugin validate. Ini memeriksa manifest dan menjalankan analisis statis yang sama pada sumber hooks module yang dijalankan Claude Code saat memuat mod. Di shell Anda, jalankan pada direktori mod:
claude plugin validate ./first-mod
Untuk first-mod, output mencakup baris-baris ini.
❯ ./register.js hooks: session.start, tool.call, command.run{command=tally}, ui.render{component=Spinner}
❯ ./register.js calls: $.command.register, $.ui.invalidate
✔ Validation passed
Baris hooks: mencantumkan peristiwa yang dihubungkan modul Anda, masing-masing dengan filternya dalam kurung kurawal. Baris calls: mencantumkan setiap metode mods API yang dipanggilnya. Modul yang membaca atau menetapkan variabel lingkungan juga mendapat baris env reads: dan env writes:, dan yang menggunakan $.state mendapat state reads: dan state writes:.
Jika event yang dimaksudkan untuk dihubungkan hilang dari baris pertama, Claude Code juga tidak akan memanggil hook itu. Penyebab biasanya adalah nama event yang salah eja, yang dilaporkan perintah sebagai kesalahan seperti "tool.calls" is not an event.
Ikuti aturan-aturan ini sehingga analisis statis dapat menemukan setiap hook dan panggilan:
- Eja setiap panggilan mods API secara lengkap:
$, namespace, kemudian metode, seperti dalam$.store.get('notes'). Anda dapat meneruskan$ke fungsi yang dideklarasikan di tingkat atas file yang sama, dan untuk fungsi Anda bernamaloadNotes, bariscalls:kemudian membaca$.store.get (via loadNotes). Meneruskan$ke metode, fungsi yang didefinisikan di dalam hook, atau fungsi yang Anda impor dari file lain Anda gagal validasi. Fungsireaddanupdateyang digunakan$.stateadalah impor yang dapat mengambilnya. Jangan menetapkan$atau salah satu namespace-nya ke variabel, destructure, atau indexnya dengan nama yang dihitung.const ui = $.uigagal dengan$.ui is used as a value. - Tulis nama event dalam setiap panggilan
onsebagai string literal, seperti'tool.call'. Variabel, atau loop atas daftar nama, gagal denganthe event name passed to on() is not a string literal. - Di dalam
register, jangan deklarasikan variabel atau parameter kedua bernamaon. Validasi gagal dengan"on" is declared again (shadowed). - Impor hanya dari file di dalam direktori plugin, dengan jalur relatif. Satu-satunya impor bare yang diizinkan adalah
claude-code, untuk tipe dan beberapa helper. - Gunakan deklarasi
importdi atas file, seperti dalamimport { name } from './file.js'.import()dinamis gagal dengana dynamic import(); a hooks module imports its own files with an import declaration. - Tulis setiap file sebagai ES module, dengan
importdan bukanrequire. Reference mencantumkan ekstensi file yang dimuat Claude Code.
Uji mod
Anda dapat menulis tes otomatis untuk mod dan menjalankannya dari shell Anda dengan claude plugin test, tanpa sesi, sign-in, atau jaringan. Tes menaikkan peristiwa yang ditangani hooks Anda dan memeriksa apa yang dilakukan hooks.
Tes ini menaikkan dua panggilan alat, menjalankan /tally, dan memeriksa bahwa balasan menghitung keduanya. Simpan sebagai 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')
})
Di shell Anda, jalankan tes dari direktori first-mod:
claude plugin test
Output menamai setiap tes dan apakah itu lulus, dengan waktu yang bervariasi dari run ke run:
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]
Uji mod mencakup stubbing panggilan model atau store, dan menguji timer dan gambar.
Bagikan mod Anda
Mod adalah plugin, jadi Anda memversinya dalam manifest dan orang menginstal dan memperbarui dengan perintah /plugin. Untuk memberikannya kepada orang lain, tambahkan ke marketplace.
Sebelum Anda melakukannya, periksa name plugin: claude plugin validate gagal nama yang terlihat seperti salah satu milik Anthropic sendiri, seperti yang dimulai dengan claude-. Peristiwa dan metode dapat berubah antar rilis, jadi README Anda adalah tempat untuk mengatakan versi Claude Code mana yang Anda uji.
Terus berkembang terhadap direktori dengan --plugin-dir, bukan terhadap salinan yang diinstal. Claude Code cache plugin yang diinstal berdasarkan versi, sehingga edit Anda tidak mencapai salinan yang diinstal sampai Anda menaikkan versi dan menginstal lagi.
Langkah berikutnya
- Gambar di antarmuka: buka pane, gambar di atas prompt, dan tambahkan tombol dan field teks
- Bereaksi terhadap peristiwa: hook panggilan alat, prompt, dan giliran
- Gunakan mods API: tambahkan perintah dan alat, panggil model, dan jalankan pekerjaan pada timer
- Uji mod: stub apa yang akan dijawab Claude Code, dan uji timer dan gambar
- Troubleshoot mod: alasan mod tidak melakukan apa pun, dan debug log
- Baca sumber mod bawaan: plugin lengkap, masing-masing dengan hooks module dan tesnya