Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/sessions/contrib/chat/browser/customizationsDebugLog.contribution.ts
13401 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 { CancellationToken } from '../../../../base/common/cancellation.js';
7
import { Disposable } from '../../../../base/common/lifecycle.js';
8
import { autorun } from '../../../../base/common/observable.js';
9
import { ILogger, ILoggerService } from '../../../../platform/log/common/log.js';
10
import { IWorkspaceContextService } from '../../../../platform/workspace/common/workspace.js';
11
import { IWorkbenchContribution, registerWorkbenchContribution2, WorkbenchPhase } from '../../../../workbench/common/contributions.js';
12
import { IAICustomizationWorkspaceService, applyStorageSourceFilter, IStorageSourceFilter } from '../../../../workbench/contrib/chat/common/aiCustomizationWorkspaceService.js';
13
import { IPromptsService, PromptsStorage, IPromptPath } from '../../../../workbench/contrib/chat/common/promptSyntax/service/promptsService.js';
14
import { PromptsType } from '../../../../workbench/contrib/chat/common/promptSyntax/promptTypes.js';
15
import { AICustomizationManagementSection } from '../../../../workbench/contrib/chat/browser/aiCustomization/aiCustomizationManagement.js';
16
import { IMcpService } from '../../../../workbench/contrib/mcp/common/mcpTypes.js';
17
18
const PROMPT_SECTIONS: { section: AICustomizationManagementSection; type: PromptsType }[] = [
19
{ section: AICustomizationManagementSection.Agents, type: PromptsType.agent },
20
{ section: AICustomizationManagementSection.Skills, type: PromptsType.skill },
21
{ section: AICustomizationManagementSection.Instructions, type: PromptsType.instructions },
22
{ section: AICustomizationManagementSection.Hooks, type: PromptsType.hook },
23
];
24
25
class CustomizationsDebugLogContribution extends Disposable implements IWorkbenchContribution {
26
27
static readonly ID = 'sessions.customizationsDebugLog';
28
29
private readonly _logger: ILogger;
30
31
constructor(
32
@ILoggerService loggerService: ILoggerService,
33
@IPromptsService private readonly _promptsService: IPromptsService,
34
@IAICustomizationWorkspaceService private readonly _workspaceService: IAICustomizationWorkspaceService,
35
@IWorkspaceContextService private readonly _workspaceContextService: IWorkspaceContextService,
36
@IMcpService private readonly _mcpService: IMcpService,
37
) {
38
super();
39
this._logger = this._register(loggerService.createLogger('customizationsDebug', { name: 'Customizations Debug' }));
40
41
this._register(this._promptsService.onDidChangeCustomAgents(() => this._logSnapshot()));
42
this._register(this._promptsService.onDidChangeSlashCommands(() => this._logSnapshot()));
43
this._register(this._workspaceContextService.onDidChangeWorkspaceFolders(() => this._logSnapshot()));
44
this._register(autorun(reader => {
45
this._workspaceService.activeProjectRoot.read(reader);
46
this._logSnapshot();
47
}));
48
this._register(autorun(reader => {
49
this._mcpService.servers.read(reader);
50
this._logSnapshot();
51
}));
52
}
53
54
private _pendingSnapshot: Promise<void> | undefined;
55
private _snapshotDirty = false;
56
57
private _logSnapshot(): void {
58
if (this._pendingSnapshot) {
59
this._snapshotDirty = true;
60
return;
61
}
62
this._pendingSnapshot = this._doLogSnapshot().finally(() => {
63
this._pendingSnapshot = undefined;
64
if (this._snapshotDirty) {
65
this._snapshotDirty = false;
66
this._logSnapshot();
67
}
68
});
69
}
70
71
private async _doLogSnapshot(): Promise<void> {
72
const root = this._workspaceService.getActiveProjectRoot()?.fsPath ?? '(none)';
73
74
this._logger.info('');
75
this._logger.info('=== Customizations Snapshot ===');
76
this._logger.info(` Root: ${root}`);
77
this._logger.info(` Sections: ${this._workspaceService.managementSections.join(', ')}`);
78
this._logger.info('');
79
80
// Header
81
this._logger.info(` ${'Section'.padEnd(16)} ${'Local'.padStart(6)} ${'User'.padStart(6)} ${'Ext'.padStart(6)} ${'Total'.padStart(7)}`);
82
this._logger.info(` ${'--------'.padEnd(16)} ${'-----'.padStart(6)} ${'----'.padStart(6)} ${'---'.padStart(6)} ${'-----'.padStart(7)}`);
83
84
for (const { section, type } of PROMPT_SECTIONS) {
85
const filter = this._workspaceService.getStorageSourceFilter(type);
86
await this._logSectionRow(section, type, filter);
87
}
88
89
this._logger.info('');
90
91
// Details per section
92
for (const { section, type } of PROMPT_SECTIONS) {
93
const filter = this._workspaceService.getStorageSourceFilter(type);
94
await this._logSectionDetails(section, type, filter);
95
}
96
97
// MCP Servers
98
this._logMcpServers();
99
}
100
101
private _logMcpServers(): void {
102
const servers = this._mcpService.servers.get();
103
this._logger.info(` -- MCP Servers (${servers.length}) --`);
104
if (servers.length === 0) {
105
this._logger.info(' (none registered)');
106
}
107
for (const server of servers) {
108
const state = server.connectionState.get();
109
const stateStr = state?.state ?? 'unknown';
110
this._logger.info(` ${server.definition.label} [${stateStr}] id=${server.definition.id}`);
111
}
112
this._logger.info('');
113
}
114
115
private async _logSectionRow(section: AICustomizationManagementSection, type: PromptsType, filter: IStorageSourceFilter): Promise<void> {
116
try {
117
const [localFiles, userFiles, extensionFiles] = await Promise.all([
118
this._promptsService.listPromptFilesForStorage(type, PromptsStorage.local, CancellationToken.None),
119
this._promptsService.listPromptFilesForStorage(type, PromptsStorage.user, CancellationToken.None),
120
this._promptsService.listPromptFilesForStorage(type, PromptsStorage.extension, CancellationToken.None),
121
]);
122
const all: IPromptPath[] = [...localFiles, ...userFiles, ...extensionFiles];
123
const filtered = applyStorageSourceFilter(all, filter);
124
const local = filtered.filter(f => f.storage === PromptsStorage.local).length;
125
const user = filtered.filter(f => f.storage === PromptsStorage.user).length;
126
const ext = filtered.filter(f => f.storage === PromptsStorage.extension).length;
127
128
this._logger.info(` ${section.padEnd(16)} ${String(local).padStart(6)} ${String(user).padStart(6)} ${String(ext).padStart(6)} ${String(filtered.length).padStart(7)}`);
129
} catch {
130
this._logger.info(` ${section.padEnd(16)} (error)`);
131
}
132
}
133
134
private async _logSectionDetails(section: AICustomizationManagementSection, type: PromptsType, filter: IStorageSourceFilter): Promise<void> {
135
try {
136
// Source folders - where we look for files
137
const sourceFolders = await this._promptsService.getSourceFolders(type);
138
if (sourceFolders.length > 0) {
139
this._logger.info(` -- ${section} --`);
140
this._logger.info(` Search paths:`);
141
for (const sf of sourceFolders) {
142
this._logger.info(` [${sf.storage}] ${sf.uri.fsPath}`);
143
}
144
}
145
146
const [localFiles, userFiles, extensionFiles] = await Promise.all([
147
this._promptsService.listPromptFilesForStorage(type, PromptsStorage.local, CancellationToken.None),
148
this._promptsService.listPromptFilesForStorage(type, PromptsStorage.user, CancellationToken.None),
149
this._promptsService.listPromptFilesForStorage(type, PromptsStorage.extension, CancellationToken.None),
150
]);
151
const all: IPromptPath[] = [...localFiles, ...userFiles, ...extensionFiles];
152
const filtered = applyStorageSourceFilter(all, filter);
153
154
if (filtered.length > 0) {
155
if (sourceFolders.length === 0) {
156
this._logger.info(` -- ${section} --`);
157
}
158
this._logger.info(` Filter: sources=[${filter.sources.join(', ')}]${filter.includedUserFileRoots ? `, roots=[${filter.includedUserFileRoots.map(r => r.fsPath).join(', ')}]` : ''}`);
159
this._logger.info(` Found ${filtered.length} item(s):`);
160
for (const f of filtered) {
161
this._logger.info(` [${f.storage}] ${f.uri.fsPath}`);
162
}
163
}
164
165
if (sourceFolders.length > 0 || filtered.length > 0) {
166
this._logger.info('');
167
}
168
} catch {
169
// already logged in row
170
}
171
}
172
}
173
174
registerWorkbenchContribution2(
175
CustomizationsDebugLogContribution.ID,
176
CustomizationsDebugLogContribution,
177
WorkbenchPhase.AfterRestored,
178
);
179
180