Skip to content

Commit 76fa2dc

Browse files
maweibinclaude
andcommitted
fix(onboard-skills): keep emoji / surrogate pairs intact during skill description truncation
Use truncateUtf16Safe() instead of .slice() to count full code points, preventing lone surrogates in onboarding skill summary output. Co-Authored-By: Claude Opus 4.8 <[email protected]>
1 parent 775ef96 commit 76fa2dc

2 files changed

Lines changed: 12 additions & 2 deletions

File tree

src/commands/onboard-skills.test.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// Onboard skills tests cover skill setup prompts, package manager config, and skip behavior.
2+
import { truncateUtf16Safe } from "@openclaw/normalization-core/utf16-slice";
23
import { beforeEach, describe, expect, it, vi } from "vitest";
34
import type { OpenClawConfig } from "../config/config.js";
45
import type { RuntimeEnv } from "../runtime.js";
@@ -509,4 +510,12 @@ describe("setupSkills", () => {
509510
expect(prompter.text).not.toHaveBeenCalled();
510511
expect(prompter.multiselect).not.toHaveBeenCalled();
511512
});
513+
514+
it("does not split surrogate pairs when truncating skill install failure text", () => {
515+
const suffix = "🚀tail";
516+
const content = "x".repeat(139) + suffix;
517+
const result = truncateUtf16Safe(content, 139) + "…";
518+
expect(result).toBe("x".repeat(139) + "…");
519+
expect(result).not.toContain(suffix.slice(0, 1));
520+
});
512521
});

src/commands/onboard-skills.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { truncateUtf16Safe } from "@openclaw/normalization-core/utf16-slice";
12
/**
23
* Interactive skill dependency setup for onboarding.
34
*
@@ -47,7 +48,7 @@ function summarizeInstallFailure(message: string): string | undefined {
4748
return undefined;
4849
}
4950
const maxLen = 140;
50-
return cleaned.length > maxLen ? `${cleaned.slice(0, maxLen - 1)}…` : cleaned;
51+
return cleaned.length > maxLen ? `${truncateUtf16Safe(cleaned, maxLen - 1)}…` : cleaned;
5152
}
5253

5354
function formatSkillHint(skill: {
@@ -61,7 +62,7 @@ function formatSkillHint(skill: {
6162
return "install";
6263
}
6364
const maxLen = 90;
64-
return combined.length > maxLen ? `${combined.slice(0, maxLen - 1)}…` : combined;
65+
return combined.length > maxLen ? `${truncateUtf16Safe(combined, maxLen - 1)}…` : combined;
6566
}
6667

6768
const SKIP_REASON_LABELS = {

0 commit comments

Comments
 (0)