Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
6e1dfb7
add multi edit tool
Jul 24, 2025
f4a0a6a
comment out reminder
Jul 24, 2025
26969ce
comment out reminder
Jul 24, 2025
666791b
fix multi_replace_string_in_file issue in intents
Jul 24, 2025
3752918
Merge branch 'yemohyle/multi_replace_string' of github.com:yemohyleye…
Jul 24, 2025
89ca44c
add multi_replace_string_in_file related instructions
Jul 24, 2025
fcea3a3
Merge branch 'yemohyle/multi_replace_string' of github.com:yemohyleye…
Jul 24, 2025
0065bdf
add more instructions
Jul 24, 2025
bba4878
Merge branch 'yemohyle/multi_replace_string' of github.com:yemohyleye…
Jul 24, 2025
5be9ac7
Merge branch 'microsoft:main' into yemohyle/multi_replace_string
yemohyleyemohyle Jul 28, 2025
c50436d
Merge branch 'yemohyle/multi_replace_string' into yemohyle/main
yemohyleyemohyle Aug 11, 2025
032229b
Merge pull request #6 from yemohyleyemohyle/yemohyle/main
yemohyleyemohyle Aug 11, 2025
01f2991
Merge branch 'microsoft:main' into yemohyle/multi_replace_string
yemohyleyemohyle Aug 11, 2025
1b39015
remove next tool prediction parsing
Aug 11, 2025
8317ef4
remove next_tool_prediction from toolSchemaNormalizer.ts
Aug 11, 2025
cfb143d
add initial user reminder
Aug 11, 2025
88af994
add hasMultiReplaceString
Aug 11, 2025
77eec74
add hasMultiReplaceString
Aug 11, 2025
3be4cd5
add references to MultiReplaceStringTool
Aug 11, 2025
397306b
Merge branch 'main' into yemohyle/multi_replace_string_initial_user_r…
yemohyleyemohyle Aug 12, 2025
4785b9f
Delete test_implementation.js
yemohyleyemohyle Aug 12, 2025
e4c309e
Delete test_dummy_parameter.js
yemohyleyemohyle Aug 12, 2025
f316a7f
Delete test_required_dummy.js
yemohyleyemohyle Aug 12, 2025
f36bb02
Delete verify_implementation.js
yemohyleyemohyle Aug 12, 2025
bc4d78b
Delete src/extension/tools/test/common/addNextToolPredictionParameter…
yemohyleyemohyle Aug 12, 2025
b27b29e
Fix formatting of multi-replace string reminder
yemohyleyemohyle Aug 12, 2025
951a48b
Merge remote-tracking branch 'origin/main' into pr/yemohyleyemohyle/573
connor4312 Aug 13, 2025
9d2c980
tidy up and exp
connor4312 Aug 13, 2025
9c8e3ab
eng: refactor multi and single edit tools
connor4312 Aug 13, 2025
b571c12
Merge remote-tracking branch 'origin/main' into connor4312/573
connor4312 Aug 13, 2025
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
52 changes: 52 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -740,6 +740,58 @@
]
}
},
{
"name": "copilot_multiReplaceString",
"toolReferenceName": "multiReplaceString",
"displayName": "%copilot.tools.multiReplaceString.name%",
"modelDescription": "This tool allows you to apply multiple replace_string_in_file operations in a single call, which is more efficient than calling replace_string_in_file multiple times. It takes an array of replacement operations and applies them sequentially. Each replacement operation has the same parameters as replace_string_in_file: filePath, oldString, newString, and explanation. This tool is ideal when you need to make multiple edits across different files or multiple edits in the same file. The tool will provide a summary of successful and failed operations.",
"when": "!config.github.copilot.chat.disableReplaceTool",
"inputSchema": {
"type": "object",
"properties": {
"explanation": {
"type": "string",
"description": "A brief explanation of what the multi-replace operation will accomplish."
},
"replacements": {
"type": "array",
"description": "An array of replacement operations to apply sequentially.",
"items": {
"type": "object",
"properties": {
"explanation": {
"type": "string",
"description": "A brief explanation of this specific replacement operation."
},
"filePath": {
"type": "string",
"description": "An absolute path to the file to edit."
},
"oldString": {
"type": "string",
"description": "The exact literal text to replace, preferably unescaped. Include at least 3 lines of context BEFORE and AFTER the target text, matching whitespace and indentation precisely. If this string is not the exact literal text or does not match exactly, this replacement will fail."
},
"newString": {
"type": "string",
"description": "The exact literal text to replace `oldString` with, preferably unescaped. Provide the EXACT text. Ensure the resulting code is correct and idiomatic."
}
},
"required": [
"explanation",
"filePath",
"oldString",
"newString"
]
},
"minItems": 1
}
},
"required": [
"explanation",
"replacements"
]
}
},
{
"name": "copilot_editNotebook",
"toolReferenceName": "editNotebook",
Expand Down
1 change: 1 addition & 0 deletions package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,7 @@
"copilot.tools.createFile.name": "Create File",
"copilot.tools.insertEdit.name": "Edit File",
"copilot.tools.replaceString.name": "Replace String in File",
"copilot.tools.multiReplaceString.name": "Multi-Replace String in Files",
"copilot.tools.editNotebook.name": "Edit Notebook",
"copilot.tools.runNotebookCell.name": "Run Notebook Cell",
"copilot.tools.getNotebookCellOutput.name": "Get Notebook Cell Output",
Expand Down
8 changes: 7 additions & 1 deletion src/extension/intents/node/agentIntent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,14 +61,20 @@ const getTools = (instaService: IInstantiationService, request: vscode.ChatReque

const allowTools: Record<string, boolean> = {};
allowTools[ToolName.EditFile] = true;
allowTools[ToolName.ReplaceString] = modelSupportsReplaceString(model) || !!(model.family.includes('gemini') && configurationService.getExperimentBasedConfig(ConfigKey.Internal.GeminiReplaceString, experimentationService));
allowTools[ToolName.ReplaceString] = modelSupportsReplaceString(model);
allowTools[ToolName.ApplyPatch] = await modelSupportsApplyPatch(model) && !!toolsService.getTool(ToolName.ApplyPatch);

if (modelCanUseReplaceStringExclusively(model)) {
allowTools[ToolName.ReplaceString] = true;
allowTools[ToolName.EditFile] = false;
}

if (allowTools[ToolName.ReplaceString]) {
if (configurationService.getExperimentBasedConfig(ConfigKey.Internal.MultiReplaceString, experimentationService)) {
allowTools[ToolName.MultiReplaceString] = true;
}
}

allowTools[ToolName.RunTests] = await testService.hasAnyTests();
allowTools[ToolName.CoreRunTask] = !!(configurationService.getConfig(ConfigKey.AgentCanRunTasks) && tasksService.getTasks().length);

Expand Down
7 changes: 6 additions & 1 deletion src/extension/intents/node/editCodeIntent2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import type * as vscode from 'vscode';
import { ChatLocation } from '../../../platform/chat/common/commonTypes';
import { ConfigKey, IConfigurationService } from '../../../platform/configuration/common/configurationService';
import { modelSupportsReplaceString } from '../../../platform/endpoint/common/chatModelCapabilities';
import { IEndpointProvider } from '../../../platform/endpoint/common/endpointProvider';
import { IEnvService } from '../../../platform/env/common/envService';
import { ILogService } from '../../../platform/log/common/logService';
Expand Down Expand Up @@ -40,14 +41,18 @@ const getTools = (instaService: IInstantiationService, request: vscode.ChatReque
const experimentalService = accessor.get<IExperimentationService>(IExperimentationService);
const model = await endpointProvider.getChatEndpoint(request);
const lookForTools = new Set<string>([ToolName.EditFile]);
const experimentationService = accessor.get<IExperimentationService>(IExperimentationService);


if (configurationService.getExperimentBasedConfig(ConfigKey.EditsCodeNewNotebookAgentEnabled, experimentalService) !== false && requestHasNotebookRefs(request, notebookService, { checkPromptAsWell: true })) {
lookForTools.add(ToolName.CreateNewJupyterNotebook);
}

if (model.family.startsWith('claude')) {
if (modelSupportsReplaceString(model)) {
lookForTools.add(ToolName.ReplaceString);
if (configurationService.getExperimentBasedConfig(ConfigKey.Internal.MultiReplaceString, experimentationService)) {
lookForTools.add(ToolName.MultiReplaceString);
}
}
lookForTools.add(ToolName.EditNotebook);
if (requestHasNotebookRefs(request, notebookService, { checkPromptAsWell: true })) {
Expand Down
7 changes: 6 additions & 1 deletion src/extension/intents/node/notebookEditorIntent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import { IToolsService } from '../../tools/common/toolsService';
import { EditCodeIntent, EditCodeIntentOptions } from './editCodeIntent';
import { EditCode2IntentInvocation } from './editCodeIntent2';
import { getRequestedToolCallIterationLimit } from './toolCallingLoop';
import { modelSupportsReplaceString } from '../../../platform/endpoint/common/chatModelCapabilities';

const getTools = (instaService: IInstantiationService, request: vscode.ChatRequest): Promise<vscode.LanguageModelToolInformation[]> =>
instaService.invokeFunction(async accessor => {
Expand All @@ -43,13 +44,17 @@ const getTools = (instaService: IInstantiationService, request: vscode.ChatReque
const experimentalService = accessor.get<IExperimentationService>(IExperimentationService);
const model = await endpointProvider.getChatEndpoint(request);
const lookForTools = new Set<string>([ToolName.EditFile]);
const experimentationService = accessor.get<IExperimentationService>(IExperimentationService);

if (configurationService.getExperimentBasedConfig(ConfigKey.EditsCodeNewNotebookAgentEnabled, experimentalService) !== false && requestHasNotebookRefs(request, notebookService, { checkPromptAsWell: true })) {
lookForTools.add(ToolName.CreateNewJupyterNotebook);
}

if (model.family.startsWith('claude')) {
if (modelSupportsReplaceString(model)) {
lookForTools.add(ToolName.ReplaceString);
if (configurationService.getExperimentBasedConfig(ConfigKey.Internal.MultiReplaceString, experimentationService)) {
lookForTools.add(ToolName.MultiReplaceString);
}
}

lookForTools.add(ToolName.EditNotebook);
Expand Down
27 changes: 16 additions & 11 deletions src/extension/prompts/node/agent/agentInstructions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -129,13 +129,15 @@ export class DefaultAgentPrompt extends PromptElement<DefaultAgentPromptProps> {
{tools[ToolName.ReplaceString] ?
<>
Before you edit an existing file, make sure you either already have it in the provided context, or read it with the {ToolName.ReadFile} tool, so that you can make proper changes.<br />
Use the {ToolName.ReplaceString} tool to edit files, paying attention to context to ensure your replacement is unique. You can use this tool multiple times per file.<br />
Use the {ToolName.EditFile} tool to insert code into a file ONLY if {ToolName.ReplaceString} has failed.<br />
{tools[ToolName.MultiReplaceString]
? <>Use the {ToolName.ReplaceString} tool for single string replacements, paying attention to context to ensure your replacement is unique. Prefer the {ToolName.MultiReplaceString} tool when you need to make multiple string replacements across one or more files in a single operation. This is significantly more efficient than calling {ToolName.ReplaceString} multiple times and should be your first choice for: fixing similar patterns across files, applying consistent formatting changes, bulk refactoring operations, or any scenario where you need to make the same type of change in multiple places.<br /></>
: <>Use the {ToolName.ReplaceString} tool to edit files, paying attention to context to ensure your replacement is unique. You can use this tool multiple times per file.<br /></>}
Use the {ToolName.EditFile} tool to insert code into a file ONLY if {tools[ToolName.MultiReplaceString] ? `${ToolName.MultiReplaceString}/` : ''}{ToolName.ReplaceString} has failed.<br />
When editing files, group your changes by file.<br />
{isGpt5 && <>Make the smallest set of edits needed and avoid reformatting or moving unrelated code. Preserve existing style and conventions, and keep imports, exports, and public APIs stable unless the task requires changes. Prefer completing all edits for a file within a single message when practical.<br /></>}
NEVER show the changes to the user, just call the tool, and the edits will be applied and shown to the user.<br />
NEVER print a codeblock that represents a change to a file, use {ToolName.ReplaceString} or {ToolName.EditFile} instead.<br />
For each file, give a short description of what needs to be changed, then use the {ToolName.ReplaceString} or {ToolName.EditFile} tools. You can use any tool multiple times in a response, and you can keep writing text after using a tool.<br /></>
NEVER print a codeblock that represents a change to a file, use {ToolName.ReplaceString}{tools[ToolName.MultiReplaceString] ? `, ${ToolName.MultiReplaceString},` : ''} or {ToolName.EditFile} instead.<br />
For each file, give a short description of what needs to be changed, then use the {ToolName.ReplaceString}{tools[ToolName.MultiReplaceString] ? `, ${ToolName.MultiReplaceString},` : ''} or {ToolName.EditFile} tools. You can use any tool multiple times in a response, and you can keep writing text after using a tool.<br /></>
: <>
Don't try to edit an existing file without reading it first, so you can make changes properly.<br />
Use the {ToolName.EditFile} tool to edit files. When editing files, group your changes by file.<br />
Expand Down Expand Up @@ -656,20 +658,22 @@ export class AlternateGPTPrompt extends PromptElement<DefaultAgentPromptProps> {
{tools[ToolName.ReplaceString] ?
<>
Before you edit an existing file, make sure you either already have it in the provided context, or read it with the {ToolName.ReadFile} tool, so that you can make proper changes.<br />
Use the {ToolName.ReplaceString} tool to edit files, paying attention to context to ensure your replacement is unique. You can use this tool multiple times per file.<br />
Use the {ToolName.EditFile} tool to insert code into a file ONLY if {ToolName.ReplaceString} has failed.<br />
{tools[ToolName.MultiReplaceString]
? <>Use the {ToolName.ReplaceString} tool for single string replacements, paying attention to context to ensure your replacement is unique. Prefer the {ToolName.MultiReplaceString} tool when you need to make multiple string replacements across one or more files in a single operation. This is significantly more efficient than calling {ToolName.ReplaceString} multiple times and should be your first choice for: fixing similar patterns across files, applying consistent formatting changes, bulk refactoring operations, or any scenario where you need to make the same type of change in multiple places.<br /></>
: <>Use the {ToolName.ReplaceString} tool to edit files, paying attention to context to ensure your replacement is unique. You can use this tool multiple times per file.<br /></>}
Use the {ToolName.EditFile} tool to insert code into a file ONLY if {tools[ToolName.MultiReplaceString] ? `${ToolName.MultiReplaceString}/` : ''}{ToolName.ReplaceString} has failed.<br />
When editing files, group your changes by file.<br />
{isGpt5 && <>Make the smallest set of edits needed and avoid reformatting or moving unrelated code. Preserve existing style and conventions, and keep imports, exports, and public APIs stable unless the task requires changes. Prefer completing all edits for a file within a single message when practical.<br /></>}
NEVER show the changes to the user, just call the tool, and the edits will be applied and shown to the user.<br />
NEVER print a codeblock that represents a change to a file, use {ToolName.ReplaceString} or {ToolName.EditFile} instead.<br />
For each file, give a short description of what needs to be changed, then use the {ToolName.ReplaceString} or {ToolName.EditFile} tools. You can use any tool multiple times in a response, and you can keep writing text after using a tool.<br /></> :
NEVER print a codeblock that represents a change to a file, use {ToolName.ReplaceString}{tools[ToolName.MultiReplaceString] ? `, ${ToolName.MultiReplaceString},` : ''} or {ToolName.EditFile} instead.<br />
For each file, give a short description of what needs to be changed, then use the {ToolName.ReplaceString}{tools[ToolName.MultiReplaceString] ? `, ${ToolName.MultiReplaceString},` : ''} or {ToolName.EditFile} tools. You can use any tool multiple times in a response, and you can keep writing text after using a tool.<br /></> :
<>
Don't try to edit an existing file without reading it first, so you can make changes properly.<br />
Use the {ToolName.ReplaceString} tool to edit files. When editing files, group your changes by file.<br />
Use the {ToolName.EditFile} tool to edit files. When editing files, group your changes by file.<br />
{isGpt5 && <>Make the smallest set of edits needed and avoid reformatting or moving unrelated code. Preserve existing style and conventions, and keep imports, exports, and public APIs stable unless the task requires changes. Prefer completing all edits for a file within a single message when practical.<br /></>}
NEVER show the changes to the user, just call the tool, and the edits will be applied and shown to the user.<br />
NEVER print a codeblock that represents a change to a file, use {ToolName.ReplaceString} instead.<br />
For each file, give a short description of what needs to be changed, then use the {ToolName.ReplaceString} tool. You can use any tool multiple times in a response, and you can keep writing text after using a tool.<br />
NEVER print a codeblock that represents a change to a file, use {ToolName.EditFile} instead.<br />
For each file, give a short description of what needs to be changed, then use the {ToolName.EditFile} tool. You can use any tool multiple times in a response, and you can keep writing text after using a tool.<br />
</>}
<GenericEditingTips {...this.props} />
The {ToolName.EditFile} tool is very smart and can understand how to apply your edits to the user's files, you just need to provide minimal hints.<br />
Expand Down Expand Up @@ -858,6 +862,7 @@ export class SweBenchAgentPrompt extends PromptElement<DefaultAgentPromptProps>
</Tag>
{!!tools[ToolName.ReplaceString] && <Tag name='ReplaceStringToolInstructions'>
{ToolName.ReplaceString} tool is a tool for editing files. For moving or renaming files, you should generally use the {ToolName.CoreRunInTerminal} with the 'mv' command instead. For larger edits, split it into small edits and call the edit tool multiple times to finish the whole edit carefully.<br />
{tools[ToolName.MultiReplaceString] && <>Use the {ToolName.MultiReplaceString} tool when you need to make multiple string replacements across one or more files in a single operation.<br /></>}
Before using {ToolName.ReplaceString} tool, you must use {ToolName.ReadFile} tool to understand the file's contents and context you want to edit<br />
To make a file edit, provide the following:<br />
1. filePath: The absolute path to the file to modify (must be absolute, not relative)<br />
Expand Down
Loading