From 6800faa65da378e5de48a059da2f746974df34e8 Mon Sep 17 00:00:00 2001 From: beppe2880 <55625685+beppe2880@users.noreply.github.com> Date: Wed, 11 Feb 2026 23:31:38 +0100 Subject: [PATCH] fix(cli): run bun install after writing npm dependencies in ocx add --- packages/cli/src/commands/add.ts | 38 ++++++++++++++++++++++++++++++++ packages/cli/tests/add.test.ts | 32 +++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) diff --git a/packages/cli/src/commands/add.ts b/packages/cli/src/commands/add.ts index c8aa4874..53f3cba4 100644 --- a/packages/cli/src/commands/add.ts +++ b/packages/cli/src/commands/add.ts @@ -594,6 +594,24 @@ async function runRegistryAddCore( npmSpin?.fail(`Failed to update ${packageJsonPath}`) throw error } + + // Run bun install to actually install the dependencies + const packageDir = isFlattened ? cwd : join(cwd, ".opencode") + const installSpin2 = options.quiet + ? null + : createSpinner({ text: "Installing npm dependencies..." }) + installSpin2?.start() + + try { + await runBunInstall(packageDir) + installSpin2?.succeed("Installed npm dependencies") + } catch (_error) { + installSpin2?.fail("Failed to install npm dependencies") + logger.warn( + `Could not run 'bun install' in ${packageDir}. ` + + "You may need to install dependencies manually.", + ) + } } // Save lock file @@ -886,6 +904,26 @@ async function updateOpencodeDevDependencies( } } +/** + * Run `bun install` in the given directory to install dependencies + * declared in package.json. + * + * @param cwd - Directory containing the package.json + * @throws Error if the install process exits with a non-zero code + */ +async function runBunInstall(cwd: string): Promise { + const proc = Bun.spawn(["bun", "install"], { + cwd, + stdout: "pipe", + stderr: "pipe", + }) + const exitCode = await proc.exited + if (exitCode !== 0) { + const stderr = await new Response(proc.stderr).text() + throw new Error(`bun install failed (exit ${exitCode}): ${stderr.trim()}`) + } +} + /** * Find which component installed a given file path */ diff --git a/packages/cli/tests/add.test.ts b/packages/cli/tests/add.test.ts index 1bf2e3f4..eba3fef6 100644 --- a/packages/cli/tests/add.test.ts +++ b/packages/cli/tests/add.test.ts @@ -225,6 +225,38 @@ describe("ocx add", () => { expect(opencode.plugin).toContain("no-mcp-plugin") }) + it("should install npm dependencies after writing package.json", async () => { + testDir = await createTempDir("add-npm-install") + + // Init and add registry + await runCLI(["init", "--force"], testDir) + + const configPath = join(testDir, ".opencode", "ocx.jsonc") + const config = parseJsonc(await readFile(configPath, "utf-8")) + config.registries = { + kdco: { url: registry.url }, + } + await writeFile(configPath, JSON.stringify(config, null, 2)) + + // Install component that has npmDependencies (test-plugin has lodash@^4.17.21) + const { exitCode, output } = await runCLI(["add", "kdco/test-plugin", "--force"], testDir) + + if (exitCode !== 0) { + console.log(output) + } + expect(exitCode).toBe(0) + + // Verify package.json was written + const packageJsonPath = join(testDir, ".opencode", "package.json") + expect(existsSync(packageJsonPath)).toBe(true) + + // Verify node_modules was created (bun install ran) + expect(existsSync(join(testDir, ".opencode", "node_modules"))).toBe(true) + + // Verify the dependency was actually installed + expect(existsSync(join(testDir, ".opencode", "node_modules", "lodash"))).toBe(true) + }) + it("should fail if integrity check fails", async () => { testDir = await createTempDir("add-integrity-fail")