How GitHub-copilot plugin uses VSCode Proposed API

VSCode was built with the ability to extend functionality through plugins in mind. From the UI interface to its AI agents, almost every part of VS Code can be customized and enhanced using the VSCode API. Many parts of VSCode itself are actually plugins.

There is a detailed documentation on creating your own plugin, and a large number of “mini-plugins” demonstrating the capabilities can be found in the repository vscode-extension-samples. The dts itself with the API description is in the main repo microsoft/vscode Here here. Except src/vscode-dts/vscode.d.ts There are many other files in the directory in the format vscode.proposed.*.d.ts. This is an experimental API. This article will discuss it.

What and how?

Plugin github-copilot (for inline hints) uses the following proposed API

// package.json плагина github-copilot
{
    ...
    "enabledApiProposals": [
		"inlineCompletionsAdditions"
	],
    ...
}

That's almost all you need to make your plugin use any proposed API. You also need to add the dts of the corresponding API to the plugin sources. This can be done conveniently using the package @vscode-dts.

Microsoft recommends using such extensions only in VSCode Insiders – a special dev version of VSCode. This is due to the fact that the proposed API is not stable and may change dramatically in the next release of VSCode (there will be an example in the article).

Using it in the usual VSCode is also possible, but there is one limitation – your VSCode, if you do not ask it, will not be able to use the plugin functionality based on the proposed API (this is not always the case, but more on that later). You can ask like this: edit the loading arguments of VSCode itself, i.e. execute the command Preferences: Configure Runtime Arguments and add there

// file .vscode/argv.json
{
    ...
    "enable-proposed-api": ["<YOUR-EXTENSION-ID>"]
}

And most importantly, an extension that uses the proposed API cannot be published in the official Extensions Marketplace. You will have to install it via the command Install from VSIX.

Generating a commit message

Let's look at an example of how expansion github-copilot-chat generates a commit message. To do this, it uses a proposed API called contribSourceControlInputBoxMenu. This API allows you to add a button to the input block in the Source Control menu and write something there. It looks like this

In fact, this API allows you to work with scm/inputBox (which will soon be completely replaced by SourceControl interface). The usage looks very simple

const repo: git.Repository = ...; 
repo.inputBox.value="My awesome commit message";

In the plugin it is set as follows:

// package.json плагина github-copilot-chat
{
  "enabledApiProposals": [
    "contribSourceControlInputBoxMenu"
  ],
  "contributes": {
    "commands": [
      {
		"command": "github.copilot.git.generateCommitMessage",
		"title": "%github.copilot.git.generateCommitMessage%",
		"icon": "$(sparkle)",
		"enablement": "!github.copilot.interactiveSession.disabled",
		"category": "GitHub Copilot"
      },
      ...
    ],
    ...
  },
  ...
}

The button itself with the icon $(sparkle) and when pressed, the command is simply called github.copilot.git.generateCommitMessage.

Stability

Proposed API is not a stable thing. A good example is the proposed API called interactive. Proposal is quite old and up to version 1.89.0 allowed adding inline chat to your plugin. It looked like this

But now this proposal (like all chat proposals) is only available with an authorized github copilot and only as an integration in github-copilot-chat.

Example

Let's write a toy plugin that shows the terminal's cwd shell-integration when running a command. For this, we'll use the proposed API called terminalShellIntegration.

Let's create our awesome sample project through yo

The process of creating a plugin structure

The process of creating a plugin structure

We will not analyze the structure in detail, we will only analyze package.json And src/extension.ts

// package.json
{
  "name": "awesomesample",
  "displayName": "awesomeSample",
  "publisher": "awesomePublisher",
  ...
  "activationEvents": [
    "onStartupFinished"
  ],
  "enabledApiProposals": [
    "terminalShellIntegration"
  ],
  ...
}

To use the proposed API, first install @vscode/dts

> npm i @vscode/dts

and then we'll run it

> npx vscode-dts dev
Downloading vscode.proposed.terminalShellIntegration.d.ts
To:   /Users/d.artyushin/Documents/habr/github-copilot-proposed-api/awesomesample/vscode.proposed.terminalShellIntegration.d.ts
From: https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.terminalShellIntegration.d.ts
Read more about proposed API at: https://code.visualstudio.com/api/advanced-topics/using-proposed-api

A file should appear in the root of the project vscode.proposed.terminalShellIntegration.d.ts with a description of the available API. Let's use it

// src/extension.ts
import * as vscode from 'vscode';

