chat: add support for agent plugin sources (#299081)

* chat: add support for agent plugin sources

- Adds support for agent plugins to reference sources as specified in
  PLUGIN_SOURCES.md, enabling installation from GitHub, npm, pip, and
  other package registries
- Integrates source parsing and validation into the plugin installation
  service and repository service
- Adds comprehensive test coverage for plugin source handling and
  installation from various sources
- Creates PLUGIN_SOURCES.md documentation describing how to specify
  plugin source configurations

(Commit message generated by Copilot)

* comments

* windows fixes and fault handling

* fix tests
This commit is contained in:
Connor Peet
2026-03-04 07:20:21 -08:00
committed by GitHub
parent 8a03516dd4
commit a4e35e0d69
16 changed files with 1571 additions and 45 deletions

View File

@@ -1040,19 +1040,29 @@ export class CommandCenter {
}
@command('_git.cloneRepository')
async cloneRepository(url: string, parentPath: string): Promise<void> {
async cloneRepository(url: string, localPath: string, ref?: string): Promise<void> {
const opts = {
location: ProgressLocation.Notification,
title: l10n.t('Cloning git repository "{0}"...', url),
cancellable: true
};
const parentPath = path.dirname(localPath);
const targetName = path.basename(localPath);
await window.withProgress(
opts,
(progress, token) => this.model.git.clone(url, { parentPath, progress }, token)
(progress, token) => this.model.git.clone(url, { parentPath, targetName, progress, ref }, token)
);
}
@command('_git.checkout')
async checkoutRepository(repositoryPath: string, treeish: string, detached?: boolean): Promise<void> {
const dotGit = await this.git.getRepositoryDotGit(repositoryPath);
const repo = new GitRepository(this.git, repositoryPath, undefined, dotGit, this.logger);
await repo.checkout(treeish, [], detached ? { detached: true } : {});
}
@command('_git.pull')
async pullRepository(repositoryPath: string): Promise<void> {
const dotGit = await this.git.getRepositoryDotGit(repositoryPath);

View File

@@ -378,6 +378,7 @@ const STASH_FORMAT = '%H%n%P%n%gd%n%gs%n%at%n%ct';
export interface ICloneOptions {
readonly parentPath: string;
readonly targetName?: string;
readonly progress: Progress<{ increment: number }>;
readonly recursive?: boolean;
readonly ref?: string;
@@ -433,14 +434,16 @@ export class Git {
}
async clone(url: string, options: ICloneOptions, cancellationToken?: CancellationToken): Promise<string> {
const baseFolderName = decodeURI(url).replace(/[\/]+$/, '').replace(/^.*[\/\\]/, '').replace(/\.git$/, '') || 'repository';
const baseFolderName = options.targetName || decodeURI(url).replace(/[\/]+$/, '').replace(/^.*[\/\\]/, '').replace(/\.git$/, '') || 'repository';
let folderName = baseFolderName;
let folderPath = path.join(options.parentPath, folderName);
let count = 1;
while (count < 20 && await new Promise(c => exists(folderPath, c))) {
folderName = `${baseFolderName}-${count++}`;
folderPath = path.join(options.parentPath, folderName);
if (!options.targetName) {
while (count < 20 && await new Promise(c => exists(folderPath, c))) {
folderName = `${baseFolderName}-${count++}`;
folderPath = path.join(options.parentPath, folderName);
}
}
await mkdirp(options.parentPath);