Skip to main content

Создание расширений для интерфейс командной строки GitHub Copilot

Создайте расширения, которые добавляют ваши инструменты и команды слэша в Второй пилот CLI.

Примечание.

интерфейс командной строки GitHub Copilot Расширения в настоящее время являются экспериментальной функцией и могут измениться.

Расширение позволяет добавить свои собственные возможности в Второй пилот CLI. Каждое расширение — это небольшой модуль Node.js, который работает как отдельный процесс вместе с вашей интерактивной сессией и подключается обратно к нему. Через это соединение расширение может добавлять инструменты , которые Copilot могут вызывать во время работы от вашего имени, а также выполнять команды слэша , которые вы запускаете сами.

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

  • Инструмент под названием tool-time, который Copilot может позвонить, чтобы сообщить, сколько времени заняли вызовы инструментов за сессию.
  • Команда слэша, называемая /tokencount, которая показывает, сколько жетонов вы использовали с начала подсчёта.

Оба примера зависят только от SDK, который идёт в Второй пилот CLIкомплекте с , так что дополнительного для установки нечего. Для информации о том, как работают расширения, см. About extensions for интерфейс командной строки GitHub Copilot.

Предупреждение

Расширения выполняются на вашем компьютере с вашими правами. Загружайте только тот код расширения, которым доверяете, так же как вы запускаете только другие скрипты, которые не написали сами.

Prerequisites

  • интерфейс командной строки GitHub Copilot: Вам нужно Второй пилот CLI установить и настроить. См . раздел AUTOTITLE.
  • Включены экспериментальные функции: Расширения в настоящее время являются экспериментальной функцией. Шаги этого урока включают экспериментальные функции каждый раз при запуске CLI, используя --experimental опцию командной строки.
  • JavaScript: Расширения пишутся на JavaScript, поэтому для создания собственных расширений нужно хорошо знать этот язык.
  • Репозиторий: во втором примере добавлено расширение на уровне проекта, поэтому для добавления расширения потребуется локальная копия репозитория Git.

Пример расширения 1: инструмент «tool time»

В этом примере добавлено пользовательское расширение под названием tool-time. Он добавляет новый инструмент — называемый session_tool_time— который Copilot может позвонить, чтобы сообщить, сколько времени заняли вызовы инструментов в этой сессии и сколько звонков это охватывает.

Чтобы инструмент использовался CLI, он должен выполнять то, что CLI не может сделать самостоятельно. В данном случае session_tool_time инструмент отслеживает, сколько времени занимают вызовы инструментов, прослушивая события из CLI о начале и завершении вызовов инструмента, и записывая тайминги. Поскольку CLI не фиксирует эти тайминги нигде для Copilot чтения, единственный способ Copilot узнать их — это вызвать инструмент. Описание инструмента объясняет это модели, которая направляет её к использованию инструмента, когда вы спрашиваете о тайминге вызова инструмента.

Шаг 1: Создайте файл расширения

  1. Создайте следующую папку и файл в вашей домашней директории:

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

    Поскольку это в ~/.copilot/extensions/разделе , расширение доступно во всех ваших сессиях CLI, в каждом каталоге — а не только в одном репозитории.

  2. Добавьте этот код в 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;
    });
    

Примечание.

* @github/copilot-sdk/extension — это расширение SDK, которое входит в комплект с CLI. CLI автоматически разрешает этот импорт при запуске вашего расширения, так что вам не нужно добавлять его в package.json пакет или запускать менеджер пакетов.

  • Значения startTimes, totalMsи callCount находятся в области действия модуля. Поскольку расширение работает как единый долговечный процесс на протяжении всей сессии, они накапливаются столько, сколько она открыта.

Шаг 2: Загрузите удлинитель

  1. Начните интерактивную сессию с включёнными экспериментальными функциями:

    Shell
    copilot --experimental
    

    Поскольку расширение находится под ~/.copilot/extensions/, вы можете запустить CLI из любой папки, и расширение будет доступно.

    Если у вас уже была открыта сессия, запустите /clear новую сессию, которая загружает расширения с диска.

  2. Без предоставления повышенных разрешений ни новому расширению, ни всем расширениям, ни всем инструментам, вам будет предложено разрешить новому расширению пропускать запросы разрешения инструмента. Выберите либо Да , либо Да, и всегда разрешайте «user:tool-time» в этой директории.

    Примечание.

    Для минимальных повышенных разрешений, чтобы не видеть это сообщение при запуске CLI, добавьте это в команду запуска CLI:

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

    Дополнительные сведения см. в разделе Разрешение и отказ в использовании инструментов.

