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

# 使用 mods API

> 從 Claude Code mod 呼叫 mods API 以新增命令和工具、呼叫模型、在計時器上執行工作、向其他工作階段傳送訊息，以及存取檔案和網路。

mods API 是 mod 呼叫以執行動作的方法集合：新增命令和工具、呼叫模型、在事件之間執行工作，以及存取檔案系統、程序和網路。每個 hook 都會將其作為第一個引數 `$` 接收，方法分組在命名空間中，例如 `$.ui` 和 `$.fs`。[事件](/docs/zh-TW/plugins/mods/events)決定何時執行 hook，mods API 是 hook 執行後呼叫的內容。

在開始之前，請先建立您的[第一個 mod](/docs/zh-TW/plugins/mods/create)。對於每個方法，請參閱 [mods API 方法](/docs/zh-TW/plugins/mods/reference#mods-api-methods)或閱讀[您的建置類型](/docs/zh-TW/plugins/mods/create#get-the-types-for-your-build)。

<h2 id="add-a-command-or-a-tool">
  新增命令或工具
</h2>

mod 可以新增供使用者執行的命令和供 Claude 呼叫的工具。在 [`session.start`](/docs/zh-TW/plugins/mods/reference#session) hook 中註冊兩者。Claude Code 在第一個提示之前等待該 hook，因此您註冊的內容從第一個回合開始就可用。

<h3 id="add-a-command">
  新增命令
</h3>

命令是供使用者使用的。註冊它，然後為其名稱處理 [`command.run`](/docs/zh-TW/plugins/mods/reference#commands-and-configuration)。此範例新增了一個 `/standup` 命令，該命令採用可選的天數：

```javascript theme={null}
on('session.start', async ($, e, next) => {
  // 將 /standup 新增到命令列表，並附上使用者在該處看到的描述
  await $.command.register({ name: 'standup', description: 'Summarize what changed today', argumentHint: '[days]' })
  return next(e)
})

// 匹配器將 hook 限制為 /standup，因此其他命令不會到達它
on('command.run', { command: 'standup' }, async ($, e) => {
  // e.args 是在命令名稱後輸入的文字，或空字串
  return { text: 'Summary for the last ' + (e.args || '1') + ' day(s): ...' }
})
```

工作階段開始後，`/standup` 會與其描述一起出現在您輸入 `/` 時看到的列表中。`argumentHint` 在您輸入命令和空格後顯示在提示中，如 `/standup [days]`。當您執行 `/standup 3` 時，第二個 hook 會傳回 `Summary for the last 3 day(s): ...`，並且文字記錄會在外掛程式名稱後顯示該文字。hook 永遠不會呼叫 `next`，因為命令除了您的行為外沒有其他行為。

您傳回的 `text` 會列印在文字記錄中，Claude 會讀取它。若要不列印任何內容，如只開啟[窗格](/docs/zh-TW/plugins/mods/interface#pick-where-to-draw)的命令，請傳回 `{}`。若要讓命令在 Claude 工作時執行，請將 `immediate: true` 新增到註冊中。

選擇沒有內建命令使用的名稱。在工作階段中輸入 `/` 以查看它們。`$.command.register` 會針對已佔用的名稱擲回，並顯示類似 `"/focus" refused: it is the built-in /focus` 的訊息。擲回的 hook 會被跳過，因此該 `session.start` hook 的其餘部分也不會執行。在該 hook 中最後註冊命令，或將呼叫包裝在 `try` 和 `catch` 中。

<h3 id="add-a-tool">
  新增工具
</h3>

工具是供 Claude 使用的。使用名稱、Claude 讀取的描述和其輸入的 JSON Schema 來註冊它。Claude 會在由 `mcp__`、您的外掛程式名稱、兩個底線和您註冊的名稱組成的較長名稱下看到它。您在 [`tool.call`](/docs/zh-TW/plugins/mods/events#guard-or-change-a-tool-call) hook 中處理其呼叫，該 hook 已篩選為該完整名稱。此範例來自名為 `my-mod` 的外掛程式，註冊 `ticket`，因此完整名稱是 `mcp__my-mod__ticket`。它為 Claude 提供了一個在問題追蹤器中查詢票證的工具：

```javascript theme={null}
on('session.start', async ($, e, next) => {
  await $.tool.register({
    name: 'ticket',
    // Claude 根據此描述決定何時呼叫工具
    description: 'Look up a ticket by its id and return its title and status',
    // Claude 必須傳送的引數：一個名為 id 的必需字串
    inputSchema: { type: 'object', properties: { id: { type: 'string' } }, required: ['id'] },
  })
  return next(e)
})

// 完整工具名稱是 mcp__、外掛程式名稱和已註冊名稱
on('tool.call', { tool: 'mcp__my-mod__ticket' }, async ($, e) => {
  // 工具的引數是 e 的欄位，因此 id 是 e.id
  const response = await $.http.fetch('https://tickets.example.com/api/' + encodeURIComponent(e.id))
  // 無論如何都傳回結果，以便 Claude 了解查詢何時失敗
  return { result: response.ok ? response.text : 'Lookup failed with status ' + response.status }
})
```

當您詢問票證時，Claude 可以使用其 id 呼叫 `mcp__my-mod__ticket`。第二個 hook 會擷取票證並傳回回應本文，Claude 會將其讀取為工具的結果。當伺服器以錯誤狀態回答時，Claude 會讀取 `Lookup failed with status` 和數字。

<h2 id="call-a-model">
  呼叫模型
</h2>

模組可以在對話外提出自己的問題，用於排序或摘要文字等小工作。`$.model.complete` 會使用您的工作階段認證向模型發送一個提示，並解析為回覆。它沒有對話歷史。

此 hook 透過要求小型模型標記在其後輸入的文字來回答 `/triage` 命令（[註冊為命令](#add-a-command)）：

```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 }
})
```

當您執行 `/triage the export button does nothing` 時，模組會將該文字傳送給模型並列印其答案，例如 `Label: bug`。Claude 的對話不是請求的一部分。當模型沒有回答時，標籤為 `unknown`。

Claude API 失敗不會拒絕呼叫，因此請檢查 `r.isAnswered`，當其為 `false` 時請讀取 `r.reason`。呼叫只會因為 Claude Code 不會傳送的請求而被拒絕，例如您的組織封鎖的模型。[您的建置類型](/docs/zh-TW/plugins/mods/create#get-the-types-for-your-build)列出其他選項，例如 `effort`，而[限制](/docs/zh-TW/plugins/mods/reference#limits)提供 `maxTokens` 預設值。

`$.model.fork({ prompt })` 改為在目前對話上提出一個問題，使用相同的模型和系統提示，因此 Claude API 會從提示快取中提供大部分內容。

這些呼叫使用使用者的方案或 API 金鑰。

<h2 id="run-work-in-the-background">
  在背景執行工作
</h2>

超越一個事件的工作，例如每分鐘檢查一次，在您從 `session.start` 啟動的計時器上執行。hook 本身為一個事件執行，並有 10 秒的自己執行時間限制。在 `next` 或 mods API 呼叫上花費的時間不計算，除了 `$.clock.sleep`。`$.clock.every` 和 `$.clock.after` 取代 `setInterval` 和 `setTimeout`，延遲以毫秒為單位首先：`$.clock.after(5000, fn)` 在五秒後呼叫 `fn` 一次。每個都傳回一個具有 `cancel()` 方法的計時器，而 `await $.clock.now()` 給出以毫秒為單位的時間。

此 hook 每分鐘查詢一次提取請求的檢查，並在提示下方顯示結果。`summarize` 是您自己的函式，將命令的 JSON 輸出轉換為幾個單詞：

```javascript theme={null}
on('session.start', async ($, e, next) => {
  // 每 60,000 毫秒呼叫一次函式，從現在開始一分鐘後開始
  $.clock.every(60_000, async () => {
    const status = await $.process.run(['gh', 'pr', 'checks', '--json', 'state'])
    // 用最新摘要取代提示下方的行
    $.ui.status('checks: ' + summarize(status.stdout))
  })
  // 傳回而不等待計時器，以便工作階段立即開始
  return next(e)
})
```

工作階段照常開始。一分鐘後，提示下方會出現一行，其中包含 `⚠`、mod 的名稱，然後是 `checks:` 和您的摘要。之後每分鐘會被取代一次。計時器的回呼在任何事件之外執行，因此它在回合之間保持執行，不會啟動一個。如果回呼擲回，錯誤會進入[偵錯日誌](/docs/zh-TW/plugins/mods/troubleshoot#read-the-debug-log)，計時器在下一個間隔再次執行。

<h3 id="show-something-without-starting-a-turn">
  顯示某些內容而不啟動回合
</h3>

背景工作可以向使用者顯示某些內容而不啟動回合。這些呼叫中的每一個都將文字放在不同的位置：

| 呼叫 | 使用者看到的內容 |
| :- | :- |
| `$.ui.status(text)` | 提示下方的一行，保持到您變更它為止。它以 `⚠` 和 mod 的名稱開頭，如 `⚠ my-mod: checks: 3 passing`。 |
| `$.ui.toast(text)` | 右上角的一個小框，mod 的名稱在文字上方，幾秒後消失 |
| `$.ui.log(text)` | 文字記錄中的一條暗線，Claude 不讀取。它以 `●` 和 mod 的名稱開頭，如 `● my-mod: build finished`。 |

<h3 id="start-a-turn-from-a-background-job">
  從背景工作啟動回合
</h3>

當背景工作發現需要 Claude 注意的內容時，它可以透過使用 `$.prompt.submit({ text })` 提交提示來啟動回合。Claude 讀取文字後面有一句話，該句話將您的 mod 命名為寄件者。若要將其作為使用者自己的話語傳送，不帶該句話，請新增 `asUser: true`。呼叫會等待直到工作階段閒置，然後啟動新回合。它在該回合啟動時解析，因此不要在 Claude 工作時執行的處理程式中 `await` 它。

<h3 id="stop-background-work">
  停止背景工作
</h3>

背景工作以兩種方式停止。當模組重新載入時，計時器停止。對於 hook 內的長時間執行工作，[`next.signal`](/docs/zh-TW/plugins/mods/reference#the-hook-function) 是一個 `AbortSignal`，當您的 hook 正在處理的事件被放棄時中止，例如當使用者中斷時，因此將其傳遞給任何長時間執行的內容。

<h2 id="send-and-receive-messages-between-sessions">
  在工作階段之間傳送和接收訊息
</h2>

一個 mod 可以向另一個工作階段或此工作階段的子代理傳送純文字訊息，並觀察到達和離開的訊息。`$.session.send({ to, text })` 傳送一個訊息，與 SendMessage 工具進行相同的傳遞。`to` 是工作階段的 `{ sessionId }`、來自 `$.agent.list()` 的子代理的 `{ agentId }`，或接收訊息來自的字串位址。呼叫在訊息排隊後解析，返回 `{ isDelivered: true }`。當沒有任何內容被傳遞時，它會以 `{ isDelivered: false, reason }` 解析，`reason` 說明原因。

此 hook 透過詢問您在其後輸入的 id 的工作階段的狀態，來回答 `/ping` 命令（[註冊為命令](#add-a-command)）：

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

當訊息排隊時，您的工作階段中不會出現任何內容，另一個工作階段的 Claude 會讀取 `Status? One line.`。當沒有任何內容被傳遞時，右上角的小方塊會顯示原因，並在幾秒後消失。

兩個事件讓 mod 觀察訊息。從兩者都返回 `next(e)` 以不變地傳遞每個訊息：

| 事件 | 何時觸發 | 有用的欄位 |
| :- | :- | :- |
| `session.receive` | 訊息到達此工作階段，在 Claude 讀取之前 | `e.text` 和 `e.origin.kind`，例如另一個工作階段或代理的 `peer` 或 `peer-send-message`、`task-notification` 或 `scheduled-trigger`。返回 `{ consumed: reason }` 以防止 Claude 讀取。 |
| `session.send` | 訊息即將離開，來自 SendMessage 工具或 mod | `e.to`、`e.text` 和 `e.origin.kind`，即 `model` 或 `plugin` |

設定為[拒絕入站訊息](/docs/zh-TW/cross-session-messaging#control-inbound-messages)的工作階段在 `session.receive` 觸發之前拒絕訊息，因此 hook 永遠不會看到它。為您的批准而保留的訊息首先到達 hook，因此 mod 可以讀取您尚未批准的訊息。hook 的 `next(e)` 在訊息未被傳遞時拒絕。

接收訊息上的寄件者名稱是寄件者寫的任何內容，因此不要基於它做出決定。

<h2 id="reach-files-processes-and-the-network">
  存取檔案、程序和網路
</h2>

mod 透過 mods API 存取檔案系統、程序和網路，具有與執行 Claude Code 的使用者相同的權限。hooks 模組本身沒有 Node.js API、沒有計時器全域變數（例如 `setTimeout`），也沒有自己的網路或檔案存取。標準 JavaScript 和網路 API（例如 `URL`、`TextEncoder`、`AbortController` 和 `crypto.subtle`）可用。下面的每個命名空間涵蓋一種存取：

| 命名空間 | 它的作用 |
| :- | :- |
| `$.fs` | `read(path)`、`write(path, text)`、`exists(path)`、`stat(path)` 和 `list(path)` 在檔案和目錄上工作 |
| `$.process` | `run(['git', 'status'])` 啟動命令並在其退出時解析。`spawn` 串流長時間執行命令的輸出。 |
| `$.http` | `fetch(url, init)` 透過 `http` 或 `https`。它在讀取本文後解析為 `{ status, ok, headers, text }`。 |
| `$.store` | 您外掛程式自己的 JSON 鍵值存放區，在工作階段之間保留 |
| `$.env` | `get` 和 `set` 環境變數。將名稱寫為文字字串。 |
| `$.settings` | `read` 設定檔和受管原則保留的內容 |
| `$.session` | `messages()` 將文字記錄傳回為 `{ role, text, toolUses }` 的列表。也是工作目錄、模型等。[`usage()`](/docs/zh-TW/plugins/mods/reference#mods-api-methods) 傳回內容視窗使用和計畫限制。 |
| `$.mcp` | `call` 連接的 MCP 伺服器上的工具 |

檔案和程序有幾個自己的規則：

* **路徑**：相對路徑在工作階段的工作目錄下
* **`$.fs.list`**：將一個目錄的項目傳回為 `{ name, kind, size, isLink }`，不會下降到子目錄中
* **`$.process.run`**：採用引數列表，不使用 shell。無論退出代碼如何，它都解析為 `{ exitCode, stdout, stderr }`。如果程式無法啟動或在逾時時仍在執行，它會拒絕，預設為 30 秒，因此將其包裝在 `try` 和 `catch` 中。

這些呼叫中的每一個本身都是一個事件，以其命名空間和方法命名，不帶 `$.`，例如 `$.fs.read` 的 `fs.read`。鏈中[較早的](/docs/zh-TW/plugins/mods/events#the-order-mods-run-in) mod 可以觀察、重寫或拒絕您的呼叫，這是組織限制 mod 到達的方式。

<h2 id="next-steps">
  後續步驟
</h2>

* [對事件做出反應](/docs/zh-TW/plugins/mods/events)：hook 工具呼叫、提示和回合
* [在介面中繪製](/docs/zh-TW/plugins/mods/interface)：在窗格或提示上方顯示您的 mod 收集的內容
* [測試 mod](/docs/zh-TW/plugins/mods/test)：在測試中存根這些呼叫中的任何一個
* [Mods 參考](/docs/zh-TW/plugins/mods/reference)：每個事件、每個 mods API 方法和限制
