Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/contrib/chat/common/promptSyntax/chatPromptFilesContribution.ts
4780 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { localize } from '../../../../../nls.js';
7
import { IWorkbenchContribution } from '../../../../common/contributions.js';
8
import * as extensionsRegistry from '../../../../services/extensions/common/extensionsRegistry.js';
9
import { ExtensionIdentifier } from '../../../../../platform/extensions/common/extensions.js';
10
import { joinPath, isEqualOrParent } from '../../../../../base/common/resources.js';
11
import { IPromptsService } from './service/promptsService.js';
12
import { PromptsType } from './promptTypes.js';
13
import { DisposableMap } from '../../../../../base/common/lifecycle.js';
14
15
interface IRawChatFileContribution {
16
readonly path: string;
17
readonly name?: string;
18
readonly description?: string;
19
}
20
21
type ChatContributionPoint = 'chatPromptFiles' | 'chatInstructions' | 'chatAgents';
22
23
function registerChatFilesExtensionPoint(point: ChatContributionPoint) {
24
return extensionsRegistry.ExtensionsRegistry.registerExtensionPoint<IRawChatFileContribution[]>({
25
extensionPoint: point,
26
jsonSchema: {
27
description: localize('chatContribution.schema.description', 'Contributes {0} for chat prompts.', point),
28
type: 'array',
29
items: {
30
additionalProperties: false,
31
type: 'object',
32
defaultSnippets: [{
33
body: {
34
path: './relative/path/to/file.md',
35
}
36
}],
37
required: ['path'],
38
properties: {
39
path: {
40
description: localize('chatContribution.property.path', 'Path to the file relative to the extension root.'),
41
type: 'string'
42
},
43
name: {
44
description: localize('chatContribution.property.name', '(Optional) Name for this entry.'),
45
deprecationMessage: localize('chatContribution.property.name.deprecated', 'Specify "name" in the prompt file itself instead.'),
46
type: 'string'
47
},
48
description: {
49
description: localize('chatContribution.property.description', '(Optional) Description of the entry.'),
50
deprecationMessage: localize('chatContribution.property.description.deprecated', 'Specify "description" in the prompt file itself instead.'),
51
type: 'string'
52
}
53
}
54
}
55
}
56
});
57
}
58
59
const epPrompt = registerChatFilesExtensionPoint('chatPromptFiles');
60
const epInstructions = registerChatFilesExtensionPoint('chatInstructions');
61
const epAgents = registerChatFilesExtensionPoint('chatAgents');
62
63
function pointToType(contributionPoint: ChatContributionPoint): PromptsType {
64
switch (contributionPoint) {
65
case 'chatPromptFiles': return PromptsType.prompt;
66
case 'chatInstructions': return PromptsType.instructions;
67
case 'chatAgents': return PromptsType.agent;
68
}
69
}
70
71
function key(extensionId: ExtensionIdentifier, type: PromptsType, path: string) {
72
return `${extensionId.value}/${type}/${path}`;
73
}
74
75
export class ChatPromptFilesExtensionPointHandler implements IWorkbenchContribution {
76
public static readonly ID = 'workbench.contrib.chatPromptFilesExtensionPointHandler';
77
78
private readonly registrations = new DisposableMap<string>();
79
80
constructor(
81
@IPromptsService private readonly promptsService: IPromptsService,
82
) {
83
this.handle(epPrompt, 'chatPromptFiles');
84
this.handle(epInstructions, 'chatInstructions');
85
this.handle(epAgents, 'chatAgents');
86
}
87
88
private handle(extensionPoint: extensionsRegistry.IExtensionPoint<IRawChatFileContribution[]>, contributionPoint: ChatContributionPoint) {
89
extensionPoint.setHandler((_extensions, delta) => {
90
for (const ext of delta.added) {
91
const type = pointToType(contributionPoint);
92
for (const raw of ext.value) {
93
if (!raw.path) {
94
ext.collector.error(localize('extension.missing.path', "Extension '{0}' cannot register {1} entry without path.", ext.description.identifier.value, contributionPoint));
95
continue;
96
}
97
const fileUri = joinPath(ext.description.extensionLocation, raw.path);
98
if (!isEqualOrParent(fileUri, ext.description.extensionLocation)) {
99
ext.collector.error(localize('extension.invalid.path', "Extension '{0}' {1} entry '{2}' resolves outside the extension.", ext.description.identifier.value, contributionPoint, raw.path));
100
continue;
101
}
102
try {
103
const d = this.promptsService.registerContributedFile(type, fileUri, ext.description, raw.name, raw.description);
104
this.registrations.set(key(ext.description.identifier, type, raw.path), d);
105
} catch (e) {
106
const msg = e instanceof Error ? e.message : String(e);
107
ext.collector.error(localize('extension.registration.failed', "Extension '{0}' {1}. Failed to register {2}: {3}", ext.description.identifier.value, contributionPoint, raw.path, msg));
108
}
109
}
110
}
111
for (const ext of delta.removed) {
112
const type = pointToType(contributionPoint);
113
for (const raw of ext.value) {
114
this.registrations.deleteAndDispose(key(ext.description.identifier, type, raw.path));
115
}
116
}
117
});
118
}
119
}
120
121