Шаг 3: Убедитесь, что расширение работает

Выполните /extensions manage команду, чтобы открыть менеджер расширений. Ваше tool-time расширение должно быть указано в группе пользователей со статусом запущенного. Нажмите Esc , чтобы закрыть менеджера.

Шаг 4: Попробуйте

  1. В отличие от команды слэш, вы не вызываете инструмент сами —Copilot вызываете его, когда он полезен. Во-первых, дайте Copilot работу, включающую несколько вызовов инструментов, например:

    Copilot prompt
    Explore the files in the current directory and give me a short summary of what's here.
    
  2. Когда Copilot закончишь отвечать, спроси:

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

    Агент звонит в инструмент session_tool_time с нового расширения и использует его, чтобы ответить на ваш вопрос.

    Совет

    Вы можете подтвердить, что агент использовал новый инструмент, посмотрев в начале Copilotответа. Ответ должен быть с префиксом имени использованного инструмента; в данном случае . session_tool_time

Как работает инструмент

Единственный вызов превращает joinSession простой Node.js файл в Второй пилот CLI расширение. Он соединяет работающий процесс с вашей сессией и регистрирует всё, что расширение добавляет в CLI — в данном случае это один инструмент.

Инструмент определяется следующими полями:

  • name—идентификатор, Copilot используемый для вызова инструмента.
  • description— что делает инструмент. Модель опирается на этот текст для решения момента, когда вызывать инструмент, поэтому стоит быть чётким. Это описание говорит модели, что ей не сообщают эти тайминги, что направляет её к вызову инструмента, а не к тому, чтобы пытаться вычислить тайминги другим способом.
  • parameters— схема JSON, описывающая аргументы инструмента. Этот инструмент не принимает никаких свойств, поэтому схема — это объект без свойств.
  • handler—асинхронная функция, которая запускается при Copilot вызове инструмента. Какая строка, которую он возвращает, становится результатом инструмента, который модель читает.

Два дополнительных поля определяют, как инструмент предлагается модели:

  • defer: "never"—всегда хранит описание инструмента в списке инструментов модели. По умолчанию, когда доступно множество инструментов, CLI может отложить редко используемые инструменты и позволить модели искать их по требованию. Настройка defer отключает "never" этот инструмент от такого поведения, поэтому Copilot всегда его видит.

    Внимание

    defer: "never" просто делает инструмент доступным для Copilot. Он не _заставляет_Copilot её вызывать. Нет способа, чтобы расширение требовало, чтобы определённый инструмент всегда использовался, если существует альтернативный способ генерации соответствующего ответа на запрос. Модель всегда сама решает, какой инструмент использовать. В этом примере новый инструмент используется надёжно, потому что нет другого способа для модели узнать информацию, которую он предоставляет.

  • skipPermission: true позволяет инструменту работать без просьбы одобрить каждый звонок. Это уместно здесь, потому что инструмент читает только итоги, которые расширение уже собрало; Он не трогает ваши файлы и не запускает команды.

Расписание составляется путем просмотра сессии. Расширение подписывается на два события, которые CLI генерирует вокруг каждого вызова инструмента:

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

Событие tool.execution_start носит имя инструмента (event.data.toolName). Мероприятие несёт tool.execution_complete``success флаг. Ни одно из событий не передаёт информацию другого, поэтому расширение сопоставляет их с помощью того, toolCallId что отображается на каждом: когда инструмент запускается, он фиксирует текущее время под этим идентификатором. Когда приходит совпадающее завершение, к текущей сумме добавляются прошедшие миллисекунды. Инструмент пропускает свои собственные вызовы, поэтому фигура отражает Copilotреальную работу фигуры.

Поскольку итоги находятся в процессе длительного продления, они покрывают всю сессию. Вот с такими продлениями работы хороши: наблюдать за происходящим в сессии и поддерживать состояние между звонками.

Примечание.

  • Итоги хранятся в памяти, поэтому они сбрасываются при перезагрузке расширения или перезапуске сессии (например, после /clear).
  • Этот рисунок — это время настенных часов, измеряемое с точки зрения расширения — промежуток между событиями запуска и завершения каждого вызова инструмента — то есть включает любое время, проведённое в ожидании вашего одобрения.

Пример расширения 2: Команда использования токена на слэш

