From 788e25338a1f21fe0b15db92397cb7fc1c236c6a Mon Sep 17 00:00:00 2001 From: shanevcantwell <153727980+shanevcantwell@users.noreply.github.com> Date: Fri, 26 Dec 2025 02:34:36 -0700 Subject: [PATCH 1/8] fix: wrap fileURLToPath in try-catch for WSL2 compatibility MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The fileURLToPath function can throw "The URL must be of scheme file" in some WSL2/remote environments with malformed URIs. Wrap in try-catch to fall through to HOME directory fallback. Fixes #8091 πŸ€– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- core/tools/implementations/runTerminalCommand.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/core/tools/implementations/runTerminalCommand.ts b/core/tools/implementations/runTerminalCommand.ts index 19377a4eeb2..7fc26e9d7ed 100644 --- a/core/tools/implementations/runTerminalCommand.ts +++ b/core/tools/implementations/runTerminalCommand.ts @@ -52,7 +52,12 @@ function resolveWorkingDirectory(workspaceDirs: string[]): string { dir.startsWith("file:/"), ); if (fileWorkspaceDir) { - return fileURLToPath(fileWorkspaceDir); + try { + return fileURLToPath(fileWorkspaceDir); + } catch { + // fileURLToPath can fail on malformed URIs or in some remote environments + // Fall through to default handling + } } // Default to user's home directory with fallbacks try { From eab2ac1e045f2e4ba994cb7371b8e3a8e8aaf4b7 Mon Sep 17 00:00:00 2001 From: shanevcantwell <153727980+shanevcantwell@users.noreply.github.com> Date: Fri, 26 Dec 2025 05:10:13 -0700 Subject: [PATCH 2/8] fix: properly resolve WSL2 workspace paths in runTerminalCommand MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Parse vscode-remote://wsl+distro/path URIs to extract the actual workspace path instead of falling back to Windows HOME directory. πŸ€– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- core/tools/implementations/runTerminalCommand.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/core/tools/implementations/runTerminalCommand.ts b/core/tools/implementations/runTerminalCommand.ts index 7fc26e9d7ed..747e53fa3ea 100644 --- a/core/tools/implementations/runTerminalCommand.ts +++ b/core/tools/implementations/runTerminalCommand.ts @@ -48,6 +48,20 @@ import { getBooleanArg, getStringArg } from "../parseArgs"; * Falls back to home directory or temp directory if no workspace is available. */ function resolveWorkingDirectory(workspaceDirs: string[]): string { + // Handle vscode-remote://wsl+distro/path URIs (WSL2 remote workspaces) + const wslWorkspaceDir = workspaceDirs.find((dir) => + dir.startsWith("vscode-remote://wsl"), + ); + if (wslWorkspaceDir) { + try { + const url = new URL(wslWorkspaceDir); + return url.pathname; // e.g., "/home/user/project" + } catch { + // Fall through to other handlers + } + } + + // Handle file:// URIs (local workspaces) const fileWorkspaceDir = workspaceDirs.find((dir) => dir.startsWith("file:/"), ); @@ -59,6 +73,7 @@ function resolveWorkingDirectory(workspaceDirs: string[]): string { // Fall through to default handling } } + // Default to user's home directory with fallbacks try { return process.env.HOME || process.env.USERPROFILE || process.cwd(); From 074a6c74baf35b98e0092f0e6b37027bb6561eba Mon Sep 17 00:00:00 2001 From: shanevcantwell <153727980+shanevcantwell@users.noreply.github.com> Date: Fri, 26 Dec 2025 05:14:40 -0700 Subject: [PATCH 3/8] fix: decode URI-encoded pathname for WSL workspace paths MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Paths with spaces or special characters (e.g., /home/user/my%20project) need decodeURIComponent() to match actual filesystem paths. πŸ€– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- core/tools/implementations/runTerminalCommand.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/tools/implementations/runTerminalCommand.ts b/core/tools/implementations/runTerminalCommand.ts index 747e53fa3ea..e80414f921f 100644 --- a/core/tools/implementations/runTerminalCommand.ts +++ b/core/tools/implementations/runTerminalCommand.ts @@ -55,7 +55,7 @@ function resolveWorkingDirectory(workspaceDirs: string[]): string { if (wslWorkspaceDir) { try { const url = new URL(wslWorkspaceDir); - return url.pathname; // e.g., "/home/user/project" + return decodeURIComponent(url.pathname); // e.g., "/home/user/project" } catch { // Fall through to other handlers } From 3ef9f89e8f7d429faeb1aa3049fd1d0f7cb71a9b Mon Sep 17 00:00:00 2001 From: shanevcantwell <153727980+shanevcantwell@users.noreply.github.com> Date: Fri, 26 Dec 2025 05:30:02 -0700 Subject: [PATCH 4/8] test: add comprehensive tests for WSL workspace path resolution MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tests cover: - Basic WSL URI parsing - URL-encoded spaces and special characters - Unicode path handling - Different WSL distro names - Priority of WSL URIs over file:// URIs - Fallback behavior - Edge cases (plus signs, percent signs, mixed encoding) - Comparison with fileURLToPath behavior πŸ€– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .../resolveWorkingDirectory.vitest.ts | 202 ++++++++++++++++++ 1 file changed, 202 insertions(+) create mode 100644 core/tools/implementations/resolveWorkingDirectory.vitest.ts diff --git a/core/tools/implementations/resolveWorkingDirectory.vitest.ts b/core/tools/implementations/resolveWorkingDirectory.vitest.ts new file mode 100644 index 00000000000..f4d4c308eb8 --- /dev/null +++ b/core/tools/implementations/resolveWorkingDirectory.vitest.ts @@ -0,0 +1,202 @@ +import { describe, expect, it } from "vitest"; +import { fileURLToPath } from "node:url"; + +/** + * Test suite for workspace directory resolution logic. + * + * This tests the URI parsing behavior used in runTerminalCommand.ts + * to ensure correct handling of various workspace URI formats. + */ + +// Replicate the resolution logic for testing +function resolveWorkingDirectory(workspaceDirs: string[]): string { + // Handle vscode-remote://wsl+distro/path URIs (WSL2 remote workspaces) + const wslWorkspaceDir = workspaceDirs.find((dir) => + dir.startsWith("vscode-remote://wsl"), + ); + if (wslWorkspaceDir) { + try { + const url = new URL(wslWorkspaceDir); + return decodeURIComponent(url.pathname); + } catch { + // Fall through to other handlers + } + } + + // Handle file:// URIs (local workspaces) + const fileWorkspaceDir = workspaceDirs.find((dir) => + dir.startsWith("file:/"), + ); + if (fileWorkspaceDir) { + try { + return fileURLToPath(fileWorkspaceDir); + } catch { + // Fall through to default handling + } + } + + // Default to user's home directory with fallbacks + try { + return process.env.HOME || process.env.USERPROFILE || process.cwd(); + } catch { + return "/tmp"; + } +} + +describe("resolveWorkingDirectory", () => { + describe("WSL remote URIs (vscode-remote://wsl+...)", () => { + it("should parse basic WSL URI", () => { + const result = resolveWorkingDirectory([ + "vscode-remote://wsl+Ubuntu/home/user/project", + ]); + expect(result).toBe("/home/user/project"); + }); + + it("should decode URL-encoded spaces in path", () => { + const result = resolveWorkingDirectory([ + "vscode-remote://wsl+Ubuntu/home/user/my%20project", + ]); + expect(result).toBe("/home/user/my project"); + }); + + it("should decode URL-encoded special characters", () => { + const result = resolveWorkingDirectory([ + "vscode-remote://wsl+Ubuntu/home/user/path%23with%23hashes", + ]); + expect(result).toBe("/home/user/path#with#hashes"); + }); + + it("should decode URL-encoded unicode characters", () => { + const result = resolveWorkingDirectory([ + "vscode-remote://wsl+Ubuntu/home/user/%E4%B8%AD%E6%96%87%E8%B7%AF%E5%BE%84", + ]); + expect(result).toBe("/home/user/δΈ­ζ–‡θ·―εΎ„"); + }); + + it("should handle different WSL distro names", () => { + const ubuntu = resolveWorkingDirectory([ + "vscode-remote://wsl+Ubuntu-22.04/home/user/project", + ]); + expect(ubuntu).toBe("/home/user/project"); + + const debian = resolveWorkingDirectory([ + "vscode-remote://wsl+Debian/home/user/project", + ]); + expect(debian).toBe("/home/user/project"); + }); + + it("should handle root path", () => { + const result = resolveWorkingDirectory([ + "vscode-remote://wsl+Ubuntu/", + ]); + expect(result).toBe("/"); + }); + + it("should prioritize WSL URIs over file:// URIs", () => { + const result = resolveWorkingDirectory([ + "file:///c:/Users/user/project", + "vscode-remote://wsl+Ubuntu/home/user/project", + ]); + expect(result).toBe("/home/user/project"); + }); + }); + + describe("file:// URIs (local workspaces)", () => { + it("should parse basic file:// URI on Unix", () => { + const result = resolveWorkingDirectory([ + "file:///home/user/project", + ]); + expect(result).toBe("/home/user/project"); + }); + + it("should decode URL-encoded spaces in file:// URI", () => { + const result = resolveWorkingDirectory([ + "file:///home/user/my%20project", + ]); + expect(result).toBe("/home/user/my project"); + }); + + it("should handle Windows-style file:// URI", () => { + // fileURLToPath handles Windows paths correctly + const result = resolveWorkingDirectory([ + "file:///C:/Users/user/project", + ]); + // On Unix, this will be /C:/Users/user/project + // On Windows, this will be C:\Users\user\project + expect(result).toMatch(/project$/); + }); + }); + + describe("fallback behavior", () => { + it("should fall back to HOME when no valid URIs", () => { + const originalHome = process.env.HOME; + process.env.HOME = "/test/home"; + + const result = resolveWorkingDirectory([]); + expect(result).toBe("/test/home"); + + process.env.HOME = originalHome; + }); + + it("should handle empty workspace dirs array", () => { + const result = resolveWorkingDirectory([]); + // Should return HOME or USERPROFILE or cwd + expect(typeof result).toBe("string"); + expect(result.length).toBeGreaterThan(0); + }); + + it("should handle invalid URIs gracefully", () => { + const result = resolveWorkingDirectory([ + "not-a-valid-uri", + "also://not/handled", + ]); + // Should fall through to HOME fallback + expect(typeof result).toBe("string"); + }); + + it("should handle malformed vscode-remote URI", () => { + const result = resolveWorkingDirectory([ + "vscode-remote://wsl+Ubuntu", // Missing path + ]); + // new URL() should still parse this, pathname would be empty or "/" + expect(typeof result).toBe("string"); + }); + }); + + describe("URL encoding edge cases", () => { + it("should handle plus signs (not spaces)", () => { + // In URL encoding, + is literal plus, %2B is encoded plus, %20 is space + const result = resolveWorkingDirectory([ + "vscode-remote://wsl+Ubuntu/home/user/c%2B%2B-project", + ]); + expect(result).toBe("/home/user/c++-project"); + }); + + it("should handle percent sign itself", () => { + const result = resolveWorkingDirectory([ + "vscode-remote://wsl+Ubuntu/home/user/100%25-complete", + ]); + expect(result).toBe("/home/user/100%-complete"); + }); + + it("should handle mixed encoded and unencoded characters", () => { + const result = resolveWorkingDirectory([ + "vscode-remote://wsl+Ubuntu/home/user/normal-path/with%20space/more", + ]); + expect(result).toBe("/home/user/normal-path/with space/more"); + }); + }); + + describe("comparison with fileURLToPath behavior", () => { + it("should match fileURLToPath decoding for equivalent paths", () => { + const fileResult = fileURLToPath("file:///home/user/my%20project"); + const wslResult = resolveWorkingDirectory([ + "vscode-remote://wsl+Ubuntu/home/user/my%20project", + ]); + + // Both should decode %20 to space + expect(fileResult).toBe("/home/user/my project"); + expect(wslResult).toBe("/home/user/my project"); + }); + }); +}); From 27adb1d677653904c8a2efad1fc1065deb7993e7 Mon Sep 17 00:00:00 2001 From: shanevcantwell <153727980+shanevcantwell@users.noreply.github.com> Date: Fri, 26 Dec 2025 05:36:39 -0700 Subject: [PATCH 5/8] fix: use try/finally for env cleanup in test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ensures process.env.HOME is restored even if assertions fail, preventing flaky behavior in subsequent tests. πŸ€– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .../resolveWorkingDirectory.vitest.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/core/tools/implementations/resolveWorkingDirectory.vitest.ts b/core/tools/implementations/resolveWorkingDirectory.vitest.ts index f4d4c308eb8..76858ae14d0 100644 --- a/core/tools/implementations/resolveWorkingDirectory.vitest.ts +++ b/core/tools/implementations/resolveWorkingDirectory.vitest.ts @@ -130,12 +130,13 @@ describe("resolveWorkingDirectory", () => { describe("fallback behavior", () => { it("should fall back to HOME when no valid URIs", () => { const originalHome = process.env.HOME; - process.env.HOME = "/test/home"; - - const result = resolveWorkingDirectory([]); - expect(result).toBe("/test/home"); - - process.env.HOME = originalHome; + try { + process.env.HOME = "/test/home"; + const result = resolveWorkingDirectory([]); + expect(result).toBe("/test/home"); + } finally { + process.env.HOME = originalHome; + } }); it("should handle empty workspace dirs array", () => { From e2511133268620d485d05d7918618dd3c3d15c13 Mon Sep 17 00:00:00 2001 From: shanevcantwell <153727980+shanevcantwell@users.noreply.github.com> Date: Fri, 26 Dec 2025 06:22:54 -0700 Subject: [PATCH 6/8] docs: add CLAUDE.md with WSL2 development notes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Documents workarounds for: - Building VSIX from WSL2 (extension host selection) - File count issues affecting activation - Terminal command working directory resolution πŸ€– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- CLAUDE.md | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000000..f7d17365a6f --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,51 @@ +# Continue Development Notes + +## WSL2 Development + +### Building VSIX from WSL2 + +The VS Code extension VSIX built on Linux contains Linux-only native binaries (sqlite3, LanceDB, ripgrep). When VS Code runs in WSL remote mode, extensions can run in either: + +- **Windows extension host** - needs Windows binaries +- **WSL extension host** - Linux binaries work + +If both hosts try to load the extension, the Windows host fails with: +``` +Error: node_sqlite3.node is not a valid Win32 application +``` + +**Workaround for local testing:** + +1. Build VSIX normally: `npm run package` (in extensions/vscode) +2. Install: `code --install-extension extensions/vscode/build/continue-*.vsix` +3. Delete Windows installation to force WSL-only: + ```bash + rm -rf /mnt/c/Users//.vscode/extensions/continue.continue-* + ``` +4. Reload VS Code + +This forces Continue to run exclusively in WSL extension host where Linux binaries work. + +**Related:** GitHub Issue #9326 + +### File Count and Extension Activation + +Large file counts (300K+) from node_modules and build artifacts can cause extension activation issues. If Continue fails to load: + +1. Delete build artifacts: + ```bash + rm -rf */node_modules */*/node_modules node_modules + rm -rf */dist */*/dist */out */*/out */build + ``` +2. Reload VS Code +3. Rebuild only when needed for testing + +### Terminal Command Working Directory + +The `run_terminal_command` tool resolves workspace directories from VS Code URIs. In WSL2: + +- URIs are `vscode-remote://wsl+Ubuntu/path` (not `file://`) +- Must parse with `new URL()` and extract pathname +- Must `decodeURIComponent()` for paths with spaces/special chars + +See: `core/tools/implementations/runTerminalCommand.ts` From 6b30a88fb074b9dbd4927b33ea7e9c1d80e590f7 Mon Sep 17 00:00:00 2001 From: shanevcantwell <153727980+shanevcantwell@users.noreply.github.com> Date: Fri, 26 Dec 2025 06:27:00 -0700 Subject: [PATCH 7/8] style: fix prettier formatting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit πŸ€– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- CLAUDE.md | 1 + .../resolveWorkingDirectory.vitest.ts | 12 +++--------- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index f7d17365a6f..4de463f016c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -10,6 +10,7 @@ The VS Code extension VSIX built on Linux contains Linux-only native binaries (s - **WSL extension host** - Linux binaries work If both hosts try to load the extension, the Windows host fails with: + ``` Error: node_sqlite3.node is not a valid Win32 application ``` diff --git a/core/tools/implementations/resolveWorkingDirectory.vitest.ts b/core/tools/implementations/resolveWorkingDirectory.vitest.ts index 76858ae14d0..6940af2ea72 100644 --- a/core/tools/implementations/resolveWorkingDirectory.vitest.ts +++ b/core/tools/implementations/resolveWorkingDirectory.vitest.ts @@ -86,9 +86,7 @@ describe("resolveWorkingDirectory", () => { }); it("should handle root path", () => { - const result = resolveWorkingDirectory([ - "vscode-remote://wsl+Ubuntu/", - ]); + const result = resolveWorkingDirectory(["vscode-remote://wsl+Ubuntu/"]); expect(result).toBe("/"); }); @@ -103,9 +101,7 @@ describe("resolveWorkingDirectory", () => { describe("file:// URIs (local workspaces)", () => { it("should parse basic file:// URI on Unix", () => { - const result = resolveWorkingDirectory([ - "file:///home/user/project", - ]); + const result = resolveWorkingDirectory(["file:///home/user/project"]); expect(result).toBe("/home/user/project"); }); @@ -118,9 +114,7 @@ describe("resolveWorkingDirectory", () => { it("should handle Windows-style file:// URI", () => { // fileURLToPath handles Windows paths correctly - const result = resolveWorkingDirectory([ - "file:///C:/Users/user/project", - ]); + const result = resolveWorkingDirectory(["file:///C:/Users/user/project"]); // On Unix, this will be /C:/Users/user/project // On Windows, this will be C:\Users\user\project expect(result).toMatch(/project$/); From 8e5a7c2152a2e4e0baf88466900b258a9f2c28fd Mon Sep 17 00:00:00 2001 From: shanevcantwell <153727980+shanevcantwell@users.noreply.github.com> Date: Tue, 6 Jan 2026 19:08:33 -0700 Subject: [PATCH 8/8] refactor: generalize remote URI handling and remove CLAUDE.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Remove WSL-specific check in favor of generic URI handling - Support all vscode-remote:// URI schemes (WSL, SSH, dev-container) - Prefer file:// URIs when available, fall back to remote URIs - Add tests for various remote URI formats - Remove CLAUDE.md per reviewer feedback (use AGENTS.md convention) πŸ€– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- CLAUDE.md | 52 ------------------- .../implementations/runTerminalCommand.ts | 26 +++++----- .../runTerminalCommand.vitest.ts | 52 +++++++++++++++++++ 3 files changed, 65 insertions(+), 65 deletions(-) delete mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index 4de463f016c..00000000000 --- a/CLAUDE.md +++ /dev/null @@ -1,52 +0,0 @@ -# Continue Development Notes - -## WSL2 Development - -### Building VSIX from WSL2 - -The VS Code extension VSIX built on Linux contains Linux-only native binaries (sqlite3, LanceDB, ripgrep). When VS Code runs in WSL remote mode, extensions can run in either: - -- **Windows extension host** - needs Windows binaries -- **WSL extension host** - Linux binaries work - -If both hosts try to load the extension, the Windows host fails with: - -``` -Error: node_sqlite3.node is not a valid Win32 application -``` - -**Workaround for local testing:** - -1. Build VSIX normally: `npm run package` (in extensions/vscode) -2. Install: `code --install-extension extensions/vscode/build/continue-*.vsix` -3. Delete Windows installation to force WSL-only: - ```bash - rm -rf /mnt/c/Users//.vscode/extensions/continue.continue-* - ``` -4. Reload VS Code - -This forces Continue to run exclusively in WSL extension host where Linux binaries work. - -**Related:** GitHub Issue #9326 - -### File Count and Extension Activation - -Large file counts (300K+) from node_modules and build artifacts can cause extension activation issues. If Continue fails to load: - -1. Delete build artifacts: - ```bash - rm -rf */node_modules */*/node_modules node_modules - rm -rf */dist */*/dist */out */*/out */build - ``` -2. Reload VS Code -3. Rebuild only when needed for testing - -### Terminal Command Working Directory - -The `run_terminal_command` tool resolves workspace directories from VS Code URIs. In WSL2: - -- URIs are `vscode-remote://wsl+Ubuntu/path` (not `file://`) -- Must parse with `new URL()` and extract pathname -- Must `decodeURIComponent()` for paths with spaces/special chars - -See: `core/tools/implementations/runTerminalCommand.ts` diff --git a/core/tools/implementations/runTerminalCommand.ts b/core/tools/implementations/runTerminalCommand.ts index e80414f921f..bed8aceb85a 100644 --- a/core/tools/implementations/runTerminalCommand.ts +++ b/core/tools/implementations/runTerminalCommand.ts @@ -48,19 +48,6 @@ import { getBooleanArg, getStringArg } from "../parseArgs"; * Falls back to home directory or temp directory if no workspace is available. */ function resolveWorkingDirectory(workspaceDirs: string[]): string { - // Handle vscode-remote://wsl+distro/path URIs (WSL2 remote workspaces) - const wslWorkspaceDir = workspaceDirs.find((dir) => - dir.startsWith("vscode-remote://wsl"), - ); - if (wslWorkspaceDir) { - try { - const url = new URL(wslWorkspaceDir); - return decodeURIComponent(url.pathname); // e.g., "/home/user/project" - } catch { - // Fall through to other handlers - } - } - // Handle file:// URIs (local workspaces) const fileWorkspaceDir = workspaceDirs.find((dir) => dir.startsWith("file:/"), @@ -74,6 +61,19 @@ function resolveWorkingDirectory(workspaceDirs: string[]): string { } } + // Handle other URI schemes (vscode-remote://wsl, vscode-remote://ssh-remote, etc.) + const remoteWorkspaceDir = workspaceDirs.find( + (dir) => dir.includes("://") && !dir.startsWith("file:/"), + ); + if (remoteWorkspaceDir) { + try { + const url = new URL(remoteWorkspaceDir); + return decodeURIComponent(url.pathname); + } catch { + // Fall through to other handlers + } + } + // Default to user's home directory with fallbacks try { return process.env.HOME || process.env.USERPROFILE || process.cwd(); diff --git a/core/tools/implementations/runTerminalCommand.vitest.ts b/core/tools/implementations/runTerminalCommand.vitest.ts index 428cc83065a..089c855028e 100644 --- a/core/tools/implementations/runTerminalCommand.vitest.ts +++ b/core/tools/implementations/runTerminalCommand.vitest.ts @@ -553,6 +553,58 @@ describe("runTerminalCommandImpl", () => { // This demonstrates why the fix is needed - fileURLToPath throws on non-file URIs expect(() => fileURLToPath(nonFileUri)).toThrow(); }); + + it("should handle vscode-remote URIs by extracting pathname", async () => { + // Various remote URI formats that VS Code uses + const remoteUris = [ + "vscode-remote://wsl+Ubuntu/home/user/project", + "vscode-remote://ssh-remote+myserver/home/user/project", + "vscode-remote://dev-container+abc123/workspace", + ]; + + for (const uri of remoteUris) { + mockGetWorkspaceDirs.mockResolvedValue([uri]); + + // Should not throw - the generic URI handler extracts the pathname + await expect( + runTerminalCommandImpl( + { command: "echo test", waitForCompletion: false }, + createMockExtras(), + ), + ).resolves.toBeDefined(); + } + }); + + it("should decode URI-encoded characters in remote workspace paths", async () => { + // Path with spaces and special characters + const encodedUri = + "vscode-remote://wsl+Ubuntu/home/user/my%20project%20%28test%29"; + mockGetWorkspaceDirs.mockResolvedValue([encodedUri]); + + // Should handle without throwing - decodeURIComponent is applied + await expect( + runTerminalCommandImpl( + { command: "echo test", waitForCompletion: false }, + createMockExtras(), + ), + ).resolves.toBeDefined(); + }); + + it("should prefer file:// URIs over remote URIs when both present", async () => { + const workspaceDirs = [ + "vscode-remote://wsl+Ubuntu/home/user/remote-project", + "file:///home/user/local-project", + ]; + mockGetWorkspaceDirs.mockResolvedValue(workspaceDirs); + + // Should succeed, preferring the file:// URI + await expect( + runTerminalCommandImpl( + { command: "echo test", waitForCompletion: false }, + createMockExtras(), + ), + ).resolves.toBeDefined(); + }); }); describe("remote environment handling", () => {