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

使用 mods API

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

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

在開始之前,請先建立您的第一個 mod。對於每個方法,請參閱 mods API 方法或閱讀您的建置類型。

新增命令或工具

mod 可以新增供使用者執行的命令和供 Claude 呼叫的工具。在 session.start hook 中註冊兩者。Claude Code 在第一個提示之前等待該 hook,因此您註冊的內容從第一個回合開始就可用。

新增命令

命令是供使用者使用的。註冊它,然後為其名稱處理 command.run。此範例新增了一個 /standup 命令,該命令採用可選的天數:

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 會讀取它。若要不列印任何內容,如只開啟窗格的命令,請傳回 {}。若要讓命令在 Claude 工作時執行,請將 immediate: true 新增到註冊中。

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

新增工具

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

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 和數字。

呼叫模型

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

此 hook 透過要求小型模型標記在其後輸入的文字來回答 /triage 命令(註冊為命令):

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 不會傳送的請求而被拒絕,例如您的組織封鎖的模型。您的建置類型列出其他選項,例如 effort,而限制提供 maxTokens 預設值。

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

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

在背景執行工作

超越一個事件的工作,例如每分鐘檢查一次,在您從 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 輸出轉換為幾個單詞:

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: 和您的摘要。之後每分鐘會被取代一次。計時器的回呼在任何事件之外執行,因此它在回合之間保持執行,不會啟動一個。如果回呼擲回,錯誤會進入偵錯日誌,計時器在下一個間隔再次執行。

顯示某些內容而不啟動回合

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

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

從背景工作啟動回合

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

停止背景工作

背景工作以兩種方式停止。當模組重新載入時,計時器停止。對於 hook 內的長時間執行工作,next.signal 是一個 AbortSignal,當您的 hook 正在處理的事件被放棄時中止,例如當使用者中斷時,因此將其傳遞給任何長時間執行的內容。

在工作階段之間傳送和接收訊息

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

此 hook 透過詢問您在其後輸入的 id 的工作階段的狀態,來回答 /ping 命令(註冊為命令):

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

設定為拒絕入站訊息的工作階段在 session.receive 觸發之前拒絕訊息,因此 hook 永遠不會看到它。為您的批准而保留的訊息首先到達 hook,因此 mod 可以讀取您尚未批准的訊息。hook 的 next(e) 在訊息未被傳遞時拒絕。

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

存取檔案、程序和網路

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() 傳回內容視窗使用和計畫限制。
$.mcp call 連接的 MCP 伺服器上的工具

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

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

這些呼叫中的每一個本身都是一個事件,以其命名空間和方法命名,不帶 $.,例如 $.fs.read 的 fs.read。鏈中較早的 mod 可以觀察、重寫或拒絕您的呼叫,這是組織限制 mod 到達的方式。

後續步驟