Observação
CLI do GitHub Copilot no momento, as extensões são um recurso experimental e estão sujeitas a alterações.
Uma extensão permite que você adicione seus próprios recursos a CLI do Copilot. Cada extensão é um pequeno módulo Node.js que é executado como um processo separado junto com sua sessão interativa e se conecta novamente a ela. Por meio dessa conexão, uma extensão pode adicionar ferramentas que Copilot pode chamar enquanto trabalha em seu nome, e comandos de barra que você mesmo executa.
Neste tutorial, você criará duas extensões simples como exemplos do que você pode fazer:
- Uma ferramenta, chamada
tool-time, que Copilot pode chamar para relatar quanto tempo suas chamadas de ferramenta levaram até agora em uma sessão. - Um comando slash, chamado
/tokencount, que informa quantos tokens você usou desde o início da contagem.
Ambos os exemplos dependem apenas do SDK que é agrupado com o CLI do Copilot, portanto, não há nada extra para instalar. Para obter informações sobre como as extensões funcionam, consulte About extensions for CLI do GitHub Copilot.
Aviso
As extensões são executadas em seu computador com seus privilégios. Carregue apenas o código de uma extensão em que você confia, assim como só executaria qualquer outro script que você mesmo não escreveu.
Prerequisites
- CLI do GitHub Copilot: Você precisa ter CLI do Copilot instalado e configurado. Consulte Introdução à CLI do GitHub Copilot.
- Recursos experimentais habilitados: as extensões são atualmente um recurso experimental. As etapas neste tutorial ativam os recursos experimentais sempre que você inicia a CLI, usando a opção
--experimentalde linha de comando. - JavaScript: As extensões são escritas em JavaScript, portanto, você precisará estar familiarizado com esse idioma para criar suas próprias extensões.
- Um repositório: o segundo exemplo adiciona uma extensão no nível do projeto, portanto, você precisará de uma cópia local de um repositório Git no qual adicionar a extensão.
Exemplo de extensão 1: uma ferramenta de "tempo de uso"
Este exemplo adiciona uma extensão de nível de usuário chamada tool-time. Isso adiciona uma nova ferramenta — chamada session_tool_time — que Copilot pode chamar para informar quanto tempo as chamadas de ferramentas levaram até agora nesta sessão e quantas chamadas isso abrange.
Para garantir que uma ferramenta seja usada pela CLI, a ferramenta deve fazer algo que a CLI não pode fazer por conta própria. Nesse caso, a ferramenta session_tool_time acompanha a duração das chamadas de ferramenta ao ouvir eventos da CLI sobre quando elas começam e terminam, e registra ela mesma esses tempos. Como a CLI não registra esses intervalos em qualquer lugar que Copilot possa ler, a única maneira de Copilot conhecê-los é chamar a ferramenta. A descrição da ferramenta explica isso ao modelo, o que o leva a usar a ferramenta quando você pergunta sobre os tempos das chamadas da ferramenta.
Etapa 1: Criar o arquivo de extensão
-
Crie o seguinte diretório e arquivo em seu diretório inicial:
~/.copilot/extensions/tool-time/extension.mjsComo isso está em
~/.copilot/extensions/, a extensão está disponível em todas as suas sessões da CLI, em todos os diretórios — e não apenas em um repositório. -
Adicione este código a
extension.mjs:JavaScript // Extension: tool-time // Adds a session_tool_time tool that reports how long Copilot's tool calls // have taken this session. Copilot is never told these timings and they // aren't written anywhere, so calling the tool is the only way to find // them out. import { joinSession } from "@github/copilot-sdk/extension"; // The tool's own name, so it can avoid timing its own calls. const TOOL_NAME = "session_tool_time"; // Module-level state persists for the whole session, because the extension // runs as a single long-lived process. const startTimes = new Map(); // tool call id -> Date.now() when call started let totalMs = 0; // total milliseconds spent across finished tool calls let callCount = 0; // number of finished tool calls measured const session = await joinSession({ tools: [ { name: TOOL_NAME, description: "Report how long your tool calls have taken in total so far " + "in THIS session, and how many calls that covers. You are " + "never told how long your tool calls take and the timings " + "aren't recorded anywhere you can read, so call this tool " + "whenever you are asked about it rather than estimating.", // Always keep this tool's description in the model's tool list, // even when tool search is active, so Copilot reliably sees it: defer: "never", // Force a permissions approval prompt once, for this extension, // rather than on every call of this tool: skipPermission: true, parameters: { type: "object", properties: {} }, handler: async () => { const seconds = (totalMs / 1000).toFixed(1); return `So far this session, Copilot's tool calls have taken ${seconds}s in total across ${callCount} call(s).`; }, }, ], }); // When a tool starts, record the time, keyed by the tool call id so the // matching completion can be found later. The tool's own calls are skipped. session.on("tool.execution_start", (event) => { const data = event.data ?? {}; if (data.toolName && data.toolName !== TOOL_NAME) { startTimes.set(data.toolCallId, Date.now()); } }); // When a tool finishes, add the elapsed time to the running total. session.on("tool.execution_complete", (event) => { const data = event.data ?? {}; const startedAt = startTimes.get(data.toolCallId); if (startedAt === undefined) { return; } startTimes.delete(data.toolCallId); totalMs += Date.now() - startedAt; callCount += 1; });// Extension: tool-time // Adds a session_tool_time tool that reports how long Copilot's tool calls // have taken this session. Copilot is never told these timings and they // aren't written anywhere, so calling the tool is the only way to find // them out. import { joinSession } from "@github/copilot-sdk/extension"; // The tool's own name, so it can avoid timing its own calls. const TOOL_NAME = "session_tool_time"; // Module-level state persists for the whole session, because the extension // runs as a single long-lived process. const startTimes = new Map(); // tool call id -> Date.now() when call started let totalMs = 0; // total milliseconds spent across finished tool calls let callCount = 0; // number of finished tool calls measured const session = await joinSession({ tools: [ { name: TOOL_NAME, description: "Report how long your tool calls have taken in total so far " + "in THIS session, and how many calls that covers. You are " + "never told how long your tool calls take and the timings " + "aren't recorded anywhere you can read, so call this tool " + "whenever you are asked about it rather than estimating.", // Always keep this tool's description in the model's tool list, // even when tool search is active, so Copilot reliably sees it: defer: "never", // Force a permissions approval prompt once, for this extension, // rather than on every call of this tool: skipPermission: true, parameters: { type: "object", properties: {} }, handler: async () => { const seconds = (totalMs / 1000).toFixed(1); return `So far this session, Copilot's tool calls have taken ${seconds}s in total across ${callCount} call(s).`; }, }, ], }); // When a tool starts, record the time, keyed by the tool call id so the // matching completion can be found later. The tool's own calls are skipped. session.on("tool.execution_start", (event) => { const data = event.data ?? {}; if (data.toolName && data.toolName !== TOOL_NAME) { startTimes.set(data.toolCallId, Date.now()); } }); // When a tool finishes, add the elapsed time to the running total. session.on("tool.execution_complete", (event) => { const data = event.data ?? {}; const startedAt = startTimes.get(data.toolCallId); if (startedAt === undefined) { return; } startTimes.delete(data.toolCallId); totalMs += Date.now() - startedAt; callCount += 1; });
Observação
*
@github/copilot-sdk/extension é o SDK de extensão, que é agrupado com a CLI. A CLI resolve essa importação automaticamente quando ela executa sua extensão, portanto, você não precisa adicioná-la a um package.json gerenciador de pacotes ou executá-la.
- Os valores
startTimes,totalMsecallCountestão no escopo do módulo. Como a extensão é executada como um único processo de longa duração para toda a sessão, elas se acumulam enquanto a sessão estiver aberta.
Etapa 2: Carregar a extensão
-
Inicie uma sessão interativa com recursos experimentais habilitados:
Shell copilot --experimental
copilot --experimentalComo a extensão fica em
~/.copilot/extensions/, você pode executar a CLI de qualquer diretório e a extensão estará disponível.Se você já tiver uma sessão aberta, execute
/clearpara iniciar uma nova sessão, que recarrega extensões do disco. -
Sem conceder permissões elevadas para a nova extensão, todas as extensões ou todas as ferramentas, você será solicitado a permitir que a nova extensão ignore os prompts de permissão da ferramenta. Escolha Sim ou Sim e sempre permita "user:tool-time" neste diretório.
Observação
Para obter as permissões mínimas elevadas para evitar ver essa mensagem ao iniciar a CLI, adicione-a ao comando de inicialização da CLI:
Shell --allow-tool='extension-permission-access(user:tool-time)'
--allow-tool='extension-permission-access(user:tool-time)'Para obter mais informações, consulte Permitir e negar o uso da ferramenta.
Etapa 3: confirmar se a extensão está em execução
Execute o /extensions manage comando para abrir o gerenciador de extensões. Sua extensão tool-time deve aparecer no grupo Usuário com status em execução. Pressione Esc para fechar o gerenciador.
Etapa 4: Experimente
-
Ao contrário de um comando com barra, você não invoca uma ferramenta por conta própria — Copilot a chama quando isso for útil. Primeiro, dê a Copilot uma tarefa que envolva algumas chamadas de ferramentas, por exemplo:
Copilot prompt Explore the files in the current directory and give me a short summary of what's here.
Explore the files in the current directory and give me a short summary of what's here. -
Quando Copilot terminar de responder, pergunte:
Copilot prompt How long have tool calls taken so far this session?
How long have tool calls taken so far this session?O agente chama a
session_tool_timeferramenta, da nova extensão, e a usa para responder à sua pergunta.Dica
Você pode confirmar que o agente usou a nova ferramenta examinando o início da Copilotresposta. A resposta deve ser prefixada pelo nome da ferramenta que foi usada; nesse caso,
session_tool_time.
Como a ferramenta funciona
A única chamada para joinSession é o que transforma um arquivo Node.js simples em uma extensão CLI do Copilot. Ele conecta o processo em execução à sessão e registra tudo o que a extensão adiciona à CLI, nesse caso, uma única ferramenta.
Uma ferramenta é definida por estes campos:
name— o identificador Copilot usa para chamar a ferramenta.description— o que a ferramenta faz. O modelo depende desse texto para decidir quando chamar a ferramenta, portanto, vale a pena ser explícito. Essa descrição informa ao modelo que ele não é informado sobre esses intervalos em si, o que o orienta a chamar a ferramenta em vez de tentar resolver os tempos de outra maneira.parameters— um esquema JSON que descreve os argumentos da ferramenta. Essa ferramenta não usa nenhuma, portanto, o esquema é um objeto sem propriedades.handler— uma função assíncrona que é executada quando Copilot chama a ferramenta. Qualquer cadeia de caracteres retornada torna-se o resultado da ferramenta, que o modelo lê.
Dois campos adicionais moldam como a ferramenta é oferecida ao modelo:
-
defer: "never"— mantém a descrição da ferramenta na lista de ferramentas do modelo o tempo todo. Por padrão, quando muitas ferramentas estão disponíveis, a CLI pode adiar ferramentas raramente usadas e permitir que o modelo as pesquise sob demanda. Definirdefercomo"never"exclui esta ferramenta desse comportamento, portanto, Copilot sempre o vê.Importante
defer: "never"simplesmente torna a ferramenta disponível para Copilot. Não _força_Copilot a chamá-lo. Não há como uma extensão exigir que uma ferramenta específica sempre seja usada se houver um meio alternativo de gerar uma resposta apropriada a um prompt. O modelo sempre decide por si mesmo qual ferramenta usar. Neste exemplo, a nova ferramenta é usada de forma confiável porque não há outra maneira de o modelo conhecer as informações fornecidas pela ferramenta. -
skipPermission: truepermite que a ferramenta seja executada sem solicitar que você aprove cada chamada. Isso é apropriado aqui porque a ferramenta lê somente totais que a extensão já coletou; ele não toca em seus arquivos nem executa comandos.
Os tempos são coletados observando a sessão. A extensão se inscreve em dois eventos que a CLI emite em torno de cada chamada de ferramenta:
session.on("tool.execution_start", (event) => { /* ... */ });
session.on("tool.execution_complete", (event) => { /* ... */ });
Um tool.execution_start evento carrega o nome da ferramenta (event.data.toolName). Um evento tool.execution_complete contém um sinalizador success. Nenhum evento carrega as informações do outro, portanto, a extensão as correlaciona usando a toolCallId que aparece em cada um: quando uma ferramenta é iniciada, ela registra a hora atual sob essa ID. Quando a conclusão correspondente chega, o tempo decorrido em milissegundos é adicionado ao total acumulado. A ferramenta desconsidera suas próprias chamadas para que a figura reflita o trabalho real de Copilot.
Como os totais residem no processo de extensão de longa duração, eles abrangem toda a sessão. Esse é o tipo de tarefa em que as extensões são boas: observar o que está acontecendo na sessão e manter o estado entre chamadas.
Observação
- Os totais são mantidos na memória, portanto, são redefinidos sempre que a extensão é recarregada ou a sessão é reiniciada (por exemplo, depois
/clear). - O valor mostrado é o tempo de relógio medido do ponto de vista da extensão — o intervalo entre os eventos de início e conclusão de cada invocação de ferramenta —, portanto inclui também qualquer tempo em que uma chamada ficou aguardando sua aprovação.
Exemplo de extensão 2: um comando slash para uso de tokens
Este exemplo adiciona uma extensão de projeto chamada token-counter. A extensão adiciona um comando slash /tokencount que permite verificar quantos tokens você usou ao interagir com Copilot na CLI. Você executa /tokencount start quando deseja começar a medir o uso do token e, em seguida, pode executar /tokencount em qualquer ponto posterior para verificar quantos tokens você usou desde que iniciou a contagem.
A extensão mantém um total acumulado de tokens usados na sessão ao se inscrever em eventos emitidos pela CLI, algo que um comando único de shell não pode fazer.
Etapa 1: Criar o arquivo de extensão
-
Na raiz do repositório Git para seu projeto, crie os seguintes diretórios e arquivo:
.github/extensions/token-counter/extension.mjs -
Adicione este código a
extension.mjs:JavaScript // Extension: token-counter // Adds a /tokencount slash command that reports how many tokens you've used // since you started counting. import { joinSession } from "@github/copilot-sdk/extension"; // Module-level state. The extension runs as a single long-lived process for // the whole session, so these values persist between command invocations. let tokensUsed = 0; // Running total of tokens used so far this session. let startedAt = null; // tokensUsed when "/tokencount start" was last run. // null = not started. // startedAt allows you to count tokens multiple times in a session. const session = await joinSession({ commands: [ { name: "tokencount", description: "Report how many tokens you've used since " + "'/tokencount start'.", handler: async (ctx) => { const arg = (ctx.args ?? "").trim(); if (arg === "start") { // Reset: remember the current total as the new baseline. startedAt = tokensUsed; await session.log( "Token counter started. Run '/tokencount' later to " + "see how many tokens you've used.", { level: "info" }, ); return; } if (startedAt === null) { await session.log( "The token counter has not been started. Start by " + "entering '/tokencount start'.", { level: "info" }, ); return; } const used = tokensUsed - startedAt; await session.log( `You have used ${used} tokens since entering ` + "'/tokencount start'.", { level: "info" }, ); }, }, ], }); // The CLI emits an "assistant.usage" event after each assistant turn. Add the // tokens it reports to a running total kept in the extension's memory. session.on("assistant.usage", (event) => { const { inputTokens = 0, outputTokens = 0 } = event.data ?? {}; tokensUsed += inputTokens + outputTokens; });// Extension: token-counter // Adds a /tokencount slash command that reports how many tokens you've used // since you started counting. import { joinSession } from "@github/copilot-sdk/extension"; // Module-level state. The extension runs as a single long-lived process for // the whole session, so these values persist between command invocations. let tokensUsed = 0; // Running total of tokens used so far this session. let startedAt = null; // tokensUsed when "/tokencount start" was last run. // null = not started. // startedAt allows you to count tokens multiple times in a session. const session = await joinSession({ commands: [ { name: "tokencount", description: "Report how many tokens you've used since " + "'/tokencount start'.", handler: async (ctx) => { const arg = (ctx.args ?? "").trim(); if (arg === "start") { // Reset: remember the current total as the new baseline. startedAt = tokensUsed; await session.log( "Token counter started. Run '/tokencount' later to " + "see how many tokens you've used.", { level: "info" }, ); return; } if (startedAt === null) { await session.log( "The token counter has not been started. Start by " + "entering '/tokencount start'.", { level: "info" }, ); return; } const used = tokensUsed - startedAt; await session.log( `You have used ${used} tokens since entering ` + "'/tokencount start'.", { level: "info" }, ); }, }, ], }); // The CLI emits an "assistant.usage" event after each assistant turn. Add the // tokens it reports to a running total kept in the extension's memory. session.on("assistant.usage", (event) => { const { inputTokens = 0, outputTokens = 0 } = event.data ?? {}; tokensUsed += inputTokens + outputTokens; });
Etapa 2: Carregar a extensão
Inicie uma sessão interativa no mesmo repositório, com recursos experimentais habilitados:
copilot --experimental
copilot --experimental
Se você já tiver uma sessão aberta, poderá executar /clear para iniciar uma nova sessão, que recarrega extensões do disco.
Etapa 3: confirmar se a extensão está em execução
Execute o /extensions manage comando para abrir o gerenciador de extensões. A extensão token-counter deve aparecer no grupo Project com o status em execução. Pressione Esc para fechar o gerenciador.
Você também pode executar /env para ver um resumo de tudo o que foi carregado na sessão, incluindo extensões.
Etapa 4: Experimente
Diferentemente de uma ferramenta, você mesmo executa um comando slash. Inicie o contador:
/tokencount start
/tokencount start
Envie um ou dois prompts para Copilot para que use alguns tokens — por exemplo, peça que explique um arquivo. Em seguida, verifique quantos tokens você usou:
/tokencount
/tokencount
Se você executar /tokencount start novamente, a contagem será reiniciada de zero.
Como o exemplo funciona
A chamada para joinSession registra tudo o que a extensão adiciona à CLI — neste caso, um único comando slash.
Um comando com barra é definido por três campos:
name— o nome do comando, sem a barra inicial. Registrartokencounté o que torna/tokencountdisponível na sessão.description—o texto exibido ao lado do comando no seletor de comandos com barra.handler— uma função assíncrona que é executada quando você invoca o comando. Ele recebe um objeto de contexto cujaargspropriedade contém o texto bruto digitado após o nome do comando. Para/tokencount start,ctx.argsé"start"; para um/tokencountsimples, é uma string vazia.
O manipulador grava sua saída de volta para a sessão com session.log(message, { level: "info" }), que imprime a mensagem na transcrição.
Para saber quantos tokens foram usados, a extensão se inscreve em um evento de sessão:
session.on("assistant.usage", (event) => {
const { inputTokens = 0, outputTokens = 0 } = event.data ?? {};
tokensUsed += inputTokens + outputTokens;
});
CLI emite um evento assistant.usage após cada turno do assistente, incluindo as contagens de tokens de entrada e de saída desse turno. A extensão os adiciona ao total em execução em tokensUsed. Quando você executa /tokencount start, o manipulador registra o total atual em startedAt. Inserir /tokencount sem argumentos mais tarde na sessão informa a diferença. Como ambas as variáveis vivem no processo de extensão de longa duração, elas persistem durante toda a sessão.
Observação
Os totais são mantidos na memória, portanto, são redefinidos sempre que a extensão é recarregada ou a sessão é reiniciada (por exemplo, depois /clear).
Editando e recarregando uma extensão
Ao desenvolver uma extensão, você vai editar extension.mjs e vai querer ver suas alterações. Depois de salvar o arquivo, você pode pegar a nova versão de qualquer uma destas maneiras:
- Peça Copilot para recarregar as extensões, por exemplo:
Reload my extensions. - Execute
/clearpara iniciar uma nova sessão, que recarrega extensões do disco. - Reinicie a CLI.
Se uma extensão falhar ao iniciar ou se comportar inesperadamente, execute /extensions manage e inspecione a extensão para ver seu status e o caminho para o arquivo de log. Cada extensão grava um log em ~/.copilot/logs/extensions/, que é o melhor lugar para procurar quando algo dá errado.
Próximas Etapas
- Adapte um desses exemplos às suas próprias necessidades. Na CLI, peça Copilot para modificar o comportamento de qualquer uma das extensões de exemplo.
- Compartilhe uma extensão no nível do usuário. Por exemplo, mova a
tool-timeextensão para o diretório de.github/extensions/um repositório para compartilhá-la com todos que trabalham nesse repositório. - Peça Copilot para criar uma nova extensão do zero para você. CLI do Copilot as extensões são alimentadas pelo SDK do Copilot, portanto, uma extensão pode fazer qualquer coisa que o SDK possibilita, permitindo que você adicione exibições interativas com botões e formulários, bem como novas ferramentas e comandos. Consulte SDK do Copilot.