Resolves#712 and #714.
Claude Code's plugin spec (code.claude.com/docs/en/plugins-reference) requires
"All paths must be relative to the plugin root and start with ./". CC 2.1.144+
rejects the bare "skills" form with `Validation errors: skills: Invalid input`,
breaking plugin installs from this repo.
Changes:
- 47 plugin.json manifests migrated from "skills": "skills" → ["./skills"]
(canonical post-CC 2.1.144 form, also accepts string "./skills")
- scripts/check_plugin_json.py rewritten to enforce the live spec:
* Hard-fails on non-"./"-prefixed strings (except the legacy "skills" literal)
* Hard-fails on empty strings/arrays and non-string array entries
* Emits WARN (non-fatal) on the legacy "skills" literal as a safety net
for copied templates — the WARN tolerance can be removed later
* Tested against all four canonical forms + 3 garbage forms
- CLAUDE.md §5 rewritten to document the live spec, including the historical
context of the v2.1.107 → v2.1.144 regression window that's now closed
- 15 stale broken symlinks under .gemini/skills/ removed (orphans from older
reorgs that the sync script didn't prune because its cleanup is disabled)
Verification:
- scripts/check_plugin_json.py --all → 0 FAIL, 0 WARN, exit 0 (69 files OK)
- pytest tests/ → 2103 passed
- find . -type l ! -exec test -e {} \; → 0 broken symlinks
- Live spec forms tested: ["./"], ["./skills"], "./skills", ["./a","./b"] all pass
- Garbage forms rejected: bare "./", empty array, non-string entries, plain strings
Claude Code 2.1.133+ rejects "skills": "./skills" with a "Path escapes
plugin directory" warning, even though ./skills resolves to a valid
subdirectory inside the plugin root. Per the upstream docs example, the
correct value is "skills" (no ./ prefix), which works across all CC
versions and other tools (Codex, Gemini CLI, etc.).
Applied to all 47 plugin.json files that used "./skills"; updated
CLAUDE.md ClawHub publishing constraints to document the new convention
and reference the issue.
Claude Code v2.1.126 rejects "skills": ["./"] (array form) at runtime
with the same "Path escapes plugin directory: ./" error as the bare
string form, despite the official plugins reference docs explicitly
showing ["./"] as a valid example. `claude plugin validate` accepts it,
so the bug is in the runtime loader, not the manifest validator.
The proven workaround (already used for self-improving-agent in #536)
is to follow the canonical layout:
<plugin>/
├── .claude-plugin/plugin.json skills: "./skills"
└── skills/<plugin-name>/
├── SKILL.md
├── scripts/
├── references/
└── ...
Applied to 21 single-skill plugins:
- engineering-team/a11y-audit, google-workspace-cli, snowflake-development
- engineering/behuman, code-tour, data-quality-auditor, demo-video,
docker-development, helm-chart-builder, karpathy-coder, llm-cost-optimizer,
llm-wiki, prompt-governance, statistical-analyst, terraform-patterns
- finance/business-investment-advisor
- marketing-skill/video-content-strategist
- product-team/agile-product-owner, apple-hig-expert, code-to-prd,
research-summarizer
Moved with each SKILL.md: scripts/, references/, assets/, templates/,
expected_outputs/. Kept at plugin root (per Claude Code requirements):
agents/, commands/, hooks/, settings.json, README.md.
Bumped patch version on each restructured plugin so /plugin update
picks up the new layout.
Refs #539
PR #587 fixed `"skills": "./"` in 35 plugin.json files but did not bump
versions. Claude Code uses version as the cache key — same version string
means /plugin update is a no-op and users keep loading the cached
plugin.json with the broken path. The /plugin UI shows "1 error" on
every plugin from this marketplace because the installed cache is stale.
Per the official version-management docs:
> If you set `version` in `plugin.json`, you must bump it every time
> you want users to receive changes. Pushing new commits alone is not
> enough.
Bump every plugin.json by one patch using max(plugin_version,
marketplace_version) as the base so no version moves backward. Sync the
new versions back into .claude-plugin/marketplace.json.
After this lands, users run `/plugin marketplace update claude-code-skills`
followed by `/plugin update --all` to pull the fixed manifests.
Claude Code v2.1.107+ rejects bare "skills": "./" with "Path escapes
plugin directory: ./". This blocks every plugin in the marketplace from
loading. Replace with the layout-appropriate form per the official
plugins reference:
- Single-skill plugin (SKILL.md at root):
"skills": ["./"] array form (per docs example)
- Plugin with skills/ subdir:
"skills": "./skills" standard subdir layout
- Multi-skill domain plugin (skills are subfolders at root):
"skills": ["./sub1", "./sub2"] explicit list of skill dirs
The multi-skill case omits "./" from the array so the index SKILL.md
at the plugin root does not register as a skill — that would create
a redundant `marketing-skills:marketing-skills` namespace.
Verified by running `claude plugin validate` against all 35 manifests.
Closes#539
- Move from data-analysis/ to engineering/
- Fix 5 cross-references to use correct domain paths
- Fix Python 3.9 compat in sample_size_calculator.py
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>