# Каталоги плагинов

Плагин — это каталог, который объединяет расширения SDK — навыки, хуки, MCP-серверы, пользовательские агенты и конфигурацию LSP — за одним манифеста. Указание SDK на каталог плагинов загружает всё, что добавляет плагин, так что вы можете отправлять повторно используемые пакеты возможностей без записи проводки по разным расширениям в каждом хост-приложении.

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

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

В этом руководстве объясняется структура папок плагинов, как загружать плагин из каталога, когда использовать каталоги плагинов и когда регистрировать отдельные расширения, а также как сделать наборы плагинов детерминированными.

## Когда использовать каталоги плагинов

Используйте папку плагинов, когда хотите:

* **Распределите набор возможностей** в одном едином блоке — например, пакет «TypeScript reviewer» с навыком, `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, навыки, агенты) имеет свой загрузчик и является опциональной — плагину нужны только те части, которые он вносит.

Полную схему манифеста смотрите документацию по выполнению, на которую ссылка из команды слэш вашего `/plugin` CLI.

## Загрузка каталога плагинов из SDK

Каталоги плагинов загружаются путём передачи `--plugin-dir <path>` Copilot CLI, когда SDK его создаёт. Каждый язык раскрывает это через опцию extra args в runtime connection. Флаг можно повторить для загрузки нескольких плагинов.

<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";

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

  await client.start();
}

main();
```

```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
package main

import (
    "context"

    copilot "github.com/github/copilot-sdk/go"
)

func main() {
    ctx := context.Background()
    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
    }
}
```

```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
import com.github.copilot.CopilotClient;
import com.github.copilot.rpc.CopilotClientOptions;

public class PluginDirectoriesExample {
    public static void main(String[] args) throws Exception {
        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();
    }
}
```

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

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let _client = Client::start(
        ClientOptions::new().with_extra_args([
            "--plugin-dir", "./plugins/code-reviewer",
            "--plugin-dir", "./plugins/lint-fix",
        ]),
    )
    .await?;
    Ok(())
}
```

```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 runtime соединение — стандартное, когда SDK объединяет CLI. Если вы подключаетесь к внешнему серверу выполнения через URL (`forUri` / `ForUri`), передайте данные `--plugin-dir` на долгоработающий CLI-сервер при запуске; SDK не пересылает `--plugin-dir` время выполнения, которые он не создал.

## Что может внести плагин

Загрузка каталога плагинов делает его расширения видимыми для каждой сессии, созданной клиентом. Runtime объединяет расширения, предоставленные плагинами, с тем, что вы регистрируете в строке:

| Плагин вносит вклад                      | Видимо для сессии как                                          |
| ---------------------------------------- | -------------------------------------------------------------- |
| Навыки (`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(...)`          |

Агенты плагинов — это первоклассные субагенты в [Режим флота](/ru/copilot/how-tos/copilot-sdk/features/fleet-mode): родительский агент может отправить их через `agent_type`, и runtime запускает `subagentStart` / `subagentStop` крючки для них, как и любой другой подагент.

## Plugin-dir против плагинов marketplace

В среде выполнения есть два способа установки плагинов, и оба в итоге выглядят одинаково для сессии:

* **Плагины Marketplace / прямой репозиторий** постоянно устанавливаются через команду слэш 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>

<!-- docs-validate: hidden -->

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

async function main() {
  process.env.COPILOT_PLUGIN_DIR_ONLY = "true";
  const client = new CopilotClient({
    connection: RuntimeConnection.forStdio({
      args: ["--plugin-dir", "./plugins/code-reviewer"],
    }),
  });
  await client.start();
}

main();
```

<!-- /docs-validate: hidden -->

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

<!-- docs-validate: hidden -->

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

async function main() {
  const client = new CopilotClient();
  await client.start();
  const session = await client.createSession({
    onPermissionRequest: async () => ({ kind: "approve-once" }),
  });

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

main();
```

<!-- /docs-validate: hidden -->

```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` BY, появляются в этом списке с их кэш-путём, установленным в указанной вами директории. Установки маркетплейса отмечены исходным кодом реестра.

## Troubleshooting

* **"нет plugin.json или SKILL.md в \<DIR>"** — каталог существует, но не считается плагином. Добавьте `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` игнорируется при подключении к внешнему runtime** — SDK пересылает дополнительные arg-файлы только при создании самого CLI. Для внешних условий выполнения (`forUri`/`ForUri`), передайте `--plugin-dir` командную строку, которая запускает сервер выполнения.

## Related

* [Пользовательские агенты и оркестровка субагентов](/ru/copilot/how-tos/copilot-sdk/features/custom-agents): пишите агенты, которые отправляются внутри папки `agents/` плагина.
* [Настраиваемые навыки](/ru/copilot/how-tos/copilot-sdk/features/skills): как `SKILL.md` загружаются файлы и правила порядка уровней навыков.
* [Работа с крючками](/ru/copilot/how-tos/copilot-sdk/features/hooks): хуки, определяемые плагинами, вместе с хуками, зарегистрированными в SDK.
* [Использование MCP-серверов с SDK GitHub Copilot](/ru/copilot/how-tos/copilot-sdk/features/mcp): MCP-серверы, предоставляемые плагинами, интегрируются так же, как и встроенные регистрации.
* [Режим флота](/ru/copilot/how-tos/copilot-sdk/features/fleet-mode): агенты, предоставленные плагинами, могут диспетчеризироваться как субагенты.