You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Currently skills, hooks, and other artifacts need to be uniquely named otherwise links break — when conflicts are detected, a plugin-name or hash prefix is appended to the artifact folder name. This only works for specific marketplaces where they follow unique naming standards.
This is fine for internal plugins since we control the naming. But doesn't work for public plugins like superpowers which have names conflicting with other plugins.
Current Behavior
When plugins are synced, skills are copied to client skill directories (e.g., .claude/skills/, .agents/skills/). The folder name on disk becomes the skill's identity — it's how clients discover and reference skills.
Current dedup logic (src/utils/skill-name-resolver.ts):
No conflict → folder name as-is (e.g., coding-standards/)
Folder name conflicts across plugins → {plugin}_{skill} (e.g., plugin-a_coding-standards/)
Folder AND plugin name conflict → {org}_{plugin}_{skill} (e.g., acme_plugin-a_coding-standards/)
Why this breaks
Dedup only kicks in when there's a conflict. This means the same skill gets different names depending on what other plugins are installed:
User installs superpowers plugin → skill lands as brainstorming/
User installs another plugin that also has brainstorming/ → both get renamed to superpowers_brainstorming/ and other_brainstorming/
Any hardcoded references to brainstorming (in AGENTS.md, other skills, etc.) break silently when step 2 happens.
Proposed Solution: Always-Namespaced Mode
Add a config option to always prefix skill names with the plugin namespace, regardless of conflicts:
Add mode param to resolveSkillNames(), always-qualify path
src/core/sync.ts
Thread config through buildPluginSkillNameMaps() calls (~lines 1841 and 2220)
tests/unit/utils/skill-name-resolver.test.ts
Add always mode tests (existing tests cover current tiers)
docs/src/content/docs/reference/configuration.mdx
Document new option
docs/src/content/docs/guides/plugins.mdx
Update dedup section
Implementation detail
In src/utils/skill-name-resolver.ts, add a mode parameter to resolveSkillNames(). When always, skip the "no conflict" fast path (line 90-101) and always qualify with plugin name.
Scope boundaries
Skills that reference other skills by name in their content (e.g., "run the brainstorming skill first") would still need to use the namespaced name. This is a documentation/convention problem, not a code problem.
The {org}_{plugin}_{skill} tier 3 disambiguator is still needed even in always mode if two plugins from different orgs have the same plugin name.
Related
PR fix(sync): deduplicate repo skills and rename heading #339 fixed a separate issue: repository skills (discovered from workspace repos, embedded as an index in AGENTS.md) had duplicates. Those now deduplicate by picking a winner (.agents/ priority, then largest file). That is a different mechanism from plugin skill dedup — repo skills are pointers in an index, not copied files.
Problem
Currently skills, hooks, and other artifacts need to be uniquely named otherwise links break — when conflicts are detected, a plugin-name or hash prefix is appended to the artifact folder name. This only works for specific marketplaces where they follow unique naming standards.
This is fine for internal plugins since we control the naming. But doesn't work for public plugins like superpowers which have names conflicting with other plugins.
Current Behavior
When plugins are synced, skills are copied to client skill directories (e.g.,
.claude/skills/,.agents/skills/). The folder name on disk becomes the skill's identity — it's how clients discover and reference skills.Current dedup logic (
src/utils/skill-name-resolver.ts):coding-standards/){plugin}_{skill}(e.g.,plugin-a_coding-standards/){org}_{plugin}_{skill}(e.g.,acme_plugin-a_coding-standards/)Why this breaks
Dedup only kicks in when there's a conflict. This means the same skill gets different names depending on what other plugins are installed:
superpowersplugin → skill lands asbrainstorming/brainstorming/→ both get renamed tosuperpowers_brainstorming/andother_brainstorming/Any hardcoded references to
brainstorming(in AGENTS.md, other skills, etc.) break silently when step 2 happens.Proposed Solution: Always-Namespaced Mode
Add a config option to always prefix skill names with the plugin namespace, regardless of conflicts:
auto(default)always{plugin}_{skill}namingImplementation Guide
Key files
src/models/workspace-config.tsskillNamespacefield to schemasrc/utils/skill-name-resolver.tsmodeparam toresolveSkillNames(), always-qualify pathsrc/core/sync.tsbuildPluginSkillNameMaps()calls (~lines 1841 and 2220)tests/unit/utils/skill-name-resolver.test.tsalwaysmode tests (existing tests cover current tiers)docs/src/content/docs/reference/configuration.mdxdocs/src/content/docs/guides/plugins.mdxImplementation detail
In
src/utils/skill-name-resolver.ts, add amodeparameter toresolveSkillNames(). Whenalways, skip the "no conflict" fast path (line 90-101) and always qualify with plugin name.Scope boundaries
brainstormingskill first") would still need to use the namespaced name. This is a documentation/convention problem, not a code problem.{org}_{plugin}_{skill}tier 3 disambiguator is still needed even inalwaysmode if two plugins from different orgs have the same plugin name.Related
.agents/priority, then largest file). That is a different mechanism from plugin skill dedup — repo skills are pointers in an index, not copied files.