Skip to main content

Creación de extensiones para CLI de GitHub Copilot

Compile extensiones que agreguen sus propias herramientas y comandos de barra diagonal a CLI de Copilot.

Nota:

CLI de GitHub Copilot las extensiones son actualmente una característica experimental y están sujetas a cambios.

Una extensión le permite agregar sus propias funcionalidades a CLI de Copilot. Cada extensión es un módulo de Node.js pequeño que se ejecuta como un proceso independiente junto con la sesión interactiva y se conecta de nuevo a ella. A través de esa conexión, una extensión puede añadir herramientas que Copilot puede invocar mientras trabaja en su nombre, y comandos con barra que ejecuta usted mismo.

En este tutorial, creará dos extensiones simples como ejemplos de lo que puede hacer:

  • Una herramienta, denominada tool-time, a la que Copilot puede llamar para informar del tiempo que han tardado hasta el momento sus invocaciones de herramientas en una sesión.
  • Un comando con barra, llamado /tokencount, que indica cuántos tokens has usado desde que empezaste a contar.

Ambos ejemplos solo se basan en el SDK que se incluye con CLI de Copilot, por lo que no hay nada adicional que instalar. Para obtener información sobre cómo funcionan las extensiones, consulte About extensions for CLI de GitHub Copilot.

Advertencia

Las extensiones se ejecutan en tu equipo con tus privilegios. Cargue solo el código de extensión que confíe, de la misma manera que solo ejecutaría cualquier otro script que no haya escrito usted mismo.

Prerequisites

  • CLI de GitHub Copilot: Necesita tener CLI de Copilot instalado y configurado. Consulte Introducción a la CLI de GitHub Copilot.
  • Características experimentales habilitadas: las extensiones son actualmente una característica experimental. Los pasos de este tutorial activan las características experimentales cada vez que inicia la CLI mediante la --experimental opción de línea de comandos.
  • JavaScript: las extensiones se escriben en JavaScript, por lo que deberá estar familiarizado con este lenguaje para crear sus propias extensiones.
  • Un repositorio: en el segundo ejemplo se agrega una extensión de nivel de proyecto, por lo que necesitará una copia local de un repositorio de Git en el que agregar la extensión.

Extensión de ejemplo 1: una herramienta de tiempo de uso

En este ejemplo se agrega una extensión de nivel de usuario denominada tool-time. Agrega una nueva herramienta, denominada session_tool_time, que Copilot puede invocar para informar de cuánto tiempo han tardado hasta ahora las llamadas a herramientas en esta sesión y cuántas llamadas comprende.

Para asegurarse de que la CLI usa una herramienta, la herramienta debe hacer algo que la CLI no puede hacer por sí sola. En este caso, la session_tool_time herramienta realiza un seguimiento del tiempo que tardan las llamadas a herramientas escuchando eventos de la CLI sobre cuándo se inician y finalizan las llamadas de herramienta y graban los tiempos en sí. Dado que la CLI no registra estos tiempos en ningún lugar que Copilot pueda leer, la única manera de Copilot saberlos es llamar a la herramienta. La descripción de la herramienta explica esto al modelo, lo que lo orienta a usar la herramienta cuando preguntas sobre los tiempos de las llamadas a herramientas.

Paso 1: Crear el archivo de extensión

  1. Cree el siguiente directorio y archivo en el directorio principal:

    ~/.copilot/extensions/tool-time/extension.mjs
    

    Dado que se encuentra en ~/.copilot/extensions/, la extensión está disponible en todas las sesiones de la CLI, en todos los directorios, no solo en un repositorio.

  2. Agregue 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;
    });
    

Nota:

* @github/copilot-sdk/extension es el SDK de extensión, que se incluye con la CLI. La CLI resuelve esta importación automáticamente cuando ejecuta tu extensión, por lo que no necesitas añadirla a package.json ni ejecutar un gestor de paquetes.

  • Los valores startTimes, totalMs y callCount están en el ámbito del módulo. Dado que la extensión se ejecuta como un único proceso de larga duración para toda la sesión, se acumulan mientras la sesión esté abierta.

Paso 2: Cargar la extensión

  1. Inicie una sesión interactiva con características experimentales habilitadas:

    Shell
    copilot --experimental
    

    Dado que la extensión reside en ~/.copilot/extensions/, puede iniciar la CLI desde cualquier directorio y la extensión estará disponible.

    Si ya ha abierto una sesión, ejecute /clear para iniciar una sesión nueva, que vuelve a cargar extensiones desde el disco.

  2. Sin conceder permisos elevados a la nueva extensión, todas las extensiones o todas las herramientas, se le pedirá que permita que la nueva extensión omita las solicitudes de permisos de herramientas. Elija o Sí y permita siempre "user:tool-time" en este directorio.

    Nota:

    Para los permisos mínimos elevados para evitar ver este mensaje al iniciar la CLI, agréguelo al comando de inicio de la CLI:

    Shell
    --allow-tool='extension-permission-access(user:tool-time)'
    

    Para obtener más información, vea Permitir y denegar el uso de la herramienta.

