diff --git a/src/core/tools/ApplyDiffTool.ts b/src/core/tools/ApplyDiffTool.ts index 3b664b3bd2..bc0ad7b4c2 100644 --- a/src/core/tools/ApplyDiffTool.ts +++ b/src/core/tools/ApplyDiffTool.ts @@ -130,10 +130,9 @@ export class ApplyDiffTool extends BaseTool<"apply_diff"> { const state = await provider?.getState() const diagnosticsEnabled = state?.diagnosticsEnabled ?? true const writeDelayMs = state?.writeDelayMs ?? DEFAULT_WRITE_DELAY_MS - const isPreventFocusDisruptionEnabled = experiments.isEnabled( - state?.experiments ?? {}, - EXPERIMENT_IDS.PREVENT_FOCUS_DISRUPTION, - ) + const isPreventFocusDisruptionEnabled = + experiments.isEnabled(state?.experiments ?? {}, EXPERIMENT_IDS.PREVENT_FOCUS_DISRUPTION) || + (state?.autoApprovalEnabled === true && state?.alwaysAllowWrite === true) // Check if file is write-protected const isWriteProtected = task.rooProtectedController?.isWriteProtected(relPath) || false diff --git a/src/core/tools/ApplyPatchTool.ts b/src/core/tools/ApplyPatchTool.ts index 3f3295404b..228bb56544 100644 --- a/src/core/tools/ApplyPatchTool.ts +++ b/src/core/tools/ApplyPatchTool.ts @@ -173,10 +173,9 @@ export class ApplyPatchTool extends BaseTool<"apply_patch"> { const state = await provider?.getState() const diagnosticsEnabled = state?.diagnosticsEnabled ?? true const writeDelayMs = state?.writeDelayMs ?? DEFAULT_WRITE_DELAY_MS - const isPreventFocusDisruptionEnabled = experiments.isEnabled( - state?.experiments ?? {}, - EXPERIMENT_IDS.PREVENT_FOCUS_DISRUPTION, - ) + const isPreventFocusDisruptionEnabled = + experiments.isEnabled(state?.experiments ?? {}, EXPERIMENT_IDS.PREVENT_FOCUS_DISRUPTION) || + (state?.autoApprovalEnabled === true && state?.alwaysAllowWrite === true) const sanitizedDiff = sanitizeUnifiedDiff(diff || "") const diffStats = computeDiffStats(sanitizedDiff) || undefined @@ -329,10 +328,9 @@ export class ApplyPatchTool extends BaseTool<"apply_patch"> { const state = await provider?.getState() const diagnosticsEnabled = state?.diagnosticsEnabled ?? true const writeDelayMs = state?.writeDelayMs ?? DEFAULT_WRITE_DELAY_MS - const isPreventFocusDisruptionEnabled = experiments.isEnabled( - state?.experiments ?? {}, - EXPERIMENT_IDS.PREVENT_FOCUS_DISRUPTION, - ) + const isPreventFocusDisruptionEnabled = + experiments.isEnabled(state?.experiments ?? {}, EXPERIMENT_IDS.PREVENT_FOCUS_DISRUPTION) || + (state?.autoApprovalEnabled === true && state?.alwaysAllowWrite === true) const sanitizedDiff = sanitizeUnifiedDiff(diff) const diffStats = computeDiffStats(sanitizedDiff) || undefined diff --git a/src/core/tools/EditFileTool.ts b/src/core/tools/EditFileTool.ts index 2495a372bc..f500afb615 100644 --- a/src/core/tools/EditFileTool.ts +++ b/src/core/tools/EditFileTool.ts @@ -392,10 +392,9 @@ export class EditFileTool extends BaseTool<"edit_file"> { const state = await provider?.getState() const diagnosticsEnabled = state?.diagnosticsEnabled ?? true const writeDelayMs = state?.writeDelayMs ?? DEFAULT_WRITE_DELAY_MS - const isPreventFocusDisruptionEnabled = experiments.isEnabled( - state?.experiments ?? {}, - EXPERIMENT_IDS.PREVENT_FOCUS_DISRUPTION, - ) + const isPreventFocusDisruptionEnabled = + experiments.isEnabled(state?.experiments ?? {}, EXPERIMENT_IDS.PREVENT_FOCUS_DISRUPTION) || + (state?.autoApprovalEnabled === true && state?.alwaysAllowWrite === true) const sanitizedDiff = sanitizeUnifiedDiff(diff || "") const diffStats = computeDiffStats(sanitizedDiff) || undefined diff --git a/src/core/tools/EditTool.ts b/src/core/tools/EditTool.ts index 79338c17a6..4ef974d320 100644 --- a/src/core/tools/EditTool.ts +++ b/src/core/tools/EditTool.ts @@ -167,10 +167,9 @@ export class EditTool extends BaseTool<"edit"> { const state = await provider?.getState() const diagnosticsEnabled = state?.diagnosticsEnabled ?? true const writeDelayMs = state?.writeDelayMs ?? DEFAULT_WRITE_DELAY_MS - const isPreventFocusDisruptionEnabled = experiments.isEnabled( - state?.experiments ?? {}, - EXPERIMENT_IDS.PREVENT_FOCUS_DISRUPTION, - ) + const isPreventFocusDisruptionEnabled = + experiments.isEnabled(state?.experiments ?? {}, EXPERIMENT_IDS.PREVENT_FOCUS_DISRUPTION) || + (state?.autoApprovalEnabled === true && state?.alwaysAllowWrite === true) const sanitizedDiff = sanitizeUnifiedDiff(diff) const diffStats = computeDiffStats(sanitizedDiff) || undefined diff --git a/src/core/tools/SearchReplaceTool.ts b/src/core/tools/SearchReplaceTool.ts index 2d8817364f..76b4394d43 100644 --- a/src/core/tools/SearchReplaceTool.ts +++ b/src/core/tools/SearchReplaceTool.ts @@ -163,10 +163,9 @@ export class SearchReplaceTool extends BaseTool<"search_replace"> { const state = await provider?.getState() const diagnosticsEnabled = state?.diagnosticsEnabled ?? true const writeDelayMs = state?.writeDelayMs ?? DEFAULT_WRITE_DELAY_MS - const isPreventFocusDisruptionEnabled = experiments.isEnabled( - state?.experiments ?? {}, - EXPERIMENT_IDS.PREVENT_FOCUS_DISRUPTION, - ) + const isPreventFocusDisruptionEnabled = + experiments.isEnabled(state?.experiments ?? {}, EXPERIMENT_IDS.PREVENT_FOCUS_DISRUPTION) || + (state?.autoApprovalEnabled === true && state?.alwaysAllowWrite === true) const sanitizedDiff = sanitizeUnifiedDiff(diff) const diffStats = computeDiffStats(sanitizedDiff) || undefined diff --git a/src/core/tools/WriteToFileTool.ts b/src/core/tools/WriteToFileTool.ts index c8455ef3d9..4b37a24998 100644 --- a/src/core/tools/WriteToFileTool.ts +++ b/src/core/tools/WriteToFileTool.ts @@ -103,10 +103,9 @@ export class WriteToFileTool extends BaseTool<"write_to_file"> { const state = await provider?.getState() const diagnosticsEnabled = state?.diagnosticsEnabled ?? true const writeDelayMs = state?.writeDelayMs ?? DEFAULT_WRITE_DELAY_MS - const isPreventFocusDisruptionEnabled = experiments.isEnabled( - state?.experiments ?? {}, - EXPERIMENT_IDS.PREVENT_FOCUS_DISRUPTION, - ) + const isPreventFocusDisruptionEnabled = + experiments.isEnabled(state?.experiments ?? {}, EXPERIMENT_IDS.PREVENT_FOCUS_DISRUPTION) || + (state?.autoApprovalEnabled === true && state?.alwaysAllowWrite === true) if (isPreventFocusDisruptionEnabled) { task.diffViewProvider.editType = fileExists ? "modify" : "create" @@ -204,10 +203,9 @@ export class WriteToFileTool extends BaseTool<"write_to_file"> { const provider = task.providerRef.deref() const state = await provider?.getState() - const isPreventFocusDisruptionEnabled = experiments.isEnabled( - state?.experiments ?? {}, - EXPERIMENT_IDS.PREVENT_FOCUS_DISRUPTION, - ) + const isPreventFocusDisruptionEnabled = + experiments.isEnabled(state?.experiments ?? {}, EXPERIMENT_IDS.PREVENT_FOCUS_DISRUPTION) || + (state?.autoApprovalEnabled === true && state?.alwaysAllowWrite === true) if (isPreventFocusDisruptionEnabled) { return diff --git a/src/core/tools/__tests__/writeToFileTool.spec.ts b/src/core/tools/__tests__/writeToFileTool.spec.ts index 6c63387ee1..d3724ca86a 100644 --- a/src/core/tools/__tests__/writeToFileTool.spec.ts +++ b/src/core/tools/__tests__/writeToFileTool.spec.ts @@ -148,6 +148,11 @@ describe("writeToFileTool", () => { userEdits: null, finalContent: "final content", }), + saveDirectly: vi.fn().mockResolvedValue({ + newProblemsMessage: "", + userEdits: null, + finalContent: "final content", + }), scrollToFirstDiff: vi.fn(), updateDiagnosticSettings: vi.fn(), pushToolWriteResult: vi.fn().mockImplementation(async function ( @@ -442,6 +447,69 @@ describe("writeToFileTool", () => { }) }) + describe("auto-approve write skips diff view", () => { + it("uses saveDirectly when autoApprovalEnabled and alwaysAllowWrite are both true", async () => { + mockCline.providerRef = { + deref: vi.fn().mockReturnValue({ + getState: vi.fn().mockResolvedValue({ + diagnosticsEnabled: true, + writeDelayMs: 1000, + autoApprovalEnabled: true, + alwaysAllowWrite: true, + }), + }), + } + + await executeWriteFileTool({}, { fileExists: false }) + + // Should NOT open diff view + expect(mockCline.diffViewProvider.open).not.toHaveBeenCalled() + // Should use saveDirectly instead of saveChanges + expect(mockCline.diffViewProvider.saveDirectly).toHaveBeenCalled() + expect(mockCline.diffViewProvider.saveChanges).not.toHaveBeenCalled() + }) + + it("uses diff view when autoApprovalEnabled is true but alwaysAllowWrite is false", async () => { + mockCline.providerRef = { + deref: vi.fn().mockReturnValue({ + getState: vi.fn().mockResolvedValue({ + diagnosticsEnabled: true, + writeDelayMs: 1000, + autoApprovalEnabled: true, + alwaysAllowWrite: false, + }), + }), + } + + await executeWriteFileTool({}, { fileExists: false }) + + // Should open diff view (normal path) + expect(mockCline.diffViewProvider.open).toHaveBeenCalled() + expect(mockCline.diffViewProvider.saveChanges).toHaveBeenCalled() + expect(mockCline.diffViewProvider.saveDirectly).not.toHaveBeenCalled() + }) + + it("uses diff view when autoApprovalEnabled is false even if alwaysAllowWrite is true", async () => { + mockCline.providerRef = { + deref: vi.fn().mockReturnValue({ + getState: vi.fn().mockResolvedValue({ + diagnosticsEnabled: true, + writeDelayMs: 1000, + autoApprovalEnabled: false, + alwaysAllowWrite: true, + }), + }), + } + + await executeWriteFileTool({}, { fileExists: false }) + + // Should open diff view (normal path) + expect(mockCline.diffViewProvider.open).toHaveBeenCalled() + expect(mockCline.diffViewProvider.saveChanges).toHaveBeenCalled() + expect(mockCline.diffViewProvider.saveDirectly).not.toHaveBeenCalled() + }) + }) + describe("error handling", () => { it("handles general file operation errors", async () => { mockCline.diffViewProvider.open.mockRejectedValue(new Error("General error"))