export function activate(context: vscode.ExtensionContext) {
	// подписываемся на запуск команды в терминале
    const terminalCwd = vscode.window.onDidStartTerminalShellExecution((event) => {
		// event имеет тип vscode.TerminalShellExecutionStartEvent
        // если shellIntegration есть, то у него берем поле cwd
		const currentCwd = event.terminal.shellIntegration?.cwd;

		if (currentCwd === undefined) {
			vscode.window.showInformationMessage('No found cwd :(');	
		} else {
			vscode.window.showInformationMessage(`Cwd is ${currentCwd}`);
    	}
	});

	context.subscriptions.push(terminalCwd);
}

export function deactivate() {}

Don't forget to add in argv.json line (try running the plugin without it first)

// .vscode/argv.json
{
  ...
  // Enable proposed API features.
  "enable-proposed-api": ["awesomePublisher.awesomesample"],
}

Finally, we launch the plugin and try the command ls V zsh And bash:

Run ls in zsh

Run ls in zsh

Run ls in bash

Run ls in bash

Proposed API in Copilot plugins

Usually github tests its new APIs on the github-copilot-chat plugin. Here is an example

// package.json плагина github-copilot-chat
{
  ...
  "enabledApiProposals": [
    "interactive",
    "terminalDataWriteEvent",
	"terminalExecuteCommandEvent",
	"terminalSelection",
	"terminalQuickFixProvider",
	"chatParticipantAdditions",
	"defaultChatParticipant",
	"embeddings",
	"chatVariableResolver",
	"chatProvider",
	"mappedEditsProvider",
	"aiRelatedInformation",
	"codeActionAI",
	"findTextInFiles",
	"textSearchProvider",
	"commentReveal",
	"contribSourceControlInputBoxMenu",
	"contribCommentEditorActionsMenu",
	"contribCommentThreadAdditionalMenu",
	"contribCommentsViewThreadMenus",
	"newSymbolNamesProvider",
	"findFiles2",
	"extensionsAny",
	"authLearnMore",
	"testObserver",
	"aiTextSearchProvider",
	"documentFiltersExclusive",
	"chatParticipantPrivate",
	"lmTools"
  ],
  ...
}

A fair question: “How does github publish its plugins to the Extension Marketplace then?”. In the VSCode source code, you can find the following code

...
        // NEW world - product.json spells out what proposals each extension can use
		if (productService.extensionEnabledApiProposals) {
			for (const [k, value] of Object.entries(productService.extensionEnabledApiProposals)) {
				const key = ExtensionIdentifier.toKey(k);
				const proposalNames = value.filter(name => {
					if (!allApiProposals[<ApiProposalName>name]) {
						_logService.warn(`Via 'product.json#extensionEnabledApiProposals' extension '${key}' wants API proposal '${name}' but that proposal DOES NOT EXIST. Likely, the proposal has been finalized (check 'vscode.d.ts') or was abandoned.`);
						return false;
					}
					return true;
				});
				this._productEnabledExtensions.set(key, proposalNames);
			}
		}
	}

	updateEnabledApiProposals(extensions: IExtensionDescription[]): void {
		for (const extension of extensions) {
			this.doUpdateEnabledApiProposals(extension);
		}
	}

...   

The comment in the code speaks for itself. File product.json after installation VSCode is located in the path (for MacOS) /Applications/Visual Studio Code.app/Contents/Resources/app/product.json . There, for “important” extensions, the available proposal APIs are indicated.

{
    ...
    "extensionEnabledApiProposals": {
        ...
        "GitHub.copilot": [
			"inlineCompletionsAdditions"
		],
		"GitHub.copilot-nightly": [
			"inlineCompletionsAdditions"
		],
		"GitHub.copilot-chat": [
			"interactive",
			"terminalDataWriteEvent",
			"terminalExecuteCommandEvent",
			"terminalSelection",
			"terminalQuickFixProvider",
			"chatParticipantAdditions",
			"defaultChatParticipant",
			"embeddings",
			"chatVariableResolver",
			"chatProvider",
			"mappedEditsProvider",
			"aiRelatedInformation",
			"codeActionAI",
			"findTextInFiles",
			"textSearchProvider",
			"commentReveal",
			"contribSourceControlInputBoxMenu",
			"contribCommentEditorActionsMenu",
			"contribCommentThreadAdditionalMenu",
			"contribCommentsViewThreadMenus",
			"newSymbolNamesProvider",
			"findFiles2",
			"extensionsAny",
			"authLearnMore",
			"testObserver",
			"aiTextSearchProvider",
			"documentFiltersExclusive",
			"chatParticipantPrivate",
			"lmTools"
		],
        ...
}

You (or your users) can also update this file to avoid manipulation with argv.

Conclusion

The conclusion is very simple – take into account the nuances and use the VSCode proposed API, there is a lot of cool and useful stuff there 🙂 I also encourage you to follow the Release Notes of new versions of VSCode – there is always a section there Proposed APIs.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *