Skip to content
Open
Show file tree
Hide file tree
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
5 changes: 5 additions & 0 deletions .changeset/easy-years-boil.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"kilo-code": minor
---

Restore apply_diff tool, correctly hidden behind Diff Enabled Advanced setting
2 changes: 0 additions & 2 deletions packages/types/src/global-settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,6 @@ export const globalSettingsSchema = z.object({
diagnosticsEnabled: z.boolean().optional(),

rateLimitSeconds: z.number().optional(),
diffEnabled: z.boolean().optional(),
fuzzyMatchThreshold: z.number().optional(),
experiments: experimentsSchema.optional(),

Expand Down Expand Up @@ -379,7 +378,6 @@ export const EVALS_SETTINGS: RooCodeSettings = {

diagnosticsEnabled: true,

diffEnabled: true,
fuzzyMatchThreshold: 1,

enableCheckpoints: false,
Expand Down
1 change: 1 addition & 0 deletions packages/types/src/tool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export const toolNames = [
"fetch_instructions",
"codebase_search",
// kilocode_change start
"search_and_replace",
"edit_file",
"new_rule",
"report_bug",
Expand Down
10 changes: 1 addition & 9 deletions src/__tests__/common-mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -213,15 +213,7 @@ export function setupCommonMocks() {
Task: vi
.fn()
.mockImplementation(
(
_provider,
_apiConfiguration,
_customInstructions,
_diffEnabled,
_fuzzyMatchThreshold,
_task,
taskId,
) => ({
(_provider, _apiConfiguration, _customInstructions, _fuzzyMatchThreshold, _task, taskId) => ({
api: undefined,
abortTask: vi.fn(),
handleWebviewAskResponse: vi.fn(),
Expand Down
41 changes: 0 additions & 41 deletions src/core/config/ProviderSettingsManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,12 +170,6 @@ export class ProviderSettingsManager {
isDirty = true
}

if (!providerProfiles.migrations.diffSettingsMigrated) {
await this.migrateDiffSettings(providerProfiles)
providerProfiles.migrations.diffSettingsMigrated = true
isDirty = true
}

if (!providerProfiles.migrations.openAiHeadersMigrated) {
await this.migrateOpenAiHeaders(providerProfiles)
providerProfiles.migrations.openAiHeadersMigrated = true
Expand Down Expand Up @@ -236,41 +230,6 @@ export class ProviderSettingsManager {
}
}

private async migrateDiffSettings(providerProfiles: ProviderProfiles) {
try {
let diffEnabled: boolean | undefined
let fuzzyMatchThreshold: number | undefined

try {
diffEnabled = await this.context.globalState.get<boolean>("diffEnabled")
fuzzyMatchThreshold = await this.context.globalState.get<number>("fuzzyMatchThreshold")
} catch (error) {
console.error("[MigrateDiffSettings] Error getting global diff settings:", error)
}

if (diffEnabled === undefined) {
// Failed to get the existing value, use the default.
diffEnabled = true
}

if (fuzzyMatchThreshold === undefined) {
// Failed to get the existing value, use the default.
fuzzyMatchThreshold = 1.0
}

for (const [_name, apiConfig] of Object.entries(providerProfiles.apiConfigs)) {
if (apiConfig.diffEnabled === undefined) {
apiConfig.diffEnabled = diffEnabled
}
if (apiConfig.fuzzyMatchThreshold === undefined) {
apiConfig.fuzzyMatchThreshold = fuzzyMatchThreshold
}
}
} catch (error) {
console.error(`[MigrateDiffSettings] Failed to migrate diff settings:`, error)
}
}

private async migrateOpenAiHeaders(providerProfiles: ProviderProfiles) {
try {
for (const [_name, apiConfig] of Object.entries(providerProfiles.apiConfigs)) {
Expand Down
1 change: 0 additions & 1 deletion src/core/config/__tests__/ProviderSettingsManager.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,6 @@ describe("ProviderSettingsManager", () => {
modeApiConfigs: {},
migrations: {
rateLimitSecondsMigrated: true,
diffSettingsMigrated: true,
openAiHeadersMigrated: true,
consecutiveMistakeLimitMigrated: true,
todoListEnabledMigrated: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,6 @@ describe("filterNativeToolsForMode", () => {
// Should include all tools (code mode has all groups)
expect(toolNames).toContain("read_file")
expect(toolNames).toContain("write_to_file")
expect(toolNames).toContain("apply_diff")
expect(toolNames).toContain("execute_command")
expect(toolNames).toContain("browser_action")
expect(toolNames).toContain("ask_followup_question")
Expand Down
12 changes: 12 additions & 0 deletions src/core/prompts/tools/filter-tools-for-mode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,18 @@ export function filterNativeToolsForMode(
allowedToolNames.delete("run_slash_command")
}

// Conditionally exclude run_slash_command if experiment is not enabled
if (!experiments?.runSlashCommand) {
allowedToolNames.delete("run_slash_command")
}

// Conditionally exclude apply_diff if disabled in settings
if (state?.apiConfiguration?.diffEnabled != true) {
allowedToolNames.delete("apply_diff")
} else {
allowedToolNames.add("search_and_replace")
}

// Conditionally exclude browser_action if disabled in settings
if (
settings?.browserToolEnabled === false ||
Expand Down
2 changes: 1 addition & 1 deletion src/core/prompts/tools/native-tools/apply_diff.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type OpenAI from "openai"

export const apply_diff_single_file = {
export const apply_diff = {
type: "function",
function: {
name: "apply_diff",
Expand Down
3 changes: 2 additions & 1 deletion src/core/prompts/tools/native-tools/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import searchFiles from "./search_files"
import switchMode from "./switch_mode"
import updateTodoList from "./update_todo_list"
import writeToFile from "./write_to_file"
// import { apply_diff_single_file } from "./apply_diff" // kilocode_change
import { apply_diff } from "./apply_diff" // kilocode_change

import searchAndReplace from "./kilocode/search_and_replace"
import deleteFile from "./kilocode/delete_file"
Expand Down Expand Up @@ -47,6 +47,7 @@ export const nativeTools = [
listFiles,
newTask,
read_file,
apply_diff, //kilocode_change
runSlashCommand,
searchFiles,
switchMode,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export type SearchAndReplaceParameters = z.infer<typeof SearchAndReplaceParamete
export default {
type: "function",
function: {
name: "apply_diff",
name: "search_and_replace",
description: "Replace a specific string in a file with a new string. This is used for making precise edits.",
strict: true,
parameters: z.toJSONSchema(SearchAndReplaceParametersSchema),
Expand Down
10 changes: 5 additions & 5 deletions src/core/tools/kilocode/searchAndReplaceTool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ export async function searchAndReplaceTool(

if (!fileExists) {
cline.consecutiveMistakeCount++
cline.recordToolError("apply_diff", "file_does_not_exist")
cline.recordToolError("search_and_replace", "file_does_not_exist")
const formattedError = formatResponse.toolError(
`File does not exist at path: ${absolutePath}\nThe specified file could not be found. Please verify the file path and try again.`,
)
Expand All @@ -118,7 +118,7 @@ export async function searchAndReplaceTool(
fileContent = await fs.readFile(absolutePath, "utf-8")
} catch (error) {
cline.consecutiveMistakeCount++
cline.recordToolError("apply_diff", "exception")
cline.recordToolError("search_and_replace", "exception")
const errorMessage = `Error reading file: ${absolutePath}\nFailed to read the file content: ${
error instanceof Error ? error.message : String(error)
}\nPlease verify file permissions and try again.`
Expand All @@ -138,7 +138,7 @@ export async function searchAndReplaceTool(
const matchCount = fileContent.match(searchPattern)?.length ?? 0
if (matchCount > 1) {
cline.consecutiveMistakeCount++
cline.recordToolError("apply_diff", "multiple_matches")
cline.recordToolError("search_and_replace", "multiple_matches")
pushToolResult(
formatResponse.toolError(
`Found ${matchCount} matches for replacement text. Please provide more context to make a unique match.`,
Expand All @@ -156,7 +156,7 @@ export async function searchAndReplaceTool(
const diff = formatResponse.createPrettyPatch(validRelPath, fileContent, newContent)
if (!diff) {
cline.consecutiveMistakeCount++
cline.recordToolError("apply_diff", "no_match")
cline.recordToolError("search_and_replace", "no_match")
pushToolResult(
formatResponse.toolError(
`No match found for replacement in '${validRelPath}'. Please check your text and try again.`,
Expand Down Expand Up @@ -227,7 +227,7 @@ export async function searchAndReplaceTool(
pushToolResult(message)

// Record successful tool usage and cleanup
cline.recordToolUsage("apply_diff")
cline.recordToolUsage("search_and_replace")
await cline.diffViewProvider.reset()

// Process any queued messages after file edit completes
Expand Down
9 changes: 4 additions & 5 deletions src/core/webview/ClineProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1023,7 +1023,6 @@ ${prompt}

const {
apiConfiguration,
diffEnabled: enableDiff,
enableCheckpoints,
checkpointTimeout,
fuzzyMatchThreshold,
Expand All @@ -1032,6 +1031,8 @@ ${prompt}
taskSyncEnabled,
} = await this.getState()

const enableDiff = apiConfiguration.diffEnabled ?? false

const task = new Task({
context: this.context, // kilocode_change
provider: this,
Expand Down Expand Up @@ -2038,7 +2039,6 @@ ${prompt}
soundEnabled,
ttsEnabled,
ttsSpeed,
diffEnabled,
enableCheckpoints,
checkpointTimeout,
// taskHistory, // kilocode_change
Expand Down Expand Up @@ -2213,7 +2213,6 @@ ${prompt}
soundEnabled: soundEnabled ?? false,
ttsEnabled: ttsEnabled ?? false,
ttsSpeed: ttsSpeed ?? 1.0,
diffEnabled: diffEnabled ?? true,
enableCheckpoints: enableCheckpoints ?? true,
checkpointTimeout: checkpointTimeout ?? DEFAULT_CHECKPOINT_TIMEOUT_SECONDS,
shouldShowAnnouncement: false, // kilocode_change
Expand Down Expand Up @@ -2483,7 +2482,6 @@ ${prompt}
soundEnabled: stateValues.soundEnabled ?? false,
ttsEnabled: stateValues.ttsEnabled ?? false,
ttsSpeed: stateValues.ttsSpeed ?? 1.0,
diffEnabled: stateValues.diffEnabled ?? true,
enableCheckpoints: stateValues.enableCheckpoints ?? true,
checkpointTimeout: stateValues.checkpointTimeout ?? DEFAULT_CHECKPOINT_TIMEOUT_SECONDS,
soundVolume: stateValues.soundVolume,
Expand Down Expand Up @@ -2961,7 +2959,6 @@ ${prompt}
const {
apiConfiguration,
organizationAllowList,
diffEnabled: enableDiff,
enableCheckpoints,
checkpointTimeout,
fuzzyMatchThreshold,
Expand All @@ -2970,6 +2967,8 @@ ${prompt}
remoteControlEnabled,
} = await this.getState()

const enableDiff = apiConfiguration.diffEnabled ?? false

if (!ProfileValidator.isProfileAllowed(apiConfiguration, organizationAllowList)) {
throw new OrganizationAllowListViolationError(t("common:errors.violated_organization_allowlist"))
}
Expand Down
11 changes: 0 additions & 11 deletions src/core/webview/__tests__/ClineProvider.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -551,7 +551,6 @@ describe("ClineProvider", () => {
uriScheme: "vscode",
soundEnabled: false,
ttsEnabled: false,
diffEnabled: false,
enableCheckpoints: false,
writeDelayMs: 1000,
browserViewportSize: "900x600",
Expand Down Expand Up @@ -776,7 +775,6 @@ describe("ClineProvider", () => {
// expect(state).toHaveProperty("taskHistory") // kilocode_change
expect(state).toHaveProperty("soundEnabled")
expect(state).toHaveProperty("ttsEnabled")
expect(state).toHaveProperty("diffEnabled")
expect(state).toHaveProperty("writeDelayMs")
})

Expand All @@ -788,15 +786,6 @@ describe("ClineProvider", () => {
expect(state.language).toBe("pt-BR")
})

test("diffEnabled defaults to true when not set", async () => {
// Mock globalState.get to return undefined for diffEnabled
;(mockContext.globalState.get as any).mockReturnValue(undefined)

const state = await provider.getState()

expect(state.diffEnabled).toBe(true)
})

test("writeDelayMs defaults to 1000ms", async () => {
// Mock globalState.get to return undefined for writeDelayMs
;(mockContext.globalState.get as any).mockImplementation((key: string) =>
Expand Down
3 changes: 1 addition & 2 deletions src/core/webview/generateSystemPrompt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ export const generateSystemPrompt = async (provider: ClineProvider, message: Web
customModePrompts,
customInstructions,
browserViewportSize,
diffEnabled,
mcpEnabled,
fuzzyMatchThreshold,
experiments,
Expand Down Expand Up @@ -82,7 +81,7 @@ export const generateSystemPrompt = async (provider: ClineProvider, message: Web
customModePrompts,
customModes,
customInstructions,
diffEnabled,
apiConfiguration.diffEnabled ?? true,
experiments,
enableMcpServerCreation,
language,
Expand Down
1 change: 0 additions & 1 deletion src/services/settings-sync/SettingsSyncService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ export class SettingsSyncService {
"deniedCommands",
"autoApprovalEnabled",
"fuzzyMatchThreshold",
"diffEnabled",
"directoryContextAddedContext",
"language",
"customModes",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,14 @@ describe("SettingsSyncService", () => {
"kilo-code.deniedCommands",
"kilo-code.autoApprovalEnabled",
"kilo-code.fuzzyMatchThreshold",
"kilo-code.diffEnabled",
"kilo-code.directoryContextAddedContext",
"kilo-code.language",
"kilo-code.customModes",
"kilo-code.firstInstallCompleted",
"kilo-code.telemetrySetting",
])
expect(mockOutputChannel.appendLine).toHaveBeenCalledWith(
expect.stringContaining("[SettingsSyncService] Registered 10 keys for synchronization"),
expect.stringContaining("[SettingsSyncService] Registered 9 keys for synchronization"),
)
})

Expand Down Expand Up @@ -124,7 +123,6 @@ describe("SettingsSyncService", () => {
"kilo-code.deniedCommands",
"kilo-code.autoApprovalEnabled",
"kilo-code.fuzzyMatchThreshold",
"kilo-code.diffEnabled",
"kilo-code.directoryContextAddedContext",
"kilo-code.language",
"kilo-code.customModes",
Expand Down
1 change: 0 additions & 1 deletion src/shared/ExtensionMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,6 @@ export type ExtensionState = Pick<
| "terminalZdotdir"
| "terminalCompressProgressBar"
| "diagnosticsEnabled"
| "diffEnabled"
| "fuzzyMatchThreshold"
| "morphApiKey" // kilocode_change: Morph fast apply - global setting
| "fastApplyModel" // kilocode_change: Fast Apply model selection
Expand Down
2 changes: 2 additions & 0 deletions src/shared/tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,7 @@ export const TOOL_DISPLAY_NAMES: Record<ToolName, string> = {
write_to_file: "write files",
apply_diff: "apply changes",
// kilocode_change start
search_and_replace: "search and replace contents in files",
edit_file: "edit file",
delete_file: "delete files",
report_bug: "report bug",
Expand Down Expand Up @@ -291,6 +292,7 @@ export const TOOL_GROUPS: Record<ToolGroup, ToolGroupConfig> = {
edit: {
tools: [
"apply_diff",
"search_and_replace", // kilocode_change
"edit_file", // kilocode_change: Morph fast apply
"write_to_file",
"delete_file", // kilocode_change
Expand Down
2 changes: 1 addition & 1 deletion webview-ui/src/components/settings/DiffSettingsControl.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ interface DiffSettingsControlProps {
}

export const DiffSettingsControl: React.FC<DiffSettingsControlProps> = ({
diffEnabled = true,
diffEnabled = false,
fuzzyMatchThreshold = 1.0,
onChange,
}) => {
Expand Down
Loading