Skip to content

Commit 755a36b

Browse files
committed
feat: allow file_edit_insert to create files implicitly
1 parent 4e97ff9 commit 755a36b

File tree

4 files changed

+23
-23
lines changed

4 files changed

+23
-23
lines changed

src/services/tools/file_edit_insert.test.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,20 +96,33 @@ describe("file_edit_insert tool", () => {
9696
}
9797
});
9898

99-
it("creates new file when create flag is provided", async () => {
99+
it("creates a new file without requiring create flag or guards", async () => {
100100
const newFile = path.join(testDir, "new.txt");
101101
const tool = createTestTool(testDir);
102102
const args: FileEditInsertToolArgs = {
103103
file_path: path.relative(testDir, newFile),
104104
content: "Hello world!\n",
105-
create: true,
106105
};
107106

108107
const result = (await tool.execute!(args, mockToolCallOptions)) as FileEditInsertToolResult;
109108
expect(result.success).toBe(true);
110109
expect(await fs.readFile(newFile, "utf-8")).toBe("Hello world!\n");
111110
});
112111

112+
it("still supports the legacy create flag for backwards compatibility", async () => {
113+
const newFile = path.join(testDir, "legacy.txt");
114+
const tool = createTestTool(testDir);
115+
const args: FileEditInsertToolArgs = {
116+
file_path: path.relative(testDir, newFile),
117+
content: "Legacy\n",
118+
create: true,
119+
};
120+
121+
const result = (await tool.execute!(args, mockToolCallOptions)) as FileEditInsertToolResult;
122+
expect(result.success).toBe(true);
123+
expect(await fs.readFile(newFile, "utf-8")).toBe("Legacy\n");
124+
});
125+
113126
it("fails when no guards are provided", async () => {
114127
const tool = createTestTool(testDir);
115128
const args: FileEditInsertToolArgs = {

src/services/tools/file_edit_insert.ts

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ export const createFileEditInsertTool: ToolFactory = (config: ToolConfiguration)
4848
description: TOOL_DEFINITIONS.file_edit_insert.description,
4949
inputSchema: TOOL_DEFINITIONS.file_edit_insert.schema,
5050
execute: async (
51-
{ file_path, content, before, after, create }: FileEditInsertToolArgs,
51+
{ file_path, content, before, after }: FileEditInsertToolArgs,
5252
{ abortSignal }
5353
): Promise<FileEditInsertToolResult> => {
5454
try {
@@ -71,14 +71,6 @@ export const createFileEditInsertTool: ToolFactory = (config: ToolConfiguration)
7171
const exists = await fileExists(config.runtime, resolvedPath, abortSignal);
7272

7373
if (!exists) {
74-
if (!create) {
75-
return {
76-
success: false,
77-
error: `File not found: ${file_path}. Set create: true to create it.`,
78-
note: `${EDIT_FAILED_NOTE_PREFIX} File does not exist. Set create: true to create it, or check the file path.`,
79-
};
80-
}
81-
8274
try {
8375
await writeFileString(config.runtime, resolvedPath, content, abortSignal);
8476
} catch (err) {
@@ -139,7 +131,7 @@ function insertContent(
139131
}
140132

141133
if (before === undefined && after === undefined) {
142-
return guardFailure("Provide either a before or after guard to anchor the insertion point.");
134+
return guardFailure("Provide either a before or after guard when editing existing files.");
143135
}
144136

145137
return insertWithGuards(originalContent, contentToInsert, { before, after });

src/types/tools.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ export interface FileEditErrorResult {
7474
export interface FileEditInsertToolArgs {
7575
file_path: string;
7676
content: string;
77-
/** When true, create the file if it doesn't exist */
77+
/** @deprecated Legacy no-op. Files are created automatically when missing. */
7878
create?: boolean;
7979
/** Optional substring that must appear immediately before the insertion point */
8080
before?: string;

src/utils/tools/toolDefinitions.ts

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -116,13 +116,16 @@ export const TOOL_DEFINITIONS = {
116116
description:
117117
"Insert content into a file using substring guards. " +
118118
"Provide exactly one of before or after to anchor the operation when editing an existing file. " +
119-
"Set create: true to write a brand new file without guards. " +
119+
"Missing files are created automatically, so guards are optional when creating new files. " +
120120
`Optional before/after substrings must uniquely match surrounding content. ${TOOL_EDIT_WARNING}`,
121121
schema: z
122122
.object({
123123
file_path: FILE_EDIT_FILE_PATH,
124124
content: z.string().describe("The content to insert"),
125-
create: z.boolean().optional().describe("If true, create the file if it does not exist"),
125+
create: z
126+
.boolean()
127+
.optional()
128+
.describe("Legacy compatibility; files are created automatically when missing, so this flag is ignored."),
126129
before: z
127130
.string()
128131
.min(1)
@@ -134,14 +137,6 @@ export const TOOL_DEFINITIONS = {
134137
.optional()
135138
.describe("Optional substring that must appear immediately after the insertion point"),
136139
})
137-
.refine(
138-
(data) => data.create === true || data.before !== undefined || data.after !== undefined,
139-
{
140-
message:
141-
"Provide before or after when editing existing files, or set create: true to write a new file.",
142-
path: ["before"],
143-
}
144-
)
145140
.refine((data) => !(data.before !== undefined && data.after !== undefined), {
146141
message: "Provide only one of before or after (not both).",
147142
path: ["before"],

0 commit comments

Comments
 (0)