> ## Documentation Index
> Fetch the complete documentation index at: https://code.claude.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# 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](/docs/id/plugins/mods/events) menentukan kapan hook berjalan, dan mods API adalah apa yang dipanggil hook setelah berjalan.

Bangun [mod pertama Anda](/docs/id/plugins/mods/create) sebelum Anda mulai di sini. Untuk setiap metode, lihat [mods API methods](/docs/id/plugins/mods/reference#mods-api-methods) atau baca [tipe untuk build Anda](/docs/id/plugins/mods/create#get-the-types-for-your-build).

<h2 id="add-a-command-or-a-tool">
  Tambahkan perintah atau alat
</h2>

Mod dapat menambahkan perintah untuk dijalankan pengguna dan alat untuk dipanggil Claude. Daftarkan keduanya dalam hook [`session.start`](/docs/id/plugins/mods/reference#session). Claude Code menunggu hook itu sebelum prompt pertama, jadi apa yang Anda daftarkan tersedia dari giliran pertama.

<h3 id="add-a-command">
  Tambahkan perintah
</h3>

Perintah adalah untuk pengguna. Daftarkan, kemudian tangani [`command.run`](/docs/id/plugins/mods/reference#commands-and-configuration) untuk namanya. Contoh ini menambahkan perintah `/standup` yang mengambil jumlah hari opsional:

```javascript theme={null}
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](/docs/id/plugins/mods/interface#pick-where-to-draw), 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`.

<h3 id="add-a-tool">
  Tambahkan alat
</h3>

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`](/docs/id/plugins/mods/events#guard-or-change-a-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:

```javascript theme={null}
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.

<h2 id="call-a-model">
  Panggil model
</h2>

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](#add-a-command), dengan menanyakan model kecil untuk memberi label pada teks yang diketik setelahnya:

```javascript theme={null}
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](/docs/id/plugins/mods/create#get-the-types-for-your-build) mencantumkan opsi lain, seperti `effort`, dan [batas](/docs/id/plugins/mods/reference#limits) 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.

<h2 id="run-work-in-the-background">
  Jalankan pekerjaan di latar belakang
</h2>

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:

```javascript theme={null}
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](/docs/id/plugins/mods/troubleshoot#read-the-debug-log) dan timer berjalan lagi pada interval berikutnya.

<h3 id="show-something-without-starting-a-turn">
  Tampilkan sesuatu tanpa memulai giliran
</h3>

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`. |

<h3 id="start-a-turn-from-a-background-job">
  Mulai giliran dari pekerjaan latar belakang
</h3>

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.

<h3 id="stop-background-work">
  Hentikan pekerjaan latar belakang
</h3>

Pekerjaan latar belakang berhenti dengan dua cara. Timer berhenti saat modul dimuat ulang. Untuk pekerjaan jangka panjang dalam hook, [`next.signal`](/docs/id/plugins/mods/reference#the-hook-function) adalah `AbortSignal` yang membatalkan saat peristiwa yang ditangani hook Anda ditinggalkan, misalnya saat pengguna mengganggu, jadi teruskan ke apa pun yang berjalan lama.

<h2 id="send-and-receive-messages-between-sessions">
  Mengirim dan menerima pesan antar sesi
</h2>

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](#add-a-command), dengan meminta sesi yang id-nya Anda ketik setelahnya untuk status:

```javascript theme={null}
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](/docs/id/cross-session-messaging#control-inbound-messages) 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.

<h2 id="reach-files-processes-and-the-network">
  Jangkau file, proses, dan jaringan
</h2>

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()`](/docs/id/plugins/mods/reference#mods-api-methods) 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](/docs/id/plugins/mods/events#the-order-mods-run-in) dapat mengamati, menulis ulang, atau menolak panggilan Anda, yang merupakan cara organisasi membatasi apa yang dijangkau mod.

<h2 id="next-steps">
  Langkah berikutnya
</h2>

* [React to events](/docs/id/plugins/mods/events): hook tool calls, prompts, dan turns
* [Draw in the interface](/docs/id/plugins/mods/interface): tampilkan apa yang dikumpulkan mod Anda dalam pane atau di atas prompt
* [Test a mod](/docs/id/plugins/mods/test): stub salah satu dari panggilan ini dalam tes
* [Mods reference](/docs/id/plugins/mods/reference): setiap peristiwa, setiap metode mods API, dan batasnya
