Skip to content
Draft
Changes from all commits
Commits
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
73 changes: 32 additions & 41 deletions packages/core/src/tools/modifiable-tool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import { EditorType, openDiff } from '../utils/editor.js';
import os from 'os';
import path from 'path';
import fs from 'fs';
import fsp from 'node:fs/promises';
import * as Diff from 'diff';
import { DEFAULT_DIFF_OPTIONS } from './diffOptions.js';
import { isNodeError } from '../utils/errors.js';
Expand Down Expand Up @@ -49,17 +49,15 @@ export function isModifiableDeclarativeTool(
return 'getModifyContext' in tool;
}

function createTempFilesForModify(
async function createTempFilesForModify(
currentContent: string,
proposedContent: string,
file_path: string,
): { oldPath: string; newPath: string } {
): Promise<{ oldPath: string; newPath: string }> {
const tempDir = os.tmpdir();
const diffDir = path.join(tempDir, 'gemini-cli-tool-modify-diffs');

if (!fs.existsSync(diffDir)) {
fs.mkdirSync(diffDir, { recursive: true });
}
await fsp.mkdir(diffDir, { recursive: true });

const ext = path.extname(file_path);
const fileName = path.basename(file_path, ext);
Expand All @@ -73,34 +71,30 @@ function createTempFilesForModify(
`gemini-cli-modify-${fileName}-new-${timestamp}${ext}`,
);

fs.writeFileSync(tempOldPath, currentContent, 'utf8');
fs.writeFileSync(tempNewPath, proposedContent, 'utf8');
await Promise.all([
fsp.writeFile(tempOldPath, currentContent, 'utf8'),
fsp.writeFile(tempNewPath, proposedContent, 'utf8'),
]);

return { oldPath: tempOldPath, newPath: tempNewPath };
}

function getUpdatedParams<ToolParams>(
async function getUpdatedParams<ToolParams>(
tmpOldPath: string,
tempNewPath: string,
originalParams: ToolParams,
modifyContext: ModifyContext<ToolParams>,
): { updatedParams: ToolParams; updatedDiff: string } {
let oldContent = '';
let newContent = '';

try {
oldContent = fs.readFileSync(tmpOldPath, 'utf8');
} catch (err) {
if (!isNodeError(err) || err.code !== 'ENOENT') throw err;
oldContent = '';
}

try {
newContent = fs.readFileSync(tempNewPath, 'utf8');
} catch (err) {
if (!isNodeError(err) || err.code !== 'ENOENT') throw err;
newContent = '';
}
): Promise<{ updatedParams: ToolParams; updatedDiff: string }> {
const [oldContent, newContent] = await Promise.all([
fsp.readFile(tmpOldPath, 'utf8').catch((err) => {
if (!isNodeError(err) || err.code !== 'ENOENT') throw err;
return '';
}),
fsp.readFile(tempNewPath, 'utf8').catch((err) => {
if (!isNodeError(err) || err.code !== 'ENOENT') throw err;
return '';
}),
]);

const updatedParams = modifyContext.createUpdatedParams(
oldContent,
Expand All @@ -119,18 +113,15 @@ function getUpdatedParams<ToolParams>(
return { updatedParams, updatedDiff };
}

function deleteTempFiles(oldPath: string, newPath: string): void {
try {
fs.unlinkSync(oldPath);
} catch {
console.error(`Error deleting temp diff file: ${oldPath}`);
}

try {
fs.unlinkSync(newPath);
} catch {
console.error(`Error deleting temp diff file: ${newPath}`);
}
async function deleteTempFiles(oldPath: string, newPath: string): Promise<void> {
await Promise.all([
fsp.unlink(oldPath).catch(() => {
console.error(`Error deleting temp diff file: ${oldPath}`);
}),
fsp.unlink(newPath).catch(() => {
console.error(`Error deleting temp diff file: ${newPath}`);
}),
]);
}

/**
Expand All @@ -148,15 +139,15 @@ export async function modifyWithEditor<ToolParams>(
const proposedContent =
await modifyContext.getProposedContent(originalParams);

const { oldPath, newPath } = createTempFilesForModify(
const { oldPath, newPath } = await createTempFilesForModify(
currentContent,
proposedContent,
modifyContext.getFilePath(originalParams),
);

try {
await openDiff(oldPath, newPath, editorType, onEditorClose);
const result = getUpdatedParams(
const result = await getUpdatedParams(
oldPath,
newPath,
originalParams,
Expand All @@ -165,6 +156,6 @@ export async function modifyWithEditor<ToolParams>(

return result;
} finally {
deleteTempFiles(oldPath, newPath);
await deleteTempFiles(oldPath, newPath);
}
}