Path: blob/main/src/vs/sessions/contrib/chat/browser/customizationsDebugLog.contribution.ts
13401 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { CancellationToken } from '../../../../base/common/cancellation.js';6import { Disposable } from '../../../../base/common/lifecycle.js';7import { autorun } from '../../../../base/common/observable.js';8import { ILogger, ILoggerService } from '../../../../platform/log/common/log.js';9import { IWorkspaceContextService } from '../../../../platform/workspace/common/workspace.js';10import { IWorkbenchContribution, registerWorkbenchContribution2, WorkbenchPhase } from '../../../../workbench/common/contributions.js';11import { IAICustomizationWorkspaceService, applyStorageSourceFilter, IStorageSourceFilter } from '../../../../workbench/contrib/chat/common/aiCustomizationWorkspaceService.js';12import { IPromptsService, PromptsStorage, IPromptPath } from '../../../../workbench/contrib/chat/common/promptSyntax/service/promptsService.js';13import { PromptsType } from '../../../../workbench/contrib/chat/common/promptSyntax/promptTypes.js';14import { AICustomizationManagementSection } from '../../../../workbench/contrib/chat/browser/aiCustomization/aiCustomizationManagement.js';15import { IMcpService } from '../../../../workbench/contrib/mcp/common/mcpTypes.js';1617const PROMPT_SECTIONS: { section: AICustomizationManagementSection; type: PromptsType }[] = [18{ section: AICustomizationManagementSection.Agents, type: PromptsType.agent },19{ section: AICustomizationManagementSection.Skills, type: PromptsType.skill },20{ section: AICustomizationManagementSection.Instructions, type: PromptsType.instructions },21{ section: AICustomizationManagementSection.Hooks, type: PromptsType.hook },22];2324class CustomizationsDebugLogContribution extends Disposable implements IWorkbenchContribution {2526static readonly ID = 'sessions.customizationsDebugLog';2728private readonly _logger: ILogger;2930constructor(31@ILoggerService loggerService: ILoggerService,32@IPromptsService private readonly _promptsService: IPromptsService,33@IAICustomizationWorkspaceService private readonly _workspaceService: IAICustomizationWorkspaceService,34@IWorkspaceContextService private readonly _workspaceContextService: IWorkspaceContextService,35@IMcpService private readonly _mcpService: IMcpService,36) {37super();38this._logger = this._register(loggerService.createLogger('customizationsDebug', { name: 'Customizations Debug' }));3940this._register(this._promptsService.onDidChangeCustomAgents(() => this._logSnapshot()));41this._register(this._promptsService.onDidChangeSlashCommands(() => this._logSnapshot()));42this._register(this._workspaceContextService.onDidChangeWorkspaceFolders(() => this._logSnapshot()));43this._register(autorun(reader => {44this._workspaceService.activeProjectRoot.read(reader);45this._logSnapshot();46}));47this._register(autorun(reader => {48this._mcpService.servers.read(reader);49this._logSnapshot();50}));51}5253private _pendingSnapshot: Promise<void> | undefined;54private _snapshotDirty = false;5556private _logSnapshot(): void {57if (this._pendingSnapshot) {58this._snapshotDirty = true;59return;60}61this._pendingSnapshot = this._doLogSnapshot().finally(() => {62this._pendingSnapshot = undefined;63if (this._snapshotDirty) {64this._snapshotDirty = false;65this._logSnapshot();66}67});68}6970private async _doLogSnapshot(): Promise<void> {71const root = this._workspaceService.getActiveProjectRoot()?.fsPath ?? '(none)';7273this._logger.info('');74this._logger.info('=== Customizations Snapshot ===');75this._logger.info(` Root: ${root}`);76this._logger.info(` Sections: ${this._workspaceService.managementSections.join(', ')}`);77this._logger.info('');7879// Header80this._logger.info(` ${'Section'.padEnd(16)} ${'Local'.padStart(6)} ${'User'.padStart(6)} ${'Ext'.padStart(6)} ${'Total'.padStart(7)}`);81this._logger.info(` ${'--------'.padEnd(16)} ${'-----'.padStart(6)} ${'----'.padStart(6)} ${'---'.padStart(6)} ${'-----'.padStart(7)}`);8283for (const { section, type } of PROMPT_SECTIONS) {84const filter = this._workspaceService.getStorageSourceFilter(type);85await this._logSectionRow(section, type, filter);86}8788this._logger.info('');8990// Details per section91for (const { section, type } of PROMPT_SECTIONS) {92const filter = this._workspaceService.getStorageSourceFilter(type);93await this._logSectionDetails(section, type, filter);94}9596// MCP Servers97this._logMcpServers();98}99100private _logMcpServers(): void {101const servers = this._mcpService.servers.get();102this._logger.info(` -- MCP Servers (${servers.length}) --`);103if (servers.length === 0) {104this._logger.info(' (none registered)');105}106for (const server of servers) {107const state = server.connectionState.get();108const stateStr = state?.state ?? 'unknown';109this._logger.info(` ${server.definition.label} [${stateStr}] id=${server.definition.id}`);110}111this._logger.info('');112}113114private async _logSectionRow(section: AICustomizationManagementSection, type: PromptsType, filter: IStorageSourceFilter): Promise<void> {115try {116const [localFiles, userFiles, extensionFiles] = await Promise.all([117this._promptsService.listPromptFilesForStorage(type, PromptsStorage.local, CancellationToken.None),118this._promptsService.listPromptFilesForStorage(type, PromptsStorage.user, CancellationToken.None),119this._promptsService.listPromptFilesForStorage(type, PromptsStorage.extension, CancellationToken.None),120]);121const all: IPromptPath[] = [...localFiles, ...userFiles, ...extensionFiles];122const filtered = applyStorageSourceFilter(all, filter);123const local = filtered.filter(f => f.storage === PromptsStorage.local).length;124const user = filtered.filter(f => f.storage === PromptsStorage.user).length;125const ext = filtered.filter(f => f.storage === PromptsStorage.extension).length;126127this._logger.info(` ${section.padEnd(16)} ${String(local).padStart(6)} ${String(user).padStart(6)} ${String(ext).padStart(6)} ${String(filtered.length).padStart(7)}`);128} catch {129this._logger.info(` ${section.padEnd(16)} (error)`);130}131}132133private async _logSectionDetails(section: AICustomizationManagementSection, type: PromptsType, filter: IStorageSourceFilter): Promise<void> {134try {135// Source folders - where we look for files136const sourceFolders = await this._promptsService.getSourceFolders(type);137if (sourceFolders.length > 0) {138this._logger.info(` -- ${section} --`);139this._logger.info(` Search paths:`);140for (const sf of sourceFolders) {141this._logger.info(` [${sf.storage}] ${sf.uri.fsPath}`);142}143}144145const [localFiles, userFiles, extensionFiles] = await Promise.all([146this._promptsService.listPromptFilesForStorage(type, PromptsStorage.local, CancellationToken.None),147this._promptsService.listPromptFilesForStorage(type, PromptsStorage.user, CancellationToken.None),148this._promptsService.listPromptFilesForStorage(type, PromptsStorage.extension, CancellationToken.None),149]);150const all: IPromptPath[] = [...localFiles, ...userFiles, ...extensionFiles];151const filtered = applyStorageSourceFilter(all, filter);152153if (filtered.length > 0) {154if (sourceFolders.length === 0) {155this._logger.info(` -- ${section} --`);156}157this._logger.info(` Filter: sources=[${filter.sources.join(', ')}]${filter.includedUserFileRoots ? `, roots=[${filter.includedUserFileRoots.map(r => r.fsPath).join(', ')}]` : ''}`);158this._logger.info(` Found ${filtered.length} item(s):`);159for (const f of filtered) {160this._logger.info(` [${f.storage}] ${f.uri.fsPath}`);161}162}163164if (sourceFolders.length > 0 || filtered.length > 0) {165this._logger.info('');166}167} catch {168// already logged in row169}170}171}172173registerWorkbenchContribution2(174CustomizationsDebugLogContribution.ID,175CustomizationsDebugLogContribution,176WorkbenchPhase.AfterRestored,177);178179180