plugins: enable installing plugins from marketplaces

Supports both Copilot marketplaces as well as Claude marketplaces (when configured).

Still todo:

- Currently enumerating plugins hit public GH APIs. But this would fail for private repos. In this case we should generalize the PluginInstallService to allow cloning the repo for the purpose of enumeration, not just install.
- Updating plugins still needs to be hooked up.
- Marketplace-installed plugins should get their own Discovery implementation rather than configuring the setting.
- We should normalize the type of plugin a bit so it flows from the marketplace type rather than getting re-discovered from disk.
This commit is contained in:
Connor Peet
2026-02-23 16:08:12 -08:00
parent 3e137f4a03
commit dc4cd84cdb
8 changed files with 389 additions and 47 deletions

View File

@@ -9,7 +9,7 @@ import { Command, commands, Disposable, MessageOptions, Position, QuickPickItem,
import TelemetryReporter from '@vscode/extension-telemetry';
import { uniqueNamesGenerator, adjectives, animals, colors, NumberDictionary } from '@joaomoreno/unique-names-generator';
import { ForcePushMode, GitErrorCodes, RefType, Status, CommitOptions, RemoteSourcePublisher, Remote, Branch, Ref } from './api/git';
import { Git, GitError, Stash, Worktree } from './git';
import { Git, GitError, Repository as GitRepository, Stash, Worktree } from './git';
import { Model } from './model';
import { GitResourceGroup, Repository, Resource, ResourceGroupType } from './repository';
import { DiffEditorSelectionHunkToolbarContext, LineChange, applyLineChanges, getIndexDiffInformation, getModifiedRange, getWorkingTreeDiffInformation, intersectDiffWithRange, invertLineChange, toLineChanges, toLineRanges, compareLineChanges } from './staging';
@@ -1037,6 +1037,18 @@ export class CommandCenter {
await this.cloneManager.clone(url, { parentPath, recursive: true });
}
@command('_git.cloneRepository')
async cloneRepository(url: string, parentPath: string): Promise<void> {
await this.cloneManager.clone(url, { parentPath, postCloneAction: 'none' });
}
@command('_git.pull')
async pullRepository(repositoryPath: string): Promise<void> {
const dotGit = await this.git.getRepositoryDotGit(repositoryPath);
const repo = new GitRepository(this.git, repositoryPath, undefined, dotGit, this.logger);
await repo.pull();
}
@command('git.init')
async init(skipFolderPrompt = false): Promise<void> {
let repositoryPath: string | undefined = undefined;