From 79ced03bec143814947d77aed4bd79dee2848017 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Padez?= Date: Wed, 19 Aug 2026 09:52:01 +0000 Subject: [PATCH] put ~/.local/bin on the agent's PATH MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A plugin may declare a dependency no package manager carries — tea, Gitea's CLI, is the first — and installRelease puts it in ~/.local/bin: no root, and nothing downloaded from the internet sitting beside what the distribution installed. But PM2 hands the platform a login-less PATH (/usr/local/sbin:...:/snap/bin), so the agent it spawns inherited one too, and the binary the platform had just installed was invisible to the agent it was installed FOR. Which is exactly what happened on the first real test: gitea's affordance told the agent to use `tea`, the agent tried, and got `command not found` for something sitting in the owner's home. The affordance worked perfectly and the advice it gave was impossible to follow. Appended rather than prepended, so a system binary of the same name still wins — `identify` in os-deps is what settles which one is really wanted. Co-Authored-By: Claude Opus 5 --- src/servers/sidecar/claude/claude-manager.ts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/servers/sidecar/claude/claude-manager.ts b/src/servers/sidecar/claude/claude-manager.ts index a45ba4b3..f44cb25c 100644 --- a/src/servers/sidecar/claude/claude-manager.ts +++ b/src/servers/sidecar/claude/claude-manager.ts @@ -66,6 +66,23 @@ const NESTED_SESSION_ENV = [ function envWithoutParentSession(): Record { const out: Record = { ...process.env } as Record; for (const name of NESTED_SESSION_ENV) delete out[name]; + + // `~/.local/bin` on PATH, because that is where the platform installs a plugin's release binaries. + // + // A plugin may declare a dependency no package manager carries — `tea`, Gitea's CLI, is the first — + // and `installRelease` puts it there: no root, and nothing downloaded from the internet sitting beside + // what the distribution installed. But PM2 hands this process a login-less PATH + // (`/usr/local/sbin:…:/snap/bin`), so the agent it spawns inherits one too, and the binary the platform + // just installed is invisible to the agent it was installed FOR. + // + // Which is exactly what happened: gitea's affordance told the agent to use `tea`, the agent tried, and + // got `command not found` for something sitting in the owner's home. + // + // Appended rather than prepended: a system binary of the same name should still win, and `identify` in + // os-deps is what settles which one is really wanted. + const localBin = join(homedir(), '.local', 'bin'); + if (out.PATH && !out.PATH.split(':').includes(localBin)) out.PATH = `${out.PATH}:${localBin}`; + return out; }