separate prompt and instructions

This commit is contained in:
Martin Aeschlimann
2025-04-10 16:14:02 +02:00
committed by Oleg Solomko
parent 46ff50ed05
commit 1631f03483
11 changed files with 134 additions and 146 deletions

View File

@@ -25,10 +25,10 @@ import { ISelectPromptOptions, askToSelectPrompt } from './dialogs/askToSelectPr
/**
* Action ID for the `Attach Prompt` action.
*/
const ATTACH_PROMPT_ACTION_ID = 'workbench.action.chat.attach.prompt';
const ATTACH_INSTRUCTIONS_ACTION_ID = 'workbench.action.chat.attach.instructions';
/**
* Options for the {@link AttachPromptAction} action.
* Options for the {@link AttachInstructionsAction} action.
*/
export interface IChatAttachPromptActionOptions extends Pick<
ISelectPromptOptions, 'resource' | 'widget'
@@ -51,11 +51,11 @@ export interface IChatAttachPromptActionOptions extends Pick<
/**
* Action to attach a prompt to a chat widget input.
*/
class AttachPromptAction extends Action2 {
class AttachInstructionsAction extends Action2 {
constructor() {
super({
id: ATTACH_PROMPT_ACTION_ID,
title: localize2('workbench.action.chat.attach.prompt.label', "Use Prompt"),
id: ATTACH_INSTRUCTIONS_ACTION_ID,
title: localize2('workbench.action.chat.attach.instructions.label', "Attach Instructions"),
f1: false,
precondition: ContextKeyExpr.and(PromptsConfig.enabledCtx, ChatContextKeys.enabled),
category: CHAT_CATEGORY,
@@ -100,10 +100,11 @@ class AttachPromptAction extends Action2 {
}
// find all prompt files in the user workspace
const promptFiles = await promptsService.listPromptFiles();
const promptFiles = await promptsService.listPromptFiles('instructions');
await askToSelectPrompt({
...options,
type: 'instructions',
promptFiles,
fileService,
viewsService,
@@ -118,7 +119,7 @@ class AttachPromptAction extends Action2 {
/**
* Runs the `Attach Prompt` action with provided options. We export this
* function instead of {@link ATTACH_PROMPT_ACTION_ID} directly to
* function instead of {@link ATTACH_INSTRUCTIONS_ACTION_ID} directly to
* encapsulate/enforce the correct options to be passed to the action.
*/
export const runAttachPromptAction = async (
@@ -126,7 +127,7 @@ export const runAttachPromptAction = async (
commandService: ICommandService,
): Promise<void> => {
return await commandService.executeCommand(
ATTACH_PROMPT_ACTION_ID,
ATTACH_INSTRUCTIONS_ACTION_ID,
options,
);
};
@@ -135,5 +136,5 @@ export const runAttachPromptAction = async (
* Helper to register the `Attach Prompt` action.
*/
export const registerAttachPromptActions = () => {
registerAction2(AttachPromptAction);
registerAction2(AttachInstructionsAction);
};

View File

@@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { DOCS_OPTION } from './constants.js';
import { PROMPT_DOCS_OPTION } from './constants.js';
import { IChatWidget } from '../../../../chat.js';
import { attachPrompt } from './utils/attachPrompt.js';
import { handleButtonClick } from './utils/handleButtonClick.js';
@@ -13,7 +13,7 @@ import { createPromptPickItem } from './utils/createPromptPickItem.js';
import { createPlaceholderText } from './utils/createPlaceholderText.js';
import { extUri } from '../../../../../../../../base/common/resources.js';
import { WithUriValue } from '../../../../../../../../base/common/types.js';
import { IPromptPath } from '../../../../../common/promptSyntax/service/types.js';
import { IPromptPath, TPromptsType } from '../../../../../common/promptSyntax/service/types.js';
import { DisposableStore } from '../../../../../../../../base/common/lifecycle.js';
import { IFileService } from '../../../../../../../../platform/files/common/files.js';
import { ILabelService } from '../../../../../../../../platform/label/common/label.js';
@@ -48,6 +48,11 @@ export interface ISelectPromptOptions {
*/
readonly promptFiles: readonly IPromptPath[];
/**
* Type of the prompt files to select.
*/
readonly type: TPromptsType;
readonly fileService: IFileService;
readonly labelService: ILabelService;
readonly viewsService: IViewsService;
@@ -66,7 +71,7 @@ export interface ISelectPromptOptions {
export const askToSelectPrompt = async (
options: ISelectPromptOptions,
): Promise<void> => {
const { promptFiles, resource, quickInputService, labelService } = options;
const { promptFiles, resource, quickInputService, labelService, type } = options;
const fileOptions = promptFiles.map((promptFile) => {
return createPromptPickItem(promptFile, labelService);
@@ -75,7 +80,7 @@ export const askToSelectPrompt = async (
/**
* Add a link to the documentation to the end of prompts list.
*/
fileOptions.push(DOCS_OPTION);
fileOptions.push(PROMPT_DOCS_OPTION);
// if a resource is provided, create an `activeItem` for it to pre-select
// it in the UI, and sort the list so the active item appears at the top
@@ -94,7 +99,8 @@ export const askToSelectPrompt = async (
uri: resource,
// "user" prompts are always registered in the prompts list, hence it
// should be safe to assume that `resource` is not "user" prompt here
type: 'local',
storage: 'local',
type,
}, labelService);
fileOptions.push(activeItem);
}
@@ -164,7 +170,7 @@ export const askToSelectPrompt = async (
const selectedOption = selectedItems[0];
// whether user selected the docs link option
const docsSelected = (selectedOption === DOCS_OPTION);
const docsSelected = (selectedOption === PROMPT_DOCS_OPTION);
// if documentation item was selected, open its link in a browser
if (docsSelected) {

View File

@@ -8,7 +8,7 @@ import { URI } from '../../../../../../../../base/common/uri.js';
import { Codicon } from '../../../../../../../../base/common/codicons.js';
import { WithUriValue } from '../../../../../../../../base/common/types.js';
import { ThemeIcon } from '../../../../../../../../base/common/themables.js';
import { DOCUMENTATION_URL } from '../../../../../common/promptSyntax/constants.js';
import { INSTRUCTIONS_DOCUMENTATION_URL, PROMPT_DOCUMENTATION_URL } from '../../../../../common/promptSyntax/constants.js';
import { isLinux, isWindows } from '../../../../../../../../base/common/platform.js';
import { IQuickInputButton, IQuickPickItem } from '../../../../../../../../platform/quickinput/common/quickInput.js';
@@ -25,15 +25,29 @@ export const ALT_KEY_NAME = (isWindows || isLinux) ? 'Alt' : '⌥';
/**
* A special quick pick item that links to the documentation.
*/
export const DOCS_OPTION: WithUriValue<IQuickPickItem> = Object.freeze({
export const PROMPT_DOCS_OPTION: WithUriValue<IQuickPickItem> = Object.freeze({
type: 'item',
label: localize(
'commands.prompts.use.select-dialog.docs-label',
'Learn how to create reusable prompts',
),
description: DOCUMENTATION_URL,
tooltip: DOCUMENTATION_URL,
value: URI.parse(DOCUMENTATION_URL),
description: PROMPT_DOCUMENTATION_URL,
tooltip: PROMPT_DOCUMENTATION_URL,
value: URI.parse(PROMPT_DOCUMENTATION_URL),
});
/**
* A special quick pick item that links to the documentation.
*/
export const INSTRUCTIONS_DOCS_OPTION: WithUriValue<IQuickPickItem> = Object.freeze({
type: 'item',
label: localize(
'commands.instructions.use.select-dialog.docs-label',
'Learn how to create custom instructions',
),
description: INSTRUCTIONS_DOCUMENTATION_URL,
tooltip: INSTRUCTIONS_DOCUMENTATION_URL,
value: URI.parse(INSTRUCTIONS_DOCUMENTATION_URL),
});
/**

View File

@@ -19,19 +19,19 @@ export const createPromptPickItem = (
promptFile: IPromptPath,
labelService: ILabelService,
): WithUriValue<IQuickPickItem> => {
const { uri, type } = promptFile;
const { uri, storage } = promptFile;
const fileWithoutExtension = getCleanPromptName(uri);
// if a "user" prompt, don't show its filesystem path in
// the user interface, but do that for all the "local" ones
const description = (type === 'user')
const description = (storage === 'user')
? localize(
'user-prompt.capitalized',
'User prompt',
)
: labelService.getUriLabel(dirname(uri), { relative: true });
const tooltip = (type === 'user')
const tooltip = (storage === 'user')
? description
: uri.fsPath;

View File

@@ -48,7 +48,7 @@ import { ILanguageModelIgnoredFilesService, LanguageModelIgnoredFilesService } f
import { ILanguageModelsService, LanguageModelsService } from '../common/languageModels.js';
import { ILanguageModelStatsService, LanguageModelStatsService } from '../common/languageModelStats.js';
import { ILanguageModelToolsService } from '../common/languageModelToolsService.js';
import { DOCUMENTATION_URL } from '../common/promptSyntax/constants.js';
import { PROMPT_DOCUMENTATION_URL } from '../common/promptSyntax/constants.js';
import { registerReusablePromptLanguageFeatures } from '../common/promptSyntax/languageFeatures/providers/index.js';
import { PromptsService } from '../common/promptSyntax/service/promptsService.js';
import { IPromptsService } from '../common/promptSyntax/service/types.js';
@@ -282,7 +282,7 @@ configurationRegistry.registerConfiguration({
'chat.reusablePrompts.config.enabled.description',
"Enable reusable prompt files (`*{0}`) in Chat, Edits, and Inline Chat sessions. [Learn More]({1}).",
PROMPT_FILE_EXTENSION,
DOCUMENTATION_URL,
PROMPT_DOCUMENTATION_URL,
),
default: true,
restricted: true,
@@ -306,7 +306,7 @@ configurationRegistry.registerConfiguration({
'chat.reusablePrompts.config.locations.description',
"Specify location(s) of reusable prompt files (`*{0}`) that can be attached in Chat, Edits, and Inline Chat sessions. [Learn More]({1}).\n\nRelative paths are resolved from the root folder(s) of your workspace.",
PROMPT_FILE_EXTENSION,
DOCUMENTATION_URL,
PROMPT_DOCUMENTATION_URL,
),
default: {
[PROMPT_FILES_DEFAULT_SOURCE_FOLDER]: true,

View File

@@ -17,7 +17,7 @@ import { PromptsConfig } from '../../../../../../../platform/prompts/common/conf
import { ICommandService } from '../../../../../../../platform/commands/common/commands.js';
import { ContextKeyExpr } from '../../../../../../../platform/contextkey/common/contextkey.js';
import { MenuId, MenuRegistry } from '../../../../../../../platform/actions/common/actions.js';
import { IPromptPath, IPromptsService } from '../../../../common/promptSyntax/service/types.js';
import { IPromptsService, TPromptsStorage, TPromptsType } from '../../../../common/promptSyntax/service/types.js';
import { IQuickInputService } from '../../../../../../../platform/quickinput/common/quickInput.js';
import { ServicesAccessor } from '../../../../../../../platform/instantiation/common/instantiation.js';
import { IWorkspaceContextService } from '../../../../../../../platform/workspace/common/workspace.js';
@@ -26,37 +26,13 @@ import { IUserDataSyncEnablementService, SyncResource } from '../../../../../../
import { KeybindingsRegistry, KeybindingWeight } from '../../../../../../../platform/keybinding/common/keybindingsRegistry.js';
import { INotificationService, NeverShowAgainScope, Severity } from '../../../../../../../platform/notification/common/notification.js';
/**
* Base command ID prefix.
*/
const BASE_COMMAND_ID = 'workbench.command.prompts.create';
/**
* Command ID for creating a 'local' prompt.
*/
const LOCAL_COMMAND_ID = `${BASE_COMMAND_ID}.local`;
/**
* Command ID for creating a 'user' prompt.
*/
const USER_COMMAND_ID = `${BASE_COMMAND_ID}.user`;
/**
* Title of the 'create local prompt' command.
*/
const LOCAL_COMMAND_TITLE = localize('commands.prompts.create.title.local', "Create Prompt");
/**
* Title of the 'create user prompt' command.
*/
const USER_COMMAND_TITLE = localize('commands.prompts.create.title.user', "Create User Prompt");
/**
* The command implementation.
*/
const command = async (
accessor: ServicesAccessor,
type: IPromptPath['type'],
type: TPromptsType,
storage: TPromptsStorage
): Promise<void> => {
const logService = accessor.get(ILogService);
const fileService = accessor.get(IFileService);
@@ -69,13 +45,14 @@ const command = async (
const workspaceService = accessor.get(IWorkspaceContextService);
const userDataSyncEnablementService = accessor.get(IUserDataSyncEnablementService);
const fileName = await askForPromptName(type, quickInputService);
const fileName = await askForPromptName(storage, quickInputService);
if (!fileName) {
return;
}
const selectedFolder = await askForPromptSourceFolder({
type: type,
storage: storage,
labelService,
openerService,
promptsService,
@@ -101,7 +78,7 @@ const command = async (
await openerService.open(promptUri);
if (type !== 'user') {
if (storage !== 'user') {
return;
}
@@ -148,55 +125,33 @@ const command = async (
);
};
/**
* Factory for creating the command handler with specific prompt `type`.
*/
const commandFactory = (type: 'local' | 'user') => {
return async (accessor: ServicesAccessor): Promise<void> => {
return command(accessor, type);
};
};
function register(type: TPromptsType, storage: TPromptsStorage, id: string, title: string) {
/**
* Register the command.
*/
KeybindingsRegistry.registerCommandAndKeybindingRule({
id,
weight: KeybindingWeight.WorkbenchContrib,
handler: async (accessor: ServicesAccessor): Promise<void> => {
return command(accessor, type, storage);
},
when: ContextKeyExpr.and(PromptsConfig.enabledCtx, ChatContextKeys.enabled),
});
/**
* Register the "Create Prompt" command.
*/
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: LOCAL_COMMAND_ID,
weight: KeybindingWeight.WorkbenchContrib,
handler: commandFactory('local'),
when: ContextKeyExpr.and(PromptsConfig.enabledCtx, ChatContextKeys.enabled),
});
/**
* Register the command in the command palette.
*/
MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
command: {
id,
title,
category: CHAT_CATEGORY
},
when: ContextKeyExpr.and(PromptsConfig.enabledCtx, ChatContextKeys.enabled)
});
}
/**
* Register the "Create User Prompt" command.
*/
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: USER_COMMAND_ID,
weight: KeybindingWeight.WorkbenchContrib,
handler: commandFactory('user'),
when: ContextKeyExpr.and(PromptsConfig.enabledCtx, ChatContextKeys.enabled),
});
/**
* Register the "Create Prompt" command in the command palette.
*/
MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
command: {
id: LOCAL_COMMAND_ID,
title: LOCAL_COMMAND_TITLE,
category: CHAT_CATEGORY
},
when: ContextKeyExpr.and(PromptsConfig.enabledCtx, ChatContextKeys.enabled)
});
/**
* Register the "Create User Prompt" command in the command palette.
*/
MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
command: {
id: USER_COMMAND_ID,
title: USER_COMMAND_TITLE,
category: CHAT_CATEGORY,
},
when: ContextKeyExpr.and(PromptsConfig.enabledCtx, ChatContextKeys.enabled)
});
register('instructions', 'local', 'workbench.command.instructions.create.local', localize('commands.instructions.create.title.local', "Create Instructions File"));
register('instructions', 'user', 'workbench.command.instructions.create.user', localize('commands.instructions.create.title.user', "Create User Instructions File"));
register('prompt', 'local', 'workbench.command.prompts.create.local', localize('commands.prompts.create.title.local', "Create Prompt File"));
register('prompt', 'user', 'workbench.command.prompts.create.user', localize('commands.prompts.create.title.user', "Create User Prompt File"));

View File

@@ -6,9 +6,9 @@
import { localize } from '../../../../../../../../nls.js';
import { URI } from '../../../../../../../../base/common/uri.js';
import { WithUriValue } from '../../../../../../../../base/common/types.js';
import { DOCUMENTATION_URL } from '../../../../../common/promptSyntax/constants.js';
import { PROMPT_DOCUMENTATION_URL } from '../../../../../common/promptSyntax/constants.js';
import { basename, extUri } from '../../../../../../../../base/common/resources.js';
import { IPromptsService } from '../../../../../common/promptSyntax/service/types.js';
import { IPromptsService, TPromptsStorage, TPromptsType } from '../../../../../common/promptSyntax/service/types.js';
import { ILabelService } from '../../../../../../../../platform/label/common/label.js';
import { IOpenerService } from '../../../../../../../../platform/opener/common/opener.js';
import { IWorkspaceContextService } from '../../../../../../../../platform/workspace/common/workspace.js';
@@ -18,10 +18,9 @@ import { IPickOptions, IQuickInputService, IQuickPickItem } from '../../../../..
* Options for {@link askForPromptSourceFolder} dialog.
*/
interface IAskForFolderOptions {
/**
* Prompt type.
*/
readonly type: 'local' | 'user';
readonly type: TPromptsType;
readonly storage: TPromptsStorage;
readonly labelService: ILabelService;
readonly openerService: IOpenerService;
@@ -37,10 +36,10 @@ interface IAskForFolderOptions {
export const askForPromptSourceFolder = async (
options: IAskForFolderOptions,
): Promise<URI | undefined> => {
const { type, promptsService, quickInputService, labelService, openerService, workspaceService } = options;
const { storage, type, promptsService, quickInputService, labelService, openerService, workspaceService } = options;
// get prompts source folders based on the prompt type
const folders = promptsService.getSourceFolders(type);
const folders = promptsService.getSourceFolders(type, storage);
// if no source folders found, show 'learn more' dialog
// note! this is a temporary solution and must be replaced with a dialog to select
@@ -122,9 +121,9 @@ const showNoFoldersDialog = async (
'commands.prompts.create.ask-folder.empty.docs-label',
'Learn how to configure reusable prompts',
),
description: DOCUMENTATION_URL,
tooltip: DOCUMENTATION_URL,
value: URI.parse(DOCUMENTATION_URL),
description: PROMPT_DOCUMENTATION_URL,
tooltip: PROMPT_DOCUMENTATION_URL,
value: URI.parse(PROMPT_DOCUMENTATION_URL),
};
const result = await quickInputService.pick(

View File

@@ -22,19 +22,19 @@ import { IActiveCodeEditor, isCodeEditor, isDiffEditor } from '../../../../../..
import { KeybindingsRegistry, KeybindingWeight } from '../../../../../../platform/keybinding/common/keybindingsRegistry.js';
/**
* Command ID of the "Use Prompt" command.
* Command ID of the "Use Instructions" command.
*/
export const COMMAND_ID = 'workbench.command.prompts.use';
export const COMMAND_ID = 'workbench.command.instructions.use';
/**
* Keybinding of the "Use Prompt" command.
* Keybinding of the "Use Instructions" command.
* The `cmd + /` is the current keybinding for 'attachment', so we use
* the `alt` key modifier to convey the "prompt attachment" action.
* the `alt` key modifier to convey the "instructions attachment" action.
*/
const COMMAND_KEY_BINDING = KeyMod.CtrlCmd | KeyCode.Slash | KeyMod.Alt;
/**
* Implementation of the "Use Prompt" command. The command works in the following way.
* Implementation of the "Use Instructions" command. The command works in the following way.
*
* When executed, it tries to see if a `prompt file` was open in the active code editor
* (see {@link IChatAttachPromptActionOptions.resource resource}), and if a chat input
@@ -139,12 +139,12 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
});
/**
* Register the "Use Prompt" command in the `command palette`.
* Register the "Use Instructions" command in the `command palette`.
*/
MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
command: {
id: COMMAND_ID,
title: localize('commands.prompts.use.title', "Use Prompt"),
title: localize('commands.prompts.use.title', "Use Instructions"),
category: CHAT_CATEGORY
},
when: ContextKeyExpr.and(PromptsConfig.enabledCtx, ChatContextKeys.enabled)

View File

@@ -8,7 +8,8 @@ import { LanguageFilter } from '../../../../../editor/common/languageSelector.js
/**
* Documentation link for the reusable prompts feature.
*/
export const DOCUMENTATION_URL = 'https://aka.ms/vscode-ghcp-prompt-snippets';
export const PROMPT_DOCUMENTATION_URL = 'https://aka.ms/vscode-ghcp-prompt-snippets';
export const INSTRUCTIONS_DOCUMENTATION_URL = PROMPT_DOCUMENTATION_URL; // TODO: instructions specific link
/**
* Language ID for the reusable prompt syntax.

View File

@@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IPromptPath, IPromptsService } from './types.js';
import { IPromptPath, IPromptsService, TPromptsStorage, TPromptsType } from './types.js';
import { URI } from '../../../../../../base/common/uri.js';
import { assert } from '../../../../../../base/common/assert.js';
import { PromptFilesLocator } from '../utils/promptFilesLocator.js';
@@ -83,34 +83,35 @@ export class PromptsService extends Disposable implements IPromptsService {
return this.cache.get(model);
}
public async listPromptFiles(): Promise<readonly IPromptPath[]> {
public async listPromptFiles(type: TPromptsType): Promise<readonly IPromptPath[]> {
const userLocations = [this.userDataService.currentProfile.promptsHome];
const prompts = await Promise.all([
this.fileLocator.listFilesIn(userLocations)
.then(withType('user')),
.then(withType('user', type)),
this.fileLocator.listFiles()
.then(withType('local')),
.then(withType('local', type)),
]);
return prompts.flat();
}
public getSourceFolders(
type: IPromptPath['type'],
type: TPromptsType,
storage: TPromptsStorage,
): readonly IPromptPath[] {
// sanity check to make sure we don't miss a new
// prompt type that could be added in the future
assert(
type === 'local' || type === 'user',
`Unknown prompt type '${type}'.`,
storage === 'local' || storage === 'user',
`Unknown prompt storage '${storage}'.`,
);
const prompts = (type === 'user')
const prompts = (storage === 'user')
? [this.userDataService.currentProfile.promptsHome]
: this.fileLocator.getConfigBasedSourceFolders();
return prompts.map(addType(type));
return prompts.map(addType(storage, type));
}
}
@@ -118,10 +119,11 @@ export class PromptsService extends Disposable implements IPromptsService {
* Utility to add a provided prompt `type` to a prompt URI.
*/
const addType = (
type: 'local' | 'user',
storage: TPromptsStorage,
type: TPromptsType,
): (uri: URI) => IPromptPath => {
return (uri) => {
return { uri, type: type };
return { uri, storage, type };
};
};
@@ -129,10 +131,11 @@ const addType = (
* Utility to add a provided prompt `type` to a list of prompt URIs.
*/
const withType = (
type: 'local' | 'user',
storage: TPromptsStorage,
type: TPromptsType,
): (uris: readonly URI[]) => (readonly IPromptPath[]) => {
return (uris) => {
return uris
.map(addType(type));
.map(addType(storage, type));
};
};

View File

@@ -15,11 +15,14 @@ import { createDecorator } from '../../../../../../platform/instantiation/common
export const IPromptsService = createDecorator<IPromptsService>('IPromptsService');
/**
* Supported prompt types.
* - `local` means the prompt is a local file.
* - `user` means a "roam-able" prompt file (similar to snippets).
*/
type TPromptsType = 'local' | 'user';
* Where the prompt is stored.
*/
export type TPromptsStorage = 'local' | 'user';
/**
* What the prompt is used for.
*/
export type TPromptsType = 'instructions' | 'prompt';
/**
* Represents a prompt path with its type.
@@ -32,11 +35,17 @@ export interface IPromptPath {
readonly uri: URI;
/**
* Type of the prompt.
* Storage of the prompt.
*/
readonly storage: TPromptsStorage;
/**
* Type
*/
readonly type: TPromptsType;
}
/**
* Provides prompt services.
*/
@@ -54,12 +63,12 @@ export interface IPromptsService extends IDisposable {
/**
* List all available prompt files.
*/
listPromptFiles(): Promise<readonly IPromptPath[]>;
listPromptFiles(type: TPromptsType): Promise<readonly IPromptPath[]>;
/**
* Get a list of prompt source folders based on the provided prompt type.
* Get a list of prompt source folders based on the provided prompt type and storage.
*/
getSourceFolders(type: TPromptsType): readonly IPromptPath[];
getSourceFolders(type: TPromptsType, storage: TPromptsStorage): readonly IPromptPath[];
}
/**