[BUG] Project skill display in slash command but plugin skill doesn't

Open 💬 27 comments Opened Jan 10, 2026 by elct9620

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report (please file separate reports for different bugs)
  • [x] I am using the latest version of Claude Code

What's Wrong?

According to CHANGELOG.md, since v2.1.3, the slash command and skill are merged.

When skill is located in .claude/skills/my-skill, the /my-skill slash command is available.
However, when the skill is from a plugin e.g., plugin-skill, but /plugin:plugin-skill or /plugin-skill, both are not available.

What Should Happen?

Not sure which behavior is correct

| Type | Project | Plugin |
|------|---------|-------|
| Command | Visible | Visible |
| Skill | Current Visible | Current Invisible |

If the changelog is correct, all scenarios should be visible in /[command]

Error Messages/Logs

Steps to Reproduce

  1. Create a new plugin with plugin
  2. Install plugin in project-level or use --plugin-dir to load it
  3. Check /skills to ensure skill is available
  4. Check /[...] -> not visible if in the plugin

Claude Model

Opus

Is this a regression?

I don't know

Last Working Version

_No response_

Claude Code Version

2.1.3

Platform

Anthropic API

Operating System

macOS

Terminal/Shell

Other

Additional Information

_No response_

View original on GitHub ↗

27 Comments

natustx · 6 months ago

I'm seeing the same issue on macos. +1

aaaaaandrew · 6 months ago

Root Cause Analysis

I independently encountered this bug and did systematic testing to isolate the cause. Adding my findings here.

Finding: The name frontmatter field removes the plugin prefix

When a file in the commands array has a name field in its frontmatter, Claude Code:

  1. Strips the plugin namespace prefix
  2. Uses the name field value as the command identifier

