Skip to main content

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

Используйте каталоги плагинов для загрузки навыков, хуков, MCP-серверов, пользовательских агентов и настроек LSP из одного манифеста.

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

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

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

  • Распределите набор возможностей в одном едином блоке — например, пакет «TypeScript reviewer» с навыком, preToolUse крючок, обеспечивающий линт, и пользовательский агент, управляющий рецензентом.
  • Возможности поставщика упаковываются в репозиторий , так что каждый клон хост-приложения загружает одни и те же расширения детерминированно.
  • Разработайте плагин локально перед тем, как публиковать его на маркетплейсе.
  • Переопределите или расширите плагин, установленный на маркетплейсе, с помощью локальной проверки для тестирования.

Если вам нужно добавить только один MCP-сервер, один крюк или один пользовательский агент, вы можете зарегистрировать его в строке через конфигурацию SDK (mcpServers, hooks, customAgents). Папки плагинов наиболее полезны, когда у вас есть три или более связанных расширений, которые поставляются вместе.

Оформление папки плагина

Copilot CLI сканирует каждую папку плагина в поисках манифеста plugin.json или корневого уровня SKILL.md. Минимальный плагин выглядит так:

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. Флаг можно повторить для загрузки нескольких плагинов.

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();
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();
Python
from copilot import CopilotClient, StdioRuntimeConnection

client = CopilotClient(
    connection=StdioRuntimeConnection(
        args=(
            "--plugin-dir", "./plugins/code-reviewer",
            "--plugin-dir", "./plugins/lint-fix",
        ),
    ),
)
await client.start()
Go
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
    }
}
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
}
.NET
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();
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();
    }
}
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();
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(())
}
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?;

Приведённый выше пример использует 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(...)

Агенты плагинов — это первоклассные субагенты в Режим флота: родительский агент может отправить их через 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 которые вы проходите.

Node.js / 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();
process.env.COPILOT_PLUGIN_DIR_ONLY = "true";

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

Используйте это в CI, в развертённых серверах без головы и везде, где нужен воспроизводимый набор плагинов, не зависящий от конфигурации пользователя хоста.

Проверка загрузки плагинов

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

Node.js / 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();
const plugins = await session.rpc.plugins.list();
for (const plugin of plugins.plugins) {
  console.log(`${plugin.name} (${plugin.enabled ? "enabled" : "disabled"})`);
}

Плагины, загруженные через --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 командную строку, которая запускает сервер выполнения.