Plugin skill names with non-ASCII (e.g. Japanese) characters are sanitized to hyphens, causing identifier collisions and "Unknown skill" on Skill-tool invocation
Summary
When a plugin bundles skills whose directory names (and SKILL.md frontmatter name) contain non-ASCII characters (Japanese, in our case), Claude Code generates the namespaced skill identifier by replacing each non-ASCII character with a single hyphen - while preserving ASCII characters. This produces two problems:
- Identifier collisions — every skill with the same number of non-ASCII characters collapses to the same identifier. In our plugin, all 13 two-character-named skills become the single identifier
my-plugin:--. Unknown skillon Skill-tool invocation — the assistant calling the Skill tool with the actual name (my-plugin:残量) fails withUnknown skill: my-plugin:残量, because the registered identifier is the sanitized/collidedmy-plugin:--. The available-skills list injected into the model context likewise shows every such skill as the sanitized colliding name, making them indistinguishable to the model.
The same SKILL.md works correctly as a personal skill (~/.claude/skills/残量/SKILL.md, un-namespaced): typing /残量 resolves and runs. The defect is specific to the plugin namespacing path.
Environment
| Item | Value |
|---|---|
| Claude Code | 2.1.169 |
| Node | v24.11.1 |
| OS | macOS 26.5 (build 25F71) |
| Plugin source | local directory-type marketplace |
| Plugin | a plugin bundling ~23 skills under skills/<name>/SKILL.md |
Steps to reproduce
- Create a plugin with a directory-type marketplace.
- Add two or more skills whose directory names are non-ASCII and have the same character count, e.g.:
skills/残量/SKILL.md(frontmattername: 残量)skills/改善/SKILL.md(frontmattername: 改善)
- Install/enable the plugin (
claude plugin install ...) and start a session. - Inspect the available skills, and have the assistant attempt
Skill("my-plugin:残量").
Observed behavior
- In the available-skills list, the skills appear under sanitized, colliding identifiers. Each non-ASCII char → one
-, ASCII preserved. Actual examples observed in our plugin (raphael-skills): 残量/改善/調査(2 chars) →raphael-skills:--マージ(3) →raphael-skills:---コミット/要件定義(4) →raphael-skills:----E2Eテスト(E2E + テスト) →raphael-skills:E2E---PRP仕様書(PRP + 仕様書) →raphael-skills:PRP---- Assistant Skill-tool call
Skill("raphael-skills:残量")→Unknown skill: raphael-skills:残量. - A user typing the slash form
/raphael-skills:--does resolve (the slash menu disambiguates by description) and loads the intended skill — confirming the registered command name is the sanitized--, not the original残量.
Expected behavior
- The namespaced identifier should preserve the skill's (Unicode) name, e.g.
raphael-skills:残量, consistent with how personal skills already behave (~/.claude/skills/残量/→/残量works). - The model-facing identifier and the actually-invokable name must match.
- If sanitization is intentional, it must be collision-free and documented.
Impact
- Plugins localized with non-ASCII skill names are effectively un-invocable by name: the assistant cannot call them (
Unknown skill), and multiple skills become indistinguishable in both the identifier and the model-facing list. - Inconsistency: identical
SKILL.mdcontent works as a personal skill but breaks as a plugin skill.
Docs reference
- "How a skill gets its command name" (code.claude.com/docs/en/skills): plugin
skills/subdirectory command name = directory name, namespaced by plugin. There is no documented mention of non-ASCII sanitization. - Plugin manifest schema (code.claude.com/docs/en/plugins-reference): the plugin
nameis described as "Unique identifier (kebab-case, no spaces)" — this constrains the plugin name, and does not state that skill names must be ASCII or that non-ASCII is prohibited.
Possibly related (not confirmed same root cause)
- #14873 — UTF-8 multi-byte handling in skill descriptions
- #34144 — plugin skill slash commands return
Unknown skillunder certain config
Suggested fix direction
Either preserve Unicode in the generated namespaced identifier, or apply a documented, collision-free transliteration; in all cases ensure parity with personal-skill resolution and that the model-facing identifier equals the invokable name.