Path: blob/main/extensions/copilot/src/extension/agents/vscode-node/githubOrgInstructionsProvider.ts
13399 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import * as vscode from 'vscode';6import { INSTRUCTION_FILE_EXTENSION, PromptsType } from '../../../platform/customInstructions/common/promptTypes';7import { IOctoKitService } from '../../../platform/github/common/githubService';8import { ILogService } from '../../../platform/log/common/logService';9import { Disposable } from '../../../util/vs/base/common/lifecycle';10import { IGitHubOrgChatResourcesService } from './githubOrgChatResourcesService';1112const INSTRUCTIONS_BASE_FILE_NAME = 'default';13const REFRESH_INTERVAL_MS = 2 * 60 * 1000;1415export class GitHubOrgInstructionsProvider extends Disposable implements vscode.ChatInstructionsProvider {1617private readonly _onDidChangeInstructions = this._register(new vscode.EventEmitter<void>());18readonly onDidChangeInstructions = this._onDidChangeInstructions.event;1920constructor(21@ILogService private readonly logService: ILogService,22@IOctoKitService private readonly octoKitService: IOctoKitService,23@IGitHubOrgChatResourcesService private readonly githubOrgChatResourcesService: IGitHubOrgChatResourcesService,24) {25super();2627// Set up polling with provider-specific interval28this._register(this.githubOrgChatResourcesService.startPolling(REFRESH_INTERVAL_MS, this.pollInstructions.bind(this)));29}3031async provideInstructions(32_options: unknown,33token: vscode.CancellationToken34): Promise<vscode.ChatResource[]> {35try {36const orgId = await this.githubOrgChatResourcesService.getPreferredOrganizationName();37if (!orgId) {38this.logService.trace('[GitHubOrgInstructionsProvider] No organization available for providing agents');39return [];40}4142if (token.isCancellationRequested) {43this.logService.trace('[GitHubOrgInstructionsProvider] provideCustomAgents was cancelled');44return [];45}4647return await this.githubOrgChatResourcesService.listCachedFiles(PromptsType.instructions, orgId);48} catch (error) {49this.logService.error(`[GitHubOrgInstructionsProvider] Error reading from cache: ${error}`);50return [];51}52}5354private async pollInstructions(orgId: string): Promise<void> {55try {56const instructions = await this.octoKitService.getOrgCustomInstructions(orgId, {});57if (!instructions) {58await this.githubOrgChatResourcesService.clearCache(PromptsType.instructions, orgId);59this.logService.trace(`[GitHubOrgInstructionsProvider] No custom instructions found for org ${orgId}`);60return;61}6263// Write the instructions to cache64const fileName = `${INSTRUCTIONS_BASE_FILE_NAME}${INSTRUCTION_FILE_EXTENSION}`;65const content = `---66applyTo: '**'67---68${instructions}`;69const contentChanged = await this.githubOrgChatResourcesService.writeCacheFile(PromptsType.instructions, orgId, fileName, content, { checkForChanges: true });7071// If no changes, we can return72if (!contentChanged) {73this.logService.trace(`[GitHubOrgInstructionsProvider] No changes detected in cache for org ${orgId}`);74return;75}7677// Otherwise, fire event to notify consumers that instructions have changed78this._onDidChangeInstructions.fire();79} catch (error) {80this.logService.error(`[GitHubOrgInstructionsProvider] Error polling for instructions: ${error}`);81}82}83}848586