summaryrefslogtreecommitdiff
path: root/packages/core/src/tools/edit.ts
diff options
context:
space:
mode:
Diffstat (limited to 'packages/core/src/tools/edit.ts')
-rw-r--r--packages/core/src/tools/edit.ts172
1 files changed, 40 insertions, 132 deletions
diff --git a/packages/core/src/tools/edit.ts b/packages/core/src/tools/edit.ts
index 39352121..3317d460 100644
--- a/packages/core/src/tools/edit.ts
+++ b/packages/core/src/tools/edit.ts
@@ -6,7 +6,6 @@
import * as fs from 'fs';
import * as path from 'path';
-import * as os from 'os';
import * as Diff from 'diff';
import {
BaseTool,
@@ -23,9 +22,8 @@ import { GeminiClient } from '../core/client.js';
import { Config, ApprovalMode } from '../config/config.js';
import { ensureCorrectEdit } from '../utils/editCorrector.js';
import { DEFAULT_DIFF_OPTIONS } from './diffOptions.js';
-import { openDiff } from '../utils/editor.js';
import { ReadFileTool } from './read-file.js';
-import { EditorType } from '../utils/editor.js';
+import { ModifiableTool, ModifyContext } from './modifiable-tool.js';
/**
* Parameters for the Edit tool
@@ -64,13 +62,14 @@ interface CalculatedEdit {
/**
* Implementation of the Edit tool logic
*/
-export class EditTool extends BaseTool<EditToolParams, ToolResult> {
+export class EditTool
+ extends BaseTool<EditToolParams, ToolResult>
+ implements ModifiableTool<EditToolParams>
+{
static readonly Name = 'replace';
private readonly config: Config;
private readonly rootDirectory: string;
private readonly client: GeminiClient;
- private tempOldDiffPath?: string;
- private tempNewDiffPath?: string;
/**
* Creates a new instance of the EditLogic
@@ -433,132 +432,6 @@ Expectation for required parameters:
}
/**
- * Creates temp files for the current and proposed file contents and opens a diff tool.
- * When the diff tool is closed, the tool will check if the file has been modified and provide the updated params.
- * @returns Updated params and diff if the file has been modified, undefined otherwise.
- */
- async onModify(
- params: EditToolParams,
- _abortSignal: AbortSignal,
- editorType: EditorType,
- ): Promise<
- { updatedParams: EditToolParams; updatedDiff: string } | undefined
- > {
- const { oldPath, newPath } = this.createTempFiles(params);
- this.tempOldDiffPath = oldPath;
- this.tempNewDiffPath = newPath;
-
- await openDiff(this.tempOldDiffPath, this.tempNewDiffPath, editorType);
- return await this.getUpdatedParamsIfModified(params, _abortSignal);
- }
-
- private async getUpdatedParamsIfModified(
- params: EditToolParams,
- _abortSignal: AbortSignal,
- ): Promise<
- { updatedParams: EditToolParams; updatedDiff: string } | undefined
- > {
- if (!this.tempOldDiffPath || !this.tempNewDiffPath) return undefined;
- let oldContent = '';
- let newContent = '';
- try {
- oldContent = fs.readFileSync(this.tempOldDiffPath, 'utf8');
- } catch (err) {
- if (!isNodeError(err) || err.code !== 'ENOENT') throw err;
- oldContent = '';
- }
- try {
- newContent = fs.readFileSync(this.tempNewDiffPath, 'utf8');
- } catch (err) {
- if (!isNodeError(err) || err.code !== 'ENOENT') throw err;
- newContent = '';
- }
-
- // Combine the edits into a single edit
- const updatedParams: EditToolParams = {
- ...params,
- old_string: oldContent,
- new_string: newContent,
- };
-
- const updatedDiff = Diff.createPatch(
- path.basename(params.file_path),
- oldContent,
- newContent,
- 'Current',
- 'Proposed',
- DEFAULT_DIFF_OPTIONS,
- );
-
- this.deleteTempFiles();
- return { updatedParams, updatedDiff };
- }
-
- private createTempFiles(params: EditToolParams): Record<string, string> {
- this.deleteTempFiles();
-
- const tempDir = os.tmpdir();
- const diffDir = path.join(tempDir, 'gemini-cli-edit-tool-diffs');
-
- if (!fs.existsSync(diffDir)) {
- fs.mkdirSync(diffDir, { recursive: true });
- }
-
- const fileName = path.basename(params.file_path);
- const timestamp = Date.now();
- const tempOldPath = path.join(
- diffDir,
- `gemini-cli-edit-${fileName}-old-${timestamp}`,
- );
- const tempNewPath = path.join(
- diffDir,
- `gemini-cli-edit-${fileName}-new-${timestamp}`,
- );
-
- let currentContent = '';
- try {
- currentContent = fs.readFileSync(params.file_path, 'utf8');
- } catch (err) {
- if (!isNodeError(err) || err.code !== 'ENOENT') throw err;
- currentContent = '';
- }
-
- let proposedContent = currentContent;
- proposedContent = this._applyReplacement(
- proposedContent,
- params.old_string,
- params.new_string,
- params.old_string === '' && currentContent === '',
- );
-
- fs.writeFileSync(tempOldPath, currentContent, 'utf8');
- fs.writeFileSync(tempNewPath, proposedContent, 'utf8');
- return {
- oldPath: tempOldPath,
- newPath: tempNewPath,
- };
- }
-
- private deleteTempFiles(): void {
- try {
- if (this.tempOldDiffPath) {
- fs.unlinkSync(this.tempOldDiffPath);
- this.tempOldDiffPath = undefined;
- }
- } catch {
- console.error(`Error deleting temp diff file: `, this.tempOldDiffPath);
- }
- try {
- if (this.tempNewDiffPath) {
- fs.unlinkSync(this.tempNewDiffPath);
- this.tempNewDiffPath = undefined;
- }
- } catch {
- console.error(`Error deleting temp diff file: `, this.tempNewDiffPath);
- }
- }
-
- /**
* Creates parent directories if they don't exist
*/
private ensureParentDirectoriesExist(filePath: string): void {
@@ -567,4 +440,39 @@ Expectation for required parameters:
fs.mkdirSync(dirName, { recursive: true });
}
}
+
+ getModifyContext(_: AbortSignal): ModifyContext<EditToolParams> {
+ return {
+ getFilePath: (params: EditToolParams) => params.file_path,
+ getCurrentContent: async (params: EditToolParams): Promise<string> => {
+ try {
+ return fs.readFileSync(params.file_path, 'utf8');
+ } catch (err) {
+ if (!isNodeError(err) || err.code !== 'ENOENT') throw err;
+ return '';
+ }
+ },
+ getProposedContent: async (params: EditToolParams): Promise<string> => {
+ try {
+ const currentContent = fs.readFileSync(params.file_path, 'utf8');
+ return this._applyReplacement(
+ currentContent,
+ params.old_string,
+ params.new_string,
+ params.old_string === '' && currentContent === '',
+ );
+ } catch (err) {
+ if (!isNodeError(err) || err.code !== 'ENOENT') throw err;
+ return '';
+ }
+ },
+ createUpdatedParams: (
+ modifiedProposedContent: string,
+ originalParams: EditToolParams,
+ ): EditToolParams => ({
+ ...originalParams,
+ new_string: modifiedProposedContent,
+ }),
+ };
+ }
}