Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 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
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
Empty file added IMPLEMENTATION_SUMMARY.md
Empty file.
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 @@ -270,6 +270,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
Original file line number Diff line number Diff line change
Expand Up @@ -139,17 +139,13 @@ export class LanguageModelAccess extends Disposable implements IExtensionContrib
}

const baseCount = await PromptRenderer.create(this._instantiationService, endpoint, LanguageModelAccessPrompt, { noSafety: false, messages: [] }).countTokens();
let multiplierString = endpoint.multiplier !== undefined ? `${endpoint.multiplier}x` : undefined;
if (endpoint.model === AutoChatEndpoint.id) {
multiplierString = 'Variable';
}

const model: vscode.LanguageModelChatInformation = {
id: endpoint.model,
name: endpoint.model === AutoChatEndpoint.id ? 'Auto' : endpoint.name,
family: endpoint.family,
description: modelDescription,
cost: multiplierString,
cost: endpoint.multiplier !== undefined && endpoint.multiplier !== 0 ? `${endpoint.multiplier}x` : endpoint.multiplier === 0 ? localize('languageModel.costIncluded', 'Included') : undefined,
category: modelCategory,
version: endpoint.version,
maxInputTokens: endpoint.modelMaxPromptTokens - baseCount - BaseTokensPerCompletion,
Expand Down
1 change: 1 addition & 0 deletions src/extension/intents/node/agentIntent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ const getTools = (instaService: IInstantiationService, request: vscode.ChatReque

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

Expand Down
1 change: 1 addition & 0 deletions src/extension/intents/node/editCodeIntent2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ const getTools = (instaService: IInstantiationService, request: vscode.ChatReque

if (model.family.startsWith('claude')) {
lookForTools.add(ToolName.ReplaceString);
lookForTools.add(ToolName.MultiReplaceString);
}
lookForTools.add(ToolName.EditNotebook);
if (requestHasNotebookRefs(request, notebookService, { checkPromptAsWell: true })) {
Expand Down
1 change: 1 addition & 0 deletions src/extension/intents/node/notebookEditorIntent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ const getTools = (instaService: IInstantiationService, request: vscode.ChatReque

if (model.family.startsWith('claude')) {
lookForTools.add(ToolName.ReplaceString);
lookForTools.add(ToolName.MultiReplaceString);
}

lookForTools.add(ToolName.EditNotebook);
Expand Down
16 changes: 10 additions & 6 deletions src/extension/prompts/node/agent/agentInstructions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,8 @@ 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 />
{tools[ToolName.MultiReplaceString] && <>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 for single string replacements, paying attention to context to ensure your replacement is unique.<br />
Use the {ToolName.EditFile} tool to insert code into a file ONLY if {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 /></>}
Expand Down Expand Up @@ -657,19 +658,21 @@ export class AlternateGPTPrompt extends PromptElement<DefaultAgentPromptProps> {
<>
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.MultiReplaceString} tool to make edits simultaneously.<br />
Use the {ToolName.EditFile} tool to insert code into a file ONLY if {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}, {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}, {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 />
{tools[ToolName.MultiReplaceString] && <>Prefer the {ToolName.MultiReplaceString} tool when you need to make multiple string replacements across one or more files in a single operation. This should be your first choice for bulk edits.<br /></>}
Use the {ToolName.ReplaceString} 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.ReplaceString} or {ToolName.MultiReplaceString} instead.<br />
For each file, give a short description of what needs to be changed, then use the {ToolName.ReplaceString} or {ToolName.MultiReplaceString} 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 @@ -891,7 +894,8 @@ export class SweBenchAgentPrompt extends PromptElement<DefaultAgentPromptProps>
</Tag>}
{!!tools[ToolName.EditFile] && <Tag name='editFileInstructions'>
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 make edits in the file in string replacement way, but only if you are sure that the string is unique enough to not cause any issues. You can use this tool multiple times per file.<br />
Use the {ToolName.ReplaceString} tool to make single edits in the file in string replacement way, but only if you are sure that the string is unique enough to not cause any issues. You can use this tool multiple times per file.<br />
Use {ToolName.MultiReplaceString} tool when you need to make multiple string replacements across one or more files in a single operation.<br />
Use the {ToolName.EditFile} tool to insert code into a file.<br />
When editing files, group your changes by file.<br />
NEVER show the changes to the user, just call the tool, and the edits will be applied and shown to the user.<br />
Expand Down
4 changes: 3 additions & 1 deletion src/extension/prompts/node/agent/agentPrompt.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,7 @@ export class AgentUserMessage extends PromptElement<AgentUserMessageProps> {

const query = await this.promptVariablesService.resolveToolReferencesInPrompt(this.props.request, this.props.toolReferences ?? []);
const hasReplaceStringTool = !!this.props.availableTools?.find(tool => tool.name === ToolName.ReplaceString);
const hasMultiReplaceStringTool = !!this.props.availableTools?.find(tool => tool.name === ToolName.MultiReplaceString);
const hasApplyPatchTool = !!this.props.availableTools?.find(tool => tool.name === ToolName.ApplyPatch);
const hasCreateFileTool = !!this.props.availableTools?.find(tool => tool.name === ToolName.CreateFile);
const hasEditFileTool = !!this.props.availableTools?.find(tool => tool.name === ToolName.EditFile);
Expand Down Expand Up @@ -363,6 +364,7 @@ export class AgentUserMessage extends PromptElement<AgentUserMessageProps> {
{getEditingReminder(hasEditFileTool, hasReplaceStringTool, modelNeedsStrongReplaceStringHint(this.props.endpoint))}
<NotebookReminderInstructions chatVariables={this.props.chatVariables} query={this.props.request} />
{getExplanationReminder(this.props.endpoint.family, hasTodoTool)}
{hasMultiReplaceStringTool && <>For maximum efficiency, whenever you plan to perform multiple independent edit operations, invoke them simultaneously using {ToolName.MultiReplaceString} tool rather than sequentially. This will greatly improve user's cost and time efficiency leading to a better user experience.<br /></>}
</Tag>
)}
{query && <Tag name='userRequest' priority={900} flexGrow={7}>{query + attachmentHint}</Tag>}
Expand Down Expand Up @@ -686,7 +688,7 @@ export function getEditingReminder(hasEditFileTool: boolean, hasReplaceStringToo
}
if (hasEditFileTool && hasReplaceStringTool) {
if (useStrongReplaceStringHint) {
lines.push(<>You must always try making file edits using {ToolName.ReplaceString} tool. NEVER use {ToolName.EditFile} unless told to by the user or by a tool.</>);
lines.push(<>You must always try making file edits using {ToolName.ReplaceString} or {ToolName.MultiReplaceString} tools. NEVER use {ToolName.EditFile} unless told to by the user or by a tool.</>);
} else {
lines.push(<>It is much faster to edit using the {ToolName.ReplaceString} tool. Prefer {ToolName.ReplaceString} for making edits and only fall back to {ToolName.EditFile} if it fails.</>);
}
Expand Down
2 changes: 2 additions & 0 deletions src/extension/tools/common/toolNames.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export enum ToolName {
EditFile = 'insert_edit_into_file',
CreateFile = 'create_file',
ReplaceString = 'replace_string_in_file',
MultiReplaceString = 'multi_replace_string_in_file',
EditNotebook = 'edit_notebook_file',
RunNotebookCell = 'run_notebook_cell',
GetNotebookSummary = 'copilot_getNotebookSummary',
Expand Down Expand Up @@ -76,6 +77,7 @@ export enum ContributedToolName {
EditFile = 'copilot_insertEdit',
CreateFile = 'copilot_createFile',
ReplaceString = 'copilot_replaceString',
MultiReplaceString = 'copilot_multiReplaceString',
EditNotebook = 'copilot_editNotebook',
RunNotebookCell = 'copilot_runNotebookCell',
GetNotebookSummary = 'copilot_getNotebookSummary',
Expand Down
1 change: 1 addition & 0 deletions src/extension/tools/node/allTools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import './githubRepoTool';
import './insertEditTool';
import './installExtensionTool';
import './listDirTool';
import './multiReplaceStringTool';
import './newNotebookTool';
import './newWorkspace/newWorkspaceTool';
import './newWorkspace/projectSetupInfoTool';
Expand Down
Loading
Loading