Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/copilot/src/extension/chatSessions/vscode-node/claudeSessionOptionBuilder.ts
13399 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 { PermissionMode } from '@anthropic-ai/claude-agent-sdk';
7
import * as l10n from '@vscode/l10n';
8
import * as vscode from 'vscode';
9
import { ConfigKey, IConfigurationService } from '../../../platform/configuration/common/configurationService';
10
import { IWorkspaceService } from '../../../platform/workspace/common/workspaceService';
11
import { CancellationToken } from '../../../util/vs/base/common/cancellation';
12
import { basename } from '../../../util/vs/base/common/resources';
13
import { URI } from '../../../util/vs/base/common/uri';
14
import { IChatFolderMruService } from '../common/folderRepositoryManager';
15
import { folderMRUToChatProviderOptions, getSelectedOption, toWorkspaceFolderOptionItem } from './sessionOptionGroupBuilder';
16
17
const permissionModes: ReadonlySet<PermissionMode> = new Set<PermissionMode>(['default', 'acceptEdits', 'bypassPermissions', 'plan', 'dontAsk']);
18
19
export function isPermissionMode(value: string): value is PermissionMode {
20
return permissionModes.has(value as PermissionMode);
21
}
22
23
export const PERMISSION_MODE_OPTION_ID = 'permissionMode';
24
export const FOLDER_OPTION_ID = 'folder';
25
const MAX_MRU_ENTRIES = 10;
26
27
/**
28
* Builds and reads chat session option groups (permission mode, folder picker).
29
* Pure construction logic with no metadata or session-state dependencies — the
30
* controller resolves session-specific values and delegates here.
31
*/
32
export class ClaudeSessionOptionBuilder {
33
private _lastUsedPermissionMode: PermissionMode = 'acceptEdits';
34
35
get lastUsedPermissionMode(): PermissionMode {
36
return this._lastUsedPermissionMode;
37
}
38
39
constructor(
40
private readonly _configurationService: IConfigurationService,
41
private readonly _folderMruService: IChatFolderMruService,
42
private readonly _workspaceService: IWorkspaceService,
43
) { }
44
45
async buildNewSessionGroups(): Promise<vscode.ChatSessionProviderOptionGroup[]> {
46
const groups: vscode.ChatSessionProviderOptionGroup[] = [];
47
48
const folderGroup = await this.buildNewFolderGroup();
49
if (folderGroup) {
50
groups.push(folderGroup);
51
}
52
53
const permissionGroup = this.buildPermissionModeGroup();
54
const selectedPermission = permissionGroup.items.find(i => i.id === this._lastUsedPermissionMode);
55
groups.push({
56
...permissionGroup,
57
selected: selectedPermission ?? permissionGroup.items[0],
58
});
59
60
return groups;
61
}
62
63
async buildExistingSessionGroups(permissionMode: PermissionMode, folderUri: URI | undefined): Promise<vscode.ChatSessionProviderOptionGroup[]> {
64
const groups: vscode.ChatSessionProviderOptionGroup[] = [];
65
66
if (folderUri) {
67
groups.push(this.buildExistingFolderGroup(folderUri));
68
}
69
70
const permissionGroup = this.buildPermissionModeGroup();
71
const selectedItem = permissionGroup.items.find(i => i.id === permissionMode) ?? permissionGroup.items[0];
72
groups.push({
73
...permissionGroup,
74
selected: selectedItem,
75
});
76
77
return groups;
78
}
79
80
buildPermissionModeGroup(): vscode.ChatSessionProviderOptionGroup {
81
const bypassEnabled = this._configurationService.getConfig(ConfigKey.ClaudeAgentAllowDangerouslySkipPermissions);
82
return buildPermissionModeItems(bypassEnabled);
83
}
84
85
async buildNewFolderGroup(): Promise<vscode.ChatSessionProviderOptionGroup | undefined> {
86
const workspaceFolders = this._workspaceService.getWorkspaceFolders();
87
if (workspaceFolders.length === 1) {
88
return undefined;
89
}
90
91
const folderItems = await this.getFolderOptionItems();
92
return {
93
id: FOLDER_OPTION_ID,
94
name: l10n.t('Folder'),
95
description: l10n.t('Pick Folder'),
96
items: folderItems,
97
selected: folderItems[0],
98
};
99
}
100
101
buildExistingFolderGroup(folderUri: URI): vscode.ChatSessionProviderOptionGroup {
102
const folderItem: vscode.ChatSessionProviderOptionItem = {
103
...toWorkspaceFolderOptionItem(folderUri, this._workspaceService.getWorkspaceFolderName(folderUri) || basename(folderUri)),
104
locked: true,
105
};
106
return {
107
id: FOLDER_OPTION_ID,
108
name: l10n.t('Folder'),
109
description: l10n.t('Pick Folder'),
110
items: [folderItem],
111
selected: folderItem,
112
};
113
}
114
115
async getFolderOptionItems(): Promise<vscode.ChatSessionProviderOptionItem[]> {
116
const workspaceFolders = this._workspaceService.getWorkspaceFolders();
117
118
if (workspaceFolders.length === 0) {
119
const mruEntries = await this._folderMruService.getRecentlyUsedFolders(CancellationToken.None);
120
return folderMRUToChatProviderOptions(mruEntries).slice(0, MAX_MRU_ENTRIES);
121
}
122
123
return workspaceFolders.map(folder =>
124
toWorkspaceFolderOptionItem(folder, this._workspaceService.getWorkspaceFolderName(folder))
125
);
126
}
127
128
async getDefaultFolder(): Promise<URI | undefined> {
129
const workspaceFolders = this._workspaceService.getWorkspaceFolders();
130
if (workspaceFolders.length > 0) {
131
return workspaceFolders[0];
132
}
133
134
const mru = await this._folderMruService.getRecentlyUsedFolders(CancellationToken.None);
135
if (mru.length > 0) {
136
return mru[0].folder;
137
}
138
139
return undefined;
140
}
141
142
/**
143
* Reads the current selections from option groups and updates
144
* {@link lastUsedPermissionMode} as a side-effect.
145
*/
146
getSelections(groups: readonly vscode.ChatSessionProviderOptionGroup[]): { permissionMode?: PermissionMode; folderUri?: URI } {
147
const selectedPermission = getSelectedOption(groups, PERMISSION_MODE_OPTION_ID);
148
let permissionMode: PermissionMode | undefined;
149
if (selectedPermission && isPermissionMode(selectedPermission.id)) {
150
this._lastUsedPermissionMode = selectedPermission.id;
151
permissionMode = selectedPermission.id;
152
}
153
154
const selectedFolder = getSelectedOption(groups, FOLDER_OPTION_ID);
155
const folderUri = selectedFolder ? URI.file(selectedFolder.id) : undefined;
156
157
return { permissionMode, folderUri };
158
}
159
}
160
161
// #region Pure group-building functions (observable-friendly)
162
163
/**
164
* Build the permission mode option group from explicit inputs.
165
* Pure and synchronous — suitable for use in `derived` computations.
166
*/
167
export function buildPermissionModeItems(bypassEnabled: boolean): vscode.ChatSessionProviderOptionGroup {
168
const items: vscode.ChatSessionProviderOptionItem[] = [
169
{ id: 'default', name: l10n.t('Ask before edits'), slashCommand: 'ask' },
170
{ id: 'acceptEdits', name: l10n.t('Edit automatically'), slashCommand: 'edit' },
171
{ id: 'plan', name: l10n.t('Plan mode'), slashCommand: 'plan' },
172
];
173
174
if (bypassEnabled) {
175
items.push({ id: 'bypassPermissions', name: l10n.t('Bypass all permissions'), slashCommand: 'yolo' });
176
}
177
178
return {
179
id: PERMISSION_MODE_OPTION_ID,
180
name: l10n.t('Permission Mode'),
181
description: l10n.t('Pick Permission Mode'),
182
items,
183
kind: 'permissions',
184
};
185
}
186
187
// #endregion
188
189