diff --git a/.changeset/theme-shape-render-call.md b/.changeset/theme-shape-render-call.md new file mode 100644 --- /dev/null +++ b/.changeset/theme-shape-render-call.md @@ -0,0 +1,5 @@ +--- +"@aliou/pi-processes": patch +--- + +Detect tool render themes by shape instead of class identity so Pi and oh-my-pi render process calls correctly when package copies differ. diff --git a/extensions/processes/tools/index.test.ts b/extensions/processes/tools/index.test.ts --- a/extensions/processes/tools/index.test.ts +++ b/extensions/processes/tools/index.test.ts @@ -13,7 +13,7 @@ */ import { type ExtensionAPI, - Theme, + type Theme, type ToolDefinition, } from "@earendil-works/pi-coding-agent"; import { describe, expect, it, vi } from "vitest"; @@ -32,13 +32,8 @@ third?: RenderCallFnContext | Theme, ) => ReturnType; -/** pi's theme: a real Theme instance (passes `instanceof Theme`). */ -const piTheme = Object.create(Theme.prototype) as Theme; -piTheme.fg = (_color: string, text: string) => text; -piTheme.bold = (text: string) => text; - -/** OMP's theme: same surface, but not a pi Theme instance. */ -const ompTheme = { +/** Theme-like object, as passed by host Pi/OMP package copies. */ +const theme = { fg: (_color: string, text: string) => text, bold: (text: string) => text, } as Theme; @@ -54,7 +49,7 @@ describe("renderCall host argument order", () => { it("renders with pi order: (args, theme, context)", () => { const renderCall = captureRenderCall(); - const component = renderCall({ action: "list" }, piTheme, { + const component = renderCall({ action: "list" }, theme, { expanded: false, } as RenderCallFnContext); expect(component.render(80).join("\n")).toContain("Process:"); @@ -65,7 +60,7 @@ const component = renderCall( { action: "list" }, { expanded: false, isPartial: true } as RenderCallFnContext, - ompTheme, + theme, ); expect(component.render(80).join("\n")).toContain("Process:"); }); diff --git a/extensions/processes/tools/index.ts b/extensions/processes/tools/index.ts --- a/extensions/processes/tools/index.ts +++ b/extensions/processes/tools/index.ts @@ -3,7 +3,7 @@ defineTool, type ExtensionAPI, type ExtensionContext, - Theme, + type Theme, type ToolDefinition, } from "@earendil-works/pi-coding-agent"; import { type Component, Container, Text } from "@earendil-works/pi-tui"; @@ -112,13 +112,17 @@ type RenderCallFnContext = Parameters[2]; /** - * pi invokes renderCall as (args, theme, context) where theme is pi's Theme - * class instance. Detecting that exact class distinguishes pi's argument - * order from OMP's (args, options, theme), where the second argument is a - * plain options object. + * pi invokes renderCall as (args, theme, context), while OMP invokes it as + * (args, options, theme). Detect the theme by shape instead of Theme class + * identity because host and extension package copies can differ. */ -function isPiTheme(value: unknown): value is Theme { - return value instanceof Theme; +function isThemeLike(value: unknown): value is Theme { + return ( + typeof value === "object" && + value !== null && + typeof (value as { fg?: unknown }).fg === "function" && + typeof (value as { bold?: unknown }).bold === "function" + ); } function renderProcessCall( @@ -126,7 +130,7 @@ second: Theme | RenderCallFnContext, third?: RenderCallFnContext | Theme, ): Component { - const usesPiOrder = isPiTheme(second); + const usesPiOrder = isThemeLike(second); const theme = usesPiOrder ? second : (third as Theme); const context = (usesPiOrder ? third : second) as | RenderCallFnContext