SpyBara
Go Premium

plugins/mods/api.md 2026-10-01 23:59 UTC to 2026-10-02 11:59 UTC

This page contains 19 additions and 19 deletions.

2026
Thu 1 23:59 Fri 2 13:00

Gunakan mods API

Panggil mods API dari mod Claude Code untuk menambahkan perintah dan alat, memanggil model, menjalankan pekerjaan pada timer, mengirim pesan ke sesi lain, dan mengakses file serta jaringan.

Mods API adalah kumpulan metode yang dipanggil mod untuk bertindak: menambahkan perintah dan alat, memanggil model, menjalankan pekerjaan antar peristiwa, dan mengakses sistem file, proses, dan jaringan. Setiap hook menerimanya sebagai argumen pertamanya, $, dengan metode yang dikelompokkan dalam namespace seperti $.ui dan $.fs. Events menentukan kapan hook berjalan, dan mods API adalah apa yang dipanggil hook setelah berjalan.

Bangun mod pertama Anda sebelum Anda mulai di sini. Untuk setiap metode, lihat mods API methods atau baca tipe untuk build Anda.

Tambahkan perintah atau alat

Mod dapat menambahkan perintah untuk dijalankan pengguna dan alat untuk dipanggil Claude. Daftarkan keduanya dalam hook session.start. Claude Code menunggu hook itu sebelum prompt pertama, jadi apa yang Anda daftarkan tersedia dari giliran pertama.

Tambahkan perintah

Perintah adalah untuk pengguna. Daftarkan, kemudian tangani command.run untuk namanya. Contoh ini menambahkan perintah /standup yang mengambil jumlah hari opsional:

on('session.start', async ($, e, next) => {
  // Add /standup to the command list, with the description the user sees there
  await $.command.register({ name: 'standup', description: 'Summarize what changed today', argumentHint: '[days]' })
  return next(e)
})

// The matcher limits the hook to /standup, so other commands don't reach it
on('command.run', { command: 'standup' }, async ($, e) => {
  // e.args is the text typed after the command name, or an empty string
  return { text: 'Summary for the last ' + (e.args || '1') + ' day(s): ...' }
})

Setelah sesi dimulai, /standup muncul dengan deskripsinya dalam daftar yang Anda lihat saat mengetik /. argumentHint ditampilkan dalam prompt setelah Anda mengetik perintah dan spasi, seperti /standup [days]. Saat Anda menjalankan /standup 3, hook kedua mengembalikan Summary for the last 3 day(s): ..., dan transkrip menunjukkan teks itu setelah nama plugin. Hook tidak pernah memanggil next, karena perintah tidak memiliki perilaku selain milik Anda.

text yang Anda kembalikan dicetak dalam transkrip dan Claude membacanya. Untuk tidak mencetak apa pun, seperti perintah yang hanya membuka pane, kembalikan {}. Untuk membiarkan perintah berjalan saat Claude sedang bekerja, tambahkan immediate: true ke pendaftaran.

Pilih nama yang tidak digunakan oleh perintah bawaan. Ketik / dalam sesi untuk melihatnya. $.command.register melempar untuk nama yang diambil, dengan pesan seperti "/focus" refused: it is the built-in /focus. Hook yang melempar dilewati, jadi sisa hook session.start Anda tidak berjalan juga. Daftarkan perintah terakhir dalam hook itu, atau bungkus panggilan dalam try dan catch.

Tambahkan alat

Alat adalah untuk Claude. Daftarkan dengan nama, deskripsi yang dibaca Claude, dan JSON Schema untuk inputnya. Claude melihatnya dengan nama yang lebih panjang yang terdiri dari mcp__, nama plugin Anda, dua garis bawah, dan nama yang Anda daftarkan. Anda menangani panggilannya dalam hook tool.call yang disaring ke nama lengkap itu. Contoh ini, dari plugin bernama my-mod, mendaftarkan ticket, jadi nama lengkapnya adalah mcp__my-mod__ticket. Ini memberi Claude alat yang mencari tiket dalam pelacak masalah:

