From 93757ece4714ff8a1e499b71a94e18cda1609d06 Mon Sep 17 00:00:00 2001 From: JonathanLab Date: Fri, 31 Jul 2026 18:15:35 +0200 Subject: [PATCH 1/2] fix(ui): collapse Pi skill invocations --- .../components/chat-thread/ChatThread.tsx | 7 ++++--- .../session-update/UserMessage.test.tsx | 11 +++++++++++ .../components/session-update/UserMessage.tsx | 7 ++++--- .../session-update/parseFileMentions.test.ts | 18 ++++++++++++++++++ .../session-update/parseFileMentions.tsx | 13 +++++++++++++ 5 files changed, 50 insertions(+), 6 deletions(-) create mode 100644 packages/ui/src/features/sessions/components/session-update/parseFileMentions.test.ts diff --git a/packages/ui/src/features/sessions/components/chat-thread/ChatThread.tsx b/packages/ui/src/features/sessions/components/chat-thread/ChatThread.tsx index 46ae7513f0..7c8b06836b 100644 --- a/packages/ui/src/features/sessions/components/chat-thread/ChatThread.tsx +++ b/packages/ui/src/features/sessions/components/chat-thread/ChatThread.tsx @@ -87,6 +87,7 @@ import { extractCanvasInstructions } from "@posthog/ui/features/sessions/compone import { extractChannelContext } from "@posthog/ui/features/sessions/components/session-update/channelContext"; import { extractCustomInstructions } from "@posthog/ui/features/sessions/components/session-update/customInstructions"; import { + collapsePiSkillInvocation, hasFileMentions, MentionChip, parseFileMentions, @@ -361,9 +362,9 @@ function UserBubble({ () => extractCustomInstructions(afterCanvasInstructions), [afterCanvasInstructions], ); - const displayContent = customInstructions - ? customInstructions.stripped - : afterCanvasInstructions; + const displayContent = collapsePiSkillInvocation( + customInstructions ? customInstructions.stripped : afterCanvasInstructions, + ); const showChannelContextTag = !!channelContext && bluebirdEnabled; const showCanvasInstructionsTag = !!canvasInstructions && bluebirdEnabled; const showHeaderChips = showChannelContextTag || showCanvasInstructionsTag; diff --git a/packages/ui/src/features/sessions/components/session-update/UserMessage.test.tsx b/packages/ui/src/features/sessions/components/session-update/UserMessage.test.tsx index f78b4b091c..6d5f0bf51a 100644 --- a/packages/ui/src/features/sessions/components/session-update/UserMessage.test.tsx +++ b/packages/ui/src/features/sessions/components/session-update/UserMessage.test.tsx @@ -30,6 +30,9 @@ const PROMPT_WITH_CONTEXT = const PROMPT_WITH_CANVAS_INSTRUCTIONS = "add a retention chart\n\n\nauthoring contract\n"; +const PROMPT_WITH_PI_SKILL = + '\nReferences are relative to /skills/code-review.\n\n# Review\n\nInspect the diff.\n\n\nReview this pull request.'; + describe("UserMessage", () => { // useFeatureFlag falls back to import.meta.env.DEV, which is true under // vitest. Pin DEV off in the flag-gating cases so they exercise the flag @@ -79,6 +82,14 @@ describe("UserMessage", () => { expect(screen.queryByText(/channel_context/)).not.toBeInTheDocument(); }); + it("renders Pi skill invocations as a command chip", () => { + renderWithFlags(, true); + + expect(screen.getByText("/code-review")).toBeInTheDocument(); + expect(screen.getByText("Review this pull request.")).toBeInTheDocument(); + expect(screen.queryByText("Inspect the diff.")).not.toBeInTheDocument(); + }); + it("shows the canvas-instructions tag when project-bluebird is enabled", () => { vi.stubEnv("DEV", false); renderWithFlags( diff --git a/packages/ui/src/features/sessions/components/session-update/UserMessage.tsx b/packages/ui/src/features/sessions/components/session-update/UserMessage.tsx index 00cf6ff966..acd8c5ccdf 100644 --- a/packages/ui/src/features/sessions/components/session-update/UserMessage.tsx +++ b/packages/ui/src/features/sessions/components/session-update/UserMessage.tsx @@ -20,6 +20,7 @@ import { extractCanvasInstructions } from "./canvasInstructions"; import { extractChannelContext } from "./channelContext"; import { extractCustomInstructions } from "./customInstructions"; import { + collapsePiSkillInvocation, hasFileMentions, MentionChip, parseFileMentions, @@ -92,9 +93,9 @@ export const UserMessage = memo(function UserMessage({ () => extractCustomInstructions(afterCanvasInstructions), [afterCanvasInstructions], ); - const displayContent = customInstructions - ? customInstructions.stripped - : afterCanvasInstructions; + const displayContent = collapsePiSkillInvocation( + customInstructions ? customInstructions.stripped : afterCanvasInstructions, + ); const showChannelContextTag = !!channelContext && bluebirdEnabled; const showCanvasInstructionsTag = !!canvasInstructions && bluebirdEnabled; const openChannelContextInSplit = usePanelLayoutStore( diff --git a/packages/ui/src/features/sessions/components/session-update/parseFileMentions.test.ts b/packages/ui/src/features/sessions/components/session-update/parseFileMentions.test.ts new file mode 100644 index 0000000000..c49ecb9bbe --- /dev/null +++ b/packages/ui/src/features/sessions/components/session-update/parseFileMentions.test.ts @@ -0,0 +1,18 @@ +import { describe, expect, it } from "vitest"; +import { collapsePiSkillInvocation } from "./parseFileMentions"; + +describe("collapsePiSkillInvocation", () => { + it("replaces Pi skill instructions with the command and user request", () => { + expect( + collapsePiSkillInvocation( + '\nReferences are relative to /skills/code-review.\n\n# Review\n\nInspect the diff.\n\n\nReview this pull request.', + ), + ).toBe("/code-review\n\nReview this pull request."); + }); + + it("keeps non-skill messages unchanged", () => { + expect(collapsePiSkillInvocation("Review this pull request.")).toBe( + "Review this pull request.", + ); + }); +}); diff --git a/packages/ui/src/features/sessions/components/session-update/parseFileMentions.tsx b/packages/ui/src/features/sessions/components/session-update/parseFileMentions.tsx index f50e9bde9d..37df4a20fc 100644 --- a/packages/ui/src/features/sessions/components/session-update/parseFileMentions.tsx +++ b/packages/ui/src/features/sessions/components/session-update/parseFileMentions.tsx @@ -16,6 +16,19 @@ const MENTION_TAG_REGEX = const MENTION_TAG_TEST = /<(?:file\s+path|folder\s+path|github_issue\s+number|github_pr\s+number|error_context\s+label)="[^"]+"/; const SLASH_COMMAND_START = /^\/([a-zA-Z][\w-]*)(?=\s|$)/; +const PI_SKILL_INVOCATION = + /^\n[\s\S]*?\n<\/skill>(?:\n\n([\s\S]+))?$/; + +export function collapsePiSkillInvocation(content: string): string { + const match = content.match(PI_SKILL_INVOCATION); + if (!match) { + return content; + } + + const name = unescapeXmlAttr(match[1]); + const userMessage = match[2]?.trim(); + return userMessage ? `/${name}\n\n${userMessage}` : `/${name}`; +} const inlineComponents: Components = { ...baseComponents, From d71d5e4761bc08d6f22701a194053fd83e7eb3b2 Mon Sep 17 00:00:00 2001 From: JonathanLab Date: Fri, 31 Jul 2026 18:18:38 +0200 Subject: [PATCH 2/2] fix(ui): isolate Pi skill display helper --- .../components/chat-thread/ChatThread.tsx | 2 +- .../components/session-update/UserMessage.tsx | 2 +- .../session-update/parseFileMentions.test.ts | 2 +- .../session-update/parseFileMentions.tsx | 13 ------------- .../session-update/piSkillInvocation.ts | 15 +++++++++++++++ 5 files changed, 18 insertions(+), 16 deletions(-) create mode 100644 packages/ui/src/features/sessions/components/session-update/piSkillInvocation.ts diff --git a/packages/ui/src/features/sessions/components/chat-thread/ChatThread.tsx b/packages/ui/src/features/sessions/components/chat-thread/ChatThread.tsx index 7c8b06836b..e96ed83084 100644 --- a/packages/ui/src/features/sessions/components/chat-thread/ChatThread.tsx +++ b/packages/ui/src/features/sessions/components/chat-thread/ChatThread.tsx @@ -87,11 +87,11 @@ import { extractCanvasInstructions } from "@posthog/ui/features/sessions/compone import { extractChannelContext } from "@posthog/ui/features/sessions/components/session-update/channelContext"; import { extractCustomInstructions } from "@posthog/ui/features/sessions/components/session-update/customInstructions"; import { - collapsePiSkillInvocation, hasFileMentions, MentionChip, parseFileMentions, } from "@posthog/ui/features/sessions/components/session-update/parseFileMentions"; +import { collapsePiSkillInvocation } from "@posthog/ui/features/sessions/components/session-update/piSkillInvocation"; import { SessionUpdateView } from "@posthog/ui/features/sessions/components/session-update/SessionUpdateView"; import { UserShellExecuteView } from "@posthog/ui/features/sessions/components/session-update/UserShellExecuteView"; import { UserMessageAttachments } from "@posthog/ui/features/sessions/components/UserMessageAttachments"; diff --git a/packages/ui/src/features/sessions/components/session-update/UserMessage.tsx b/packages/ui/src/features/sessions/components/session-update/UserMessage.tsx index acd8c5ccdf..74d7a5dc05 100644 --- a/packages/ui/src/features/sessions/components/session-update/UserMessage.tsx +++ b/packages/ui/src/features/sessions/components/session-update/UserMessage.tsx @@ -20,11 +20,11 @@ import { extractCanvasInstructions } from "./canvasInstructions"; import { extractChannelContext } from "./channelContext"; import { extractCustomInstructions } from "./customInstructions"; import { - collapsePiSkillInvocation, hasFileMentions, MentionChip, parseFileMentions, } from "./parseFileMentions"; +import { collapsePiSkillInvocation } from "./piSkillInvocation"; interface UserMessageProps { content: string; diff --git a/packages/ui/src/features/sessions/components/session-update/parseFileMentions.test.ts b/packages/ui/src/features/sessions/components/session-update/parseFileMentions.test.ts index c49ecb9bbe..bd4fda670e 100644 --- a/packages/ui/src/features/sessions/components/session-update/parseFileMentions.test.ts +++ b/packages/ui/src/features/sessions/components/session-update/parseFileMentions.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from "vitest"; -import { collapsePiSkillInvocation } from "./parseFileMentions"; +import { collapsePiSkillInvocation } from "./piSkillInvocation"; describe("collapsePiSkillInvocation", () => { it("replaces Pi skill instructions with the command and user request", () => { diff --git a/packages/ui/src/features/sessions/components/session-update/parseFileMentions.tsx b/packages/ui/src/features/sessions/components/session-update/parseFileMentions.tsx index 37df4a20fc..f50e9bde9d 100644 --- a/packages/ui/src/features/sessions/components/session-update/parseFileMentions.tsx +++ b/packages/ui/src/features/sessions/components/session-update/parseFileMentions.tsx @@ -16,19 +16,6 @@ const MENTION_TAG_REGEX = const MENTION_TAG_TEST = /<(?:file\s+path|folder\s+path|github_issue\s+number|github_pr\s+number|error_context\s+label)="[^"]+"/; const SLASH_COMMAND_START = /^\/([a-zA-Z][\w-]*)(?=\s|$)/; -const PI_SKILL_INVOCATION = - /^\n[\s\S]*?\n<\/skill>(?:\n\n([\s\S]+))?$/; - -export function collapsePiSkillInvocation(content: string): string { - const match = content.match(PI_SKILL_INVOCATION); - if (!match) { - return content; - } - - const name = unescapeXmlAttr(match[1]); - const userMessage = match[2]?.trim(); - return userMessage ? `/${name}\n\n${userMessage}` : `/${name}`; -} const inlineComponents: Components = { ...baseComponents, diff --git a/packages/ui/src/features/sessions/components/session-update/piSkillInvocation.ts b/packages/ui/src/features/sessions/components/session-update/piSkillInvocation.ts new file mode 100644 index 0000000000..efbfd1dd0e --- /dev/null +++ b/packages/ui/src/features/sessions/components/session-update/piSkillInvocation.ts @@ -0,0 +1,15 @@ +import { unescapeXmlAttr } from "@posthog/shared"; + +const PI_SKILL_INVOCATION = + /^\n[\s\S]*?\n<\/skill>(?:\n\n([\s\S]+))?$/; + +export function collapsePiSkillInvocation(content: string): string { + const match = content.match(PI_SKILL_INVOCATION); + if (!match) { + return content; + } + + const name = unescapeXmlAttr(match[1]); + const userMessage = match[2]?.trim(); + return userMessage ? `/${name}\n\n${userMessage}` : `/${name}`; +}