Paso 3: Confirmar que la extensión se está ejecutando

Ejecute el /extensions manage comando para abrir el administrador de extensiones. Tu extensión tool-time debería figurar en el grupo Usuario con el estado en ejecución. Presione Esc para cerrar el administrador.

Paso 4: Probarlo

  1. A diferencia de un comando con barra, usted no invoca la herramienta usted mismo—Copilot la llama cuando resulta útil. Primero, asígnele a Copilot una tarea que implique algunas llamadas a herramientas, por ejemplo:

    Copilot prompt
    Explore the files in the current directory and give me a short summary of what's here.
    
  2. Una vez que Copilot termine de responder, pregunte:

    Copilot prompt
    How long have tool calls taken so far this session?
    

    El agente llama a la session_tool_time herramienta, desde la nueva extensión, y la usa para responder a la pregunta.

    Sugerencia

    Puede confirmar que el agente usó la nueva herramienta observando el comienzo de la respuesta de Copilot. La respuesta debe tener como prefijo el nombre de la herramienta que se usó; en este caso, session_tool_time.

Funcionamiento de la herramienta

La única llamada a joinSession es lo que convierte un archivo Node.js sin formato en una CLI de Copilot extensión. Conecta el proceso en ejecución a la sesión y registra todo lo que la extensión agrega a la CLI, en este caso, una sola herramienta.

Estos campos definen una herramienta:

  • name: el identificador Copilot usa para llamar a la herramienta.
  • description—qué hace la herramienta. El modelo se basa en este texto para decidir cuándo llamar a la herramienta, por lo que merece la pena ser explícito. Esta descripción le indica al modelo que no se le proporcionan esos tiempos, lo que lo lleva a invocar la herramienta en lugar de intentar deducirlos de alguna otra manera.
  • parameters: un esquema JSON que describe los argumentos de la herramienta. Esta herramienta no toma ninguno, por lo que el esquema es un objeto sin propiedades.
  • handler: una función asincrónica que se ejecuta cuando Copilot llama a la herramienta. La cadena que devuelve se convierte en el resultado de la herramienta, que el modelo lee.

Dos campos adicionales dan forma a cómo se ofrece la herramienta al modelo:

  • defer: "never": mantiene la descripción de la herramienta en la lista de herramientas del modelo en todo momento. De forma predeterminada, cuando hay muchas herramientas disponibles, la CLI puede aplazar las herramientas que se usan con poca frecuencia y permitir que el modelo los busque a petición. Configurar defer como "never" excluye esta herramienta de ese comportamiento, por lo que Copilot siempre la ve.

    Importante

    defer: "never" simplemente hace que la herramienta esté disponible para Copilot. No _obliga_Copilot a llamarlo. No hay forma de que una extensión exija que siempre se use una herramienta determinada si hay un medio alternativo para generar una respuesta adecuada a una solicitud. El modelo siempre decide por sí mismo qué herramienta usar. En este ejemplo, la nueva herramienta se usa de forma confiable porque no hay otra manera de que el modelo conozca la información que proporciona la herramienta.

  • skipPermission: true permite que la herramienta se ejecute sin pedirle que apruebe cada llamada. Esto es adecuado aquí porque la herramienta solo lee los totales que la extensión ya ha recopilado; no toca los archivos ni ejecuta comandos.

Los tiempos se obtienen observando la sesión. La extensión se suscribe a dos eventos que la CLI emite en torno a cada llamada de herramienta:

session.on("tool.execution_start", (event) => { /* ... */ });
session.on("tool.execution_complete", (event) => { /* ... */ });

Un tool.execution_start evento lleva el nombre de la herramienta (event.data.toolName). Un evento tool.execution_complete incluye un indicador success. Ninguno de los eventos contiene la información del otro, por lo que la extensión los correlaciona con el toolCallId que aparece en cada uno: cuando se inicia una herramienta, registra la hora actual en ese identificador. Cuando llega la finalización correspondiente, añade los milisegundos transcurridos al total acumulado. La herramienta se salta sus propias llamadas para que la cifra refleje el trabajo real de Copilot.

Dado que los totales se mantienen en el proceso persistente de la extensión, abarcan toda la sesión. Este es el tipo de tarea para la que las extensiones son buenas: observar lo que está ocurriendo en la sesión y mantener el estado entre llamadas.

Nota:

  • Los totales se almacenan en memoria, por lo que se restablecen cada vez que se vuelve a cargar la extensión o se reinicia la sesión (por ejemplo, después de /clear).
  • La cifra corresponde al tiempo transcurrido real medido desde el punto de vista de la extensión —la diferencia entre los eventos de inicio y finalización de cada invocación de herramienta—, por lo que incluye cualquier tiempo que una invocación haya pasado esperando su aprobación.

Extensión de ejemplo 2: comando con barra para el uso de tokens