on('session.start', async ($, e, next) => {
  await $.tool.register({
    name: 'ticket',
    // Claude decides when to call the tool from this description
    description: 'Look up a ticket by its id and return its title and status',
    // The arguments Claude has to send: one required string named id
    inputSchema: { type: 'object', properties: { id: { type: 'string' } }, required: ['id'] },
  })
  return next(e)
})

// The full tool name is mcp__, the plugin's name, and the registered name
on('tool.call', { tool: 'mcp__my-mod__ticket' }, async ($, e) => {
  // The tool's arguments are fields of e, so the id is e.id
  const response = await $.http.fetch('https://tickets.example.com/api/' + encodeURIComponent(e.id))
  // Return a result either way, so Claude learns when the lookup failed
  return { result: response.ok ? response.text : 'Lookup failed with status ' + response.status }
})

Saat Anda bertanya tentang tiket, Claude dapat memanggil mcp__my-mod__ticket dengan id-nya. Hook kedua mengambil tiket dan mengembalikan badan respons, yang dibaca Claude sebagai hasil alat. Saat server menjawab dengan status kesalahan, Claude membaca Lookup failed with status dan nomornya.

Panggil model

Mod dapat menanyakan model pertanyaan sendiri, di luar percakapan, untuk pekerjaan kecil seperti mengurutkan atau merangkum sepotong teks. $.model.complete mengirim satu prompt ke model dengan kredensial sesi Anda dan menyelesaikan balasan. Ini tidak memiliki riwayat percakapan.

Hook ini menjawab perintah /triage, didaftarkan sebagai perintah, dengan menanyakan model kecil untuk memberi label pada teks yang diketik setelahnya:

on('command.run', { command: 'triage' }, async ($, e) => {
  const r = await $.model.complete({
    model: 'haiku',
    // The system prompt sets the job, and the prompt carries the text to label
    system: 'Reply with one word: bug, feature, or question.',
    prompt: e.args,
    // One word needs few tokens, and the call gives up after 15 seconds
    maxTokens: 20,
    timeoutMs: 15000,
  })
  // r.text exists only when the model answered, so check r.isAnswered first
  const label = r.isAnswered ? r.text.trim() : 'unknown'
  return { text: 'Label: ' + label }
})

Saat Anda menjalankan /triage the export button does nothing, mod mengirim teks itu ke model dan mencetak jawabannya, seperti Label: bug. Percakapan Claude bukan bagian dari permintaan. Saat model tidak menjawab, labelnya adalah unknown.

Kegagalan Claude API tidak menolak panggilan, jadi periksa r.isAnswered, dan baca r.reason saat itu false. Panggilan menolak untuk permintaan yang tidak akan dikirim Claude Code, seperti model yang diblokir organisasi Anda. Tipe untuk build Anda mencantumkan opsi lain, seperti effort, dan batas memberikan default maxTokens.

$.model.fork({ prompt }) menanyakan satu pertanyaan atas percakapan saat ini, dengan model dan prompt sistem yang sama, jadi Claude API melayani sebagian besar dari cache prompt.

Panggilan ini menggunakan paket atau kunci API pengguna.

Jalankan pekerjaan di latar belakang

Pekerjaan yang melampaui satu peristiwa, seperti memeriksa sesuatu sekali semenit, berjalan pada timer yang Anda mulai dari session.start. Hook itu sendiri berjalan untuk satu peristiwa dan memiliki batas waktu untuk waktu eksekusinya sendiri. Waktu yang dihabiskan menunggu next atau panggilan mods API tidak dihitung, kecuali $.clock.sleep. $.clock.every dan $.clock.after menggantikan setInterval dan setTimeout, dengan penundaan dalam milidetik terlebih dahulu: $.clock.after(5000, fn) memanggil fn sekali, lima detik dari sekarang. Masing-masing mengembalikan timer dengan metode cancel(), dan await $.clock.now() memberikan waktu dalam milidetik.

