diff --git a/src/core/Cline.ts b/src/core/Cline.ts
index c50d48b6cc..8b5afe9806 100644
--- a/src/core/Cline.ts
+++ b/src/core/Cline.ts
@@ -3501,7 +3501,7 @@ export class Cline extends EventEmitter {
async getEnvironmentDetails(includeFileDetails: boolean = false) {
let details = ""
- const { terminalOutputLineLimit } = (await this.providerRef.deref()?.getState()) ?? {}
+ const { terminalOutputLineLimit, maxWorkspaceFiles } = (await this.providerRef.deref()?.getState()) ?? {}
// It could be useful for cline to know if the user went from one or no file to another between messages, so we always include this context
details += "\n\n# VSCode Visible Files"
@@ -3509,6 +3509,7 @@ export class Cline extends EventEmitter {
?.map((editor) => editor.document?.uri?.fsPath)
.filter(Boolean)
.map((absolutePath) => path.relative(cwd, absolutePath))
+ .slice(0, maxWorkspaceFiles ?? 200)
// Filter paths through rooIgnoreController
const allowedVisibleFiles = this.rooIgnoreController
@@ -3715,7 +3716,8 @@ export class Cline extends EventEmitter {
// don't want to immediately access desktop since it would show permission popup
details += "(Desktop files not shown automatically. Use list_files to explore if needed.)"
} else {
- const [files, didHitLimit] = await listFiles(cwd, true, 200)
+ const maxFiles = maxWorkspaceFiles ?? 200
+ const [files, didHitLimit] = await listFiles(cwd, true, maxFiles)
const { showRooIgnoredFiles } = (await this.providerRef.deref()?.getState()) ?? {}
const result = formatResponse.formatFilesList(
cwd,
diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts
index b98934400d..87f33dc418 100644
--- a/src/core/webview/ClineProvider.ts
+++ b/src/core/webview/ClineProvider.ts
@@ -1518,6 +1518,11 @@ export class ClineProvider implements vscode.WebviewViewProvider {
await this.updateGlobalState("maxOpenTabsContext", tabCount)
await this.postStateToWebview()
break
+ case "maxWorkspaceFiles":
+ const fileCount = Math.min(Math.max(0, message.value ?? 200), 500)
+ await this.updateGlobalState("maxWorkspaceFiles", fileCount)
+ await this.postStateToWebview()
+ break
case "browserToolEnabled":
await this.updateGlobalState("browserToolEnabled", message.bool ?? true)
await this.postStateToWebview()
@@ -2297,6 +2302,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
autoApprovalEnabled,
experiments,
maxOpenTabsContext,
+ maxWorkspaceFiles,
browserToolEnabled,
telemetrySetting,
showRooIgnoredFiles,
@@ -2359,6 +2365,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
experiments: experiments ?? experimentDefault,
mcpServers: this.mcpHub?.getAllServers() ?? [],
maxOpenTabsContext: maxOpenTabsContext ?? 20,
+ maxWorkspaceFiles: maxWorkspaceFiles ?? 200,
cwd,
browserToolEnabled: browserToolEnabled ?? true,
telemetrySetting,
@@ -2516,6 +2523,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
autoApprovalEnabled: stateValues.autoApprovalEnabled ?? false,
customModes,
maxOpenTabsContext: stateValues.maxOpenTabsContext ?? 20,
+ maxWorkspaceFiles: stateValues.maxWorkspaceFiles ?? 200,
openRouterUseMiddleOutTransform: stateValues.openRouterUseMiddleOutTransform ?? true,
browserToolEnabled: stateValues.browserToolEnabled ?? true,
telemetrySetting: stateValues.telemetrySetting || "unset",
diff --git a/src/core/webview/__tests__/ClineProvider.test.ts b/src/core/webview/__tests__/ClineProvider.test.ts
index abe7a8475a..7530052b85 100644
--- a/src/core/webview/__tests__/ClineProvider.test.ts
+++ b/src/core/webview/__tests__/ClineProvider.test.ts
@@ -448,6 +448,7 @@ describe("ClineProvider", () => {
customModes: [],
experiments: experimentDefault,
maxOpenTabsContext: 20,
+ maxWorkspaceFiles: 200,
browserToolEnabled: true,
telemetrySetting: "unset",
showRooIgnoredFiles: true,
@@ -794,6 +795,17 @@ describe("ClineProvider", () => {
expect(state.customModePrompts).toEqual({})
})
+ test("handles maxWorkspaceFiles message", async () => {
+ await provider.resolveWebviewView(mockWebviewView)
+ const messageHandler = (mockWebviewView.webview.onDidReceiveMessage as jest.Mock).mock.calls[0][0]
+
+ await messageHandler({ type: "maxWorkspaceFiles", value: 300 })
+
+ expect(mockContextProxy.updateGlobalState).toHaveBeenCalledWith("maxWorkspaceFiles", 300)
+ expect(mockContext.globalState.update).toHaveBeenCalledWith("maxWorkspaceFiles", 300)
+ expect(mockPostMessage).toHaveBeenCalled()
+ })
+
test.only("uses mode-specific custom instructions in Cline initialization", async () => {
// Setup mock state
const modeCustomInstructions = "Code mode instructions"
diff --git a/src/exports/roo-code.d.ts b/src/exports/roo-code.d.ts
index e5338bd737..1c45dec84b 100644
--- a/src/exports/roo-code.d.ts
+++ b/src/exports/roo-code.d.ts
@@ -211,6 +211,7 @@ export type GlobalStateKey =
| "modelMaxTokens"
| "mistralCodestralUrl"
| "maxOpenTabsContext"
+ | "maxWorkspaceFiles"
| "browserToolEnabled"
| "lmStudioSpeculativeDecodingEnabled"
| "lmStudioDraftModelId"
diff --git a/src/shared/ExtensionMessage.ts b/src/shared/ExtensionMessage.ts
index c0bcb4ed65..5b018ec22e 100644
--- a/src/shared/ExtensionMessage.ts
+++ b/src/shared/ExtensionMessage.ts
@@ -147,6 +147,7 @@ export interface ExtensionState {
customModes: ModeConfig[]
toolRequirements?: Record // Map of tool names to their requirements (e.g. {"apply_diff": true} if diffEnabled)
maxOpenTabsContext: number // Maximum number of VSCode open tabs to include in context (0-500)
+ maxWorkspaceFiles: number // Maximum number of files to include in current working directory details (0-500)
cwd?: string // Current working directory
telemetrySetting: TelemetrySetting
telemetryKey?: string
diff --git a/src/shared/WebviewMessage.ts b/src/shared/WebviewMessage.ts
index e9a64d891b..bb77ea7203 100644
--- a/src/shared/WebviewMessage.ts
+++ b/src/shared/WebviewMessage.ts
@@ -97,6 +97,7 @@ export interface WebviewMessage {
| "checkpointRestore"
| "deleteMcpServer"
| "maxOpenTabsContext"
+ | "maxWorkspaceFiles"
| "humanRelayResponse"
| "humanRelayCancel"
| "browserToolEnabled"
diff --git a/src/shared/globalState.ts b/src/shared/globalState.ts
index 9896c37697..cec7bdf9ce 100644
--- a/src/shared/globalState.ts
+++ b/src/shared/globalState.ts
@@ -116,6 +116,7 @@ export const GLOBAL_STATE_KEYS = [
"telemetrySetting",
"showRooIgnoredFiles",
"remoteBrowserEnabled",
+ "maxWorkspaceFiles",
] as const
type CheckGlobalStateKeysExhaustiveness =
diff --git a/webview-ui/src/__mocks__/lucide-react.ts b/webview-ui/src/__mocks__/lucide-react.ts
index d85cd25d6a..64ab05eb34 100644
--- a/webview-ui/src/__mocks__/lucide-react.ts
+++ b/webview-ui/src/__mocks__/lucide-react.ts
@@ -4,3 +4,4 @@ export const Check = () => React.createElement("div")
export const ChevronsUpDown = () => React.createElement("div")
export const Loader = () => React.createElement("div")
export const X = () => React.createElement("div")
+export const Database = (props: any) => React.createElement("span", { "data-testid": "database-icon", ...props })
diff --git a/webview-ui/src/components/settings/ContextManagementSettings.tsx b/webview-ui/src/components/settings/ContextManagementSettings.tsx
index f8563071f2..d5f4c33f9a 100644
--- a/webview-ui/src/components/settings/ContextManagementSettings.tsx
+++ b/webview-ui/src/components/settings/ContextManagementSettings.tsx
@@ -12,13 +12,17 @@ import { Section } from "./Section"
type ContextManagementSettingsProps = HTMLAttributes & {
terminalOutputLineLimit?: number
maxOpenTabsContext: number
+ maxWorkspaceFiles: number
showRooIgnoredFiles?: boolean
- setCachedStateField: SetCachedStateField<"terminalOutputLineLimit" | "maxOpenTabsContext" | "showRooIgnoredFiles">
+ setCachedStateField: SetCachedStateField<
+ "terminalOutputLineLimit" | "maxOpenTabsContext" | "maxWorkspaceFiles" | "showRooIgnoredFiles"
+ >
}
export const ContextManagementSettings = ({
terminalOutputLineLimit,
maxOpenTabsContext,
+ maxWorkspaceFiles,
showRooIgnoredFiles,
setCachedStateField,
className,
@@ -26,7 +30,7 @@ export const ContextManagementSettings = ({
}: ContextManagementSettingsProps) => {
return (
-
+
Context Management
@@ -48,6 +52,7 @@ export const ContextManagementSettings = ({
setCachedStateField("terminalOutputLineLimit", parseInt(e.target.value))
}
className="h-2 focus:outline-0 w-4/5 accent-vscode-button-background"
+ data-testid="terminal-output-limit-slider"
/>
{terminalOutputLineLimit ?? 500}
@@ -70,6 +75,7 @@ export const ContextManagementSettings = ({
value={maxOpenTabsContext ?? 20}
onChange={(e) => setCachedStateField("maxOpenTabsContext", parseInt(e.target.value))}
className="h-2 focus:outline-0 w-4/5 accent-vscode-button-background"
+ data-testid="open-tabs-limit-slider"
/>
{maxOpenTabsContext ?? 20}
@@ -80,12 +86,36 @@ export const ContextManagementSettings = ({
+
+
+
Workspace files context limit
+
+ setCachedStateField("maxWorkspaceFiles", parseInt(e.target.value))}
+ className="h-2 focus:outline-0 w-4/5 accent-vscode-button-background"
+ data-testid="workspace-files-limit-slider"
+ />
+ {maxWorkspaceFiles ?? 200}
+
+
+
+ Maximum number of files to include in current working directory details. Higher values provide
+ more context but increase token usage.
+
+
+
{
setCachedStateField("showRooIgnoredFiles", e.target.checked)
- }}>
+ }}
+ data-testid="show-rooignored-files-checkbox">
Show .rooignore'd files in lists and searches
diff --git a/webview-ui/src/components/settings/SettingsView.tsx b/webview-ui/src/components/settings/SettingsView.tsx
index c267acfee1..1cdc1d00fa 100644
--- a/webview-ui/src/components/settings/SettingsView.tsx
+++ b/webview-ui/src/components/settings/SettingsView.tsx
@@ -85,6 +85,7 @@ const SettingsView = forwardRef(({ onDone },
experiments,
fuzzyMatchThreshold,
maxOpenTabsContext,
+ maxWorkspaceFiles,
mcpEnabled,
rateLimitSeconds,
requestDelaySeconds,
@@ -196,6 +197,7 @@ const SettingsView = forwardRef(({ onDone },
vscode.postMessage({ type: "requestDelaySeconds", value: requestDelaySeconds })
vscode.postMessage({ type: "rateLimitSeconds", value: rateLimitSeconds })
vscode.postMessage({ type: "maxOpenTabsContext", value: maxOpenTabsContext })
+ vscode.postMessage({ type: "maxWorkspaceFiles", value: maxWorkspaceFiles ?? 200 })
vscode.postMessage({ type: "showRooIgnoredFiles", bool: showRooIgnoredFiles })
vscode.postMessage({ type: "currentApiConfigName", text: currentApiConfigName })
vscode.postMessage({ type: "updateExperimental", values: experiments })
@@ -410,6 +412,7 @@ const SettingsView = forwardRef(({ onDone },
diff --git a/webview-ui/src/components/settings/__tests__/ContextManagementSettings.test.tsx b/webview-ui/src/components/settings/__tests__/ContextManagementSettings.test.tsx
index 98ea4f78f5..25e7fa3e50 100644
--- a/webview-ui/src/components/settings/__tests__/ContextManagementSettings.test.tsx
+++ b/webview-ui/src/components/settings/__tests__/ContextManagementSettings.test.tsx
@@ -5,6 +5,7 @@ describe("ContextManagementSettings", () => {
const defaultProps = {
terminalOutputLineLimit: 500,
maxOpenTabsContext: 20,
+ maxWorkspaceFiles: 200,
showRooIgnoredFiles: false,
setCachedStateField: jest.fn(),
}
@@ -18,21 +19,25 @@ describe("ContextManagementSettings", () => {
// Terminal output limit
expect(screen.getByText("Terminal output limit")).toBeInTheDocument()
- expect(screen.getByRole("slider", { name: /Terminal output limit/i })).toHaveValue("500")
+ expect(screen.getByTestId("terminal-output-limit-slider")).toHaveValue("500")
// Open tabs context limit
expect(screen.getByText("Open tabs context limit")).toBeInTheDocument()
- expect(screen.getByRole("slider", { name: /Open tabs context limit/i })).toHaveValue("20")
+ expect(screen.getByTestId("open-tabs-limit-slider")).toHaveValue("20")
+
+ // Workspace files limit
+ expect(screen.getByText("Workspace files context limit")).toBeInTheDocument()
+ expect(screen.getByTestId("workspace-files-limit-slider")).toHaveValue("200")
// Show .rooignore'd files
expect(screen.getByText("Show .rooignore'd files in lists and searches")).toBeInTheDocument()
- expect(screen.getByRole("checkbox")).not.toBeChecked()
+ expect(screen.getByTestId("show-rooignored-files-checkbox")).not.toBeChecked()
})
it("updates terminal output limit", () => {
render()
- const slider = screen.getByRole("slider", { name: /Terminal output limit/i })
+ const slider = screen.getByTestId("terminal-output-limit-slider")
fireEvent.change(slider, { target: { value: "1000" } })
expect(defaultProps.setCachedStateField).toHaveBeenCalledWith("terminalOutputLineLimit", 1000)
@@ -41,16 +46,25 @@ describe("ContextManagementSettings", () => {
it("updates open tabs context limit", () => {
render()
- const slider = screen.getByRole("slider", { name: /Open tabs context limit/i })
+ const slider = screen.getByTestId("open-tabs-limit-slider")
fireEvent.change(slider, { target: { value: "50" } })
expect(defaultProps.setCachedStateField).toHaveBeenCalledWith("maxOpenTabsContext", 50)
})
+ it("updates workspace files contextlimit", () => {
+ render()
+
+ const slider = screen.getByTestId("workspace-files-limit-slider")
+ fireEvent.change(slider, { target: { value: "50" } })
+
+ expect(defaultProps.setCachedStateField).toHaveBeenCalledWith("maxWorkspaceFiles", 50)
+ })
+
it("updates show rooignored files setting", () => {
render()
- const checkbox = screen.getByRole("checkbox")
+ const checkbox = screen.getByTestId("show-rooignored-files-checkbox")
fireEvent.click(checkbox)
expect(defaultProps.setCachedStateField).toHaveBeenCalledWith("showRooIgnoredFiles", true)
diff --git a/webview-ui/src/context/ExtensionStateContext.tsx b/webview-ui/src/context/ExtensionStateContext.tsx
index d79f263e83..b05a1a9f24 100644
--- a/webview-ui/src/context/ExtensionStateContext.tsx
+++ b/webview-ui/src/context/ExtensionStateContext.tsx
@@ -69,6 +69,8 @@ export interface ExtensionStateContextType extends ExtensionState {
customModes: ModeConfig[]
setCustomModes: (value: ModeConfig[]) => void
setMaxOpenTabsContext: (value: number) => void
+ maxWorkspaceFiles: number
+ setMaxWorkspaceFiles: (value: number) => void
setTelemetrySetting: (value: TelemetrySetting) => void
remoteBrowserEnabled?: boolean
setRemoteBrowserEnabled: (value: boolean) => void
@@ -137,6 +139,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
autoApprovalEnabled: false,
customModes: [],
maxOpenTabsContext: 20,
+ maxWorkspaceFiles: 200,
cwd: "",
browserToolEnabled: true,
telemetrySetting: "unset",
@@ -280,6 +283,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
setAutoApprovalEnabled: (value) => setState((prevState) => ({ ...prevState, autoApprovalEnabled: value })),
setCustomModes: (value) => setState((prevState) => ({ ...prevState, customModes: value })),
setMaxOpenTabsContext: (value) => setState((prevState) => ({ ...prevState, maxOpenTabsContext: value })),
+ setMaxWorkspaceFiles: (value) => setState((prevState) => ({ ...prevState, maxWorkspaceFiles: value })),
setBrowserToolEnabled: (value) => setState((prevState) => ({ ...prevState, browserToolEnabled: value })),
setTelemetrySetting: (value) => setState((prevState) => ({ ...prevState, telemetrySetting: value })),
setShowRooIgnoredFiles: (value) => setState((prevState) => ({ ...prevState, showRooIgnoredFiles: value })),
diff --git a/webview-ui/src/context/__tests__/ExtensionStateContext.test.tsx b/webview-ui/src/context/__tests__/ExtensionStateContext.test.tsx
index f8acf4f4d3..6b7742ba06 100644
--- a/webview-ui/src/context/__tests__/ExtensionStateContext.test.tsx
+++ b/webview-ui/src/context/__tests__/ExtensionStateContext.test.tsx
@@ -116,6 +116,7 @@ describe("mergeExtensionState", () => {
experiments: {} as Record,
customModes: [],
maxOpenTabsContext: 20,
+ maxWorkspaceFiles: 100,
apiConfiguration: { providerId: "openrouter" } as ApiConfiguration,
telemetrySetting: "unset",
showRooIgnoredFiles: true,