# 插件目录

插件是一个目录，它在单个清单后面捆绑了 SDK 扩展 — 技能、钩子、MCP 服务器、自定义智能体和 LSP 配置。 将 SDK 指向插件目录会加载插件提供的所有内容，因此你可以在每个主机应用程序中交付可重用的功能包，而无需在每个主机应用程序中编写每扩展连接。

<!-- markdownlint-disable GHD046 GHD005 -->

<!-- Suppressed: GHD046 (outdated release terminology), GHD005 (hardcoded data variable) -->

本指南介绍插件文件夹布局、如何从目录中加载插件、何时使用插件目录与注册各个扩展，以及如何使插件集具有确定性。

## 何时使用插件目录

在以下情况下，请使用插件目录：

* **将一组能力作为一个整体分发**，例如一个“TypeScript 审查器”包，其中包含一个技能、一个执行 lint 检查的 `preToolUse` 钩子，以及一个运行该审查器的自定义代理。
* **供应商功能包到存储库中** ，以便主机应用程序的每个克隆都能确定地加载相同的扩展。
* **在本地开发插件** ，然后将其发布到市场。
* ```
            使用本地签出**覆盖或扩展**已安装市场的插件以进行测试。
  ```

如果你只需要添加单个 MCP 服务器、单个钩子或单个自定义代理，则可以通过 SDK 配置（`mcpServers`、`hooks`、`customAgents`）直接以内联方式注册。 一旦有三个或更多相关的扩展一起交付，插件目录就非常有用。

## 插件文件夹布局

Copilot CLI 扫描每个插件目录以查找 `plugin.json` 清单或根级 `SKILL.md`。 最小的插件如下所示：

```text
my-plugin/
├── plugin.json              # manifest (required unless using SKILL.md only)
├── SKILL.md                 # optional: top-level skill
├── hooks.json               # optional: hooks config
├── .mcp.json                # optional: MCP server config
├── agents/                  # optional: custom agents (one .md file per agent)
│   └── code-reviewer.md
└── skills/                  # optional: additional skills
    └── lint-fix/
        └── SKILL.md
```

清单也可能位于 `.github/plugin.json` 或 `.github/plugin/plugin.json`，这样插件就可以放在现有代码库中，而无需更改其根目录结构。 每个子系统（挂钩、MCP、LSP、技能、代理）都有自己的加载程序，并且是可选的 — 插件只需要它贡献的部分。

有关完整的清单架构，请参阅你的 CLI 的 `/plugin` 斜杠命令所引用的运行时文档。

## 从 SDK 加载插件目录

当 SDK 生成插件目录时，将 `--plugin-dir <path>` 传递给 Copilot CLI 来加载插件目录。 每种语言都通过运行时连接的 extra-args 选项公开此内容。 可以重复此标志来加载多个插件。

<div class="ghd-codetabs">
<div class="ghd-codetab" data-lang="typescript" data-label="TypeScript"><div class="ghd-codetab-fallback-label" role="heading" aria-level="3">TypeScript</div>

```typescript
import { CopilotClient, RuntimeConnection } from "@github/copilot-sdk";

const client = new CopilotClient({
  connection: RuntimeConnection.forStdio({
    args: [
      "--plugin-dir", "./plugins/code-reviewer",
      "--plugin-dir", "./plugins/lint-fix",
    ],
  }),
});

await client.start();
```

</div>

<div class="ghd-codetab" data-lang="python" data-label="Python"><div class="ghd-codetab-fallback-label" role="heading" aria-level="3">Python</div>

<!-- docs-validate: wrap-async -->

```python
from copilot import CopilotClient, StdioRuntimeConnection

client = CopilotClient(
    connection=StdioRuntimeConnection(
        args=(
            "--plugin-dir", "./plugins/code-reviewer",
            "--plugin-dir", "./plugins/lint-fix",
        ),
    ),
)
await client.start()
```

</div>

<div class="ghd-codetab" data-lang="go" data-label="Go"><div class="ghd-codetab-fallback-label" role="heading" aria-level="3">Go</div>

```golang
client := copilot.NewClient(&copilot.ClientOptions{
    Connection: copilot.StdioConnection{
        Args: []string{
            "--plugin-dir", "./plugins/code-reviewer",
            "--plugin-dir", "./plugins/lint-fix",
        },
    },
})
if err := client.Start(ctx); err != nil {
    return err
}
```

