SpyBara
Go Premium

plugins/mods/api.md 2026-09-30 23:00 UTC to 2026-10-01 21:59 UTC

This page contains 218 additions and 0 deletions.

2026
Thu 1 23:02

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 hanya 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 10 detik waktu berjalannya 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) Kotak kecil 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

Pekerjaan latar belakang berhenti dengan dua cara. 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 salah satu sesi Anda yang lain atau ke salah satu subagen sesi ini, dan mengamati pesan yang tiba dan pergi. $.session.send({ to, text }) mengirim satu, pengiriman yang sama dengan yang dilakukan alat SendMessage. to adalah { sessionId } untuk sesi, { agentId } untuk subagen dari $.agent.list(), atau alamat string tempat pesan yang diterima berasal. Panggilan diselesaikan setelah pesan antri, dengan { isDelivered: true }. Ketika tidak ada yang dikirim, pesan diselesaikan dengan { isDelivered: false, reason }, dan reason mengatakan mengapa.

Hook ini menjawab perintah /ping, terdaftar sebagai perintah, dengan meminta sesi yang id-nya Anda ketik setelahnya untuk status:

on('command.run', { command: 'ping' }, async ($, e) => {
  // e.args adalah id sesi yang diketik setelah /ping
  const sent = await $.session.send({ to: { sessionId: e.args }, text: 'Status? One line.' })
  // Panggilan diselesaikan dengan cara apa pun, jadi periksa isDelivered untuk mengetahui apa yang terjadi
  if (!sent.isDelivered) $.ui.toast('Not delivered: ' + sent.reason)
  // Hasil kosong tidak mencetak apa pun dalam transkrip sesi ini
  return {}
})

Ketika pesan antri, tidak ada yang muncul di sesi Anda, dan Claude sesi lain membaca Status? One line. Ketika tidak ada yang dikirim, kotak kecil di kanan atas memberikan alasan dan hilang setelah beberapa detik.

Dua peristiwa memungkinkan mod mengamati pesan. Kembalikan next(e) dari keduanya untuk melewatkan setiap pesan tanpa perubahan:

Peristiwa Terjadi ketika Bidang yang berguna
session.receive Pesan tiba untuk sesi ini, sebelum Claude membacanya e.text, dan e.origin.kind, seperti peer atau peer-send-message untuk sesi atau agen lain, task-notification, atau scheduled-trigger. Kembalikan { consumed: reason } untuk mencegahnya dari Claude.
session.send Pesan akan pergi, dari alat SendMessage atau mod e.to, e.text, dan e.origin.kind, yang merupakan model atau plugin

Sesi yang diatur untuk menolak pesan masuk menolak pesan sebelum session.receive terjadi, jadi hook tidak pernah melihatnya. Pesan yang ditahan untuk persetujuan Anda mencapai hook terlebih dahulu, jadi mod dapat membaca pesan yang belum Anda setujui. next(e) hook menolak ketika pesan tidak dikirim.

Nama pengirim pada pesan yang diterima adalah apa pun yang ditulis pengirim, jadi jangan membuat keputusan berdasarkan itu.

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 variabel lingkungan. 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 berada di bawah 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