Skip to content

fix(marketplace): restore /plugin install path and namespace per-skill entries#46

Merged
tw93 merged 1 commit into
tw93:mainfrom
qishaoyumu:fix/plugin-install-path
May 5, 2026
Merged

fix(marketplace): restore /plugin install path and namespace per-skill entries#46
tw93 merged 1 commit into
tw93:mainfrom
qishaoyumu:fix/plugin-install-path

Conversation

@qishaoyumu

@qishaoyumu qishaoyumu commented May 4, 2026

Copy link
Copy Markdown
Contributor

fix(marketplace): restore /plugin install path and namespace per-skill entries

背景

v3.12.2 之后,/plugin install 这条路径处于静默失败状态:marketplace add 和 plugin install 命令都报告成功,但装完之后没有任何 slash 命令变得可用。这个 PR 让 marketplace plugin 路径完整可用,并在 README 里补全两条主流 host(Claude Code、Codex)的安装范例。

这次修复的思路来自 tw93/Kami#16(已合并)。那条 PR 解决了 Kami 单 skill 扁平仓库下同类的 /plugin install 路径校验问题:把字段从 plugin.json 移到 marketplace entry,加上 skills: ["./"] + strict: false,让 Anthropic plugin loader 找到 plugin 根的 SKILL.md。同一套字段组合在那条 PR 上验证有效;本 PR 把它泛化到 Waza 的多 skill 拓扑(bundle + 8 个 per-skill 入口),并配套处理命名空间和 verify 守卫。

现象

/plugin marketplace add tw93/Waza
/plugin install think@waza
✓ Installed think. Run /reload-plugins to apply.
/reload-plugins
Reloaded: ... plugins ... skills ...

命令报告成功,reload 不报错,但 /help 里没有任何新增 slash 命令,/think:think/think 都不存在。/plugin install check@waza 等其余 7 条 per-skill 入口同样表现。

诊断

1. 8 个独立入口装上后注册 0 个 skill

marketplace.json 里 8 条 per-skill 入口都声明 source: "./skills/<name>",但每个目录都是扁平的:SKILL.md 直接在那一层下,下面没有再嵌套 .claude-plugin/plugin.json,也没有 skills/<name>/SKILL.md

