feat: add marketplace.json sync validation and fix Copilot schema

Applies the same shared-field sync logic to the two marketplace.json
files that we already applied to plugin.json: name, owner.name,
description, version, and the full plugin catalog (name, source,
description per entry) must match across .claude-plugin/ and
.github/plugin/ manifests.

Fixes .github/plugin/marketplace.json to use the canonical Copilot
schema (description and version nested under metadata) rather than
a copy of Claude's top-level format. Adds owner field to both files.
Validate.sh handles the structural divergence by extracting values
from either location before comparing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-20 13:46:25 +00:00
parent 5ab7054976
commit b9c41da8c3
3 changed files with 62 additions and 5 deletions

View File

@@ -224,12 +224,14 @@ ROOT="$(cd "$ROOT" && pwd)"
# ── validate marketplace.json ─────────────────────────────────────────────────
CLAUDE_MARKETPLACE="$ROOT/.claude-plugin/marketplace.json"
COPILOT_MARKETPLACE="$ROOT/.github/plugin/marketplace.json"
MARKETPLACE_JSON=""
for mp in "$ROOT/.claude-plugin/marketplace.json" "$ROOT/.github/plugin/marketplace.json"; do
for mp in "$CLAUDE_MARKETPLACE" "$COPILOT_MARKETPLACE"; do
if [[ -f "$mp" ]]; then
MARKETPLACE_JSON="$mp"
[[ -z "$MARKETPLACE_JSON" ]] && MARKETPLACE_JSON="$mp"
validate_marketplace_json "$mp"
break
fi
done
@@ -237,6 +239,57 @@ if [[ -z "$MARKETPLACE_JSON" ]]; then
warn "No marketplace.json found. Expected at .claude-plugin/marketplace.json"
fi
# Marketplace sync check — shared identity fields must match; description/version
# legitimately differ in structure (Claude: top-level; Copilot: under metadata)
if [[ -f "$CLAUDE_MARKETPLACE" && -f "$COPILOT_MARKETPLACE" ]]; then
cm_val=$(jq -r '.name // empty' "$CLAUDE_MARKETPLACE")
cp_val=$(jq -r '.name // empty' "$COPILOT_MARKETPLACE")
if [[ "$cm_val" != "$cp_val" ]]; then
error "marketplace: 'name' differs — .claude-plugin ('$cm_val') vs .github/plugin ('$cp_val')"
fi
cm_val=$(jq -r '.owner.name // empty' "$CLAUDE_MARKETPLACE")
cp_val=$(jq -r '.owner.name // empty' "$COPILOT_MARKETPLACE")
if [[ ( -n "$cm_val" || -n "$cp_val" ) && "$cm_val" != "$cp_val" ]]; then
error "marketplace: 'owner.name' differs — '$cm_val' vs '$cp_val'"
fi
# description: Claude top-level, Copilot under metadata — compare values regardless of path
cm_val=$(jq -r '.description // .metadata.description // empty' "$CLAUDE_MARKETPLACE")
cp_val=$(jq -r '.metadata.description // .description // empty' "$COPILOT_MARKETPLACE")
if [[ ( -n "$cm_val" || -n "$cp_val" ) && "$cm_val" != "$cp_val" ]]; then
error "marketplace: description differs between .claude-plugin/marketplace.json and .github/plugin/marketplace.json"
fi
# version: same structural divergence as description
cm_val=$(jq -r '.version // .metadata.version // empty' "$CLAUDE_MARKETPLACE")
cp_val=$(jq -r '.metadata.version // .version // empty' "$COPILOT_MARKETPLACE")
if [[ ( -n "$cm_val" || -n "$cp_val" ) && "$cm_val" != "$cp_val" ]]; then
error "marketplace: version differs — '$cm_val' vs '$cp_val'"
fi
# Plugin catalog must be identical across both files
cm_plugins=$(jq -r '.plugins[].name' "$CLAUDE_MARKETPLACE" 2>/dev/null | sort)
cp_plugins=$(jq -r '.plugins[].name' "$COPILOT_MARKETPLACE" 2>/dev/null | sort)
if [[ "$cm_plugins" != "$cp_plugins" ]]; then
error "marketplace: plugin lists differ between .claude-plugin/marketplace.json and .github/plugin/marketplace.json"
else
while IFS= read -r pname; do
[[ -z "$pname" ]] && continue
cm_val=$(jq -r --arg n "$pname" '.plugins[] | select(.name==$n) | .source // empty' "$CLAUDE_MARKETPLACE")
cp_val=$(jq -r --arg n "$pname" '.plugins[] | select(.name==$n) | .source // empty' "$COPILOT_MARKETPLACE")
if [[ "$cm_val" != "$cp_val" ]]; then
error "marketplace plugin '$pname': source differs — '$cm_val' vs '$cp_val'"
fi
cm_val=$(jq -r --arg n "$pname" '.plugins[] | select(.name==$n) | .description // empty' "$CLAUDE_MARKETPLACE")
cp_val=$(jq -r --arg n "$pname" '.plugins[] | select(.name==$n) | .description // empty' "$COPILOT_MARKETPLACE")
if [[ ( -n "$cm_val" || -n "$cp_val" ) && "$cm_val" != "$cp_val" ]]; then
error "marketplace plugin '$pname': description differs between the two marketplace.json files"
fi
done <<< "$cm_plugins"
fi
fi
# ── validate plugins ──────────────────────────────────────────────────────────
if [[ -n "$PLUGIN_ONLY" ]]; then

View File

@@ -1,5 +1,6 @@
{
"name": "holocron",
"owner": { "name": "Your Name", "email": "you@example.com" },
"description": "AI development skills for Claude Code and GitHub Copilot CLI — factory, design, implement, review, and cross-cutting workflows.",
"version": "0.1.0",
"plugins": [

View File

@@ -1,7 +1,10 @@
{
"name": "holocron",
"description": "AI development skills for Claude Code and GitHub Copilot CLI — factory, design, implement, review, and cross-cutting workflows.",
"version": "0.1.0",
"owner": { "name": "Your Name", "email": "you@example.com" },
"metadata": {
"description": "AI development skills for Claude Code and GitHub Copilot CLI — factory, design, implement, review, and cross-cutting workflows.",
"version": "0.1.0"
},
"plugins": [
{
"name": "hello-world",