Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions src/extension/tools/common/toolUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,21 @@
import { URI } from '../../../util/vs/base/common/uri';
import { Location } from '../../../vscodeTypes';

export function formatUriForFileWidget(uriOrLocation: URI | Location): string {
type FileUriMetadata = {
vscodeLinkType: 'skill';
linkText: string;
};

export function formatUriForFileWidget(uriOrLocation: URI | Location, metadata?: FileUriMetadata): string {
const uri = URI.isUri(uriOrLocation) ? uriOrLocation : uriOrLocation.uri;
const rangePart = URI.isUri(uriOrLocation) ?
'' :
`#${uriOrLocation.range.start.line + 1}-${uriOrLocation.range.end.line + 1}`;

// Empty link text -> rendered as file widget
if (metadata) {
const uriWithQuery = uri.with({ query: `vscodeLinkType=${metadata.vscodeLinkType}` });
return `[${metadata.linkText}](${uriWithQuery.toString()}${rangePart})`;
}

return `[](${uri.toString()}${rangePart})`;
}
2 changes: 1 addition & 1 deletion src/extension/tools/node/findTestsFilesTool.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ class FindTestFilesTool extends Disposable implements ICopilotTool<IFindTestFile
}

private formatURIs(uris: URI[]): string {
return uris.map(formatUriForFileWidget).join(', ');
return uris.map(uri => formatUriForFileWidget(uri)).join(', ');
}

async provideInput(promptContext: IBuildPromptContext): Promise<IFindTestFilesToolsParams | undefined> {
Expand Down
2 changes: 1 addition & 1 deletion src/extension/tools/node/getErrorsTool.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ export class GetErrorsTool extends Disposable implements ICopilotTool<IGetErrors
}

private formatURIs(uris: URI[]): string {
return uris.map(formatUriForFileWidget).join(', ');
return uris.map(uri => formatUriForFileWidget(uri)).join(', ');
}

private getNotebookCellDiagnostics(uri: URI) {
Expand Down
32 changes: 32 additions & 0 deletions src/extension/tools/node/readFileTool.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,23 @@ export class ReadFileTool implements ICopilotTool<ReadFileParams> {
}

const { start, end } = getParamRanges(input, documentSnapshot);
const skillInfo = this.customInstructionsService.getSkillInfo(uri);

if (start === 1 && end === documentSnapshot.lineCount) {
if (skillInfo) {
const { skillName } = skillInfo;
if (this.customInstructionsService.isSkillMdFile(uri)) {
return {
invocationMessage: new MarkdownString(l10n.t`Reading skill ${formatUriForFileWidget(uri, { vscodeLinkType: 'skill', linkText: skillName })}`),
pastTenseMessage: new MarkdownString(l10n.t`Read skill ${formatUriForFileWidget(uri, { vscodeLinkType: 'skill', linkText: skillName })}`),
};
} else {
return {
invocationMessage: new MarkdownString(l10n.t`Reading skill \`${skillName}\`: ${formatUriForFileWidget(uri)}`),
pastTenseMessage: new MarkdownString(l10n.t`Read skill \`${skillName}\`: ${formatUriForFileWidget(uri)}`),
};
}
}
return {
invocationMessage: new MarkdownString(l10n.t`Reading ${formatUriForFileWidget(uri)}`),
pastTenseMessage: new MarkdownString(l10n.t`Read ${formatUriForFileWidget(uri)}`),
Expand All @@ -180,6 +196,22 @@ export class ReadFileTool implements ICopilotTool<ReadFileParams> {

// Jump to the start of the range, don't select the whole range
const readLocation = new Location(uri, new Range(start - 1, 0, start - 1, 0));
if (this.customInstructionsService.isSkillFile(uri)) {
if (skillInfo) {
const { skillName } = skillInfo;
if (this.customInstructionsService.isSkillMdFile(uri)) {
return {
invocationMessage: new MarkdownString(l10n.t`Reading skill ${formatUriForFileWidget(readLocation, { vscodeLinkType: 'skill', linkText: skillName })}, lines ${start} to ${end}`),
pastTenseMessage: new MarkdownString(l10n.t`Read skill ${formatUriForFileWidget(readLocation, { vscodeLinkType: 'skill', linkText: skillName })}, lines ${start} to ${end}`),
};
} else {
return {
invocationMessage: new MarkdownString(l10n.t`Reading skill \`${skillName}\`: ${formatUriForFileWidget(readLocation)}, lines ${start} to ${end}`),
pastTenseMessage: new MarkdownString(l10n.t`Read skill \`${skillName}\`: ${formatUriForFileWidget(readLocation)}, lines ${start} to ${end}`),
};
}
}
}
return {
invocationMessage: new MarkdownString(l10n.t`Reading ${formatUriForFileWidget(readLocation)}, lines ${start} to ${end}`),
pastTenseMessage: new MarkdownString(l10n.t`Read ${formatUriForFileWidget(readLocation)}, lines ${start} to ${end}`),
Expand Down
37 changes: 1 addition & 36 deletions src/extension/tools/node/test/editFileToolUtils.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ import { homedir } from 'os';
import { afterEach, beforeEach, describe, expect, test } from 'vitest';
import { DefaultsOnlyConfigurationService } from '../../../../platform/configuration/common/defaultsOnlyConfigurationService';
import { InMemoryConfigurationService } from '../../../../platform/configuration/test/common/inMemoryConfigurationService';
import type { ICustomInstructionsService } from '../../../../platform/customInstructions/common/customInstructionsService';
import { IAlternativeNotebookContentService } from '../../../../platform/notebook/common/alternativeContent';
import { MockAlternativeNotebookContentService } from '../../../../platform/notebook/common/mockAlternativeContentService';
import { INotebookService } from '../../../../platform/notebook/common/notebookService';
import { MockCustomInstructionsService } from '../../../../platform/test/common/testCustomInstructionsService';
import { TestWorkspaceService } from '../../../../platform/test/node/testWorkspaceService';
import { WorkspaceEdit as WorkspaceEditShim } from '../../../../util/common/test/shims/editing';
import { createTextDocumentData, IExtHostDocumentData, setDocText } from '../../../../util/common/test/shims/textDocument';
Expand Down Expand Up @@ -654,41 +654,6 @@ describe('assertPathIsSafe (Windows scenarios)', () => {
});

describe('makeUriConfirmationChecker', async () => {
// Mock custom instructions service
class MockCustomInstructionsService implements ICustomInstructionsService {
declare readonly _serviceBrand: undefined;
private externalFiles = new Set<string>();

setExternalFiles(uris: URI[]) {
this.externalFiles.clear();
uris.forEach(uri => this.externalFiles.add(uri.toString()));
}

isExternalInstructionsFile(uri: URI): boolean {
return this.externalFiles.has(uri.toString());
}

isExternalInstructionsFolder(uri: URI): boolean {
return false;
}

isSkillFile(uri: URI): boolean {
return false;
}

fetchInstructionsFromSetting(): Promise<any[]> {
return Promise.resolve([]);
}

fetchInstructionsFromFile(): Promise<any> {
return Promise.resolve(undefined);
}

getAgentInstructions(): Promise<URI[]> {
return Promise.resolve([]);
}
}

let configService: InMemoryConfigurationService;
let workspaceService: TestWorkspaceService;
let customInstructionsService: MockCustomInstructionsService;
Expand Down
Loading