Hook ini mencari pemeriksaan permintaan tarik sekali semenit dan menampilkan hasilnya di bawah prompt. summarize adalah fungsi Anda sendiri yang mengubah output JSON perintah menjadi beberapa kata:

on('session.start', async ($, e, next) => {
  // Call the function every 60,000 milliseconds, starting one minute from now
  $.clock.every(60_000, async () => {
    const status = await $.process.run(['gh', 'pr', 'checks', '--json', 'state'])
    // Replace the line under the prompt with the latest summary
    $.ui.status('checks: ' + summarize(status.stdout))
  })
  // Return without waiting for the timer, so the session starts right away
  return next(e)
})

Sesi dimulai seperti biasa. Satu menit kemudian, baris muncul di bawah prompt dengan ⚠, nama mod, dan kemudian checks: dan ringkasan Anda. Itu diganti sekali semenit setelahnya. Callback timer berjalan di luar peristiwa apa pun, jadi terus berjalan antar giliran dan tidak memulai satu. Jika callback melempar, kesalahan masuk ke debug log dan timer berjalan lagi pada interval berikutnya.

Tampilkan sesuatu tanpa memulai giliran

Pekerjaan latar belakang dapat menunjukkan kepada pengguna sesuatu tanpa memulai giliran. Masing-masing panggilan ini menempatkan teks di tempat yang berbeda:

Panggilan Apa yang dilihat pengguna
$.ui.status(text) Satu baris di bawah prompt yang tetap sampai Anda mengubahnya. Dimulai dengan ⚠ dan nama mod, seperti ⚠ my-mod: checks: 3 passing.
$.ui.toast(text) Notifikasi toast di kanan atas, dengan nama mod di atas teks, yang hilang setelah beberapa detik
$.ui.log(text) Baris redup dalam transkrip yang tidak dibaca Claude. Dimulai dengan ● dan nama mod, seperti ● my-mod: build finished.

Mulai giliran dari pekerjaan latar belakang

Saat pekerjaan latar belakang menemukan sesuatu yang memerlukan perhatian Claude, itu dapat memulai giliran dengan mengirimkan prompt dengan $.prompt.submit({ text }). Claude membaca teks setelah kalimat yang menyebutkan mod Anda sebagai pengirim. Untuk mengirimnya sebagai kata-kata pengguna sendiri, tanpa kalimat itu, tambahkan asUser: true. Panggilan menunggu sampai sesi menganggur dan kemudian memulai giliran baru. Itu menyelesaikan saat giliran itu dimulai, jadi jangan await dalam handler yang berjalan saat Claude sedang bekerja.

Hentikan pekerjaan latar belakang

Timer berhenti saat modul dimuat ulang. Untuk pekerjaan jangka panjang dalam hook, next.signal adalah AbortSignal yang membatalkan saat peristiwa yang ditangani hook Anda ditinggalkan, misalnya saat pengguna mengganggu, jadi teruskan ke apa pun yang berjalan lama.

Mengirim dan menerima pesan antar sesi

Sebuah mod dapat mengirim pesan teks biasa ke sesi Anda yang lain atau ke salah satu subagent milik sesi ini, serta mengamati pesan yang masuk dan keluar. $.session.send({ to, text }) mengirim satu pesan, dengan pengiriman yang sama seperti yang dilakukan tool SendMessage. to adalah { sessionId } untuk sebuah sesi, { agentId } untuk subagent dari $.agent.list(), atau alamat string asal pesan yang diterima. Panggilan ini selesai setelah pesan masuk antrean, dengan { isDelivered: true }. Ketika tidak ada yang tersampaikan, panggilan selesai dengan { isDelivered: false, reason }, dan reason menjelaskan alasannya.

Hook ini menjawab perintah /ping, yang didaftarkan sebagai perintah, dengan menanyakan status kepada sesi yang id-nya Anda ketik setelah perintah tersebut:

on('command.run', { command: 'ping' }, async ($, e) => {
  // e.args is the session id typed after /ping
  const sent = await $.session.send({ to: { sessionId: e.args }, text: 'Status? One line.' })
  // The call resolves either way, so check isDelivered to learn what happened
  if (!sent.isDelivered) $.ui.toast('Not delivered: ' + sent.reason)
  // An empty result prints nothing in this session's transcript
  return {}
})

