diff --git a/README.md b/README.md index 2a5440f4bc..1088ca2eab 100644 --- a/README.md +++ b/README.md @@ -88,7 +88,7 @@ across chat, provider settings, and rendering. - [简体中文](locales/zh-CN/README.md) - [繁體中文](locales/zh-TW/README.md) - ... - + --- diff --git a/apps/vscode-e2e/fixtures/read-file.json b/apps/vscode-e2e/fixtures/read-file.json index 9839348abd..813b18703d 100644 --- a/apps/vscode-e2e/fixtures/read-file.json +++ b/apps/vscode-e2e/fixtures/read-file.json @@ -109,6 +109,21 @@ } ] } + }, + { + "match": { + "sequenceIndex": 0, + "userMessage": "READ_FILE_MULTI_ROOT_REPRO" + }, + "response": { + "toolCalls": [ + { + "name": "read_file", + "arguments": "{\"path\":\"secondary-root-read-file.txt\",\"mode\":\"slice\",\"offset\":1,\"limit\":50,\"indentation\":{\"anchor_line\":1,\"max_levels\":0,\"include_siblings\":false,\"include_header\":true,\"max_lines\":50}}", + "id": "call_read_file_multi_root_secondary_001" + } + ] + } } ] } diff --git a/apps/vscode-e2e/src/fixtures/read-file.ts b/apps/vscode-e2e/src/fixtures/read-file.ts index dd9bb1c10b..7c55d6cbcb 100644 --- a/apps/vscode-e2e/src/fixtures/read-file.ts +++ b/apps/vscode-e2e/src/fixtures/read-file.ts @@ -71,6 +71,12 @@ export function addReadFileResultFixtures(mock: InstanceType) { result: "The file [`large-read-file.txt`](large-read-file.txt) contains 100 lines, each following the pattern: `Line N: This is a test line with some content`, where `N` is the line number (from 1 to 100). The structure is consistent throughout the file, with only the line number changing on each line.", id: "call_read_file_large_002", }, + { + toolCallId: "call_read_file_multi_root_secondary_001", + expected: ["File: secondary-root-read-file.txt", "SECONDARY_ROOT_MARKER_204"], + result: "The read_file tool successfully read `secondary-root-read-file.txt` from the secondary workspace root. Its contents include `SECONDARY_ROOT_MARKER_204`.", + id: "call_read_file_multi_root_secondary_002", + }, ] for (const fixture of fixtures) { diff --git a/apps/vscode-e2e/src/runTest.ts b/apps/vscode-e2e/src/runTest.ts index ca968af06e..f1829b322c 100644 --- a/apps/vscode-e2e/src/runTest.ts +++ b/apps/vscode-e2e/src/runTest.ts @@ -26,11 +26,25 @@ function isDeepSeekTargetedRun(testFile?: string, testGrep?: string) { return testGrep?.toLowerCase().includes("deepseek") ?? false } +function isMultiRootTargetedRun(testFile?: string, testGrep?: string) { + if (testFile?.toLowerCase().includes("multi-root-read-file-content")) { + return true + } + + if (!testFile && !testGrep) { + // The default mocked CI path runs the whole suite, which includes the multi-root repro. + return true + } + + return testGrep?.toLowerCase().includes("multi-root") ?? false +} + async function main() { const isRecord = process.env.AIMOCK_RECORD === "true" const testGrep = getCliFlagValue("--grep") || process.env.TEST_GREP const testFile = getCliFlagValue("--file") || process.env.TEST_FILE const isDeepSeekTest = isDeepSeekTargetedRun(testFile, testGrep) + const isMultiRootTest = isMultiRootTargetedRun(testFile, testGrep) const isGeminiTest = testFile?.toLowerCase().includes("gemini.test") ?? false if (isRecord && isDeepSeekTest && !process.env.DEEPSEEK_API_KEY) { @@ -63,11 +77,28 @@ async function main() { const extensionTestsPath = path.resolve(__dirname, "./suite/index") let testWorkspace: string | undefined + let secondaryWorkspace: string | undefined + let multiRootWorkspaceFile: string | undefined try { // Create a temporary workspace folder for tests before installing fixtures that // need workspace-specific paths. testWorkspace = await fs.mkdtemp(path.join(os.tmpdir(), "roo-test-workspace-")) + if (isMultiRootTest) { + secondaryWorkspace = await fs.mkdtemp(path.join(os.tmpdir(), "roo-test-secondary-workspace-")) + multiRootWorkspaceFile = path.join(os.tmpdir(), `roo-test-workspace-${Date.now()}.code-workspace`) + await fs.writeFile( + multiRootWorkspaceFile, + JSON.stringify( + { + folders: [{ path: testWorkspace }, { path: secondaryWorkspace }], + }, + null, + 2, + ), + "utf8", + ) + } if (useMock) { const fixturesDir = path.resolve(__dirname, "../fixtures") @@ -144,7 +175,7 @@ async function main() { await runTests({ extensionDevelopmentPath, extensionTestsPath, - launchArgs: [testWorkspace], + launchArgs: [multiRootWorkspaceFile ?? testWorkspace], extensionTestsEnv, version: process.env.VSCODE_VERSION || "1.101.2", }) @@ -152,6 +183,12 @@ async function main() { console.error("Failed to run tests", error) process.exitCode = 1 } finally { + if (multiRootWorkspaceFile) { + await fs.rm(multiRootWorkspaceFile, { force: true }) + } + if (secondaryWorkspace) { + await fs.rm(secondaryWorkspace, { recursive: true, force: true }) + } if (testWorkspace) { await fs.rm(testWorkspace, { recursive: true, force: true }) } diff --git a/apps/vscode-e2e/src/suite/multi-root-read-file-content.test.ts b/apps/vscode-e2e/src/suite/multi-root-read-file-content.test.ts new file mode 100644 index 0000000000..3da2752d4f --- /dev/null +++ b/apps/vscode-e2e/src/suite/multi-root-read-file-content.test.ts @@ -0,0 +1,96 @@ +import * as assert from "assert" +import * as fs from "fs/promises" +import * as path from "path" +import * as vscode from "vscode" + +import { RooCodeEventName, type ClineMessage } from "@roo-code/types" + +import { setDefaultSuiteTimeout } from "./test-utils" +import { waitFor } from "./utils" + +suite("Multi-root readFileContent repro", function () { + setDefaultSuiteTimeout(this) + + test("should read a file that exists only in the secondary workspace root", async () => { + await waitFor(() => (vscode.workspace.workspaceFolders?.length ?? 0) >= 2, { + timeout: 60_000, + interval: 250, + }) + + const primaryWorkspace = vscode.workspace.workspaceFolders?.[0] + assert.ok(primaryWorkspace, "Expected a primary workspace folder") + const secondaryWorkspace = vscode.workspace.workspaceFolders?.[1] + assert.ok(secondaryWorkspace, "Expected a secondary workspace folder") + + const primaryRoot = primaryWorkspace.uri.fsPath + const secondaryRoot = secondaryWorkspace.uri.fsPath + const secondaryFileName = "secondary-root-read-file.txt" + const expectedContent = "SECONDARY_ROOT_MARKER_204\n" + const secondaryFilePath = path.join(secondaryRoot, secondaryFileName) + + await fs.writeFile(secondaryFilePath, expectedContent, "utf8") + + const api = globalThis.api + const messages: ClineMessage[] = [] + const messageHandler = ({ message }: { message: ClineMessage }) => { + if (message.partial !== true) { + messages.push(message) + } + } + api.on(RooCodeEventName.Message, messageHandler) + + let taskCompleted = false + let taskId = "" + const taskCompletedHandler = (id: string) => { + if (id === taskId) { + taskCompleted = true + } + } + api.on(RooCodeEventName.TaskCompleted, taskCompletedHandler) + + try { + taskId = await api.startNewTask({ + configuration: { + mode: "code", + autoApprovalEnabled: true, + alwaysAllowReadOnly: true, + alwaysAllowReadOnlyOutsideWorkspace: true, + }, + text: + `READ_FILE_MULTI_ROOT_REPRO: Use only the read_file tool to read "${secondaryFileName}". ` + + `The file exists in the current VS Code workspace, but only inside the secondary workspace root. ` + + `After the read attempt, explain exactly what happened.`, + }) + + await waitFor(() => taskCompleted, { timeout: 60_000, interval: 250 }) + + assert.ok( + vscode.workspace.workspaceFolders?.some((folder) => folder.uri.fsPath === secondaryRoot), + `Expected secondary root ${secondaryRoot} to remain part of the workspace during the repro`, + ) + + const completionMessage = messages.find( + (message) => + message.type === "say" && + (message.say === "completion_result" || message.say === "text") && + message.text?.includes("SECONDARY_ROOT_MARKER_204"), + ) + + assert.ok( + completionMessage, + `Expected the task to read the secondary-root file. Primary root was ${primaryRoot}, secondary root was ${secondaryRoot}, secondary file was ${secondaryFilePath}, and messages were ${JSON.stringify(messages, null, 2)}.`, + ) + } finally { + api.off(RooCodeEventName.Message, messageHandler) + api.off(RooCodeEventName.TaskCompleted, taskCompletedHandler) + + try { + await api.cancelCurrentTask() + } catch { + // Ignore cleanup races if the task already ended. + } + + await fs.rm(secondaryFilePath, { force: true }) + } + }) +}) diff --git a/locales/ca/README.md b/locales/ca/README.md index 39086b92b8..90a360f16f 100644 --- a/locales/ca/README.md +++ b/locales/ca/README.md @@ -62,7 +62,7 @@ Pots trobar una guia ràpida per passar de Roo Code a Zoo Code a la [guia de mig - [简体中文](../zh-CN/README.md) - [繁體中文](../zh-TW/README.md) - ... - + --- diff --git a/locales/de/README.md b/locales/de/README.md index 7c13790d6b..e1aa627fba 100644 --- a/locales/de/README.md +++ b/locales/de/README.md @@ -62,7 +62,7 @@ Eine kurze Anleitung für den Wechsel von Roo Code zu Zoo Code findest du im [Ro - [简体中文](../zh-CN/README.md) - [繁體中文](../zh-TW/README.md) - ... - + --- diff --git a/locales/es/README.md b/locales/es/README.md index 081a4bee75..f3fa1b2f7f 100644 --- a/locales/es/README.md +++ b/locales/es/README.md @@ -62,7 +62,7 @@ Puedes encontrar una guía rápida para pasar de Roo Code a Zoo Code en la [guí - [简体中文](../zh-CN/README.md) - [繁體中文](../zh-TW/README.md) - ... - + --- diff --git a/locales/fr/README.md b/locales/fr/README.md index 4b7e0b2235..9e628daddc 100644 --- a/locales/fr/README.md +++ b/locales/fr/README.md @@ -62,7 +62,7 @@ Tu peux trouver un guide rapide pour passer de Roo Code à Zoo Code dans le [gui - [简体中文](../zh-CN/README.md) - [繁體中文](../zh-TW/README.md) - ... - + --- diff --git a/locales/hi/README.md b/locales/hi/README.md index c7035ead66..4f8dbe74ee 100644 --- a/locales/hi/README.md +++ b/locales/hi/README.md @@ -62,7 +62,7 @@ Roo Code से Zoo Code में आने के लिए एक quick guide - [简体中文](../zh-CN/README.md) - [繁體中文](../zh-TW/README.md) - ... - + --- diff --git a/locales/id/README.md b/locales/id/README.md index 00041025c8..9ee9fb0f6e 100644 --- a/locales/id/README.md +++ b/locales/id/README.md @@ -62,7 +62,7 @@ Kamu bisa menemukan panduan singkat untuk berpindah dari Roo Code ke Zoo Code di - [简体中文](../zh-CN/README.md) - [繁體中文](../zh-TW/README.md) - ... - + --- diff --git a/locales/it/README.md b/locales/it/README.md index dbc8c97cb2..5bc0a9f582 100644 --- a/locales/it/README.md +++ b/locales/it/README.md @@ -62,7 +62,7 @@ Puoi trovare una guida rapida per passare da Roo Code a Zoo Code nella [guida al - [简体中文](../zh-CN/README.md) - [繁體中文](../zh-TW/README.md) - ... - + --- diff --git a/locales/ja/README.md b/locales/ja/README.md index 1795d59748..54e5638f94 100644 --- a/locales/ja/README.md +++ b/locales/ja/README.md @@ -62,7 +62,7 @@ Roo Code から Zoo Code へ移行するためのクイックガイドは、[Roo - [简体中文](../zh-CN/README.md) - [繁體中文](../zh-TW/README.md) - ... - + --- diff --git a/locales/ko/README.md b/locales/ko/README.md index 07aa5925e1..8045aca414 100644 --- a/locales/ko/README.md +++ b/locales/ko/README.md @@ -62,7 +62,7 @@ Roo Code에서 Zoo Code로 옮겨오는 빠른 가이드는 [Roo→Zoo 마이그 - [简体中文](../zh-CN/README.md) - [繁體中文](../zh-TW/README.md) - ... - + --- diff --git a/locales/nl/README.md b/locales/nl/README.md index 1f37ed43f7..7c0f3fd9d0 100644 --- a/locales/nl/README.md +++ b/locales/nl/README.md @@ -62,7 +62,7 @@ Je vindt een korte handleiding voor de overstap van Roo Code naar Zoo Code in de - [简体中文](../zh-CN/README.md) - [繁體中文](../zh-TW/README.md) - ... - + --- diff --git a/locales/pl/README.md b/locales/pl/README.md index c57d1e2235..2be9387db8 100644 --- a/locales/pl/README.md +++ b/locales/pl/README.md @@ -62,7 +62,7 @@ Szybki przewodnik po przejściu z Roo Code do Zoo Code znajdziesz w [przewodniku - [简体中文](../zh-CN/README.md) - [繁體中文](../zh-TW/README.md) - ... - + --- diff --git a/locales/pt-BR/README.md b/locales/pt-BR/README.md index f7fab3e196..b7db71ecbf 100644 --- a/locales/pt-BR/README.md +++ b/locales/pt-BR/README.md @@ -62,7 +62,7 @@ Você encontra um guia rápido para migrar do Roo Code para o Zoo Code no [guia - [简体中文](../zh-CN/README.md) - [繁體中文](../zh-TW/README.md) - ... - + --- diff --git a/locales/ru/README.md b/locales/ru/README.md index 8c0b5d86c5..7ffdb4c34e 100644 --- a/locales/ru/README.md +++ b/locales/ru/README.md @@ -62,7 +62,7 @@ - [简体中文](../zh-CN/README.md) - [繁體中文](../zh-TW/README.md) - ... - + --- diff --git a/locales/tr/README.md b/locales/tr/README.md index 21dbc2d8c1..4c681f86f7 100644 --- a/locales/tr/README.md +++ b/locales/tr/README.md @@ -62,7 +62,7 @@ Roo Code'dan Zoo Code'a geçmek için hızlı bir rehberi [Roo→Zoo geçiş reh - [简体中文](../zh-CN/README.md) - [繁體中文](../zh-TW/README.md) - ... - + --- diff --git a/locales/vi/README.md b/locales/vi/README.md index c1651f0919..4a6b2cb581 100644 --- a/locales/vi/README.md +++ b/locales/vi/README.md @@ -62,7 +62,7 @@ Bạn có thể xem hướng dẫn nhanh để chuyển từ Roo Code sang Zoo C - [简体中文](../zh-CN/README.md) - [繁體中文](../zh-TW/README.md) - ... - + --- diff --git a/locales/zh-CN/README.md b/locales/zh-CN/README.md index d044819f26..01b13a8e1d 100644 --- a/locales/zh-CN/README.md +++ b/locales/zh-CN/README.md @@ -62,7 +62,7 @@ - [简体中文](../zh-CN/README.md) - [繁體中文](../zh-TW/README.md) - ... - + --- diff --git a/locales/zh-TW/README.md b/locales/zh-TW/README.md index 45cb6339f3..40b90e5ad7 100644 --- a/locales/zh-TW/README.md +++ b/locales/zh-TW/README.md @@ -62,7 +62,7 @@ - [简体中文](../zh-CN/README.md) - [繁體中文](../zh-TW/README.md) - ... - + ---