diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index 024306064..9290adc81 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -4,7 +4,7 @@ ### Fixed -- Fixed legacy pi extensions failing extension validation when importing `getPackageDir` or `getProjectDir` from `@earendil-works/pi-coding-agent` (aliased to the legacy shim). The shim only re-exported `getAgentDir`; the two missing path helpers now resolve — `getProjectDir` from `@oh-my-pi/pi-utils` and `getPackageDir` from omp's canonical package-root helper — so extensions like `@gotgenes/pi-permission-system` install and load ([#5968](https://github.com/can1357/oh-my-pi/issues/5968)). +- Fixed legacy pi extensions failing extension validation when importing `getPackageDir` or `getProjectDir` from `@earendil-works/pi-coding-agent` (aliased to the legacy shim). The shim only re-exported `getAgentDir`; the two missing path helpers now resolve — `getProjectDir` from `@oh-my-pi/pi-utils`, and `getPackageDir` as a string-valued wrapper over omp's canonical package-root helper that falls back to the executable's directory inside `bun --compile` binaries (where the canonical helper returns `undefined`), matching pi's string contract. Extensions like `@gotgenes/pi-permission-system` install and load, and `path.join(getPackageDir(), …)` no longer crashes in the shipped binary ([#5968](https://github.com/can1357/oh-my-pi/issues/5968)). ## [17.0.4] - 2026-07-18 diff --git a/packages/coding-agent/src/extensibility/legacy-pi-coding-agent-shim.ts b/packages/coding-agent/src/extensibility/legacy-pi-coding-agent-shim.ts index 3e351e589..6542a6481 100644 --- a/packages/coding-agent/src/extensibility/legacy-pi-coding-agent-shim.ts +++ b/packages/coding-agent/src/extensibility/legacy-pi-coding-agent-shim.ts @@ -22,8 +22,10 @@ import { getAgentDbPath, getAgentDir, getProjectDir, + isCompiledBinary, parseFrontmatter as parseOmpFrontmatter, } from "@oh-my-pi/pi-utils"; +import { getPackageDir as getOmpPackageDir } from "../config"; import type { PromptTemplate } from "../config/prompt-templates"; import { type SettingPath, Settings } from "../config/settings"; import { EditTool } from "../edit"; @@ -1336,12 +1338,28 @@ export function readStoredCredential(provider: string): AuthCredential | undefin // Pi SDK path helpers. `export * from "../index"` above only forwards // `getAgentDir`; `getProjectDir` (a `@oh-my-pi/pi-utils` helper) and -// `getPackageDir` (omp's canonical coding-agent package-root helper, matching -// pi's "install directory of the coding-agent package" semantics) are absent -// from that barrel, so legacy extensions importing either fail Bun's static -// export check during validation (issue #5968). +// `getPackageDir` are absent from that barrel, so legacy extensions importing +// either fail Bun's static export check during validation (issue #5968). export { getProjectDir } from "@oh-my-pi/pi-utils"; -export { getPackageDir } from "../config"; + +/** + * Coding-agent package install directory, matching pi's string-valued + * `getPackageDir()` contract (extensions do `path.join(getPackageDir(), ...)` + * to auto-allow bundled docs/resources). + * + * omp's canonical `getPackageDir()` (`../config`) returns `undefined` inside a + * `bun --compile` binary — `import.meta.dir` is `/$bunfs/root` and no owning + * `package.json` exists (issue #1423). Returning `undefined` there would crash + * every legacy `path.join(getPackageDir(), ...)` at runtime in the shipped + * binary, the primary distribution. So fall back to the executable's own + * directory in compiled mode, where the binary *is* the install root. The + * `PI_PACKAGE_DIR` override and dev/source/npm-dist walk-up still win via the + * canonical helper. + */ +export function getPackageDir(): string { + return getOmpPackageDir() ?? (isCompiledBinary() ? path.dirname(process.execPath) : process.cwd()); +} + export * from "../index"; export { formatBytes as formatSize } from "../tools/render-utils"; export { Type } from "./typebox"; diff --git a/packages/coding-agent/test/extensibility/legacy-pi-path-helpers.test.ts b/packages/coding-agent/test/extensibility/legacy-pi-path-helpers.test.ts index 14e3a3d3a..d2732fe87 100644 --- a/packages/coding-agent/test/extensibility/legacy-pi-path-helpers.test.ts +++ b/packages/coding-agent/test/extensibility/legacy-pi-path-helpers.test.ts @@ -1,6 +1,8 @@ -import { describe, expect, it } from "bun:test"; +import { afterEach, describe, expect, it, spyOn, vi } from "bun:test"; import * as path from "node:path"; +import * as configModule from "@oh-my-pi/pi-coding-agent/config"; import * as shim from "@oh-my-pi/pi-coding-agent/extensibility/legacy-pi-coding-agent-shim"; +import * as utils from "@oh-my-pi/pi-utils"; // Issue #5968: pi extensions import the SDK path helpers (`getAgentDir`, // `getProjectDir`, `getPackageDir`) from `@earendil-works/pi-coding-agent`, @@ -10,17 +12,37 @@ import * as shim from "@oh-my-pi/pi-coding-agent/extensibility/legacy-pi-coding- // error and any importing extension failed validation. These pin the full // path-helper surface through the public package specifier. describe("legacy shim path helpers", () => { + afterEach(() => vi.restoreAllMocks()); + it("exports the three pi SDK path helpers as callable functions", () => { expect(typeof shim.getAgentDir).toBe("function"); expect(typeof shim.getProjectDir).toBe("function"); expect(typeof shim.getPackageDir).toBe("function"); }); - it("getPackageDir resolves the coding-agent package root", () => { + it("getPackageDir resolves the coding-agent package root in source mode", () => { // omp's canonical helper returns the package root containing package.json // (pi's "install directory of the coding-agent package" semantics). const dir = shim.getPackageDir(); - expect(dir).toBeDefined(); - expect(path.basename(dir as string)).toBe("coding-agent"); + expect(path.basename(dir)).toBe("coding-agent"); + }); + + // Pi's getPackageDir() is string-valued: extensions do + // `path.join(getPackageDir(), ...)`. omp's canonical helper returns + // `undefined` inside a `bun --compile` binary (import.meta.dir is + // /$bunfs/root, no package.json — issue #1423), which would crash every + // such call in the shipped binary. The shim MUST fall back to a real + // directory instead of forwarding undefined. + it("getPackageDir returns a string even when the canonical helper yields undefined", () => { + spyOn(configModule, "getPackageDir").mockReturnValue(undefined); + const dir = shim.getPackageDir(); + expect(typeof dir).toBe("string"); + expect(dir.length).toBeGreaterThan(0); + }); + + it("getPackageDir falls back to the executable directory in compiled-binary mode", () => { + spyOn(configModule, "getPackageDir").mockReturnValue(undefined); + spyOn(utils, "isCompiledBinary").mockReturnValue(true); + expect(shim.getPackageDir()).toBe(path.dirname(process.execPath)); }); });