From 865951c48f86ef0d43c3cce6bd86efeb6a4db1ec Mon Sep 17 00:00:00 2001 From: Elliott de Launay Date: Sun, 26 Jul 2026 22:43:47 +0000 Subject: [PATCH 1/5] fix(ripgrep): resolve platform-specific binary for @vscode/ripgrep >=1.18 --- src/services/ripgrep/__tests__/index.spec.ts | 41 ++++++++++++++++++++ src/services/ripgrep/index.ts | 30 ++++++++++++-- 2 files changed, 68 insertions(+), 3 deletions(-) diff --git a/src/services/ripgrep/__tests__/index.spec.ts b/src/services/ripgrep/__tests__/index.spec.ts index e6a613ad67..25076de471 100644 --- a/src/services/ripgrep/__tests__/index.spec.ts +++ b/src/services/ripgrep/__tests__/index.spec.ts @@ -10,7 +10,20 @@ vi.mock("../../../utils/fs", () => ({ fileExistsAtPath: vi.fn(), })) +vi.mock("fs", () => ({ + existsSync: vi.fn(), +})) + +vi.mock("module", () => ({ + createRequire: vi.fn(), +})) + +import * as fs from "fs" +import { createRequire } from "module" + const mockFileExists = vi.mocked(fileExistsAtPath) +const mockExistsSync = vi.mocked(fs.existsSync) +const mockCreateRequire = vi.mocked(createRequire) describe("Ripgrep line truncation", () => { // The default MAX_LINE_LENGTH is 500 in the implementation @@ -67,6 +80,9 @@ describe("getBinPath", () => { beforeEach(() => { mockFileExists.mockReset() mockFileExists.mockResolvedValue(false) + mockExistsSync.mockReset() + mockExistsSync.mockReturnValue(false) + mockCreateRequire.mockReset() }) it("resolves ripgrep from the classic @vscode/ripgrep layout", async () => { @@ -95,4 +111,29 @@ describe("getBinPath", () => { expect(await getBinPath(appRoot)).toBeUndefined() }) + + // Regression test for https://github.com/Zoo-Code-Org/Zoo-Code/issues/1024 + // VS Code 1.130+ ships @vscode/ripgrep >=1.18, where the binary lives in a + // platform-specific optional package resolved via the wrapper's package.json. + // None of the six hardcoded candidate paths match this layout, so getBinPath + // returns undefined on affected Windows installs, causing every task to hang. + it("resolves ripgrep via the @vscode/ripgrep >=1.18 platform-package layout", async () => { + const platformBin = `/vscode/ripgrep-${process.platform}-${process.arch}/bin/${binName}` + + // All static candidates miss. + mockFileExists.mockResolvedValue(false) + + // Simulate @vscode/ripgrep wrapper manifest existing and resolving to the platform bin. + mockExistsSync.mockReturnValue(true) + const mockRequireFromWrapper = { resolve: vi.fn().mockReturnValue(platformBin) } + const mockRequireFromApp = { resolve: vi.fn().mockReturnValue("/vscode/ripgrep/index.js") } + mockCreateRequire + .mockReturnValueOnce(mockRequireFromApp as unknown as NodeRequire) + .mockReturnValueOnce(mockRequireFromWrapper as unknown as NodeRequire) + + // The resolved platform bin exists on disk. + mockFileExists.mockImplementation(async (p: string) => p === platformBin) + + expect(await getBinPath(appRoot)).toBe(platformBin) + }) }) diff --git a/src/services/ripgrep/index.ts b/src/services/ripgrep/index.ts index 96f5c34e4b..0b18962971 100644 --- a/src/services/ripgrep/index.ts +++ b/src/services/ripgrep/index.ts @@ -1,6 +1,8 @@ import * as childProcess from "child_process" +import * as fs from "fs" import * as path from "path" import * as readline from "readline" +import { createRequire } from "module" import * as vscode from "vscode" @@ -104,12 +106,30 @@ export function ripgrepCandidatePaths(vscodeAppRoot: string): readonly string[] ] } +/** + * Resolves ripgrep for @vscode/ripgrep >=1.18, which ships the binary inside a + * platform-specific optional package (e.g. @vscode/ripgrep-win32-x64) rather + * than directly in @vscode/ripgrep/bin/. VS Code 1.130+ uses this layout. + */ +export function resolvePlatformRipgrepPath(vscodeAppRoot: string): string | undefined { + try { + const wrapperManifest = path.join(vscodeAppRoot, "node_modules", "@vscode", "ripgrep", "package.json") + if (!fs.existsSync(wrapperManifest)) return undefined + const requireFromApp = createRequire(path.join(vscodeAppRoot, "package.json")) + const wrapperEntry = requireFromApp.resolve("@vscode/ripgrep") + const requireFromWrapper = createRequire(wrapperEntry) + return requireFromWrapper.resolve(`@vscode/ripgrep-${process.platform}-${process.arch}/bin/${binName}`) + } catch { + return undefined + } +} + /** * Get the path to the ripgrep binary shipped inside the VS Code installation. * - * Both the long-standing `@vscode/ripgrep` layout and the newer - * `@vscode/ripgrep-universal` layout are checked — the latter is what VS Code - * Insiders' staged-install builds use (see microsoft/vscode#252063). + * Checks the long-standing @vscode/ripgrep and @vscode/ripgrep-universal static + * layouts first, then falls back to the @vscode/ripgrep >=1.18 platform-package + * layout used by VS Code 1.130+ (see microsoft/vscode#252063). * * Returns `undefined` when ripgrep cannot be located. */ @@ -117,6 +137,10 @@ export async function getBinPath(vscodeAppRoot: string): Promise Date: Sun, 26 Jul 2026 22:52:06 +0000 Subject: [PATCH 2/5] fix(environment): gracefully handle ripgrep not found during file listing --- .../__tests__/getEnvironmentDetails.spec.ts | 10 +++++++ src/core/environment/getEnvironmentDetails.ts | 28 +++++++++++-------- 2 files changed, 26 insertions(+), 12 deletions(-) diff --git a/src/core/environment/__tests__/getEnvironmentDetails.spec.ts b/src/core/environment/__tests__/getEnvironmentDetails.spec.ts index 1fcdac0ed7..04bbfdd4df 100644 --- a/src/core/environment/__tests__/getEnvironmentDetails.spec.ts +++ b/src/core/environment/__tests__/getEnvironmentDetails.spec.ts @@ -446,4 +446,14 @@ describe("getEnvironmentDetails", () => { expect(getGitStatus).toHaveBeenCalledWith(mockCwd, 5) }) + + // Regression test for https://github.com/Zoo-Code-Org/Zoo-Code/issues/1024 + // When ripgrep cannot be found (e.g. @vscode/ripgrep >=1.18 platform layout on + // Windows), listFiles throws "Could not find ripgrep binary". getEnvironmentDetails + // must not propagate this — the task should proceed to the API call, not hang at 0%. + it("should not throw when listFiles rejects with ripgrep not found", async () => { + ;(listFiles as Mock).mockRejectedValue(new Error("Could not find ripgrep binary")) + + await expect(getEnvironmentDetails(mockCline as Task, true)).resolves.not.toThrow() + }) }) diff --git a/src/core/environment/getEnvironmentDetails.ts b/src/core/environment/getEnvironmentDetails.ts index 141ffdf3e2..0e7d18a57a 100644 --- a/src/core/environment/getEnvironmentDetails.ts +++ b/src/core/environment/getEnvironmentDetails.ts @@ -241,18 +241,22 @@ export async function getEnvironmentDetails(cline: Task, includeFileDetails: boo if (maxFiles === 0) { details += "(Workspace files context disabled. Use list_files to explore if needed.)" } else { - const [files, didHitLimit] = await listFiles(cline.cwd, true, maxFiles) - const { showRooIgnoredFiles = false } = state ?? {} - - const result = formatResponse.formatFilesList( - cline.cwd, - files, - didHitLimit, - cline.rooIgnoreController, - showRooIgnoredFiles, - ) - - details += result + try { + const [files, didHitLimit] = await listFiles(cline.cwd, true, maxFiles) + const { showRooIgnoredFiles = false } = state ?? {} + + const result = formatResponse.formatFilesList( + cline.cwd, + files, + didHitLimit, + cline.rooIgnoreController, + showRooIgnoredFiles, + ) + + details += result + } catch (error) { + details += `(File listing unavailable: ${error instanceof Error ? error.message : String(error)})` + } } } } From dadc736593dd4394db97b8136c19cce81a73beb6 Mon Sep 17 00:00:00 2001 From: Elliott de Launay Date: Sun, 26 Jul 2026 22:54:59 +0000 Subject: [PATCH 3/5] fix(ripgrep): use npm_config_arch for platform-package resolution --- src/services/ripgrep/__tests__/index.spec.ts | 46 +++++++++++++++++--- src/services/ripgrep/index.ts | 3 +- 2 files changed, 43 insertions(+), 6 deletions(-) diff --git a/src/services/ripgrep/__tests__/index.spec.ts b/src/services/ripgrep/__tests__/index.spec.ts index 25076de471..0559c1898a 100644 --- a/src/services/ripgrep/__tests__/index.spec.ts +++ b/src/services/ripgrep/__tests__/index.spec.ts @@ -112,6 +112,18 @@ describe("getBinPath", () => { expect(await getBinPath(appRoot)).toBeUndefined() }) + it("returns undefined when platform-package resolution fails", async () => { + mockFileExists.mockResolvedValue(false) + mockExistsSync.mockReturnValue(true) + mockCreateRequire.mockReturnValue({ + resolve: vi.fn(() => { + throw new Error("module not found") + }), + } as unknown as NodeRequire) + + await expect(getBinPath(appRoot)).resolves.toBeUndefined() + }) + // Regression test for https://github.com/Zoo-Code-Org/Zoo-Code/issues/1024 // VS Code 1.130+ ships @vscode/ripgrep >=1.18, where the binary lives in a // platform-specific optional package resolved via the wrapper's package.json. @@ -120,20 +132,44 @@ describe("getBinPath", () => { it("resolves ripgrep via the @vscode/ripgrep >=1.18 platform-package layout", async () => { const platformBin = `/vscode/ripgrep-${process.platform}-${process.arch}/bin/${binName}` - // All static candidates miss. mockFileExists.mockResolvedValue(false) - - // Simulate @vscode/ripgrep wrapper manifest existing and resolving to the platform bin. mockExistsSync.mockReturnValue(true) const mockRequireFromWrapper = { resolve: vi.fn().mockReturnValue(platformBin) } const mockRequireFromApp = { resolve: vi.fn().mockReturnValue("/vscode/ripgrep/index.js") } mockCreateRequire .mockReturnValueOnce(mockRequireFromApp as unknown as NodeRequire) .mockReturnValueOnce(mockRequireFromWrapper as unknown as NodeRequire) - - // The resolved platform bin exists on disk. mockFileExists.mockImplementation(async (p: string) => p === platformBin) expect(await getBinPath(appRoot)).toBe(platformBin) }) + + it("respects npm_config_arch override when resolving platform-package", async () => { + const overrideArch = "x64" + const platformBin = `/vscode/ripgrep-${process.platform}-${overrideArch}/bin/${binName}` + + const original = process.env.npm_config_arch + process.env.npm_config_arch = overrideArch + try { + mockFileExists.mockResolvedValue(false) + mockExistsSync.mockReturnValue(true) + const mockRequireFromWrapper = { resolve: vi.fn().mockReturnValue(platformBin) } + const mockRequireFromApp = { resolve: vi.fn().mockReturnValue("/vscode/ripgrep/index.js") } + mockCreateRequire + .mockReturnValueOnce(mockRequireFromApp as unknown as NodeRequire) + .mockReturnValueOnce(mockRequireFromWrapper as unknown as NodeRequire) + mockFileExists.mockImplementation(async (p: string) => p === platformBin) + + expect(await getBinPath(appRoot)).toBe(platformBin) + expect(mockRequireFromWrapper.resolve).toHaveBeenCalledWith( + `@vscode/ripgrep-${process.platform}-${overrideArch}/bin/${binName}`, + ) + } finally { + if (original === undefined) { + delete process.env.npm_config_arch + } else { + process.env.npm_config_arch = original + } + } + }) }) diff --git a/src/services/ripgrep/index.ts b/src/services/ripgrep/index.ts index 0b18962971..b526a18387 100644 --- a/src/services/ripgrep/index.ts +++ b/src/services/ripgrep/index.ts @@ -118,7 +118,8 @@ export function resolvePlatformRipgrepPath(vscodeAppRoot: string): string | unde const requireFromApp = createRequire(path.join(vscodeAppRoot, "package.json")) const wrapperEntry = requireFromApp.resolve("@vscode/ripgrep") const requireFromWrapper = createRequire(wrapperEntry) - return requireFromWrapper.resolve(`@vscode/ripgrep-${process.platform}-${process.arch}/bin/${binName}`) + const arch = process.env.npm_config_arch || process.arch + return requireFromWrapper.resolve(`@vscode/ripgrep-${process.platform}-${arch}/bin/${binName}`) } catch { return undefined } From 0a57e84af85aaaccae6f415b06df1ccfc8b6ccd2 Mon Sep 17 00:00:00 2001 From: Elliott de Launay Date: Sun, 26 Jul 2026 23:06:19 +0000 Subject: [PATCH 4/5] refactor(ripgrep): replace createRequire resolver with static candidate paths --- src/services/ripgrep/__tests__/index.spec.ts | 85 +++++++------------- src/services/ripgrep/index.ts | 40 +++------ 2 files changed, 39 insertions(+), 86 deletions(-) diff --git a/src/services/ripgrep/__tests__/index.spec.ts b/src/services/ripgrep/__tests__/index.spec.ts index 0559c1898a..defc6d23b2 100644 --- a/src/services/ripgrep/__tests__/index.spec.ts +++ b/src/services/ripgrep/__tests__/index.spec.ts @@ -10,20 +10,7 @@ vi.mock("../../../utils/fs", () => ({ fileExistsAtPath: vi.fn(), })) -vi.mock("fs", () => ({ - existsSync: vi.fn(), -})) - -vi.mock("module", () => ({ - createRequire: vi.fn(), -})) - -import * as fs from "fs" -import { createRequire } from "module" - const mockFileExists = vi.mocked(fileExistsAtPath) -const mockExistsSync = vi.mocked(fs.existsSync) -const mockCreateRequire = vi.mocked(createRequire) describe("Ripgrep line truncation", () => { // The default MAX_LINE_LENGTH is 500 in the implementation @@ -80,9 +67,6 @@ describe("getBinPath", () => { beforeEach(() => { mockFileExists.mockReset() mockFileExists.mockResolvedValue(false) - mockExistsSync.mockReset() - mockExistsSync.mockReturnValue(false) - mockCreateRequire.mockReset() }) it("resolves ripgrep from the classic @vscode/ripgrep layout", async () => { @@ -112,58 +96,43 @@ describe("getBinPath", () => { expect(await getBinPath(appRoot)).toBeUndefined() }) - it("returns undefined when platform-package resolution fails", async () => { - mockFileExists.mockResolvedValue(false) - mockExistsSync.mockReturnValue(true) - mockCreateRequire.mockReturnValue({ - resolve: vi.fn(() => { - throw new Error("module not found") - }), - } as unknown as NodeRequire) - - await expect(getBinPath(appRoot)).resolves.toBeUndefined() - }) - // Regression test for https://github.com/Zoo-Code-Org/Zoo-Code/issues/1024 // VS Code 1.130+ ships @vscode/ripgrep >=1.18, where the binary lives in a - // platform-specific optional package resolved via the wrapper's package.json. - // None of the six hardcoded candidate paths match this layout, so getBinPath - // returns undefined on affected Windows installs, causing every task to hang. - it("resolves ripgrep via the @vscode/ripgrep >=1.18 platform-package layout", async () => { - const platformBin = `/vscode/ripgrep-${process.platform}-${process.arch}/bin/${binName}` + // platform-specific optional package (e.g. @vscode/ripgrep-win32-x64). + // None of the previous candidate paths matched this layout. + it("resolves ripgrep from the @vscode/ripgrep >=1.18 platform-package layout", async () => { + const arch = process.env.npm_config_arch || process.arch + const rg = path.join(appRoot, `node_modules/@vscode/ripgrep-${process.platform}-${arch}/bin`, binName) + mockFileExists.mockImplementation(async (p: string) => p === rg) - mockFileExists.mockResolvedValue(false) - mockExistsSync.mockReturnValue(true) - const mockRequireFromWrapper = { resolve: vi.fn().mockReturnValue(platformBin) } - const mockRequireFromApp = { resolve: vi.fn().mockReturnValue("/vscode/ripgrep/index.js") } - mockCreateRequire - .mockReturnValueOnce(mockRequireFromApp as unknown as NodeRequire) - .mockReturnValueOnce(mockRequireFromWrapper as unknown as NodeRequire) - mockFileExists.mockImplementation(async (p: string) => p === platformBin) - - expect(await getBinPath(appRoot)).toBe(platformBin) + expect(await getBinPath(appRoot)).toBe(rg) }) - it("respects npm_config_arch override when resolving platform-package", async () => { - const overrideArch = "x64" - const platformBin = `/vscode/ripgrep-${process.platform}-${overrideArch}/bin/${binName}` + it("resolves ripgrep from the unpacked @vscode/ripgrep >=1.18 platform-package layout", async () => { + const arch = process.env.npm_config_arch || process.arch + const rg = path.join( + appRoot, + `node_modules.asar.unpacked/@vscode/ripgrep-${process.platform}-${arch}/bin`, + binName, + ) + mockFileExists.mockImplementation(async (p: string) => p === rg) + expect(await getBinPath(appRoot)).toBe(rg) + }) + + it("respects npm_config_arch when selecting the platform package", async () => { + const overrideArch = "x64" const original = process.env.npm_config_arch process.env.npm_config_arch = overrideArch try { - mockFileExists.mockResolvedValue(false) - mockExistsSync.mockReturnValue(true) - const mockRequireFromWrapper = { resolve: vi.fn().mockReturnValue(platformBin) } - const mockRequireFromApp = { resolve: vi.fn().mockReturnValue("/vscode/ripgrep/index.js") } - mockCreateRequire - .mockReturnValueOnce(mockRequireFromApp as unknown as NodeRequire) - .mockReturnValueOnce(mockRequireFromWrapper as unknown as NodeRequire) - mockFileExists.mockImplementation(async (p: string) => p === platformBin) - - expect(await getBinPath(appRoot)).toBe(platformBin) - expect(mockRequireFromWrapper.resolve).toHaveBeenCalledWith( - `@vscode/ripgrep-${process.platform}-${overrideArch}/bin/${binName}`, + const rg = path.join( + appRoot, + `node_modules/@vscode/ripgrep-${process.platform}-${overrideArch}/bin`, + binName, ) + mockFileExists.mockImplementation(async (p: string) => p === rg) + + expect(await getBinPath(appRoot)).toBe(rg) } finally { if (original === undefined) { delete process.env.npm_config_arch diff --git a/src/services/ripgrep/index.ts b/src/services/ripgrep/index.ts index b526a18387..79e0335a4f 100644 --- a/src/services/ripgrep/index.ts +++ b/src/services/ripgrep/index.ts @@ -1,8 +1,6 @@ import * as childProcess from "child_process" -import * as fs from "fs" import * as path from "path" import * as readline from "readline" -import { createRequire } from "module" import * as vscode from "vscode" @@ -58,6 +56,12 @@ const binName = isWindows ? "rg.exe" : "rg" // bin/-/ rather than directly in bin/. const ripgrepUniversalBinDir = `bin/${process.platform}-${process.arch}` +// @vscode/ripgrep >=1.18 ships the binary in a platform-specific optional +// package (e.g. @vscode/ripgrep-win32-x64). Matches the wrapper's own arch +// selection: process.env.npm_config_arch || process.arch. +const platformPkgArch = process.env.npm_config_arch || process.arch +const ripgrepPlatformPkg = `@vscode/ripgrep-${process.platform}-${platformPkgArch}` + interface SearchFileResult { file: string searchResults: SearchResult[] @@ -103,34 +107,18 @@ export function ripgrepCandidatePaths(vscodeAppRoot: string): readonly string[] `node_modules.asar.unpacked/@vscode/ripgrep-universal/${ripgrepUniversalBinDir}`, binName, ), + // @vscode/ripgrep >=1.18 (VS Code 1.130+): binary lives in a platform-specific optional package. + path.join(vscodeAppRoot, `node_modules/${ripgrepPlatformPkg}/bin`, binName), + path.join(vscodeAppRoot, `node_modules.asar.unpacked/${ripgrepPlatformPkg}/bin`, binName), ] } -/** - * Resolves ripgrep for @vscode/ripgrep >=1.18, which ships the binary inside a - * platform-specific optional package (e.g. @vscode/ripgrep-win32-x64) rather - * than directly in @vscode/ripgrep/bin/. VS Code 1.130+ uses this layout. - */ -export function resolvePlatformRipgrepPath(vscodeAppRoot: string): string | undefined { - try { - const wrapperManifest = path.join(vscodeAppRoot, "node_modules", "@vscode", "ripgrep", "package.json") - if (!fs.existsSync(wrapperManifest)) return undefined - const requireFromApp = createRequire(path.join(vscodeAppRoot, "package.json")) - const wrapperEntry = requireFromApp.resolve("@vscode/ripgrep") - const requireFromWrapper = createRequire(wrapperEntry) - const arch = process.env.npm_config_arch || process.arch - return requireFromWrapper.resolve(`@vscode/ripgrep-${process.platform}-${arch}/bin/${binName}`) - } catch { - return undefined - } -} - /** * Get the path to the ripgrep binary shipped inside the VS Code installation. * - * Checks the long-standing @vscode/ripgrep and @vscode/ripgrep-universal static - * layouts first, then falls back to the @vscode/ripgrep >=1.18 platform-package - * layout used by VS Code 1.130+ (see microsoft/vscode#252063). + * Probes all known layouts: classic @vscode/ripgrep, @vscode/ripgrep-universal + * (VS Code Insiders staged-install), and the @vscode/ripgrep >=1.18 + * platform-package layout used by VS Code 1.130+. * * Returns `undefined` when ripgrep cannot be located. */ @@ -138,10 +126,6 @@ export async function getBinPath(vscodeAppRoot: string): Promise Date: Sun, 26 Jul 2026 23:33:02 +0000 Subject: [PATCH 5/5] fix(ripgrep): evaluate npm_config_arch at call time in candidate paths --- .../__tests__/getEnvironmentDetails.spec.ts | 12 ++++++++++-- src/services/ripgrep/index.ts | 13 +++++-------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/core/environment/__tests__/getEnvironmentDetails.spec.ts b/src/core/environment/__tests__/getEnvironmentDetails.spec.ts index 04bbfdd4df..df47e83c21 100644 --- a/src/core/environment/__tests__/getEnvironmentDetails.spec.ts +++ b/src/core/environment/__tests__/getEnvironmentDetails.spec.ts @@ -451,9 +451,17 @@ describe("getEnvironmentDetails", () => { // When ripgrep cannot be found (e.g. @vscode/ripgrep >=1.18 platform layout on // Windows), listFiles throws "Could not find ripgrep binary". getEnvironmentDetails // must not propagate this — the task should proceed to the API call, not hang at 0%. - it("should not throw when listFiles rejects with ripgrep not found", async () => { + it("should degrade gracefully when listFiles rejects with an Error", async () => { ;(listFiles as Mock).mockRejectedValue(new Error("Could not find ripgrep binary")) - await expect(getEnvironmentDetails(mockCline as Task, true)).resolves.not.toThrow() + const result = await getEnvironmentDetails(mockCline as Task, true) + expect(result).toContain("File listing unavailable: Could not find ripgrep binary") + }) + + it("should degrade gracefully when listFiles rejects with a non-Error value", async () => { + ;(listFiles as Mock).mockRejectedValue("unexpected string rejection") + + const result = await getEnvironmentDetails(mockCline as Task, true) + expect(result).toContain("File listing unavailable: unexpected string rejection") }) }) diff --git a/src/services/ripgrep/index.ts b/src/services/ripgrep/index.ts index 79e0335a4f..0d4a25056b 100644 --- a/src/services/ripgrep/index.ts +++ b/src/services/ripgrep/index.ts @@ -56,12 +56,6 @@ const binName = isWindows ? "rg.exe" : "rg" // bin/-/ rather than directly in bin/. const ripgrepUniversalBinDir = `bin/${process.platform}-${process.arch}` -// @vscode/ripgrep >=1.18 ships the binary in a platform-specific optional -// package (e.g. @vscode/ripgrep-win32-x64). Matches the wrapper's own arch -// selection: process.env.npm_config_arch || process.arch. -const platformPkgArch = process.env.npm_config_arch || process.arch -const ripgrepPlatformPkg = `@vscode/ripgrep-${process.platform}-${platformPkgArch}` - interface SearchFileResult { file: string searchResults: SearchResult[] @@ -96,6 +90,9 @@ export function truncateLine(line: string, maxLength: number = MAX_LINE_LENGTH): * resolution) and the diagnostic command (existence report for all paths). */ export function ripgrepCandidatePaths(vscodeAppRoot: string): readonly string[] { + // Read at call time so process.env.npm_config_arch overrides take effect, + // matching @vscode/ripgrep's own arch selection logic. + const platformPkg = `@vscode/ripgrep-${process.platform}-${process.env.npm_config_arch || process.arch}` return [ path.join(vscodeAppRoot, "node_modules/@vscode/ripgrep/bin/", binName), path.join(vscodeAppRoot, "node_modules/vscode-ripgrep/bin", binName), @@ -108,8 +105,8 @@ export function ripgrepCandidatePaths(vscodeAppRoot: string): readonly string[] binName, ), // @vscode/ripgrep >=1.18 (VS Code 1.130+): binary lives in a platform-specific optional package. - path.join(vscodeAppRoot, `node_modules/${ripgrepPlatformPkg}/bin`, binName), - path.join(vscodeAppRoot, `node_modules.asar.unpacked/${ripgrepPlatformPkg}/bin`, binName), + path.join(vscodeAppRoot, `node_modules/${platformPkg}/bin`, binName), + path.join(vscodeAppRoot, `node_modules.asar.unpacked/${platformPkg}/bin`, binName), ] }