diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index 9c2395e3d..76bfeb815 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -2,6 +2,10 @@ ## [Unreleased] +### Fixed + +- Fixed legacy extension plugin validation failing with `Export named 'calculateCost' not found in module '.../legacy-pi-ai-shim.ts'` when the extension imports `calculateCost` (or `modelsAreEqual` / `getBundledProviders`) from `@oh-my-pi/pi-ai`. Those symbols were relocated to `@oh-my-pi/pi-catalog/models` during the catalog split but were never bridged back through the legacy `pi-ai` root shim; the shim now re-exports them alongside the existing `getModel` / `getModels` aliases so plugins written against pre-split pi-ai load again ([#4584](https://github.com/can1357/oh-my-pi/issues/4584)). + ## [16.3.6] - 2026-07-04 ### Changed diff --git a/packages/coding-agent/src/extensibility/legacy-pi-ai-shim.ts b/packages/coding-agent/src/extensibility/legacy-pi-ai-shim.ts index 22f484992..e8aa49837 100644 --- a/packages/coding-agent/src/extensibility/legacy-pi-ai-shim.ts +++ b/packages/coding-agent/src/extensibility/legacy-pi-ai-shim.ts @@ -19,7 +19,13 @@ * `types.ts` via the `export *` below — pi-ai still exports both as types, * only the runtime `Type` builder and `StringEnum()` helper were removed. */ -import { getBundledModel, getBundledModels } from "@oh-my-pi/pi-catalog/models"; +import { + calculateCost, + getBundledModel, + getBundledModels, + getBundledProviders, + modelsAreEqual, +} from "@oh-my-pi/pi-catalog/models"; import { type TSchema, Type } from "./typebox"; export interface StringEnumOptions { @@ -66,8 +72,15 @@ export function StringEnum( } export * from "@oh-my-pi/pi-ai"; -export { Type }; - -/** Compatibility aliases for renamed catalog functions */ +/** + * Compatibility re-exports for catalog symbols that pi-ai historically exposed + * from its own barrel prior to the `refactor(catalog)!: split model catalog + * from pi-ai` change. Legacy extensions still import these from the pi-ai + * root, so the shim bridges them through to their new home in + * `@oh-my-pi/pi-catalog/models`. `getModel`/`getModels` are the historical + * pi-ai names for `getBundledModel`/`getBundledModels`; the remaining symbols + * kept their names across the move. + */ +export { calculateCost, getBundledProviders, modelsAreEqual, Type }; export const getModel = getBundledModel; export const getModels = getBundledModels; diff --git a/packages/coding-agent/test/extensibility/legacy-pi-ai-type-remap.test.ts b/packages/coding-agent/test/extensibility/legacy-pi-ai-type-remap.test.ts index d27295d68..30661b82d 100644 --- a/packages/coding-agent/test/extensibility/legacy-pi-ai-type-remap.test.ts +++ b/packages/coding-agent/test/extensibility/legacy-pi-ai-type-remap.test.ts @@ -3,7 +3,13 @@ import * as fs from "node:fs/promises"; import * as os from "node:os"; import * as path from "node:path"; import * as url from "node:url"; -import { getBundledModel, getBundledModels } from "@oh-my-pi/pi-catalog/models"; +import { + calculateCost, + getBundledModel, + getBundledModels, + getBundledProviders, + modelsAreEqual, +} from "@oh-my-pi/pi-catalog/models"; import { __resetLegacyPiResolutionCache, installLegacyPiSpecifierShim, @@ -124,6 +130,35 @@ describe("legacy-pi @(scope)/pi-ai root `Type` remap (issue #1437)", () => { expect(loaded.testGetModels).toBe(getBundledModels); }); + it("re-exports calculateCost from @oh-my-pi/pi-catalog/models (issue #4584)", async () => { + // `calculateCost` was moved from the `@oh-my-pi/pi-ai` barrel to + // `@oh-my-pi/pi-catalog/models` in the catalog split. Legacy extensions + // still import it from the pi-ai root, so the shim must bridge it back + // to the catalog implementation. The historical regression was a plain + // `SyntaxError: Export named 'calculateCost' not found in module + // '.../legacy-pi-ai-shim.ts'` at extension-validation time. + const loaded = (await loadLegacyPiModule( + await writeFixtureExtension( + 'import { calculateCost } from "@oh-my-pi/pi-ai"; export const probe = calculateCost;', + ), + )) as { probe: unknown }; + expect(loaded.probe).toBe(calculateCost); + }); + + it("re-exports modelsAreEqual and getBundledProviders from @oh-my-pi/pi-catalog/models", async () => { + const loaded = (await loadLegacyPiModule( + await writeFixtureExtension( + [ + 'import { modelsAreEqual, getBundledProviders } from "@oh-my-pi/pi-ai";', + "export const eq = modelsAreEqual;", + "export const providers = getBundledProviders;", + ].join("\n"), + ), + )) as { eq: unknown; providers: unknown }; + expect(loaded.eq).toBe(modelsAreEqual); + expect(loaded.providers).toBe(getBundledProviders); + }); + it("exports StringEnum as a schema builder with options support", async () => { const loaded = (await loadLegacyPiModule( await writeFixtureExtension(