From 43819dcdac632f21e85a7147c6bfa5d1d6134741 Mon Sep 17 00:00:00 2001 From: Colton Padden Date: Wed, 19 Aug 2026 11:19:38 -0400 Subject: [PATCH 1/2] perf(eve): speed up fresh npm init Signed-off-by: Colton Padden --- .changeset/quick-npm-init.md | 5 ++++ .../src/cli/commands/init.integration.test.ts | 26 +++++++++++++++++-- packages/eve/src/cli/commands/init.ts | 7 +++++ packages/eve/src/setup/primitives/pm/npm.ts | 1 + packages/eve/src/setup/primitives/pm/types.ts | 2 ++ .../eve/src/setup/primitives/run-pnpm.test.ts | 16 ++++++++++++ 6 files changed, 55 insertions(+), 2 deletions(-) create mode 100644 .changeset/quick-npm-init.md diff --git a/.changeset/quick-npm-init.md b/.changeset/quick-npm-init.md new file mode 100644 index 0000000000..34d851e2e3 --- /dev/null +++ b/.changeset/quick-npm-init.md @@ -0,0 +1,5 @@ +--- +"eve": patch +--- + +Speed up fresh standalone npm scaffolds by skipping npm's resolution of unused optional peer dependencies. Existing projects and ancestor workspaces retain normal peer dependency resolution. diff --git a/packages/eve/src/cli/commands/init.integration.test.ts b/packages/eve/src/cli/commands/init.integration.test.ts index e308e43efa..feda2110b8 100644 --- a/packages/eve/src/cli/commands/init.integration.test.ts +++ b/packages/eve/src/cli/commands/init.integration.test.ts @@ -706,6 +706,28 @@ describe("runInitCommand", () => { }, ); + it("keeps npm peer resolution for a fresh ancestor workspace member", async () => { + const workspaceRoot = await mkdtemp(join(tmpdir(), "eve-init-npm-workspace-member-")); + const appsDirectory = join(workspaceRoot, "apps"); + await mkdir(appsDirectory, { recursive: true }); + await writeFile( + join(workspaceRoot, "package.json"), + `${JSON.stringify({ private: true, workspaces: ["apps/*"] }, null, 2)}\n`, + "utf8", + ); + await writeFile(join(workspaceRoot, "package-lock.json"), "", "utf8"); + const output = logger(); + const deps = dependencies(); + + await runInitCommand(output, appsDirectory, "my-agent", {}, deps); + + expect(deps.runPackageManagerInstall).toHaveBeenCalledWith( + "npm", + join(appsDirectory, "my-agent"), + expect.objectContaining({ skipPeerDependencyResolution: false }), + ); + }); + it("adds Web Chat to an npm-owned fresh scaffold without pnpm configuration", async () => { const parentDirectory = await mkdtemp(join(tmpdir(), "eve-init-agent-web-npm-")); const output = logger(); @@ -720,7 +742,7 @@ describe("runInitCommand", () => { expect(deps.runPackageManagerInstall).toHaveBeenCalledWith( "npm", projectPath, - expect.anything(), + expect.objectContaining({ skipPeerDependencyResolution: true }), ); expect(deps.spawnPackageManager).toHaveBeenCalledWith("npm", projectPath, [ "exec", @@ -1019,7 +1041,7 @@ describe("runInitCommand", () => { expect(deps.runPackageManagerInstall).toHaveBeenCalledWith( kind, projectRoot, - expect.anything(), + expect.objectContaining({ skipPeerDependencyResolution: false }), ); expect(deps.spawnPackageManager).toHaveBeenCalledWith(kind, projectRoot, [...devArguments]); }, diff --git a/packages/eve/src/cli/commands/init.ts b/packages/eve/src/cli/commands/init.ts index 4c00a1cfbe..32a299fef3 100644 --- a/packages/eve/src/cli/commands/init.ts +++ b/packages/eve/src/cli/commands/init.ts @@ -485,6 +485,13 @@ async function runInitSteps(input: { // The scaffold pins versions younger than typical release-age cooldown // windows; gating them would fail every fresh bootstrap. bypassMinimumReleaseAge: true, + // npm otherwise resolves hundreds of unused optional peers exposed by + // Nitro's integration ecosystem. Keep normal peer validation when eve + // adds to an existing project or installs inside an ancestor workspace. + skipPeerDependencyResolution: + project.kind === "created" && + project.packageManager === "npm" && + !project.workspaceMember, progressDetails: process.stdout.isTTY === true && !debug, onOutput: (line) => { if (line.text.trim() !== "") { diff --git a/packages/eve/src/setup/primitives/pm/npm.ts b/packages/eve/src/setup/primitives/pm/npm.ts index c080cce3d7..6ed5699ef4 100644 --- a/packages/eve/src/setup/primitives/pm/npm.ts +++ b/packages/eve/src/setup/primitives/pm/npm.ts @@ -9,6 +9,7 @@ export const npmPackageManager = { installArguments: (options) => [ "install", ...(options.bypassMinimumReleaseAge === true ? ["--min-release-age=0"] : []), + ...(options.skipPeerDependencyResolution === true ? ["--legacy-peer-deps"] : []), ...(options.progressDetails === true ? ["--loglevel=silly"] : []), ], prepareArguments: (_projectRoot, args) => args, diff --git a/packages/eve/src/setup/primitives/pm/types.ts b/packages/eve/src/setup/primitives/pm/types.ts index 1769611f89..952766d58c 100644 --- a/packages/eve/src/setup/primitives/pm/types.ts +++ b/packages/eve/src/setup/primitives/pm/types.ts @@ -28,6 +28,8 @@ export interface PackageManagerInstallOptions { readonly bypassMinimumReleaseAge?: boolean; /** Resolves the project standalone even when an ancestor workspace exists. */ readonly ignoreWorkspace?: boolean; + /** Skips peer dependency resolution for an eve-owned standalone scaffold when supported. */ + readonly skipPeerDependencyResolution?: boolean; /** Requests verbose package-manager output for a live progress display. */ readonly progressDetails?: boolean; } diff --git a/packages/eve/src/setup/primitives/run-pnpm.test.ts b/packages/eve/src/setup/primitives/run-pnpm.test.ts index 95311463b5..7ab7d9ec52 100644 --- a/packages/eve/src/setup/primitives/run-pnpm.test.ts +++ b/packages/eve/src/setup/primitives/run-pnpm.test.ts @@ -169,6 +169,22 @@ describe("runPackageManagerInstall", () => { ); }); + test("skips npm peer dependency resolution when requested", async () => { + expect( + packageManagerInstallSucceeded( + await runPackageManagerInstall("npm", "/tmp/app", { + skipPeerDependencyResolution: true, + }), + ), + ).toBe(true); + + expect(mockedSpawn).toHaveBeenCalledWith( + "npm", + ["install", "--legacy-peer-deps"], + expect.objectContaining({ cwd: "/tmp/app" }), + ); + }); + test("requests npm output before registry operations complete", async () => { expect( packageManagerInstallSucceeded( From 3f83d28c227f1d0c62d2baf73037e281efbbb58c Mon Sep 17 00:00:00 2001 From: Colton Padden Date: Wed, 19 Aug 2026 11:31:45 -0400 Subject: [PATCH 2/2] test(eve): cover npm init install arguments Signed-off-by: Colton Padden --- packages/eve/src/cli/commands/init.ts | 8 +------- packages/eve/test/scenarios/eve-init.scenario.test.ts | 2 +- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/packages/eve/src/cli/commands/init.ts b/packages/eve/src/cli/commands/init.ts index 32a299fef3..f4f5b3045f 100644 --- a/packages/eve/src/cli/commands/init.ts +++ b/packages/eve/src/cli/commands/init.ts @@ -485,13 +485,7 @@ async function runInitSteps(input: { // The scaffold pins versions younger than typical release-age cooldown // windows; gating them would fail every fresh bootstrap. bypassMinimumReleaseAge: true, - // npm otherwise resolves hundreds of unused optional peers exposed by - // Nitro's integration ecosystem. Keep normal peer validation when eve - // adds to an existing project or installs inside an ancestor workspace. - skipPeerDependencyResolution: - project.kind === "created" && - project.packageManager === "npm" && - !project.workspaceMember, + skipPeerDependencyResolution: project.kind === "created" && !project.workspaceMember, progressDetails: process.stdout.isTTY === true && !debug, onOutput: (line) => { if (line.text.trim() !== "") { diff --git a/packages/eve/test/scenarios/eve-init.scenario.test.ts b/packages/eve/test/scenarios/eve-init.scenario.test.ts index ab0b92eba2..727d72dcb2 100644 --- a/packages/eve/test/scenarios/eve-init.scenario.test.ts +++ b/packages/eve/test/scenarios/eve-init.scenario.test.ts @@ -262,7 +262,7 @@ describe("eve init smoke", () => { await expect(pathExists(join(projectDir, "package-lock.json"))).resolves.toBe(true); expect(await fakeNpm.readCalls()).toEqual([ { - args: ["install", "--min-release-age=0"], + args: ["install", "--min-release-age=0", "--legacy-peer-deps"], cwd: canonicalProjectDir, }, {