Claude Code 默认的 plugin 自动发现规则只扫 <plugin_root>/skills/<name>/SKILL.md<plugin_root>/commands/*.md。这些 per-skill plugin 装上后发现机制找不到任何 skill 文件,注册数量为 0。安装命令本身不报错(marketplace 入口存在),但加载阶段静默失败。

2. bundle 入口在 v3.12.2 被一并误删

v3.12.2 的 commit c9851f6 fix: keep desktop dispatcher out of source root 一次做了两件事,把它们的对错分开看:

(a) 删除仓库根的 SKILL.md:方向正确。

仓库根曾经存在的 dispatcher SKILL.mdnpx skills add tw93/Waza 默认只扫到根那一层就停。npx CLI 默认找到根 SKILL.md 就装它,不会继续往子目录扫。issue #44 报告的"升级后只剩 /waza"就是这条机制的副产品,多 skill 仓库不应该有根 SKILL.md。删掉它(动态生成逻辑搬到 package-skill.sh),npx 默认行为回到扫子目录,能正常装出 8 个 skill。

(b) 同时删除 marketplace.json 里的 waza bundle 入口:连带误删。

release notes 第 4 条原话:

Removed the waza@waza root bundle entry; marketplace installs remain available through individual entries such as think@waza.

这条结论建立在一个隐含前提上:bundle 入口"只服务于根 dispatcher SKILL.md,根 SKILL.md 没了 bundle 也没用"。

这个前提不准确。bundle 入口的 source: "./" 配合仓库已经存在的 8 个 skills/<name>/SKILL.md 子目录,自动发现机制会注册 8 个 skill 到 waza namespace 下:/waza:think/waza:check/waza:hunt/waza:design/waza:read/waza:write/waza:learn/waza:health。dispatcher SKILL.md 对这条路径并非必需,bundle 不依赖根 SKILL.md,连带它一起删除是无意之举。

release notes 同段说"marketplace installs remain available through individual entries such as think@waza"也不成立。实测 v3.12.2 主干上 think@waza 装出来注册 0 skill,就是诊断 1 描述的失败模式。

3. 命名空间为什么必须用 waza-<skill> 前缀

Claude Code plugin 的调用规则是 <plugin_name>:<skill_name>,每个 marketplace 入口的 name 在 plugin 系统下都是一个独立 plugin,有独立 namespace、独立安装、独立卸载。三种入口的命名结果:

  • bundle 入口 name: "waza":装 /plugin install waza@waza 后,8 个子 skill 落在 /waza: namespace 下(/waza:think/waza:check 等)。namespace 直接就叫 waza,不需要额外前缀。
  • per-skill 入口保持原 skill 名thinkcheck...):plugin name 直接是 think,调用名变成 /think:think,同字重复。这种裸名在用户全局 plugin 列表里很容易和其他来源的同名 plugin 撞名(Anthropic 官方插件、其他社区插件都可能用同样的名字)。
  • per-skill 入口加 waza- 前缀waza-thinkwaza-check...):plugin name 是 waza-think,调用名变成 /waza-think:think,和 bundle 的 /waza: 同属 waza 命名空间。用户记一种规则就够,全局也不容易撞名。

bundle 不需要前缀因为它本身就叫 waza;per-skill 必须前缀,因为每条都是独立 plugin,需要稳定的命名空间避免冲突。

修复

marketplace.json

  • 恢复 name: "waza"source: "./" 的 bundle 入口(version: "3.12.2"
  • 8 个 per-skill 入口的 name 改成 waza-<skill>waza-thinkwaza-checkwaza-huntwaza-designwaza-readwaza-writewaza-learnwaza-health
  • 每个 per-skill 入口加 skills: ["./"] + strict: false

skills: ["./"] 告诉 plugin loader:"SKILL.md 直接位于 plugin 根,而不是 skills/<name>/ 子目录"。
strict: false 告诉 Claude Code:"marketplace 入口本身就是完整 plugin 定义,不需要额外的 plugin.json"。

两个字段都是官方 plugin reference 明确支持的写法:

scripts/verify-skills.sh

marketplace 校验段重写以支持新拓扑:

  • bundle 入口现在是必须存在的(一键全装是项目契约的一部分),其 source 必须严格为 "./"
  • per-skill 入口必须命名为 waza-<skill> 且 source 必须为 ./skills/<skill>,空后缀(如 name: "waza-")会被独立守卫拦下
  • version 和 description 跨表对齐检查的 keying 改为基于 skill 目录名(从 plugin name 移除 waza- 前缀派生)

旧守卫 if name == "waza": fail("MARKETPLACE BUNDLE REMOVED: ...") 被删除,它和恢复 bundle 这条主线直接冲突。它原本要防止的(dispatcher 复活进源码树)现在由 bundle 入口 source 锁死为 "./" 这条新守卫继续兜底。

README 默认 npx install 命令的 grep guard 不动。

Makefile

smoke-verify-skills target 的两个 fixture 同步更新到新命名拓扑:repo2 fixture 的 plugin name 从 ghost 改成 waza-ghost(让它先通过 INVALID PLUGIN NAME 守卫,再被 MISSING SKILL DIRECTORY: ghost 拦下,保持 fixture 原意图),repo3 fixture 找 name == 'check' 改成 name == 'waza-check',对应 grep keyword 改为 WRONG SOURCE: waza-check。其他 4 个 fixture(frontmatter / broken link / RESOLVER / pipe)不受拓扑变化影响,保持不动。

README.md

在已有的 ## Install and Update 段落里:

  • Claude Code direct slash commands:补一条单装范例 npx skills add tw93/Waza --skill think -a claude-code -g -y
  • Claude Code plugin marketplace:把原来单条的 /plugin install think@waza 改成 bundle + per-skill 双入口,前面加一句 prose 解释"waza 装全部、waza-<skill> 装单个"
  • Codex:补一条单装范例 --skill think

默认 npx 命令保留 v3.12.2 的形态(不带 --full-depth)。v3.12.2 删除根 SKILL.md 后,npx CLI 默认就会扫到 skills/<name>/SKILL.md。本地用 npx skills add tw93/Waza -l 做 dry-run,带和不带 --full-depth 两种都报告 Found 8 skills

验证

四条安装路径全部端到端测试通过,每条都从 clean uninstall + marketplace remove 起步:

# 路径 命令 结果
1 本地仓库 marketplace /plugin marketplace add /Users/cyrus/projects/forks/Waza + /plugin install waza@waza OK,8 个 /waza:* 全部注册
2a 修复分支远程 marketplace(bundle) /plugin marketplace add qishaoyumu/Waza@fix/plugin-install-path + /plugin install waza@waza OK,/waza:think 触发 skill,base dir ~/.claude/plugins/cache/waza/waza/3.12.2/skills/think
2b 远程 marketplace + 单装 think /plugin install waza-think@waza OK,/waza-think:think 触发 skill,base dir ~/.claude/plugins/cache/waza/waza-think/3.17.0
2c 远程 marketplace + 单装 check /plugin install waza-check@waza OK,/waza-check:check 注册
2d 反向(v3.12.2 已不存在的入口) /plugin install think@waza 按预期失败:Plugin "think" not found in marketplace "waza"
3 Claude Desktop ZIP 本地 bash scripts/package-skill.sh 重打 + 上传 dist/waza.zip OK,安装并调用 skill 表现与 v3.12.2 一致
4a npx skills 全装 npx skills add tw93/Waza -a claude-code -g -y OK,dry-run 报告 Found 8 skills
4b npx skills 单装 npx skills add tw93/Waza --skill think -a claude-code -g -y OK,dry-run 显示仅 think 入选

/reload-plugins 在每条路径下都报 0 error。bash scripts/verify-skills.sh 通过(rc=0)。

本地闭环测试(16 fixtures,不进 PR)

另起一份 16 case 闭环测试覆盖 PR 涉及的全部 fail 路径:baseline、bundle 缺失/重复/source 错、per-skill 缺前缀/空后缀/source 错位、缺 entry、多余 entry、version/description mismatch、plugin 缺 name/version/description、README 缺默认 npx 命令。15 条 fail case 全部按预期触发对应 keyword,1 条 baseline PASS。

Claude Desktop ZIP 不受 PR 影响的证据

unzip -l dist/waza.zip | grep -i "marketplace"   # 输出 none

.claude-plugin/ 整个目录被 package-skill.sh 的 awk filter 排除,zip 里没有 marketplace.json,根 dispatcher SKILL.md 仍然仅在 make package 时动态生成。

不影响的范围

  • npx skills add CLI 路径:不读 marketplace.json,仍按 v3.12.2 的源码布局扫到 skills/<name>/SKILL.md
  • Claude Desktop ZIP 路径:见上文验证,zip 内容与 v3.12.2 一致
  • skills/<name>/SKILL.md 内容和 frontmatter:完全未动
  • scripts/package-skill.sh:完全未动
  • verify-skills.sh 中其他守卫:SKILL.md schema、RESOLVER.md 覆盖、description 长度、root-SKILL.md 守卫、README install command 守卫、English coaching 守卫,全部未动

Scope

.claude-plugin/marketplace.json | 56 +++++++++++++++++++++++++++++------------
Makefile                        |  6 ++---
README.md                       | 21 +++++++++++++---
scripts/verify-skills.sh        | 44 ++++++++++++++++++++++++++++----
4 files changed, 99 insertions(+), 28 deletions(-)

单 commit,分支 fix/plugin-install-path 基于 upstream/main HEAD a1acdba,0 behind / 1 ahead。

…l entries

The /plugin install path on v3.12.2 silently fails. Marketplace add and
plugin install commands report success, but no slash command becomes
available afterward. This change makes the marketplace plugin path work
end-to-end and adds the missing install examples to README.

Per-skill entries register zero skills today
--------------------------------------------

The eight per-skill entries declare source: ./skills/<name>, but each
subdirectory is flat: SKILL.md sits directly under it, with no
.claude-plugin/plugin.json and no nested skills/<name>/SKILL.md. Claude
Code's default auto-discovery only scans <plugin_root>/skills/<name>/SKILL.md
and <plugin_root>/commands/*.md, so the per-skill plugins install without
error but register zero skills.

Add skills: ["./"] and strict: false to each per-skill entry. strict: false
tells Claude Code that the marketplace entry is the full plugin definition,
so a separate plugin.json is not required. skills: ["./"] tells the plugin
loader that SKILL.md lives at the plugin root rather than in a
skills/<name>/ subdirectory. Both fields are documented in the official
plugin reference:

  skills: ["./"] — https://code.claude.com/docs/en/plugins-reference#path-behavior-rules
  strict: false — https://code.claude.com/docs/en/plugin-marketplaces#strict-mode

Bundle entry was removed by mistake
-----------------------------------

v3.12.2 removed the waza bundle entry as part of "keep desktop dispatcher
out of source root." That commit also removed the repo-root SKILL.md so
npx skills add tw93/Waza could discover the eight skills/<name>/
subdirectories without --full-depth. The bundle entry was removed
alongside it because it was assumed to only register the dispatcher
SKILL.md.

That assumption is incorrect. With source: "./", the plugin loader's
default auto-discovery walks the plugin root for skills/<name>/SKILL.md,
exactly the same eight subdirectories the v3.12.2 layout already exposes.
Installing the bundle therefore registers all eight skills under the waza
namespace as /waza:think, /waza:check, /waza:hunt, etc. The dispatcher
SKILL.md was not load-bearing for this behavior in the current plugin
loader.

Restore the waza bundle entry with a description that names the eight
slash commands it exposes.

Per-skill plugin names use a waza- prefix
-----------------------------------------

To keep plugin path commands inside the same waza namespace as the
bundle, each per-skill entry's plugin name is renamed from <skill> to
waza-<skill>: waza-think, waza-check, waza-hunt, waza-design, waza-read,
waza-write, waza-learn, waza-health. Installing waza-think@waza exposes
/waza-think:think; installing the bundle keeps /waza:think.

The skill source directories and SKILL.md frontmatter are unchanged.

verify-skills.sh updated
------------------------

The marketplace section is rewritten to support both shapes:

- The bundle entry is now required (the one-command install path is part
  of the project contract); its source is enforced to "./".
- Per-skill entries must be named waza-<skill> with source
  ./skills/<skill>. An empty-suffix guard catches malformed names like
  'waza-'.
- Version and description cross-checks now key on the skill directory
  name derived from the plugin name.

The README install-command guard for the default npx install command is
unchanged.

README updated
--------------

- Document plugin marketplace install paths in the existing
  "Install and Update" section: the bundle (/plugin install waza@waza,
  exposes /waza:think etc.) and per-skill entries
  (/plugin install waza-think@waza, exposes /waza-think:think).
- Add a single-skill install example (--skill think) under both the
  Claude Code direct slash commands section and the Codex section.

What is unchanged
-----------------

- The npx skills add CLI install path. It does not read marketplace.json
  and continues to discover skills/<name>/SKILL.md directly through the
  v3.12.2 source layout.
- skills/<name>/SKILL.md content and frontmatter.
- scripts/package-skill.sh and the Claude Desktop ZIP. The single-root
  SKILL.md is still generated only during make package.
- All other verify guards: SKILL.md schema, RESOLVER.md coverage,
  description length and exclusion-clause checks, root-SKILL.md guard.
@qishaoyumu qishaoyumu force-pushed the fix/plugin-install-path branch from 1e89537 to 45bb4f9 Compare May 4, 2026 17:16
@tw93 tw93 merged commit 174e0e6 into tw93:main May 5, 2026
1 check passed
@qishaoyumu qishaoyumu deleted the fix/plugin-install-path branch May 5, 2026 06:30
tw93 added a commit that referenced this pull request May 6, 2026
fix(marketplace): restore /plugin install path and namespace per-skill entries
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants