Skip to content
Closed
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
2 changes: 2 additions & 0 deletions packages/types/src/global-settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ export const globalSettingsSchema = z.object({
maxWorkspaceFiles: z.number().optional(),
showRooIgnoredFiles: z.boolean().optional(),
maxReadFileLine: z.number().optional(),
includeVSCodeFileContext: z.boolean().optional(),

terminalOutputLineLimit: z.number().optional(),
terminalOutputCharacterLimit: z.number().optional(),
Expand Down Expand Up @@ -272,6 +273,7 @@ export const EVALS_SETTINGS: RooCodeSettings = {
maxWorkspaceFiles: 200,
showRooIgnoredFiles: true,
maxReadFileLine: -1, // -1 to enable full file reading.
includeVSCodeFileContext: true, // Include VSCode file context in all API calls by default

includeDiagnosticMessages: true,
maxDiagnosticMessages: 50,
Expand Down
51 changes: 51 additions & 0 deletions src/core/environment/__tests__/getEnvironmentDetails.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ describe("getEnvironmentDetails", () => {
terminalOutputLineLimit: 100,
maxWorkspaceFiles: 50,
maxOpenTabsContext: 10,
includeVSCodeFileContext: true,
mode: "code",
customModes: [],
experiments: {},
Expand Down Expand Up @@ -361,4 +362,54 @@ describe("getEnvironmentDetails", () => {

await expect(getEnvironmentDetails(mockCline as Task)).resolves.not.toThrow()
})

describe("VSCode file context inclusion", () => {
it("should include VSCode file context when includeVSCodeFileContext is true", async () => {
mockProvider.getState.mockResolvedValue({
...mockState,
includeVSCodeFileContext: true,
})

const result = await getEnvironmentDetails(mockCline as Task, false)

expect(result).toContain("# VSCode Visible Files")
expect(result).toContain("# VSCode Open Tabs")
})

it("should exclude VSCode file context when includeVSCodeFileContext is false and includeFileDetails is false", async () => {
mockProvider.getState.mockResolvedValue({
...mockState,
includeVSCodeFileContext: false,
})

const result = await getEnvironmentDetails(mockCline as Task, false)

expect(result).not.toContain("# VSCode Visible Files")
expect(result).not.toContain("# VSCode Open Tabs")
})

it("should always include VSCode file context when includeFileDetails is true regardless of includeVSCodeFileContext", async () => {
mockProvider.getState.mockResolvedValue({
...mockState,
includeVSCodeFileContext: false,
})

const result = await getEnvironmentDetails(mockCline as Task, true)

expect(result).toContain("# VSCode Visible Files")
expect(result).toContain("# VSCode Open Tabs")
})

it("should default to including VSCode file context when includeVSCodeFileContext is undefined", async () => {
mockProvider.getState.mockResolvedValue({
...mockState,
includeVSCodeFileContext: undefined,
})

const result = await getEnvironmentDetails(mockCline as Task, false)

expect(result).toContain("# VSCode Visible Files")
expect(result).toContain("# VSCode Open Tabs")
})
})
})
84 changes: 45 additions & 39 deletions src/core/environment/getEnvironmentDetails.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,48 +30,54 @@ export async function getEnvironmentDetails(cline: Task, includeFileDetails: boo
terminalOutputLineLimit = 500,
terminalOutputCharacterLimit = DEFAULT_TERMINAL_OUTPUT_CHARACTER_LIMIT,
maxWorkspaceFiles = 200,
includeVSCodeFileContext = true,
} = state ?? {}

// 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"

const visibleFilePaths = vscode.window.visibleTextEditors
?.map((editor) => editor.document?.uri?.fsPath)
.filter(Boolean)
.map((absolutePath) => path.relative(cline.cwd, absolutePath))
.slice(0, maxWorkspaceFiles)

// Filter paths through rooIgnoreController
const allowedVisibleFiles = cline.rooIgnoreController
? cline.rooIgnoreController.filterPaths(visibleFilePaths)
: visibleFilePaths.map((p) => p.toPosix()).join("\n")

if (allowedVisibleFiles) {
details += `\n${allowedVisibleFiles}`
} else {
details += "\n(No visible files)"
}
// Only include VSCode file context if enabled in settings or if this is the first request
const shouldIncludeVSCodeContext = includeFileDetails || includeVSCodeFileContext

if (shouldIncludeVSCodeContext) {
// 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"

const visibleFilePaths = vscode.window.visibleTextEditors
?.map((editor) => editor.document?.uri?.fsPath)
.filter(Boolean)
.map((absolutePath) => path.relative(cline.cwd, absolutePath))
.slice(0, maxWorkspaceFiles)

details += "\n\n# VSCode Open Tabs"
const { maxOpenTabsContext } = state ?? {}
const maxTabs = maxOpenTabsContext ?? 20
const openTabPaths = vscode.window.tabGroups.all
.flatMap((group) => group.tabs)
.map((tab) => (tab.input as vscode.TabInputText)?.uri?.fsPath)
.filter(Boolean)
.map((absolutePath) => path.relative(cline.cwd, absolutePath).toPosix())
.slice(0, maxTabs)

// Filter paths through rooIgnoreController
const allowedOpenTabs = cline.rooIgnoreController
? cline.rooIgnoreController.filterPaths(openTabPaths)
: openTabPaths.map((p) => p.toPosix()).join("\n")

if (allowedOpenTabs) {
details += `\n${allowedOpenTabs}`
} else {
details += "\n(No open tabs)"
// Filter paths through rooIgnoreController
const allowedVisibleFiles = cline.rooIgnoreController
? cline.rooIgnoreController.filterPaths(visibleFilePaths)
: visibleFilePaths.map((p) => p.toPosix()).join("\n")

if (allowedVisibleFiles) {
details += `\n${allowedVisibleFiles}`
} else {
details += "\n(No visible files)"
}

details += "\n\n# VSCode Open Tabs"
const { maxOpenTabsContext } = state ?? {}
const maxTabs = maxOpenTabsContext ?? 20
const openTabPaths = vscode.window.tabGroups.all
.flatMap((group) => group.tabs)
.map((tab) => (tab.input as vscode.TabInputText)?.uri?.fsPath)
.filter(Boolean)
.map((absolutePath) => path.relative(cline.cwd, absolutePath).toPosix())
.slice(0, maxTabs)

// Filter paths through rooIgnoreController
const allowedOpenTabs = cline.rooIgnoreController
? cline.rooIgnoreController.filterPaths(openTabPaths)
: openTabPaths.map((p) => p.toPosix()).join("\n")

if (allowedOpenTabs) {
details += `\n${allowedOpenTabs}`
} else {
details += "\n(No open tabs)"
}
}

// Get task-specific and background terminals.
Expand Down
3 changes: 3 additions & 0 deletions src/core/webview/ClineProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1420,6 +1420,7 @@ export class ClineProvider
experiments,
maxOpenTabsContext,
maxWorkspaceFiles,
includeVSCodeFileContext,
browserToolEnabled,
telemetrySetting,
showRooIgnoredFiles,
Expand Down Expand Up @@ -1523,6 +1524,7 @@ export class ClineProvider
mcpServers: this.mcpHub?.getAllServers() ?? [],
maxOpenTabsContext: maxOpenTabsContext ?? 20,
maxWorkspaceFiles: maxWorkspaceFiles ?? 200,
includeVSCodeFileContext: includeVSCodeFileContext ?? true,
cwd,
browserToolEnabled: browserToolEnabled ?? true,
telemetrySetting,
Expand Down Expand Up @@ -1697,6 +1699,7 @@ export class ClineProvider
customModes,
maxOpenTabsContext: stateValues.maxOpenTabsContext ?? 20,
maxWorkspaceFiles: stateValues.maxWorkspaceFiles ?? 200,
includeVSCodeFileContext: stateValues.includeVSCodeFileContext ?? true,
openRouterUseMiddleOutTransform: stateValues.openRouterUseMiddleOutTransform ?? true,
browserToolEnabled: stateValues.browserToolEnabled ?? true,
telemetrySetting: stateValues.telemetrySetting || "unset",
Expand Down
20 changes: 20 additions & 0 deletions src/core/webview/__tests__/ClineProvider.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -528,6 +528,7 @@ describe("ClineProvider", () => {
experiments: experimentDefault,
maxOpenTabsContext: 20,
maxWorkspaceFiles: 200,
includeVSCodeFileContext: true,
browserToolEnabled: true,
telemetrySetting: "unset",
showRooIgnoredFiles: true,
Expand Down Expand Up @@ -1079,6 +1080,25 @@ describe("ClineProvider", () => {
expect(mockPostMessage).toHaveBeenCalled()
})

test("handles includeVSCodeFileContext message", async () => {
await provider.resolveWebviewView(mockWebviewView)
const messageHandler = (mockWebviewView.webview.onDidReceiveMessage as any).mock.calls[0][0]

// Test setting to false
await messageHandler({ type: "includeVSCodeFileContext", bool: false })

expect(updateGlobalStateSpy).toHaveBeenCalledWith("includeVSCodeFileContext", false)
expect(mockContext.globalState.update).toHaveBeenCalledWith("includeVSCodeFileContext", false)
expect(mockPostMessage).toHaveBeenCalled()

// Test setting to true
await messageHandler({ type: "includeVSCodeFileContext", bool: true })

expect(updateGlobalStateSpy).toHaveBeenCalledWith("includeVSCodeFileContext", true)
expect(mockContext.globalState.update).toHaveBeenCalledWith("includeVSCodeFileContext", true)
expect(mockPostMessage).toHaveBeenCalled()
})

test("handles mode-specific custom instructions updates", async () => {
await provider.resolveWebviewView(mockWebviewView)
const messageHandler = (mockWebviewView.webview.onDidReceiveMessage as any).mock.calls[0][0]
Expand Down
4 changes: 4 additions & 0 deletions src/core/webview/webviewMessageHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1236,6 +1236,10 @@ export const webviewMessageHandler = async (
await updateGlobalState("maxWorkspaceFiles", fileCount)
await provider.postStateToWebview()
break
case "includeVSCodeFileContext":
await updateGlobalState("includeVSCodeFileContext", message.bool ?? true)
await provider.postStateToWebview()
break
case "alwaysAllowFollowupQuestions":
await updateGlobalState("alwaysAllowFollowupQuestions", message.bool ?? false)
await provider.postStateToWebview()
Expand Down
2 changes: 2 additions & 0 deletions src/shared/ExtensionMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,7 @@ export type ExtensionState = Pick<
// | "maxOpenTabsContext" // Optional in GlobalSettings, required here.
// | "maxWorkspaceFiles" // Optional in GlobalSettings, required here.
// | "showRooIgnoredFiles" // Optional in GlobalSettings, required here.
// | "includeVSCodeFileContext" // Optional in GlobalSettings, required here.
// | "maxReadFileLine" // Optional in GlobalSettings, required here.
| "maxConcurrentFileReads" // Optional in GlobalSettings, required here.
| "terminalOutputLineLimit"
Expand Down Expand Up @@ -277,6 +278,7 @@ export type ExtensionState = Pick<
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)
showRooIgnoredFiles: boolean // Whether to show .rooignore'd files in listings
includeVSCodeFileContext: boolean // Whether to include VSCode file context (visible files and open tabs) in API calls after the first one
maxReadFileLine: number // Maximum number of lines to read from a file before truncating

experiments: Experiments // Map of experiment IDs to their enabled state
Expand Down
1 change: 1 addition & 0 deletions src/shared/WebviewMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ export interface WebviewMessage {
| "deleteMcpServer"
| "maxOpenTabsContext"
| "maxWorkspaceFiles"
| "includeVSCodeFileContext"
| "humanRelayResponse"
| "humanRelayCancel"
| "browserToolEnabled"
Expand Down
1 change: 1 addition & 0 deletions webview-ui/src/context/ExtensionStateContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
customModes: [],
maxOpenTabsContext: 20,
maxWorkspaceFiles: 200,
includeVSCodeFileContext: true,
cwd: "",
browserToolEnabled: true,
telemetrySetting: "unset",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ describe("mergeExtensionState", () => {
sharingEnabled: false,
profileThresholds: {},
hasOpenedModeSelector: false, // Add the new required property
includeVSCodeFileContext: true,
}

const prevState: ExtensionState = {
Expand Down