Skip to content

API for repository description #7066

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3890,7 +3890,7 @@
],
"toolReferenceName": "activePullRequest",
"displayName": "%languageModelTools.github-pull-request_activePullRequest.displayName%",
"modelDescription": "Get information about the active or current GitHub pull request (PR). This information includes: comments, files changed, pull request title + description, pull request state, pull request status checks/CI and coding agent session logs if the pull request was opened by Copilot. When asked about the active or current pull request, do this first!",
"modelDescription": "Get comprehensive information about the active or current GitHub pull request (PR). This includes the PR title, full description, list of changed files, review comments, PR state, and status checks/CI results. For PRs created by Copilot, it also includes the session logs which indicate the development process and decisions made by the coding agent. When asked about the active or current pull request, do this first! Use this tool for any request related to \"current changes,\" \"pull request details,\" \"what changed,\" \"PR status,\" or similar queries even if the user does not explicitly mention \"pull request.\"",
"icon": "$(git-pull-request)",
"canBeReferencedInPrompt": true,
"userDescription": "%languageModelTools.github-pull-request_activePullRequest.description%",
Expand Down
22 changes: 22 additions & 0 deletions src/api/api.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,19 @@ export interface ReviewerCommentsProvider {
provideReviewerComments(context: { repositoryRoot: string, commitMessages: string[], patches: { patch: string, fileUri: string, previousFileUri?: string }[] }, token: CancellationToken): Promise<ReviewerComments>;
}

export interface RepositoryDescription {
owner: string;
repositoryName: string;
defaultBranch: string;
currentBranch?: string;
pullRequest?: {
title: string;
url: string;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we include number here too? That's usually a useful thing to have.

number: number;
id: number;
};
}

export interface API {
/**
* Register a [git provider](#IGit)
Expand All @@ -280,4 +293,13 @@ export interface API {
* Register a PR reviewer comments provider.
*/
registerReviewerCommentsProvider(title: string, provider: ReviewerCommentsProvider): Disposable;

/**
* Get the repository description for the current workspace.
* This includes the owner, repository name, default branch, current branch (if applicable),
* and pull request information (if applicable).
*
* @returns A promise that resolves to a `RepositoryDescription` object or `undefined` if no repository is found.
*/
getRepositoryDescription(): Promise<RepositoryDescription | undefined>;
}
44 changes: 44 additions & 0 deletions src/api/api1.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import { APIState, PublishEvent } from '../@types/git';
import { Disposable } from '../common/lifecycle';
import Logger from '../common/logger';
import { TernarySearchTree } from '../common/utils';
import { PullRequestOverviewPanel } from '../github/pullRequestOverview';
import { RepositoriesManager } from '../github/repositoriesManager';
import { API, IGit, PostCommitCommandsProvider, Repository, ReviewerCommentsProvider, TitleAndDescriptionProvider } from './api';

