From e964db95b97e1128c3fe4f45e0a56317fdca1274 Mon Sep 17 00:00:00 2001 From: "Robby (robbyczgw-cla)" Date: Fri, 23 Jan 2026 13:39:50 +0000 Subject: [PATCH] fix(linux): add user bin directories to systemd service PATH MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #1503 On Linux, the systemd service PATH was hardcoded to only include system directories (/usr/local/bin, /usr/bin, /bin), causing binaries installed via npm global with custom prefix or node version managers to not be found. This adds common Linux user bin directories to the PATH: - ~/.local/bin (XDG standard, pip, etc.) - ~/.npm-global/bin (npm custom prefix) - ~/bin (user's personal bin) - Node version manager paths (nvm, fnm, volta, asdf) - ~/.local/share/pnpm (pnpm global) - ~/.bun/bin (Bun) User directories are added before system directories so user-installed binaries take precedence. 🤖 AI-assisted (Claude Opus 4.5 via Clawdbot) 📋 Testing: Existing unit tests pass (7/7) --- src/daemon/service-env.ts | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/src/daemon/service-env.ts b/src/daemon/service-env.ts index 8851cdb59..4fe78800c 100644 --- a/src/daemon/service-env.ts +++ b/src/daemon/service-env.ts @@ -17,6 +17,7 @@ import { export type MinimalServicePathOptions = { platform?: NodeJS.Platform; extraDirs?: string[]; + home?: string; }; type BuildServicePathOptions = MinimalServicePathOptions & { @@ -33,6 +34,31 @@ function resolveSystemPathDirs(platform: NodeJS.Platform): string[] { return []; } +/** + * Resolve common user bin directories for Linux. + * These are paths where npm global installs and node version managers typically place binaries. + */ +function resolveLinuxUserBinDirs(home: string | undefined): string[] { + if (!home) return []; + + const dirs: string[] = []; + + // Common user bin directories + dirs.push(`${home}/.local/bin`); // XDG standard, pip, etc. + dirs.push(`${home}/.npm-global/bin`); // npm custom prefix (recommended for non-root) + dirs.push(`${home}/bin`); // User's personal bin + + // Node version managers + dirs.push(`${home}/.nvm/current/bin`); // nvm with current symlink + dirs.push(`${home}/.fnm/current/bin`); // fnm + dirs.push(`${home}/.volta/bin`); // Volta + dirs.push(`${home}/.asdf/shims`); // asdf + dirs.push(`${home}/.local/share/pnpm`); // pnpm global bin + dirs.push(`${home}/.bun/bin`); // Bun + + return dirs; +} + export function getMinimalServicePathParts(options: MinimalServicePathOptions = {}): string[] { const platform = options.platform ?? process.platform; if (platform === "win32") return []; @@ -41,12 +67,17 @@ export function getMinimalServicePathParts(options: MinimalServicePathOptions = const extraDirs = options.extraDirs ?? []; const systemDirs = resolveSystemPathDirs(platform); + // Add Linux user bin directories (npm global, nvm, fnm, volta, etc.) + const linuxUserDirs = platform === "linux" ? resolveLinuxUserBinDirs(options.home) : []; + const add = (dir: string) => { if (!dir) return; if (!parts.includes(dir)) parts.push(dir); }; for (const dir of extraDirs) add(dir); + // User dirs first so user-installed binaries take precedence + for (const dir of linuxUserDirs) add(dir); for (const dir of systemDirs) add(dir); return parts; @@ -59,7 +90,10 @@ export function buildMinimalServicePath(options: BuildServicePathOptions = {}): return env.PATH ?? ""; } - return getMinimalServicePathParts(options).join(path.delimiter); + return getMinimalServicePathParts({ + ...options, + home: options.home ?? env.HOME, + }).join(path.delimiter); } export function buildServiceEnvironment(params: {