En este ejemplo se agrega una extensión de proyecto denominada token-counter. La extensión agrega un /tokencount comando de barra diagonal que puede usar para comprobar cuántos tokens ha usado al interactuar con Copilot en la CLI. Se ejecuta /tokencount start cuando desea empezar a medir el uso del token y, a continuación, puede ejecutarse /tokencount en cualquier punto posterior para comprobar cuántos tokens ha usado desde el inicio del recuento.

La extensión mantiene un recuento acumulado de los tokens utilizados en la sesión al suscribirse a los eventos emitidos por la CLI, algo que un comando puntual de shell no puede hacer.

Paso 1: Crear el archivo de extensión

  1. En la raíz del repositorio de Git del proyecto, cree los directorios y el archivo siguientes:

    .github/extensions/token-counter/extension.mjs
    
  2. Agregue 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;
    });
    

Paso 2: Cargar la extensión

Inicie una sesión interactiva desde el mismo repositorio, con características experimentales habilitadas:

Shell
copilot --experimental

Si ya ha abierto una sesión, puede ejecutar /clear para iniciar una sesión nueva, que vuelve a cargar extensiones desde el disco.

Paso 3: Confirmar que la extensión se está ejecutando

Ejecute el /extensions manage comando para abrir el administrador de extensiones. Tu extensión token-counter debería figurar en el grupo Proyecto con el estado en ejecución. Presione Esc para cerrar el administrador.

También puede ejecutar /env para ver un resumen de todo lo cargado en la sesión, incluidas las extensiones.

Paso 4: Probarlo

A diferencia de una herramienta, tú mismo ejecutas un comando con barra. Inicie el contador:

Copilot prompt
/tokencount start

Envíe Copilot un mensaje o dos para que use algunos tokens, por ejemplo, pídale que explique un archivo. A continuación, compruebe cuántos tokens ha usado:

Copilot prompt
/tokencount

Si vuelve a ejecutarse /tokencount start , el recuento se reinicia desde cero.

Funcionamiento del ejemplo

La llamada a joinSession registra todo lo que la extensión agrega a la CLI, en este caso, un único comando de barra diagonal.

Un comando de barra diagonal se define mediante tres campos:

  • name: el nombre del comando, sin la barra diagonal inicial. Registrar tokencount es lo que hace que /tokencount esté disponible en la sesión.
  • description: el texto que se muestra junto al comando en el selector de comandos de barra diagonal.
  • handler: una función asincrónica que se ejecuta al invocar el comando. Recibe un objeto de contexto cuya propiedad args contiene el texto sin formato escrito después del nombre del comando. Para /tokencount start, ctx.args es "start"; para un /tokencount sin sistema operativo, es una cadena vacía.

El controlador vuelve a escribir su salida en la sesión con session.log(message, { level: "info" }), que imprime el mensaje en la transcripción.

Para saber cuántos tokens se han usado, la extensión se suscribe a un evento de sesión:

session.on("assistant.usage", (event) => {
    const { inputTokens = 0, outputTokens = 0 } = event.data ?? {};
    tokensUsed += inputTokens + outputTokens;
});

La CLI emite un evento assistant.usage después de cada turno del asistente, que incluye el número de tokens de entrada y de salida de ese turno. La extensión los agrega al total en ejecución en tokensUsed. Al ejecutar /tokencount start, el controlador registra el total actual en startedAt. Introducir /tokencount sin argumentos más adelante en la sesión muestra la diferencia. Dado que ambas variables residen en el proceso de extensión de larga duración, se conservan para toda la sesión.

Nota:

Los totales se almacenan en memoria, por lo que se restablecen cada vez que se vuelve a cargar la extensión o se reinicia la sesión (por ejemplo, después de /clear).

Edición y recarga de una extensión

A medida que desarrolles una extensión, editarás extension.mjs y querrás ver los cambios. Después de guardar el archivo, puede seleccionar la nueva versión de cualquiera de estas maneras:

  • Pida Copilot que vuelva a cargar extensiones, por ejemplo, Reload my extensions.
  • Ejecute /clear para iniciar una nueva sesión, que vuelve a cargar extensiones desde el disco.
  • Reinicie la CLI.

Si una extensión no se inicia o se comporta inesperadamente, ejecute /extensions manage e inspeccione la extensión para ver su estado y la ruta de acceso a su archivo de registro. Cada extensión genera un registro bajo ~/.copilot/logs/extensions/, que es el mejor lugar para buscar cuando algo falla.

Pasos siguientes

  • Adapte uno de estos ejemplos a sus propias necesidades. En la CLI, pida Copilot que modifique el comportamiento de cualquiera de las extensiones de ejemplo.
  • Comparta una extensión de nivel de usuario. Por ejemplo, mueva la tool-time extensión al directorio de .github/extensions/ un repositorio para compartirla con todos los usuarios que trabajan en ese repositorio.
  • Pida Copilot que cree una nueva extensión desde cero. CLI de Copilot las extensiones están basadas en SDK de Copilot, por lo que una extensión puede hacer cualquier cosa que el SDK haga posible, lo que le permite agregar vistas interactivas con botones y formularios, así como nuevas herramientas y comandos. Consulte SDK de Copilot.

Lectura adicional