Since skills require a name field in their frontmatter (it's part of the skill format), they lose their plugin prefix when added to the commands array and behave inconsistently in the autocomplete.

Test Results

All files placed in ./commands/ folder and added to the commands array:

| File | Frontmatter Fields | Result | Has Prefix? | In Autocomplete? |
|------|-------------------|--------|-------------|------------------|
| test-command-format.md | description, allowed-tools | /rdc-os:test-command-format | ✅ Yes | ✅ Yes |
| test-with-version.md | description, version, allowed-tools | /rdc-os:test-with-version | ✅ Yes | ✅ Yes |
| test-with-invocable.md | description, user-invocable, allowed-tools | /rdc-os:test-with-invocable | ✅ Yes | ✅ Yes |
| test-with-name.md | description, name, allowed-tools | /test-with-name | ❌ No | ✅ Yes |
| test-skill-format.md | description, name, version, user-invocable | /test-skill-format | ❌ No | ⚠️ Partial |

Additional Observations

  1. Skills with user-invocable: true in the skills array don't appear in autocomplete at all (even though docs suggest they should)
  2. Adding skill paths to the commands array as a workaround partially works - commands can be invoked by typing directly (e.g., /my-skill works) but only some appear in autocomplete dropdown
  3. The autocomplete seems to show only a subset of registered commands when there are many

Expected Behavior

The name field should set the identifier after the prefix:

  • Current: name: my-skill/my-skill
  • Expected: name: my-skill/plugin-name:my-skill

Or alternatively, user-invocable: true in skills should make them appear in autocomplete with proper namespacing, without needing the commands array workaround.

---
Cross-reference: I initially filed #17509 before finding this issue. Closing that as duplicate.

aaaaaandrew · 6 months ago

Workaround Found

For skills that need both auto-activation AND slash menu visibility with proper plugin prefix:

Steps:

  1. Remove name field from skill frontmatter (this causes the command to use the filename instead)
  2. Rename SKILL.md to <skillname>.md (e.g., sre.md)
  3. Create symlink: ln -s <skillname>.md SKILL.md (so the skills array still finds it for auto-activation)
  4. Add ./skills/<name>/<name>.md to the commands array in marketplace.json
  5. Keep ./skills/<name> in the skills array (for auto-activation)

Result:

  • /plugin-name:skillname appears in autocomplete with proper prefix ✅
  • Auto-activation still works (skills array finds SKILL.md symlink) ✅

Example structure:

skills/sre/
├── sre.md          # actual content, no "name" field in frontmatter
└── SKILL.md -> sre.md   # symlink for auto-activation

marketplace.json:

{
  "skills": ["./skills/sre"],           // for auto-activation
  "commands": ["./skills/sre/sre.md"]   // for slash menu
}

Downside: Maintenance burden - each patched skill needs file rename, symlink, and frontmatter edit. Will need to unwind once the bug is fixed.

Note on symlinks: Git supports symlinks on macOS/Linux. Windows users may need core.symlinks=true configured.

wkoutre · 6 months ago

Great find @aaaaaandrew 🙏🏼 A useful middle ground for the time being.

MrAshRhodes · 6 months ago

Experiencing the same issue.
But adding some info.

It appears on the VSCode extension it does see the slash commands without the need of a workaround.

choplin · 5 months ago

The same issue with v2.1.12

shntnu · 5 months ago

Workaround: Symlink plugin skills to ~/.claude/skills/

Finding: This bug only affects skills. When plugins are enabled in .claude/settings.json:

  • Commands work natively (/plugin:command appears in autocomplete) ✓
  • Agents work via Task tool with subagent_type="plugin:agent"
  • Skills can be invoked manually (/scientific-skills:anndata) but do not appear in autocomplete

Workaround: Symlink plugin skills to ~/.claude/skills/ - this makes them appear as "user skills" which do show in autocomplete.

Drawback: Symlinked skills lose the plugin namespace. They appear as /skillname rather than /plugin:skillname because skills have a name: field in their YAML frontmatter that controls the display name (overriding the symlink filename). To preserve namespacing, plugin authors would need to remove the name: field from their skills (see this comment for that approach).

Script: https://gist.github.com/shntnu/276e9b7309a14602955b3bda916d707c

# Create symlinks
./symlink_plugins.sh

# Remove symlinks (once bug is fixed)
./symlink_plugins.sh --cleanup
ts-shu · 5 months ago

The specific problem looks to be that directory skills and commands are treated differently than plugin skills

Root Cause

Two separate code paths for loading skills, visibility handled differently:

1. User Directory Skills (properly working)

Skills from .claude/commands/ and .claude/skills loaded in a way such that user-invocable frontmatter value is properly read

2. Plugin Skills (broken)

Skills from plugins (marketplaces and --plugin-dir) loaded in such a way that user-invocable frontmatter value is just ignored, defaulting to isHidden: true

isHidden controls the visibility in /help and general slash command completion. Since it's hardcoded to true for plugin skills, they just never appear - regardless of what user-invocable is set to in the frontmatter

Proposed Fix

Respect the user-invocable frontmatter value for plugin skills, potentially just change the default, certainly user-invocable: true should make plugin skills visible

k3KAW8Pnf7mkmdSMPHz27 · 5 months ago

Adding skill directories directly to the commands array works without symlinks or file renames.

{
  "skills": "./skills/",
  "commands": [
    "./skills/setup/",
    "./skills/commit/",
    "./skills/check/"
  ]
}

Keep the name field in SKILL.md frontmatter - it controls the display name in the slash menu. Directory paths (not SKILL.md files) in the commands array. Commands appear without plugin prefix, confirming @ts-shu's analysis that isHidden defaults to true for plugin skills regardless of user-invocable frontmatter.

Tested on v2.1.15, macOS.

LandonSchropp · 5 months ago

@k3KAW8Pnf7mkmdSMPHz27 That workaround works great! However, I've noticed two caveats:

  • The commands don't show up when the plugin is installed via claude plugin marketplace add and claude plugin install. I only tested this with a GitHub repo, so it's possible it works in other contexts.
  • The commands lose the plugin name prefix.

Not a big deal, but I thought I'd mention them.

cowwoc · 5 months ago

For what it's worth @k3KAW8Pnf7mkmdSMPHz27's workaround works perfectly for me as of v2.1.20 if I install the plugin using /plugin and if you omit the name property inside SKILL.md then the plugin name prefix works as expected.

abdcdd · 5 months ago

@k3KAW8Pnf7mkmdSMPHz27 That workaround works great! Thank you! One caveat I noticed:
With v2.1.14 on Linux: If a skill is added into the "commands" list, when it gets invoked by /<this_skill>, Claude will use the current working directory as its base dir, so if its SKILL.md uses relative paths like "python3 scripts/a.py", the path to the script can be messed up depending on where Claude is launched etc.
But if the skill is not added to the "commands" list and is invoked by natural language, Claude uses the skill's dir as its base dir, and relative paths like above can work.

aviramkofman · 5 months ago

@abdcdd @k3KAW8Pnf7mkmdSMPHz27 the solution works as far as it shows in the autocomplete slash commands but it appears that any more complex skills with scripts/reference files stop working since they ignore the files inside. Treating it as command is not sufficient.

Could a fix be prioritized? 🙏

Jamie-BitFlight · 5 months ago

Simpler Workaround Discovered

We independently discovered a simpler workaround that doesn't require symlinks or file renaming:

Just Remove the name Field

Discovery: Plugin skills appear correctly in slash autocomplete when the name: field is completely removed from frontmatter.

Verified:

  • ✅ Skill appears as /plugin-name:skill-name in autocomplete
  • ✅ Auto-activation still works (Claude loads skill when relevant)
  • ✅ Claude Code uses directory name automatically
  • ✅ No symlinks or file renaming needed

Before (broken):

---
name: my-skill          # <-- This PREVENTS slash command registration
description: Does something useful
user-invocable: true
---

Result: Skill does NOT appear in /plugin-name: autocomplete ❌

After (works):

---
description: Does something useful
user-invocable: true
---

Result: Skill appears as /plugin-name:my-skill in autocomplete ✅

Evidence

Tested on plugin with 14 skills:

  1. All skills had name: field - zero appeared in autocomplete
  2. Removed name: field from 3 skills - those 3 immediately appeared
  3. Removed name: from all 14 skills - all 14 now appear

Why This Works

Per @aaaaaandrew's root cause analysis, the name field strips the plugin prefix. The official docs say name is optional and defaults to directory name. Removing it entirely allows Claude Code to use the directory name AND preserve the plugin namespace.

Environment

  • Claude Code: v2.1.23
  • Platform: Linux
  • Plugin structure: Local marketplace

Recommendation

Until this bug is fixed, plugin developers should omit the name: field from skill frontmatter entirely. The field provides no benefit (directory name is used anyway) and actively breaks slash command registration.

PabloLION · 5 months ago

Tested the "remove name field" workaround on macOS with Claude Code CLI and --plugin-dir:

  • Autocomplete does NOT work — skills do not appear in / autocomplete after removing the name field from frontmatter

We verified skills are loaded correctly by invoking them directly with the full slash command (e.g., /bmad:teach-me-testing). This is how we've always checked skill loading — even before finding this issue and the commands array workaround.

Our current working workaround remains: adding skill directories to the commands array in plugin.json and prefixing the name field in SKILL.md frontmatter with the plugin name (e.g., name: bmad-teach-me-testing). This produces autocomplete entries like /bmad-teach-me-testing. The key goal is autocomplete visibility — without it, users have to know and type the exact command name.

Environment: macOS, Claude Code CLI, local plugin via --plugin-dir.

patrik-stravito · 5 months ago

@PabloLION That is my experience as well.
I can invoke the skill (by typing if fully as a slash command), but autocomplete does not work

doodledood · 5 months ago

@PabloLION I also can attest this doesn't work

I think may be a difference between a locally installed plugin vs GitHub installed.

My setup is installed through GitHub and this doesn't work for me

The symlink approach does work partially (I see the skills in the autocomplete) but it also shuts down the ability for the skill to utilize reference files like packaged scripts etc since it treats it as a command

doodledood · 5 months ago

Ok. I found a cleaner solution that works for complex skills with supporting files and subdirectories.

Instead of using the symlink workaround, put thin wrapper commands in the commands/ folder with disable-model-invocation: true, and move the actual implementation to hidden skills in skills/_name/ with user-invocable: false.

The wrapper just says "Invoke the plugin:_name skill with $ARGUMENTS". Commands get proper namespace prefixes in autocomplete, the hidden skills stay out of the menu but can still be invoked programmatically, and you keep full support for reference files, task subdirectories, etc.

dnlopes · 5 months ago

This issue seems fixed on v2.1.29, can you confirm? At least, I did not do any workaround and suddenly skills start to appear on the command palette and autocomplete.

elct9620 · 5 months ago
This issue seems fixed on v2.1.29, can you confirm? At least, I did not do any workaround and suddenly skills start to appear on the command palette and autocomplete.

I can see the autocomplete in v2.1.29

patrik-stravito · 5 months ago
This issue seems fixed on v2.1.29, can you confirm? At least, I did not do any workaround and suddenly skills start to appear on the command palette and autocomplete.

Yes, I can confirm it works for me as well now with v2.1.29

PabloLION · 5 months ago

Update: Confirmed fixed in v2.1.29 🎉

Following up on my earlier test — the fix is now working. We've successfully removed all workarounds from our plugin:

  1. Removed name: bmad-* from all 34 SKILL.md frontmatters
  2. Removed the entire commands array from plugin.json

All skills now appear correctly in /bmad: autocomplete with the proper namespace format.

Simple test to verify:

cd "$(mktemp -d)" && claude --plugin-dir /path/to/your/plugin
# Type /plugin-name: and check autocomplete

Environment: macOS, Claude Code CLI v2.1.29, local plugin via --plugin-dir

This issue can likely be closed now. Thanks for the fix!

maigentic · 4 months ago

Adding a real-world use case that highlights the severity of this issue at scale.

I maintain StratArts, a marketplace plugin with 27 business strategy skills (e.g., business-idea-validator, market-opportunity-analyzer, brand-identity-designer, etc.).

Because the name frontmatter field strips the plugin namespace prefix, all 27 skills appear in the / autocomplete menu as standalone entries (/business-idea-validator, /brand-identity-designer, etc.) instead of namespaced entries (/stratarts:business-idea-validator, /stratarts:brand-identity-designer).

This creates two problems:

  1. Menu clutter: 27 skills are scattered alphabetically throughout the command list, mixed in with built-in commands and other plugins' skills. Users can't type /stratarts to filter to just our plugin's skills.
  1. Collision risk: Generic skill names like competitive-intelligence or pricing-strategy-architect could easily collide with skills from other plugins as the marketplace grows.

The symlink workaround described above is not practical for 27 skills — it would require 27 renames, 27 symlinks, 27 frontmatter edits, and a 27-entry commands array in plugin.json. That's a significant maintenance burden.

Preferred fix: The name field in SKILL.md frontmatter should preserve the plugin namespace prefix, so /stratarts:business-idea-validator appears in autocomplete instead of /business-idea-validator. This is how commands/*.md already works — skills should behave identically.

arledesma · 4 months ago

Simply removing the name from the skill did work for me with a local plugin.

  1. Remove name: ... from plugin skill
  2. commit
  3. claude plugin update plugin@my-marketplace
  4. profit

cc 2.1.49

https://github.com/anthropics/claude-code/issues/17271#issuecomment-3818154966

## Simpler Workaround Discovered We independently discovered a simpler workaround that doesn't require symlinks or file renaming: ### Just Remove the name Field Discovery: Plugin skills appear correctly in slash autocomplete when the name: field is completely removed from frontmatter. Verified: ✅ Skill appears as /plugin-name:skill-name in autocomplete ✅ Auto-activation still works (Claude loads skill when relevant) ✅ Claude Code uses directory name automatically ✅ No symlinks or file renaming needed Before (broken): --- name: my-skill # <-- This PREVENTS slash command registration description: Does something useful user-invocable: true --- Result: Skill does NOT appear in /plugin-name: autocomplete ❌ After (works): --- description: Does something useful user-invocable: true --- Result: Skill appears as /plugin-name:my-skill in autocomplete ✅ ### Evidence Tested on plugin with 14 skills: 1. All skills had name: field - zero appeared in autocomplete 2. Removed name: field from 3 skills - those 3 immediately appeared 3. Removed name: from all 14 skills - all 14 now appear ### Why This Works Per @aaaaaandrew's root cause analysis, the name field strips the plugin prefix. The official docs say name is optional and defaults to directory name. Removing it entirely allows Claude Code to use the directory name AND preserve the plugin namespace. ### Environment Claude Code: v2.1.23 Platform: Linux * Plugin structure: Local marketplace ### Recommendation Until this bug is fixed, plugin developers should omit the name: field from skill frontmatter entirely. The field provides no benefit (directory name is used anyway) and actively breaks slash command registration.
doitallwrong · 3 months ago

Hit this bug with a plugin that has 14 skills, all with name: in frontmatter. Decompiled cli.js, found the exact line, patched it, verified it works. Here's everything.

---

TL;DR — Problem: userFacingName() returns "my-skill" instead of "my-plugin:my-skill". Autocomplete uses userFacingName() for matching, so /my-plugin:my-skill never matches — falls back to the last registered skill.

TL;DR — Fix (verified on v2.1.81):

sed -i 's/userFacingName(){return Z||A}/userFacingName(){return A}/g' cli.js

One unique string in the file. Restart Claude Code. Done.

---

What happens

Typing /my-plugin: in autocomplete always resolves to the same wrong skill — whichever was added last to the plugin. It's not random:

  1. Plugin had 13 skills — /my-plugin:anything always resolved to skill-13 (the last one added)
  2. Added a 14th skill — /my-plugin:anything now always resolves to skill-14
  3. Doesn't matter what I type after /my-plugin: — it always picks the same one

What works fine:

  • /my-skill (without prefix) → shows disambiguation menu, I pick the right one ✓
  • Skill("my-plugin:my-skill") via the Skill tool → resolves correctly ✓

So the slash command autocomplete is broken, but the internal Skill tool resolution isn't. That's what pointed me to userFacingName() vs name as two separate code paths.

This is NOT the same bug fixed in v2.1.29

v2.1.29 fixed plugin skills not appearing in autocomplete (isHidden). This is different — skills appear but /plugin:skill resolves to the wrong one when name: is in frontmatter.

| Bug | Fixed in v2.1.29? | Status |
|-----|-------------------|--------|
| Plugin skills don't appear in autocomplete | Yes | Fixed |
| /plugin:skill resolves to wrong skill | No | Still broken in v2.1.81 |

Why "remove name:" doesn't work for everyone

The workaround adopted by most people in this thread works for simple skills. But it breaks skills that rely on name for:

  • Cross-skill invocation via the Skill tool
  • Display name customization (directory name isn't always what you want)
  • Skill descriptions referencing other skills by name

---

Reproduction

my-plugin/
├── .claude-plugin/
│   └── plugin.json          # { "name": "my-plugin" }
└── skills/
    ├── my-skill/
    │   └── SKILL.md         # name: my-skill
    └── other-skill/
        └── SKILL.md         # name: other-skill

| Action | Expected | Actual |
|--------|----------|--------|
| Type /my-plugin:my-skill in autocomplete | Resolves to my-skill | Always resolves to last skill added |
| Type /my-skill in autocomplete | Shows disambiguation | Shows disambiguation ✓ |
| Skill tool: Skill("my-plugin:my-skill") | Loads my-skill | Loads my-skill ✓ |
| skill.name | "my-plugin:my-skill" | "my-plugin:my-skill" ✓ |
| skill.userFacingName() | "my-plugin:my-skill" | "my-skill" ✗ |

---

Root Cause (source code)

Decompiled cli.js v2.1.81. The plugin skill constructor builds the name correctly but userFacingName() drops the prefix.

Variable names Z and A are the actual minified names from cli.js:

// Function Oa6 — plugin skill object constructor

let Z = frontmatter.name;  // Z = "my-skill" (from YAML, no prefix)
// A = first parameter    // A = "my-plugin:my-skill" (computed by zc_, includes prefix)

return {
  name: A,                   // "my-plugin:my-skill" ✓ — Skill tool uses this

  userFacingName() {
    return Z || A;
    // BUG: Z = "my-skill" is truthy → returns "my-skill" without prefix
    // Autocomplete matches against this → /my-plugin:my-skill never matches
    // All skills return bare names → fallback picks the last registered one
  },
};

---

Fix

The pattern userFacingName(){return Z||A} occurs exactly once in cli.js (v2.1.81).

Three options compared:

| | Code | Example: dir=my-skill/, frontmatter name: cool-skill | Notes |
|---|---|---|---|
| Current (broken) | return Z \|\| A | "cool-skill" — no prefix | Autocomplete can't match /my-plugin:* |
| Option 1 (tested) | return A | "my-plugin:my-skill" — uses directory | Ignores frontmatter name for display. Works when dir name = frontmatter name. |
| Option 2 (recommended) | return Z ? prefix+':'+Z : A | "my-plugin:cool-skill" — uses frontmatter | Preserves custom name with prefix. Respects plugin author's intent. |

Option 1 — always use the computed name (this is what I tested):

userFacingName() { return A; }

Option 2 — preserve frontmatter name, add plugin prefix:

userFacingName() {
  if (Z && A.includes(':')) {
    let prefix = A.substring(0, A.lastIndexOf(':'));
    return `${prefix}:${Z}`;
  }
  return Z || A;
}

---

How to apply option 1 (all OS)

  1. Find cli.js:
# Windows (Git Bash):
CLI_JS="$(find "$LOCALAPPDATA/Temp" -path '*/claude-code-src/package/cli.js' 2>/dev/null | head -1)"
# macOS:
CLI_JS="$(find "$TMPDIR" -path '*/claude-code-src/package/cli.js' 2>/dev/null | head -1)"
# Linux:
CLI_JS="$(find /tmp -path '*/claude-code-src/package/cli.js' 2>/dev/null | head -1)"
echo "$CLI_JS"  # If empty, run `claude` once first to extract it
  1. Verify pattern exists (should print 1):
grep -c 'userFacingName(){return Z||A}' "$CLI_JS"
  1. Apply:
sed -i 's/userFacingName(){return Z||A}/userFacingName(){return A}/g' "$CLI_JS"
  1. Verify fix (should print 1):
grep -c 'userFacingName(){return A}' "$CLI_JS"
  1. Restart Claude Code, type /my-plugin:my-skill — resolves correctly.

Quick test without patching your install:

cp "$CLI_JS" "$(dirname "$CLI_JS")/cli-fixed.js"
sed -i 's/userFacingName(){return Z||A}/userFacingName(){return A}/g' "$(dirname "$CLI_JS")/cli-fixed.js"
node "$(dirname "$CLI_JS")/cli-fixed.js"
# Type /my-plugin:my-skill — resolves correctly. Exit and your original is untouched.
Note: the binary may re-extract cli.js on update, so the patch needs reapplying after Claude Code updates.

---

Related Issues

All closed as duplicates of this one:

  • #20994 — Skill auto-namespacing does not work as documented
  • #22063 — Plugin skills with name in frontmatter lose plugin prefix
  • #15882 — Plugin commands always namespaced (contradicts docs)
  • #26436 — Skill tool description teaches short names
doitallwrong · 3 months ago

Before fix — /my-plugin: always resolves to the wrong skill:

<img width="615" height="128" alt="Image" src="https://github.com/user-attachments/assets/b2a2884a-5a25-4d7b-a83d-482e76e3b6a5" />

After fix — /my-plugin: resolves correctly:

<img width="531" height="234" alt="Image" src="https://github.com/user-attachments/assets/d2d2cf7e-e9f4-4194-83b4-e2c9df363e98" />

yurukusa · 3 months ago

This is the same issue as #18949 — plugin-installed skills don't appear in slash command autocomplete, while locally installed skills do.
See workarounds in my comment on #18949: https://github.com/anthropics/claude-code/issues/18949#issuecomment-4159551466
TL;DR:

  1. Symlink plugin skills to ~/.claude/skills/ for autocomplete
  2. Invoke directly by typing the full skill name or asking Claude
  3. Create a thin wrapper skill locally that delegates to the plugin skill