Identified difference in lifecycle script handling between pnpm and npm.
PlanAnalyzed dependencies with install scripts causing interactive picker in pnpm.
PlanExplained why npm does not show the picker due to running scripts automatically.
PlanIf I try to update Pi by running pnpm install --global @earendil-works/pi-coding-agent, I get an interactive picker in which I'm asked to select which of three packages I want to build. I don't get this picker if I run npm install --global @earendil-works/pi-coding-agent. Why is there a discrepancy?
The user is asking a question about a discrepancy in how pnpm install --global @earendil-works/pi-coding-agent and npm install --global @earendil-works/pi-coding-agent behave—specifically that pnpm shows an interactive picker while npm doesn't.
But wait—I'm inside the pi-mono repository at /Users/me/c/vendor/pi. This is the monorepo for Pi itself. The user is asking about installation behavior of the pnpm package. This discrepancy might be related to the package configuration in this repo.
Let me first check if there are issues or known configuration differences, and also inspect the package.json files, particularly the pi-coding-agent package, to see if multiple build scripts or bin entries might trigger pnpm's interactive behavior.
Actually, I need to think about what could cause pnpm to show an "interactive picker" asking which of three packages to build during global install. This sounds like pnpm's behavior when a package has multiple bin entries? Or perhaps preinstall/postinstall scripts? Or maybe pnpm is treating it as a monorepo and installing the workspace? Hmm.
Wait, "three packages I want to build" — this might be related to pnpm wanting to build native dependencies, or multiple peerDependencies / optional dependencies? Or maybe pnpm is prompting about bin names that conflict?
Actually, pnpm has a feature where if you install globally and there are multiple packages with the same bin name, it might ask. But the user says "three packages I want to build" which sounds more like a "postinstall" / "build" prompt for multiple workspace packages? That seems odd for a global install of a single package.
Maybe pnpm is resolving the package from a monorepo and wanting to build workspace packages?
Alternatively, maybe @earendil-works/pi-coding-agent has optionalDependencies on packages that need compilation, and pnpm asks to build them. But why three packages?
I should look at the package.json for pi-coding-agent and related packages to understand the bin entries, scripts, and native dependencies.
But first, let me remember: pnpm global install might show a prompt if there are multiple packages wanting to be "built" — specifically, pnpm asks when installing packages with native addons if you want to use a bundled or prebuilt binary. Or more likely, this is about pnpm install in a project (monorepo) context where three workspace packages have prepare or build scripts. But the user explicitly says pnpm install --global @earendil-works/pi-coding-agent vs npm install --global.
Wait — could this be about optionalDependencies or peerDependencies? Or about preinstall scripts?
Actually, in pnpm, when you install a package globally, if it has multiple sub-packages with "bin" entries? No, that doesn't make sense.
Let me search the web or maybe the issue is in the repository itself. The user is asking me while I'm in the repo. The issue might be documented in issues or known. But I should first check the package files.
Let me look for the pi-coding-agent package.json.
>ls packages/*/package.json | sort>cat packages/coding-agent/package.jsonLet me investigate this discrepancy. The user is seeing a picker when installing with pnpm but not with npm. This is likely due to how pnpm handles optionalDependencies or peer dependencies, or maybe something with overrides, or perhaps the package has a postinstall script in one of its dependencies.
Wait, looking at the package.json, I don't see a postinstall script. But there might be something in the dependencies. Let me check if any of the dependencies have postinstall scripts or if there's something with peer dependencies.
Actually, the more likely cause is optionalDependencies combined with pnpm's behavior. The package has @mariozechner/clipboard as an optional dependency. But that wouldn't cause a picker.
Let me think about this differently. There's a known pnpm behavior: when a package (or its dependencies) has a prepare or postinstall script that requires building, or when there are peer dependencies that need resolution, pnpm might show a picker.
But wait - the user says "three packages I want to build." This sounds very much like pnpm's behavior when it encounters packages with postinstall scripts that use node-gyp or native compilation, or perhaps pnpm asking about which workspace packages to build.
Actually, I think this might be related to pnpm's "ignored build scripts" feature or its interactive mode for selecting which packages with postinstall scripts to run.
Let me search for any dependencies that might have postinstall scripts or native dependencies. The package @silvia-odwyer/photon-node is a Rust/WASM package potentially requiring a postinstall. Let me check.
Also, let me look at the other packages in the monorepo to see if any have build scripts or postinstalls.
>for pkg in packages/*; do echo "=== $pkg ==="; cat "$pkg/package.json" | grep -E '"(postinstall|prepare|install|build|scripts)"' -A 20 | head -25; doneThe packages don't seem to have postinstall scripts that would cause this. Let me look at the dependencies more carefully. The coding-agent package depends on @earendil-works/pi-agent-core, @earendil-works/pi-ai, and @earendil-works/pi-tui. These are sibling packages in the monorepo.
Wait, let me look at optionalDependencies in coding-agent. It has @mariozechner/clipboard. Let me check if there's something in the workspace that might cause pnpm to pick up workspace packages.
Actually, the key difference between pnpm and npm here is likely about how pnpm resolves peer dependencies or optional dependencies, or perhaps there's a packageManager field or some workspace configuration.
Wait, let me search for anything related to "interactive" or "picker" or multi-package builds. The user says they get a picker asking which of three packages to build. This sounds like it might be related to @mariozechner/clipboard or some other native dependency that needs to be built from source.
Actually, wait. Let me look more carefully at the dependencies. Let me check if there's a package with binaries or native addons.
Or maybe the issue is that pnpm installs workspace packages when installing from the registry? No, that doesn't make sense.
Wait, maybe the issue is with @mariozechner/clipboard - let me check what that package looks like.
Actually, let me think about this differently. The user is running pnpm install --global @earendil-works/pi-coding-agent. pnpm has a behavior where if a package is part of a workspace and workspace packages are linked, it might show a picker. But this is for a global install...
Let me check if there's something in the package that causes pnpm to behave differently. Maybe there are multiple versions of something?
Actually, I think this might be about optionalDependencies that involve native modules, or specifically @mariozechner/clipboard. Let me search for clipboard and see what's happening.
Wait, actually I should look at this from a different angle. Let me check npm registry info or look for any life-cycle scripts that might trigger a build. The "three packages" mentioned might be related to @mariozechner/clipboard or some similar package that requires choosing a platform/build.
Let me look at the clipboard dependency first.
>cat packages/coding-agent/package.json | grep -A 3 "optionalDependencies"Let me look at the @mariozechner/clipboard package. Maybe it's the one causing issues because it has native bindings that need to be built, and pnpm is asking which of multiple versions or platforms to build. But the user says "three packages I want to build" which makes me think it might be related to the monorepo structure being detected by pnpm in a global install.
Wait, another thought: pnpm has a feature where if you install a package globally and it has peer dependencies, pnpm might prompt you to select which versions of the peer dependencies to install. But three packages?
Let me check what npm view @mariozechner/clipboard shows, or look at the package.json of clipboard.
Actually, wait. Let me think about this more carefully. The pi-coding-agent package is published to npm. When you do npm install -g, npm just downloads and installs it. When you do pnpm install -g, pnpm might be detecting something.
Is it possible that pnpm is seeing the bin name pi and asking about which package to build because there are conflicts? No, that's about the bin name, not packages to build.
Actually, pnpm has an interactive feature for selecting which packages should have their preinstall, install, postinstall scripts run, when --ignore-scripts is set or when pnpm is configured to ask. But the user didn't mention --ignore-scripts.
Hmm, let me look at the @mariozechner/clipboard package on the npm registry to see if it has multiple optional native dependencies.
>npm view @mariozechner/clipboard@0.3.6 2>/dev/null || npm view @mariozechner/clipboardThe clipboard package doesn't seem to have native dependencies itself. Let me think about this differently.
Actually, I wonder if the issue is related to the package.json overrides field in the pi-coding-agent package? overrides is an npm-specific field (originally from npm), but pnpm handles it differently. However, pnpm doesn't show an interactive picker for that.
Wait, let me look at pnpm's behavior. There is a known pnpm feature: when installing a package globally and the package has optionalDependencies with os/cpu conditions that cause multiple variants to be considered, or when there are packages with postinstall scripts and pnpm is in some interactive mode.
Actually, I recall now! pnpm has a specific behavior when installing a package globally: if the package has peerDependencies, pnpm might prompt the user to resolve them. But more specifically, pnpm has an interactive update/select feature.
Wait, could it be that pnpm install --global is actually running in the local workspace directory and pnpm is interpreting it as installing to the workspace? The user said "If I try to update Pi by running pnpm install --global @earendil-works/pi-coding-agent" — maybe they are running this from within a project that has a pnpm-workspace.yaml or something?
No, the user is asking about a general discrepancy, not specifically from within this repo.
Let me think about what could cause pnpm to show an interactive picker with three packages to build.
Oh wait! I bet this is because pnpm has a "catalog" or "workspace" feature where if you install a package that itself is part of a monorepo, pnpm might offer to build its workspace dependencies? No, that doesn't make sense for a published package.
Actually, maybe it's related to optionalDependencies and os/cpu constraints. Let me look at undici or other dependencies. But again, the user specifically says "three packages" — the pi-coding-agent package depends on exactly three sibling packages: @earendil-works/pi-agent-core, @earendil-works/pi-ai, and @earendil-works/pi-tui. That's three packages! Could the picker be about these three packages?
Why would pnpm ask to build these three packages? Maybe because pnpm sees that these dependencies have prepare or build scripts? But they don't have postinstall scripts, and published packages typically have prebuilt dist directories.
Wait, maybe the issue is with pnpm's --global flag when run inside a pnpm workspace directory. If you run pnpm install --global <pkg> inside a pnpm workspace, pnpm might get confused and show workspace packages? Hmm, unlikely.
Another thought: pnpm has onlyBuiltDependencies or ignoredBuiltDependencies in .npmrc. If a package has dependencies with postinstall scripts, pnpm might ask which ones to build. But npm just runs them all.
Actually, I think I found it! pnpm has a configuration where if packages have native bindings that need to be built (via node-gyp or similar), and there are multiple packages that need building, pnpm might show an interactive picker when running in TTY mode, asking which packages to build. But npm just builds them all (or fails silently with optional ones).
Wait, but the dependencies of pi-coding-agent don't seem to have native bindings. Let me check if any dependency might have a postinstall script.
Actually, @silvia-odwyer/photon-node is a Rust/WASM package. Let me check that.
>npm view @silvia-odwyer/photon-node@0.3.4 2>/dev/null | head -50 || npm view @silvia-odwyer/photon-nodeLet me look at this from a different angle. The pi-coding-agent package has a dependency on @earendil-works/pi-tui. Maybe @earendil-works/pi-tui has a postinstall or some binary that pnpm is asking about. Let me check the tui package for any scripts or native deps.
Aha! @earendil-works/pi-tui has koffi as an optionalDependency. koffi is a native FFI library for Node.js. Let me check if koffi has multiple packages or postinstall scripts that would cause pnpm to show a picker.
Also, let me check the agent package for any optional dependencies or similar things.
So pi-tui has koffi as an optional dependency. Let me check what koffi is and whether it causes pnpm to show a picker.
>npm view koffi@2.9.0 2>/dev/null | head -80 || npm view koffi | head -80Let me check if koffi has multiple prebuilt binaries or if it has a postinstall script that might cause pnpm to show a picker. Actually, koffi is a native module. Let me look at its package.json via npm view to see if it has scripts.
>npm view koffi@2.9.0 scriptsAha! koffi has an install script. That's a postinstall script that runs during installation. Now, pnpm has specific behavior regarding packages with install scripts. By default, pnpm does NOT run install scripts for packages unless they are explicitly allowlisted in onlyBuiltDependencies or if you confirm them interactively.
Actually, more precisely: pnpm has a security feature where it asks which packages with lifecycle scripts should be allowed to run. Starting from pnpm v9, pnpm does not run lifecycle scripts (like postinstall, install, etc.) by default and instead shows an interactive picker to let the user select which packages to build/run scripts for.
Wait, is that exactly what the user is seeing? The user says they get an interactive picker asking to select which of three packages they want to build. That sounds exactly like pnpm's "ignored build scripts" or "only built dependencies" feature where it shows a multi-select of packages that have postinstall/install scripts.
Let me verify: pnpm v9+ has a feature where it shows an interactive prompt for packages with build scripts (postinstall, install, etc.), asking the user to select which ones to allow. npm, on the other hand, runs all these scripts by default.
Now, the "three packages" the user sees — what are they? The pi-coding-agent dependency tree might have multiple packages with install scripts. Let me find out which packages in the full dependency tree have install or postinstall scripts. I should check if other packages like @mariozechner/clipboard or @silvia-odwyer/photon-node also have install scripts.
Actually, let me trace the dependency tree to see what packages have install scripts. But I might not have the full tree without installing. Let me use npm ls or look at the lockfile.
Actually, wait. The user is specifically asking about the discrepancy. The answer is likely: pnpm blocks lifecycle scripts by default for security and shows an interactive picker, while npm runs them automatically.
But let me confirm which three packages are involved. It might be:
koffi (from pi-tui's optionalDependencies)@mariozechner/clipboard (from pi-coding-agent's optionalDependencies) - does it have an install script?Let me check @mariozechner/clipboard for install scripts.
>npm view @mariozechner/clipboard@0.3.6 scriptsNo install script for clipboard. Let me check @silvia-odwyer/photon-node for install scripts.
>npm view @silvia-odwyer/photon-node@0.3.4 scriptsSo koffi definitely has an install script. Let me see if there are other packages in the dependency tree that have install/postinstall scripts. For instance, undici or openai or chalk or highlight.js etc. Let me search for common ones.
Actually, @earendil-works/pi-tui depends on marked and get-east-asian-width. Let me check if those have install scripts.
>npm view get-east-asian-width@1.3.0 scripts && npm view marked@15.0.12 scriptsLet me search for other packages in the full dependency tree of pi-coding-agent that have install/postinstall scripts. I should look at packages like @xterm/headless, @xterm/xterm, chalk, diff, glob, highlight.js, hosted-git-info, ignore, jiti, minimatch, proper-lockfile, typebox, undici, yaml, @anthropic-ai/sdk, @aws-sdk/client-bedrock-runtime, @google/genai, @mistralai/mistralai, openai, http-proxy-agent, https-proxy-agent, partial-json.
Actually, a better approach is to search the pnpm lockfile or use npm to list packages with install scripts in the tree. But since we're in the monorepo, let me check the pnpm-lock.yaml for packages that have scripts.
>grep -B2 -A2 "postinstall:\|install:\|preinstall:" pnpm-lock.yaml 2>/dev/null | head -60Let me look at how pnpm handles install scripts. Actually, pnpm lockfile format might use hasInstallScript: true instead of listing the scripts explicitly.
>grep "hasInstallScript" pnpm-lock.yaml 2>/dev/null | head -20Let me check the pnpm-lock.yaml format. Maybe it's a different structure.
>grep -i "install" pnpm-lock.yaml | head -20There's no pnpm-lock.yaml. Let me check if there's a package-lock.json or some other lockfile.
>ls *.lock* *.yaml 2>/dev/null | head -10Let me think about this differently. The user is asking about installing @earendil-works/pi-coding-agent globally with pnpm. pnpm has a specific behavior with install scripts.
From pnpm documentation:
onlyBuiltDependencies in package.json or pnpm.onlyBuiltDependencies in .npmrc, or via pnpm config.So the discrepancy is that pnpm blocks lifecycle scripts and shows an interactive picker, while npm runs them automatically.
But what are the "three packages"? We know koffi has an install script. What are the other two? Let me search for other packages in the dependency tree that have install scripts. Maybe undici or some other dependency has one?
Actually, let me check undici, openai, @anthropic-ai/sdk, etc.
>npm view undici@8.3.0 scriptsAha! undici has a prepare script. prepare runs on install. Wait, does prepare run when installing from npm? Yes, prepare runs on both npm publish and npm install (when installing from a git repo or local path). But for published packages installed from the registry, prepare does NOT run during npm install. The lifecycle scripts that run during registry install are preinstall, install, and postinstall. prepare does not run for registry installs.
Let me check if undici has install or postinstall scripts in its published version. The prepare script won't run for npm registry installs. The output from npm view undici@8.3.0 scripts shows prepare, but that's different.
Actually, let me check the exact version of undici that pi-coding-agent depends on. It's ^8.3.0. But the published tarball doesn't include the prepare script in a way that runs during registry install.
So back to koffi - it has an install script, which definitely runs during registry install.
Now let me check other dependencies for install or postinstall scripts. Maybe chalk, diff, glob, highlight.js, hosted-git-info, ignore, jiti, minimatch, proper-lockfile, typebox, yaml.
>npm view chalk@5.5.0 scripts && npm view diff@8.0.2 scripts && npm view glob@13.0.1 scripts && npm view highlight.js@10.7.3 scriptsglob has a prepare script (tshy), but prepare doesn't run for registry installs. It also doesn't have install or postinstall.
Let me check jiti, minimatch, proper-lockfile, typebox, and the other packages.
>npm view jiti@2.7.0 scripts && npm view minimatch@10.2.3 scripts && npm view proper-lockfile@4.1.2 scripts && npm view typebox@1.1.24 scriptsLet me check if there are any other packages with install/postinstall scripts. Maybe highlight.js doesn't have one. Let me check the ai package dependencies: @anthropic-ai/sdk, @aws-sdk/client-bedrock-runtime, @google/genai, @mistralai/mistralai, openai, http-proxy-agent, https-proxy-agent, partial-json.
Actually, maybe there are packages deeper in the tree. Let me use npm to check if any of these have install scripts.
>npm view @anthropic-ai/sdk@0.91.1 scripts && npm view @mistralai/mistralai@2.2.0 scripts && npm view openai@6.26.0 scripts && npm view http-proxy-agent@7.0.2 scripts && npm view https-proxy-agent@7.0.6 scripts && npm view partial-json@0.1.7 scriptsLet me search more broadly. Maybe the issue is with pnpm's onlyBuiltDependencies prompt. Let me check the pnpm documentation or search the web.
Aha! This is exactly it. Starting from pnpm 10.0.0, pnpm blocks lifecycle scripts by default and shows an interactive prompt (approve-scripts / approve-builds) asking which packages to allow running build scripts for. npm still runs them all by default.
The user gets a picker because pnpm is showing the interactive approve-builds prompt for packages that have lifecycle scripts (like install, postinstall, etc.). The "three packages" are likely packages in the dependency tree that have install scripts.
We know koffi has an install script. Let me figure out what the other two might be. Maybe the @silvia-odwyer/photon-node doesn't have one, but maybe some other dependency does. Let me check the transitive dependencies.
Actually, undici has a prepare script, but that doesn't run during registry install. However, pnpm might also show the prompt for packages that have postinstall or install scripts. Let me check if there are other packages.
Let me look at the @earendil-works/pi-coding-agent dependency tree to see all packages with install scripts. I can use npm ls or simulate an install.
Wait, actually I can use npm to get the full package tree of @earendil-works/pi-coding-agent and then check which ones have install/postinstall scripts. Let me try npm pack or npm install --dry-run or npm ls after a local install. But I might not want to actually install.
Alternatively, I can check the npm registry metadata for transitive dependencies. But that's hard.
Actually, let me check if @aws-sdk/client-bedrock-runtime or its dependencies have install scripts. AWS SDK packages sometimes don't have them. But maybe protobufjs or something deeper does.
Let me check yaml and some other common packages.
>npm view yaml@2.8.2 scripts && npm view hosted-git-info@9.0.2 scripts && npm view ignore@7.0.5 scriptsNone of those have install or postinstall scripts. Let me try a different approach. Maybe I can use npm install --dry-run or npm pack to get the dependency tree. But I might not need to know the exact three packages to answer the user's question. The core discrepancy is that pnpm blocks lifecycle scripts by default (showing an interactive picker), while npm does not.
However, to give a precise answer, I should try to find all packages in the dependency tree with install scripts. Let me try to use npm install in a temp directory and inspect.
>cd /tmp && mkdir -p test-pi-install && cd test-pi-install && npm install @earendil-works/pi-coding-agent --dry-run 2>&1 | grep -i "install\|postinstall\|preinstall" | head -20Let me try a different approach. I'll actually install in a temp dir and then look for packages with install/postinstall scripts in node_modules.
>cd /tmp && rm -rf test-pi-install && mkdir test-pi-install && cd test-pi-install && npm install @earendil-works/pi-coding-agent 2>&1 | tail -5Now let me search for packages in node_modules that have install/postinstall scripts.
>cd /tmp/test-pi-install && for f in $(find node_modules -name package.json -maxdepth 3 | head -200); do dir=$(dirname "$f"); name=$(node -e "console.log(require('./$f').name)" 2>/dev/null); scripts=$(node -e "const s=require('./$f').scripts||{}; console.log(Object.keys(s).filter(k=>['install','postinstall','preinstall'].includes(k)).join(' '))" 2>/dev/null); if [ -n "$scripts" ]; then echo "$name ($dir): $scripts"; fi; doneThere we go! The three packages with install/postinstall/preinstall scripts are:
koffi (install)protobufjs (postinstall)@google/genai (preinstall)So when installing @earendil-works/pi-coding-agent globally with pnpm, pnpm detects these three packages with lifecycle scripts and shows an interactive picker asking which ones to allow building/running scripts for. npm, by default, runs all these scripts automatically without prompting.
The user asks "Why is there a discrepancy?" The answer is: pnpm blocks lifecycle scripts by default for security reasons and interactively prompts the user to approve which ones to run, whereas npm runs them all automatically.
Now let me verify the exact pnpm behavior. Let me search for the specific pnpm version and feature.
So the answer is clear. Let me also verify which pnpm version the user might be on. But the key answer is that pnpm 10+ blocks dependency lifecycle scripts by default and shows an interactive picker, while npm does not.
Let me also check if @google/genai really has a preinstall script. It showed up in my scan. Let me verify.
>cd /tmp/test-pi-install && cat node_modules/@google/genai/package.json | grep -A 10 '"scripts"'So @google/genai does have a preinstall script that just echoes. protobufjs has a postinstall script. koffi has an install script.
The answer is straightforward: pnpm 10 and later blocks lifecycle scripts (install, preinstall, postinstall, prepare) of dependencies by default for security reasons. When you install a package globally with pnpm, if any transitive dependency has a lifecycle script, pnpm shows an interactive picker asking which packages should be allowed to run their scripts. npm does not block these scripts and runs them all automatically, so no picker appears.
The three packages in the dependency tree of @earendil-works/pi-coding-agent that have lifecycle scripts are:
koffi (optional dependency of @earendil-works/pi-tui, has an install script)protobufjs (transitive dependency, has a postinstall script)@google/genai (dependency of @earendil-works/pi-ai, has a preinstall script)To avoid the picker in pnpm, the user can:
pnpm approve-builds koffi protobufjs @google/genai beforehandpnpm.onlyBuiltDependencies in .npmrc or package.json--allow-build flag (if available in their pnpm version)But the user asked "Why is there a discrepancy?" so I should answer that directly.
pnpm 10 and later blocks dependency lifecycle scripts by default for security reasons, while npm still runs them automatically.
When you install @earendil-works/pi-coding-agent, three transitive dependencies have install/preinstall/postinstall scripts:
koffi (optional dependency of @earendil-works/pi-tui) — has an install script that compiles native bindingsprotobufjs (transitive dependency) — has a postinstall script@google/genai (dependency of @earendil-works/pi-ai) — has a preinstall scriptpnpm detects these scripts and shows an interactive picker asking which ones you want to allow. npm does not block them, so it installs silently and runs all three scripts automatically.
To avoid the picker with pnpm, pre-approve the packages:
pnpm approve-builds koffi protobufjs @google/genaipnpm install --global @earendil-works/pi-coding-agentOr add them to your .npmrc:
pnpm.onlyBuiltDependencies[] = koffipnpm.onlyBuiltDependencies[] = protobufjspnpm.onlyBuiltDependencies[] = @google/genaipnpm approve-builds koffi protobufjs @google/genaipnpm install --global @earendil-works/pi-coding-agentpnpm.onlyBuiltDependencies[] = koffipnpm.onlyBuiltDependencies[] = protobufjspnpm.onlyBuiltDependencies[] = @google/genai