fix: preserve shell PATH for managed OpenCode

Restores shell-first PATH resolution for desktop-launched OpenCode
Keeps app PATH entries as fallback
Adds coverage for managed OpenCode PATH merging
This commit is contained in:
Bohdan Triapitsyn
2026-04-28 01:19:12 +03:00
parent 6f2ffdb2d6
commit ccab4f49df
3 changed files with 14 additions and 11 deletions
@@ -2,7 +2,7 @@ import { spawnSync } from 'node:child_process';
import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import { pathLooksUserConfigured, mergePathValues } from './path-utils.js';
import { mergePathValues } from './path-utils.js';
export const createOpenCodeEnvRuntime = (deps) => {
const {
@@ -183,10 +183,11 @@ export const createOpenCodeEnvRuntime = (deps) => {
const currentPath = process.env.PATH || '';
const shellPath = snapshot.PATH || '';
const home = os.homedir();
if (!pathLooksUserConfigured(currentPath, home, path.delimiter) && shellPath) {
process.env.PATH = mergePathValues(shellPath, currentPath, path.delimiter);
if (!shellPath) {
return;
}
process.env.PATH = mergePathValues(shellPath, currentPath, path.delimiter);
};
const isWslExecutableValue = (value) => {
@@ -76,11 +76,6 @@ export const createServerUtilsRuntime = (dependencies) => {
const buildManagedOpenCodePath = () => {
const currentPath = process.env.PATH || '';
const loginShellPath = getLoginShellPath();
const home = os.homedir();
if (pathLooksUserConfigured(currentPath, home, path.delimiter)) {
return currentPath;
}
return mergePathValues(loginShellPath || '', currentPath, path.delimiter);
};
@@ -32,7 +32,7 @@ const createRuntime = (loginShellPath) => createServerUtilsRuntime({
});
describe('server utils runtime', () => {
it('keeps managed OpenCode PATH literal when process PATH is user-configured', () => {
it('prefers shell PATH for managed OpenCode before appending process-only entries', () => {
const home = os.homedir();
const currentPath = [
path.join(home, '.opencode', 'bin'),
@@ -51,7 +51,14 @@ describe('server utils runtime', () => {
path.join(home, '.cargo', 'bin'),
].join(path.delimiter));
expect(runtime.buildManagedOpenCodePath()).toBe(currentPath);
expect(runtime.buildManagedOpenCodePath()).toBe([
path.join(home, '.opencode', 'bin'),
path.join(home, '.bun', 'bin'),
'/opt/homebrew/bin',
'/usr/bin',
path.join(home, '.cargo', 'bin'),
path.join(home, 'Library', 'pnpm'),
].join(path.delimiter));
});
it('uses login shell PATH for managed OpenCode when process PATH is minimal', () => {