Remarque
CLI GitHub Copilot les extensions sont actuellement une fonctionnalité expérimentale et sont sujettes à modification.
Une extension vous permet d’ajouter vos propres fonctionnalités à Copilot pour CLI. Chaque extension est un petit module Node.js qui s’exécute en tant que processus distinct en même temps que votre session interactive et se connecte à celui-ci. Grâce à cette connexion, une extension peut ajouter des outils qui Copilot peuvent appeler pendant qu’elle fonctionne en votre nom et des commandes obliques que vous exécutez vous-même.
Dans ce tutoriel, vous allez créer deux extensions simples comme exemples de ce que vous pouvez faire :
- Un outil, appelé
tool-time, qui Copilot peut appeler pour signaler combien de temps ses appels d’outil ont pris jusqu’à présent dans une session. - Il s’agit d’une commande slash appelée
/tokencount, qui indique combien de jetons vous avez utilisés depuis que vous avez commencé à compter.
Les deux exemples s’appuient uniquement sur le Kit de développement logiciel (SDK) fourni avec le Copilot pour CLIkit . Il n’y a donc rien d’supplémentaire à installer. Pour plus d’informations sur le fonctionnement des extensions, consultez About extensions for CLI GitHub Copilot.
Avertissement
Les extensions s’exécutent sur votre ordinateur avec vos privilèges. Chargez uniquement le code d’extension que vous approuvez, de la même façon que vous n’exécutez que tout autre script que vous n’avez pas écrit vous-même.
Prerequisites
- CLI GitHub Copilot: Vous avez besoin d’installer Copilot pour CLI et de configurer. Consultez « Commencer avec l'interface en ligne de commande GitHub Copilot ».
- Fonctionnalités expérimentales activées : les extensions sont actuellement une fonctionnalité expérimentale. Les étapes de ce didacticiel activent les fonctionnalités expérimentales chaque fois que vous démarrez l’interface CLI à l’aide de l’option
--experimentalde ligne de commande. - JavaScript : Les extensions sont écrites en JavaScript. Vous devez donc vous familiariser avec ce langage pour créer vos propres extensions.
- Référentiel : Le deuxième exemple ajoute une extension au niveau du projet. Vous aurez donc besoin d’une copie locale d’un référentiel Git dans lequel ajouter l’extension.
Exemple d’extension 1 : un outil « tool time »
Cet exemple ajoute une extension au niveau de l’utilisateur appelée tool-time. Il ajoute un nouvel outil — appelé session_tool_time — que Copilot peut appeler pour signaler la durée totale des appels à l’outil effectués jusqu’à présent au cours de cette session, ainsi que le nombre d’appels concernés.
Pour vous assurer qu’un outil est utilisé par l’interface CLI, l’outil doit faire quelque chose que l’interface CLI ne peut pas faire par elle-même. Dans ce cas, l’outil session_tool_time effectue le suivi du temps nécessaire aux appels d’outils en écoutant les événements de l’interface CLI sur le moment où les appels d’outil démarrent et se terminent, et enregistrent les minutages proprement dits. Étant donné que la CLI n’enregistre ces temps nulle part où Copilot peut les lire, le seul moyen pour Copilot de les connaître est d’invoquer l’outil. La description de l’outil explique cela au modèle, ce qui l’incite à utiliser l’outil lorsque vous posez des questions sur le moment où les appels d’outil ont lieu.
Étape 1 : Créer le fichier d’extension
-
Créez le répertoire et le fichier suivants dans votre répertoire de base :
~/.copilot/extensions/tool-time/extension.mjsComme cela se trouve sous
~/.copilot/extensions/, l’extension est disponible dans toutes vos sessions CLI, dans chaque répertoire, et pas seulement dans un référentiel. -
Ajoutez ce code à
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; });
Remarque
*
@github/copilot-sdk/extension est le Kit de développement logiciel (SDK) d’extension, qui est fourni avec l’interface CLI. L’interface CLI résout automatiquement cette importation lorsqu’elle exécute votre extension. Vous n’avez donc pas besoin de l’ajouter à un package.json gestionnaire de package ou de l’exécuter.
- Les valeurs
startTimes,totalMsetcallCountsont définies au niveau du module. Comme l’extension s’exécute sous la forme d’un seul processus persistant pendant toute la session, elles s’accumulent tant que la session reste ouverte.
Étape 2 : Charger l’extension
-
Démarrez une session interactive avec les fonctionnalités expérimentales activées :
Shell copilot --experimental
copilot --experimentalÉtant donné que l’extension se trouve sous
~/.copilot/extensions/, vous pouvez démarrer l’interface CLI à partir de n’importe quel répertoire et l’extension sera disponible.Si vous avez déjà ouvert une session, exécutez
/clearpour démarrer une nouvelle session, ce qui recharge les extensions à partir du disque. -
Sans accorder d’autorisations élevées à la nouvelle extension, toutes les extensions ou tous les outils, vous êtes invité à autoriser la nouvelle extension à ignorer les invites d’autorisation de l’outil. Choisissez Oui ou Oui, et autorisez toujours « user :tool-time » dans ce répertoire.
Remarque
Pour que les autorisations minimales élevées empêchent de voir ce message lorsque vous démarrez l’interface CLI, ajoutez-la à votre commande de démarrage CLI :
Shell --allow-tool='extension-permission-access(user:tool-time)'
--allow-tool='extension-permission-access(user:tool-time)'Pour plus d’informations, consultez « Autorisation et refus de l’utilisation de l’outil ».
Étape 3 : Confirmer que l’extension est en cours d’exécution
Exécutez la /extensions manage commande pour ouvrir le gestionnaire d’extensions. Votre tool-time extension doit être répertoriée sous le groupe d’utilisateurs avec l’état d’exécution. Appuyez sur Échap pour fermer le gestionnaire.
Étape 4 : Essayer
-
Contrairement à une commande slash, vous n’appelez pas vous-même un outil : Copilot l’appelle lorsque c’est utile. Tout d’abord, donnez Copilot un certain travail qui implique quelques appels d’outils, par exemple :
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. -
Une fois que Copilot a fini de répondre, demandez :
Copilot prompt How long have tool calls taken so far this session?
How long have tool calls taken so far this session?L’agent appelle l’outil
session_tool_time, à partir de la nouvelle extension et l’utilise pour répondre à votre question.Conseil
Vous pouvez confirmer que l’agent a utilisé le nouvel outil en examinant le début de la réponse de Copilot. La réponse doit être précédée du nom de l’outil utilisé ; dans ce cas,
session_tool_time.
Fonctionnement de l’outil
Le simple appel à joinSession est ce qui transforme un simple fichier Node.js en une extension Copilot pour CLI. Il connecte le processus en cours d’exécution à votre session et enregistre tout ce que l’extension ajoute à l’interface CLI, dans ce cas, un seul outil.
Un outil est défini par ces champs :
name— l’identificateur que Copilot utilise pour appeler l’outil.description— ce que fait l’outil. Le modèle s’appuie sur ce texte pour décider quand appeler l’outil. Il vaut donc la peine d’être explicite. Cette description indique au modèle qu’on ne lui communique pas directement ces délais, ce qui l’incite à appeler l’outil plutôt qu’à tenter de déterminer ces délais par un autre moyen.parameters— schéma JSON décrivant les arguments de l’outil. Cet outil n’accepte aucun, de sorte que le schéma est un objet sans propriétés.handler— fonction asynchrone qui s’exécute lors Copilot de l’appel de l’outil. Quelle que soit la chaîne qu’il renvoie, elle devient le résultat de l’outil, que le modèle lit.
Deux autres champs permettent de mettre en forme la façon dont l’outil est proposé au modèle :
-
defer: "never": conserve la description de l’outil dans la liste des outils du modèle à tout moment. Par défaut, lorsque de nombreux outils sont disponibles, l’interface CLI peut différer les outils rarement utilisés et laisser le modèle les rechercher à la demande. Définirdefersur"never"exclut cet outil de ce comportement, ainsi Copilot le voit toujours.Important
defer: "never"rend simplement l’outil disponible pour Copilot. Il n’_oblige_Copilot pas à l’appeler. Il n’existe aucun moyen, pour une extension, d’exiger qu’un outil particulier soit toujours utilisé s’il existe un autre moyen de générer une réponse appropriée à partir d’une invite. Le modèle décide toujours pour lui-même de l’outil à utiliser. Dans cet exemple, le nouvel outil est utilisé de manière fiable, car il n’existe aucun autre moyen pour le modèle de connaître les informations fournies par l’outil. -
skipPermission: truepermet à l’outil de s’exécuter sans vous demander d’approuver chaque appel. Cela convient ici, car l’outil lit uniquement les totaux que l’extension a déjà collectés ; il n’accède pas à vos fichiers et n’exécute aucune commande.
Les temps sont relevés en observant la session. L’extension s’abonne à deux événements que l’interface CLI émet autour de chaque appel d’outil :
session.on("tool.execution_start", (event) => { /* ... */ });
session.on("tool.execution_complete", (event) => { /* ... */ });
Un tool.execution_start événement porte le nom de l’outil (event.data.toolName). Un tool.execution_complete événement porte un success indicateur. Aucun événement ne contient les informations de l’autre, de sorte que l’extension les met en corrélation à l’aide de l’élément toolCallId qui apparaît sur chacun d’eux : lorsqu’un outil démarre, il enregistre l’heure actuelle sous cet ID. Lorsque la fin d’exécution correspondante survient, elle ajoute les millisecondes écoulées au total cumulé. L’outil ignore ses propres appels afin que le graphique reflète le travail réel de Copilot.
Comme les totaux sont conservés dans le processus d’extension persistant, ils couvrent l’ensemble de la session. C’est le genre de tâche pour lequel les extensions sont bien adaptées : observer ce qui se passe dans la session et maintenir l’état entre les appels.
Remarque
- Les totaux sont conservés en mémoire. Ils sont donc réinitialisés chaque fois que l’extension est rechargée ou que la session redémarre (par exemple, après
/clear). - La valeur indiquée correspond au temps réel mesuré du point de vue de l’extension — c’est-à-dire l’intervalle entre les événements de début et de fin de chaque appel à un outil — ; elle inclut donc aussi tout le temps qu’un appel a passé en attente de votre approbation.
Exemple d’extension 2 : une commande slash d’utilisation du jeton
Cet exemple ajoute une extension de projet appelée token-counter. L’extension ajoute une commande slash /tokencount que vous pouvez utiliser pour vérifier combien de tokens vous avez utilisés lorsque vous interagissez avec Copilot dans l’interface en ligne de commande. Vous exécutez /tokencount start quand vous souhaitez commencer à mesurer l’utilisation de vos jetons, puis vous pouvez l’exécuter /tokencount à tout moment pour vérifier le nombre de jetons que vous avez utilisés depuis le démarrage du nombre.
L’extension maintient un décompte cumulatif des tokens utilisés au cours de la session en s’abonnant aux événements émis par le CLI, ce qu’une commande shell ponctuelle ne peut pas faire.
Étape 1 : Créer le fichier d’extension
-
À la racine du dépôt Git de votre projet, créez les répertoires et fichiers suivants :
.github/extensions/token-counter/extension.mjs -
Ajoutez ce code à
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; });
Étape 2 : Charger l’extension
Démarrez une session interactive à partir du même référentiel, avec les fonctionnalités expérimentales activées :
copilot --experimental
copilot --experimental
Si vous avez déjà ouvert une session, vous pouvez exécuter /clear pour démarrer une nouvelle session, qui recharge les extensions à partir du disque.
Étape 3 : Confirmer que l’extension est en cours d’exécution
Exécutez la /extensions manage commande pour ouvrir le gestionnaire d’extensions. Votre token-counter extension doit être répertoriée sous le groupe Project avec l’état d’exécution. Appuyez sur Échap pour fermer le gestionnaire.
Vous pouvez également exécuter /env pour afficher un résumé de tous les éléments chargés dans la session, y compris les extensions.
Étape 4 : Essayer
Contrairement à un outil, vous lancez vous-même une commande slash. Démarrez le compteur :
/tokencount start
/tokencount start
Envoyez Copilot une invite ou deux pour qu’elle utilise certains jetons, par exemple, demandez-lui d’expliquer un fichier. Vérifiez ensuite le nombre de jetons que vous avez utilisés :
/tokencount
/tokencount
Si vous réexécutez /tokencount start , le nombre redémarre de zéro.
Fonctionnement de l’exemple
L’appel à joinSession enregistre tout ce que l’extension ajoute à l’interface en ligne de commande — dans ce cas, une seule commande slash.
Une commande slash est définie par trois champs :
name— nom de la commande, sans barre oblique de début. L’enregistrement detokencountpermet de rendre/tokencountdisponible dans la session.description—le texte affiché à côté de la commande dans le sélecteur de commandes slash.handler— fonction asynchrone qui s’exécute lorsque vous appelez la commande. Il reçoit un objet de contexte dontargsla propriété contient le texte brut tapé après le nom de la commande. Pour/tokencount start,ctx.argsest"start"; pour un/tokencountseul, c’est une chaîne vide.
Le gestionnaire écrit sa sortie dans la session avec session.log(message, { level: "info" }), ce qui affiche le message dans la transcription.
Pour savoir combien de jetons ont été utilisés, l’extension s’abonne à un événement de session :
session.on("assistant.usage", (event) => {
const { inputTokens = 0, outputTokens = 0 } = event.data ?? {};
tokensUsed += inputTokens + outputTokens;
});
Le CLI émet un événement assistant.usage après chaque tour de l’assistant, qui contient les nombres de jetons d’entrée et de sortie pour ce tour. L’extension les ajoute au total cumulatif dans tokensUsed. Lorsque vous exécutez /tokencount start, le gestionnaire enregistre le total actuel en startedAt. Le fait de saisir /tokencount sans argument plus tard dans la session affiche la différence. Étant donné que les deux variables vivent dans le processus d’extension de longue durée, elles persistent pour toute la session.
Remarque
Les totaux sont conservés en mémoire. Ils sont donc réinitialisés chaque fois que l’extension est rechargée ou que la session redémarre (par exemple, après /clear).
Modification et rechargement d’une extension
Lorsque vous développez une extension, vous allez modifier extension.mjs et vouloir voir vos modifications. Après avoir enregistré le fichier, vous pouvez récupérer la nouvelle version de l’une des manières suivantes :
- Demandez à Copilot de recharger les extensions, par exemple
Reload my extensions. - Exécutez
/clearpour démarrer une nouvelle session, qui recharge les extensions à partir du disque. - Redémarrez l’interface CLI.
Si une extension ne parvient pas à démarrer ou se comporte de façon inattendue, exécutez et inspectez /extensions manage l’extension pour voir son état et le chemin d’accès à son fichier journal. Chaque extension écrit un fichier journal sous ~/.copilot/logs/extensions/, c’est donc le premier endroit à consulter en cas de problème.
Étapes suivantes
- Adaptez l’un de ces exemples à vos propres besoins. Dans l’interface CLI, demandez Copilot à modifier le comportement de l’un des exemples d’extensions.
- Partagez une extension au niveau de l’utilisateur. Par exemple, déplacez l’extension
tool-timedans le répertoire d’un.github/extensions/référentiel pour le partager avec tous ceux qui travaillent dans ce référentiel. - Demandez à Copilot de créer une nouvelle extension pour vous à partir de zéro. Copilot pour CLI Les extensions s’appuient sur le SDK Copilot SDK, de sorte qu’une extension peut faire tout ce que le SDK permet, ce qui vous permet d’ajouter des vues interactives avec des boutons et des formulaires, ainsi que de nouveaux outils et commandes. Consultez « Kit de développement logiciel (SDK) Copilot ».