From 5fb9a0b11040feaec4dc4928bf699327bd0b33cd Mon Sep 17 00:00:00 2001 From: "robopipi[bot]" <336283572+robopipi[bot]@users.noreply.github.com> Date: Tue, 27 Oct 2026 09:17:54 +0100 Subject: [PATCH] fix(coding-agent): skip npm audit and fund output when installing pi packages (#11469) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `pi install`, `pi remove` and `pi update --extensions` run npm without `--no-audit`, so npm prints its "N vulnerabilities … run `npm audit fix`" advice after every install. pi gives users no way to clear that warning, and following it causes harm. In `~/.pi/agent/npm`, `npm audit fix` runs without `--legacy-peer-deps`, so npm installs the host-provided `@earendil-works/pi-*` peers there: a second copy of pi. The comment above `getNpmInstallArgs` warns against exactly that. The change adds `--no-audit --no-fund` wherever pi already passes `--legacy-peer-deps` to npm: - `getNpmInstallArgs` (npm package installs and updates) - `getGitDependencyInstallArgs` (dependency installs for git packages) - npm uninstall pi's managed self-update (`runManagedNpmCi`) already passes both flags. bun and pnpm arguments are unchanged. I did not edit `CHANGELOG.md`, per CONTRIBUTING.md. **Testing** - Updated the expected npm argument arrays in `test/package-manager.test.ts`. With the source change reverted, 11 tests fail; with it, all 132 pass. - `npm run check` passes. - Ran the built CLI by hand with `PI_CODING_AGENT_DIR=/tmp/pa pi install npm:lodash@4.17.20`: - Before the change, the output ended with npm's "To address all issues, run: npm audit fix" block. - After, it shows only "added 1 package" and "Installed npm:lodash@4.17.20". Fixes #11409 Requested by @badlogic Co-authored-by: triagent --- .../coding-agent/src/core/package-manager.ts | 7 +- .../coding-agent/test/package-manager.test.ts | 82 ++++++++++++++----- 2 files changed, 67 insertions(+), 22 deletions(-) diff --git a/packages/coding-agent/src/core/package-manager.ts b/packages/coding-agent/src/core/package-manager.ts index 2535b1f93..9b7a734d4 100644 --- a/packages/coding-agent/src/core/package-manager.ts +++ b/packages/coding-agent/src/core/package-manager.ts @@ -1858,7 +1858,7 @@ export class DefaultPackageManager implements PackageManager { "--config.strict-dep-builds=false", ]; case "npm": - return ["install", "--omit=dev", "--legacy-peer-deps"]; + return ["install", "--omit=dev", "--legacy-peer-deps", "--no-audit", "--no-fund"]; default: return ["install"]; } @@ -1891,7 +1891,8 @@ export class DefaultPackageManager implements PackageManager { "--config.strict-dep-builds=false", ]; } - return ["install", ...specs, "--prefix", installRoot, "--legacy-peer-deps"]; + // npm audit fix would run without --legacy-peer-deps and install pi peers, so skip audit advice. + return ["install", ...specs, "--prefix", installRoot, "--legacy-peer-deps", "--no-audit", "--no-fund"]; } private async installNpm(source: NpmSource, scope: SourceScope, temporary: boolean): Promise { @@ -1914,7 +1915,7 @@ export class DefaultPackageManager implements PackageManager { } const args = ["uninstall", source.name, "--prefix", installRoot]; if (packageManagerName !== "pnpm") { - args.push("--legacy-peer-deps"); + args.push("--legacy-peer-deps", "--no-audit", "--no-fund"); } await this.runNpmCommand(args); } diff --git a/packages/coding-agent/test/package-manager.test.ts b/packages/coding-agent/test/package-manager.test.ts index f8446b657..38d93b232 100644 --- a/packages/coding-agent/test/package-manager.test.ts +++ b/packages/coding-agent/test/package-manager.test.ts @@ -756,6 +756,8 @@ Content`, "--prefix", realpathSync(join(agentDir, "npm")), "--legacy-peer-deps", + "--no-audit", + "--no-fund", ], undefined, ); @@ -769,7 +771,15 @@ Content`, expect(runCommandSpy).toHaveBeenCalledWith( "npm", - ["uninstall", "@scope/pkg", "--prefix", realpathSync(join(agentDir, "npm")), "--legacy-peer-deps"], + [ + "uninstall", + "@scope/pkg", + "--prefix", + realpathSync(join(agentDir, "npm")), + "--legacy-peer-deps", + "--no-audit", + "--no-fund", + ], undefined, ); }); @@ -793,12 +803,12 @@ Content`, expect(runCommandSpy).toHaveBeenCalledWith( "npm", - ["install", "@scope/pkg", "--prefix", realNpmRoot, "--legacy-peer-deps"], + ["install", "@scope/pkg", "--prefix", realNpmRoot, "--legacy-peer-deps", "--no-audit", "--no-fund"], undefined, ); expect(runCommandSpy).toHaveBeenCalledWith( "npm", - ["uninstall", "@scope/pkg", "--prefix", realNpmRoot, "--legacy-peer-deps"], + ["uninstall", "@scope/pkg", "--prefix", realNpmRoot, "--legacy-peer-deps", "--no-audit", "--no-fund"], undefined, ); }); @@ -849,9 +859,13 @@ Content`, await packageManager.install(source); - expect(runCommandSpy).toHaveBeenCalledWith("npm", ["install", "--omit=dev", "--legacy-peer-deps"], { - cwd: targetDir, - }); + expect(runCommandSpy).toHaveBeenCalledWith( + "npm", + ["install", "--omit=dev", "--legacy-peer-deps", "--no-audit", "--no-fund"], + { + cwd: targetDir, + }, + ); }); it("should remove a newly created checkout when git clone fails", async () => { @@ -915,9 +929,13 @@ Content`, cwd: targetDir, }); expect(runCommandSpy).toHaveBeenCalledWith("git", ["clean", "-fdx"], { cwd: targetDir }); - expect(runCommandSpy).toHaveBeenCalledWith("npm", ["install", "--omit=dev", "--legacy-peer-deps"], { - cwd: targetDir, - }); + expect(runCommandSpy).toHaveBeenCalledWith( + "npm", + ["install", "--omit=dev", "--legacy-peer-deps", "--no-audit", "--no-fund"], + { + cwd: targetDir, + }, + ); }); it("should reconcile an existing git checkout to its update target when installing without a ref", async () => { @@ -1066,9 +1084,13 @@ Content`, await packageManager.update(source); - expect(runCommandSpy).toHaveBeenCalledWith("npm", ["install", "--omit=dev", "--legacy-peer-deps"], { - cwd: targetDir, - }); + expect(runCommandSpy).toHaveBeenCalledWith( + "npm", + ["install", "--omit=dev", "--legacy-peer-deps", "--no-audit", "--no-fund"], + { + cwd: targetDir, + }, + ); }); it("should repair missing git package dependencies when the checkout is already current", async () => { @@ -1093,9 +1115,13 @@ Content`, await packageManager.update(source); - expect(runCommandSpy).toHaveBeenCalledWith("npm", ["install", "--omit=dev", "--legacy-peer-deps"], { - cwd: targetDir, - }); + expect(runCommandSpy).toHaveBeenCalledWith( + "npm", + ["install", "--omit=dev", "--legacy-peer-deps", "--no-audit", "--no-fund"], + { + cwd: targetDir, + }, + ); expect(runCommandSpy).not.toHaveBeenCalledWith("git", ["clean", "-fdx"], { cwd: targetDir }); }); @@ -1127,9 +1153,13 @@ Content`, await expect(packageManager.update(source)).rejects.toThrow("simulated clean failure"); - expect(runCommandSpy).toHaveBeenCalledWith("npm", ["install", "--omit=dev", "--legacy-peer-deps"], { - cwd: targetDir, - }); + expect(runCommandSpy).toHaveBeenCalledWith( + "npm", + ["install", "--omit=dev", "--legacy-peer-deps", "--no-audit", "--no-fund"], + { + cwd: targetDir, + }, + ); }); it("should disable peer installation through wrapped pnpm when updating git dependencies", async () => { @@ -2506,7 +2536,15 @@ export default function(api) { api.registerTool({ name: "test", description: "te ); expect(runCommandSpy).toHaveBeenCalledWith( "npm", - ["install", "example@^1.0.0", "--prefix", realpathSync(join(tempDir, ".pi", "npm")), "--legacy-peer-deps"], + [ + "install", + "example@^1.0.0", + "--prefix", + realpathSync(join(tempDir, ".pi", "npm")), + "--legacy-peer-deps", + "--no-audit", + "--no-fund", + ], undefined, ); }); @@ -2572,6 +2610,8 @@ export default function(api) { api.registerTool({ name: "test", description: "te "--prefix", realpathSync(join(agentDir, "npm")), "--legacy-peer-deps", + "--no-audit", + "--no-fund", ]); mkdirSync(managedPath, { recursive: true }); writeFileSync( @@ -2689,6 +2729,8 @@ export default function(api) { api.registerTool({ name: "test", description: "te "--prefix", realpathSync(join(agentDir, "npm")), "--legacy-peer-deps", + "--no-audit", + "--no-fund", ], undefined, ); @@ -2702,6 +2744,8 @@ export default function(api) { api.registerTool({ name: "test", description: "te "--prefix", realpathSync(join(tempDir, ".pi", "npm")), "--legacy-peer-deps", + "--no-audit", + "--no-fund", ], undefined, );