mirror of
https://github.com/microsoft/vscode.git
synced 2026-07-07 07:16:41 +01:00
Fix instructions reading in WSL (#295898)
This commit is contained in:
@@ -7,6 +7,7 @@ import { CancellationToken } from '../../../../../base/common/cancellation.js';
|
||||
import { match, splitGlobAware } from '../../../../../base/common/glob.js';
|
||||
import { ResourceMap, ResourceSet } from '../../../../../base/common/map.js';
|
||||
import { Schemas } from '../../../../../base/common/network.js';
|
||||
import { OperatingSystem } from '../../../../../base/common/platform.js';
|
||||
import { basename, dirname } from '../../../../../base/common/resources.js';
|
||||
import { URI } from '../../../../../base/common/uri.js';
|
||||
import { localize } from '../../../../../nls.js';
|
||||
@@ -14,6 +15,7 @@ import { IConfigurationService } from '../../../../../platform/configuration/com
|
||||
import { IFileService } from '../../../../../platform/files/common/files.js';
|
||||
import { ILabelService } from '../../../../../platform/label/common/label.js';
|
||||
import { ILogService } from '../../../../../platform/log/common/log.js';
|
||||
import { IRemoteAgentService } from '../../../../services/remote/common/remoteAgentService.js';
|
||||
import { ITelemetryService } from '../../../../../platform/telemetry/common/telemetry.js';
|
||||
import { IWorkspaceContextService } from '../../../../../platform/workspace/common/workspace.js';
|
||||
import { ChatRequestVariableSet, IChatRequestVariableEntry, isPromptFileVariableEntry, toPromptFileVariableEntry, toPromptTextVariableEntry, PromptFileVariableKind, IPromptTextVariableEntry, ChatRequestToolReferenceEntry, toToolVariableEntry } from '../attachments/chatVariableEntries.js';
|
||||
@@ -68,6 +70,7 @@ export class ComputeAutomaticInstructions {
|
||||
@IConfigurationService private readonly _configurationService: IConfigurationService,
|
||||
@IWorkspaceContextService private readonly _workspaceService: IWorkspaceContextService,
|
||||
@IFileService private readonly _fileService: IFileService,
|
||||
@IRemoteAgentService private readonly _remoteAgentService: IRemoteAgentService,
|
||||
@ITelemetryService private readonly _telemetryService: ITelemetryService,
|
||||
@ILanguageModelToolsService private readonly _languageModelToolsService: ILanguageModelToolsService,
|
||||
) {
|
||||
@@ -294,6 +297,10 @@ export class ComputeAutomaticInstructions {
|
||||
const readTool = this._getTool('readFile');
|
||||
const runSubagentTool = this._getTool(VSCodeToolReference.runSubagent);
|
||||
|
||||
const remoteEnv = await this._remoteAgentService.getEnvironment();
|
||||
const remoteOS = remoteEnv?.os;
|
||||
const filePath = (uri: URI) => getFilePath(uri, remoteOS);
|
||||
|
||||
const entries: string[] = [];
|
||||
if (readTool) {
|
||||
|
||||
@@ -316,13 +323,13 @@ export class ComputeAutomaticInstructions {
|
||||
if (description) {
|
||||
entries.push(`<description>${description}</description>`);
|
||||
}
|
||||
entries.push(`<file>${getFilePath(uri)}</file>`);
|
||||
entries.push(`<file>${filePath(uri)}</file>`);
|
||||
const applyToPattern = this._getApplyToPattern(applyTo, paths);
|
||||
if (applyToPattern) {
|
||||
entries.push(`<applyTo>${applyToPattern}</applyTo>`);
|
||||
}
|
||||
} else {
|
||||
entries.push(`<file>${getFilePath(uri)}</file>`);
|
||||
entries.push(`<file>${filePath(uri)}</file>`);
|
||||
}
|
||||
entries.push('</instruction>');
|
||||
hasContent = true;
|
||||
@@ -335,7 +342,7 @@ export class ComputeAutomaticInstructions {
|
||||
const description = folderName.trim().length === 0 ? localize('instruction.file.description.agentsmd.root', 'Instructions for the workspace') : localize('instruction.file.description.agentsmd.folder', 'Instructions for folder \'{0}\'', folderName);
|
||||
entries.push('<instruction>');
|
||||
entries.push(`<description>${description}</description>`);
|
||||
entries.push(`<file>${getFilePath(uri)}</file>`);
|
||||
entries.push(`<file>${filePath(uri)}</file>`);
|
||||
entries.push('</instruction>');
|
||||
hasContent = true;
|
||||
|
||||
@@ -378,7 +385,7 @@ export class ComputeAutomaticInstructions {
|
||||
if (skill.description) {
|
||||
entries.push(`<description>${skill.description}</description>`);
|
||||
}
|
||||
entries.push(`<file>${getFilePath(skill.uri)}</file>`);
|
||||
entries.push(`<file>${filePath(skill.uri)}</file>`);
|
||||
entries.push('</skill>');
|
||||
}
|
||||
entries.push('</skills>', '', ''); // add trailing newline
|
||||
@@ -492,9 +499,19 @@ export class ComputeAutomaticInstructions {
|
||||
}
|
||||
|
||||
|
||||
function getFilePath(uri: URI): string {
|
||||
export function getFilePath(uri: URI, remoteOS: OperatingSystem | undefined): string {
|
||||
if (uri.scheme === Schemas.file || uri.scheme === Schemas.vscodeRemote) {
|
||||
return uri.fsPath;
|
||||
const fsPath = uri.fsPath;
|
||||
// uri.fsPath uses the local OS's path separators, but the path
|
||||
// may belong to a remote with a different OS. Normalize separators
|
||||
// to match the remote OS (idempotent when local and remote match).
|
||||
if (remoteOS !== undefined) {
|
||||
if (remoteOS === OperatingSystem.Windows) {
|
||||
return fsPath.replace(/\//g, '\\');
|
||||
}
|
||||
return fsPath.replace(/\\/g, '/');
|
||||
}
|
||||
return fsPath;
|
||||
}
|
||||
return uri.toString();
|
||||
}
|
||||
|
||||
+57
-1
@@ -8,6 +8,7 @@ import * as sinon from 'sinon';
|
||||
import { CancellationToken } from '../../../../../../base/common/cancellation.js';
|
||||
import { Event } from '../../../../../../base/common/event.js';
|
||||
import { Schemas } from '../../../../../../base/common/network.js';
|
||||
import { OperatingSystem } from '../../../../../../base/common/platform.js';
|
||||
import { URI } from '../../../../../../base/common/uri.js';
|
||||
import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../../base/test/common/utils.js';
|
||||
import { ILanguageService } from '../../../../../../editor/common/languages/language.js';
|
||||
@@ -27,7 +28,7 @@ import { testWorkspace } from '../../../../../../platform/workspace/test/common/
|
||||
import { IUserDataProfileService } from '../../../../../services/userDataProfile/common/userDataProfile.js';
|
||||
import { TestContextService, TestUserDataProfileService } from '../../../../../test/common/workbenchTestServices.js';
|
||||
import { ChatRequestVariableSet, isPromptFileVariableEntry, isPromptTextVariableEntry, toFileVariableEntry } from '../../../common/attachments/chatVariableEntries.js';
|
||||
import { ComputeAutomaticInstructions, InstructionsCollectionEvent } from '../../../common/promptSyntax/computeAutomaticInstructions.js';
|
||||
import { ComputeAutomaticInstructions, getFilePath, InstructionsCollectionEvent } from '../../../common/promptSyntax/computeAutomaticInstructions.js';
|
||||
import { PromptsConfig } from '../../../common/promptSyntax/config/config.js';
|
||||
import { AGENTS_SOURCE_FOLDER, CLAUDE_RULES_SOURCE_FOLDER, INSTRUCTION_FILE_EXTENSION, INSTRUCTIONS_DEFAULT_SOURCE_FOLDER, LEGACY_MODE_DEFAULT_SOURCE_FOLDER, PROMPT_DEFAULT_SOURCE_FOLDER, PROMPT_FILE_EXTENSION } from '../../../common/promptSyntax/config/promptFileLocations.js';
|
||||
import { INSTRUCTIONS_LANGUAGE_ID, PROMPT_LANGUAGE_ID } from '../../../common/promptSyntax/promptTypes.js';
|
||||
@@ -39,6 +40,7 @@ import { IPathService } from '../../../../../services/path/common/pathService.js
|
||||
import { IFileQuery, ISearchService } from '../../../../../services/search/common/search.js';
|
||||
import { IExtensionService } from '../../../../../services/extensions/common/extensions.js';
|
||||
import { ILanguageModelToolsService } from '../../../common/tools/languageModelToolsService.js';
|
||||
import { IRemoteAgentService } from '../../../../../../workbench/services/remote/common/remoteAgentService.js';
|
||||
import { basename } from '../../../../../../base/common/resources.js';
|
||||
import { match } from '../../../../../../base/common/glob.js';
|
||||
import { ChatModeKind } from '../../../common/constants.js';
|
||||
@@ -169,6 +171,10 @@ suite('ComputeAutomaticInstructions', () => {
|
||||
} as unknown as ILanguageModelToolsService;
|
||||
instaService.stub(ILanguageModelToolsService, toolsService);
|
||||
|
||||
instaService.stub(IRemoteAgentService, {
|
||||
getEnvironment: () => Promise.resolve(null),
|
||||
});
|
||||
|
||||
service = disposables.add(instaService.createInstance(PromptsService));
|
||||
instaService.stub(IPromptsService, service);
|
||||
});
|
||||
@@ -2011,3 +2017,53 @@ suite('ComputeAutomaticInstructions', () => {
|
||||
assert.ok(!paths.includes(claudeMdUri.path), 'Should not include CLAUDE.md (symlink to copilot)');
|
||||
});
|
||||
});
|
||||
|
||||
suite('getFilePath', () => {
|
||||
|
||||
ensureNoDisposablesAreLeakedInTestSuite();
|
||||
|
||||
test('should return fsPath for file:// URIs', () => {
|
||||
const uri = URI.file('/workspace/src/file.ts');
|
||||
const result = getFilePath(uri, undefined);
|
||||
assert.strictEqual(result, uri.fsPath);
|
||||
});
|
||||
|
||||
test('should return fsPath for vscode-remote URIs', () => {
|
||||
const uri = URI.from({ scheme: Schemas.vscodeRemote, path: '/workspace/src/file.ts' });
|
||||
const result = getFilePath(uri, undefined);
|
||||
assert.strictEqual(result, uri.fsPath);
|
||||
});
|
||||
|
||||
test('should return uri.toString() for other schemes', () => {
|
||||
const uri = URI.from({ scheme: 'untitled', path: '/workspace/src/file.ts' });
|
||||
const result = getFilePath(uri, undefined);
|
||||
assert.strictEqual(result, uri.toString());
|
||||
});
|
||||
|
||||
test('should use backslashes when remote is Windows', () => {
|
||||
const uri = URI.from({ scheme: Schemas.vscodeRemote, path: '/C:/Users/dev/project/file.ts' });
|
||||
const result = getFilePath(uri, OperatingSystem.Windows);
|
||||
assert.ok(!result.includes('/'), 'Should not contain forward slashes');
|
||||
assert.ok(result.includes('\\'), 'Should contain backslashes');
|
||||
});
|
||||
|
||||
test('should use forward slashes when remote is Linux', () => {
|
||||
const uri = URI.from({ scheme: Schemas.vscodeRemote, path: '/home/user/project/file.ts' });
|
||||
const result = getFilePath(uri, OperatingSystem.Linux);
|
||||
assert.ok(!result.includes('\\'), 'Should not contain backslashes');
|
||||
assert.ok(result.includes('/home/user/project/file.ts'), 'Should contain the forward-slash path');
|
||||
});
|
||||
|
||||
test('should use forward slashes when remote is macOS', () => {
|
||||
const uri = URI.from({ scheme: Schemas.vscodeRemote, path: '/Users/dev/project/file.ts' });
|
||||
const result = getFilePath(uri, OperatingSystem.Macintosh);
|
||||
assert.ok(!result.includes('\\'), 'Should not contain backslashes');
|
||||
assert.ok(result.includes('/Users/dev/project/file.ts'), 'Should contain the forward-slash path');
|
||||
});
|
||||
|
||||
test('should not replace slashes when remoteOS is undefined', () => {
|
||||
const uri = URI.file('/workspace/src/file.ts');
|
||||
const result = getFilePath(uri, undefined);
|
||||
assert.strictEqual(result, uri.fsPath);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -47,6 +47,7 @@ import { InMemoryStorageService, IStorageService } from '../../../../../../../pl
|
||||
import { IPathService } from '../../../../../../services/path/common/pathService.js';
|
||||
import { IFileMatch, IFileQuery, ISearchService } from '../../../../../../services/search/common/search.js';
|
||||
import { IExtensionService } from '../../../../../../services/extensions/common/extensions.js';
|
||||
import { IRemoteAgentService } from '../../../../../../services/remote/common/remoteAgentService.js';
|
||||
import { ChatModeKind } from '../../../../common/constants.js';
|
||||
import { HookType } from '../../../../common/promptSyntax/hookSchema.js';
|
||||
|
||||
@@ -153,6 +154,10 @@ suite('PromptsService', () => {
|
||||
}
|
||||
});
|
||||
|
||||
instaService.stub(IRemoteAgentService, {
|
||||
getEnvironment: () => Promise.resolve(null),
|
||||
});
|
||||
|
||||
service = disposables.add(instaService.createInstance(PromptsService));
|
||||
instaService.stub(IPromptsService, service);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user