Skip to content

Commit d750477

Browse files
lzyyzznlsteipete
andauthored
fix(migrate-claude): use truncateUtf16Safe for skill description truncation (#102616)
* fix(migrate-claude): use truncateUtf16Safe for skill description truncation One .slice(0, 180) truncation site in the migrate-claude extension may cut UTF-16 surrogate pairs in half when the skill description contains multi-byte characters such as emoji. Replace it with truncateUtf16Safe to keep the truncated output valid Unicode. * test(migrate-claude): cover UTF-16 skill description --------- Co-authored-by: Peter Steinberger <[email protected]>
1 parent 0aae1ea commit d750477

2 files changed

Lines changed: 14 additions & 5 deletions

File tree

extensions/migrate-claude/provider.test.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,11 @@ describe("Claude migration provider", () => {
133133
},
134134
}),
135135
);
136-
await writeFile(path.join(source, ".claude", "commands", "ship.md"), "Ship $ARGUMENTS\n");
136+
const commandDescriptionPrefix = "a".repeat(179);
137+
await writeFile(
138+
path.join(source, ".claude", "commands", "ship.md"),
139+
`${commandDescriptionPrefix}😀tail\n`,
140+
);
137141
await writeFile(path.join(source, ".claude", "skills", "Review", "SKILL.md"), "# Review\n");
138142

139143
const config = {
@@ -169,9 +173,13 @@ describe("Claude migration provider", () => {
169173
expect(await fs.readFile(path.join(workspaceDir, "AGENTS.md"), "utf8")).toContain(
170174
"Imported from Claude: project CLAUDE.md",
171175
);
172-
await expect(
173-
fs.access(path.join(workspaceDir, "skills", "claude-command-ship", "SKILL.md")),
174-
).resolves.toBeUndefined();
176+
const generatedSkill = await fs.readFile(
177+
path.join(workspaceDir, "skills", "claude-command-ship", "SKILL.md"),
178+
"utf8",
179+
);
180+
expect(generatedSkill.split("\n").find((line) => line.startsWith("description: "))).toBe(
181+
`description: ${JSON.stringify(commandDescriptionPrefix)}`,
182+
);
175183
await expect(
176184
fs.access(path.join(workspaceDir, "skills", "review", "SKILL.md")),
177185
).resolves.toBeUndefined();

extensions/migrate-claude/skills.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
MIGRATION_REASON_TARGET_EXISTS,
1010
} from "openclaw/plugin-sdk/migration";
1111
import type { MigrationItem } from "openclaw/plugin-sdk/plugin-entry";
12+
import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
1213
import { exists, readText, sanitizeName } from "./helpers.js";
1314
import type { ClaudeSource } from "./source.js";
1415
import type { PlannedTargets } from "./targets.js";
@@ -153,7 +154,7 @@ function generatedCommandSkillContent(params: {
153154
return [
154155
"---",
155156
`name: ${params.skillName}`,
156-
`description: ${JSON.stringify(description.slice(0, 180))}`,
157+
`description: ${JSON.stringify(truncateUtf16Safe(description, 180))}`,
157158
"disable-model-invocation: true",
158159
"---",
159160
"",

0 commit comments

Comments
 (0)