// ***************************************************************************** // Copyright (C) 2024 EclipseSource GmbH. // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License v. 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0. // // This Source Code may also be made available under the following Secondary // Licenses when the conditions for such availability set forth in the Eclipse // Public License v. 2.0 are satisfied: GNU General Public License, version 2 // with the GNU Classpath Exception which is available at // https://www.gnu.org/software/classpath/license.html. // // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** import { FrontendApplicationContribution } from '@theia/core/lib/browser'; import { inject, injectable } from '@theia/core/shared/inversify'; import { CUSTOM_AGENT_WORKSPACE_DIRECTORIES, DefaultPromptFragmentCustomizationService, PromptFragmentCustomizationProperties } from '@theia/ai-core/lib/browser/frontend-prompt-customization-service'; import { PROMPT_TEMPLATE_WORKSPACE_DIRECTORIES_PREF, PROMPT_TEMPLATE_ADDITIONAL_EXTENSIONS_PREF, PROMPT_TEMPLATE_WORKSPACE_FILES_PREF } from '../common/workspace-preferences'; import { WorkspaceService } from '@theia/workspace/lib/browser'; import { Path, PreferenceService } from '@theia/core'; import { WorkspaceTrustService } from '@theia/workspace/lib/browser/workspace-trust-service'; @injectable() export class TemplatePreferenceContribution implements FrontendApplicationContribution { @inject(PreferenceService) protected readonly preferenceService: PreferenceService; @inject(DefaultPromptFragmentCustomizationService) protected readonly customizationService: DefaultPromptFragmentCustomizationService; @inject(WorkspaceService) protected readonly workspaceService: WorkspaceService; @inject(WorkspaceTrustService) protected readonly workspaceTrustService: WorkspaceTrustService; onStart(): void { Promise.all([ this.preferenceService.ready, this.workspaceService.ready, this.workspaceTrustService.getWorkspaceTrust() ]).then(() => { // Set initial template configuration from preferences this.updateConfiguration(); // Listen for preference changes this.preferenceService.onPreferenceChanged(event => { if (event.preferenceName === PROMPT_TEMPLATE_WORKSPACE_DIRECTORIES_PREF || event.preferenceName === PROMPT_TEMPLATE_ADDITIONAL_EXTENSIONS_PREF || event.preferenceName === PROMPT_TEMPLATE_WORKSPACE_FILES_PREF) { this.updateConfiguration(event.preferenceName); } }); // Listen for workspace root changes this.workspaceService.onWorkspaceLocationChanged(() => { this.updateConfiguration(); }); // Listen for trust changes to reload/unload workspace templates this.workspaceTrustService.onDidChangeWorkspaceTrust(() => { this.updateConfiguration(); }); }); } /** * Updates the template configuration in the customization service. * If a specific preference name is provided, only that configuration aspect is updated. * @param changedPreference Optional name of the preference that changed */ protected async updateConfiguration(changedPreference?: string): Promise { const workspaceRoots = this.workspaceService.tryGetRoots(); if (workspaceRoots.length === 0) { return; } const trusted = await this.workspaceTrustService.getWorkspaceTrust(); const configProperties: PromptFragmentCustomizationProperties = {}; if (!changedPreference || changedPreference === PROMPT_TEMPLATE_WORKSPACE_DIRECTORIES_PREF) { if (trusted) { const relativeDirectories = this.preferenceService.get(PROMPT_TEMPLATE_WORKSPACE_DIRECTORIES_PREF, []); configProperties.directoryPaths = workspaceRoots.flatMap(root => relativeDirectories.map(dir => { const path = new Path(dir); const uri = root.resource.resolve(path.toString()); return uri.path.toString(); }) ); } else { configProperties.directoryPaths = []; } } if (!changedPreference || changedPreference === PROMPT_TEMPLATE_ADDITIONAL_EXTENSIONS_PREF) { if (trusted) { configProperties.extensions = this.preferenceService.get(PROMPT_TEMPLATE_ADDITIONAL_EXTENSIONS_PREF, []); } else { configProperties.extensions = []; } } if (!changedPreference || changedPreference === PROMPT_TEMPLATE_WORKSPACE_FILES_PREF) { if (trusted) { const relativeFilePaths = this.preferenceService.get(PROMPT_TEMPLATE_WORKSPACE_FILES_PREF, []); configProperties.filePaths = workspaceRoots.flatMap(root => relativeFilePaths.map(filePath => { const path = new Path(filePath); const uri = root.resource.resolve(path.toString()); return uri.path.toString(); }) ); } else { configProperties.filePaths = []; } } // The built-in custom-agent directories (`.agents`/`.prompts`) depend only on the workspace // roots and trust state, not on any preference, so they are recomputed on every full update. if (!changedPreference) { if (trusted) { configProperties.agentDirectoryPaths = workspaceRoots.flatMap(root => CUSTOM_AGENT_WORKSPACE_DIRECTORIES.map(dir => root.resource.resolve(dir).path.toString()) ); } else { configProperties.agentDirectoryPaths = []; } } await this.customizationService.updateConfiguration(configProperties); } }