diff --git a/package.json b/package.json index 55442fb2a6..8735d7c600 100644 --- a/package.json +++ b/package.json @@ -2612,6 +2612,12 @@ "title": "%github.copilot.command.checkoutPullRequestReroute.title%", "icon": "$(git-pull-request)", "category": "GitHub Pull Request" + }, + { + "command": "github.copilot.chat.cloudSessions.openRepository", + "title": "%github.copilot.command.cloudSessions.openRepository.title%", + "icon": "$(repo)", + "category": "GitHub Copilot" } ], "configuration": [ @@ -4497,6 +4503,10 @@ "command": "github.copilot.chat.checkoutPullRequestReroute", "when": "false" }, + { + "command": "github.copilot.chat.cloudSessions.openRepository", + "when": "false" + }, { "command": "github.copilot.nes.captureExpected.start", "when": "github.copilot.inlineEditsEnabled" diff --git a/package.nls.json b/package.nls.json index e344699ffe..8f8f501fb2 100644 --- a/package.nls.json +++ b/package.nls.json @@ -381,6 +381,7 @@ "github.copilot.command.installPRExtension.title": "Install GitHub Pull Request Extension", "github.copilot.chat.applyCopilotCLIAgentSessionChanges.apply": "Apply", "github.copilot.command.checkoutPullRequestReroute.title": "Checkout", + "github.copilot.command.cloudSessions.openRepository.title": "Browse repositories...", "github.copilot.command.applyCopilotCLIAgentSessionChanges": "Apply Changes to Workspace", "github.copilot.config.githubMcpServer.enabled": "Enable built-in support for the GitHub MCP Server.", "github.copilot.config.githubMcpServer.toolsets": "Specify toolsets to use from the GitHub MCP Server. [Learn more](https://aka.ms/vscode-gh-mcp-toolsets).", diff --git a/src/extension/chatSessions/vscode-node/copilotCloudSessionsProvider.ts b/src/extension/chatSessions/vscode-node/copilotCloudSessionsProvider.ts index 52f117698d..166e445a0d 100644 --- a/src/extension/chatSessions/vscode-node/copilotCloudSessionsProvider.ts +++ b/src/extension/chatSessions/vscode-node/copilotCloudSessionsProvider.ts @@ -62,6 +62,7 @@ const DEFAULT_REPOSITORY_ID = '___vscode_repository_default___'; const ACTIVE_SESSION_POLL_INTERVAL_MS = 5 * 1000; // 5 seconds const SEEN_DELEGATION_PROMPT_KEY = 'seenDelegationPromptBefore'; +const OPEN_REPOSITORY_COMMAND_ID = 'github.copilot.chat.cloudSessions.openRepository'; // TODO: No API from GH yet. const HARDCODED_PARTNER_AGENTS: { id: string; name: string; at?: string; assignableActorLogin?: string }[] = [ @@ -144,6 +145,8 @@ export class CopilotCloudSessionsProvider extends Disposable implements vscode.C public readonly onDidCommitChatSessionItem = this._onDidCommitChatSessionItem.event; private readonly _onDidChangeChatSessionProviderOptions = this._register(new vscode.EventEmitter()); public readonly onDidChangeChatSessionProviderOptions = this._onDidChangeChatSessionProviderOptions.event; + private readonly _onDidChangeChatSessionOptions = this._register(new vscode.EventEmitter()); + public readonly onDidChangeChatSessionOptions = this._onDidChangeChatSessionOptions.event; private chatSessions: Map = new Map(); private chatSessionItemsPromise: Promise | undefined; private readonly sessionCustomAgentMap = new ResourceMap(); @@ -295,6 +298,68 @@ export class CopilotCloudSessionsProvider extends Disposable implements vscode.C } }; this._register(vscode.commands.registerCommand('github.copilot.chat.checkoutPullRequestReroute', checkoutPullRequestReroute)); + + // Command for browsing repositories in the repository picker + const openRepositoryCommand = async (sessionItemResource?: vscode.Uri) => { + const quickPick = vscode.window.createQuickPick(); + quickPick.placeholder = l10n.t('Search for a repository...'); + quickPick.matchOnDescription = true; + quickPick.matchOnDetail = true; + quickPick.busy = true; + quickPick.show(); + + // Load initial repositories + try { + const repos = await this.fetchAllRepositoriesFromGitHub(); + quickPick.items = repos.map(repo => ({ label: repo.name })); + } catch (error) { + this.logService.error(`Error fetching initial repositories: ${error}`); + } finally { + quickPick.busy = false; + } + + // Handle dynamic search + let searchTimeout: ReturnType | undefined; + const onDidChangeValueDisposable = quickPick.onDidChangeValue(async (value) => { + if (searchTimeout) { + clearTimeout(searchTimeout); + } + searchTimeout = setTimeout(async () => { + quickPick.busy = true; + try { + const searchResults = await this.fetchAllRepositoriesFromGitHub(value); + quickPick.items = searchResults.map(repo => ({ label: repo.name })); + } finally { + quickPick.busy = false; + } + }, 300); + }); + + const onDidAcceptDisposable = quickPick.onDidAccept(() => { + const selected = quickPick.selectedItems[0]; + if (selected && sessionItemResource) { + this.sessionRepositoryMap.set(sessionItemResource, selected.label); + this._onDidChangeChatSessionOptions.fire({ + resource: sessionItemResource, + updates: [{ + optionId: REPOSITORIES_OPTION_GROUP_ID, + value: { id: selected.label, name: selected.label, icon: new vscode.ThemeIcon('repo') } + }] + }); + } + quickPick.hide(); + }); + + quickPick.onDidHide(() => { + if (searchTimeout) { + clearTimeout(searchTimeout); + } + onDidChangeValueDisposable.dispose(); + onDidAcceptDisposable.dispose(); + quickPick.dispose(); + }); + }; + this._register(vscode.commands.registerCommand(OPEN_REPOSITORY_COMMAND_ID, openRepositoryCommand)); } private getRefreshIntervalTime(hasHistoricalSessions: boolean): number { @@ -514,12 +579,13 @@ export class CopilotCloudSessionsProvider extends Disposable implements vscode.C optionGroups.push({ id: REPOSITORIES_OPTION_GROUP_ID, name: vscode.l10n.t('Repository'), + description: vscode.l10n.t('Select repository'), icon: new vscode.ThemeIcon('repo'), items, - onSearch: async (query, token) => { - return await this.fetchAllRepositoriesFromGitHub(query); - }, - searchable: true + commands: [{ + command: OPEN_REPOSITORY_COMMAND_ID, + title: vscode.l10n.t('Browse repositories...'), + }] }); } @@ -546,6 +612,7 @@ export class CopilotCloudSessionsProvider extends Disposable implements vscode.C id: `${repoId.org}/${repoId.repo}`, name: `${repoId.org}/${repoId.repo}`, default: index === 0, + icon: new vscode.ThemeIcon('repo'), }); }); } else { @@ -557,6 +624,7 @@ export class CopilotCloudSessionsProvider extends Disposable implements vscode.C items.push({ id: nwo, name: nwo, + icon: new vscode.ThemeIcon('repo'), }); } } catch (error) { diff --git a/src/extension/vscode.proposed.chatSessionsProvider.d.ts b/src/extension/vscode.proposed.chatSessionsProvider.d.ts index a98d051b85..277b886b1c 100644 --- a/src/extension/vscode.proposed.chatSessionsProvider.d.ts +++ b/src/extension/vscode.proposed.chatSessionsProvider.d.ts @@ -529,6 +529,12 @@ declare module 'vscode' { * @returns Additional items to display in the searchable QuickPick. */ readonly onSearch?: (query: string, token: CancellationToken) => Thenable; + + /** + * Commands to display as actions in the option group dropdown. + * These commands are shown as clickable options in the picker UI. + */ + readonly commands?: Command[]; } export interface ChatSessionProviderOptions {