diff --git a/webui/src/components/thread/ThreadComposer.tsx b/webui/src/components/thread/ThreadComposer.tsx
index 4fb0f4ab..23757353 100644
--- a/webui/src/components/thread/ThreadComposer.tsx
+++ b/webui/src/components/thread/ThreadComposer.tsx
@@ -286,6 +286,7 @@ interface SlashPaletteCommand {
title: string;
description: string;
icon: string;
+ kind?: "skill";
argHint?: string;
detail: string;
badge?: string;
@@ -1009,6 +1010,7 @@ export function ThreadComposer({
description,
detail: description,
icon: "brain",
+ kind: "skill" as const,
recent: recentSlashCommands.includes(command),
};
})
@@ -2582,6 +2584,7 @@ function SlashCommandPalette({
{commands.map((command, index) => {
const Icon = COMMAND_ICONS[command.icon] ?? CircleHelp;
const selected = index === selectedIndex;
+ const isSkill = command.kind === "skill";
const commandKey = slashCommandI18nKey(command.command);
const title = t(`thread.composer.slash.commands.${commandKey}.title`, {
defaultValue: command.title,
@@ -2617,23 +2620,34 @@ function SlashCommandPalette({
-
+
{title}
{command.detail || description}
-
- {command.badge || command.recent ? (
-
- {command.badge ?? t("thread.composer.slash.badges.recent")}
-
- ) : null}
-
- {command.argHint ? `${command.command} ${command.argHint}` : command.command}
+ {!isSkill || command.badge || command.recent ? (
+
+ {command.badge || command.recent ? (
+
+ {command.badge ?? t("thread.composer.slash.badges.recent")}
+
+ ) : null}
+ {!isSkill ? (
+
+ {command.argHint ? `${command.command} ${command.argHint}` : command.command}
+
+ ) : null}
-
+ ) : null}
);
})}
diff --git a/webui/src/tests/thread-composer.test.tsx b/webui/src/tests/thread-composer.test.tsx
index d3727a4b..1fa83010 100644
--- a/webui/src/tests/thread-composer.test.tsx
+++ b/webui/src/tests/thread-composer.test.tsx
@@ -1146,14 +1146,15 @@ describe("ThreadComposer", () => {
});
});
- it("opens skills only from a $ reference anywhere", () => {
+ it("opens skills only from a $ reference and prioritizes the skill name", () => {
+ const skillName = "arxiv-intelligence-filter";
render(
{
fireEvent.change(input, { target: { value: "/git", selectionStart: 4 } });
expect(screen.queryByRole("listbox", { name: "Slash commands" })).not.toBeInTheDocument();
- fireEvent.change(input, { target: { value: "please use $git", selectionStart: 15 } });
+ fireEvent.change(input, { target: { value: "please use $arxiv", selectionStart: 17 } });
const palette = screen.getByRole("listbox", { name: "Slash commands" });
- expect(within(palette).getByRole("option", { name: /github/i })).toHaveTextContent("$github");
+ const option = within(palette).getByRole("option", { name: new RegExp(skillName, "i") });
+ const name = within(option).getByText(skillName);
+ expect(name).not.toHaveClass("truncate");
+ expect(within(option).queryByText(`$${skillName}`)).not.toBeInTheDocument();
expect(within(palette).queryByText("/model")).not.toBeInTheDocument();
fireEvent.keyDown(input, { key: "Tab" });
- expect(input).toHaveValue("please use $github ");
+ expect(input).toHaveValue(`please use $${skillName} `);
});
it("shows right-side source badges so users can distinguish CLI apps from MCP servers", () => {