</div>

<div class="ghd-codetab" data-lang="dotnet" data-label=".NET"><div class="ghd-codetab-fallback-label" role="heading" aria-level="3">.NET</div>

```csharp
using GitHub.Copilot;

await using var client = new CopilotClient(new CopilotClientOptions
{
    Connection = RuntimeConnection.ForStdio(args: new[]
    {
        "--plugin-dir", "./plugins/code-reviewer",
        "--plugin-dir", "./plugins/lint-fix",
    }),
});

await client.StartAsync();
```

</div>

<div class="ghd-codetab" data-lang="java" data-label="Java"><div class="ghd-codetab-fallback-label" role="heading" aria-level="3">Java</div>

```java
var options = new CopilotClientOptions()
    .setCliArgs(new String[] {
        "--plugin-dir", "./plugins/code-reviewer",
        "--plugin-dir", "./plugins/lint-fix",
    });

var client = new CopilotClient(options);
client.start().get();
```

</div>

<div class="ghd-codetab" data-lang="rust" data-label="Rust"><div class="ghd-codetab-fallback-label" role="heading" aria-level="3">Rust</div>

```rust
use github_copilot_sdk::{Client, ClientOptions};

let client = Client::start(
    ClientOptions::new().with_extra_args([
        "--plugin-dir", "./plugins/code-reviewer",
        "--plugin-dir", "./plugins/lint-fix",
    ]),
)
.await?;
```

</div>

</div>

> 上面的示例使用 stdio 运行时连接 - SDK 捆绑 CLI 时的默认值。 如果通过 URL（`forUri` / `ForUri`）连接到外部运行时，请在启动长时间运行的 CLI 服务器时将 `--plugin-dir` 传给它；SDK 不会将 `--plugin-dir` 转发给它未启动的运行时。

## 受信任的宿主内置插件目录

寄送其自己的受信任插件的应用程序可以将它们注册为客户端启动选项。 SDK 在连接并验证协议后，会在 `start` 返回之前以及创建任何会话之前发送完整的有序集。 路径必须是绝对路径；如果选项未设置或为空，则不会发起 RPC 调用。

每个 SDK 中的等效选项为：

| SDK                | 启动选项                                                  |
| ------------------ | ----------------------------------------------------- |
| Node.js/TypeScript | `builtinPluginDirectories: string[]`                  |
| Python             | `builtin_plugin_directories=[...]`                    |
| Go                 | `BuiltinPluginDirectories: []string{...}`             |
| .NET               | `BuiltinPluginDirectories = [...]`                    |
| Java               | `.setBuiltinPluginDirectories(List.of(Path.of(...)))` |
| Rust               | `.with_builtin_plugin_directories([...])`             |

这是由主机应用程序捆绑和控制的插件的信任边界。 它不同于 `--plugin-dir`CLI 进程启动参数，用于显式加载普通插件目录。 启动选项在连接到现有运行时时也有效，因为它通过 JSON-RPC 而不是作为进程参数转发。

## 插件可提供的功能

加载插件目录会使扩展对客户端创建的每个会话可见。 运行时会将插件提供的扩展与你以内联方式注册的内容合并：

| 插件贡献                                | 对会话可见为                               |
| ----------------------------------- | ------------------------------------ |
| 技能（`SKILL.md`， `skills/*/SKILL.md`） |                                      |
| `session.skills.list()` 中的项；可按名称注入  |                                      |
| 自定义代理 （`agents/*.md`）               | 可通过 `task(agent_type=...)` 工具调度      |
| 钩子（`hooks.json`）                    | 与通过 SDK 注册的钩子一起触发                    |
| MCP 服务器 （`.mcp.json`）               | 可通过 `session.mcp.*` 访问的工具和资源         |
| LSP 服务器 （`.lsp.json`）               | 通过 `session.lsp.initialize(...)` 初始化 |

插件代理是 [机队模式](/zh/copilot/how-tos/copilot-sdk/features/fleet-mode) 中的一级子代理：父代理可以通过 `agent_type` 分派它们，运行时会像对待其他任何子代理一样为它们触发 `subagentStart` / `subagentStop` 钩子。

