anomalyco / anomalyco/opencode
plugin: git dep with a build/prepare script fails — compiled binary can't run its own bundled npm-cli.js
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 209k
- Forks
- 27.5k
- PR merge metrics
- PR metrics pending
Description
Summary
opencode plugin add/plugins fails with NpmInstallFailedError: git dep preparation failed for any git dependency whose package.json has a
build, prepare, preinstall, install, postinstall, or prepack
script, or a workspaces field — regardless of git host. Root cause: the
compiled opencode binary re-invokes itself as the "npm" binary for pacote's
git-dependency prepare step, but its own CLI doesn't know how to run that,
so it just prints help and exits instead of running the prepare install.
Environment
- opencode version: 2.0.7 (channel: latest)
- OS: Darwin 25.6.0 (macOS, darwin arm64)
- Shell: /bin/zsh
- Install: global npm-style install (
@opencode/cli), compiled binary
Reproduction
Fully public, no auth/network involved beyond a normal git clone:
opencode plugin add "git+ssh://git@github.com/lnwu/opencode-plugin-repro-withbuild.git"
That repo's package.json is just:
{
"name": "opencode-plugin-repro-withbuild",
"version": "1.0.0",
"scripts": { "build": "echo building" }
}
Renaming that one script key to anything other than build / prepare /
preinstall / install / postinstall / prepack (and not using
workspaces) makes the exact same install succeed — nothing else about the
repo changes. This is host-independent: I originally suspected a self-hosted
git server was the trigger, but a scriptless package installs fine from that
same self-hosted host, and this scripted package fails from github.com too.
Only the presence of one of those script names (or workspaces) matters.
Root Cause
opencode plugin add installs git specifiers through npm's real
@npmcli/arborist (packages/core/src/npm.ts, Npm.reify →
arborist.reify()), not Bun's own installer. Arborist's git fetching goes
through pacote's GitFetcher. pacote/lib/git.js's #prepareDir runs an
extra step only when the cloned package.json declares one of those
scripts or workspaces — it shells out to a real npm install inside the
clone so any prepare-style script has its own deps available:
// pacote/lib/git.js
return npm(
this.npmBin,
[].concat(this.npmInstallCmd).concat(this.npmCliConfig),
dir,
{ ...process.env, _PACOTE_NO_PREPARE_: noPrepare.join('\n') },
{ message: 'git dep preparation failed' } // <- the error we see
)
pacote's npmBin defaults to the string 'npm' (resolved via PATH), but
opencode's server log shows it's configured to a path inside its own bundled
filesystem instead:
args=["/$bunfs/bin/npm-cli.js","install","--force","--cache=...", ...]
pacote/lib/util/npm.js spawns this as <process.execPath> <npmBin> <args...> (the same way you'd run node npm-cli.js install ...). That's
correct for a normal Node runtime, but process.execPath for opencode's
compiled binary is the opencode binary itself — so this ends up invoking
opencode with /$bunfs/bin/npm-cli.js install --force ... as CLI arguments.
opencode's own CLI parser doesn't recognize those as a valid subcommand, so
it just prints help and exits (confirmed in the server log, immediately
before the NpmInstallFailedError):
message="cli starting" args=["/$bunfs/bin/npm-cli.js","install","--force", ...]
message="cli process failed" cause="Cause([Fail(~effect/cli/CliError/ShowHelp: Help requested)])"
message="cli process failed" cause="Cause([Fail(NpmInstallFailedError (cause: Error: git dep preparation failed))])"
So the nested npm install never actually runs, pacote's prepare step fails,
and opencode's error wrapper discards the real cause down to the generic
NpmInstallFailedError.
Confirmed by calling the exact same Arborist.reify() opencode calls
directly (outside opencode, both as a plain script and as a bun build --compile'd binary) — it installs the same git dependency successfully in
both cases, because npmBin isn't overridden to a bunfs path there.
Expected Behavior
Per the plugins docs ("Git
repositories can use hosted shortcuts, HTTPS, or SSH..."), any git dependency
should install the same way npm/bun would handle it, including ones with
a build/prepare script.
Workaround
None needed if you control the package: avoid build/prepare/
preinstall/install/postinstall/prepack script names (rename them) and
avoid workspaces in the installed package's package.json, and ship any
build output pre-built/committed instead. Not viable for git dependencies you
don't control.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in packages/core/src/npm.ts at Npm.reify and trace how Arborist and pacote select the npm binary during git-dependency preparation. Reproduce with the provided opencode plugin add command and the linked package containing a build script. Done means git dependencies with build, prepare, install scripts, or workspaces install successfully through the compiled opencode binary.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- bun, git, node.js, typescript
- Domain
- cli, tooling
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 72/100