From ddd1316644ed4b4b48cd7524b4ac543d01784525 Mon Sep 17 00:00:00 2001 From: gsxdsm Date: Sun, 19 Jul 2026 21:40:57 -0700 Subject: [PATCH] FN-8413: add ACP SDK to published CLI dependencies Ensure packed Fusion installs the ACP SDK required by the vendored Claude CLI extension. - Declare the compatible ACP SDK version in the CLI runtime dependencies. - Guard published-manifest dependency coverage and clean-install resolution with tests. - Add a patch changeset for the published package fix. Files changed: .changeset/fn-8413-pi-claude-cli-acp-sdk-dep.md | 7 ++ packages/cli/package.json | 1 + packages/cli/src/__tests__/package-config.test.ts | 26 +++++++ .../pi-claude-cli-publish-resolve.smoke.test.ts | 81 ++++++++++++++++++++++ pnpm-lock.yaml | 3 + 5 files changed, 118 insertions(+) Fusion-Task-Id: FN-8413 Fusion-Task-Lineage: c3cb8cc1-0be8-4196-b43e-eacd5837105d Co-authored-by: Fusion (runfusion.ai) --- .../fn-8413-pi-claude-cli-acp-sdk-dep.md | 7 ++ packages/cli/package.json | 1 + .../cli/src/__tests__/package-config.test.ts | 26 ++++++ ...i-claude-cli-publish-resolve.smoke.test.ts | 81 +++++++++++++++++++ pnpm-lock.yaml | 3 + 5 files changed, 118 insertions(+) create mode 100644 .changeset/fn-8413-pi-claude-cli-acp-sdk-dep.md create mode 100644 packages/cli/src/__tests__/pi-claude-cli-publish-resolve.smoke.test.ts diff --git a/.changeset/fn-8413-pi-claude-cli-acp-sdk-dep.md b/.changeset/fn-8413-pi-claude-cli-acp-sdk-dep.md new file mode 100644 index 0000000000..25c55b21aa --- /dev/null +++ b/.changeset/fn-8413-pi-claude-cli-acp-sdk-dep.md @@ -0,0 +1,7 @@ +--- +"@runfusion/fusion": patch +--- + +summary: Install @agentclientprotocol/sdk with @runfusion/fusion so the Claude CLI pi extension can load. +category: fix +dev: FN-8413 / issue #2355 — nested dist/pi-claude-cli/package.json declared the SDK but npm only installs root dependencies; pin remains 0.24.0 (do not bump to 1.x). diff --git a/packages/cli/package.json b/packages/cli/package.json index 8406fb63e0..6d915f363d 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -63,6 +63,7 @@ "test:pre-release": "pnpm test:slow-cli && pnpm test:build-exe" }, "dependencies": { + "@agentclientprotocol/sdk": "0.24.0", "@earendil-works/pi-ai": "0.80.10", "@earendil-works/pi-coding-agent": "0.80.10", "claude-code-cli-acp": "0.1.1", diff --git a/packages/cli/src/__tests__/package-config.test.ts b/packages/cli/src/__tests__/package-config.test.ts index 11bd91b329..3a2e5adff5 100644 --- a/packages/cli/src/__tests__/package-config.test.ts +++ b/packages/cli/src/__tests__/package-config.test.ts @@ -167,6 +167,32 @@ describe("CLI package.json publishing config", () => { assertRuntimeDepsAreNotOptionalPeers(applyPrepackTransform(pkg), "published manifest"); }); + /* + FNXC:PublishBoundary 2026-07-19-21:20: + FN-8413 / issue #2355 requires the root published manifest to carry every + third-party dependency used by the raw TypeScript pi-claude-cli extension. + The nested private dist/pi-claude-cli/package.json is inert during npm + install, so keep the SDK pinned to the driver's compatible 0.24.0 API here + and after prepack rather than relying on the workspace dependency. + */ + it("keeps raw pi-claude-cli runtime dependencies on the published root manifest", () => { + const piClaudeCliPkg = loadPackageJson("pi-claude-cli"); + const publishedPkg = applyPrepackTransform(pkg); + + expect(pkg.dependencies).toHaveProperty("@agentclientprotocol/sdk", "0.24.0"); + expect(publishedPkg.dependencies).toHaveProperty("@agentclientprotocol/sdk", "0.24.0"); + + for (const [name, specifier] of Object.entries(piClaudeCliPkg.dependencies ?? {})) { + if (name.startsWith("@fusion/") || typeof specifier !== "string" || specifier.includes("workspace:")) { + continue; + } + expect( + pkg.dependencies, + `raw pi-claude-cli dependency ${name} must be installed from the published root manifest`, + ).toHaveProperty(name, specifier); + } + }); + it("defines test:docs-index as a single-file docs README index lane", () => { const script = pkg.scripts?.["test:docs-index"]; const parts = script?.trim().split(/\s+/) ?? []; diff --git a/packages/cli/src/__tests__/pi-claude-cli-publish-resolve.smoke.test.ts b/packages/cli/src/__tests__/pi-claude-cli-publish-resolve.smoke.test.ts new file mode 100644 index 0000000000..0fea5c4c89 --- /dev/null +++ b/packages/cli/src/__tests__/pi-claude-cli-publish-resolve.smoke.test.ts @@ -0,0 +1,81 @@ +import { + existsSync, + mkdirSync, + mkdtempSync, + readdirSync, + readFileSync, + rmSync, + writeFileSync, +} from "node:fs"; +import { createRequire } from "node:module"; +import { join } from "node:path"; +import { tmpdir } from "node:os"; +import { spawnSync } from "node:child_process"; +import { describe, expect, it } from "vitest"; + +const cliRoot = join(__dirname, "..", ".."); +const shouldRun = + process.env.FUSION_TEST_PI_CLAUDE_PUBLISH_RESOLVE === "1" || + process.env.FUSION_TEST_PI_CLAUDE_PUBLISH_RESOLVE === "true"; + +function run(command: string, args: string[], cwd: string): void { + const result = spawnSync(command, args, { cwd, encoding: "utf8", stdio: "pipe" }); + if (result.status !== 0) { + throw new Error( + `${command} ${args.join(" ")} failed (exit ${result.status}):\n${result.stderr || result.stdout}`, + ); + } +} + +/* +FNXC:PublishBoundary 2026-07-19-21:20: +FN-8413 / issue #2355 reproduces the real npm install boundary because the +private nested dist/pi-claude-cli/package.json cannot install dependencies for +raw TypeScript extension files. Pack and install with plain npm, then resolve +from acp-driver.ts to prove the root SDK pin remains the driver's compatible +0.24.0 API instead of silently relying on workspace hoisting or upgrading to 1.x. +*/ +describe.skipIf(!shouldRun)("pi-claude-cli published package resolution", () => { + it("resolves the ACP SDK from the packed raw extension after a clean npm install", () => { + const driverPath = join(cliRoot, "dist", "pi-claude-cli", "src", "acp-driver.ts"); + if (!existsSync(driverPath)) { + run("pnpm", ["run", "build:package"], cliRoot); + } + expect(existsSync(driverPath), "CLI build must stage the raw pi-claude-cli driver").toBe(true); + + const smokeDir = mkdtempSync(join(tmpdir(), "fusion-pi-claude-resolve-")); + try { + const packDir = join(smokeDir, "tarballs"); + const installDir = join(smokeDir, "install"); + mkdirSync(installDir, { recursive: true }); + run("pnpm", ["pack", "--pack-destination", packDir], cliRoot); + + const tarball = readdirSync(packDir).find( + (file) => file.startsWith("runfusion-fusion-") && file.endsWith(".tgz"), + ); + expect(tarball, "pnpm pack must produce the @runfusion/fusion tarball").toBeDefined(); + + writeFileSync( + join(installDir, "package.json"), + JSON.stringify({ name: "fusion-pi-claude-resolve-smoke", version: "0.0.0", private: true }), + ); + run( + "npm", + ["install", "--no-audit", "--no-fund", "--ignore-scripts", join(packDir, tarball!)], + installDir, + ); + + const installedRoot = join(installDir, "node_modules", "@runfusion", "fusion"); + const installedPackage = JSON.parse(readFileSync(join(installedRoot, "package.json"), "utf8")); + expect(installedPackage.dependencies).toHaveProperty("@agentclientprotocol/sdk", "0.24.0"); + + const installedDriver = join(installedRoot, "dist", "pi-claude-cli", "src", "acp-driver.ts"); + expect(existsSync(installedDriver), "packed package must retain the raw extension driver").toBe(true); + expect(createRequire(installedDriver).resolve("@agentclientprotocol/sdk")).toContain( + "@agentclientprotocol/sdk", + ); + } finally { + rmSync(smokeDir, { recursive: true, force: true }); + } + }, 300_000); +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3e066ea527..d1621eac96 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -50,6 +50,9 @@ importers: packages/cli: dependencies: + '@agentclientprotocol/sdk': + specifier: 0.24.0 + version: 0.24.0(zod@4.3.6) '@earendil-works/pi-ai': specifier: 0.80.10 version: 0.80.10(@modelcontextprotocol/sdk@1.28.0(zod@4.3.6))(ws@8.20.0)(zod@4.3.6)