## Plugin-dir 与应用市场插件

运行时有两种方法来安装插件，两种方法最终都与会话相同：

* ```
            **市场/直接仓库插件**通过 CLI 的 `/plugin` 斜杠命令或基础的 `installedPlugins` 用户设置持久安装。 它们是*全局性的*——针对同一用户配置运行的每个会话都能看到它们，并且它们会参与插件发现规则。
  ```
* **`--plugin-dir` 插件** 是 *显式的临时* 插件 ， 它们仅适用于使用该标志启动的 CLI 进程。 它们优先于环境发现，并根据具有相同缓存路径的市场条目进行去重，因此当两个表面都引用它时，同一个插件不会加载两次。

对于 SDK 驱动的应用程序， `--plugin-dir` 通常是正确的选择：它使插件设置在应用程序的控制之下，而不是取决于每台计算机的用户状态。

## 使插件集具有确定性

当主机可能安装了其他插件（市场或个人插件）时，在运行时环境中设置 `COPILOT_PLUGIN_DIR_ONLY=true` 以禁止自动插件发现。 只有通过 `--plugin-dir` 传入的目录才会被加载。

<details open>
<summary>
<strong>Node.js/TypeScript</strong></summary>

```typescript
process.env.COPILOT_PLUGIN_DIR_ONLY = "true";

const client = new CopilotClient({
  connection: RuntimeConnection.forStdio({
    args: ["--plugin-dir", "./plugins/code-reviewer"],
  }),
});
await client.start();
```

</details>

在 CI 中，在无外设服务器部署中，以及想要一个不依赖于主机用户配置的可重现插件集的任何位置使用此插件。

## 检查已加载了哪些插件

创建会话后，列出活动插件以确认目录已正确拾取：

<details open>
<summary>
<strong>Node.js/TypeScript</strong></summary>

```typescript
const plugins = await session.rpc.plugins.list();
for (const plugin of plugins.plugins) {
  console.log(`${plugin.name} (${plugin.enabled ? "enabled" : "disabled"})`);
}
```

</details>

通过 `--plugin-dir` 加载的插件会出现在此列表中，其缓存路径会被设置为你提供的目录。 市场安装标记有其注册表源。

## 故障排除

* **“在 dir< 中>找不到 plugin.json 或 SKILL.md”** — 目录存在，但不符合插件的条件。 在根目录（或在 `plugin.json` 下）添加一个 `.github/` 清单文件，或包含一个顶层 `SKILL.md`。
* **插件已加载，但代理/技能不可见** — 确保插件清单声明其贡献的代理/技能，或使用隐式布局（`agents/*.md`， `skills/*/SKILL.md`）。 然后调用 `session.rpc.skills.reload()`，无需重启即可使更改生效。
* ```
            **重复的钩子触发** — 运行时通过 `cache_path` 去重，但仅当同一目录同时作为市场安装和 `--plugin-dir` 被引用时。 如果两个不同的目录包含相同的插件，则两者都将加载。 删除一个或使用 `COPILOT_PLUGIN_DIR_ONLY=true`。
  ```
* **`--plugin-dir` 连接到外部运行时时被忽略** — SDK 仅在生成 CLI 本身时转发额外的参数。 对于外部运行时（`forUri`/`ForUri`），请传递 `--plugin-dir` 启动运行时服务器的命令行。

## Related

* ```
            [AUTOTITLE](/copilot/how-tos/copilot-sdk/features/custom-agents)：编写在插件 `agents/` 文件夹中发布的智能体。
  ```
* [自定义技能](/zh/copilot/how-tos/copilot-sdk/features/skills)：如何加载`SKILL.md`文件，以及技能层级排序规则。
* [使用挂钩](/zh/copilot/how-tos/copilot-sdk/features/hooks)：插件定义的钩子会与 SDK 注册的钩子同时触发。
* [将 MCP 服务器与 GitHub Copilot SDK 配合使用](/zh/copilot/how-tos/copilot-sdk/features/mcp)：插件提供的 MCP 服务器与内联注册的集成方式相同。
* [机队模式](/zh/copilot/how-tos/copilot-sdk/features/fleet-mode)：插件提供的代理可作为子代理进行调度。