From accabda65c75badcda49e04fab9421741ea222eb Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Wed, 11 Mar 2026 09:11:20 -0400 Subject: [PATCH] Skills: normalize emoji presentation across outputs --- src/cli/skills-cli.format.ts | 12 ++++++++---- src/cli/skills-cli.test.ts | 28 ++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/src/cli/skills-cli.format.ts b/src/cli/skills-cli.format.ts index dc335fb6c21..580f17b2d40 100644 --- a/src/cli/skills-cli.format.ts +++ b/src/cli/skills-cli.format.ts @@ -38,8 +38,12 @@ function formatSkillStatus(skill: SkillStatusEntry): string { return theme.error("✗ missing"); } +function normalizeSkillEmoji(emoji?: string): string { + return (emoji ?? "📦").replaceAll("\uFE0E", "\uFE0F"); +} + function formatSkillName(skill: SkillStatusEntry): string { - const emoji = (skill.emoji ?? "📦").replaceAll("\uFE0E", "\uFE0F"); + const emoji = normalizeSkillEmoji(skill.emoji); return `${emoji} ${theme.command(skill.name)}`; } @@ -154,7 +158,7 @@ export function formatSkillInfo( } const lines: string[] = []; - const emoji = skill.emoji ?? "📦"; + const emoji = normalizeSkillEmoji(skill.emoji); const status = skill.eligible ? theme.success("✓ Ready") : skill.disabled @@ -282,7 +286,7 @@ export function formatSkillsCheck(report: SkillStatusReport, opts: SkillsCheckOp lines.push(""); lines.push(theme.heading("Ready to use:")); for (const skill of eligible) { - const emoji = skill.emoji ?? "📦"; + const emoji = normalizeSkillEmoji(skill.emoji); lines.push(` ${emoji} ${skill.name}`); } } @@ -291,7 +295,7 @@ export function formatSkillsCheck(report: SkillStatusReport, opts: SkillsCheckOp lines.push(""); lines.push(theme.heading("Missing requirements:")); for (const skill of missingReqs) { - const emoji = skill.emoji ?? "📦"; + const emoji = normalizeSkillEmoji(skill.emoji); const missing = formatSkillMissingSummary(skill); lines.push(` ${emoji} ${skill.name} ${theme.muted(`(${missing})`)}`); } diff --git a/src/cli/skills-cli.test.ts b/src/cli/skills-cli.test.ts index 37323e7f21d..e87f8b2d313 100644 --- a/src/cli/skills-cli.test.ts +++ b/src/cli/skills-cli.test.ts @@ -148,6 +148,18 @@ describe("skills-cli", () => { expect(output).toContain("Any binaries"); expect(output).toContain("API_KEY"); }); + + it("normalizes text-presentation emoji selectors in info output", () => { + const report = createMockReport([ + createMockSkill({ + name: "info-emoji", + emoji: "🎛\uFE0E", + }), + ]); + + const output = formatSkillInfo(report, "info-emoji", {}); + expect(output).toContain("🎛️"); + }); }); describe("formatSkillsCheck", () => { @@ -170,6 +182,22 @@ describe("skills-cli", () => { expect(output).toContain("go"); // missing binary expect(output).toContain("npx clawhub"); }); + + it("normalizes text-presentation emoji selectors in check output", () => { + const report = createMockReport([ + createMockSkill({ name: "ready-emoji", emoji: "🎛\uFE0E", eligible: true }), + createMockSkill({ + name: "missing-emoji", + emoji: "🎙\uFE0E", + eligible: false, + missing: { bins: ["ffmpeg"], anyBins: [], env: [], config: [], os: [] }, + }), + ]); + + const output = formatSkillsCheck(report, {}); + expect(output).toContain("🎛️ ready-emoji"); + expect(output).toContain("🎙️ missing-emoji"); + }); }); describe("JSON output", () => {