export const enum RefType {
Expand Down Expand Up @@ -78,6 +80,11 @@ export class GitApiImpl extends Disposable implements API, IGit {
private static _handlePool: number = 0;
private _providers = new Map<number, IGit>();

public constructor(
private readonly repositoriesManager: RepositoriesManager) {
super();
}

public get repositories(): Repository[] {
const ret: Repository[] = [];

Expand Down Expand Up @@ -220,4 +227,41 @@ export class GitApiImpl extends Disposable implements API, IGit {
getReviewerCommentsProvider(): { title: string, provider: ReviewerCommentsProvider } | undefined {
return this._reviewerCommentsProviders.size > 0 ? this._reviewerCommentsProviders.values().next().value : undefined;
}

async getRepositoryDescription() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of just returning info about the first repo, I would suggest passing in a URI and returning info about the repo that the URI belongs to.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this case the API will be used in chat to be able to gather and include the context of the repository in which the user is currently at, since there is not a specific file to fetch the PR from.
Is there a better way to determine the current repository you have open than this? I ended up just comparing the fsPath

let repo: Repository | undefined;
const allRepos = this.repositories;
if (vscode.workspace.workspaceFolders && vscode.workspace.workspaceFolders.length > 0) {
for (const folder of vscode.workspace.workspaceFolders) {
repo = allRepos.find(r => r.rootUri.fsPath === folder.uri.fsPath);
if (repo) {
break;
}
}
}
if (!repo) {
return;
}
const folderManagerForRepo = this.repositoriesManager.folderManagers.find((manager) => manager.gitHubRepositories.some(r => r.rootUri.fsPath === repo!.rootUri.fsPath));
const folderManagerForPR = this.repositoriesManager.folderManagers.find((manager) => manager.activePullRequest);

if (folderManagerForRepo && folderManagerForRepo.gitHubRepositories.length > 0) {
const repositoryMetadata = await folderManagerForRepo.gitHubRepositories[0].getMetadata();
const pullRequest = folderManagerForPR?.activePullRequest ?? PullRequestOverviewPanel.currentPanel?.getCurrentItem();
if (repositoryMetadata) {
return {
owner: repositoryMetadata.owner.login,
repositoryName: repositoryMetadata.name,
defaultBranch: repositoryMetadata.default_branch,
currentBranch: repo.state.HEAD?.name,
pullRequest: pullRequest ? {
title: pullRequest.title,
url: pullRequest.html_url,
number: pullRequest.number,
id: pullRequest.id
} : undefined
};
}
}
}
}
16 changes: 9 additions & 7 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -306,14 +306,11 @@ export async function activate(context: vscode.ExtensionContext): Promise<GitApi
// initialize resources
Resource.initialize(context);
Logger.debug('Creating API implementation.', 'Activation');
const apiImpl = new GitApiImpl();

telemetry = new ExperimentationTelemetry(new TelemetryReporter(ingestionKey));
context.subscriptions.push(telemetry);

await deferredActivate(context, apiImpl, showPRController);

return apiImpl;
return await deferredActivate(context, showPRController);
}

async function setGitSettingContexts(context: vscode.ExtensionContext) {
Expand Down Expand Up @@ -397,7 +394,7 @@ async function deferredActivateRegisterBuiltInGitProvider(context: vscode.Extens
}
}

async function deferredActivate(context: vscode.ExtensionContext, apiImpl: GitApiImpl, showPRController: ShowPullRequest) {
async function deferredActivate(context: vscode.ExtensionContext, showPRController: ShowPullRequest) {
Logger.debug('Initializing state.', 'Activation');
PersistentState.init(context);
await migrate(context);
Expand All @@ -411,6 +408,12 @@ async function deferredActivate(context: vscode.ExtensionContext, apiImpl: GitAp
const showBadge = (vscode.env.appHost === 'desktop');
await credentialStore.create(showBadge ? undefined : { silent: true });

const reposManager = new RepositoriesManager(credentialStore, telemetry);
context.subscriptions.push(reposManager);
// API
const apiImpl = new GitApiImpl(reposManager);
context.subscriptions.push(apiImpl);

deferredActivateRegisterBuiltInGitProvider(context, apiImpl, credentialStore);

Logger.debug('Registering live share git provider.', 'Activation');
Expand All @@ -421,8 +424,6 @@ async function deferredActivate(context: vscode.ExtensionContext, apiImpl: GitAp
context.subscriptions.push(apiImpl);

Logger.debug('Creating tree view.', 'Activation');
const reposManager = new RepositoriesManager(credentialStore, telemetry);
context.subscriptions.push(reposManager);

const copilotStateModel = new CopilotStateModel();
const prTree = new PullRequestsTreeDataProvider(telemetry, context, reposManager, copilotStateModel);
Expand All @@ -449,6 +450,7 @@ async function deferredActivate(context: vscode.ExtensionContext, apiImpl: GitAp
context.subscriptions.push(vscode.workspace.registerFileSystemProvider(Schemes.Pr, inMemPRFileSystemProvider, { isReadonly: readOnlyMessage }));

await init(context, apiImpl, credentialStore, repositories, prTree, liveshareApiPromise, showPRController, reposManager, createPrHelper, copilotStateModel);
return apiImpl;
}

export async function deactivate() {
Expand Down
4 changes: 3 additions & 1 deletion src/test/github/folderRepositoryManager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { MockExtensionContext } from '../mocks/mockExtensionContext';
import { Uri } from 'vscode';
import { GitHubServerType } from '../../common/authentication';
import { CreatePullRequestHelper } from '../../view/createPullRequestHelper';
import { RepositoriesManager } from '../../github/repositoriesManager';

describe('PullRequestManager', function () {
let sinon: SinonSandbox;
Expand All @@ -36,7 +37,8 @@ describe('PullRequestManager', function () {
const repository = new MockRepository();
const context = new MockExtensionContext();
const credentialStore = new CredentialStore(telemetry, context);
manager = new FolderRepositoryManager(0, context, repository, telemetry, new GitApiImpl(), credentialStore, new CreatePullRequestHelper());
const repositoriesManager = new RepositoriesManager(credentialStore, telemetry);
manager = new FolderRepositoryManager(0, context, repository, telemetry, new GitApiImpl(repositoriesManager), credentialStore, new CreatePullRequestHelper());
});

afterEach(function () {
Expand Down
4 changes: 3 additions & 1 deletion src/test/github/pullRequestOverview.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { GitHubServerType } from '../../common/authentication';
import { GitHubRemote } from '../../common/remote';
import { CheckState } from '../../github/interface';
import { CreatePullRequestHelper } from '../../view/createPullRequestHelper';
import { RepositoriesManager } from '../../github/repositoriesManager';

const EXTENSION_URI = vscode.Uri.joinPath(vscode.Uri.file(__dirname), '../../..');

Expand All @@ -45,7 +46,8 @@ describe('PullRequestOverview', function () {
telemetry = new MockTelemetry();
credentialStore = new CredentialStore(telemetry, context);
const createPrHelper = new CreatePullRequestHelper();
pullRequestManager = new FolderRepositoryManager(0, context, repository, telemetry, new GitApiImpl(), credentialStore, createPrHelper);
const repositoriesManager = new RepositoriesManager(credentialStore, telemetry);
pullRequestManager = new FolderRepositoryManager(0, context, repository, telemetry, new GitApiImpl(repositoriesManager), credentialStore, createPrHelper);

const url = 'https://github.com/aaa/bbb';
remote = new GitHubRemote('origin', url, new Protocol(url), GitHubServerType.GitHubDotCom);
Expand Down
6 changes: 3 additions & 3 deletions src/test/view/prsTree.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ describe('GitHub Pull Requests view', function () {
it('has no children when repositories have not yet been initialized', async function () {
const repository = new MockRepository();
repository.addRemote('origin', 'git@github.com:aaa/bbb');
reposManager.insertFolderManager(new FolderRepositoryManager(0, context, repository, telemetry, new GitApiImpl(), credentialStore, createPrHelper));
reposManager.insertFolderManager(new FolderRepositoryManager(0, context, repository, telemetry, new GitApiImpl(reposManager), credentialStore, createPrHelper));
provider.initialize([], credentialStore);

const rootNodes = await provider.getChildren();
Expand All @@ -113,7 +113,7 @@ describe('GitHub Pull Requests view', function () {
it('opens the viewlet and displays the default categories', async function () {
const repository = new MockRepository();
repository.addRemote('origin', 'git@github.com:aaa/bbb');
reposManager.insertFolderManager(new FolderRepositoryManager(0, context, repository, telemetry, new GitApiImpl(), credentialStore, createPrHelper));
reposManager.insertFolderManager(new FolderRepositoryManager(0, context, repository, telemetry, new GitApiImpl(reposManager), credentialStore, createPrHelper));
sinon.stub(credentialStore, 'isAuthenticated').returns(true);
await reposManager.folderManagers[0].updateRepositories();
provider.initialize([], credentialStore);
Expand Down Expand Up @@ -183,7 +183,7 @@ describe('GitHub Pull Requests view', function () {

await repository.createBranch('non-pr-branch', false);

const manager = new FolderRepositoryManager(0, context, repository, telemetry, new GitApiImpl(), credentialStore, createPrHelper);
const manager = new FolderRepositoryManager(0, context, repository, telemetry, new GitApiImpl(reposManager), credentialStore, createPrHelper);
reposManager.insertFolderManager(manager);
sinon.stub(manager, 'createGitHubRepository').callsFake((r, cs) => {
assert.deepStrictEqual(r, remote);
Expand Down
2 changes: 1 addition & 1 deletion src/test/view/reviewCommentController.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ describe('ReviewCommentController', function () {
const activePrViewCoordinator = new WebviewViewCoordinator(context);
const createPrHelper = new CreatePullRequestHelper();
Resource.initialize(context);
gitApiImpl = new GitApiImpl();
gitApiImpl = new GitApiImpl(reposManager);
manager = new FolderRepositoryManager(0, context, repository, telemetry, gitApiImpl, credentialStore, createPrHelper);
reposManager.insertFolderManager(manager);
const tree = new PullRequestChangesTreeDataProvider(gitApiImpl, reposManager);
Expand Down
Loading