feat: add granular checkboxes for re-read after edit settings

- Added reReadAfterEditGranular experiment with individual toggles for each edit operation type
- Updated all file editing tools to check their specific granular setting
- Created new ReReadAfterEditGranularSettings UI component with master toggle and individual checkboxes
- Maintained backward compatibility with legacy reReadAfterEdit setting that overrides granular settings
- Added comprehensive tests for the new granular settings functionality
- Added translations for the new UI elements

This allows users to selectively enable automatic review for specific edit types like:
- Apply Diff (search and replace blocks)
- Multi-file Apply Diff
- Write to File (complete file rewrites)
- Insert Content (add lines to files)
- Search and Replace (find and replace text)

Addresses Issue #8927 - implements the suggestion to have checkboxes for selecting review based on edit type
This commit is contained in:
Roo Code 2025-10-30 12:00:14 +00:00
parent f86af7658b
commit 8e65a4da38
14 changed files with 347 additions and 45 deletions

View file

@ -13,6 +13,7 @@ export const experimentIds = [
"imageGeneration",
"runSlashCommand",
"reReadAfterEdit",
"reReadAfterEditGranular",
] as const
export const experimentIdsSchema = z.enum(experimentIds)
@ -23,6 +24,17 @@ export type ExperimentId = z.infer<typeof experimentIdsSchema>
* Experiments
*/
// Schema for granular re-read after edit settings
export const reReadAfterEditGranularSchema = z.object({
applyDiff: z.boolean().optional(),
multiApplyDiff: z.boolean().optional(),
writeToFile: z.boolean().optional(),
insertContent: z.boolean().optional(),
searchAndReplace: z.boolean().optional(),
})
export type ReReadAfterEditGranular = z.infer<typeof reReadAfterEditGranularSchema>
export const experimentsSchema = z.object({
powerSteering: z.boolean().optional(),
multiFileApplyDiff: z.boolean().optional(),
@ -30,6 +42,7 @@ export const experimentsSchema = z.object({
imageGeneration: z.boolean().optional(),
runSlashCommand: z.boolean().optional(),
reReadAfterEdit: z.boolean().optional(),
reReadAfterEditGranular: reReadAfterEditGranularSchema.optional(),
})
export type Experiments = z.infer<typeof experimentsSchema>

View file

@ -238,11 +238,8 @@ export async function applyDiffToolLegacy(
? "\n<notice>Making multiple related changes in a single apply_diff is more efficient. If other changes are needed in this file, please include them as additional SEARCH/REPLACE blocks.</notice>"
: ""
// Check if RE_READ_AFTER_EDIT experiment is enabled
const isReReadAfterEditEnabled = experiments.isEnabled(
state?.experiments ?? {},
EXPERIMENT_IDS.RE_READ_AFTER_EDIT,
)
// Check if RE_READ_AFTER_EDIT experiment is enabled for applyDiff
const isReReadAfterEditEnabled = experiments.isReReadAfterEditEnabled(state?.experiments ?? {}, "applyDiff")
const reReadSuggestion = isReReadAfterEditEnabled
? `\n\n<review_suggestion>The file has been edited. Consider using the read_file tool to review the changes and ensure they are correct and complete.</review_suggestion>`

View file

@ -184,11 +184,8 @@ export async function insertContentTool(
// Get the formatted response message
const message = await cline.diffViewProvider.pushToolWriteResult(cline, cline.cwd, !fileExists)
// Check if RE_READ_AFTER_EDIT experiment is enabled
const isReReadAfterEditEnabled = experiments.isEnabled(
state?.experiments ?? {},
EXPERIMENT_IDS.RE_READ_AFTER_EDIT,
)
// Check if RE_READ_AFTER_EDIT experiment is enabled for insertContent
const isReReadAfterEditEnabled = experiments.isReReadAfterEditEnabled(state?.experiments ?? {}, "insertContent")
const reReadSuggestion = isReReadAfterEditEnabled
? `\n\n<review_suggestion>Content has been inserted into the file. Consider using the read_file tool to review the changes and ensure they are correct and complete.</review_suggestion>`

View file

@ -676,12 +676,12 @@ ${errorDetails ? `\nTechnical details:\n${errorDetails}\n` : ""}
? "\n<notice>Making multiple related changes in a single apply_diff is more efficient. If other changes are needed in this file, please include them as additional SEARCH/REPLACE blocks.</notice>"
: ""
// Check if RE_READ_AFTER_EDIT experiment is enabled
// Check if RE_READ_AFTER_EDIT experiment is enabled for multiApplyDiff
const provider = cline.providerRef.deref()
const state = await provider?.getState()
const isReReadAfterEditEnabled = experiments.isEnabled(
const isReReadAfterEditEnabled = experiments.isReReadAfterEditEnabled(
state?.experiments ?? {},
EXPERIMENT_IDS.RE_READ_AFTER_EDIT,
"multiApplyDiff",
)
// Count how many files were successfully edited

View file

@ -258,10 +258,10 @@ export async function searchAndReplaceTool(
false, // Always false for search_and_replace
)
// Check if RE_READ_AFTER_EDIT experiment is enabled
const isReReadAfterEditEnabled = experiments.isEnabled(
// Check if RE_READ_AFTER_EDIT experiment is enabled for searchAndReplace
const isReReadAfterEditEnabled = experiments.isReReadAfterEditEnabled(
state?.experiments ?? {},
EXPERIMENT_IDS.RE_READ_AFTER_EDIT,
"searchAndReplace",
)
const reReadSuggestion = isReReadAfterEditEnabled

View file

@ -304,10 +304,10 @@ export async function writeToFileTool(
// Get the formatted response message
const message = await cline.diffViewProvider.pushToolWriteResult(cline, cline.cwd, !fileExists)
// Check if RE_READ_AFTER_EDIT experiment is enabled
const isReReadAfterEditEnabled = experiments.isEnabled(
// Check if RE_READ_AFTER_EDIT experiment is enabled for writeToFile
const isReReadAfterEditEnabled = experiments.isReReadAfterEditEnabled(
state?.experiments ?? {},
EXPERIMENT_IDS.RE_READ_AFTER_EDIT,
"writeToFile",
)
const reReadSuggestion = isReReadAfterEditEnabled

View file

@ -0,0 +1,96 @@
import { describe, it, expect } from "vitest"
import type { Experiments, ReReadAfterEditGranular } from "@roo-code/types"
import { experiments } from "../experiments"
describe("granular re-read after edit experiment", () => {
describe("isReReadAfterEditEnabled", () => {
it("should return true when legacy reReadAfterEdit is enabled", () => {
const config: Experiments = {
reReadAfterEdit: true,
}
expect(experiments.isReReadAfterEditEnabled(config, "applyDiff")).toBe(true)
expect(experiments.isReReadAfterEditEnabled(config, "multiApplyDiff")).toBe(true)
expect(experiments.isReReadAfterEditEnabled(config, "writeToFile")).toBe(true)
expect(experiments.isReReadAfterEditEnabled(config, "insertContent")).toBe(true)
expect(experiments.isReReadAfterEditEnabled(config, "searchAndReplace")).toBe(true)
})
it("should return false when legacy reReadAfterEdit is disabled and no granular settings", () => {
const config: Experiments = {
reReadAfterEdit: false,
}
expect(experiments.isReReadAfterEditEnabled(config, "applyDiff")).toBe(false)
expect(experiments.isReReadAfterEditEnabled(config, "multiApplyDiff")).toBe(false)
expect(experiments.isReReadAfterEditEnabled(config, "writeToFile")).toBe(false)
expect(experiments.isReReadAfterEditEnabled(config, "insertContent")).toBe(false)
expect(experiments.isReReadAfterEditEnabled(config, "searchAndReplace")).toBe(false)
})
it("should return true for specific edit types when granular settings are enabled", () => {
const config: Experiments = {
reReadAfterEdit: false,
reReadAfterEditGranular: {
applyDiff: true,
multiApplyDiff: false,
writeToFile: true,
insertContent: false,
searchAndReplace: true,
},
}
expect(experiments.isReReadAfterEditEnabled(config, "applyDiff")).toBe(true)
expect(experiments.isReReadAfterEditEnabled(config, "multiApplyDiff")).toBe(false)
expect(experiments.isReReadAfterEditEnabled(config, "writeToFile")).toBe(true)
expect(experiments.isReReadAfterEditEnabled(config, "insertContent")).toBe(false)
expect(experiments.isReReadAfterEditEnabled(config, "searchAndReplace")).toBe(true)
})
it("should prioritize legacy setting over granular when both are present", () => {
const config: Experiments = {
reReadAfterEdit: true, // Legacy enabled
reReadAfterEditGranular: {
applyDiff: false, // Granular disabled
multiApplyDiff: false,
writeToFile: false,
insertContent: false,
searchAndReplace: false,
},
}
// Legacy setting should override granular settings
expect(experiments.isReReadAfterEditEnabled(config, "applyDiff")).toBe(true)
expect(experiments.isReReadAfterEditEnabled(config, "multiApplyDiff")).toBe(true)
expect(experiments.isReReadAfterEditEnabled(config, "writeToFile")).toBe(true)
expect(experiments.isReReadAfterEditEnabled(config, "insertContent")).toBe(true)
expect(experiments.isReReadAfterEditEnabled(config, "searchAndReplace")).toBe(true)
})
it("should handle partial granular settings", () => {
const config: Experiments = {
reReadAfterEdit: false,
reReadAfterEditGranular: {
applyDiff: true,
// Other fields undefined
} as ReReadAfterEditGranular,
}
expect(experiments.isReReadAfterEditEnabled(config, "applyDiff")).toBe(true)
expect(experiments.isReReadAfterEditEnabled(config, "multiApplyDiff")).toBe(false)
expect(experiments.isReReadAfterEditEnabled(config, "writeToFile")).toBe(false)
expect(experiments.isReReadAfterEditEnabled(config, "insertContent")).toBe(false)
expect(experiments.isReReadAfterEditEnabled(config, "searchAndReplace")).toBe(false)
})
it("should return false when no experiments config is provided", () => {
const config: Experiments = {}
expect(experiments.isReReadAfterEditEnabled(config, "applyDiff")).toBe(false)
expect(experiments.isReReadAfterEditEnabled(config, "multiApplyDiff")).toBe(false)
expect(experiments.isReReadAfterEditEnabled(config, "writeToFile")).toBe(false)
expect(experiments.isReReadAfterEditEnabled(config, "insertContent")).toBe(false)
expect(experiments.isReReadAfterEditEnabled(config, "searchAndReplace")).toBe(false)
})
})
})

View file

@ -1,6 +1,6 @@
// npx vitest run src/shared/__tests__/experiments.spec.ts
import type { ExperimentId } from "@roo-code/types"
import type { ExperimentId, Experiments as ExperimentsType } from "@roo-code/types"
import { EXPERIMENT_IDS, experimentConfigsMap, experiments as Experiments } from "../experiments"
@ -25,39 +25,51 @@ describe("experiments", () => {
describe("isEnabled", () => {
it("returns false when POWER_STEERING experiment is not enabled", () => {
const experiments: Record<ExperimentId, boolean> = {
const experiments: ExperimentsType = {
powerSteering: false,
multiFileApplyDiff: false,
preventFocusDisruption: false,
imageGeneration: false,
runSlashCommand: false,
reReadAfterEdit: false,
reReadAfterEditGranular: undefined,
}
expect(Experiments.isEnabled(experiments, EXPERIMENT_IDS.POWER_STEERING)).toBe(false)
})
it("returns true when experiment POWER_STEERING is enabled", () => {
const experiments: Record<ExperimentId, boolean> = {
const experiments: ExperimentsType = {
powerSteering: true,
multiFileApplyDiff: false,
preventFocusDisruption: false,
imageGeneration: false,
runSlashCommand: false,
reReadAfterEdit: false,
reReadAfterEditGranular: undefined,
}
expect(Experiments.isEnabled(experiments, EXPERIMENT_IDS.POWER_STEERING)).toBe(true)
})
it("returns false when experiment is not present", () => {
const experiments: Record<ExperimentId, boolean> = {
const experiments: ExperimentsType = {
powerSteering: false,
multiFileApplyDiff: false,
preventFocusDisruption: false,
imageGeneration: false,
runSlashCommand: false,
reReadAfterEdit: false,
reReadAfterEditGranular: undefined,
}
expect(Experiments.isEnabled(experiments, EXPERIMENT_IDS.POWER_STEERING)).toBe(false)
})
})
describe("RE_READ_AFTER_EDIT_GRANULAR", () => {
it("is configured correctly", () => {
expect(EXPERIMENT_IDS.RE_READ_AFTER_EDIT_GRANULAR).toBe("reReadAfterEditGranular")
expect(experimentConfigsMap.RE_READ_AFTER_EDIT_GRANULAR).toMatchObject({
enabled: false,
})
})
})
})

View file

@ -1,4 +1,12 @@
import type { AssertEqual, Equals, Keys, Values, ExperimentId, Experiments } from "@roo-code/types"
import type {
AssertEqual,
Equals,
Keys,
Values,
ExperimentId,
Experiments,
ReReadAfterEditGranular,
} from "@roo-code/types"
export const EXPERIMENT_IDS = {
MULTI_FILE_APPLY_DIFF: "multiFileApplyDiff",
@ -7,6 +15,7 @@ export const EXPERIMENT_IDS = {
IMAGE_GENERATION: "imageGeneration",
RUN_SLASH_COMMAND: "runSlashCommand",
RE_READ_AFTER_EDIT: "reReadAfterEdit",
RE_READ_AFTER_EDIT_GRANULAR: "reReadAfterEditGranular",
} as const satisfies Record<string, ExperimentId>
type _AssertExperimentIds = AssertEqual<Equals<ExperimentId, Values<typeof EXPERIMENT_IDS>>>
@ -14,7 +23,7 @@ type _AssertExperimentIds = AssertEqual<Equals<ExperimentId, Values<typeof EXPER
type ExperimentKey = Keys<typeof EXPERIMENT_IDS>
interface ExperimentConfig {
enabled: boolean
enabled: boolean | ReReadAfterEditGranular
}
export const experimentConfigsMap: Record<ExperimentKey, ExperimentConfig> = {
@ -24,6 +33,15 @@ export const experimentConfigsMap: Record<ExperimentKey, ExperimentConfig> = {
IMAGE_GENERATION: { enabled: false },
RUN_SLASH_COMMAND: { enabled: false },
RE_READ_AFTER_EDIT: { enabled: false },
RE_READ_AFTER_EDIT_GRANULAR: {
enabled: {
applyDiff: false,
multiApplyDiff: false,
writeToFile: false,
insertContent: false,
searchAndReplace: false,
},
},
}
export const experimentDefault = Object.fromEntries(
@ -31,9 +49,23 @@ export const experimentDefault = Object.fromEntries(
EXPERIMENT_IDS[_ as keyof typeof EXPERIMENT_IDS] as ExperimentId,
config.enabled,
]),
) as Record<ExperimentId, boolean>
) as Experiments
export const experiments = {
get: (id: ExperimentKey): ExperimentConfig | undefined => experimentConfigsMap[id],
isEnabled: (experimentsConfig: Experiments, id: ExperimentId) => experimentsConfig[id] ?? experimentDefault[id],
isReReadAfterEditEnabled: (experimentsConfig: Experiments, editType: keyof ReReadAfterEditGranular): boolean => {
// If the legacy RE_READ_AFTER_EDIT is enabled, it applies to all edit types
if (experimentsConfig.reReadAfterEdit) {
return true
}
// Check if granular settings are enabled and if the specific edit type is enabled
const granularSettings = experimentsConfig.reReadAfterEditGranular
if (granularSettings && granularSettings[editType]) {
return true
}
return false
},
} as const

View file

@ -1,7 +1,7 @@
import { HTMLAttributes } from "react"
import { FlaskConical } from "lucide-react"
import type { Experiments } from "@roo-code/types"
import type { Experiments, ReReadAfterEditGranular } from "@roo-code/types"
import { EXPERIMENT_IDS, experimentConfigsMap } from "@roo/experiments"
@ -13,10 +13,12 @@ import { SectionHeader } from "./SectionHeader"
import { Section } from "./Section"
import { ExperimentalFeature } from "./ExperimentalFeature"
import { ImageGenerationSettings } from "./ImageGenerationSettings"
import { ReReadAfterEditGranularSettings } from "./ReReadAfterEditGranularSettings"
type ExperimentalSettingsProps = HTMLAttributes<HTMLDivElement> & {
experiments: Experiments
setExperimentEnabled: SetExperimentEnabled
setReReadAfterEditGranular?: (settings: ReReadAfterEditGranular) => void
apiConfiguration?: any
setApiConfigurationField?: any
openRouterImageApiKey?: string
@ -28,6 +30,7 @@ type ExperimentalSettingsProps = HTMLAttributes<HTMLDivElement> & {
export const ExperimentalSettings = ({
experiments,
setExperimentEnabled,
setReReadAfterEditGranular,
apiConfiguration,
setApiConfigurationField,
openRouterImageApiKey,
@ -83,19 +86,38 @@ export const ExperimentalSettings = ({
/>
)
}
return (
<ExperimentalFeature
key={config[0]}
experimentKey={config[0]}
enabled={experiments[EXPERIMENT_IDS[config[0] as keyof typeof EXPERIMENT_IDS]] ?? false}
onChange={(enabled) =>
setExperimentEnabled(
EXPERIMENT_IDS[config[0] as keyof typeof EXPERIMENT_IDS],
enabled,
)
}
/>
)
// Show granular settings for RE_READ_AFTER_EDIT
if (config[0] === "RE_READ_AFTER_EDIT_GRANULAR" && setReReadAfterEditGranular) {
return (
<ReReadAfterEditGranularSettings
key={config[0]}
enabled={experiments[EXPERIMENT_IDS.RE_READ_AFTER_EDIT] ?? false}
granularSettings={experiments[EXPERIMENT_IDS.RE_READ_AFTER_EDIT_GRANULAR]}
onChange={(enabled) =>
setExperimentEnabled(EXPERIMENT_IDS.RE_READ_AFTER_EDIT, enabled)
}
onGranularChange={setReReadAfterEditGranular}
/>
)
}
// Skip the legacy RE_READ_AFTER_EDIT if granular is available
if (config[0] === "RE_READ_AFTER_EDIT" && setReReadAfterEditGranular) {
return null
}
const experimentId = EXPERIMENT_IDS[config[0] as keyof typeof EXPERIMENT_IDS]
const value = experiments[experimentId]
// Only render if it's a boolean (not granular settings object)
if (typeof value === "boolean" || value === undefined) {
return (
<ExperimentalFeature
key={config[0]}
experimentKey={config[0]}
enabled={value ?? false}
onChange={(enabled) => setExperimentEnabled(experimentId, enabled)}
/>
)
}
return null
})}
</Section>
</div>

View file

@ -0,0 +1,88 @@
import { VSCodeCheckbox } from "@vscode/webview-ui-toolkit/react"
import { useAppTranslation } from "@/i18n/TranslationContext"
import type { ReReadAfterEditGranular } from "@roo-code/types"
interface ReReadAfterEditGranularSettingsProps {
enabled: boolean
granularSettings?: ReReadAfterEditGranular
onChange: (enabled: boolean) => void
onGranularChange: (settings: ReReadAfterEditGranular) => void
}
export const ReReadAfterEditGranularSettings = ({
enabled,
granularSettings,
onChange,
onGranularChange,
}: ReReadAfterEditGranularSettingsProps) => {
const { t } = useAppTranslation()
const editTypes = [
{ key: "applyDiff", label: "Apply Diff" },
{ key: "multiApplyDiff", label: "Multi-file Apply Diff" },
{ key: "writeToFile", label: "Write to File" },
{ key: "insertContent", label: "Insert Content" },
{ key: "searchAndReplace", label: "Search and Replace" },
] as const
const handleGranularToggle = (editType: keyof ReReadAfterEditGranular, value: boolean) => {
const newSettings: ReReadAfterEditGranular = {
...granularSettings,
[editType]: value,
}
onGranularChange(newSettings)
}
const allChecked = editTypes.every((type) => granularSettings?.[type.key] === true)
const someChecked = editTypes.some((type) => granularSettings?.[type.key] === true)
const isIndeterminate = someChecked && !allChecked
const handleMasterToggle = (checked: boolean) => {
const newSettings: ReReadAfterEditGranular = {}
editTypes.forEach((type) => {
newSettings[type.key] = checked
})
onGranularChange(newSettings)
onChange(checked)
}
return (
<div className="space-y-3">
<div>
<div className="flex items-center gap-2">
<VSCodeCheckbox
checked={enabled || allChecked}
indeterminate={isIndeterminate}
onChange={(e: any) => handleMasterToggle(e.target.checked)}>
<span className="font-medium">
{t("settings:experimental.RE_READ_AFTER_EDIT_GRANULAR.name")}
</span>
</VSCodeCheckbox>
</div>
<p className="text-vscode-descriptionForeground text-sm mt-0">
{t("settings:experimental.RE_READ_AFTER_EDIT_GRANULAR.description")}
</p>
</div>
{/* Show granular options when enabled */}
{(enabled || someChecked) && (
<div className="ml-6 space-y-2 border-l-2 border-vscode-panel-border pl-4">
<p className="text-vscode-descriptionForeground text-xs mb-2">
{t("settings:experimental.RE_READ_AFTER_EDIT_GRANULAR.selectEditTypes")}
</p>
{editTypes.map((editType) => (
<div key={editType.key} className="flex items-center gap-2">
<VSCodeCheckbox
checked={granularSettings?.[editType.key] === true}
onChange={(e: any) => handleGranularToggle(editType.key, e.target.checked)}>
<span className="text-sm">
{t(`settings:experimental.RE_READ_AFTER_EDIT_GRANULAR.${editType.key}`)}
</span>
</VSCodeCheckbox>
</div>
))}
</div>
)}
</div>
)
}

View file

@ -27,7 +27,7 @@ import {
Glasses,
} from "lucide-react"
import type { ProviderSettings, ExperimentId, TelemetrySetting } from "@roo-code/types"
import type { ProviderSettings, ExperimentId, TelemetrySetting, ReReadAfterEditGranular } from "@roo-code/types"
import { vscode } from "@src/utils/vscode"
import { cn } from "@src/lib/utils"
@ -272,6 +272,26 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
})
}, [])
const setReReadAfterEditGranular = useCallback((settings: ReReadAfterEditGranular) => {
setCachedState((prevState) => {
// Only set change detected if value actually changed
const previousStr = JSON.stringify(prevState.experiments?.reReadAfterEditGranular)
const newStr = JSON.stringify(settings)
if (previousStr !== newStr) {
setChangeDetected(true)
}
return {
...prevState,
experiments: {
...prevState.experiments,
reReadAfterEditGranular: settings,
},
}
})
}, [])
const setTelemetrySetting = useCallback((setting: TelemetrySetting) => {
setCachedState((prevState) => {
if (prevState.telemetrySetting === setting) {
@ -799,6 +819,7 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
{activeTab === "experimental" && (
<ExperimentalSettings
setExperimentEnabled={setExperimentEnabled}
setReReadAfterEditGranular={setReReadAfterEditGranular}
experiments={experiments}
apiConfiguration={apiConfiguration}
setApiConfigurationField={setApiConfigurationField}

View file

@ -1,6 +1,6 @@
import { render, screen, act } from "@/utils/test-utils"
import { ProviderSettings, ExperimentId, DEFAULT_CHECKPOINT_TIMEOUT_SECONDS } from "@roo-code/types"
import { ProviderSettings, DEFAULT_CHECKPOINT_TIMEOUT_SECONDS } from "@roo-code/types"
import { ExtensionState } from "@roo/ExtensionMessage"
@ -192,7 +192,7 @@ describe("mergeExtensionState", () => {
writeDelayMs: 1000,
requestDelaySeconds: 5,
mode: "default",
experiments: {} as Record<ExperimentId, boolean>,
experiments: {} as any,
customModes: [],
maxOpenTabsContext: 20,
maxWorkspaceFiles: 100,
@ -220,7 +220,7 @@ describe("mergeExtensionState", () => {
const prevState: ExtensionState = {
...baseState,
apiConfiguration: { modelMaxTokens: 1234, modelMaxThinkingTokens: 123 },
experiments: {} as Record<ExperimentId, boolean>,
experiments: {} as any,
checkpointTimeout: DEFAULT_CHECKPOINT_TIMEOUT_SECONDS - 5,
}
@ -238,7 +238,14 @@ describe("mergeExtensionState", () => {
imageGeneration: false,
runSlashCommand: false,
reReadAfterEdit: false,
} as Record<ExperimentId, boolean>,
reReadAfterEditGranular: {
applyDiff: false,
multiApplyDiff: false,
writeToFile: false,
insertContent: false,
searchAndReplace: false,
},
} as any,
checkpointTimeout: DEFAULT_CHECKPOINT_TIMEOUT_SECONDS + 5,
}
@ -260,6 +267,13 @@ describe("mergeExtensionState", () => {
imageGeneration: false,
runSlashCommand: false,
reReadAfterEdit: false,
reReadAfterEditGranular: {
applyDiff: false,
multiApplyDiff: false,
writeToFile: false,
insertContent: false,
searchAndReplace: false,
},
})
})
})

View file

@ -779,6 +779,16 @@
"RE_READ_AFTER_EDIT": {
"name": "Suggest review after file edits",
"description": "When enabled, Roo will suggest reviewing changes after editing files. This helps catch errors that might be introduced during editing by prompting to review the changes."
},
"RE_READ_AFTER_EDIT_GRANULAR": {
"name": "Suggest review after file edits",
"description": "Select which edit operations should prompt Roo to review changes. This helps catch errors introduced during editing.",
"selectEditTypes": "Choose edit types to review:",
"applyDiff": "Apply Diff (search and replace blocks)",
"multiApplyDiff": "Multi-file Apply Diff",
"writeToFile": "Write to File (complete file rewrites)",
"insertContent": "Insert Content (add lines to files)",
"searchAndReplace": "Search and Replace (find and replace text)"
}
},
"promptCaching": {