Este guia explica o layout da pasta do plug-in, como carregar um plug-in de um diretório, quando usar diretórios de plug-in versus registrar extensões individuais e como tornar os conjuntos de plug-in determinísticos.
Quando usar diretórios de plug-in
Use um diretório de plug-in quando quiser:
-
**Distribua um pacote de recursos** como uma unidade: por exemplo, um pacote "revisor TypeScript" com uma habilidade, um gancho `preToolUse` que impõe lint e um agente personalizado que executa o revisor. - A funcionalidade do fornecedor é empacotada em um repositório para que cada clone do aplicativo host carregue as mesmas extensões deterministicamente.
- Desenvolva um plug-in localmente antes de publicá-lo em um marketplace.
-
**Substitua ou estenda** um plugin instalado via marketplace com um checkout local para testes.
Se você precisar adicionar apenas um servidor MCP, um único gancho ou um único agente personalizado, você poderá registrá-lo embutido por meio da configuração do SDK (mcpServers, hooks, customAgents). Os diretórios de plug-in são mais úteis quando você tem três ou mais extensões relacionadas que são enviadas juntas.
Estrutura da pasta do plugin
A CLI do Copilot examina cada diretório de plugin em busca de um manifesto plugin.json ou de um arquivo SKILL.md na raiz. Um plug-in mínimo tem esta aparência:
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
O manifesto também pode ficar em .github/plugin.json ou .github/plugin/plugin.json, para que os plug-ins possam ficar dentro de um repositório existente sem alterar sua estrutura raiz. Cada subsistema (ganchos, MCP, LSP, habilidades, agentes) tem seu próprio carregador e é opcional – um plug-in só precisa das partes que ele contribui.
Para o esquema completo do manifesto, consulte a documentação do runtime referenciada pelo comando de barra /plugin da sua CLI.
Carregando um diretório de plug-in do SDK
Os diretórios de plug-in são carregados passando --plugin-dir <path> para a CLI do Copilot quando o SDK o gera. Cada linguagem expõe isso por meio da opção extra-args da conexão de tempo de execução. A flag pode ser repetida para carregar múltiplos plugins.
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();
from copilot import CopilotClient, StdioRuntimeConnection
client = CopilotClient(
connection=StdioRuntimeConnection(
args=(
"--plugin-dir", "./plugins/code-reviewer",
"--plugin-dir", "./plugins/lint-fix",
),
),
)
await client.start()
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
}
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();
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();
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?;
O exemplo acima usa uma conexão de runtime stdio – o padrão quando o SDK agrupa a CLI. Se você se conectar a um runtime externo via URL (
forUri/ForUri), passe--plugin-dirpara o servidor CLI de longa execução ao iniciá-lo; o SDK não repassa--plugin-dirpara runtimes que não foram iniciados por ele.
Diretórios de plugin por sessão
--plugin-dir é um argumento de inicialização, portanto fixa um conjunto de plugins para o processo da CLI e para cada sessão criada com base nele. Quando as sessões precisarem de conjuntos de plug-ins diferentes, ou quando o SDK estiver conectado a um runtime que ele não iniciou, passe os diretórios na configuração da sessão. Eles são transmitidos nos payloads session.create e session.resume via JSON-RPC, em vez de como argumentos de processo; assim, chegam a um runtime externo da mesma forma que a opção de inicialização.
import { CopilotClient } from "@github/copilot-sdk";
const client = new CopilotClient();
await client.start();
const session = await client.createSession({
pluginDirectories: ["./plugins/code-reviewer"],
});
Caminhos relativos são resolvidos com base em workingDirectory, ou no diretório de trabalho do runtime quando ele não estiver definido, por isso caminhos absolutos são recomendados. As entradas que não podem ser resolvidas são registradas em log e puladas, em vez de fazer com que a criação da sessão falhe. A opção é de adesão explícita (opt-in), o que significa que os agentes e as regras do plugin são carregados mesmo quando enableConfigDiscovery é falso. Os ativos carregados dessa forma situam-se entre as fontes do projeto e as fontes pessoais ou locais na ordem de precedência válida para toda a sessão.
A opção equivalente em cada SDK é:
| SDK | Opção de sessão |
|---|---|
| Node.js/TypeScript | plugin |
| Python | plugin_directories=[...] |
| Go | Plugin |
| .NET | PluginDirectories = [...] |
| Java | .set |
| Rust | .with_plugin_directories([...]) |
Diretórios de plug-in em pacote de host confiáveis
Os aplicativos que enviam seus próprios plug-ins confiáveis podem registrá-los como uma opção de inicialização do cliente. O SDK envia o conjunto ordenado completo após se conectar e verificar o protocolo, antes que start retorne ou que qualquer sessão possa ser criada. Os caminhos devem ser absolutos; deixar a opção não definida ou vazia não realiza nenhuma chamada RPC.
A opção equivalente em cada SDK é:
| SDK | Opção de inicialização |
|---|---|
| Node.js/TypeScript | builtin |
| Python | builtin_plugin_ |
| Go | Builtin |
| .NET | Builtin |
| Java | .set |
| Rust | .with_builtin_ |
Esse é um limite de confiança para plug-ins agrupados e controlados pelo aplicativo host. É diferente de --plugin-dir, que é um argumento de inicialização de processo da CLI para carregar explicitamente diretórios comuns de plug-ins. A opção de inicialização também funciona ao se conectar a um runtime existente porque é enviada por JSON-RPC em vez de encaminhada como um argumento de processo.
O que um plug-in pode contribuir
Carregar um diretório de plug-in torna suas extensões visíveis para cada sessão criada pelo cliente. O tempo de execução mescla as extensões fornecidas por plugins com qualquer coisa que você registre em linha:
| O plugin contribui | Visível para a sessão como |
|---|---|
Habilidades (SKILL.md, skills/*/SKILL.md) | Itens no session.skills.list(); passíveis de injeção por nome |
Agentes personalizados (agents/*.md) | Pode ser enviado pela ferramenta task(agent_type=...) |
Hooks (hooks.json) | Disparado juntamente com os hooks registrados via SDK |
Servidores MCP (.mcp.json) | Ferramentas e recursos acessíveis por meio de session.mcp.* |
Servidores LSP (.lsp.json) | Inicializado por meio de session.lsp.initialize(...) |
Agentes de plugin são subagentes de primeira classe no Modo de frota: um agente pai pode despachá-los por meio de agent_type, e o ambiente de execução dispara os hooks subagentStart / subagentStop para eles, assim como para qualquer outro subagente.
Diretório de plugins vs. plugins do marketplace
O ambiente de execução tem duas maneiras de instalar plug-ins, e ambas acabam parecendo iguais para uma sessão:
-
**Plugins do Marketplace ou de repositórios diretos** são instalados de forma persistente por meio do comando de barra `/plugin` da CLI ou da configuração de usuário `installedPlugins` subjacente. Eles são *ambiente*: todas as sessões executadas com a mesma configuração de usuário os reconhecem, e eles participam das regras de descoberta de plugins. --plugin-diros plug-ins são explícitos e efêmeros – eles se aplicam apenas ao processo da CLI que você iniciou com esse sinalizador. Eles têm precedência sobre a descoberta de ambiente e passam por um processo de desduplicação em relação a entradas do marketplace com o mesmo caminho de cache; assim, o mesmo plugin não é carregado duas vezes quando ambas as superfícies fazem referência a ele.
Para aplicativos baseados em SDK, --plugin-dir geralmente é a escolha certa: mantém o conjunto de plug-ins sob o controle da sua aplicação, em vez de depender do estado do usuário em cada máquina.
Tornando os conjuntos de plug-in determinísticos
Quando a máquina host puder ter outros plug-ins instalados (do marketplace ou personalizados), defina COPILOT_PLUGIN_DIR_ONLY=true no ambiente de execução para suprimir a descoberta automática de plug-ins. Somente os diretórios que você informar por meio de --plugin-dir serão carregados.
Node.js/TypeScript
process.env.COPILOT_PLUGIN_DIR_ONLY = "true";
const client = new CopilotClient({
connection: RuntimeConnection.forStdio({
args: ["--plugin-dir", "./plugins/code-reviewer"],
}),
});
await client.start();
Use isso em CI, em implantações de servidor sem cabeça e em qualquer lugar que você queira um conjunto de plug-in reproduzível que não dependa da configuração do usuário do host.
Inspecionando quais plug-ins foram carregados
Depois que uma sessão for criada, liste os plug-ins ativos para confirmar se um diretório foi selecionado corretamente:
Node.js/TypeScript
const plugins = await session.rpc.plugins.list();
for (const plugin of plugins.plugins) {
console.log(`${plugin.name} (${plugin.enabled ? "enabled" : "disabled"})`);
}
Plugins carregados via --plugin-dir aparecem nesta lista com o caminho do cache definido como o diretório que você forneceu. As instalações do Marketplace são marcadas com seu registro de origem.
Troubleshooting
- "nenhum plugin.json ou SKILL.md encontrado no <dir>" – o diretório existe, mas não se qualifica como um plug-in. Adicione um manifesto
plugin.jsonna raiz (ou sob.github/), ou inclua umSKILL.mdde nível superior. - Plugin carregado, mas agentes/habilidades não estão visíveis — verifique se o manifesto do plugin declara os agentes/habilidades que ele fornece ou use o layout implícito (
agents/*.md,skills/*/SKILL.md). Em seguida, chamesession.rpc.skills.reload()para pegar as alterações sem reiniciar. -
**Disparo de hooks duplicados**: o runtime realiza a desduplicação com base em `cache_path`, mas apenas quando o mesmo diretório é referenciado tanto como uma instalação do marketplace quanto como um `--plugin-dir`. Se dois diretórios diferentes contiverem o mesmo plug-in, ambos serão carregados. Remova um ou use `COPILOT_PLUGIN_DIR_ONLY=true`. --plugin-dirignorado ao se conectar a um runtime externo – o SDK só encaminha args extras quando gera a própria CLI. Para runtimes externos (forUri/ForUri), passe--plugin-dirna linha de comando que inicia o servidor de runtime.
Related
-
[AUTOTITLE](/copilot/how-tos/copilot-sdk/features/custom-agents): escreva agentes que são distribuídos dentro da pasta `agents/` de um plugin. - Habilidades personalizadas: como
SKILL.mdarquivos são carregados e as regras de ordenação por nível de habilidade. - Trabalhando com ganchos: hooks definidos por um plugin são acionados junto com hooks registrados no SDK.
- Usando servidores MCP com o SDK do GitHub Copilot: os servidores MCP fornecidos pelo plug-in integram-se da mesma maneira que os registros embutidos.
- Modo de frota: agentes fornecidos por plug-in podem ser despachados como subagentes.