# 擴充功能
URL: /zh-TW/docs/develop/extensions

套件佈局、spiritExtension 欄位、啟動 API，以及在官方註冊表中的列出方式。



擴充功能是一個包含 `package.json` 的資料夾，其中含有 `spiritExtension`。Spirit 會為每個使用者和每個主機安裝它，而不是放在工作區的 `.spirit/` 目錄下。

| 主機      | 路徑                                    |
| ------- | ------------------------------------- |
| Desktop | `{spiritDataDir}/extensions/desktop/` |
| CLI     | `{spiritDataDir}/extensions/cli/`     |

這與 [Skills](../customize/skills.mdx) (`SKILL.md` 資料夾)、[MCP](../customize/mcp.mdx) (外部伺服器) 和 [Hooks](../customize/hooks.mdx) (`hooks.json` 腳本) 不同。`contributes.cli.hooks` 樣式化 CLI TUI 插槽。它不是 `hooks.json`。

沒有每個擴充功能的啟用開關。安裝後，擴充功能即會貢獻內容。CLI 面板的切換是佔位符。

## 套件佈局 [#套件佈局]

```text
example-extension/
  package.json
  dist/index.js
  assets/icon.svg
  styles/desktop.css
  cli-hooks.json
```

```json
{
  "name": "@example/spirit-extension",
  "version": "0.1.0",
  "description": "Example Spirit Agent extension.",
  "author": { "name": "example" },
  "homepage": "https://example.com/spirit-extension",
  "main": "dist/index.js",
  "spiritExtension": {
    "schemaVersion": 1,
    "displayName": "Example extension",
    "icon": "assets/icon.svg",
    "supportedHosts": ["cli", "desktop"],
    "activationEvents": ["onStartup", "onUserMessage"],
    "requestedCapabilities": [
      "tool-definitions",
      "tool-execution",
      "system-prompt",
      "settings",
      "secret-storage",
      "desktop-ui",
      "cli-ui"
    ],
    "contributes": {
      "tools": [
        {
          "name": "lookup_item",
          "description": "Look up an item by id.",
          "inputSchema": {
            "type": "object",
            "properties": {
              "id": { "type": "string" }
            },
            "required": ["id"]
          },
          "approvalMode": "allowed",
          "executionMode": "foreground"
        }
      ],
      "desktop": {
        "css": [{ "path": "styles/desktop.css" }],
        "settingsPage": { "title": "Example extension" }
      },
      "cli": {
        "hooks": { "path": "cli-hooks.json" }
      }
    },
    "settingsSchema": [
      {
        "key": "region",
        "type": "select",
        "title": "Region",
        "required": true,
        "defaultValue": "us",
        "options": [
          { "value": "us", "label": "US" },
          { "value": "eu", "label": "EU" }
        ]
      }
    ],
    "secretSlots": [
      {
        "key": "api_token",
        "title": "API token",
        "required": true
      }
    ]
  }
}
```

`package.json` 的 `name` 是擴充功能 ID。它必須符合 `^(?:@[a-z0-9][a-z0-9._-]*/)?[a-z0-9][a-z0-9._-]*$`。

## `package.json` 欄位 [#packagejson-欄位]

| 欄位                | 描述                                                              |
| ----------------- | --------------------------------------------------------------- |
| `name`            | 必要。npm 套件名稱；用作擴充功能 ID                                           |
| `version`         | 必要。版本字串                                                         |
| `spiritExtension` | 必要。物件。請參閱下方                                                     |
| `description`     | 選用。字串                                                           |
| `author`          | 選用。字串或 `{ name }`                                               |
| `homepage`        | 選用。字串                                                           |
| `main`            | 選用。`activate` 進入點的相對路徑。當擴充功能使用 `activationEvents`、工具執行或系統提示時為必要 |

`main`、`spiritExtension.icon`、CSS 路徑和 CLI 鉤子路徑必須是相對的，且必須位於套件內部。

## `spiritExtension` [#spiritextension]

