diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index e772b3b0..0440adcf 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -6,6 +6,7 @@ - Fixed Windows npm self-updates to move loaded native dependency packages out of the active install before reinstalling pi ([#4157](https://github.com/earendil-works/pi/issues/4157)). - Fixed `pi update --self` detection for pnpm v11 global installs whose package path resolves through the pnpm store ([#4647](https://github.com/earendil-works/pi/issues/4647)). +- Fixed Windows pnpm self-updates to resolve pnpm command shims and run through pnpm instead of requiring manual updates ([#4157](https://github.com/earendil-works/pi/issues/4157)). ## [0.75.1] - 2026-05-18 diff --git a/packages/coding-agent/src/package-manager-cli.ts b/packages/coding-agent/src/package-manager-cli.ts index a3ef1e21..d1a51e3b 100644 --- a/packages/coding-agent/src/package-manager-cli.ts +++ b/packages/coding-agent/src/package-manager-cli.ts @@ -506,8 +506,10 @@ export async function handlePackageCommand(args: string[]): Promise { return true; } const installMethod = detectInstallMethod(); - if (process.platform === "win32" && installMethod !== "npm") { - console.error(chalk.red(`${APP_NAME} self-update on Windows is only supported for npm installs.`)); + if (process.platform === "win32" && installMethod !== "npm" && installMethod !== "pnpm") { + console.error( + chalk.red(`${APP_NAME} self-update on Windows is only supported for npm and pnpm installs.`), + ); console.error(chalk.dim(`Detected install method: ${installMethod}. Update ${APP_NAME} manually.`)); process.exitCode = 1; return true; @@ -523,7 +525,9 @@ export async function handlePackageCommand(args: string[]): Promise { return true; } try { - prepareWindowsNpmSelfUpdate(); + if (installMethod === "npm") { + prepareWindowsNpmSelfUpdate(); + } await runSelfUpdate(selfUpdateCommand); } catch (error: unknown) { const message = error instanceof Error ? error.message : "Unknown package command error"; diff --git a/packages/coding-agent/src/utils/child-process.ts b/packages/coding-agent/src/utils/child-process.ts index 73fdf5af..57ebd3ae 100644 --- a/packages/coding-agent/src/utils/child-process.ts +++ b/packages/coding-agent/src/utils/child-process.ts @@ -3,9 +3,9 @@ import { existsSync, readFileSync } from "node:fs"; import { dirname, extname, join, resolve, sep } from "node:path"; const EXIT_STDIO_GRACE_MS = 100; -const WINDOWS_COMMAND_EXTENSIONS = ["", ".exe", ".cmd", ".bat"]; +const WINDOWS_COMMAND_EXTENSIONS = [".exe", ".cmd", ".bat", ""]; const WINDOWS_COMMAND_SHIM_RE = /\.(?:cmd|bat)$/i; -const NODE_SHIM_SCRIPT_RE = /(?:%~dp0|%dp0%|%basedir%)[^"'\r\n<>|&]*?\.(?:cjs|mjs|js)/i; +const NODE_SHIM_SCRIPT_RE = /(?:%~dp0|%dp0%|%basedir%)[^"'\r\n<>|&]*?\.(?:cjs|mjs|js)/gi; export interface ResolvedSpawnCommand { command: string; @@ -45,10 +45,12 @@ function expandShimPath(path: string, shimPath: string): string { } function findNodeShimScript(shimPath: string): string | undefined { - const match = readFileSync(shimPath, "utf-8").match(NODE_SHIM_SCRIPT_RE); - if (!match) return undefined; - const scriptPath = expandShimPath(match[0], shimPath); - return existsSync(scriptPath) ? scriptPath : undefined; + const matches = [...readFileSync(shimPath, "utf-8").matchAll(NODE_SHIM_SCRIPT_RE)]; + for (const match of matches.reverse()) { + const scriptPath = expandShimPath(match[0], shimPath); + if (existsSync(scriptPath)) return scriptPath; + } + return undefined; } export function resolveSpawnCommand(