Ketika pesan masuk antrean, tidak ada yang muncul di sesi Anda, dan Claude di sesi lain membaca Status? One line. Ketika tidak ada yang tersampaikan, notifikasi toast menampilkan alasannya.

session.receive dan session.send memungkinkan mod mengamati pesan. Kembalikan next(e) dari keduanya untuk meneruskan setiap pesan tanpa perubahan:

Event Dipicu ketika Field yang berguna
session.receive Sebuah pesan tiba untuk sesi ini, sebelum Claude membacanya e.text, dan e.origin.kind, seperti peer atau peer-send-message untuk sesi atau agent lain, task-notification, atau scheduled-trigger. Kembalikan { consumed: reason } agar pesan tidak sampai ke Claude.
session.send Sebuah pesan akan keluar, dari tool SendMessage atau dari mod e.to, e.text, dan e.origin.kind, yang bernilai model atau plugin

Sesi yang diatur untuk menolak pesan masuk menolak pesan sebelum session.receive dipicu, sehingga hook tidak pernah melihatnya. Pesan yang ditahan untuk menunggu persetujuan Anda sampai ke hook terlebih dahulu, sehingga mod dapat membaca pesan yang belum Anda setujui. next(e) milik hook akan ditolak (reject) ketika pesan tidak tersampaikan.

Nama pengirim pada pesan yang diterima adalah apa pun yang ditulis oleh pengirim, jadi jangan mendasarkan keputusan padanya.

Jangkau file, proses, dan jaringan

Mod menjangkau sistem file, proses, dan jaringan melalui mods API, dengan izin yang sama dengan pengguna yang menjalankan Claude Code. Modul hooks itu sendiri tidak memiliki API Node.js, tidak ada global timer seperti setTimeout, dan tidak memiliki akses jaringan atau file sendiri. API JavaScript standar dan web seperti URL, TextEncoder, AbortController, dan crypto.subtle tersedia. Setiap namespace di bawah mencakup satu jenis akses:

Namespace Apa yang dilakukannya
$.fs read(path), write(path, text), exists(path), stat(path), dan list(path) bekerja pada file dan direktori
$.process run(['git', 'status']) memulai perintah dan menyelesaikan saat keluar. spawn mengalirkan output perintah yang berjalan lama.
$.http fetch(url, init) atas http atau https. Itu menyelesaikan ke { status, ok, headers, text } setelah badan dibaca.
$.store Penyimpanan kunci-nilai JSON plugin Anda sendiri, disimpan antar sesi
$.env get dan set environment variable. Tulis nama sebagai string literal.
$.settings read apa yang dipegang file pengaturan dan kebijakan terkelola
$.session messages() mengembalikan transkrip sebagai daftar { role, text, toolUses }. Juga direktori kerja, model, dan lainnya. usage() mengembalikan penggunaan jendela konteks dan batas paket.
$.mcp call alat pada server MCP yang terhubung

File dan proses memiliki beberapa aturan mereka sendiri:

  • Paths: jalur relatif di-resolve terhadap direktori kerja sesi
  • $.fs.list: mengembalikan entri satu direktori sebagai { name, kind, size, isLink } dan tidak turun ke subdirektori
  • $.process.run: mengambil daftar argumen dan tidak menggunakan shell. Itu menyelesaikan ke { exitCode, stdout, stderr } apa pun kode keluar. Itu menolak jika program tidak dapat dimulai atau masih berjalan pada timeout, yang merupakan 30 detik secara default, jadi bungkus dalam try dan catch.

Setiap satu dari panggilan ini sendiri adalah peristiwa, dinamai untuk namespace dan metodenya tanpa $., seperti fs.read untuk $.fs.read. Mod lebih awal dalam rantai dapat mengamati, menulis ulang, atau menolak panggilan Anda, yang merupakan cara organisasi membatasi apa yang dijangkau mod.

Langkah berikutnya