| 欄位                      | 描述                                |
| ----------------------- | --------------------------------- |
| `schemaVersion`         | 選用。預設為 `1`。僅支援 `1`                |
| `displayName`           | 必要。使用者可見名稱                        |
| `icon`                  | 選用。相對於套件根目錄的路徑                    |
| `supportedHosts`        | 必要。非空的 `cli` 和/或 `desktop` 陣列     |
| `activationEvents`      | 選用。請參閱 [啟動事件](#activation-events) |
| `requestedCapabilities` | 選用。請參閱 [功能](#capabilities)        |
| `contributes`           | 選用。`tools`、`desktop` 和/或 `cli`    |
| `settingsSchema`        | 選用。設定定義                           |
| `secretSlots`           | 選用。密鑰槽定義                          |

## 功能 [#功能]

| 值                    | 執行環境                                                |
| -------------------- | --------------------------------------------------- |
| `tool-definitions`   | 需要搭配 `tool-execution` 才能將 `contributes.tools` 暴露給模型 |
| `tool-execution`     | 需要搭配 `tool-definitions` 才能執行那些工具                    |
| `system-prompt`      | 需要搭配 `main` 才能貢獻系統提示片段                              |
| `desktop-ui`         | 必須與 `contributes.desktop` 配對                        |
| `cli-ui`             | 必須與 `contributes.cli` 配對                            |
| `approval-flow`      | 僅宣告。核准由每個工具的 `approvalMode` 驅動                      |
| `questions-flow`     | 僅宣告。問題由 `approvalMode: need-questions` 驅動           |
| `settings`           | 僅宣告。設定來自 `settingsSchema`                           |
| `secret-storage`     | 與 `secretSlots` 搭配使用                                |
| `structured-results` | 僅宣告                                                 |

`desktop-ui` / `cli-ui` 和對應的 `contributes` 區塊必須同時存在或同時不存在。

## 啟動事件 [#啟動事件]

| 事件                     | 觸發時機                     |
| ---------------------- | ------------------------ |
| `onStartup`            | 主機預熱                     |
| `onExtensionInstalled` | 在 ZIP 或 marketplace 安裝之後 |
| `onSessionOpened`      | 會話變為啟用狀態                 |
| `onSessionReset`       | 會話重置                     |
| `onUserMessage`        | 使用者提交訊息                  |
| `onToolCall`           | 工具即將執行                   |
| `onToolResult`         | 工具結果可用時                  |
| `onApprovalResolved`   | 核准決定已解決時                 |

當擴充功能列出事件且具有可讀的 `main` 時，主機會呼叫 `activate`。

## `contributes.tools` [#contributestools]

| 欄位              | 描述                                              |
| --------------- | ----------------------------------------------- |
| `name`          | 必填。`[a-z0-9]+(?:[._-][a-z0-9]+)*`               |
| `description`   | 必填。顯示給模型                                        |
| `inputSchema`   | 必填。JSON Schema 物件                               |
| `outputSchema`  | 選填。JSON Schema 物件                               |
| `approvalMode`  | 選填。`allowed`、`need-approval` 或 `need-questions` |
| `executionMode` | 選填。`foreground` 或 `background`                  |

模型不會直接看到 `name`。主機會建構一個呼叫名稱，例如 `extension__{id}__{tool}__{hash}`。

## `contributes.desktop` [#contributesdesktop]

需要 `desktop-ui`。

| 欄位             | 描述                                             |
| -------------- | ---------------------------------------------- |
| `css[].path`   | 必填。相對於套件根目錄的 CSS 檔案                            |
| `css[].media`  | 選填。CSS media query                             |
| `settingsPage` | 選填。`true`、`{}` 或 `{ title }` — 新增 Desktop 設定項目 |

## `contributes.cli` [#contributescli]

需要 `cli-ui`。`hooks` 是帶有 `path` 的物件，不是內聯陣列：

```json
{
  "hooks": { "path": "cli-hooks.json" }
}
```

檔案必須是 `{ "hooks": [ ... ] }`。

| 欄位        | 說明                                                           |
| --------- | ------------------------------------------------------------ |
| `slot`    | 必要。下列插槽之一                                                    |
| `variant` | 選用。`default`、`accented`、`muted`、`warning`、`success`、`danger` |
| `tokens`  | 選用。`{ foreground?, border?, accent? }`                       |
| `prefix`  | 選用。字串                                                        |
| `suffix`  | 選用。字串                                                        |

插槽：`message.user`、`message.assistant`、`message.tool`、`assistant.thinking`、`input.frame`、`bottom_form`、`bottom_form.section`、`slash_suggestions`、`approval.panel`、`questions.panel`。

Token 角色：`default`、`primary`、`secondary`、`muted`、`accent`、`success`、`warning`、`danger`。

```json
{
  "hooks": [
    {
      "slot": "input.frame",
      "variant": "accented",
      "tokens": { "border": "accent" },
      "prefix": "[",
      "suffix": "]"
    }
  ]
}
```

## `settingsSchema` [#settingsschema]

| 欄位             | 說明                                               |
| -------------- | ------------------------------------------------ |
| `key`          | 必要。與工具名稱的模式相同                                    |
| `type`         | 必要。`string`、`boolean`、`number` 或 `select`        |
| `title`        | 必要。UI 標籤                                         |
| `description`  | 選用                                               |
| `placeholder`  | 選用                                               |
| `required`     | 選用。布林值                                           |
| `defaultValue` | 選用。必須符合 `type`                                   |
| `options`      | `select` 必填。`{ value, label, description? }` 的陣列 |

值為 `string`、`number`、`boolean` 或 `null`。`null` 會清除非必要設定。

## `secretSlots` [#secretslots]

| 欄位            | 說明     |
| ------------- | ------ |
| `key`         | 必要     |
| `title`       | 必要     |
| `description` | 選用     |
| `required`    | 選用。布林值 |

Desktop 將秘密儲存在作業系統鑰匙圈中。在 CLI 守護程式路徑下，`secrets.set` / `secrets.delete` 可能無法使用。

## `activate` [#activate]

`main` 會以動態 `import` 載入。匯出以下之一：

* `export function activate(ctx) { ... }`
* `export default function activate(ctx) { ... }`
* `export default { activate }`

### `ctx` [#ctx]

| 欄位                | 說明                                                                              |
| ----------------- | ------------------------------------------------------------------------------- |
| `extension`       | `{ id, name, version, directoryPath, manifestPath, main }`                      |
| `host`            | 主機 API。Desktop 實作 `showMessageBox`。CLI 為 `{}`                                   |
| `log`             | `(message: string) => void`                                                     |
| `settings`        | `get(key)`、`getAll()`、`set(key, value)`、`setAll(values)`                        |
| `secrets`         | `get(key)`、`has(key)`、`set(key, value)`、`delete(key)` — 金鑰必須在 `secretSlots` 中宣告 |
| `activationEvent` | 可選。`{ type, detail? }`                                                          |

### 回傳值 [#回傳值]

回傳一個物件，或從模組匯出相同的欄位。

| 欄位                | 描述                                                         |
| ----------------- | ---------------------------------------------------------- |
| `tools`           | `Record<string, (ctx) => unknown>`。鍵為 manifest 工具名稱        |
| `invokeTool`      | `(ctx) => unknown`。當存在時，用來取代 `tools[name]`                 |
| `systemPrompt`    | 靜態系統片段                                                     |
| `getSystemPrompt` | `() => string \| Promise<string>`。當存在時，用來取代 `systemPrompt` |
| `onEvent`         | `(event) => void`                                          |
| `dispose`         | `() => void`。在移除或重新載入時呼叫                                   |

### 工具處理器 `ctx` [#工具處理器-ctx]

| 欄位                | 描述                                         |
| ----------------- | ------------------------------------------ |
| `extension`       | 與 `activate` 相同的執行時期資訊                     |
| `host`            | 相同的主機 API                                  |
| `toolName`        | Manifest 工具名稱                              |
| `arguments`       | 來自模型的物件                                    |
| `log`             | 相同的記錄器                                     |
| `settings`        | 相同的設定存取器                                   |
| `secrets`         | 相同的密鑰存取器                                   |
| `toolCallId`      | 可選                                         |
| `questionsResult` | 可選。當 `approvalMode` 為 `need-questions` 時設定 |

`string` 結果會直接傳遞。其他值會轉為 `JSON.stringify(value, null, 2)`。`undefined` 變成 `""`。

### Desktop `host.showMessageBox` [#desktop-hostshowmessagebox]

| 欄位          | 描述                                            |
| ----------- | --------------------------------------------- |
| `title`     | 必填。字串                                         |
| `message`   | 必填。字串                                         |
| `detail`    | 可選。字串                                         |
| `buttons`   | 可選。字串陣列                                       |
| `cancelId`  | 可選。數字                                         |
| `defaultId` | 可選。數字                                         |
| `noLink`    | 可選。布林值                                        |
| `type`      | 可選。`none`、`info`、`error`、`question`、`warning` |

```js
export function activate(ctx) {
  return {
    systemPrompt: "Use lookup_item when the user asks for an item by id.",
    async invokeTool({ toolName, arguments: args, settings, secrets }) {
      if (toolName !== "lookup_item") {
        throw new Error(`Unknown tool: ${toolName}`);
      }
      const region = await settings.get("region");
      const token = await secrets.get("api_token");
      return { id: args.id, region, hasToken: Boolean(token) };
    },
    dispose() {
      ctx.log("example-extension disposed");
    },
  };
}
```

## 安裝 [#安裝]

* **ZIP** — 壓縮檔必須恰好包含一個 `package.json`（它可以位於子目錄中）。使用 `spirit extension import ./extension.zip` 或 **設定 → 擴充功能** 匯入。
* **Marketplace tarball** — 壓縮檔根目錄必須包含 `package/` 目錄。

`supportedHosts` 必須包含目前的主機。相同 id 的第二次安裝預設不會取代現有副本。有關頻道和安裝介面，請參閱 [Marketplace](../customize/marketplace.mdx)。

## 發布到官方登錄檔 [#發布到官方登錄檔]

[SpiritAgents/registry](https://github.com/SpiritAgents/registry) 儲存庫是 marketplace 索引。它不託管擴充功能原始碼、`dist`、ZIP 檔案或 tarball。

請依序執行下列步驟：

1. 發布一個公開的 npm 套件。
2. 開啟一個列出該套件的 pull request。

### 1. 發布 npm 套件 [#1-發布-npm-套件]

發布的 `package.json` 是主要依據，尤其是 `spiritExtension`。註冊表建置器需要：

* `spiritExtension.schemaVersion`
* `spiritExtension.displayName`
* `spiritExtension.supportedHosts`
* `spiritExtension.requestedCapabilities`

它也會讀取 `name`、`version`、`description`、`author`、`repository`、`homepage`、`keywords` 以及選用的 `spiritExtension.icon`。如果您設定了 `icon`，請將該檔案包含在發布的套件中。缺少 `spiritExtension` 物件會導致該版本的註冊表建置失敗。

官方範例：[`@spiritagent/extension-system-message-demo`](https://www.npmjs.com/package/@spiritagent/extension-system-message-demo)（`spiritagent.system-message-demo`）。

### 2. 列出套件 [#2-列出套件]

建立 `registry/extensions/<extension-id>/`，內容包含：

| 檔案           | 說明       |
| ------------ | -------- |
| `entry.json` | 市集治理     |
| `README.md`  | 市集詳細資訊文字 |

請勿手動編輯這些（請重新產生）：

* `registry/catalog.json`
* `registry/extensions/<extension-id>/detail.json`

`extensionId` 需要至少兩個以點分隔的區段，使用小寫字母和數字，區段內可包含點或連字號。範例：`spiritagent.system-message-demo`、`yourteam.some-extension`。`packageName` 和 `extensionId` 在儲存庫中必須是唯一的。

### `entry.json` [#entryjson]

| 欄位                    | 說明                                            |
| --------------------- | --------------------------------------------- |
| `schemaVersion`       | 必要。`1`                                        |
| `extensionId`         | 必要。請參閱上述規則                                    |
| `packageName`         | 必要。npm 套件名稱                                   |
| `status`              | 必要。`listed`、`hidden`、`deprecated` 或 `blocked` |
| `featured`            | 必要。布林值                                        |
| `defaultVersion`      | 必要。預設版本字串                                     |
| `defaultReviewStatus` | 必要。`unverified`、`verified` 或 `revoked`        |
| `versions`            | 必要。至少一個項目                                     |

每個 `versions[]` 項目：

| 欄位             | 說明                                     |
| -------------- | -------------------------------------- |
| `version`      | 必要                                     |
| `channel`      | 必要。`stable`、`preview` 或 `experimental` |
| `reviewStatus` | 必要。`unverified`、`verified` 或 `revoked` |
| `changelog`    | 選用。`{ summary, body }` — 存在時兩者皆為必要     |

```json
{
  "schemaVersion": 1,
  "extensionId": "example.spirit-extension",
  "packageName": "@example/spirit-extension",
  "status": "listed",
  "featured": false,
  "defaultVersion": "0.1.0",
  "defaultReviewStatus": "unverified",
  "versions": [
    {
      "version": "0.1.0",
      "channel": "stable",
      "reviewStatus": "unverified",
      "changelog": {
        "summary": "Initial public release.",
        "body": "- Initial public release."
      }
    }
  ]
}
```

市集 README 應說明擴充功能的功能、套件名稱、預設核准版本、主機與能力相容性，以及給市集讀者的注意事項。

在本機重新產生衍生檔案：

```powershell
./scripts/build-registry.ps1
```

此腳本會擷取所列版本的 npm 中繼資料，重建 `catalog.json` 和每個 `detail.json`，然後驗證一致性。

### Pull request [#pull-request]

包含 `entry.json`、市集 `README.md` 以及重新產生的 `catalog.json` / `detail.json`。僅列出已在 npm 上公開的版本。準確設定 `reviewStatus` 和 `channel`。新版本通常以 `unverified` 開始。

請勿提交擴充功能原始碼樹、`dist`、ZIP 檔案、tarball 或其他二進位成品。

開啟 pull request 並不保證會上架。在版本獲得核准或驗證之前，審查可能會要求變更。
