fix(coding-agent): disable managed extension peer resolution

closes #4907
This commit is contained in:
Armin Ronacher
2026-05-23 14:15:00 +02:00
Unverified
parent 30f48feae7
commit 15f1dea8df
3 changed files with 57 additions and 9 deletions
+4
View File
@@ -2,6 +2,10 @@
## [Unreleased]
### Fixed
- Fixed managed npm extension updates to avoid package managers installing or resolving pi host packages as peer dependencies ([#4907](https://github.com/earendil-works/pi/issues/4907)).
## [0.75.5] - 2026-05-23
### New Features
@@ -1706,13 +1706,25 @@ export class DefaultPackageManager implements PackageManager {
private getNpmInstallArgs(specs: string[], installRoot: string): string[] {
const packageManagerName = this.getPackageManagerName();
// Extension packages run inside pi and resolve pi APIs through loader aliases/virtual modules.
// Disable peer dependency resolution for managed installs (npm's --legacy-peer-deps, and
// equivalent bun/pnpm settings) so package managers do not install or solve host-provided
// @earendil-works/pi-* peers. Stale auto-installed pi peers can otherwise block updates.
if (packageManagerName === "bun") {
return ["install", ...specs, "--cwd", installRoot];
return ["install", ...specs, "--cwd", installRoot, "--omit=peer"];
}
if (packageManagerName === "pnpm") {
return ["install", ...specs, "--prefix", installRoot, "--config.strict-dep-builds=false"];
return [
"install",
...specs,
"--prefix",
installRoot,
"--config.auto-install-peers=false",
"--config.strict-peer-dependencies=false",
"--config.strict-dep-builds=false",
];
}
return ["install", ...specs, "--prefix", installRoot];
return ["install", ...specs, "--prefix", installRoot, "--legacy-peer-deps"];
}
private async installNpm(source: NpmSource, scope: SourceScope, temporary: boolean): Promise<void> {
@@ -693,7 +693,17 @@ Content`,
expect(runCommandSpy).toHaveBeenCalledWith(
"mise",
["exec", "node@20", "--", "npm", "install", "@scope/pkg", "--prefix", join(agentDir, "npm")],
[
"exec",
"node@20",
"--",
"npm",
"install",
"@scope/pkg",
"--prefix",
join(agentDir, "npm"),
"--legacy-peer-deps",
],
undefined,
);
});
@@ -714,7 +724,7 @@ Content`,
expect(runCommandSpy).toHaveBeenCalledWith(
"mise",
["exec", "bun@1", "--", "bun", "install", "@scope/pkg", "--cwd", join(agentDir, "npm")],
["exec", "bun@1", "--", "bun", "install", "@scope/pkg", "--cwd", join(agentDir, "npm"), "--omit=peer"],
undefined,
);
});
@@ -953,6 +963,8 @@ Content`,
"pnpm-pkg",
"--prefix",
join(agentDir, "npm"),
"--config.auto-install-peers=false",
"--config.strict-peer-dependencies=false",
"--config.strict-dep-builds=false",
]);
mkdirSync(join(packagePath, "extensions"), { recursive: true });
@@ -2005,7 +2017,7 @@ export default function(api) { api.registerTool({ name: "test", description: "te
);
expect(runCommandSpy).toHaveBeenCalledWith(
"npm",
["install", "example@latest", "--prefix", join(tempDir, ".pi", "npm")],
["install", "example@latest", "--prefix", join(tempDir, ".pi", "npm"), "--legacy-peer-deps"],
undefined,
);
});
@@ -2044,7 +2056,13 @@ export default function(api) { api.registerTool({ name: "test", description: "te
.mockImplementation(async (...callArgs: unknown[]) => {
const [command, args] = callArgs as [string, string[]];
expect(command).toBe("npm");
expect(args).toEqual(["install", "legacy-pkg@latest", "--prefix", join(agentDir, "npm")]);
expect(args).toEqual([
"install",
"legacy-pkg@latest",
"--prefix",
join(agentDir, "npm"),
"--legacy-peer-deps",
]);
mkdirSync(managedPath, { recursive: true });
writeFileSync(
join(managedPath, "package.json"),
@@ -2154,13 +2172,27 @@ export default function(api) { api.registerTool({ name: "test", description: "te
expect(runCommandSpy).toHaveBeenNthCalledWith(
1,
"npm",
["install", "user-old@latest", "user-unknown@latest", "--prefix", join(agentDir, "npm")],
[
"install",
"user-old@latest",
"user-unknown@latest",
"--prefix",
join(agentDir, "npm"),
"--legacy-peer-deps",
],
undefined,
);
expect(runCommandSpy).toHaveBeenNthCalledWith(
2,
"npm",
["install", "project-old@latest", "project-missing@latest", "--prefix", join(tempDir, ".pi", "npm")],
[
"install",
"project-old@latest",
"project-missing@latest",
"--prefix",
join(tempDir, ".pi", "npm"),
"--legacy-peer-deps",
],
undefined,
);
expect(updateGitSpy).toHaveBeenCalledTimes(4);