Add the setting to schema definitions:
globalSettingsSchema in src/schemas/index.tsglobalSettingsRecord in src/schemas/index.tsterminalCommandDelay: z.number().optional(),Add the setting to type definitions:
src/exports/types.tssrc/exports/roo-code.d.tssrc/shared/ExtensionMessage.tssrc/shared/WebviewMessage.tsterminalCommandDelay?: number | undefinedAdd test coverage:
Add the message type to src/shared/WebviewMessage.ts:
| "multisearchDiffEnabled"Add the setting to webview-ui/src/context/ExtensionStateContext.tsx:
Example:
interface ExtensionStateContextType {
multisearchDiffEnabled: boolean
setMultisearchDiffEnabled: (value: boolean) => void
}
Add the setting to src/core/webview/ClineProvider.ts:
Example:
case "multisearchDiffEnabled":
await this.updateGlobalState("multisearchDiffEnabled", message.bool)
await this.postStateToWebview()
break
Add the checkbox UI to webview-ui/src/components/settings/SettingsView.tsx:
Example:
<VSCodeCheckbox
checked={multisearchDiffEnabled}
onChange={(e: any) => setMultisearchDiffEnabled(e.target.checked)}
>
<span style={{ fontWeight: "500" }}>Enable multi-search diff matching</span>
</VSCodeCheckbox>
Add the setting to handleSubmit in webview-ui/src/components/settings/SettingsView.tsx:
Example:
vscode.postMessage({ type: "multisearchDiffEnabled", bool: multisearchDiffEnabled })
Style Considerations:
Example:
<div>
<VSCodeCheckbox
checked={terminalPowershellCounter ?? true}
onChange={(e: any) => setCachedStateField("terminalPowershellCounter", e.target.checked)}
data-testid="terminal-powershell-counter-checkbox">
<span className="font-medium">{t("settings:terminal.powershellCounter.label")}</span>
</VSCodeCheckbox>
<div className="text-vscode-descriptionForeground text-sm mt-1">
{t("settings:terminal.powershellCounter.description")}
</div>
</div>
Add the message type to src/shared/WebviewMessage.ts:
| "preferredLanguage"Add the setting to webview-ui/src/context/ExtensionStateContext.tsx:
Example:
interface ExtensionStateContextType {
preferredLanguage: string
setPreferredLanguage: (value: string) => void
}
Add the setting to src/core/webview/ClineProvider.ts:
Example:
case "preferredLanguage":
await this.updateGlobalState("preferredLanguage", message.text)
await this.postStateToWebview()
break
Add the select UI to webview-ui/src/components/settings/SettingsView.tsx:
Example:
<select
value={preferredLanguage}
onChange={(e) => setPreferredLanguage(e.target.value)}
style={{
width: "100%",
padding: "4px 8px",
backgroundColor: "var(--vscode-input-background)",
color: "var(--vscode-input-foreground)",
border: "1px solid var(--vscode-input-border)",
borderRadius: "2px"
}}>
<option value="English">English</option>
<option value="Spanish">Spanish</option>
...
</select>
Add the setting to handleSubmit in webview-ui/src/components/settings/SettingsView.tsx:
Example:
vscode.postMessage({ type: "preferredLanguage", text: preferredLanguage })
These steps ensure that:
To add a new configuration item to the system, the following changes are necessary:
Feature-Specific Class (if applicable)
Schema Definition
Type Definitions
UI Component
Translations
State Management
Message Handling
Implementation-Specific Logic
Testing
Ensuring Settings Persistence Across Reload
To ensure settings persist across application reload, several key components must be properly configured:
Initial State in ExtensionStateContextProvider:
Example:
const [state, setState] = useState<ExtensionState>({
// existing settings...
newSetting: false, // Default value for the new setting
})
State Loading in ClineProvider:
Example:
return {
// existing settings...
newSetting: stateValues.newSetting ?? false,
}
State Initialization in resolveWebviewView:
Example:
this.getState().then(
({
// existing settings...
newSetting,
}) => {
// Initialize the setting with its stored value or default
FeatureClass.setNewSetting(newSetting ?? false)
},
)
State Transmission to Webview:
Example:
return {
// existing settings...
newSetting: newSetting ?? false,
}
Setter Method in ExtensionStateContext:
Example:
const contextValue: ExtensionStateContextType = {
// existing properties and methods...
setNewSetting: (value) => setState((prevState) => ({ ...prevState, newSetting: value })),
}
Debugging Settings Persistence Issues
If a setting is not persisting across reload, check the following:
1. **Complete Chain of Persistence**:
- Verify that the setting is added to all required locations:
- globalSettingsSchema and globalSettingsRecord in src/schemas/index.ts
- Initial state in ExtensionStateContextProvider
- getState method in src/core/webview/ClineProvider.ts
- getStateToPostToWebview method in src/core/webview/ClineProvider.ts
- resolveWebviewView method in src/core/webview/ClineProvider.ts (if feature-specific)
- A break in any part of this chain can prevent persistence
2. **Default Values Consistency**:
- Ensure default values are consistent across all locations
- Inconsistent defaults can cause unexpected behavior
3. **Message Handling**:
- Confirm the src/core/webview/webviewMessageHandler.ts has a case for the setting
- Verify the message type matches what's sent from the UI
4. **UI Integration**:
- Check that the setting is included in the handleSubmit function in webview-ui/src/components/settings/SettingsView.tsx
- Ensure the UI component correctly updates the state
5. **Type Definitions**:
- Verify the setting is properly typed in all relevant interfaces
- Check for typos in property names across different files
6. **Storage Mechanism**:
- For complex settings, ensure proper serialization/deserialization
- Check that the setting is being correctly stored in VSCode's globalState
These checks help identify and resolve common issues with settings persistence.
Advanced Troubleshooting: The Complete Settings Persistence Chain
Settings persistence requires a complete chain of state management across multiple components. Understanding this chain is critical for both humans and AI to effectively troubleshoot persistence issues:
Schema Definition (Entry Point):
globalSettingsSchema and globalSettingsRecordz.enum(["value1", "value2"])Example:
// In src/schemas/index.ts
export const globalSettingsSchema = z.object({
// Existing settings...
commandRiskLevel: z.enum(["readOnly", "reversibleChanges", "complexChanges"]).optional(),
})
const globalSettingsRecord: GlobalSettingsRecord = {
// Existing settings...
commandRiskLevel: undefined,
}
UI Component (User Interaction):
setCachedStateField for state updates, not direct state settingvscode.postMessageExample:
// In a settings component
<Select value={commandRiskLevel} onValueChange={(value) => setCachedStateField("commandRiskLevel", value)}>
<SelectTrigger className="w-full">
<SelectValue placeholder={t("settings:common.select")} />
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectItem value="readOnly">{t("label.readOnly")}</SelectItem>
{/* Other options... */}
</SelectGroup>
</SelectContent>
</Select>
Message Handler (State Saving):
src/core/webview/webviewMessageHandler.tsupdateGlobalState with properly typed valuespostStateToWebview after updatesExample:
// In src/core/webview/webviewMessageHandler.ts
case "commandRiskLevel":
await updateGlobalState(
"commandRiskLevel",
(message.text ?? "readOnly") as "readOnly" | "reversibleChanges" | "complexChanges"
)
await provider.postStateToWebview()
break
State Retrieval (Reading State):
getState, state must be properly retrieved from stateValuesgetStateToPostToWebview, the setting must be in the destructured parameterscontextProxy.getGlobalState for direct access when neededExample:
// In src/core/webview/ClineProvider.ts getStateToPostToWebview
const {
// Other state properties...
commandRiskLevel,
} = await this.getState()
return {
// Other state properties...
commandRiskLevel: commandRiskLevel ?? "readOnly",
}
Debugging Strategies:
Common Pitfalls:
Remember: A break at ANY point in this chain can cause persistence failures. When troubleshooting, systematically check each link in the chain to identify where the issue occurs.