В этом примере добавляется расширение проекта под названием token-counter. Расширение добавляет /tokencount команду слэш, с помощью которой можно проверить, сколько токенов вы использовали при взаимодействии Copilot в CLI. Вы запускаете /tokencount start , когда хотите начать измерять использование токенов, а затем можете /tokencount проверить, сколько токенов вы использовали с начала подсчёта.

Расширение сохраняет текущую сумму токенов, используемых в сессии, подписываясь на события, исходящие от CLI, чего не может сделать разовая команда shell.

Шаг 1: Создайте файл расширения

  1. В корне репозитория Git для вашего проекта создайте следующие каталоги и файлы:

    .github/extensions/token-counter/extension.mjs
    
  2. Добавьте этот код в 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;
    });
    

Шаг 2: Загрузите удлинитель

Начните интерактивную сессию из того же репозитория с включенными экспериментальными функциями:

Shell
copilot --experimental

Если сессия уже была открыта, можно запустить /clear новую сессию, которая перезагружает расширения с диска.

Шаг 3: Убедитесь, что расширение работает

Выполните /extensions manage команду, чтобы открыть менеджер расширений. Ваше token-counter расширение должно быть указано в группе Project со статусом запущенного. Нажмите Esc , чтобы закрыть менеджера.

Вы также можете посмотреть /env сводку всего, что загружено в сессию, включая расширения.

Шаг 4: Попробуйте

В отличие от инструмента, вы сами вызываете команду слэша. Начинайте счётчик:

Copilot prompt
/tokencount start

Отправьте Copilot пару запросов, чтобы использовать какие-то токены — например, попросите объяснить файл. Затем проверьте, сколько токенов вы использовали:

Copilot prompt
/tokencount

Если вы забегаете /tokencount start снова, счёт начинается с нуля.

Как работает пример

Вызов joinSession регистрирует всё, что расширение добавляет в CLI — в данном случае одну команду слэша.

Команда косой черты определяется тремя полями:

  • name— командное имя, без переднего слэза. Регистрация tokencount — это то, что делает /tokencount сессию доступной.
  • description— текст, отображаемый рядом с командой в выборе команды Slash.
  • handler—асинхронная функция, которая запускается при вызове команды. Он получает контекстный объект, свойство args которого содержит необработанный текст, введённый после имени команды. Для /tokencount start, ctx.args это "start"; для голого /tokencount, это пустая строка.

Обработчик записывает свой вывод обратно в сессию с помощью session.log(message, { level: "info" }), которая печатает сообщение в транскрипте.

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

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

CLI assistant.usage генерирует событие после каждого хода помощника, перенося количество входных и выходных токенов за этот ход. Расширение добавляет их к текущему общему числу в tokensUsed. Когда вы запускаете /tokencount start, обработчик записывает текущий итог в startedAt. Позже в сессии /tokencount без споров мы сообщали о разнице. Поскольку обе переменные находятся в процессе длительного расширения, они сохраняются на протяжении всей сессии.

Примечание.

Итоги хранятся в памяти, поэтому они сбрасываются при перезагрузке расширения или перезапуске сессии (например, после /clear).

Редактирование и перезагрузка расширения

По мере разработки расширения вы будете редактировать extension.mjs и захотите видеть свои изменения. После сохранения файла вы можете получить новую версию любым из следующих способов:

  • Попросите Copilot перезагрузить расширения — например, Reload my extensions.
  • Запускайте /clear новую сессию, которая перезагружает расширения с диска.
  • Перезапусти CLI.

Если расширение не запускается или ведёт себя неожиданно, запустите /extensions manage и проверьте расширение, чтобы увидеть его статус и путь к файлу журнала. Каждое расширение записывает лог под ~/.copilot/logs/extensions/, что является лучшим местом для поиска, когда что-то идёт не так.

Дальнейшие действия

  • Адаптируйте один из этих примеров под свои нужды. В CLI попросите Copilot изменить поведение любого из примерных расширений.
  • Поделитесь расширением для пользователя. Например, переместить tool-time расширение в каталог репозитория .github/extensions/ , чтобы поделиться им со всеми, кто работает в этом репозитории.
  • Попросите Copilot создать для вас новое расширение с нуля. Второй пилот CLI расширения работают на Второй пилот SDK, поэтому оно может делать всё, что позволяет SDK, добавляя интерактивные виды с кнопками и формами, а также новые инструменты и команды. См . раздел AUTOTITLE.

Дополнительные материалы