| 95 | } |
| 96 | |
| 97 | class EditToolInvocation implements ToolInvocation<EditToolParams, ToolResult> { |
| 98 | constructor( |
| 99 | private readonly config: Config, |
| 100 | public params: EditToolParams, |
| 101 | ) {} |
| 102 | |
| 103 | toolLocations(): ToolLocation[] { |
| 104 | return [{ path: this.params.file_path }]; |
| 105 | } |
| 106 | |
| 107 | /** |
| 108 | * Calculates the potential outcome of an edit operation. |
| 109 | * @param params Parameters for the edit operation |
| 110 | * @returns An object describing the potential edit outcome |
| 111 | * @throws File system errors if reading the file fails unexpectedly (e.g., permissions) |
| 112 | */ |
| 113 | private async calculateEdit( |
| 114 | params: EditToolParams, |
| 115 | abortSignal: AbortSignal, |
| 116 | ): Promise<CalculatedEdit> { |
| 117 | const expectedReplacements = params.expected_replacements ?? 1; |
| 118 | let currentContent: string | null = null; |
| 119 | let fileExists = false; |
| 120 | let isNewFile = false; |
| 121 | let finalNewString = params.new_string; |
| 122 | let finalOldString = params.old_string; |
| 123 | let occurrences = 0; |
| 124 | let error: |
| 125 | | { display: string; raw: string; type: ToolErrorType } |
| 126 | | undefined = undefined; |
| 127 | |
| 128 | try { |
| 129 | currentContent = fs.readFileSync(params.file_path, 'utf8'); |
| 130 | // Normalize line endings to LF for consistent processing. |
| 131 | currentContent = currentContent.replace(/\r\n/g, '\n'); |
| 132 | fileExists = true; |
| 133 | } catch (err: unknown) { |
| 134 | if (!isNodeError(err) || err.code !== 'ENOENT') { |
| 135 | // Rethrow unexpected FS errors (permissions, etc.) |
| 136 | throw err; |
| 137 | } |
| 138 | fileExists = false; |
| 139 | } |
| 140 | |
| 141 | if (params.old_string === '' && !fileExists) { |
| 142 | // Creating a new file |
| 143 | isNewFile = true; |
| 144 | } else if (!fileExists) { |
| 145 | // Trying to edit a nonexistent file (and old_string is not empty) |
| 146 | error = { |
| 147 | display: `File not found. Cannot apply edit. Use an empty old_string to create a new file.`, |
| 148 | raw: `File not found: ${params.file_path}`, |
| 149 | type: ToolErrorType.FILE_NOT_FOUND, |
| 150 | }; |
| 151 | } else if (currentContent !== null) { |
| 152 | // Editing an existing file |
| 153 | const correctedEdit = await ensureCorrectEdit( |
| 154 | params.file_path, |
nothing calls this directly
no outgoing calls
no test coverage detected